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 (1–1440). 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 sil.ac/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 sil.ac/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://sil.ac/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 pending → ringing → live → ended 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..."
}

Re-share a session

POST/sessions/:id/reshare

Accepts an API key, an OAuth token, or a dashboard session. Reviving extends the existing invitation in place — same link, same code — so a link the customer already has starts working again. A session created without a short link can be extended but not re-sent; the response says which happened.

Revive an invitation whose link expired. mode: "revive" (the default) extends it in place, so the link already in the customer’s hands works again; mode: "new" issues a fresh code and token and permanently kills the old one, including its short link.

json
{
  "mode": "new",
  "channel": "sms",
  "recipient": "+15145550199",
  "smsConsent": true
}

channel and recipient default to the invitation’s current values and are persisted when supplied. resend is sent, suppressed (the number opted out of SMS), not_requested (the link channel — nothing is sent, the agent shares inviteShortUrl themselves) or unavailable (no short link, so the link cannot be rebuilt to send). No guest URL, code or token is ever returned.

json
{
  "expiresAt": "2026-08-07T21:12:00.000Z",
  "inviteShortUrl": "https://sil.ac/s/Qm81zRdp",
  "resend": "sent",
  "rotated": true
}

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