Concepts

Keys and authentication

Four kinds of key, two modes, scopes and caps.

curl https://jusa.localhost.co.zw/dev/v1/credit -H "Authorization: Bearer jusa_sk_test_…"

Four kinds of key

KindLooks likeFor
Secret skjusa_sk_live_…Your server. Every scope.
Restricted rkjusa_rk_live_…A service that needs less: the scopes you choose. A survey relay needs only rewards:write.
Publishable pkjusa_pk_live_…A web page: the catalog, status, and claiming a reward link with its token. Narrow it with allowed_origins.
Agent akjusa_ak_live_…An AI agent over MCP: sends and direct rewards within a threshold and to known numbers; anything more waits for a person.

A key is shown once, when it is made; after that it is jusa_sk_live_…a1b2. Keys are stored hashed, so Jusa cannot show it again either. _test_ or _live_ sets the mode. Legacy atk_ keys (Authorization: Api-Key atk_…) keep working and show as "legacy secret key".

Test and live mode

The key decides: a test key reaches your sandbox twin, a live key your organisation. They share nothing: an id from one mode is 404 in the other. See Test mode.

Scopes

A secret key holds all of them. A restricted or agent key holds the ones it was given; a call it cannot make is 403 insufficient_scope. A key can never make a key with more than it has.

ScopeAllows
catalog:readProducts, data bundles and networks.
lookups:readPhone lookups, ZESA meter confirmation and quotes.
sends:readSends, batches, schedules, checkout sessions and disputes.
sends:writeCreate, cancel and resend sends; checkout sessions; disputes.
tokens:readZESA tokens in the clear; without it they are masked (•••• 1234). Off by default on restricted and agent keys.
batches:writeCreate, approve, cancel and retry batches.
schedules:writeCreate and change schedules and their runs; run one now.
credit:readJusa Credit: balances, transactions, top-ups, reservations, alerts and statements.
credit:topupStart a top-up (and, in test mode, add test credit).
recipients:readRecipients, groups and cost centres, and their spend.
recipients:writeCreate, change, erase and import recipients; groups and cost centres.
rewards:readPrograms, rewards, reward links, reviews, rules, activities, referrals and check-ins.
rewards:writeReward someone; mint and revoke links; record activities and referrals.
programs:manageCreate and change programs, fund and defund them, decide reviews, set rules.
webhooks:manageWebhook endpoints, the events feed and the delivery log.
reports:readReports and CSV exports.
workforce:adminThe Workforce tree: departments, plans, allocation, the ledger and journal.
family:writeReserved for OAuth user tokens (MyFamilyTime); not available yet.
account:manageKeys, the request and audit logs, test-mode reset; members and settings also need an owner or admin behind the key.

People and roles

On the dashboard your role decides: an owner or admin can do everything; a developer has keys, webhooks, logs and all of test mode, but spends nothing live; a viewer reads. Changing members or account settings with a key needs an owner or admin to have made that key, and to still be one. An invitation (POST /members) waits until the person signs in and accepts it on their dashboard; until then they are not a member and see nothing. A developer or viewer works through this API and the dashboard only: the Jusa site and app never act for them on your account.

Controls on a key

  • allowed_ips: IPs or CIDRs it works from (403 ip_not_allowed elsewhere).
  • expires_at: it stops on its own.
  • spend_caps per currency (per_send, daily, monthly) and per_recipient_daily_cap: checked with the send, under the balance lock (403 key_spend_cap_reached). GET /keys/{id} shows its usage.
  • allowed_products and allowed_networks (403 product_not_allowed).
  • Agent keys: approval_threshold over a rolling 24 hours and a recipient_allowlist. Over the threshold, or to a number it does not know, the answer is 202 with an agent_approval; nothing is written or charged until an owner or admin approves it with their payment PIN.

A secret key with any of these controls is refused on the legacy /api/v1, which cannot enforce them, and on Workforce writes (403 key_controls_not_enforced): send through /v1/batches and /v1/schedules instead. A key never loosens its own limits (caps, products, networks, IPs, expiry), and a key with spend controls makes only publishable keys and changes or rolls only itself (403 key_controls_escalation): a leaked capped key stays capped. A person on the dashboard, or a key without controls, manages them.

Rolling and revoking

POST /keys/{id}/roll {"grace_period": "24h"} makes a new key and keeps the old one working for 0, 1h, 24h or 7d; the two share their caps meanwhile. DELETE /keys/{id} revokes at once. A revoked, expired or unknown key all get the same 401 invalid_api_key. Jusa warns you (api_key.expiring) a week before a key expires.

Browsers

A secret, restricted or agent key sent from a web page (with an Origin header) is refused (403 key_not_for_browsers): a secret in a browser is a leaked secret. Publishable keys are the only ones a page may hold; the endpoints they may call answer CORS without cookies.

Versions

The path is /v1; the dated version is the Jusa-Version header. Your account is pinned to the version current when its first key was made, and every response says which version answered. Within a version changes only add. A retired version answers with Deprecation and Sunset headers until it is gone. Supported now: 2026-10-01. See the changelog.

Rate limits

Per key: live 50 requests a second and 3000 a minute; test 20 a second and 1200 a minute. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; over the limit is 429 rate_limited with Retry-After. Meter lookups have their own quota (30 a minute per key, and a daily quota per account tied to what you send), so they cannot be used to harvest names.

Request ids

Every response has a Jusa-Request-Id (req_…). Your request log keeps each request for 30 days, with bodies masked; quote the id to support.