Sessions
Start, resume, inspect and end live sessions; read results.
- GET
/v1/presentations/{presentation_id}/sessions - POST
/v1/presentations/{presentation_id}/sessions - GET
/v1/sessions/{session_id} - POST
/v1/sessions/{session_id}/end - GET
/v1/sessions/{session_id}/results
List a presentation's sessions
GET /v1/presentations/{presentation_id}/sessions
Credential: Secret key (Authorization: Bearer spk_live_…)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
presentation_id required | path | string | Pattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4. |
limit | query | integer | 1–100. Default 20. |
cursor | query | string | Opaque next_cursor from the previous page; valid 24 h. |
status | query | string | One of live, ended. |
Responses
200 One page of sessions, newest first.
| Field | Type | Description |
|---|---|---|
data required | array of object | |
data[].session_id required | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
data[].status required | string | One of live, ended. |
data[].pin required | string | Pattern ^[0-9]{6}$. Example 482913. |
data[].participant_count required | integer | At least 0. |
data[].started_at required | string | Format date-time. Example 2026-10-11T18:04:05Z. |
data[].ended_at required | string | null | Format date-time. |
data[].end_reason required | string | null | |
data[].counted required | boolean | Whether this session consumed monthly quota (3 or more participants joined). |
next_cursor required | string | null |
| Status | Error codes |
|---|---|
| 400 | invalid_json, validation_failed (see details), invalid_request. |
| 401 | missing_credentials, invalid_credentials, not_signed_in (/v1/web/*). |
| 403 | wrong_credential_type, secret_key_in_browser, csrf_failed, recent_auth_required, session_full, nickname_rejected. |
| 404 | presentation_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. |
| 429 | rate_limited, with retry_after_s. |
| 500 | internal_error. |
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
| Name | In | Type | Description |
|---|---|---|---|
presentation_id required | path | string | Pattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4. |
Request body
application/json (optional)
| Field | Type | Description |
|---|---|---|
restart | boolean | End the current live session (reason restarted) and create a new one. Default false. |
Responses
200 Resumed the existing live session.
| Field | Type | Description |
|---|---|---|
session_id required | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
presentation_id required | string | Pattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4. |
presentation_version required | integer | At least 1. |
status required | live | |
resumed required | boolean | |
pin required | string | Pattern ^[0-9]{6}$. Example 482913. |
join_url required | string | Format uri. Example https://sparka.io/482913. |
join_url_display required | string | Example sparka.io/482913. |
qr_svg required | string | Square SVG encoding join_url; scales with CSS; dark modules #000; quiet zone included. |
control_token required | string | Pattern ^ctl_[0-9A-Za-z]{32,}$. |
live_url required | string | wss://<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 required | integer | At least 1. |
current_question_id required | string | null | |
participant_count required | integer | At least 0. |
started_at required | string | Format date-time. Example 2026-10-11T18:04:05Z. |
expires_at required | string | Format date-time. Example 2026-10-11T18:04:05Z. |
idle_timeout_s required | integer | At least 1. |
201 Created a new live session.
| Field | Type | Description |
|---|---|---|
session_id required | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
presentation_id required | string | Pattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4. |
presentation_version required | integer | At least 1. |
status required | live | |
resumed required | boolean | |
pin required | string | Pattern ^[0-9]{6}$. Example 482913. |
join_url required | string | Format uri. Example https://sparka.io/482913. |
join_url_display required | string | Example sparka.io/482913. |
qr_svg required | string | Square SVG encoding join_url; scales with CSS; dark modules #000; quiet zone included. |
control_token required | string | Pattern ^ctl_[0-9A-Za-z]{32,}$. |
live_url required | string | wss://<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 required | integer | At least 1. |
current_question_id required | string | null | |
participant_count required | integer | At least 0. |
started_at required | string | Format date-time. Example 2026-10-11T18:04:05Z. |
expires_at required | string | Format date-time. Example 2026-10-11T18:04:05Z. |
idle_timeout_s required | integer | At least 1. |
| Status | Error codes |
|---|---|
| 400 | invalid_json, validation_failed (see details), invalid_request. |
| 401 | missing_credentials, invalid_credentials, not_signed_in (/v1/web/*). |
| 402 | quota_exceeded with limit, used, max, resets_at, upgrade_url. |
| 403 | wrong_credential_type, secret_key_in_browser, csrf_failed, recent_auth_required, session_full, nickname_rejected. |
| 404 | presentation_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. |
| 429 | rate_limited, with retry_after_s. |
| 500 | internal_error. |
| 503 | no_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
| Name | In | Type | Description |
|---|---|---|---|
session_id required | path | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
Responses
200 Session.
| Field | Type | Description |
|---|---|---|
session_id required | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
presentation_id required | string | Pattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4. |
presentation_version required | integer | At least 1. |
status required | string | One of live, ended. |
pin required | string | Pattern ^[0-9]{6}$. Example 482913. |
join_url required | string | Format uri. |
participant_cap required | integer | At least 1. |
participant_count required | integer | At least 0. |
current_question_id required | string | null | |
questions required | array of object | |
questions[].id required | string | Pattern ^[a-z][a-z0-9_-]{0,31}$. |
questions[].state required | string | One of idle, open, closed, revealed. |
started_at required | string | Format date-time. Example 2026-10-11T18:04:05Z. |
ended_at required | string | null | Format date-time. |
end_reason required | string | null | |
live_url | string | Secret-key callers only. |
| Status | Error codes |
|---|---|
| 401 | missing_credentials, invalid_credentials, not_signed_in (/v1/web/*). |
| 403 | wrong_credential_type, secret_key_in_browser, csrf_failed, recent_auth_required, session_full, nickname_rejected. |
| 404 | presentation_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. |
| 429 | rate_limited, with retry_after_s. |
| 500 | internal_error. |
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
| Name | In | Type | Description |
|---|---|---|---|
session_id required | path | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
Request body
application/json (optional)
| Field | Type | Description |
|---|---|---|
reason | string | One of ended_by_presenter, idle_timeout, max_duration. |
Responses
200 Ended (or already ended).
| Field | Type | Description |
|---|---|---|
session_id required | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
presentation_id required | string | Pattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4. |
presentation_version required | integer | At least 1. |
status required | string | One of live, ended. |
pin required | string | Pattern ^[0-9]{6}$. Example 482913. |
join_url required | string | Format uri. |
participant_cap required | integer | At least 1. |
participant_count required | integer | At least 0. |
current_question_id required | string | null | |
questions required | array of object | |
questions[].id required | string | Pattern ^[a-z][a-z0-9_-]{0,31}$. |
questions[].state required | string | One of idle, open, closed, revealed. |
started_at required | string | Format date-time. Example 2026-10-11T18:04:05Z. |
ended_at required | string | null | Format date-time. |
end_reason required | string | null | |
live_url | string | Secret-key callers only. |
| Status | Error codes |
|---|---|
| 400 | invalid_json, validation_failed (see details), invalid_request. |
| 401 | missing_credentials, invalid_credentials, not_signed_in (/v1/web/*). |
| 403 | wrong_credential_type, secret_key_in_browser, csrf_failed, recent_auth_required, session_full, nickname_rejected. |
| 404 | presentation_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. |
| 429 | rate_limited, with retry_after_s. |
| 500 | internal_error. |
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
| Name | In | Type | Description |
|---|---|---|---|
session_id required | path | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
include | query | string | responses adds per-participant raw answers to each question. One of responses. |
Responses
200 Results.
| Field | Type | Description |
|---|---|---|
session_id required | string | Pattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0. |
presentation_id required | string | Pattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4. |
presentation_version required | integer | At least 1. |
status required | string | One of live, ended. |
complete required | boolean | |
started_at required | string | RFC 3339 UTC to whole seconds (as are all timestamps in this document). Format date-time. Example 2026-10-11T18:04:05Z. |
ended_at required | string | null | Format date-time. |
participant_count required | integer | At least 0. |
questions required | array of object | |
questions[].id required | string | Pattern ^[a-z][a-z0-9_-]{0,31}$. |
questions[].type required | string | |
questions[].prompt | string | |
questions[].state required | string | One of idle, open, closed, revealed. |
questions[].response_count required | integer | At least 0. |
questions[].results required | PollResults | QuizResults | WordCloudResults | null | Type-specific; null while the question is idle. |
questions[].responses | array of object | Only with include=responses. |
questions[].responses[].participant_id required | string | Pattern ^par_[0-9A-HJKMNP-TV-Z]{26}$. |
questions[].responses[].value required | poll answer value | quiz answer value | word_cloud answer value | The 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 required | integer | null | At least 0. |
questions[].responses[].points required | integer | null | At least 0. |
leaderboard required | array of object | |
leaderboard[].rank required | integer | At least 1. |
leaderboard[].participant_id required | string | Pattern ^par_[0-9A-HJKMNP-TV-Z]{26}$. |
leaderboard[].nickname required | string | |
leaderboard[].score required | integer | At least 0. |
leaderboard[].correct_count required | integer | At least 0. |
participants required | array of object | |
participants[].participant_id required | string | Pattern ^par_[0-9A-HJKMNP-TV-Z]{26}$. |
participants[].nickname required | string | |
participants[].joined_at required | string | Format date-time. Example 2026-10-11T18:04:05Z. |
participants[].score required | integer | At least 0. |
| Status | Error codes |
|---|---|
| 400 | invalid_json, validation_failed (see details), invalid_request. |
| 401 | missing_credentials, invalid_credentials, not_signed_in (/v1/web/*). |
| 403 | wrong_credential_type, secret_key_in_browser, csrf_failed, recent_auth_required, session_full, nickname_rejected. |
| 404 | presentation_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. |
| 429 | rate_limited, with retry_after_s. |
| 500 | internal_error. |