Signed-token public share plane
Some of what Orbit captures is only useful if a person outside your Orbit account can see it: a customer disputing what was said, an auditor reading one conversation thread, a customer checking where their callback is in the queue, a reseller’s client embedding an analytics dashboard, an end customer booking a queue appointment from a hosted page. Each of those reaches the recipient through a public share link — a URL that works in a fresh browser with no sign-in, no API key, and no Clerk session. The authentication in every one of those URLs is a signed actor token: a compact credential minted by your account that is itself the whole proof. This page explains the one model that drives all the share families, what protects the link in flight and at view time, and the limits of that protection — it is the page to hand your security team when they ask “is it safe to share this link?” It complements the object-specific pages (Recording lifecycle, Session replay) that each cover their own slice.The actor-token model — the token is the authentication
Every share family has an authenticated mint endpoint behind your session. When an agent or integration calls it, Orbit builds a small JSON payload holding exactly the identifiers the resolver needs, signs it with HMAC-SHA256, and returns a URL the recipient can open:- Conversation transcripts —
POST /api/v1/conversations/:id/sharemints the link a resolver serves atGET /api/v1/public/conversations/:token/transcript. - Recordings —
POST /api/v1/recordings/:id/sharemints the link served atGET /api/v1/public/recordings/:token. - Callback status — the callback-dispatch scheduler texts the customer a link to
GET /api/v1/public/callbacks/:token(status, one-tap cancel, one-tap reschedule). - Analytics dashboard embeds —
POST /api/v1/analytics/dashboards/sharemints the URL consumed byGET /api/v1/public/analytics/dashboards/embed?token=…; embedding your own dashboard view in an iframe carries the token as a query parameter instead. - Queue appointments — a tenant-keyed hosted booking page (
GET /api/v1/public/queues/:tenantId/:queueId/availability) that publishes a queue’s bookable slots and lets an end customer self-book a callback slot.
base64url(JSON payload) + "." + base64url(HMAC-SHA256(secret, body)). What that implies for you: the link is a capability — possession is sufficient to open it — until you add one of the gates below.
Tenant identity travels inside the signed body, never the URL
The one rule the whole plane rests on: the tenant and object identity are inside the signed payload, not in the URL path parameters. A transcript token signs{ conversationId, tenantSchema, organizationId, issuedAt, expiresAt, … }, and the resolver derives which tenant to read from the verified payload — never from the URL. Editing a path segment produces a token whose signature no longer matches, and verification fails before any storage is touched (see Tenant binding below). A recipient cannot hop to another tenant’s data by rewriting the link; the bytes they would need are bound into the HMAC tag they cannot recompute.
Token verification, and the deliberate vagueness of a 410
Verification runs in a fixed order before any content is read:- Decode the payload and recompute the HMAC-SHA256 tag.
- Compare tags in constant time.
- Validate the payload shape and wire version.
- Reject on expiry.
410 Gone with the same non-disclosing message. This is deliberate: distinguishing “this token was tampered with” from “this token expired” tells a probing attacker which kind of manipulation succeeded, so the resolver refuses to leak it.
Optional access gates: password and email-domain
On top of the bearer token, the mint can bind two optional gate fields into the same signed body:- Password — the mint stores a keyed digest of the password, never the plaintext. The resolver recomputes the digest from the recipient-supplied password and compares in constant time. The
conversationIdis folded into the digest, so a stolen digest cannot be replayed against a different conversation. - Email-domain allowlist — up to 25 normalized domains. The recipient types their email and the domain must match before any content renders. This is a soft deterrent against casually forwarded links, layered on top of the token; it is not email verification and is never a replacement for the token itself.
401 with an unlock form that re-submits to the same URL — the protected content is never sent to a recipient who only holds the link.
The JWKS story: ecosystem-wide verification
Share tokens minted by the unified actor-token service respond to a broader verification rule: the platform publishes the public half of its signing key as a standard JWK Set atGET /api/v1/public/.well-known/actor-token-jwks.json. An external verifier — a merchant, an auditor, a customer’s security team — reads the token’s key id, fetches the set, and verifies the signature offline, without being handed a shared secret. Token headers advertise the set’s own URL, so a token and the document that publishes its verifier can never drift apart.
The JWKS document only ever carries public keys; publishing it is safe by design, and rotation becomes “re-publish one public key” rather than an emergency distribution of secrets.
Tenant binding from the signed payload, never the URL
The resolver’s tenant reference is derived from the signedtenantSchema inside the verified payload. Because every byte of the payload is bound into the HMAC tag, there is no path where a recipient supplies the tenant identity — not a query parameter, not a header, not the URL path. The database read is confined to the tenant the token was minted against, and cross-tenant reads are structurally impossible, not merely checked.
One rate-limit envelope for the public surface
Every public share resolver applies the platform’s IP-keyed public unauthenticated envelope, the same cap as the other public signed-token landing surfaces. Brute-forcing a link is bounded; a mint behind your session is not throttled by this envelope, so issuance stays cheap at API volume.Read-only invariant
The public share surfaces are strictly read-only resolvers: a pure SELECT of the bound thread, recording, callback row, dashboard layout, or availability window, rendered as HTML or JSON. Callback status and queue appointments are the exception — they deliberately accept the recipient’s one-tap cancel/reschedule/book action through the same tenant-bound token, and the same flow guards mutations to the entity the token travels in. Nothing outbound is emitted from any resolver.The worked example: mint a transcript link and gate it
-
From a dashboard session, mint the link:
Orbit clamps the TTL to the share family’s allowed band (for transcript links, 1 day to 90 days, defaulting to 7 days), hashes the password, signs the payload, and returns the full public URL.
- Hand the URL plus the password to the recipient through two separate channels.
-
The recipient opens the link. Verification passes; the password gate is unsatisfied, so the resolver renders the unlock form (
401in the resolver’s internal flow, an HTML form to the human). The correct password renders the transcript; a wrong one re-renders the same unlock form until the IP-keyed rate envelope throttles the attempts. - After the TTL expires, or after the tenant rotates the signing secret, the same URL returns the same 410 as a forged token — no longer a live document.
Which share families participate
Survey instruments mint their own response tokens on a separate instrument pipeline — see The survey lifecycle; the model is the same one-plane family with its own payload shape.
What to tell your security team
- The link is a capability; possession is sufficient. Share it only with the intended recipient, and gate it with a password or email-domain allowlist when the content is sensitive.
- A tampered or expired link is indistinguishable from a wrong link —
410 Gonein both cases, with no oracle to probe. - Tenant identity is signed into the token, so forwarding the URL does not broaden access to other tenants’ data.
- Verification is decidable offline: the public JWK Set at
GET /api/v1/public/.well-known/actor-token-jwks.jsonproves the signature without access to any shared secret. - Rate limits bound link-guessing, and the read-only invariant means a resolver never emits outbound traffic on the recipient’s behalf.