API Conventions
Before calling any specific endpoint, this page is worth reading once. The patterns documented here — how requests are structured, what responses look like, how errors are formatted, how pagination works — are consistent across the entire Ziett API. Understanding them once means you won't be surprised anywhere in the reference.
Base URL
All requests go to:
https://api.ziett.co/c/v1
Every path in this reference is relative to that base. So /messages means https://api.ziett.co/c/v1/messages.
Versioning
The current version is v1. When breaking changes are introduced, they will ship under a new version prefix (e.g., /c/v2) with advance notice and a deprecation window for the previous version.
Requests
HTTP Verbs
| Method | Usage |
|---|---|
GET | Retrieve a resource or a list of resources. Never modifies state. |
POST | Create a new resource or trigger an operation. |
PATCH | Partially update an existing resource. Only the fields you send are modified. |
DELETE | Permanently remove a resource. |
Content Type
All request bodies must be sent as JSON:
Content-Type: application/json
Timestamps and IDs
All timestamps in both requests and responses follow ISO 8601 in UTC:
2025-07-14T10:30:00.000000+00:00
All resource identifiers use the UUID format:
550e8400-e29b-41d4-a716-446655440000
Responses
Synchronous — 200 OK
Operations that complete immediately return 200 OK with the resource directly in the body.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"to": "+2449XXXXXXXX",
"body": "Your verification code is 482910.",
"status": "delivered",
"created_at": "2025-07-14T10:30:00.000000+00:00"
}
Asynchronous — 202 Accepted
Operations that involve external networks (carriers, Meta) are queued and processed in the background. The API returns 202 Accepted immediately with an identifier to track the operation.
{
"message": "Campaign validation successful. Processing 1,500 messages in the background.",
"campaign_id": "550e8400-e29b-41d4-a716-446655440000"
}
Store the returned identifier and use it to query status or match against incoming webhook events. Do not poll the API in a tight loop — design around event-driven callbacks.
Resource Creation — 201 Created
When a resource is created synchronously, the API returns 201 Created with the full representation of the newly created object.
Errors
Ziett uses standard HTTP status codes. Codes in the 4xx range indicate a problem with your request. Codes in the 5xx range indicate a problem on Ziett's side — these are rare, but if they persist, check the status page.
Error Object
Every error response — regardless of cause — returns the same JSON structure:
| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error identifier (e.g., BILLING_INSUFFICIENT_FUNDS). Use this for programmatic error handling. |
message | string | Human-readable description of what went wrong. |
status | integer | The HTTP status code. |
trace_id | string | Unique identifier for this request. Always include this when contacting support. |
timestamp | string | ISO 8601 timestamp of when the error occurred. |
service | string | The internal Ziett service that generated the error (e.g., core, billing). |
fields | object | (Optional) Present only on validation errors. Maps field names to specific error messages. |
Examples
{
"code": "VALIDATION_ERROR",
"message": "One or more fields failed validation.",
"status": 422,
"trace_id": "f812a3bc91e044358a9e7011c456d901",
"timestamp": "2025-07-14T10:30:00.000000+00:00",
"service": "core",
"fields": {
"to": "Must be a valid E.164 formatted phone number (e.g., +2449XXXXXXXX).",
"body": "This field is required and cannot be empty."
}
}
{
"code": "BILLING_INSUFFICIENT_FUNDS",
"message": "Your account balance is too low to process this request.",
"status": 402,
"trace_id": "c109f78ae57744358a9e7011c123c831",
"timestamp": "2025-07-14T10:30:00.000000+00:00",
"service": "billing"
}
{
"code": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred. Please retry after a short delay.",
"status": 500,
"trace_id": "b204d91fe63344358a9e7011c890e123",
"timestamp": "2025-07-14T10:30:00.000000+00:00",
"service": "core"
}
Common Error Codes
| Code | Status | Meaning |
|---|---|---|
AUTH_INVALID_API_KEY | 401 | API key is missing, malformed, or revoked. |
AUTH_INVALID_SCOPE | 403 | API key does not have the required scope for this operation. |
RESOURCE_NOT_FOUND | 404 | The resource does not exist or does not belong to your organization. |
VALIDATION_ERROR | 422 | One or more request fields failed validation. Check the fields object. |
BILLING_INSUFFICIENT_FUNDS | 402 | Account credit balance is below the cost of this operation. |
RATE_LIMIT_EXCEEDED | 429 | Too many requests. Retry with exponential backoff. |
INTERNAL_SERVER_ERROR | 500 | Unexpected error on Ziett's side. Retry after a short delay. |
Pagination
All GET endpoints that return lists use a consistent pagination envelope.
Query Parameters
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
page | integer | 1 | — | The page number to retrieve. |
size | integer | 30 | 200 | Number of records per page. |
order_by | string | created_at | — | Field to sort results by. |
order | string | desc | — | Sort direction: asc or desc. |
Response Envelope
{
"total": 1250,
"page": 2,
"size": 50,
"pages": 25,
"entries": [
{ "id": "...", "to": "+2449XXXXXXXX", "status": "delivered" },
{ "id": "...", "to": "+2449XXXXXXXX", "status": "failed" }
]
}
| Field | Type | Description |
|---|---|---|
total | integer | Total number of matching records across all pages. |
page | integer | The current page number. |
size | integer | Records per page as requested. |
pages | integer | Total pages available. Iterate until page >= pages. |
entries | array | The records for the current page. |
Iterating All Records
import httpx
headers = {"X-API-KEY": "zk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
all_messages = []
page = 1
while True:
response = httpx.get(
"https://api.ziett.co/c/v1/messages",
headers=headers,
params={"page": page, "size": 200},
)
data = response.json()
all_messages.extend(data["entries"])
if page >= data["pages"]:
break
page += 1
print(f"Retrieved {len(all_messages)} messages.")
Idempotency
For POST requests, you can supply an Idempotency-Key header to safely retry without risk of duplicate processing.
POST /c/v1/messages HTTP/1.1
Idempotency-Key: my-unique-request-id-7f3a8bc
X-API-KEY: zk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
If a request with the same key is received within a 24-hour window, Ziett returns the original response without re-executing the operation. This is especially useful when a network timeout prevents you from knowing whether a previous request succeeded.
Use a UUID or a combination of your internal resource ID and a timestamp as the key value — something guaranteed to be unique per logical operation.
With these patterns in mind, head to Send Message to make your first API call.