Agent Integration Guide¶
How an LLM agent (Claude Code, Cursor, Codex, or any tool-using model) should use Deckrun.
Deckrun is designed for agents. The API is schema-driven, deterministic, and async-safe. This guide provides the decision logic an agent needs to use Deckrun correctly and reliably.
The agent loop¶
Discover → Build → Submit → Poll → Deliver
Each stage has a specific API call and a clear exit condition.
1. Discover¶
Before generating content, fetch the schemas and available resources.
Get the slide format schema (what markdown does Deckrun accept?):
GET /schema?category=slide
Returns the slide format definition including layout directives, surface syntax, notes blocks, timing hints, and supported attributes.
Agents should use this schema to constrain markdown generation and validate output before submission.
List available voices (if narration is needed)
GET /voices
Returns a list of voice resources (see deckrun-voice.json).
- Select a
voice_idwithstatus: "active" - Use tier:
systemfor included voices - Use tier:
premiumfor high-fidelity or cloned voices
List available connectors (where should output be delivered?)
GET /connectors
Returns supported output destination types. Use this to construct a valid output_destination object.
2. Build¶
Generate markdown that conforms to the slide format schema.
Key conventions¶
- Use
<!-- <title-slide /> -->for the title slide - Use
---(horizontal rule) to separate slides - Use
<!-- <slide-notes> --> ... <!-- </slide-notes> -->for presenter notes (Notes are used as the narration script) - Use
<!-- <two-content /> -->and<!-- <right-side /> -->for two-column layouts - Write speaker notes as complete, spoken sentences
Validate the generated markdown against the schema before submission.
3. Submit¶
POST /generate
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
{
"markdown": "...",
"slide_theme_id": "deckrun-default",
"output_types": ["pdf", "video", "audio"],
"voice_id": "<voice_id from GET /voices>",
"output_destination": {
"type": "s3",
"bucket": "my-bucket",
"prefix": "decks/"
}
}
Response behavior¶
200 OK— synchronous completion (typically PDF-only, small decks)202 Accepted— async job queued (video, audio, or heavier workloads)
For async responses, store the returned job_id and proceed to polling.
4. Poll¶
GET /jobs/{job_id}
Authorization: Bearer YOUR_API_KEY
Poll until status is terminal:
| Status | Meaning | Action |
|---|---|---|
queued |
Waiting in queue | Wait, retry |
processing |
Rendering in progress | Wait, retry |
success |
Complete | Retrieve artifacts |
failure |
Error occurred | Read error.message, retry or escalate |
timeout |
Time limit exceeded | Retry or escalate |
cancelled |
Job cancelled | Re-submit if needed |
Use queue_position and estimated_wait_seconds to control retry intervals.
Do not poll more frequently than once every 5 seconds.
5. Deliver¶
On status: "success", the response includes result with URLs for each requested output:
{
"status": "success",
"result": {
"pdf_url": "https://...",
"video_url": "https://...",
"audio_url": "https://...",
"notes_text_url": "https://..."
}
}
If an output_destination was provided, artifacts are already delivered. URLs remain available for direct access or downstream processing.
Agent-safe conventions¶
- All heavy outputs (video, audio) are async — never block on the POST /generate response
- Schema
$idfields are permanent URLs — safe to cache - Artifact filenames include timestamps — no overwrites on re-runs
- Read operations are idempotent
- Each /generate call creates a new job
- Error responses follow the job status schema — On failure, always inspect
error.message
Example: Claude Code integration¶
import httpx
API_KEY = "..."
BASE = "https://api.agenticdecks.com"
def generate_deck(markdown: str, slide_theme_id: str = "deckrun-default") -> dict:
resp = httpx.post(
f"{BASE}/generate",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"markdown": markdown, "slide_theme_id": slide_theme_id, "output_types": ["pdf"]},
)
resp.raise_for_status()
data = resp.json()
# Synchronous result (PDF-only)
if data.get("status") == "success":
return data["result"]
# Async: poll for completion
job_id = data["job_id"]
import time
while True:
time.sleep(5)
r = httpx.get(
f"{BASE}/jobs/{job_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
)
r.raise_for_status()
job = r.json()
if job["status"] == "success":
return job["result"]
if job["status"] in ("failure", "timeout", "cancelled"):
raise RuntimeError(f"Job {job_id} failed: {job['error']['message']}")
See also¶
- Quickstart — first run step-by-step
- Schemas — public schema index
- OpenAPI spec — machine-readable API definition