heydecks

API

Slides and editing

Add, change, reorder, and delete the slides of an existing deck, or edit a deck with AI.

Base URL: https://heydecks.com

All endpoints require Authorization: Bearer hd_live_…. Slide editing is available on every plan, including free; AI modes spend credits.

A deck is an ordered list of slides. Each slide has an id, a slide_type (the slide type, e.g. title, stats, codeBlock), an order, a content object whose shape matches that template, and optional notes (speaker notes).

There are two ways to change a deck:

  • Direct: you supply the exact content. Best when you already know the slide shape.
  • AI: you supply a natural-language prompt or instruction and heydecks fills in the content. Best when you want the model to do the work. AI calls cost 10 credits each.

#GET /v1/slide-templates

The slide catalog: every slide_type you can send, and the content shape each one expects. Read this before writing slides directly.

Discovery is two cheap steps, because there are 98 templates and a full field schema is large:

  1. Browse — call with no query string for a compact pick-list of every template.
  2. Fetch schemas — call again with ?keys=… for the full field schema of only the templates you picked.
NameTypeDescription
keysstringComma-separated slide types, e.g. ?keys=title,kpi,cta. Omit for the pick-list. Maximum 12 per request; duplicates don't count toward it.
verbosestring?verbose=true adds each template's placeholder defaults to a detail response.

#Browse

Shell
curl https://heydecks.com/v1/slide-templates \
  -H "Authorization: Bearer hd_live_…"

200 Response:

JSON
{
  "count": 98,
  "templates": [
    {
      "key": "title",
      "name": "Title cover",
      "group": "Covers & openers",
      "category": "Open",
      "when_to_use": "The opening cover: deck title, who it's for, and the date. Always slide one."
    }
  ]
}

group is the browsing group the dashboard picker uses ("Charts & data", "Stats & metrics" …). category is the coarser narrative arc: Open, Argue, Show, or Close.

#Fetch schemas

Shell
curl "https://heydecks.com/v1/slide-templates?keys=title,stats" \
  -H "Authorization: Bearer hd_live_…"

200 Response — the same fields plus description and fields:

JSON
{
  "count": 2,
  "templates": [
    {
      "key": "stats",
      "name": "Stat row",
      "group": "Stats & metrics",
      "category": "Show",
      "description": "Big numbers with quiet labels. Pick row or one featured stat.",
      "when_to_use": "Three or four metrics of equal weight. Use for a quick block of related numbers.",
      "fields": [
        { "key": "heading", "label": "Heading", "type": "text", "maxLen": 80 },
        {
          "key": "stats",
          "label": "Stats",
          "type": "list",
          "help": "3 or 4 numbers reads best in row layout.",
          "itemFields": [
            { "key": "value", "label": "Value", "type": "text", "maxLen": 8 },
            { "key": "label", "label": "Label", "type": "text", "maxLen": 40 }
          ]
        }
      ]
    }
  ]
}

Each entry in fields describes one key of the slide's content object: its key, a human label, a type (text, textarea, select, boolean, range, image, list, group), plus options for a select, maxLen for text, help where a field needs a hint, and nested itemFields for list and group. The example above is trimmed; real templates also carry variant and layoutVariant selects, and varying layoutVariant between neighbouring slides keeps a deck from repeating one shape.

Field key names are the literal content keys, so they stay camelCase — the snake_case convention applies to the API envelope, not to slide content.

Both guards on this endpoint are 422 invalid_input, and both name what was wrong so you can fix it without diffing the response against your request.

Requesting more than 12 keys:

JSON
{
  "error": {
    "code": "invalid_input",
    "message": "`keys` accepts at most 12 slide types per request; you sent 20. Call GET /v1/slide-templates with no query to browse the full catalog, then request schemas in batches of 12 or fewer."
  }
}

Requesting a key that isn't a slide type — the whole request is rejected rather than quietly returning fewer templates than you asked for:

JSON
{
  "error": {
    "code": "invalid_input",
    "message": "Unknown slide types: \"titel\", \"kpu\". Call GET /v1/slide-templates with no query for the list of valid keys."
  }
}

The MCP list_slide_templates tool enforces the same cap and rejects unknown keys the same way, so an agent that has learned one transport can move to the other without surprises.

