Authentication
Every request to the Zingasuite API is authenticated with an API key sent as a
bearer token. The key identifies which entity you are acting on and which
operations you are allowed to perform — there is no entity id in the URL.
API keys
Keys are created in the console (Settings → API Keys). A key
looks like this:
zk_live_xxxxxxxxxxxxxxxxxxxxxxxx # production
zk_test_xxxxxxxxxxxxxxxxxxxxxxxx # sandbox / testing
The full key is shown once, at creation time. Store it securely — only a
short prefix (for example zk_live_abc1…) is retained for display afterwards,
and a lost key cannot be recovered (create a new one and revoke the old).
Sending the key
Send the key in the Authorization header on every request:
curl https://api-v1.zingasuite.com/papi/v1/links \
-H "Authorization: Bearer zk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
If the header is missing or malformed you receive 401 with
{"detail": "missing_bearer_token"} or {"detail": "invalid_token"}. An
unknown, revoked or expired key returns 401 invalid_or_revoked_key.
Scopes
Each key carries a set of scopes that limit what it can do. Grant the
minimum a key needs.
| Scope | Grants |
|---|---|
data:read |
Read any object your role can read |
data:write:* |
Create/update/delete any object your role can write |
billing:read |
Read billing information |
messaging:read |
Read messaging channels and message status (ZingaConnect) |
messaging:send |
Send messages (ZingaConnect) |
messaging:webhooks |
Manage delivery/inbound webhooks (ZingaConnect) |
These are the only scopes you can attach to a key; any other value is rejected
at creation with 400 scope_not_allowed:<scope>.
Scopes can be changed later — edit the key in the console (Settings → API Keys →
Edit). The token stays the same, so a key that turns out to need one more
scope does not have to be re-issued to your integration.
Notes:
- Writes are all-or-nothing at the key level.
data:write:*lets a key
write any object type — there is no per-object write scope you attach to the
key. To limit a key to, say, only links, create it under a user whose
role is restricted to links (see below); the key can never exceed that
role. - Messaging scopes are separate from data scopes on purpose:
data:write:*
does not grant the ability to send messages, and a messaging key needs no
data access. - Zingalinks uses ordinary data scopes. Everything under
/papi/v1/links/*
reads withdata:readand writes withdata:write:*. There are no
Links-specific scope names to grant. - A key can also be limited to specific apps. When you create a key you may
confine it to one or more apps (ZingaShop, ZingaConnect, Zingasolve,
Zingalinks, Zingalify). A key that is not scoped to this app is rejected with
403 app_not_in_key_scope:linksno matter which data scopes it carries. - Internally the API checks a granular scope per object type (for example it
requiresdata:write:linksto write one);data:write:*satisfies all of them.
A request that lacks the required access returns403with
{"detail": "insufficient_scope:<scope>"}, e.g.insufficient_scope:data:write:links.
Keys never exceed your permissions
A key's effective permissions are the intersection of the scopes you grant
it and the permissions of the user who created it:
effective permissions = (creator's role permissions) ∩ (key scopes)
A key can never do more than its creator's role allows. If your role loses
access to an object type — or your membership in the entity is revoked — keys
you created lose that access too (a revoked membership returns
403 membership_revoked). Beyond the scope check, each write is also validated
against the creator's object-level permissions and may return
403 permission_denied:<operation>:<model>.
Test vs live keys
Use zk_test_ keys while developing and zk_live_ keys in production. Both
authenticate the same way; keep them in separate configuration so test traffic
never touches live data.