Authentication
Every request authenticates with an API key pair over HTTPS basic auth. Plain HTTP is refused, not redirected.
curl https://api.anthonyoliva.com/v1/messages \
-u "$AO_KEY_ID:$AO_KEY_SECRET"
If you prefer an explicit header, the equivalent is:
-H "Authorization: Basic $(printf '%s:%s' "$AO_KEY_ID" "$AO_KEY_SECRET" | base64)"
Key format
| Part | Example | Notes |
|---|---|---|
| Key ID | ao_live_k_7Fq2... |
Safe to log. Identifies the key in audit records. |
| Key secret | ao_live_s_9Xb4... |
Shown once at creation. Never logged by us. |
Test keys use the ao_test_ prefix. Test keys accept the same requests, return realistic responses, and never send a real message or incur a charge.
Scopes
Assign the narrowest scope that works. A leaked send-only key cannot read your message history.
| Scope | Grants |
|---|---|
messages:write |
Send SMS and MMS |
messages:read |
Read message history and delivery status |
numbers:write |
Search, buy, configure, and release numbers |
numbers:read |
List numbers and their campaign association |
registration:write |
Create and update brands and campaigns |
registration:read |
Read registration status |
lookup:read |
Perform number lookups |
webhooks:write |
Manage webhook endpoints |
Rotation
Create the new key first, deploy it, confirm traffic has moved using the per-key request counter in the console, then revoke the old key. Revocation takes effect within five seconds across all regions.
Rotate immediately if a secret is exposed in a repository, a log, a CI output, or a support ticket. Revocation is instant and irreversible.
Rate limits
Limits are applied per account, not per key.
| Endpoint group | Limit |
|---|---|
POST /v1/messages |
200 requests per second |
GET endpoints |
100 requests per second |
POST /v1/lookup batch |
10 requests per second |
| Registration endpoints | 10 requests per second |
Every response carries the current state:
RateLimit-Limit: 200
RateLimit-Remaining: 186
RateLimit-Reset: 1
Exceeding a limit returns 429 with a Retry-After header in seconds. Back off exponentially with jitter; retrying immediately in a tight loop will extend the window.
Rate limits are not the same as carrier throughput. Accepting a message at 200 per second does not mean the carrier will deliver it at that rate. Messages queue and drain at your assigned throughput, which is set per campaign by each carrier.
Idempotency
Send an Idempotency-Key header on any write request. We store the result for 24 hours and replay it if the same key is used again, so a network timeout followed by a retry cannot send the same message twice.
-H "Idempotency-Key: 8f14e45f-ea4c-4f52-a0b1-2c9e7d3b6a10"
Use a UUID per logical operation, not per HTTP attempt. Reusing a key with a different request body returns 409 idempotency_key_reused.
Versioning
The version is in the path. /v1 is current. We will not make breaking changes within a version. Additive changes — new fields on a response, new optional request fields, new enum values — can ship at any time, so parse defensively and ignore fields you do not recognize.
A new major version would be announced at least 12 months before any deprecation of the previous one.
IP allowlisting
Accounts can restrict API access to a set of source addresses under Settings → Security. Requests from outside the list are refused with 403 ip_not_allowed before authentication is evaluated.