Posts

List, update, and manage images on generated LinkedIn drafts.

GET/api/v1/projects/:id/posts

List Posts

List generated posts for a project.

Query parameters

  • status - filter by post status: pending, approved, or discarded

Response 200

json
{
  "posts": [
    {
      "id": "uuid",
      "moment_id": "uuid",
      "platform": "linkedin",
      "generated_content": "Full LinkedIn post text...",
      "first_comment": "Hashtags and CTA...",
      "status": "pending",
      "publish_tool": null,
      "image_url": null,
      "created_at": "2026-05-01T10:05:00Z"
    }
  ]
}

v1 post model: One long-form LinkedIn post per extracted moment. post_type and carousel_slides are not part of v1.

image_url values while generating: __generating__ (in progress), __error__ (failed), a normal https:// URL when ready, or null.

GET/api/v1/projects/:id/posts/:postId

Get Post

Fetch a single post. Same fields as the list endpoint.

Response 200

json
{
  "post": {
    "id": "uuid",
    "moment_id": "uuid",
    "platform": "linkedin",
    "generated_content": "...",
    "first_comment": "...",
    "status": "pending",
    "publish_tool": null,
    "image_url": null,
    "created_at": "..."
  }
}
PATCH/api/v1/projects/:id/posts/:postId

Update Post

Update a post's status and content. Optionally publish or schedule to a connected tool in the same call.

Request body (all fields optional)

json
{
  "status": "approved",
  "generated_content": "Revised content...",
  "first_comment": "Updated hashtags...",
  "image_url": "https://...",
  "provider": "linkedin",
  "account_id": "optional-provider-account-id"
}
FieldValues
statuspending, approved, discarded
generated_contentString
first_commentString or null
image_urlValid https:// URL (SparkVox R2 or legacy Supabase public storage) or null
providerlinkedin - triggers publishing (see below)
account_idProvider-specific account ID; omit to use the default account

Publishing behavior

  • Without provider - sets status: approved in SparkVox only. No post is sent anywhere.
  • With provider - you must include status: approved in the same request body. Schedules the post to the connected tool. Requires a connection in Settings → Integrations. generated_content must be present when scheduling.

Scheduling with provider only runs when status is approved in the same PATCH. Sending provider alone without status does not publish.

Error responses

  • 400 - invalid status, unknown provider, integration not connected, missing generated_content when scheduling
  • 403 - post not owned by API key user
  • 404 - project or post not found
  • 502 - provider scheduling failed

Response 200

Without provider: returns the updated post object.

With provider: returns the updated post object plus scheduling details:

json
{
  "id": "uuid",
  "status": "approved",
  "publish_tool": "linkedin",
  "generated_content": "...",
  "scheduled_at": "2026-06-02T09:00:00.000Z",
  "job_id": "provider-job-id"
}
POST/api/v1/projects/:id/posts/:postId/generate-image

Generate Image

Start AI image generation for a post (async). Matches in-app Generate image: Claude Sonnet drafts a hook-anchored visual brief, then fal.ai Flux renders a 16:9 image (heuristic fallback if the brief step fails).

Request body (optional)

json
{ "post_content": "Override text to illustrate (defaults to saved generated_content)" }

Response 202

json
{ "accepted": true }

Poll GET.../posts/:postId until image_url is a normal URL (or __error__ on failure). While running, image_url is __generating__.

POST/api/v1/projects/:id/posts/:postId/upload-url

Image Upload URL

Get a presigned URL to upload your own media (JPG, PNG, GIF, WebP - max 50 MB; MP4 - max 200 MB). After uploading with PUT to upload_url, set the post media via PATCH with image_url set to public_url.

Request body

json
{
  "filename": "hero.png",
  "mime_type": "image/png",
  "file_size_bytes": 1048576
}
FieldRequiredNotes
filenameYes
mime_typeNovideo/mp4 selects the 200 MB limit
file_size_bytesNo

The web app uses same-origin proxy uploads for browsers that cannot PUT directly to R2. API clients can use the presigned upload_url flow.

Response 200

json
{
  "upload_url": "https://...",
  "storage_path": "post-images/...",
  "content_type": "image/png",
  "public_url": "https://media.sparkvox.io/..."
}