sparka home

API reference

Sessions

Start, resume, inspect and end live sessions; read results.

List a presentation's sessions

GET /v1/presentations/{presentation_id}/sessions

Credential: Secret key (Authorization: Bearer spk_live_…)

Parameters

NameInTypeDescription
presentation_id requiredpathstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
limitqueryinteger1–100. Default 20.
cursorquerystringOpaque next_cursor from the previous page; valid 24 h.
statusquerystringOne of live, ended.

Responses

200 One page of sessions, newest first.

200 response of GET /v1/presentations/{presentation_id}/sessions
FieldTypeDescription
data requiredarray of object
data[].session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
data[].status requiredstringOne of live, ended.
data[].pin requiredstringPattern ^[0-9]{6}$. Example 482913.
data[].participant_count requiredintegerAt least 0.
data[].started_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
data[].ended_at requiredstring | nullFormat date-time.
data[].end_reason requiredstring | null
data[].counted requiredbooleanWhether this session consumed monthly quota (3 or more participants joined).
next_cursor requiredstring | null

Start or resume the live session

POST /v1/presentations/{presentation_id}/sessions

Credential: None: the presentation ID in the path is the capability, or Secret key (Authorization: Bearer spk_live_…) · Rate limit: 10/min per presentation (resumes count); 60 new sessions/hour per account

Returns the presentation's live session if one exists (200, resumed: true), else creates one (201, resumed: false). Idempotent unless restart: true, which ends the current live session first. The presentation ID in the path is the capability; the owner's secret key behaves the same. A resume never fails on quota.

Parameters

NameInTypeDescription
presentation_id requiredpathstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.

Request body

application/json (optional)

Request body of POST /v1/presentations/{presentation_id}/sessions
FieldTypeDescription
restartbooleanEnd the current live session (reason restarted) and create a new one. Default false.

Responses

200 Resumed the existing live session.

200 response of POST /v1/presentations/{presentation_id}/sessions
FieldTypeDescription
session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
presentation_version requiredintegerAt least 1.
status requiredlive
resumed requiredboolean
pin requiredstringPattern ^[0-9]{6}$. Example 482913.
join_url requiredstringFormat uri. Example https://sparka.io/482913.
join_url_display requiredstringExample sparka.io/482913.
qr_svg requiredstringSquare SVG encoding join_url; scales with CSS; dark modules #000; quiet zone included.
control_token requiredstringPattern ^ctl_[0-9A-Za-z]{32,}$.
live_url requiredstringwss://<origin>/live/<session_id>; use it unchanged. Clients add r=1 (?r=1) after two failed reconnects. Example wss://sparka.io/live/ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
participant_cap requiredintegerAt least 1.
current_question_id requiredstring | null
participant_count requiredintegerAt least 0.
started_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
expires_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
idle_timeout_s requiredintegerAt least 1.

201 Created a new live session.

