Documentation
API reference.
Bearer-token REST, JSON request/response, versioned at /api/v1. Every checkbox in the builder is a JSON field; every AI capability is an endpoint.

Quick links: Interactive reference (Scalar) · OpenAPI 3.1 spec · MCP server · Webhooks + embeds
Quickstart
One curl from key to first call:
# 1. Mint an API key (with the scopes you need):
# https://askery.app/dashboard/api-keys
# 2. List your forms:
curl -H "Authorization: Bearer ak_live_..." \
https://askery.app/api/v1/forms
# 3. Generate a form from a brief:
curl -X POST -H "Authorization: Bearer ak_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: gen-2026-06-05-001" \
https://askery.app/api/v1/forms/ai/generate \
-d '{ "brief": "Five questions for an enterprise NPS survey.", "save": true }'
# 4. Pull the response payload:
curl -H "Authorization: Bearer ak_live_..." \
"https://askery.app/api/v1/forms/<id>/responses?status=completed&limit=100"Base URL + versioning
All endpoints live under https://askery.app/api/v1/…. We bump the prefix (/api/v2, etc.) when we break a contract; existing endpoints stay frozen as long as anyone uses them.
Authentication
Bearer-token, scope-gated. Mint a key at /dashboard/api-keys; the full key is shown once on creation (format ak_live_… in production). We store only a SHA-256 hash plus an 8-char prefix for identifying the key in the UI.
Authorization: Bearer ak_live_<...>
On every request the server returns:
200/201/204— success401 missing_credentials/invalid_credentials— header missing, key unknown, or revoked403 insufficient_scope— key is valid but missing the scope this endpoint requires
Scopes
Each key carries a set of scopes you choose at creation. Pick the narrowest set you need — a key meant for a BI pipeline should not have forms:write.
| Scope | Grants |
|---|---|
| forms:read | List, get, version history, intelligence config, analytics. |
| forms:write | Create, update, delete, publish, archive, restore, duplicate, save code. |
| responses:read | List, get, CSV/JSONL export. |
| responses:write | Delete responses. |
| ai:run | Invoke generate, edit, decision-code generate (cost: AI credits). |
| intelligence:run | Run the Decision Engine sandbox; regenerate AI outcomes. |
| webhooks:write | Create, update, delete webhook subscriptions. |
| imports:run | Pull form definitions from external URLs. |
| workspace:read | Workspace identity, plan, credits, audit log. |
Legacy read / write scopes from the v1.0 read API still work — they expand to the full set of new scopes server-side.
Pagination
Cursor-based, Stripe style. Every list endpoint returns:
{
"data": [ ... ],
"pagination": {
"limit": 50,
"next_cursor": "eyJrIjoiMjAyNi0wNS0yNVQxMzoyNDoyOC44ODlaIiwiaSI6IjMxNDNlYmQ4LTcwODYtNGI1Zi1iYzE0LWQ1YTAwNWY0MzAzYiJ9",
"has_more": true
}
}To fetch the next page, send back the next_cursor as ?cursor=…. When has_more is false, next_cursor is nulland you've reached the end.
Cursors are opaque (currently base64url JSON, may change). Don't decode them; just round-trip what we sent.
Idempotency
Mutations (POST / PUT / PATCH / DELETE) accept an Idempotency-Key header up to 256 chars. We fingerprint (method, path, canonical body) and store the response for 24h, so:
- Same key, same body within 24h → replay the stored response (same status, same JSON).
- Same key, different body → 409
idempotency_conflict. That's a client bug; silent replay would mask it. - Different key → fresh execution.
Use it on any operation a network blip could double: form creation, AI generation, webhook creation, response delete.
Errors
New endpoints (everything beyond the original three read routes) use a structured envelope:
{
"error": {
"code": "form_not_found",
"message": "No form with id 'abc'.",
"request_id": "req_abc123…",
"hint": "Did you mean a draft? Check ?include_drafts=true.",
"docs_url": "https://askery.app/docs/api/errors/form_not_found",
"details": { ... }
}
}code is stable — branch on it. message is for logs and dev tools, not for end users. request_id goes back to support tickets and is in the X-Request-Id response header.
The two original read endpoints (GET /api/v1/forms and GET /api/v1/forms/{id}/responses) keep their legacy { error, message } shape for back-compat with the WordPress plugin and v1.0 customers.
What you can do
Twelve endpoint groups; one canonical schema. The interactive reference at /dashboard/developers/api-reference lets you call every endpoint live against your workspace.
Forms
- List, create, get, JSON-merge-patch, full replace, delete
- Duplicate, publish, archive, restore
- Audit-grade version history with one-click rollback
AI
- Generate — brief → FormDefinition (save inline or preview)
- Edit — chat-style edit with multi-turn history; preview vs save
- Ops — deterministic, 0-credit mutations via the EditOp protocol
Responses
- List with rich filters (
status,email,since,until) + cursor pagination - Folded answers (
answers: { [question_id]: value }) - Optional cached AI outcome via
?include=intelligence - Streaming CSV and JSONL exports
- Delete a single response
Form Intelligence + Decision Engine
- Read intelligence config (mode, rules, decision code, sections, test personas)
- Save Decision Engine code with validators (forbidden globals, hallucinated question ids, missing default export)
- AI-generate Decision Engine code from a brief (IR extraction → render → critique)
- Test code in the WASM sandbox against synthetic answers (0 credits)
- Regenerate the cached AI outcome on a single response
Import
Point at any form URL — Google Forms is parsed deterministically; everything else falls through to the LLM mapper. Returns a validated FormDefinition + an import report flagging fields that couldn't map cleanly.
Prefill links
Compose ?question_id=value URLs without manual encoding. Validates every key against the stored form, so a typo returns 400 instead of silently dropping.
Webhooks
- Full CRUD on subscriptions (URL + events + active flag)
- Secret is returned once on creation (mirroring API key behaviour)
- Per-webhook delivery log with status, latency, and error attempts
Workspace
GET /workspace— whoami: id, name, plan, credit balance, plan renewal dateGET /usage— recent credit ledger entriesGET /workspace/audit-log— form creates, member invites, key issuances, etc.GET /forms/{id}/analytics— views, starts, completions, partials, funnel, per-question summaries
Limits + plan gates
- Bodies cap at 1 MB (forms cap at ~200 KB).
- Free plan: 1 webhook subscription. Pro+: unlimited.
- AI endpoints debit workspace credits per the same usage page schedule as the dashboard. Out of credits returns
402 insufficient_creditswithdetails: { required, balance, reason }.
Webhooks vs API
Use webhooks for real-time delivery — every submission posts a single HMAC-SHA256-signed JSON payload to your URL, retried on failure. See /developers for the payload shape and signature verification recipe.
Use the API for everything else: cron-driven syncs, on-demand fetches, BI pipelines, internal admin tools, and anything that needs to mutate (create / edit / delete).
MCP — drive Askery from any AI agent
The MCP server is a Model Context Protocol bridge over the same REST API. Wire it into Claude Desktop, Cursor, or any MCP-capable client and your agent can operate Askery directly: build forms by name, fetch responses, codegen Decision Engines, manage webhooks. Same scopes, same audit trail, same rate limits — agents are first-class clients, not a separate world.
Building something specific? Email support@askery.app.