← barua.tz

Authentication and scopes#

Every request carries a key: Authorization: Bearer barua_.... Make one under Settings → API, tick the scopes it needs, and copy it while it is on screen. Only a hash is stored, so a key you did not write down cannot be shown again, only revoked and replaced.

Scopes are resource:verb, and each endpoint needs exactly one. A key without it gets 403 insufficient_scope, whatever else it can do. Give each integration the least it needs, because a key on a server is a key that can leak: the receipts service should be able to send and nothing else.

emails:sendSend email, including sandbox sends.
emails:readRead the send log and what became of each email.
domains:readList domains and the DNS records they need.
domains:writeConnect, verify and remove domains.
keys:readList the account's API keys, revoked ones included.
keys:writeMint keys, for this account or a sub-account, and revoke them.
accounts:readRead this account and its sub-accounts.
accounts:writeOpen, suspend and delete sub-accounts.
webhooks:readList webhook endpoints and their deliveries.
webhooks:writeCreate, test and delete webhook endpoints.
suppressions:readRead the suppression list.
suppressions:writeAdd addresses to the suppression list and remove them.
database:readRead whether a database is connected and its state.
database:writeConnect, test and disconnect your own database.

Keys can mint keys, and a minted key can hold only scopes the minting key holds itself. That is what makes narrowing safe: a leaked send-only key cannot manufacture a wider one, and the widest key on your account is always one a person made in the dashboard. Revoking is immediate, and a key may revoke itself, which matters when the leaked key is the only one you have to hand.

GET /api/v1/keys

List API keys. Scope keys:read.

Every key of the account this request acts for, newest first, revoked ones included.

curl
curl https://barua.tz/api/v1/keys \
  -H "Authorization: Bearer barua_YOUR_KEY"
response 200
200, no body
422 invalid_requestThe body or query failed validation. The message names the first field that failed.

POST /api/v1/keys

Mint an API key. Scope keys:write.

The new key holds the scopes you list, or all of the minting key's scopes when you list none, and can never hold one the minting key lacks. With X-Barua-Account the key belongs to that sub-account, which is how you give a customer a key of their own. The response carries the full key and this is the only time anyone will see it.

curl
curl https://barua.tz/api/v1/keys \
  -H "Authorization: Bearer barua_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "receipts service",
    "scopes": [
      "emails:send",
      "emails:read"
    ]
  }'
response 201
201
{
  "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
  "name": "receipts service",
  "prefix": "barua_3f9a1c",
  "scopes": [
    "emails:send",
    "emails:read"
  ],
  "createdAt": "2026-09-24T10:05:00.000Z",
  "key": "barua_3f9a1c7e2b4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a3c5e7b9d"
}
400 invalid_jsonThe request body could not be parsed as JSON.
409 limit_reachedThe account already has 200 sub-accounts, or 50 live keys.
422 invalid_requestThe body or query failed validation. The message names the first field that failed.

DELETE /api/v1/keys/{id}

Revoke an API key. Scope keys:write.

Requests with the key stop working at once. It stays in the list with revokedAt set, so the send log keeps its author. A key may revoke itself, which matters when the leaked key is the only one you have to hand.

curl
curl -X DELETE https://barua.tz/api/v1/keys/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f \
  -H "Authorization: Bearer barua_YOUR_KEY"
response 204
204, no body
404 not_foundNo domain, sub-account or key with that id on this account.