Skip to content

Concepts

Everything in AirFone belongs to an organization: numbers, assistants, contacts, calls. Keys act for one organization.

A developer account can exist without one. When you sign up you get a sandbox organization of your own. A business can invite you into its organization, and you can publish connectors that any organization installs.

Every organization has test and live data, kept apart. Test keys contain _test_, live keys contain _live_.

In test mode:

  • Calls are simulated. No phone rings, and a scripted caller talks to your assistant.
  • WhatsApp and SMS go to a simulated inbox you can see in the console.
  • AI usage comes from a free test quota.
  • Webhooks fire exactly as in live mode, with "livemode": false.
Key Prefix Where it lives What it can do
Secret af_sk_ Your server only Everything the organization allows
Restricted af_rk_ Your server, or a service you trust less Only the scopes you pick
Publishable af_pk_ Web pages and apps Open widget sessions on the domains you list

Send a secret or restricted key as Authorization: Bearer af_sk_.... A secret key is shown once when created. If one leaks, roll it in the console: the old key keeps working for the grace period you pick (none, 1 hour or 24 hours), then stops.

Restricted keys carry scopes in the form resource:read or resource:write. Write includes read.

calls, messages, agents, contacts, records, webhooks, events, usage, connectors.

A call without the scope it needs gets 403 with code missing_scope, and the error names the scope.

Send Idempotency-Key on every POST. If a request times out, send it again with the same key and you get the first result instead of a second call or message. Keys are kept for 24 hours. Reusing a key with a different body returns 409 idempotency_key_reused.

The API is versioned by date. Your account is pinned to the version current when you made your first request. Send AirFone-Version: 2026-10-04 to choose one per request. Breaking changes only ever arrive in a new version, and every response includes the version that served it.

Every error has the same shape:

{
"error": {
"type": "invalid_request",
"code": "parameter_missing",
"message": "to is required.",
"param": "to",
"request_id": "req_4Hk2b9"
}
}
Status type Meaning
400 invalid_request Something in the request is wrong. param says what.
401 authentication Missing, wrong or revoked key.
403 permission The key lacks a scope, or the domain is not allowed.
404 not_found No such object, or it belongs to another organization or mode.
409 conflict Idempotency key reused, or the object changed state.
422 unprocessable Valid request the platform cannot do, such as WhatsApp outside the window.
429 rate_limit Too many requests. Wait for Retry-After seconds.
5xx api_error Our fault. Safe to retry with the same idempotency key.

Limits are per key: 100 requests a second in live mode, 25 in test mode, with short bursts allowed. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. Publishable keys are also limited per visitor IP.

Every response has an AirFone-Request-Id header, and every error repeats it as request_id. Look it up under Logs in the console, and include it when you contact support.