sparka home

API reference

Presentations

Save, read, list and delete presentations (title, settings, questions).

List presentations

GET /v1/presentations

Credential: Secret key (Authorization: Bearer spk_live_…)

Newest first.

Parameters

NameInTypeDescription
limitqueryinteger1–100. Default 20.
cursorquerystringOpaque next_cursor from the previous page; valid 24 h.

Responses

200 One page of presentations.

200 response of GET /v1/presentations
FieldTypeDescription
data requiredarray of object
data[].presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
data[].title requiredstring
data[].version requiredintegerAt least 1.
data[].question_count requiredintegerAt least 1.
data[].live_session_id requiredstring | null
data[].created_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
data[].updated_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
next_cursor requiredstring | null

Create a presentation

POST /v1/presentations

Credential: Secret key (Authorization: Bearer spk_live_…) · Rate limit: 20 req/s per key, burst 40

Saves a new presentation. warnings never block a save.

Parameters

NameInTypeDescription
Idempotency-KeyheaderstringThe first response is stored 24 h per (account, key) and replayed verbatim. Same key with a different body → 409 idempotency_conflict. 1–64 chars.

Request body

application/json

Request body of POST /v1/presentations
FieldTypeDescription
title requiredstringPresentation title. 1–120 chars.
settingsSettingsOptional. Omitted fields take their defaults.
questions requiredarray of Question1-50 questions in suggested slide order. Each id must be unique. 1–50 items.
{
  "title": "Q3 All-Hands",
  "settings": {
    "phone_display": "full",
    "leaderboard_size": 5,
    "show_rank_on_phone": true
  },
  "questions": [
    {
      "id": "warmup",
      "type": "word_cloud",
      "prompt": "One word for this quarter?"
    },
    {
      "id": "priority",
      "type": "poll",
      "prompt": "What should we focus on in Q4?",
      "options": [
        "Performance",
        "New features",
        "Onboarding",
        "Docs"
      ]
    },
    {
      "id": "launch_year",
      "type": "quiz",
      "prompt": "What year did we ship 1.0?",
      "options": [
        "2021",
        "2022",
        "2023",
        "2024"
      ],
      "correct": 1,
      "explanation": "Version 1.0 shipped in March 2022.",
      "time_limit_s": 20
    }
  ]
}

Responses

201 Created.

201 response of POST /v1/presentations
FieldTypeDescription
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
version requiredintegerAt least 1.
title requiredstring
question_ids requiredarray of string
public_url requiredstringFormat uri.
sessions_url requiredstringFormat uri.
created_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
updated_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
live_sessionobjectPresent on replace when a session is live.
live_session.session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
live_session.uses_version requiredintegerAt least 1.
warnings requiredarray of object
warnings[].code requiredstringPresentation saves and POST /v1/validate: defaulted, long_prompt, few_options, speed_bonus_unused, ids_removed, live_session_uses_previous_version. GET /v1/account: payment_past_due (Pro stays active while Stripe retries) and secret_key_in_browser (a key was sent from a browser and refused). Clients must accept codes not listed here. One of defaulted, long_prompt, few_options, speed_bonus_unused, ids_removed, live_session_uses_previous_version, payment_past_due, secret_key_in_browser.
warnings[].pathstring
warnings[].message requiredstring
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.
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.

Read a presentation

GET /v1/presentations/{presentation_id}

Credential: None: the presentation ID in the path is the capability, or Secret key (Authorization: Bearer spk_live_…) · Rate limit: 120/min per IP (public view)

One path, two views. Without Authorization the presentation ID itself is the capability and the public view is returned (no secret fields such as correct, explanation; no owner metadata). With the owner's secret key the owner view is returned. A non-owner key gets 404 presentation_not_found. All defaults are materialized.

Parameters

NameInTypeDescription
presentation_id requiredpathstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
If-None-MatchheaderstringAn ETag from a previous response ("<version>").

Responses

200 The presentation. view tells which shape was returned.

One of:

PresentationPublicView

200 response of GET /v1/presentations/{presentation_id}: PresentationPublicView
FieldTypeDescription
view requiredpublic
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
version requiredintegerAt least 1.
title requiredstring
settings requiredSettingsPresentation-level settings. Every field is optional and has a default.
questions requiredarray of object
questions[].id requiredstringPattern ^[a-z][a-z0-9_-]{0,31}$.
questions[].type requiredstring
questions[].prompt requiredstring
updated_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.

PresentationOwnerView

200 response of GET /v1/presentations/{presentation_id}: PresentationOwnerView
FieldTypeDescription
view requiredowner
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
version requiredintegerAt least 1.
title requiredstring
settings requiredSettingsPresentation-level settings. Every field is optional and has a default.
questions requiredarray of Question
created_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
updated_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
live_session requiredobject | null
stats requiredobject
stats.sessions_total requiredintegerAt least 0.
stats.last_session_at requiredstring | nullFormat date-time.

304 Not modified (If-None-Match matched the current version).

Replace a presentation

PUT /v1/presentations/{presentation_id}

Credential: Secret key (Authorization: Bearer spk_live_…)

Full replace (no partial patch; omitted settings fields reset to defaults). Increments version. A live session keeps the snapshot it started with; the response then includes live_session and warning live_session_uses_previous_version. Keep question IDs stable so decks keep working.

