Docs/Sessions

Sessions

Create a live-view session, send the customer an invite, and poll its status. Timestamps are ISO-8601 UTC (…Z).

Two ways to authenticate
Creating a session, checking its status, getting a session, and minting an agent token accept a dashboard JWT, a team API key, or an OAuth token with the matching scope. Listing sessions stays dashboard-only (JWT).

Create a session

POST/sessions

Creates a session and its invitation, then dispatches the invite link over the chosen channel. Pass teamId with a dashboard JWT; an API key implies its own team.

FieldDescription
teamIdThe team's UUID. Required when authenticating with a dashboard JWT; inferred automatically from an API key.
channelHow the invite is delivered: sms, email, or link (nothing is sent — you distribute the URL yourself).
recipientPhoneThe customer's phone number in E.164 format (e.g. +15555550123). Required when channel is sms.
recipientEmailThe customer's email address. Required when channel is email — the message includes the link plus an inline QR code.
noteAn optional internal label, up to 500 characters (e.g. a ticket number), shown alongside the session in the dashboard.
ttlMinutesHow long the invite stays valid, in minutes (11440). Defaults to 30.
smsConsentRequired when channel is sms: must be true to confirm the recipient agreed to receive a text from seeitlive.io. Carrier fees may apply.
metadataOptional custom key/value pairs (a flat map of strings, e.g. your own external agent id or ticket reference). Stored opaquely and returned on reads. Up to 30 keys; keys up to 64 characters, values up to 500.
shortLinkOptional. When true, mints a short sitl.cc/s/code link for the guest URL, returned as inviteShortUrl. Defaults to false. SMS invites always use a short link.
protectionOptional. password or totp. Requires the guest to enter a secret before their camera can be shared. Omit for an unprotected link.
protectionSecretOptional. The password, or a base32 TOTP seed. Omit it and we generate one and return it once in the response.
curl -X POST https://api.seeitlive.io/v1/sessions \
  -H "Authorization: Bearer sil_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "sms", "recipientPhone": "+15555550123", "smsConsent": true, "note": "Ticket #4821" }'

inviteShortUrl is a short sitl.cc/s/code alias of the guest inviteUrl, or null when no short link was minted. Agents always embed the full agentViewUrl.

json
{
  "id": "f74f1305-2222-4a11-8b1e-1234567890ab",
  "status": "pending",
  "inviteCode": "aZ3kQ9",
  "inviteUrl": "https://seeitlive.io/u/aZ3kQ9#3n9F1Qk7xY2zR8vB5tW6uL0mC4pD1sA...",
  "inviteShortUrl": "https://sitl.cc/s/aB3xK9p2",
  "agentViewUrl": "https://seeitlive.io/a/pL2mN8#7dR2Xa4Wq9Kj6bV3nF1yT8sH5cE0uZ...",
  "protection": "totp",
  "protectionSecret": "JBSWY3DPEHPK3PXP",
  "protectionOtpauthUrl": "otpauth://totp/SeeItLive:session-aZ3kQ9?secret=JBSWY3DPEHPK3PXP&issuer=SeeItLive",
  "expiresAt": "2026-07-07T09:42:00.000Z"
}
Shown once
inviteUrl and agentViewUrl carry the plaintext session tokens in their URL fragment. They're returned only on creation — store them immediately.

Get a session

GET/sessions/:id

Returns a session, including its metadata and its invitation (code, expiry, opened time, channel, recipient) — never a token hash or guest secret. Requires the view_sessions permission on a dashboard JWT, or an API key/OAuth token scoped sessions:read. Returns 404 if the session doesn't exist.

curl https://api.seeitlive.io/v1/sessions/f74f1305-2222-4a11-8b1e-1234567890ab \
  -H "Authorization: Bearer $SEEITLIVE_KEY"
json
{
  "id": "f74f1305-2222-4a11-8b1e-1234567890ab",
  "teamId": "b5a2b2b0-6c7a-4e2f-9f2a-1234567890ab",
  "env": "live",
  "status": "live",
  "createdByClientId": "9d8c7b6a-5f4e-3d2c-1b0a-abcdef123456",
  "createdByApiKeyId": null,
  "note": "Ticket #4821",
  "metadata": { "ticketId": "4821" },
  "startedAt": "2026-07-07T09:14:03.000Z",
  "endedAt": null,
  "createdAt": "2026-07-07T09:12:00.000Z",
  "invitation": {
    "code": "aZ3kQ9",
    "expiresAt": "2026-07-07T09:42:00.000Z",
    "openedAt": "2026-07-07T09:13:41.000Z",
    "channel": "sms",
    "recipient": "+15555550123"
  }
}

Session status

GET/sessions/:id/status

Poll this to know when the customer is on camera.

status moves through pendingringingliveended as the customer joins and leaves, or short-circuits to expired (the invite was never opened) or cancelled.

json
{
  "id": "f74f1305-2222-4a11-8b1e-1234567890ab",
  "status": "live",
  "startedAt": "2026-07-07T09:14:03.000Z",
  "endedAt": null,
  "producerCount": 1,
  "hasVideo": true
}

