Request and Response Format
One envelope, one set of status codes and one pagination shape, on every endpoint of every surface.
Overview
The shape of a call does not change from resource to resource. If you can read one response you can read all of them, and error handling written once keeps working as your integration grows.
Two rules carry most of it. A successful answer puts its payload under data, and a failed one puts a machine-readable code under error. The HTTP status always agrees with which of the two arrived.
Reference
The Request
Send JSON with a Content-Type: application/json header. A form-encoded POST is accepted as well, which helps when a client cannot set the header. Filters, sorting and paging travel in the query string on every method.
X-Api-Key is the fallback for clients that cannot set this one.
application/json when the body is JSON. Without it the body is read as form fields.
The Envelope
Success carries data, plus meta when there is anything to say about the payload. Failure carries error with a stable code, a human message and, on validation failures, a details map naming the fields.
{ "data": { "id": 42, "email": "[email protected]" } }
{ "data": [ ], "meta": { "total": 318, "page": 2, "limit": 25, "next_page": 3 } }
{ "error": { "code": "validation_failed", "message": "Email is not valid.",
"details": { "email": "invalid_format" } } }
Branch on the key, not on the text. code is part of the contract and stays put; message is written for a human reading a log and may be reworded.
Status Codes
Idempotency-Key is still running. Wait and retry.
details names the fields.
Retry-After.
Pagination
List endpoints take page and limit in the query string. limit defaults to 25 and is capped at 100, so asking for more silently gives you 100. Walk pages with meta.next_page, which is 0 on the last one.
Offset paging is the v1 contract. Cursor paging exists only where the data is a timeline, such as ticket messages, and those endpoints document their own cursor fields.
Response Headers
Every credentialed answer reports the state of your budget, so you can pace yourself without guessing.
Example
curl -X POST 'https://panel.example.com/api/v1/admin/clients' \
-H 'Authorization: Bearer wak_...' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: signup-8f21c0' \
-d '{"name":"Ada","surname":"Lovelace","email":"[email protected]"}'
# 201 {"data":{"id":91,...}}
# the same call again returns the same body, and does not create a second client
# 201 Idempotency-Replayed: true
Pitfalls
A list answer keeps its count at meta.total. Reading total from the root gives you nothing and no error, which reads as an empty result set. The same goes for page and limit.
While an installation is in demo mode, POST, PUT, PATCH and DELETE come back as 403 demo_mode before any resource is reached. Reads keep working, so a demo stays browsable through the API. An integration that fails only on writes is worth checking against this first.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.