Skip to main content
Version: 2.0

Agent Quickstart

This page is a single, linear runbook for an autonomous agent (or the code orchestrating one) to drive the Prezent Platform API end to end:

  1. Discover the surface area.
  2. Authenticate with a Bearer token.
  3. Start an async job.
  4. Track it to a terminal state (poll, stream, or webhook).
  5. Handle success and failure.
  6. Back off when rate-limited.

Everything here links back to the page that documents it in full. If you are wiring Prezent into an MCP-aware client (Claude, Cursor, Cline, Continue, Zed), you can skip the HTTP plumbing entirely — see Agents & MCP.

1. Discover

Two machine-readable entry points on the Prezent docs site describe the whole API (paths are relative to this documentation site's origin):

  • /llms.txt — an llms.txt index: a short summary plus links to every documentation page and the OpenAPI spec. Start here to enumerate what exists.
  • /openapi/openapi-v2.yaml — the OpenAPI 3.1 contract for API 2.0 (the current version). Every path, request body, response schema, and error code is defined here. Load it to generate a client or to validate requests before sending them. The 1.0 contract remains at /openapi/openapi.yaml.

A fuller, self-contained digest of the whole contract lives at /llms-full.txt.

The same contract is browsable interactively at the API Reference.

2. Authenticate

Every endpoint uses API key (Bearer) authentication. Send your key on every request:

Authorization: Bearer YOUR_API_KEY

API keys are issued by your Customer Success Manager (self-service key generation is not currently supported). See Getting Started → Authentication for key scoping, expiry, and the 401 codes you may see.

The canonical production host is https://api.prezent.ai.

3. Start an async job

Most useful operations are asynchronous. You POST to an entry endpoint and receive a callback_id:

curl -s -X POST https://api.prezent.ai/api/v2/generate \
-H "Authorization: Bearer $PREZENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "prompt": "A 5-slide deck on renewable energy" }'
{ "message": "Job submitted successfully",
"resData": { "callback_id": "cb_123" },
"error": null }

Note the payload is under resData, not data — success shapes vary by endpoint on this API, so read the schema for the one you are calling.

Resource-creating writes (POST/PUT/PATCH) accept an optional Idempotency-Key request header so a retry after a network error does not create a duplicate job. Send a unique key (a UUID is ideal) per logical operation:

curl -s -X POST https://api.prezent.ai/api/v2/generate \
-H "Authorization: Bearer $PREZENT_API_KEY" \
-H "Idempotency-Key: 8e6df756-18f6-4f16-8e12-7d7a5c5cb181" \
-H "Content-Type: application/json" \
-d '{ "prompt": "A 5-slide deck on renewable energy" }'

A retry with the same key and an identical body returns the cached response verbatim with an Idempotency-Replayed: true header, so the job is never created twice. See Developer Guide → Idempotency for the cache window and the behaviour on body mismatch.

The Template Converter family follows the same shape: POST /api/v1/template-converter/start returns a callback_id. See the Template Converter API.

4. Track completion

How you learn a job finished depends on the service:

ServiceMechanism
AutoGeneratorStream (SSE) — the only mechanism; there is no status endpoint
Template ConverterPoll its status endpoint
EitherWebhooks — push completion into your own backend

AutoGenerator — read the stream

Exchange the callback_id for a stream URL, then read events:

curl -s -X POST https://api.prezent.ai/api/v2/streams/sessions \
-H "Authorization: Bearer $PREZENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "callback_id": "cb_123" }'

You receive a short-lived stream_url at data.stream_url. Open it with any SSE client — no Authorization header, the token is in the URL.

Every event has the same shape, which makes this easy to consume programmatically:

{ "status": "inprogress",
"message": "Analyzing the key elements",
"callback_id": "cb_123" }

Read status until it is no longer inprogress; it then becomes success, failed, or cancelled and the stream closes. A success event carries download_url.

message is written for display — surface it to a user verbatim rather than inventing your own progress copy.

Reconnection, keepalives, and code samples are in Streaming (SSE).

Template Converter — poll

The Template Converter takes its callbackId in the path:

GET /api/v1/template-converter/status/{callbackId}

It returns 200 whenever the job is found — branch on the status field in the body, not on the HTTP status code. Start at 2–5 seconds with jittered exponential backoff up to ~30 seconds; do not poll faster than every 2 seconds.

See Developer Guide → Async operations.

Either — subscribe to webhooks

To avoid holding a connection open, subscribe an HTTPS URL once and Prezent delivers a signed POST on completion. The relevant event types are autogeneration.completed / autogeneration.failed and template_conversion.completed / template_conversion.failed. Verify the X-Prezent-Signature (HMAC-SHA256) before acting, and deduplicate on the event id. See Webhooks.

5. Handle terminal states

Whichever mechanism you chose, branch on the terminal state:

  • Success — for AutoGenerator, the stream's status == "success" (or the autogeneration.completed webhook). The terminal event carries download_url.

    caution

    download_url may be null on a generation stream — the deck is built but not yet exported. That is still a success: call POST /api/v2/autogenerator/download and read that stream for the file. Do not treat null as a failure.

    For Template Converter, status == "success" on its status response; use its download endpoint (see the Template Converter API).

  • Failure — the stream's status == "failed" or "cancelled", a failed status response, or the *.failed webhook. On a stream, the reason is in message. HTTP-level failures arrive in the standard error envelope; read error.code against the error catalog to decide whether to retry, fix the input, or surface the error.

6. Back off on rate limits

When you exceed a limit the API returns HTTP 429 with the standard error envelope. Use both the headers and the body:

  • X-RateLimit-Remaining is returned on successful responses from rate-limited endpoints — read it to self-throttle before you hit the wall (X-RateLimit-Limit and X-RateLimit-Reset accompany it).
  • On a per-category RATE_LIMIT_EXCEEDED 429, honour Retry-After (seconds to wait) — it ships with the same X-RateLimit-* headers.
  • error.code on a 429 is TOO_MANY_REQUESTS (gateway), RATE_LIMIT_EXCEEDED (per-category), or USAGE_LIMIT_EXCEEDED (annual) — the source of truth for why you were limited. The gateway throttle and the annual quota do not carry X-RateLimit-*/Retry-After, so fall back to backoff there.

Always layer your own jittered exponential backoff (start ~1–2 s, cap ~30 s) on top of Retry-After so a fleet of agents does not retry in lockstep. The full limit model (gateway throttle, per-minute limits, annual quotas) is in Developer Guide → Usage quotas and rate limits.

Putting it together

A robust agent loop, in pseudocode:

spec        = GET /openapi/openapi-v2.yaml        # on the Prezent docs site

# 1. start (retry a 429 with Retry-After + jitter; the Idempotency-Key
# makes a retry safe — it will not create a second generation)
job = POST /api/v2/generate
(Authorization: Bearer ..., Idempotency-Key: <uuid>)
callback_id = job.resData.callback_id

# 2. mint a stream URL for it
session = POST /api/v2/streams/sessions { callback_id }
stream_url = session.data.stream_url

# 3. read events. No Authorization header — the token is in the URL.
# Set NO read timeout: a :ping arrives every 15s to prove liveness.
download_url = null
for event in SSE(stream_url):
switch event.status:
case "inprogress": report event.message to the user; continue
case "success": download_url = event.download_url; break
case "failed", "cancelled": surface event.message; stop

# on a dropped connection, reconnect with &last_event_id=<last id: seen>;
# if the token expired, mint a fresh URL — it replays the whole turn.

# 4. a null download_url means "not exported yet", not "failed"
if download_url is null:
dl = POST /api/v2/autogenerator/download { callback_id, export_type }
download_url = read the same event loop over dl.stream_url

Where to go next