Parameters

NameInTypeDescription
presentation_id requiredpathstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
If-Matchheaderstring"<version>"; stale → 412 version_conflict.

Request body

application/json

Request body of PUT /v1/presentations/{presentation_id}
FieldTypeDescription
title requiredstringPresentation title. 1–120 chars.
settingsSettingsOptional. Omitted fields take their defaults.
questions requiredarray of Question1-50 questions in suggested slide order. Each id must be unique. 1–50 items.

Responses

200 Replaced.

200 response of PUT /v1/presentations/{presentation_id}
FieldTypeDescription
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
version requiredintegerAt least 1.
title requiredstring
question_ids requiredarray of string
public_url requiredstringFormat uri.
sessions_url requiredstringFormat uri.
created_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
updated_at requiredstringFormat date-time. Example 2026-10-11T18:04:05Z.
live_sessionobjectPresent on replace when a session is live.
live_session.session_id requiredstringPattern ^ses_[0-9A-HJKMNP-TV-Z]{26}$. Example ses_01K7Q4A1B2C3D4E5F6G7H8J9K0.
live_session.uses_version requiredintegerAt least 1.
warnings requiredarray of object
warnings[].code requiredstringPresentation saves and POST /v1/validate: defaulted, long_prompt, few_options, speed_bonus_unused, ids_removed, live_session_uses_previous_version. GET /v1/account: payment_past_due (Pro stays active while Stripe retries) and secret_key_in_browser (a key was sent from a browser and refused). Clients must accept codes not listed here. One of defaulted, long_prompt, few_options, speed_bonus_unused, ids_removed, live_session_uses_previous_version, payment_past_due, secret_key_in_browser.
warnings[].pathstring
warnings[].message requiredstring
StatusError codes
400invalid_json, validation_failed (see details), invalid_request.
401missing_credentials, invalid_credentials, not_signed_in (/v1/web/*).
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.
412version_conflict: the If-Match version is stale.
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.

Delete a presentation

DELETE /v1/presentations/{presentation_id}

Credential: Secret key (Authorization: Bearer spk_live_…)

Ends the live session if any, then deletes the presentation. The ID is never reused. Past sessions and their results stay readable. A repeat delete returns 404.

Parameters

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

Responses

200 Deleted.

200 response of DELETE /v1/presentations/{presentation_id}
FieldTypeDescription
deleted requiredtrue
presentation_id requiredstringPattern ^pr_[0-9A-HJKMNP-TV-Z]{26}$. Example pr_01K7Q3W8Z9G5H2M4N6P8R0S2T4.
ended_session_id requiredstring | null

Validate without saving

POST /v1/validate

Credential: None · Rate limit: 60/min per IP

Runs the save-time validator on a full presentation or on {"question": {…}}. Nothing is stored. Always 200 when the body is parseable JSON; problems are reported in errors.

Request body

application/json

One of:

Presentation

Request body of POST /v1/validate: Presentation
FieldTypeDescription
title requiredstringPresentation title. 1–120 chars.
settingsSettingsOptional. Omitted fields take their defaults.
questions requiredarray of Question1-50 questions in suggested slide order. Each id must be unique. 1–50 items.

Variant 2

Request body of POST /v1/validate: Variant 2
FieldTypeDescription
question requiredQuestionOne question of any available type, selected by its type field.

Responses

200 Validation result.

200 response of POST /v1/validate
FieldTypeDescription
valid requiredboolean
errors requiredarray of object
errors[].path requiredstringExample questions[2].correct.
errors[].question_idstringPattern ^[a-z][a-z0-9_-]{0,31}$.
errors[].code requiredstringExample out_of_range.
errors[].message requiredstring
warnings requiredarray of object
warnings[].code requiredstringPresentation saves and POST /v1/validate: defaulted, long_prompt, few_options, speed_bonus_unused, ids_removed, live_session_uses_previous_version. GET /v1/account: payment_past_due (Pro stays active while Stripe retries) and secret_key_in_browser (a key was sent from a browser and refused). Clients must accept codes not listed here. One of defaulted, long_prompt, few_options, speed_bonus_unused, ids_removed, live_session_uses_previous_version, payment_past_due, secret_key_in_browser.
warnings[].pathstring
warnings[].message requiredstring
normalized requiredobject | nullWhen valid, the input with all defaults filled in (what would be stored); otherwise null.
StatusError codes
400invalid_json, validation_failed (see details), invalid_request.
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.

List question types and their schemas

GET /v1/question-types

Credential: None

Generated from the validator, so it cannot drift. Includes planned types with status: planned.

Responses

200 Question types.

200 response of GET /v1/question-types
FieldTypeDescription
id_pattern required^[a-z][a-z0-9_-]{0,31}$
presentation_schema requiredobjectJSON Schema (draft 2020-12) of the presentation object.
types requiredarray of object
types[].type requiredstringExample poll.
types[].since requiredstringExample 2026-10.
types[].status requiredstringOne of available, planned.
types[].description requiredstring
types[].schema requiredobject | nullJSON Schema (draft 2020-12) of the owner shape; null for planned types without a schema yet.
types[].secret_fields requiredarray of string
StatusError codes
429rate_limited, with retry_after_s.
500internal_error.