Base URL and auth
zpl_ key from Settings, Integrations, API keys, or a Clerk
OAuth token. The workspace comes from the credential, never from a parameter.
Which paths need no credential
Which paths need no credential
GET /v1/health and GET /v1/openapi.json are open.A key belongs to one workspace. A Clerk token gets its workspace from the organization you
picked at consent.Scopes
A key carries the scopes you ticked when you made it.
A missing scope is a 403
forbidden. A person on OAuth has every scope their membership
allows.
Admin, and the contacts scope
Admin, and the contacts scope
admin exists inside the product but is never handed out on a key. A key that could revoke
keys is refused by the route and by the screen that issues them.contacts is part of the API vocabulary. The key screen currently offers read, write
and runs. Without contacts the contact fields are absent, not null.Paging
{ "data": [...], "next_cursor": "..." }. Pass limit (default 50,
maximum 200) and send next_cursor back as ?cursor=. Stop when it is null.
Envelope, filters and formats
Envelope, filters and formats
A single object answers
{ "data": { ... } }. GET /v1/people adds total, which is how
many rows match your filters before paging.The cursor is opaque. Do not parse one or construct one.Filters take several values comma separated: ?country=NL,BE. Dates are UTC days as
YYYY-MM-DD. Booleans are literally true or false.An unknown query parameter is a 400. A filter typo never reads as a filtered list.Idempotency
Idempotency-Key. Any client-chosen value
works, a UUID for instance. PATCH and DELETE do not take the header.
What a repeat does
What a repeat does
The same request twice with the same key has one effect and returns the same response
twice.The same key with a different body is a 409
conflict. So is a repeat while the first
request is still in flight. A POST without the header is a 400.Rate limits
Going over
Going over
Over the limit is a 429
rate_limited with a Retry-After header in seconds.Retry-After is the time until one request is allowed, not until the bucket is full.
RateLimit-Reset is the time until it is full again. Back off on Retry-After.Errors
code is the contract and is stable. message is for humans and may change. The full list
is on Errors.
Plan gating
Plan gating
The REST API sits behind the
api_keys feature and MCP behind mcp. A plan with neither
answers 402 feature_not_in_plan on every call.details then carries feature and an upgrade_url. A usage cap that was exceeded
answers 402 limit_exceeded in the same shape. Show the URL rather than retrying.