Table of Contents

How the Widget Is Requested

Helpdocs Admin Updated by Helpdocs Admin

When a ticket is opened, the client asks the provider for the widget's content. The same kind of request is sent every time the widget changes: on first load, when the user presses an action, and on refresh. This article describes that request, so a provider knows what it receives and how to use it.

In short: the client sends a POST request with a JSON body. The body says which ticket, who is asking, which server it came from, and what the widget remembered from last time. The provider replies with the widget content.

The basics

  • The method is always POST.
  • There are no query parameters added by the client. All data is in the JSON body.
  • The URL is the widget URL for the first load, or the action's url after an action is pressed. The body has the same shape either way.
  • On refresh, the client uses the URL that is currently loaded, not the original (see Actions).

What the body contains

export interface GetTicketWidget {
readonly ticketId: number;
readonly delegateToken: DelegateToken;
readonly database: string;
readonly origin: string;
readonly form?: Record<string, TicketWidgetFieldResult>;
readonly state?: Record<string, string>;
}

export interface DelegateToken {
readonly value: string;
readonly expiresIn: number;
readonly expiresAt: IsoDateString;
}

export interface TicketWidgetFieldResult {
readonly choices?: ReadonlyArray<string>;
}

  • Ticket. ticketId is the ticket the widget is shown on.
  • Server. This is an ITSM single-tenant product, so each installation has its own server. Two fields identify it:
    • database is the server's database name.
    • origin is the server host with https://, for example https://example.itsm.com.
  • An integration can use these to work out which installation sent the request, and where to call back (see "Calling the server").
  • Caller. delegateToken identifies the current user. See "Who is the user?".
  • Memory. state and form carry what the widget remembered. See "Passing data between calls".

Who is the user?

delegateToken tells the provider who is looking at the widget, and whether it is an agent or a contact. It is similar to an OpenID token.

It has three parts: value (the token itself), expiresIn (seconds until it expires) and expiresAt (the expiry time, as an ISO date).

There are two ways to check it:

  • Ask the server. Call the server's API with the token:
GET /api/v4/me
Authorization: DdAccessToken <delegate token>

The response describes the current user. This is the simplest option.

Verify it yourself

Get the public key from /.well-known/jwks.json on the server and validate the token's signature locally. This avoids a call per request.

Do not trust the token without checking it, and do not use it after it has expired.

Passing data between calls

The provider does not need to store anything between requests. The client keeps the widget's data and sends it back each time, so the provider can stay stateless.

state is where the provider stores its own data. Typical uses are navigation info (such as which step the user is on) or past actions. The client sends the state from the last response it received.

  • It is key and value strings.
  • The provider sets it in a response. The client returns it unchanged on the next request.

form holds what the user has selected. It is sent when the current widget is a form. Each field's value is a list of choices, and each choice is an ID (up to 100 characters).

{
"form": {
"priority": { "choices": ["high"] },
"teams": { "choices": ["network", "security"] }
}
}

The provider can use form for the same purpose as state. For example, it can return it in the next response to restore what the user picked. How the body is treated depends on the action pressed (see Actions): for leading and secondary actions it is a snapshot of work in progress, and for the primary action it is a real submission.

Example request

POST /widgets/order-status
Content-Type: application/json

{
"ticketId": 4821,
"delegateToken": {
"value": "<token>",
"expiresIn": 300,
"expiresAt": "2026-10-16T10:05:00Z"
},
"database": "acme",
"origin": "https://acme.example.com",
"state": { "step": "2" },
"form": { "priority": { "choices": ["high"] } }
}

Calling the server

To read more about the ticket or the user, the provider can call back to origin, using the delegate token as shown above. It acts as the current user, so it only sees what that user may see.

Designing action URLs

The request body does not say which action was pressed. The provider can only tell from the URL, so design the URLs to show it.

  • Best: a different route per action, for example /widgets/order/next and /widgets/order/back.
  • If you share one route, add a query parameter to each action URL, for example /widgets/order?action=back. The client does not add query parameters, but it keeps the ones in the URL you give it.

This matters most for telling a submission (primary action) from a snapshot (leading or secondary). Never rely on the body alone to decide.

How did we do?

Ticket Widget: Overview

Ticket Widget Anatomy and Styling

Contact