#GET /v1/decks/{id}/slides

List a deck's slides.

Shell
curl https://heydecks.com/v1/decks/deck_abc123/slides \
  -H "Authorization: Bearer hd_live_…"

200 Response

JSON
{
  "slides": [
    {
      "id": "sec_a1b2",
      "slide_type": "title",
      "order": 0,
      "content": { "title": "Q3 board update", "variant": "light" },
      "notes": null
    }
  ]
}

#POST /v1/decks/{id}/slides

Add a slide. Use one of the two modes.

AI mode — describe the slide, the model picks the template and writes the content (costs 10 credits):

Shell
curl -X POST https://heydecks.com/v1/decks/deck_abc123/slides \
  -H "Authorization: Bearer hd_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "prompt": "a stats row with three growth numbers" }'

Direct mode — supply the template and content yourself:

Shell
curl -X POST https://heydecks.com/v1/decks/deck_abc123/slides \
  -H "Authorization: Bearer hd_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "slide_type": "stats",
    "content": {
      "heading": "By the numbers",
      "stats": [
        { "value": "3x", "label": "faster onboarding" },
        { "value": "98%", "label": "retention" }
      ]
    }
  }'

Placement (optional, both modes): index (0-based) inserts at a position, or after_slide_id inserts right after a slide. Omit both to append at the end. notes sets speaker notes.

201 Response

JSON
{
  "slide": {
    "id": "sec_c3d4",
    "slide_type": "stats",
    "order": 4,
    "content": { "heading": "By the numbers", "stats": [] },
    "notes": null
  }
}

To discover valid slide_type values and the content shape for each, call GET /v1/slide-templates. You can also copy the content shape from an existing slide returned by GET /v1/decks/{id}.

#PATCH /v1/decks/{id}/slides/{slideId}

Change a slide's content. content is a partial patch merged onto the current content, so you only send the fields you want to change. Optionally update notes.

Shell
curl -X PATCH https://heydecks.com/v1/decks/deck_abc123/slides/sec_a1b2 \
  -H "Authorization: Bearer hd_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "content": { "title": "Q3 board update (final)" } }'

200 Response — the updated slide.

#DELETE /v1/decks/{id}/slides/{slideId}

Remove a slide. Returns 204 No Content.

Shell
curl -X DELETE https://heydecks.com/v1/decks/deck_abc123/slides/sec_a1b2 \
  -H "Authorization: Bearer hd_live_…"

#POST /v1/decks/{id}/slides/reorder

Reorder the deck. Pass the full set of slide ids in the order you want.

Shell
curl -X POST https://heydecks.com/v1/decks/deck_abc123/slides/reorder \
  -H "Authorization: Bearer hd_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "ordered_slide_ids": ["sec_a1b2", "sec_c3d4", "sec_e5f6"] }'

200 Response — the reordered slides.

#POST /v1/decks/{id}/edit

Change deck content with AI. Give a natural-language instruction and the planner makes the smallest set of edits that satisfies it (update, add, delete, or reorder slides, or rename the deck) instead of redrafting the whole deck. Costs 10 credits, refunded on failure.

Shell
curl -X POST https://heydecks.com/v1/decks/deck_abc123/edit \
  -H "Authorization: Bearer hd_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "instruction": "change the cover title to a sharper one-liner and add a closing CTA" }'

200 Response

JSON
{
  "summary": "Rewrote the cover title and added a closing CTA.",
  "operations": [
    { "op": "update_slide", "slide_id": "sec_a1b2", "content": { "title": "Ship faster, argue less." } },
    { "op": "add_slide", "slide_type": "cta", "content": { "heading": "Start your trial" } }
  ],
  "deck": {
    "id": "deck_abc123",
    "title": "Q3 board update",
    "slides": [ /* full updated slide list */ ]
  }
}

#Errors

StatuscodeWhen
403api_not_on_planThe key's plan does not include API access (Pro and up).
404not_foundThe deck or slide does not exist in your workspace.
422invalid_inputMissing prompt/slide_type, unknown slide_type, or a malformed body.
402insufficient_creditsOut of credits for an AI call.
502ai_unavailableThe model call failed (credits are refunded).