201 response of POST /v1/presentations/{presentation_id}/sessions
FieldTypeDescription
session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
presentation_version requiredintegerAt least 1.
status requiredlive
resumed requiredboolean
pin requiredstringPattern ^[0-9]{6}$. Example 482913.
join_url requiredstringFormat uri. Example https://sparka.io/482913.
join_url_display requiredstringExample sparka.io/482913.
qr_svg requiredstringSquare SVG encoding join_url; scales with CSS; dark modules #000; quiet zone included.
control_token requiredstringPattern ^ctl_[0-9A-Za-z]{32,}$.
live_url requiredstringwss://<origin>/live/<session_id>; use it unchanged. Clients add r=1 (?r=1) after two failed reconnects. Example wss://sparka.io/live/ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
participant_cap requiredintegerAt least 1.
current_question_id requiredstring | null
participant_count requiredintegerAt least 0.
started_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
expires_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
idle_timeout_s requiredintegerAt least 1.
StatusError codes
400invalid_json, validation_failed (see details), invalid_request.
401missing_credentials, invalid_credentials, not_signed_in (/v1/web/*).
402quota_exceeded with limit, used, max, resets_at, upgrade_url.
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.
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.

Session status

GET /v1/sessions/{session_id}

Credential: Control token (Authorization: Bearer ctl_…), or Secret key (Authorization: Bearer spk_live_…)

State as of the last checkpoint (live truth comes over the WebSocket). A secret-key caller additionally gets live_url.

Parameters

NameInTypeDescription
session_id requiredpathstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.

Responses

200 Session.

200 response of GET /v1/sessions/{session_id}
FieldTypeDescription
session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
presentation_version requiredintegerAt least 1.
status requiredstringOne of live, ended.
pin requiredstringPattern ^[0-9]{6}$. Example 482913.
join_url requiredstringFormat uri.
participant_cap requiredintegerAt least 1.
participant_count requiredintegerAt least 0.
current_question_id requiredstring | null
questions requiredarray of object
questions[].id requiredstringPattern ^[a-z][a-z0-9_-]{0,31}$.
questions[].state requiredstringOne of idle, open, closed, revealed.
started_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
ended_at requiredstring | nullFormat date-time.
end_reason requiredstring | null
live_urlstringSecret-key callers only.

End a session

POST /v1/sessions/{session_id}/end

Credential: Control token (Authorization: Bearer ctl_…), or Secret key (Authorization: Bearer spk_live_…)

The single place a session ends. Idempotent: an already-ended session returns 200 with the same body. reason is accepted only with the control token (used by the presenter's deck); default end_reason is ended_by_presenter with a control token and ended_by_api with a secret key.

Parameters

NameInTypeDescription
session_id requiredpathstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.

Request body

application/json (optional)

Request body of POST /v1/sessions/{session_id}/end
FieldTypeDescription
reasonstringOne of ended_by_presenter, idle_timeout, max_duration.

Responses

200 Ended (or already ended).

200 response of POST /v1/sessions/{session_id}/end
FieldTypeDescription
session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
presentation_version requiredintegerAt least 1.
status requiredstringOne of live, ended.
pin requiredstringPattern ^[0-9]{6}$. Example 482913.
join_url requiredstringFormat uri.
participant_cap requiredintegerAt least 1.
participant_count requiredintegerAt least 0.
current_question_id requiredstring | null
questions requiredarray of object
questions[].id requiredstringPattern ^[a-z][a-z0-9_-]{0,31}$.
questions[].state requiredstringOne of idle, open, closed, revealed.
started_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
ended_at requiredstring | nullFormat date-time.
end_reason requiredstring | null
live_urlstringSecret-key callers only.

Session results

GET /v1/sessions/{session_id}/results

Credential: Secret key (Authorization: Bearer spk_live_…)

Available while live (complete: false, as of the last checkpoint) and after end (complete: true). leaderboard is empty when no question is scored; ties share a rank.

Parameters

NameInTypeDescription
session_id requiredpathstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
includequerystringresponses adds per-participant raw answers to each question. One of responses.

Responses

200 Results.

200 response of GET /v1/sessions/{session_id}/results
FieldTypeDescription
session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
presentation_version requiredintegerAt least 1.
status requiredstringOne of live, ended.
complete requiredboolean
started_at requiredstringRFC 3339 UTC to whole seconds (as are all timestamps in this document). Format date-time. Example 2026-10-11T18:04:05Z.
ended_at requiredstring | nullFormat date-time.
participant_count requiredintegerAt least 0.
questions requiredarray of object
questions[].id requiredstringPattern ^[a-z][a-z0-9_-]{0,31}$.
questions[].type requiredstring
questions[].promptstring
questions[].state requiredstringOne of idle, open, closed, revealed.
questions[].response_count requiredintegerAt least 0.
questions[].results requiredPollResults | QuizResults | WordCloudResults | nullType-specific; null while the question is idle.
questions[].responsesarray of objectOnly with include=responses.
questions[].responses[].participant_id requiredstringPattern ^par_[0-9A-HJKMNP-TV-Z]{26}$.
questions[].responses[].value requiredpoll answer value | quiz answer value | word_cloud answer valueThe answer value as sent on the WebSocket. Poll and quiz answers share the {option} shape, so a choice answer matches both of those branches (anyOf).
questions[].responses[].answer_ms requiredinteger | nullAt least 0.
questions[].responses[].points requiredinteger | nullAt least 0.
leaderboard requiredarray of object
leaderboard[].rank requiredintegerAt least 1.
leaderboard[].participant_id requiredstringPattern ^par_[0-9A-HJKMNP-TV-Z]{26}$.
leaderboard[].nickname requiredstring
leaderboard[].score requiredintegerAt least 0.
leaderboard[].correct_count requiredintegerAt least 0.
participants requiredarray of object
participants[].participant_id requiredstringPattern ^par_[0-9A-HJKMNP-TV-Z]{26}$.
participants[].nickname requiredstring
participants[].joined_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
participants[].score requiredintegerAt least 0.