sparka home

API reference

Join

Participant PIN lookup and join, called by the join page.

Look up a PIN

GET /v1/pins/{pin}

Credential: None · Rate limit: 1,000/min (burst 1,000) per IP; 20 PIN misses/min per IP (shared with join)

Lets the join page fail fast on a mistyped PIN and show the title before the nickname step. A miss counts toward the join-miss limit. Send the PIN as exactly 6 digits. Give each attempt 5 s; after a timeout, or a 502 or 504 without an error body, retry once at once with ?r=1.

Parameters

NameInTypeDescription
pin requiredpathstringPattern ^[0-9]{6}$. Example 482913.

Responses

200 A live session has this PIN.

200 response of GET /v1/pins/{pin}
FieldTypeDescription
status requiredlive
presentation_title requiredstring
phone_display requiredstringOne of full, answers_only.
accent_color requiredstring | nullPattern ^#[0-9A-Fa-f]{6}$.
participants_full requiredboolean
StatusError codes
404presentation_not_found, session_not_found, pin_not_found, question_not_found, key_not_found, grant_not_found, oauth_request_not_found, route_not_found. Ownership failures are always 404.
409session_ended, idempotency_conflict (same key, different body; or, retryable, the first request with that key is still in progress — retry the same request in a few seconds).
429rate_limited, with retry_after_s.
500internal_error.
503no_live_capacity or service_unavailable, with retry_after_s and Retry-After. Billing endpoints return service_unavailable when billing is not configured in the environment or Stripe is unreachable.

Join a session

POST /v1/pins/{pin}/join

Credential: None, or Participant token (optional body field on re-join) · Rate limit: 1,000/min (burst 1,000) per IP; 20 PIN misses/min per IP

Joins with {nickname}, or re-joins with {participant_token} (re-joins never count against the participant cap); the PIN is the path's. This is the only participant-cap check. Questions are not returned; the live session pushes the current question over the WebSocket. Body ≤ 16 KB. Send the PIN as exactly 6 digits. Give each attempt 5 s; after a timeout, or a 502 or 504 without an error body, retry once at once with ?r=1 and the same body. With the same join_id a retry returns the first attempt's participant and token instead of admitting the phone twice.

Parameters

NameInTypeDescription
pin requiredpathstringPattern ^[0-9]{6}$. Example 482913.

Request body

application/json

One of:

Variant 1

Request body of POST /v1/pins/{pin}/join: Variant 1
FieldTypeDescription
nickname requiredstring1–20 characters after trim and NFC normalization; no control characters. at least 1 chars.
join_idstringClient join nonce: a fresh random value of at least 128 bits per join (the join page sends 16 random bytes, base64url), reused unchanged on every retry of that join. The same join_id with the same nickname in the same session returns the same participant and token for 10 minutes; with another nickname, 409 idempotency_conflict. A secret: never log or share it. Pattern ^[A-Za-z0-9_-]{22,64}$.

Variant 2

Request body of POST /v1/pins/{pin}/join: Variant 2
FieldTypeDescription
participant_token requiredstringPattern ^ptk_[0-9A-Za-z]{32,}$.

Responses

200 Joined.

200 response of POST /v1/pins/{pin}/join
FieldTypeDescription
participant_id requiredstringPattern ^par_[0-9A-HJKMNP-TV-Z]{26}$.
participant_token requiredstringPattern ^ptk_[0-9A-Za-z]{32,}$.
nickname requiredstringThe accepted name, possibly with a suffix (Sam 2).
rejoined requiredboolean
session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
live_url requiredstring
presentation requiredobject
presentation.title requiredstring
presentation.phone_display requiredstringOne of full, answers_only.
presentation.accent_color requiredstring | nullPattern ^#[0-9A-Fa-f]{6}$.
session requiredobject
session.status requiredlive
session.participant_cap requiredintegerAt least 1.
StatusError codes
400invalid_json, validation_failed (see details), invalid_request.
401invalid_credentials: the re-join participant_token is unknown or expired (for example, a token stored by the browser from an earlier session). Join again with a nickname. The message is the participant_token variant from errors.json ("This participant token is unknown or has expired. Join again with a nickname.").
403wrong_credential_type, secret_key_in_browser, csrf_failed, recent_auth_required, session_full, nickname_rejected.
404presentation_not_found, session_not_found, pin_not_found, question_not_found, key_not_found, grant_not_found, oauth_request_not_found, route_not_found. Ownership failures are always 404.
409session_ended, idempotency_conflict (same key, different body; or, retryable, the first request with that key is still in progress — retry the same request in a few seconds).
413payload_too_large: body over the limit (256 KB; 16 KB for POST /v1/pins/{pin}/join).
429rate_limited, with retry_after_s.
500internal_error.
503no_live_capacity or service_unavailable, with retry_after_s and Retry-After. Billing endpoints return service_unavailable when billing is not configured in the environment or Stripe is unreachable.