Iterant is in early access. These docs describe the product as it works today.
Iterant Docs

REST API

How the v1 public REST surface works: base URL, bearer auth, camelCase conventions, identifiers, paging, and the error envelope.

The Iterant public API is JSON over HTTPS, versioned, and camelCase. Today it is nine reads and one writable resource: agent tasks. Everything else on this surface is a GET.

Base URL

https://api.iterant.ai/api/v1/public/

Every public route lives under this prefix. Paths outside it belong to the dashboard's own API and do not accept an API key.

The OpenAPI document is served separately, and you can generate a client from it directly:

https://api.iterant.ai/api/public/schema/

The API reference is generated from that document, so it cannot drift from what the server does. Where this page disagrees with either, the schema is authoritative.

Authentication

Send your key as a bearer token:

curl https://api.iterant.ai/api/v1/public/brands/ \
  -H "Authorization: Bearer itr_kK4dQ8v…"

A missing or unrecognised key returns 401 with a WWW-Authenticate: Bearer challenge. See API keys for minting and scoping.

Naming a brand

The brand is always a path segment. Every brand-scoped route is /brands/{brandSlug}/…, and the server checks that slug against the organization your key resolves to.

curl https://api.iterant.ai/api/v1/public/brands/acme/pages/ \
  -H "Authorization: Bearer itr_kK4dQ8v…"

X-Brand-ID does not select a brand here. On a brand-pinned key, a header naming any other brand is refused before the view runs. An org-scoped key can omit the header.

Conventions

camelCase on the wire

Response bodies use camelCase keys: brandSlug, urlPath, previewUrl, updatedAt. Request bodies are read the same way, so send opportunityRef rather than opportunity_ref. A validation error names the field back to you the way you spelled it.

Enum values never camelize. build_page, failed_generation, and google_ai_mode cross the wire verbatim, so a value you read out of one response is a value you can send back in the next request.

The MCP server returns the same camelCase payloads. Its tool arguments are snake_case, and a tool result carries the same fields as the REST response for the same read, plus a _note key and, on a large result, trimming.

Identifiers

Objects are addressed by the handle a human reads:

  • Brands expose brandSlug. There is no brand id on this surface.
  • Pages expose urlPath. There is no page id and no /pages/{id} route; fetch one page through …/pages/by-path/?urlPath=/pricing.
  • Page opportunities expose a ref: slugify(title) truncated to 60 characters, a hyphen, and the first 8 characters of the row's UUID. It resolves on the suffix, so it survives a title edit. Use a ref that came back from the list route, because refs are not constructible by hand. This object also carries id, and the detail route accepts a bare UUID in the same path segment as a fallback.
  • Agent tasks expose a UUID taskId, minted by the server and returned in the response that created the task.

Paging

List routes take limit and offset and return a fixed envelope:

curl "https://api.iterant.ai/api/v1/public/brands/acme/pages/?limit=20&offset=20" \
  -H "Authorization: Bearer itr_kK4dQ8v…"
{
  "items": [
    { "urlPath": "/pricing", "title": "Pricing", "status": "published" }
  ],
  "total": 41,
  "offset": 20,
  "limit": 20,
  "note": "Showing 20 of 41 (offset 20). Narrow by status, locale or urlPathPrefix, or pass offset=40 for the next page."
}

note is a steering string, empty when nothing was hidden. Most non-list responses carry one too: the visibility and linking reads, and the brand overview's nextActions.

Two reads cap without paging. visibility/overview/ and linking/advisory/ take limit but no offset, and their note tells you to narrow.

responseFormat

Every read takes responseFormat=concise (the default) or detailed. The task routes do not. Detailed adds fields rather than only adding rows: full briefs instead of the first five outline and FAQ entries, per-dimension score breakdowns, page outlines, previewUrl and liveUrl on page list rows, anchor variants on linking recommendations. It costs several times the tokens.

Metric states

Visitor signals cover sessions, page anatomy, and conversions where isExact is false. Those figures are sampling-weighted estimates, flagged estimated: true, and are the right order of magnitude.

Errors

Every non-2xx response uses one envelope:

{
  "type": "client_error",
  "errors": [
    {
      "code": "not_found",
      "detail": "Brand not found.",
      "attr": null
    }
  ]
}

type is one of validation_error, client_error, or server_error. errors is always a list, with one entry per offending field on a validation failure and attr naming the field.

StatusMeaning
400Malformed request: an unknown enum value, a urlPath that does not start with /, a non-integer limit, a missing required query parameter
401Missing, malformed, unrecognised, or revoked key
403A brand-pinned key sent an X-Brand-ID naming a different brand
404Not found, or not yours. See below
405Method not allowed. Every route is GET only, except the task collection, which also takes POST
409A task or a page build is already running for this brand. See Agent tasks
422The opportunity you asked to build exists but is not eligible in its current state
500Server error. Retry with backoff
503The agent worker is not configured in this environment. Nothing was created

429 is not implemented. Nothing throttles this surface yet, and the one-task-per-brand 409 is a workspace constraint rather than a quota. Rate limiting is planned, and a client that already handles 429 with backoff will need no change when it arrives.

404 on objects you cannot reach

A brand-pinned key naming another brand gets a different answer depending on how you named it:

  • Through the URL, /brands/some-other-brand/pages/ returns 404, the same answer a stranger's brand gives.
  • Through the X-Brand-ID header, you get 403 This API key is restricted to a different brand. That check runs during authentication, before any object is resolved.

Endpoints

Every endpoint has its own reference page, generated from the schema, listing each parameter, the full response shape, and a request you can send. This page covers what holds across all of them.

EndpointReturns
GET /brands/The brands this credential can reach
GET /brands/{brandSlug}/overview/One-call state of a brand
GET /brands/{brandSlug}/pages/A brand's pages
GET /brands/{brandSlug}/pages/by-path/One page, by route
GET /brands/{brandSlug}/reports/pages/Page or brand-wide measurement
GET /brands/{brandSlug}/page-opportunities/The work queue
GET /brands/{brandSlug}/page-opportunities/{opportunityRef}/One opportunity in full
GET /brands/{brandSlug}/visibility/overview/AI visibility, projected by view
GET /brands/{brandSlug}/linking/advisory/Advisory internal links for a page
POST /brands/{brandSlug}/tasks/Start one agent task
GET /brands/{brandSlug}/tasks/This brand's tasks, newest first
GET /brands/{brandSlug}/tasks/{taskId}/Poll one task

The nine reads are all GET and all take responseFormat. The task routes are the only place this surface accepts a POST, and a task is one autonomous Terra turn: a free-form instruction, or a build of one page opportunity. It takes about five minutes, it counts against your usage, and only one runs per brand at a time.

curl -X POST https://api.iterant.ai/api/v1/public/brands/acme/tasks/ \
  -H "Authorization: Bearer itr_kK4dQ8v…" \
  -H "Content-Type: application/json" \
  -d '{"command": "prompt", "prompt": "Add an About page with a team section."}'

Agent tasks: both commands, the poll-then-submit pattern, callbacks, and every error the routes raise.

Not built

  • Publishing and unpublishing. Putting a page on a live customer-facing domain is a dashboard step, and there is no route for it.
  • Batch task creation. Builds serialize per brand, so a fan-out endpoint would only hide the queue. One task per call.
  • Writing back to the queue. Dismissing, scheduling, or re-opening an opportunity.
  • Idempotency keys and rate limiting. Both planned; neither exists. A repeated POST to the task route is a second task, guarded only by the one-per-brand 409.
  • Configuration writes and raw lead data. Not exposed here at all. See the overview.

Last updated on

On this page