Real-time events
Signing pages, kiosks and consoles receive live updates from an AWS AppSync Events API instead of polling: a participant viewed the document, signed, the signed PDF is ready. Real-time events are meant for screens people look at. For your back end, use webhooks: they are signed, retried and recorded, real-time events are best effort.
Channels
Section titled “Channels”| Channel | Who subscribes | Authorization |
|---|---|---|
/cases/{tenantId}/{caseId}, or /cases/{tenantId}/* |
your console | a tenant token; {tenantId} must be the token’s sub |
/participants/{participantId} |
the signing page of a participant | the participant token of the signing link (the apiToken parameter of signerUrl) |
/devices/{tenantId}/{deviceId} |
a kiosk device | the device token from POST /tenant/devices |
Only TrustInk publishes. Every other channel is refused, as are wildcards on participant and device channels and any attempt to publish with a token. A tenant can subscribe to its own cases only.
Connecting
Section titled “Connecting”<realtime host> and <http host> below are the hosts of the TrustInk events API of your environment.
-
Open
wss://<realtime host>/event/realtimewith two subprotocols:aws-appsync-event-wsandheader-<base64url JSON>, where the JSON is{"host": "<http host>", "Authorization": "<token>"}. -
Send
{"type": "connection_init"}and wait forconnection_ack. -
Subscribe:
{"type": "subscribe", "id": "sub-1", "channel": "/participants/7a1c…","authorization": {"host": "<http host>", "Authorization": "<token>"}}AppSync answers
subscribe_successorsubscribe_error. -
Events arrive as
{"type": "data", "id": "sub-1", "event": "<JSON string>"}. Keep the connection whileka(keep-alive) messages keep coming, at least every 5 minutes.
const header = btoa(JSON.stringify({host: httpHost, Authorization: token})) .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');const socket = new WebSocket(`wss://${realtimeHost}/event/realtime`, ['aws-appsync-event-ws', `header-${header}`]);
socket.onopen = () => socket.send(JSON.stringify({type: 'connection_init'}));socket.onmessage = ({data}) => { const message = JSON.parse(data); if (message.type === 'connection_ack') { socket.send(JSON.stringify({ type: 'subscribe', id: 'sub-1', channel: `/cases/${tenantId}/${caseId}`, authorization: {host: httpHost, Authorization: token}, })); } if (message.type === 'data') { const event = JSON.parse(message.event); console.log(event.type, event.data); }};The protocol is AWS’s; see the AppSync Events WebSocket protocol for the details.
Events
Section titled “Events”{"type": "participant.signed", "occurredAt": "2026-09-24T08:00:00Z", "tenantId": "…", "caseId": "…", "participantId": "…", "data": {"signedAt": "2026-09-24T08:00:00.000Z"}}type |
Channels | When | data |
|---|---|---|---|
participant.invited |
case | the case starts waiting for the participant | {} |
participant.viewed |
case | the participant opened the document | {} |
participant.signed |
case, the participant’s | the participant signed | {signedAt} |
case.revision.created |
case, the participant’s | the signature is in the document’s next PAdES revision | {revision, signatureCount} |
case.completed |
case, every participant’s | the case is done and the signed PDF ready | {status: "DONE", signedDocumentAvailable} |
case.expired |
case, every participant’s | validUntil passed |
{status: "EXPIRED", signedDocumentAvailable: false} |
case.cancelled |
case, every participant’s | you cancelled the case | {status: "CANCELLED", signedDocumentAvailable: false} |
case.failed |
case, every participant’s | the document could not be signed | {status: "FAILED", signedDocumentAvailable: false} |
device.open_case |
the device’s | a kiosk case is ready for its next participant | {caseId, participantId, signerUrl, title} |
participantId is set on the participant.* and device.open_case events. No event carries a link to a
document: a page that sees signedDocumentAvailable fetches the PDF with its own token
(GET /participant/{participantId}/signedDocument). The signerUrl of device.open_case holds the
participant’s token, which is why only the device’s own token may subscribe to its channel.
For the kiosk hand-off with device.open_case, see in-person signing.