We use PostHog analytics cookies on this marketing site to understand how visitors use sparkvox.io - only if you accept. The app at app.sparkvox.io does not use marketing analytics. Cookie Policy.

Guides

Concepts, standards, and workflows for building on the SparkVox API.

API Standards

These conventions apply to all /api/v1/* endpoints unless an endpoint page notes an exception. Following them keeps integrations predictable and secure.

Base URL and versioning

  • Base URL: https://app.sparkvox.io/api/v1
  • Version is in the path (v1). Breaking changes will ship under a new version prefix.
  • Use HTTPS only. HTTP is not supported for API calls.

Requests

  • Send JSON bodies with Content-Type: application/json
  • Pass credentials only in the Authorization header: Bearer sk_sparkvox_<key>
  • Do not put API keys in query strings or request bodies
  • Use UUIDs returned by the API for project, post, and webhook IDs

API keys are not JWTs. Do not send sk_sparkvox_ tokens to /api/developer/* or Supabase auth endpoints. Authentication for details.

Responses

  • Successful responses return JSON objects (not bare arrays at the top level)
  • Errors return JSON with a single error string field (see Error handling)
  • Timestamps use ISO 8601 UTC (e.g. 2026-05-01T10:00:00Z)
  • Money fields such as processing_cost_cents are integers in cents

HTTP methods and status codes

PatternTypical status
GET resource or list200 OK
POST create (sync)201 Created
POST create (async project)202 Accepted
POST async job (e.g. generate image)202 Accepted
PATCH update200 OK
DELETE200 OK with { deleted: true }

Pagination and filtering

List endpoints use limit and offset query parameters. Defaults and maximums are documented per endpoint (projects default limit 50, max 100). Post lists support status filters.

Public endpoints

GET /api/url-metadata does not require an API key. All /api/v1/* routes require a valid key.

Outbound webhooks (your server)

  • Register HTTPS URLs only
  • Expect Content-Type: application/json and verify X-SparkVox-Signature
  • Respond with 2xx quickly; v1 does not retry failed deliveries

Not supported in v1

  • Idempotency-Key headers
  • Batch or bulk endpoints
  • GraphQL or XML payloads
  • API key scopes (one key has full v1 access for the account)

Error Handling

When a request fails, the API returns an HTTP status code and a JSON body. Integrations should branch on status first, then read the error message for logging or user-facing copy.

Error response shape

All v1 error bodies use the same structure:

json
{ "error": "Human-readable description of what went wrong." }

There is no machine-readable error code field in v1. Match on HTTP status and parse the error string if you need specific handling.

HTTP status codes

StatusWhen it happensWhat to do
400Invalid JSON, missing field, bad enum, unsupported platform, integration not connectedFix the request body or connect the provider in the app
401Missing Authorization header, malformed key, or revoked/unknown keyCheck Bearer sk_sparkvox_... and create a new key if needed
402Insufficient credits when creating a projectTop up credits in the SparkVox app; listen for project.failed with insufficient_credits
403Resource exists but belongs to another userDo not retry; verify you are using the correct project/post ID
404Project, post, or webhook not foundVerify the ID; resource may have been deleted
405HTTP method not allowed on this pathSee endpoint reference for supported methods
429More than 200 requests in 24h for this API keyBackoff until the rate window resets; see Rate Limits
500Unexpected server or pipeline failureRetry with exponential backoff; contact support if persistent

Example error responses

http
HTTP/1.1 401 Unauthorized
Content-Type: application/json

{ "error": "Invalid API key." }
http
HTTP/1.1 400 Bad Request
Content-Type: application/json

{ "error": "source_type must be 'url' or 'transcript'. File upload is not supported in v1." }
http
HTTP/1.1 402 Payment Required
Content-Type: application/json

{ "error": "Insufficient credits." }

Rate limit errors (429)

Each API key allows 200 requests per rolling 24-hour window. When exceeded, the response is 429 with an error message that may include the seconds until reset. Some routes also send a Retry-After header. Prefer exponential backoff rather than hammering the API.

Full rate limit policy. Rate Limits for details.

Validation and 400 errors

Common 400 messages include Invalid JSON body., field is required., Invalid status, provider not connected, and generated_content is required when scheduling. Endpoint reference pages list field-level rules; the API does not return structured field errors in v1.

Async failures (projects and images)

Project ingest and generation run asynchronously. Failures after a 202 response are surfaced via:

  • project.failed webhook with data.error (see below)
  • GET /projects/:id when status is failed and error_message is set
  • 402 on POST /projects only when billing fails before the pipeline starts

project.failed data.error values

errorMeaning
insufficient_creditsAccount could not cover processing cost
billing_errorBilling system error during charge
(string)Pipeline failure message (truncated), e.g. transcription or generation errors

For post images, poll image_url on the post: __generating__ while running, __error__ on failure, or a normal URL when ready.

Webhook delivery and signature verification. Events & signatures for details.

Retry guidance

StatusRetry?
400, 401, 403, 404, 405No - fix the client request
402No - add credits first
429Yes - after Retry-After or backoff
500Yes - limited retries with backoff
202 AcceptedPoll or wait for webhook - do not resubmit the same project unless intentional

Data Model

v1 exposes projects and posts as REST resources. Moments exist in the product pipeline but are not directly addressable via the API - you interact with them through posts (moment_id on each post).

Relationships

text
Account (API key owner)
  └── Project (1 ingest job)
        └── Moment (extracted insight, internal)
              └── Post (1 LinkedIn draft per moment)

Project

FieldTypeNotes
iduuidProject identifier
titlestringDisplay name you provide on create
statusenumSee project statuses below
source_typeurl | transcriptupload not supported via API in v1
platformsstring[]Only linkedin is used today
duration_secondsintegerBilling length; required on create
processing_cost_centsinteger | nullCharged credits in cents
error_messagestring | nullSet when status is failed
created_atISO 8601Creation timestamp

Project statuses

text
pending → transcribing → extracting → generating → ready | failed

Poll GET /projects/:id or use webhooks. Do not assume fixed timing between stages.

Post

FieldTypeNotes
iduuidPost identifier
moment_iduuidSource moment (read-only)
platformstringlinkedin in v1
generated_contentstringMain post body
first_commentstring | nullHashtags, CTA, or extra text
statusenumpending, approved, or discarded
publish_toolstring | nulllinkedin, buffer, or publer after scheduling
image_urlstring | nullSee Media guide for sentinel values
created_atISO 8601Creation timestamp

Post statuses

  • pending - draft, not yet approved for publishing
  • approved - approved in SparkVox; may also be scheduled externally if provider was set on PATCH
  • discarded - rejected draft

Webhook event shape

All events include event, data, and timestamp (Unix ms). project.ready includes project_id, title, post_count, and posts_url.

HTTP status codes, error JSON shape, and retry guidance are documented in Error handling. Error handling for details.

Projects

A project represents one piece of long-form content you want repurposed into LinkedIn posts. The API starts processing immediately and returns 202 - you track progress via status fields or webhooks.

Source types

source_typeRequired fieldsNotes
urlsource_url, duration_secondsPublic audio/video URL; YouTube supported
transcripttranscript, duration_secondsFull transcript text; skips transcription

File upload (source_type: upload) is not available via the API in v1. Use url or transcript, or create uploads in the SparkVox app.

Perspective

Controls how the transcript is interpreted when writing posts:

ValueUse when
show_brandHost-led content; default for url and forced for transcript
thought_leaderYou are a guest on someone else's show
expert_guestGuest appearance; same speaker-selection behavior as thought_leader

Billing

duration_seconds is required on create and drives credit usage (per minute, rounded up). Moment extraction and post generation are included. Failed projects may still consume credits depending on pipeline stage.

Tracking progress

  • Webhook (recommended): register for project.ready and project.failed
  • Polling: GET /projects/:id until status is ready or failed
  • List: GET /projects with limit and offset for dashboards

Deleting projects

DELETE /projects/:id permanently removes the project, its moments, posts, and raw audio in storage when present. This cannot be undone.

Endpoint reference for create, list, get, and delete. Create project for details.

Posts

Posts are LinkedIn drafts generated from extracted moments. v1 produces one long-form post per moment (up to roughly 15 per project, depending on content). Each post includes generated_content and an optional first_comment.

Lifecycle

  1. Posts are created with status pending when generation completes.
  2. You may edit generated_content, first_comment, and image_url via PATCH.
  3. Set status to approved to mark ready, or discarded to reject.
  4. Optionally pass provider on PATCH to schedule to LinkedIn, Buffer, or Publer.

Listing and filtering

GET /projects/:id/posts returns all posts for a project. Use ?status=pending|approved|discarded to filter. GET /projects/:id/posts/:postId fetches one post (useful when polling image generation).

Scheduling and publish_tool

PATCH without provider only updates SparkVox state. PATCH with provider (linkedin, buffer, or publer) requires that integration to be connected in the app. The response may include scheduled_at and job_id. publish_tool on the post reflects where it was sent.

List connected tools before scheduling. List integrations for details.

Moments

moment_id links a post to its source insight. You cannot create or delete moments via the API. If a moment fails generation, there may be no post for it.

Post formats (v1 limits)

v1 does not expose post_type or carousel_slides. All posts are single long-form LinkedIn text. Future API versions may add format types when the product ships Phase 10.

Media

Post images are optional. v1 supports two paths: SparkVox generates an AI image for you, or you upload your own file to SparkVox media storage. Source audio/video upload is a separate concern (project source_type) and is not covered here.

image_url states

ValueMeaning
nullNo image attached
__generating__AI generation in progress
__error__Generation failed
https://...Ready image (SparkVox R2 or legacy Supabase public URL)

Poll GET /projects/:id/posts/:postId after starting generation. PATCH accepts a normal https URL when setting image_url manually.

AI generation

  1. POST .../posts/:postId/generate-image (optional post_content override)
  2. Receive 202 { accepted: true }
  3. Poll GET post until image_url is a URL or __error__
  4. Approve or schedule as usual; image publishes with the post when provider is set

Generation matches in-app Generate image. Images are stored on SparkVox media (Cloudflare R2). Included in project processing credits for sprout-tree usage in the app; API generation uses the same pipeline.

Full endpoint reference. Generate image for details.

Custom upload

  1. POST .../posts/:postId/upload-url with filename (and optional mime_type, file_size_bytes)
  2. PUT file bytes to upload_url with the returned content_type
  3. PATCH post with image_url set to public_url from the upload response

Supported formats: JPG, PNG, GIF, WebP. Max 50 MB. public_url is served from media.sparkvox.io.

Full endpoint reference. Image upload URL for details.

Generate vs upload

ApproachBest for
GenerateOn-brand AI visuals from post copy; no asset prep
UploadBrand photography, screenshots, or designed creatives you already have

Source content (not post images)

Projects ingest audio/video via public URL or raw transcript text. Direct file upload for project source is not available on the API in v1. Raw audio from URL projects is stored temporarily during processing and removed after transcription when applicable.