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.

A neat grid of rounded rectangles representing API endpoint groups, connected by thin diagonal lines

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 — success
  • 401 missing_credentials / invalid_credentials — header missing, key unknown, or revoked
  • 403 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.

ScopeGrants
forms:readList, get, version history, intelligence config, analytics.
forms:writeCreate, update, delete, publish, archive, restore, duplicate, save code.
responses:readList, get, CSV/JSONL export.
responses:writeDelete responses.
ai:runInvoke generate, edit, decision-code generate (cost: AI credits).
intelligence:runRun the Decision Engine sandbox; regenerate AI outcomes.
webhooks:writeCreate, update, delete webhook subscriptions.
imports:runPull form definitions from external URLs.
workspace:readWorkspace 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 date
  • GET /usage — recent credit ledger entries
  • GET /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_credits with details: { 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.