Skip to content

Conventions

Response envelope

Every JSON response uses the same shape:

json
{
  "success": true,
  "message": "Contacts retrieved",
  "status_code": 200,
  "error": null,
  "payload": { }
}

On failure, error is populated instead:

json
{
  "success": false,
  "message": "The template is not published",
  "status_code": 422,
  "error": {
    "code": "notifyzr.template.error.not_published",
    "message": "The template is not published",
    "issues": []
  },
  "payload": null
}

Match on error.code. Codes are namespaced notifyzr.<domain>.error.<name> and are stable; the human-readable message is not.

issues carries per-field validation detail when a request fails schema validation.

Status codes

CodeMeaning
200 / 201Success
202Accepted — queued work, e.g. a broadcast fan-out or a contact import
400Malformed request
401Missing, invalid, inactive or expired API key
403Authenticated, but not permitted
404Not found in your organization — which is also the answer when it exists in another one
422Well-formed but rejected by a business rule
429Rate limited

Why 404 and not 403

Cross-organization access returns 404, never 403. A 403 would confirm that the id exists somewhere, which is information about another organization.

Pagination

List endpoints take page and per_page (default 25, maximum 100) and return totals alongside the rows.

bash
curl "http://api.localhost/v1/contacts?page=2&per_page=100" -H "X-API-Key: $KEY"

Rate limiting

Requests are rate-limited per API key. Exceeding the limit returns 429. Broadcast fan-out and automation actions run inside the platform and are governed by quotas rather than this limit.

Quotas

Plans carry monthly ceilings (messages, contacts, automation runs). Crossing one does not corrupt work in flight: an import or a fan-out that runs out of quota completes with the remainder counted as skipped. Owners and admins are emailed at 80% and again at the limit.

Check current usage at GET /v1/usage.

Released under the Apache 2.0 License.