Logging Interface Access

I hope this message finds you well.

I would like to log the event of an interface being opened.
However, my understanding is that on the interface itself, unless an action such as clicking a button occurs,

it is not possible to write a record or start a process model.

When consulting ChatGPT, I received the following idea:

  1. Create a Web API on Appian that returns an image as the response and simultaneously saves a log.
  2. Place an image on the target interface and set the endpoint of the Web API created in (1) as the image source.
  3. As a result, when the interface is opened, the Web API in (1) is called, and the log is recorded.

However, it seems that the function a!writeToDataStoreEntity does not get triggered inside the Web API.

I have two questions and would appreciate your advice:

  1. What should I review to implement the above ChatGPT idea successfully?
  2. Are there alternative methods to log when an interface is opened?

Thank you in advance for your kind advice.

  Discussion posts and replies are publicly visible

  • 0
    Certified Senior Developer

    Keizo,

    Create the Web API as a POST but make sure that you check the Queries Data and not Modifies data.

    - The webAPI must create an entry onto the database table where you are logging the event.

    Create an integration to call that webAPI

    In your interface, call the integration

    a!localVariables(

    local!eventLog: rule!MY_Integration(),

    a!sectionLayout(

    label: "My Label",

    contents: {}

    )

    )

    This should work. Let me know if you got any questions

    Lejanson

  • 0
    Certified Lead Developer
    in reply to Keizo Watsuji

    This isn't really how to structure what I was suggesting.  My setup has 5 main layers - it could potentially be condensed some, but the layer structure defined here gives you levels of abstraction that will increase potential reusability and reduce rework efforts should you decide to change the way something works in the future.

    1. On your interface, call an expression rule in a Local Variable definition, passing in the person's username, and relevant details about where they're viewing from.
    2. In the expression rule, call an Integration you set up, and map the results.  It doesn't need to do anything as complex as returning an "image file", it could simply return a timestamp or boolean value.  My suggestion would be simply having it return a "last updated" timestamp (to be stored in the aforementioned Local Variable).
    3. In the integration, call the Web API you set up, passing in the details sent in from the Expression Rule.  Tie it to a Connected System pointing at your own environment and with a Service Account that has permissions to launch the process model used in the Web API.
    4. in the Web API, configure as a POST method and call the designated Process Model using a!startProcess(), passing in the same relevant details having been passed down.
    5. In the process model, you can do your DB write / update, passing back values to the caller indicating that it ran successfully (such as a "last updated" timestamp value).  If you decide to look up an existing row for the user in question (such as grouping rows by day, so as not to clutter the table with a row for every single refresh), it will be trivially easy, for instance, to pre-query any relevant row for the user per location/day, and if one exists, update that row, and otherwise, create a new one.  Or just write a new row every time, that part is up to you.
  • Thanks Jesse.

    I understood that. But the error "Smart Services cannot be executed in Web APIs that have a method of 'GET'." is still remaining...

  • I'm sorry for causing confusion. The "Portal Site" I mentioned is not the Appian Portal, but a portal site in the usual sense, meaning the entry point for users.

  • 0
    Certified Lead Developer
    in reply to Keizo Watsuji

    Web APIs using 'GET' method cannot call any smart services as per Appian Design. So for intended functionality use the 'POST' method in web API and setup an Integration that calls your web API from target interface. As soon as interface loads, the Integration (executed on load against a local variable) will call the API. API will execute the smart service and record log with a success response for integration. When setting up a!httpResponse for web API in response body you can send back the image as well.

    a!httpResponse(
      statusCode: 200,
      headers: {
        a!httpHeader(name: "content-type", value: "application/json")
      },
      body: a!iconIndicator("STATUS_WARN")
    )

    However as earlier discussed in this thread that to log the interface load activity you don't necessarily need to follow what ChatGPT suggested. 

    The web API can write to db and respond back with a successful message. That web API can be called using an integration in the interface. So on load the integration->web API->write records will execute. On Error of API execution, response can be a user friendly message asking them to re-load the interface.

    Is there any reason for image to be involved in the response and showing it on interface if you just want to log the interface load activity? 

  • 0
    Certified Lead Developer
    in reply to Harsha Sharma

    With the POST, be sure to select 'queries data', and this should work ok. If you select 'modifies data', you get an error related to the fact that the integration call is not in a saveInto.

    A possible quirk about retrieving images via API: We don't know if there are performance issues at scale when retrieving images directly via API versus using a!documentImage().So to avoid any potential issues when using an image in this way, it may be best for the API to return a document id for use with a!documentImage(). That keeps the API layer small / concise, and keeps the performance of the UI expression in line with what's generally expected when many users load the image.

       One possible reason to make this an image retrieval is so that there's a reusable UI component that's placeable in any UI, including collapsible sections / hidden parts of the UI that are conditionally shown. The reusable component would receive any metadata about the section context, automatically call the API on demand, and load a 1x1 pixel image with no padding / borders onto the UI. Without the UI component aspect, the logging isn't very reusable as an encapsulated rule.

  • Thanks Lejanson.

    >Create the Web API as a POST but make sure that you check the Queries Data and not Modifies data.
    It's very useful for me. 

  • Thanks Mike,

    I've tried implementing accoding to your advice and I'm stacking on the connection between integration and Web API.

    My Web API runs correctly when I click TEST button on it.
    But when I click TEST button on my integration, it fails as below:

    -------------------------------------------------------

    Value
    Dictionary
    success: false
    result: HttpResponse
    statusLine: "HTTP/1.1 500 "
    statusCode: 500
    headers: Dictionary
    Date: "Sun, 19 Oct 2025 11:04:35 GMT"
    Content-Type: "text/plain;charset=ISO-8859-1"
    Transfer-Encoding: "chunked"
    Connection: "keep-alive"
    X-Trace-Id: "522127991e953a7fe2716155fe5ecddc"
    Strict-Transport-Security: "max-age=31536000; includeSubDomains; preload"
    Referrer-Policy: "strict-origin-when-cross-origin"
    X-Frame-Options: "SAMEORIGIN"
    Content-Security-Policy-Report-Only: "style-src 'unsafe-inline' 'self' web-assets.appian-cdn.com 'strict-dynamic' 'nonce-YTlkMTBmNzUtZDRiMi00ZGEzLWFhMGYtYmM2NGExMWM5MWE1'; child-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval' web-assets.appian-cdn.com 'strict-dynamic' 'nonce-NGVjYmY4NjMtZDUzZS00Y2VmLTk5MWEtMmI5Y2NlNjJkYTcy'; object-src https:; frame-src https:; img-src https:; default-src 'self'; font-src 'self' https: data:; report-uri /suite/rest/a/logging/latest/csp/report; report-to report;" Content-Security-Policy: "report-uri /suite/rest/a/logging/latest/csp/report; report-to report;"
    Reporting-Endpoints: "report="/suite/rest/a/logging/latest/csp/report""

    X-XSS-Protection: "1; mode=block"
    Requested-While-Authenticated: "false"
    contentType: "text/plain;charset=ISO-8859-1"
    body: "500 - Internal Server Error"
    error: IntegrationError title: "The external system encountered an error"
    message: "Failed to connect to https://{my appian url}/suite/webapi/{my end point}"
    detail: "HTTP/1.1 500 "
    connectedSystem: null (Connected System)
    binaryDoc75MbLimitExceeded: false

    -------------------------------------------------------


    I can see connected system setting on my integration but it says 
    "connectedSystem: null (Connected System)"

  • I was able to resolve the issue on my own.

    The error was related to the setting in the onError of a!startProcess.
    In other words, the Process Model failed to start.

    Although I had configured the Web API authentication to use a security account,
    the security settings for the Process Model and Data Store Entity were missing, which caused the problem.

    I've already fixed it and the original goal of saving logs on the screen display has been about 80% completed.

    As for the remaining 20%, I will create a separate thread to ask questions.

  • 0
    Certified Lead Developer
    in reply to Keizo Watsuji

    Ok good, glad to hear you got the error resolved.