Protecting a session

A protected session asks the guest for a secret you communicate out of band — typically the agent reads it out on the phone. With password the secret is static for the life of the invitation. With totp it is a 6-digit code that rotates every 30 seconds; supply your own base32 seed to render the current code in your own agent tooling, or let us generate one.

Passwords are compared loosely on purpose
A spoken password must not fail on capitalisation, so the comparison trims whitespace and ignores case. Generated passwords use 8 characters from an alphabet with no I, O, 0 or 1.

Reading the current secret

GET/sessions/:id/protection

Returns what the agent should read out: the password, or the code of the moment with the seconds left before it rotates. The TOTP seed itself is returned only once, when the session is created.

json
{
  "type": "totp",
  "password": null,
  "code": "418205",
  "secondsRemaining": 17,
  "locked": false,
  "failedAttempts": 0
}

Set the protection

PUT/sessions/:id/protection

Change a session’s guest challenge after it was created. Omit protectionSecret and one is generated for you — an 8-character password, or a base32 TOTP seed. Returns the same shape as the GET above.

json
{
  "protection": "password",
  "protectionSecret": "K7QMX2RT"
}

Sending {"protection": "none"} removes the challenge, and clears any lockout with it. Every change resets the failed-attempt counter, because the customer is about to be told a different secret.

Clear a lockout

POST/sessions/:id/protection/reset

Five wrong answers lock the invitation. This clears the lockout; pass rotate: true to also generate a new secret. Rotation is opt-in because the usual cause is a mistyped code, and a new secret just means reading it out again.

json
{
  "rotate": true
}

Guest unlock

POST/invitations/unlock

The guest page exchanges the answer for a short-lived unlock token, which the signaling handshake then requires. Five wrong answers lock the invitation and return 403 with code: "invitation_locked" until an agent resets it.

Post the invitation code, its token (the fragment of the invite URL) and the customer's answer — the password, or the 6-digit code of the moment. Omit answer for an unprotected invitation.

json
{
  "code": "aZ3kQ9",
  "token": "3n9F1Qk7xY2zR8vB5tW6uL0mC4pD1sA...",
  "answer": "418205"
}

On success you get a short-lived proof. It expires after 60 minutes, or with the invitation if that comes first, and stops being accepted the moment the secret is rotated.

json
{
  "unlockToken": "sil_gu_eyJpIjoiOWQ4YzdiNmEiLCJlIjoxNzg1...",
  "expiresAt": "2026-08-05T10:42:00.000Z"
}

Claiming the invitation

POST/invitations/claim

The guest page calls this to authenticate and make the session ring on the agent's screen. A protected invitation must also present the unlock token from the previous step.

json
{
  "code": "aZ3kQ9",
  "token": "3n9F1Qk7xY2zR8vB5tW6uL0mC4pD1sA...",
  "unlock": "sil_gu_eyJpIjoiOWQ4YzdiNmEiLCJlIjoxNzg1..."
}

Without it — or with a proof issued for another invitation, or against a secret that has since been rotated — the call is refused with 403 and code: "protection_required", leaving the session untouched.

json
{
  "statusCode": 403,
  "error": "ForbiddenError",
  "message": "This session is protected — enter the password or code first",
  "code": "protection_required"
}

Mint an agent token

POST/sessions/:id/agent-token

Issues a fresh agent-view token, valid for 120 minutes, for a session the caller already has access to — use it to re-embed the viewer, e.g. after the original agentViewUrl expired. Accepts an optional label (1–64 characters) shown in the dashboard's viewer list; omit it and an API key or OAuth caller gets "Integration viewer", a dashboard JWT gets "Dashboard viewer". Requires the view_sessions permission on a dashboard JWT, or an API key/OAuth token scoped sessions:write.

curl -X POST https://api.seeitlive.io/v1/sessions/f74f1305-2222-4a11-8b1e-1234567890ab/agent-token \
  -H "Authorization: Bearer $SEEITLIVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "label": "Odoo — crm.lead 42" }'
json
{
  "code": "qR7mZ2",
  "token": "5aQ1nD8vC3xF6yK9bH2wS7pM4dJ0uT1gN...",
  "agentViewUrl": "https://seeitlive.io/a/qR7mZ2#5aQ1nD8vC3xF6yK9bH2wS7pM4dJ0uT1gN..."
}

Resolve an invite (public)

GET/u/:code

The public endpoint the guest page calls to resolve an invite code into its session and brand — no token or key required. Joining the stream is authenticated separately, with the token embedded in the invite URL's fragment.

json
{
  "sessionId": "f74f1305-2222-4a11-8b1e-1234567890ab",
  "brand": {
    "name": "Acme Support",
    "brandLogoUrl": null,
    "brandColor": "#1a2b3c"
  },
  "status": "pending",
  "protection": "totp",
  "locked": false
}
Rate limited
Limited to 20 requests per minute per IP, since it's unauthenticated and reachable from anywhere.
Sessions