API keys, responses and limits
authentication · API key · bearer token · Authorization header · azm_live_ · azm_test_ · scopes · permissions · rate limit · 429 · Retry-After · idempotency · Idempotency-Key · retries · response envelope · isSuccess · fieldErrors · test mode · sandbox key
Every call to the public API carries an API key. All routes start with https://dashboard.azimea.com/api/v1; the
API reference lists each one.
Make a key
Section titled “Make a key”Owners and admins make keys in Settings › API keys:
- Give the key a name you will recognise later (2–100 characters), such as the system that will use it.
- Tick only the permissions that system needs (below).
- Choose when it expires: in 30, 90 or 365 days, or never (chosen explicitly).
- Copy the key. It is shown once; afterwards the list shows only its ends (
azm_live_7f3c…5e0c) and when it was last used.
A company can have up to 10 active keys. A key stops working the moment it is revoked or expires; Azimea answers the same way for a revoked key as for one that never existed.
Permissions
Section titled “Permissions”| Permission | What it allows |
|---|---|
contacts:read |
Read contacts, segments, topics and contact fields: GET /contacts, GET /contacts/{reference}, GET /segments, GET /topics, GET /contact-attributes |
contacts:write |
Create and update contacts and contact fields: POST /contacts, POST /contacts/batch, POST /contact-attributes, PATCH /contact-attributes/{key} |
contacts:erase |
Erase a contact for good (GDPR): DELETE /contacts/{reference}. Not included in any other permission |
events:write |
Report what people did in your product: POST /events |
emails:send |
Send transactional email: POST /sending/emails |
items:read |
Read a catalogue item you sent: GET /items/{externalId} |
items:write |
Write your catalogue: POST /items, POST /items/batch, DELETE /items/{externalId} |
orders:read |
Read orders: GET /commerce/orders |
A call the key has no permission for is answered 403.
Keys for testing
Section titled “Keys for testing”Keys made inside a sandbox start with azm_test_. They work exactly like live keys, against the sandbox’s own
data. No email sent from a sandbox reaches anyone: it lands in the sandbox’s inbox, and its delivery webhooks are
still sent, so the whole integration can be tried end to end.
Send the key
Section titled “Send the key”Put it in the Authorization header as a bearer token, on every call:
curl https://dashboard.azimea.com/api/v1/segments \ -H "Authorization: Bearer $AZIMEA_API_KEY"using var http = new HttpClient { BaseAddress = new Uri("https://dashboard.azimea.com/api/v1/") };http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AZIMEA_API_KEY"));
var json = await http.GetStringAsync("segments");const response = await fetch('https://dashboard.azimea.com/api/v1/segments', { headers: { Authorization: `Bearer ${process.env.AZIMEA_API_KEY}` },});const body = await response.json();$ch = curl_init('https://dashboard.azimea.com/api/v1/segments');curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AZIMEA_API_KEY')], CURLOPT_RETURNTRANSFER => true,]);$body = json_decode(curl_exec($ch), true);import os, requests
response = requests.get( "https://dashboard.azimea.com/api/v1/segments", headers={"Authorization": f"Bearer {os.environ['AZIMEA_API_KEY']}"},)body = response.json()Keep keys on your server. A key in a browser, a mobile app or a public repository can be used by anyone who finds it: revoke it and make a new one.
What every answer looks like
Section titled “What every answer looks like”Every answer from the API has the same envelope:
{ "isSuccess": false, "value": null, "status": 5, "errors": [{ "code": "audience.contact_email_required", "message": "An email address is needed to create a new contact.", "params": null }], "fieldErrors": { "email": [{ "code": "audience.contact_email_required", "message": "An email address is needed to create a new contact.", "params": null }] }}isSuccessand the HTTP status say whether it worked.valueholds the result when it did.errorslists what went wrong. Each error has acode— stable, meant for your code — and an Englishmessagefor logs;paramscarries the numbers in the message (a minimum, a maximum) when there are any. Every code is in the error codes catalog.fieldErrorsgroups validation errors by the request field that caused them.statusis an internal result number; rely on the HTTP status instead.
| HTTP status | Meaning |
|---|---|
200 / 201 |
Done; value holds the result |
400 |
The request is not valid: see errors[].code and fieldErrors |
401 |
No key, an unknown or revoked key, or an expired one |
403 |
The key lacks the permission this call needs |
404 |
What you asked for does not exist in your account |
409 |
It conflicts with what exists (for example, a contact field with that key already exists) |
429 |
Too many calls for this key (below) |
503 |
Azimea cannot answer right now; try again later |
401, 403 and 429 are answered before the call reaches Azimea’s logic: rely on the status code, not on a body.
Rate limit
Section titled “Rate limit”Each key may make 600 calls per minute, counted together across every public endpoint, in fixed one-minute
windows. Two systems with two keys never share a limit. A batch call (POST /contacts/batch, POST /items/batch,
POST /events with up to 100 events) counts as one call, so batches are the way to move volume.
Past the limit, the answer is 429 Too Many Requests with a Retry-After header: the number of seconds to wait before
the next call is allowed.
Retries
Section titled “Retries”Retry on a network error, a timeout, 429 (after Retry-After) or 503. Do not retry a 400, 401, 403 or 404
unchanged: the same request gets the same answer. Whether a retry is safe depends on the call:
| Call | Safe to retry? |
|---|---|
POST /events |
Yes. An event’s id is yours and unique in your account: a second delivery is counted as a duplicate, not recorded twice. |
POST /contacts, POST /contacts/batch |
Yes. They create or update the same contact (by id, then external id, then email). |
POST /items, POST /items/batch |
Yes. They create or update the item with that externalId. |
PATCH /contact-attributes/{key}, DELETE /items/{externalId} |
Yes. Doing it twice ends in the same state. |
POST /contact-attributes |
Yes: the second call is refused with 409, because the field already exists. |
POST /sending/emails |
Only with an Idempotency-Key header. With it, a second call with the same key returns the first message and sends nothing more. Without it, each call queues a new email, and a retry after a timeout can send the person the same email twice. See send your first email. |
GET calls |
Yes. |