heydecks

API

Jobs

GET /v1/jobs/{id} — poll an async generation job.

#GET /v1/jobs/{id}

Returns the current state of a job created by POST /v1/generate. Poll at a 2-second interval until the status reaches a terminal state.

Base URL: https://heydecks.com

#Job statuses

StatusMeaning
queuedJob is waiting to start.
runningGeneration is in progress.
succeededDeck created. deck object is present in the response.
failedGeneration failed. error object explains the cause. Credits were refunded.

#Request example

Shell
curl https://heydecks.com/v1/jobs/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer hd_live_…"

#Response — succeeded

NameTypeDescription
idstringThe job UUID.
statusstring"succeeded"
kindstringAlways "generate" for jobs created via POST /v1/generate.
created_atstringISO 8601 timestamp when the job was created.
updated_atstringISO 8601 timestamp of the last status change.
deckobjectPresent only when status is "succeeded". See deck fields below.
deck.idstringThe new deck id. Use this with POST /v1/decks/{id}/publish.
deck.urlstringDraft URL at https://heydecks.com/d/{slug}. Not publicly accessible until published.
deck.pdf_urlstringA ready-to-download PDF link, https://heydecks.com/s/{code}. Minted when the job succeeds.
deck.pptx_urlstring | nullThe same for PPTX. null means PPTX export isn't on the account's plan — it is not an error, and the job succeeded.
deck.export_links_expire_atstringISO 8601 expiry shared by both links. Read the expiry from this field rather than assuming a fixed window.
JSON
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "succeeded",
  "kind": "generate",
  "created_at": "2026-06-12T10:00:00.000Z",
  "updated_at": "2026-06-12T10:00:28.000Z",
  "deck": {
    "id": "deck_abc123",
    "url": "https://heydecks.com/d/q3-board-update",
    "pdf_url": "https://heydecks.com/s/k3mq7rbn2xa",
    "pptx_url": null,
    "export_links_expire_at": "2026-06-13T10:00:28.000Z"
  }
}

#Exports

The links on a finished job need no auth header and work while the deck is still a draft. They are the fast path, not the whole story:

  • They expire at export_links_expire_at. Read a job after that and its links are dead (GET /s/{code} answers 410 Gone).
  • They are always a colour-logo, editable export. To choose the logo treatment, force picture-fidelity PPTX, or set your own lifetime, mint your own.
  • pptx_url is null on plans without PPTX export.

Minting is not rendering. The job writes the links but runs no browser, so the first fetch is where the render happens and it is the slow one. A PDF over 25 slides answers 202 with Retry-After and needs polling; anything shorter, and any PPTX, blocks until the file is ready. Either way a client that saves a 202 body saves an HTML holding page, not a PDF. The full rules and a poll loop are under Following the link.

The link is not a snapshot. It resolves the deck when it is fetched, so edits made between the job succeeding and the download are included.

To get a link on demand — any format, any time, draft or published — call POST /v1/decks/{id}/exports with the deck.id from the job. It is synchronous and free on every plan.

Nothing about exporting requires the dashboard.

#Response — failed

NameTypeDescription
errorobjectPresent only when status is "failed". See error fields below.
error.codestringMachine-readable failure reason, e.g. "generation_failed".
error.messagestringHuman-readable description of the failure.
JSON
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "failed",
  "kind": "generate",
  "created_at": "2026-06-12T10:00:00.000Z",
  "updated_at": "2026-06-12T10:00:05.000Z",
  "error": {
    "code": "generation_failed",
    "message": "…"
  }
}

A 404 not_found is returned if the job id doesn't exist or belongs to a different user.