Reference
API reference
Every endpoint, generated from the OpenAPI document.
Generated from the OpenAPI 3.1 document, which every response is tested
against. Base URL https://jusa.localhost.co.zw/dev/v1; every request carries Authorization: Bearer <key>
unless it says otherwise. Money is a decimal string, times are unix seconds, and every list pages with
limit, starting_after and ending_before.
- Account
- Keys
- Logs
- Catalog
- Lookups
- Status
- Sends
- Disputes
- Batches
- Schedules
- Recipients
- Programs
- Rewards
- Reward links
- Reviews
- Rules
- Claims
- Agent approvals
- MCP
- Jusa Credit
- Checkout
- Workforce
- Family
- Reports
- Webhooks
- Test mode
- Utility
Account
Your organisation, members and the dashboard home.
Retrieve the account
Returns: 200 Account. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/account \ -H "Authorization: Bearer jusa_sk_test_…"
Update the account
Owners and admins only (a key counts as its maker). Scope: account:manage.
| Body (AccountUpdate) | Type | About |
|---|---|---|
name | string | |
contact_email | string | |
support_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
statement_descriptor | string | |
batch_approval_threshold | object | |
agent_live_enabled | boolean | Dashboard only: a person turns it on. |
Returns: 200 Account. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/account \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"statement_descriptor": "ACME SURVEYS", "support_phone": "0772123456"}'
List pending invitations
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
Returns: 200 InviteList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/invites \ -H "Authorization: Bearer jusa_sk_test_…"
Retrieve an invitation
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
invite_id required | path | inv_… |
Returns: 200 Invite. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/invites/{invite_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Revoke an invitation
Owners and admins only. Scope: account:manage.
| Parameter | In | About |
|---|---|---|
invite_id required | path | inv_… |
Returns: 200 Invite. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/invites/{invite_id} \
-H "Authorization: Bearer jusa_sk_test_…"
List members
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
role | query | Only members with this role. |
Returns: 200 MemberList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/members \ -H "Authorization: Bearer jusa_sk_test_…"
Invite a member
An invitation, pending until the person signs in and accepts it from their dashboard (it lapses after 14 days). Until then they are not a member and see nothing of the account. Scope: account:manage.
| Body (MemberInvite) | Type | About |
|---|---|---|
phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
email | string | |
role | "owner" | "admin" | "developer" | "viewer" |
Returns: 201 Invite. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/members \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"email": "dev@example.com", "role": "developer"}'
Retrieve a member
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
member_id required | path | mem_… |
Returns: 200 Member. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/members/{member_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Change a member's role
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
member_id required | path | mem_… |
| Body (MemberUpdate) | Type | About |
|---|---|---|
role required | "owner" | "admin" | "developer" | "viewer" |
Returns: 200 Member. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/members/{member_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"role": "owner"}'
Remove a member
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
member_id required | path | mem_… |
Returns: 200 Member. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/members/{member_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Your organisations
Dashboard only (a signed-in person): each organisation you belong to and whether it is a developer organisation.
Returns: 200 OrganisationList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/organisations \ -H "Authorization: Bearer jusa_sk_test_…"
Create a developer organisation
Dashboard only (a signed-in person): makes the live org and its test-mode twin.
| Body (OrganisationCreate) | Type | About |
|---|---|---|
name | string |
Returns: 201 Account. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/organisations \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Invitations waiting for you
Dashboard only (a signed-in person).
Returns: 200 InviteList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/organisations/invites \ -H "Authorization: Bearer jusa_sk_test_…"
Accept an invitation
Dashboard only: the person invited, signed in.
| Parameter | In | About |
|---|---|---|
invite_id required | path | inv_… |
Returns: 200 Organisation. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/organisations/invites/{invite_id}/accept \
-H "Authorization: Bearer jusa_sk_test_…"
Decline an invitation
Dashboard only: the person invited, signed in.
| Parameter | In | About |
|---|---|---|
invite_id required | path | inv_… |
Returns: 200 Invite. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/organisations/invites/{invite_id}/decline \
-H "Authorization: Bearer jusa_sk_test_…"
The dashboard home
Jusa Credit with runway, sends today, success by network, webhook health and what needs attention. Sections the caller cannot read are null.
Returns: 200 Overview. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/overview \ -H "Authorization: Bearer jusa_sk_test_…"
Keys
API keys: sk secret, rk restricted, pk publishable, ak agent.
List API keys
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
Returns: 200 ApiKeyList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/keys \ -H "Authorization: Bearer jusa_sk_test_…"
Create an API key
The key is in secret, once. The first live key needs accept_terms: true (the API Terms, the Acceptable Use policy and the DPA). No Idempotency-Key: a replay would have to show the secret again. Scope: account:manage.
| Body (KeyCreate) | Type | About |
|---|---|---|
kind | "sk" | "rk" | "pk" | "ak" | |
label | string | |
scopes | array of "catalog:read" | "lookups:read" | "sends:read" | "sends:write" | "tokens:read" | "batches:write" | "schedules:write" | "credit:read" | "credit:topup" | "recipients:read" | "recipients:write" | "rewards:read" | "rewards:write" | "programs:manage" | "webhooks:manage" | "reports:read" | "workforce:admin" | "family:write" | "account:manage" | |
allowed_ips | array of string | |
expires_at | integer or string | Unix seconds or an ISO 8601 time. |
accept_terms | boolean | The first live key: accept the API Terms, AUP and DPA. |
allowed_products | array of string | |
allowed_networks | array of string | |
spend_caps | object | |
per_recipient_daily_cap | object | |
approval_threshold | object | |
recipient_allowlist | array of string | |
allowed_origins | array of string |
Returns: 201 ApiKey. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/keys \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"kind": "rk", "label": "survey relay", "scopes": ["rewards:write"]}'
Retrieve an API key
With usage: what it has spent against its caps. Scope: account:manage.
| Parameter | In | About |
|---|---|---|
key_id required | path | key_… |
Returns: 200 ApiKey. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/keys/{key_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Update an API key
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
key_id required | path | key_… |
| Body (KeyUpdate) | Type | About |
|---|---|---|
label | string | |
scopes | array of "catalog:read" | "lookups:read" | "sends:read" | "sends:write" | "tokens:read" | "batches:write" | "schedules:write" | "credit:read" | "credit:topup" | "recipients:read" | "recipients:write" | "rewards:read" | "rewards:write" | "programs:manage" | "webhooks:manage" | "reports:read" | "workforce:admin" | "family:write" | "account:manage" | |
allowed_ips | array of string | |
expires_at | integer or string | Unix seconds or an ISO 8601 time. |
allowed_products | array of string | |
allowed_networks | array of string | |
spend_caps | object | |
per_recipient_daily_cap | object | |
approval_threshold | object | |
recipient_allowlist | array of string | |
allowed_origins | array of string |
Returns: 200 ApiKey. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/keys/{key_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Revoke an API key
Takes effect at once. Scope: account:manage.
| Parameter | In | About |
|---|---|---|
key_id required | path | key_… |
Returns: 200 ApiKey. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/keys/{key_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Roll an API key
A new key in secret; the old one (in rolled) works for the grace period. Scope: account:manage.
| Parameter | In | About |
|---|---|---|
key_id required | path | key_… |
| Body (KeyRoll) | Type | About |
|---|---|---|
grace_period | "0" | "1h" | "24h" | "7d" | 0 | How long the old key keeps working. |
Returns: 201 ApiKey. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/keys/{key_id}/roll \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Logs
The request log and the audit log.
Recent account activity
What happened lately (events: sends, rewards, top-ups, batches …), newest first, and, with account:manage, who changed what (the audit log). Pages back with starting_after. Scope: reports:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
Returns: 200 AccountActivityList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/activity \ -H "Authorization: Bearer jusa_sk_test_…"
List audit events
Who (a key, a member, or Jusa staff viewing as you) changed what. Scope: account:manage.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
action | query | Only this action (send or reward; for the audit log, e.g. api_key.created). |
object | query | Events about this object, e.g. snd_… |
Returns: 200 AuditEventList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/audit-log \ -H "Authorization: Bearer jusa_sk_test_…"
List API requests
Kept 30 days. status is a code (402) or a class (4xx). Scope: account:manage.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
api_key | query | key_… |
status | query | Only objects in this status. |
path | query | Paths starting with this. |
method | query | GET, POST, … |
idempotency_key | query | Requests sent with this key. |
Returns: 200 RequestLogList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/requests \ -H "Authorization: Bearer jusa_sk_test_…"
Retrieve an API request
With the masked request and response bodies. Scope: account:manage.
| Parameter | In | About |
|---|---|---|
request_id required | path | req_… |
Returns: 200 RequestLog. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/requests/{request_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Catalog
Products and networks. Face value, always.
List networks
Scope: catalog:read. Publishable keys may call it (from a browser).
Returns: 200 NetworkList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/networks \ -H "Authorization: Bearer jusa_sk_test_…"
List products
Face value, always: there is no pricing endpoint. Scope: catalog:read. Publishable keys may call it (from a browser).
| Parameter | In | About |
|---|---|---|
type | query | Only this type. |
network | query | Only this network. |
currency | query | Only this currency. |
Returns: 200 ProductList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/products \ -H "Authorization: Bearer jusa_sk_test_…"
Retrieve a product
Scope: catalog:read. Publishable keys may call it (from a browser).
| Parameter | In | About |
|---|---|---|
product_id required | path | A product slug. |
Returns: 200 Product. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/products/{product_id} \
-H "Authorization: Bearer jusa_sk_test_…"
List a data product's bundles
Test mode has a simulated list; live data bundles ship in P2. Scope: catalog:read. Publishable keys may call it (from a browser).
| Parameter | In | About |
|---|---|---|
product_id required | path | A product slug. |
Returns: 200 BundleList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/products/{product_id}/bundles \
-H "Authorization: Bearer jusa_sk_test_…"
Lookups
Phone lookups, ZESA meter confirmation and quotes.
Look up a phone number
Its network and E.164 form. No provider is asked. Scope: lookups:read.
| Parameter | In | About |
|---|---|---|
phone | query | A phone number, any Zimbabwean format. |
currency | query | Only this currency. |
Returns: 200 PhoneLookup. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/lookups/phone \ -H "Authorization: Bearer jusa_sk_test_…"
Confirm a ZESA meter
The name is masked and the address is the suburb. Rationed: 30 a minute per key and a daily quota per account. 503 zesa_unavailable when ZESA is down. Scope: lookups:read.
| Parameter | In | About |
|---|---|---|
number required | path | An 11-digit meter number. |
currency | query | Only this currency. |
Returns: 200 Meter. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/meters/{number} \
-H "Authorization: Bearer jusa_sk_test_…"
Quote a send
you_pay is always the face value. For ZESA: the units and band now and after the reset, and a strictly confirmed meter that a send carries by passing the quote. Scope: lookups:read.
| Body (QuoteCreate) | Type | About |
|---|---|---|
product | string | |
amount | string or number | A decimal amount, e.g. "5.00". |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
target | string | |
bundle_id | string |
Returns: 201 Quote. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/quotes \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"product": "zesa-usd", "amount": "10.00", "target": "37261502217"}'
Retrieve a quote
Scope: lookups:read.
| Parameter | In | About |
|---|---|---|
quote_id required | path | quo_… |
Returns: 200 Quote. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/quotes/{quote_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Status
ZESA and network availability.
ZESA and network status
Publishable keys may call it (from a browser).
Returns: 200 Status. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/status \ -H "Authorization: Bearer jusa_sk_test_…"
ZESA uptime history
The last 7 days by default. Publishable keys may call it (from a browser).
| Parameter | In | About |
|---|---|---|
from | query | The start (inclusive). |
to | query | The end (inclusive). |
Returns: 200 StatusHistory. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/status/history \ -H "Authorization: Bearer jusa_sk_test_…"
The purchases behind Rate Watch
Anonymised and coarsened: no meter, no order. Scope: lookups:read.
| Parameter | In | About |
|---|---|---|
currency | query | Only this currency. |
days | query | How many days back (90 by default). |
limit | query | Rows per page. |
Returns: 200 ZesaObservations. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/zesa/observations \ -H "Authorization: Bearer jusa_sk_test_…"
What an amount buys on ZESA
Units now versus after the reset on the 1st; with meter, from where that meter stands this month (a meter is rationed like a lookup). POST /v1/quotes also confirms the meter. Scope: lookups:read.
| Parameter | In | About |
|---|---|---|
currency | query | Only this currency. |
amount | query | A decimal amount, e.g. 10.00. |
meter | query | A meter number. |
Returns: 200 ZesaRate. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/zesa/quote \ -H "Authorization: Bearer jusa_sk_test_…"
ZESA Rate Watch
ZETDC's published bands, what real purchases show a kWh costs, and the cheapest-units rule (the bands reset on the 1st). Public data, the same in test and live mode. Scope: catalog:read.
| Parameter | In | About |
|---|---|---|
currency | query | Only this currency. |
Returns: 200 ZesaRate. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/zesa/rate \ -H "Authorization: Bearer jusa_sk_test_…"
ZESA rate series
The sparkline of the observed rate and where it changed. Scope: catalog:read.
| Parameter | In | About |
|---|---|---|
currency | query | Only this currency. |
Returns: 200 ZesaSeries. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/zesa/series \ -H "Authorization: Bearer jusa_sk_test_…"
Sends
Airtime, data and ZESA tokens, one at a time. Async: 202, then webhooks.
List sends
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
type | query | Only this type. |
target | query | A phone (any format) or meter. |
client_reference | query | Your own reference. |
currency | query | Only this currency. |
needs_attention | query | unknown over 1h, requires_review, or queued past half its max_hold. |
recipient | query | rcp_… or ext:<external_id>. |
program | query | prg_… |
batch | query | bch_… |
cost_center | query | cc_… (Workforce: a department's id, or none for sends booked to none). |
api_key | query | key_… |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
expand[] | query | Inline a related object in place of its id (repeat for more): recipient, program or batch on a send; recipient, program or send on a reward. |
Returns: 200 SendList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/sends \ -H "Authorization: Bearer jusa_sk_test_…"
Create a send
Always async: the send is written and charged, then dispatched; webhooks and GET tell the rest. validate_only: true runs every check and writes nothing (200, no Idempotency-Key needed). A client_reference already used returns that send (200). An agent key over its threshold or to a new target gets 202 with an agent_approval instead. Scope: sends:write. Moves money: an Idempotency-Key is required.
| Body (SendCreate) | Type | About |
|---|---|---|
type | "airtime" | "data" | "zesa" | Inferred from product or target when left out. |
product | string | |
target | string | A phone (any Zimbabwean format) or an 11-digit meter. |
amount | string or number | A decimal amount, e.g. "5.00". |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
bundle_id | string | Data: the bundle to send. |
token_phone | string | ZESA: required. The token is texted here. |
token_email | string | |
notify_number | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
confirm | "strict" | "best_effort" | ZESA meter confirmation. strict (default) refuses when ZESA cannot answer. |
on_zesa_down | "queue" | "fail" | |
max_hold | string or integer | Seconds, or "30m", "24h", "3d". |
recipient | string or object | rcp_… or ext:<external_id>. |
program | string | prg_…: attribution only (reports, filters). A send never draws on or is capped by a program's budget: pay a program's budget out with POST /v1/rewards. |
cost_center | string | |
sms_message | string | Merge fields: {sender}, {name}, {first_name}, {amount}, {currency}, {date}… Left out, airtime and data carry a default that names you (statement_descriptor). |
sms_locale | "en" | "sn" | "nd" | The default SMS's language: English, Shona or Ndebele. |
scheduled_for | integer or string | A later send, 90 seconds to 180 days ahead: charged now, status scheduled until then, cancellable until it goes. |
client_reference | string | |
quote | string | quo_…: carries a strict meter confirmation. |
validate_only | boolean | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
reason | string | Agent keys: shown to the person asked to approve. |
Returns: 200 Send or SendValidation · 202 Send or AgentApproval. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/sends \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"product": "airtime-usd", "target": "0772123456", "amount": "1.00", "client_reference": "order-8812"}'
Retrieve a send
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
send_id required | path | snd_… (or the bare hex of a legacy order). |
expand[] | query | Inline a related object in place of its id (repeat for more): recipient, program or batch on a send; recipient, program or send on a reward. |
Returns: 200 Send. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/sends/{send_id} \
-H "Authorization: Bearer jusa_sk_test_…"
A send's attempts
When it went to the provider, and each time Hot Recharge was asked what it recorded (outcomes only). Scope: sends:read.
| Parameter | In | About |
|---|---|---|
send_id required | path | snd_… (or the bare hex of a legacy order). |
Returns: 200 SendAttemptList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/sends/{send_id}/attempts \
-H "Authorization: Bearer jusa_sk_test_…"
Cancel a send not yet sent
A scheduled send, or one still waiting to go: the credit comes back (cancelled_credited). 409 already_sent once it has gone. Scope: sends:write.
| Parameter | In | About |
|---|---|---|
send_id required | path | snd_… (or the bare hex of a legacy order). |
Returns: 200 Send. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/sends/{send_id}/cancel \
-H "Authorization: Bearer jusa_sk_test_…"
Change a ZESA send's meter
Only while it has not gone out (queued while ZESA is down, or scheduled). The new meter is confirmed now when ZESA answers, else before the send goes out. Audited, and send.meter_changed goes to every endpoint. Scope: sends:write.
| Parameter | In | About |
|---|---|---|
send_id required | path | snd_… (or the bare hex of a legacy order). |
| Body (SendMeterChange) | Type | About |
|---|---|---|
meter required | string | An 11-digit ZESA prepaid meter number. |
Returns: 200 Send. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/sends/{send_id}/meter \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"meter": "37261502217"}'
A delivered send's receipt
JSON, or ?format=pdf for the same receipt as a PDF. The token is masked without tokens:read. Scope: sends:read.
| Parameter | In | About |
|---|---|---|
send_id required | path | snd_… (or the bare hex of a legacy order). |
format | query | json (default); csv for reports and the Workforce files (the ledger also takes csv-json, the file inside JSON); pdf for a receipt. |
Returns: 200 Receipt. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/sends/{send_id}/receipt \
-H "Authorization: Bearer jusa_sk_test_…"
Text a ZESA token again
Only to the phone and email it went to first. Rate-limited. Scope: sends:write.
| Parameter | In | About |
|---|---|---|
send_id required | path | snd_… (or the bare hex of a legacy order). |
Returns: 202 TokenResend. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/sends/{send_id}/resend-token \
-H "Authorization: Bearer jusa_sk_test_…"
Retry a failed send
A terminal failed send (failed_credited or failed) only, sent again as a new send charged now, with retry_of. Hot Recharge is asked about the first send before anything is resent: delivered after all is 409 send_delivered_late, no answer is 503 provider_unavailable. 409 send_not_retryable from any other state, for a card checkout's send, and for a send already retried. Scope: sends:write. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
send_id required | path | snd_… (or the bare hex of a legacy order). |
Returns: 202 Send. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/sends/{send_id}/retry \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)"
Disputes
A send your user says did not arrive.
List disputes
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
send | query | snd_… |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 DisputeList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/disputes \ -H "Authorization: Bearer jusa_sk_test_…"
Open a dispute
Opens a staff case. A delivered send is never reversed. Scope: sends:write.
| Body (DisputeCreate) | Type | About |
|---|---|---|
send required | string | |
reason required | "token_not_received" | "wrong_number" | "not_delivered" | "other" | |
evidence | string | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 201 Dispute. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/disputes \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"send": "snd_\u2026", "reason": "token_not_received", "evidence": "Customer says no SMS came."}'
Retrieve a dispute
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
dispute_id required | path | dsp_… |
Returns: 200 Dispute. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/disputes/{dispute_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Batches
Up to 10,000 sends at once.
List batches
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 BatchList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/batches \ -H "Authorization: Bearer jusa_sk_test_…"
Create a batch
Up to 10,000 items (JSON or CSV). Credit is reserved at once by default; above the account's approval threshold it waits for a second person. Scope: batches:write. Moves money: an Idempotency-Key is required.
| Body (BatchCreate) | Type | About |
|---|---|---|
items | array of object | |
csv | string | Or a CSV with a header row. |
title | string | |
sms_message | string | |
cost_center | string | |
on_invalid | "reject" | "skip" | |
reserve_credit | boolean | |
on_insufficient | "stop" | "continue" | |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 202 Batch. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/batches \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"title": "October allowance", "items": [{"target": "0772123456", "amount": "5.00"}, {"target": "0712345678", "amount": "5.00"}]}'
Retrieve a batch
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
batch_id required | path | bch_… |
Returns: 200 Batch. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/batches/{batch_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Approve a batch
By an owner or admin other than the batch's maker. Scope: batches:write. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
batch_id required | path | bch_… |
Returns: 200 Batch. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/batches/{batch_id}/approve \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)"
Cancel a batch
Items not yet sent are cancelled; unspent reserved credit is released. Scope: batches:write.
| Parameter | In | About |
|---|---|---|
batch_id required | path | bch_… |
Returns: 200 Batch. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/batches/{batch_id}/cancel \
-H "Authorization: Bearer jusa_sk_test_…"
List a batch's items
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
batch_id required | path | bch_… |
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 BatchItemList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/batches/{batch_id}/items \
-H "Authorization: Bearer jusa_sk_test_…"
Download a batch's results
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
batch_id required | path | bch_… |
Returns: 200 CSV. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/batches/{batch_id}/results.csv \
-H "Authorization: Bearer jusa_sk_test_…"
Retry a batch's failed items
A new batch, charged again. Each failed item is retried once. Scope: batches:write. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
batch_id required | path | bch_… |
| Body (BatchRetryFailed) | Type | About |
|---|---|---|
reserve_credit | boolean |
Returns: 202 Batch. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/batches/{batch_id}/retry-failed \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'
Preview a batch
Per-line validity, totals, whether the credit and the provider float suffice. Scope: batches:write.
| Body (BatchCreate) | Type | About |
|---|---|---|
items | array of object | |
csv | string | Or a CSV with a header row. |
title | string | |
sms_message | string | |
cost_center | string | |
on_invalid | "reject" | "skip" | |
reserve_credit | boolean | |
on_insufficient | "stop" | "continue" | |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 200 BatchPreview. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/batches/preview \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"title": "October allowance", "items": [{"target": "0772123456", "amount": "5.00"}, {"target": "0712345678", "amount": "5.00"}]}'
Schedules
Recurring sends.
List schedules
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
active | query | Active or not. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 ScheduleList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/schedules \ -H "Authorization: Bearer jusa_sk_test_…"
Create a schedule
Scope: schedules:write.
| Body (ScheduleCreate) | Type | About |
|---|---|---|
name | string | |
cadence | "daily" | "weekly" | "biweekly" | "monthly" | "yearly" | "payday" | "last_day" | |
hour | integer | |
minute | integer | |
day_of_week | integer | |
day_of_month | integer | |
month_of_year | integer | |
anchor_date | string | YYYY-MM-DD, Harare time. |
group | string | |
recipients | array of string | |
product | string | |
type | "airtime" | "data" | "zesa" | |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
amount | string or number | A decimal amount, e.g. "5.00". |
sms_message | string | |
custom_sms | string | |
cost_center | string | |
meter | string | An 11-digit ZESA prepaid meter number. |
token_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
active | boolean | |
precheck_credit | boolean | |
on_insufficient | "skip" | "retry_after_topup" | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 201 Schedule. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/schedules \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Monthly allowance", "cadence": "monthly", "day_of_month": 1, "hour": 8, "minute": 0, "group": "grp_\u2026", "product": "airtime-usd", "amount": "5.00"}'
Retrieve a schedule
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
Returns: 200 Schedule. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Update a schedule
Scope: schedules:write.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
| Body (ScheduleCreate) | Type | About |
|---|---|---|
name | string | |
cadence | "daily" | "weekly" | "biweekly" | "monthly" | "yearly" | "payday" | "last_day" | |
hour | integer | |
minute | integer | |
day_of_week | integer | |
day_of_month | integer | |
month_of_year | integer | |
anchor_date | string | YYYY-MM-DD, Harare time. |
group | string | |
recipients | array of string | |
product | string | |
type | "airtime" | "data" | "zesa" | |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
amount | string or number | A decimal amount, e.g. "5.00". |
sms_message | string | |
custom_sms | string | |
cost_center | string | |
meter | string | An 11-digit ZESA prepaid meter number. |
token_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
active | boolean | |
precheck_credit | boolean | |
on_insufficient | "skip" | "retry_after_topup" | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 200 Schedule. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Monthly allowance", "cadence": "monthly", "day_of_month": 1, "hour": 8, "minute": 0, "group": "grp_\u2026", "product": "airtime-usd", "amount": "5.00"}'
Delete a schedule
Scope: schedules:write.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
Returns: 200 DeletedSchedule. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id} \
-H "Authorization: Bearer jusa_sk_test_…"
List upcoming runs
From today to 60 days on by default (YYYY-MM-DD). Scope: sends:read.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
from | query | The start (inclusive). |
to | query | The end (inclusive). |
Returns: 200 ScheduleOccurrenceList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/occurrences \
-H "Authorization: Bearer jusa_sk_test_…"
Retrieve one run
ran with its batch once it has gone out. Scope: sends:read.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
day required | path | The run's date, YYYY-MM-DD. |
Returns: 200 ScheduleOccurrence. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/occurrences/{day} \
-H "Authorization: Bearer jusa_sk_test_…"
Change one run's amount
Scope: schedules:write.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
day required | path | The run's date, YYYY-MM-DD. |
| Body (OccurrenceUpdate) | Type | About |
|---|---|---|
amount_override | string or number | A decimal amount, e.g. "5.00". |
note | string |
Returns: 200 ScheduleOccurrence. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/occurrences/{day} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Combine one run into another
The other run (this schedule's on into, or schedule's) sends both amounts in one recharge per person, and this one is combined. Same people, currency and cost centre only. Reset undoes it. Scope: schedules:write.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
day required | path | The run's date, YYYY-MM-DD. |
| Body (OccurrenceCombine) | Type | About |
|---|---|---|
into required | string | YYYY-MM-DD, Harare time. |
schedule | string | sch_…; this schedule by default. |
Returns: 200 ScheduleOccurrence. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/occurrences/{day}/combine \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"into": "2026-11-02"}'
Move one run
Scope: schedules:write.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
day required | path | The run's date, YYYY-MM-DD. |
| Body (OccurrenceMove) | Type | About |
|---|---|---|
to required | string | YYYY-MM-DD, Harare time. |
Returns: 200 ScheduleOccurrence. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/occurrences/{day}/move \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"to": "string"}'
Undo a skip, move or amount change
Scope: schedules:write.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
day required | path | The run's date, YYYY-MM-DD. |
Returns: 200 ScheduleOccurrence. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/occurrences/{day}/reset \
-H "Authorization: Bearer jusa_sk_test_…"
Skip one run
Scope: schedules:write.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
day required | path | The run's date, YYYY-MM-DD. |
Returns: 200 ScheduleOccurrence. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/occurrences/{day}/skip \
-H "Authorization: Bearer jusa_sk_test_…"
Run a schedule now
A run is a batch: it reserves its total and may wait for approval. Scope: schedules:write. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
| Body (ScheduleRunNow) | Type | About |
|---|---|---|
again | boolean | Run even if it already ran today. |
Returns: 202 Batch. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/run-now \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'
Preview running now
Scope: schedules:write.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
Returns: 200 ScheduleRunPreview. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/schedules/{schedule_id}/run-now/preview \
-H "Authorization: Bearer jusa_sk_test_…"
Recipients
The people you send to, their groups and cost centres.
List cost centres
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
is_active | query | Active or not. |
Returns: 200 CostCenterList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/cost-centers \ -H "Authorization: Bearer jusa_sk_test_…"
Create a cost centre
Scope: recipients:write.
| Body (CostCenterWrite) | Type | About |
|---|---|---|
name | string | |
gl_code | string | |
monthly_budget | string or number | A decimal amount, e.g. "5.00". |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
is_active | boolean |
Returns: 201 CostCenter. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/cost-centers \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Marketing", "gl_code": "6100", "monthly_budget": "250.00"}'
Retrieve a cost centre
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
cost_center_id required | path | cc_… |
Returns: 200 CostCenter. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/cost-centers/{cost_center_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Update a cost centre
Scope: recipients:write.
| Parameter | In | About |
|---|---|---|
cost_center_id required | path | cc_… |
| Body (CostCenterWrite) | Type | About |
|---|---|---|
name | string | |
gl_code | string | |
monthly_budget | string or number | A decimal amount, e.g. "5.00". |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
is_active | boolean |
Returns: 200 CostCenter. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/cost-centers/{cost_center_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Marketing", "gl_code": "6100", "monthly_budget": "250.00"}'
A cost centre's spend
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
cost_center_id required | path | cc_… |
from | query | The start (inclusive). |
to | query | The end (inclusive). |
Returns: 200 CostCenterSpend. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/cost-centers/{cost_center_id}/spend \
-H "Authorization: Bearer jusa_sk_test_…"
List groups
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
Returns: 200 GroupList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/groups \ -H "Authorization: Bearer jusa_sk_test_…"
Create a group
Scope: recipients:write.
| Body (GroupWrite) | Type | About |
|---|---|---|
name | string | |
description | string | |
cost_center | string | |
members | array of string |
Returns: 201 Group. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/groups \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Field team"}'
Retrieve a group
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
group_id required | path | grp_… |
Returns: 200 Group. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/groups/{group_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Update a group
Scope: recipients:write.
| Parameter | In | About |
|---|---|---|
group_id required | path | grp_… |
| Body (GroupWrite) | Type | About |
|---|---|---|
name | string | |
description | string | |
cost_center | string | |
members | array of string |
Returns: 200 Group. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/groups/{group_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Field team"}'
Delete a group
Scope: recipients:write.
| Parameter | In | About |
|---|---|---|
group_id required | path | grp_… |
Returns: 200 DeletedGroup. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/groups/{group_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Add recipients to a group
Scope: recipients:write.
| Parameter | In | About |
|---|---|---|
group_id required | path | grp_… |
| Body (GroupMembers) | Type | About |
|---|---|---|
recipients required | array of string |
Returns: 200 Group. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/groups/{group_id}/members \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"recipients": []}'
Take recipients out of a group
Scope: recipients:write.
| Parameter | In | About |
|---|---|---|
group_id required | path | grp_… |
| Body (GroupMembers) | Type | About |
|---|---|---|
recipients required | array of string |
Returns: 200 Group. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/groups/{group_id}/members \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"recipients": []}'
List recipients
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
external_id | query | Your id for the person. |
phone | query | A phone number, any Zimbabwean format. |
meter | query | A meter number. |
group | query | grp_… |
cost_center | query | cc_… (Workforce: a department's id, or none for sends booked to none). |
blocked | query | Blocked or not. |
is_active | query | Active or not. |
erased | query | Erased or not. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 RecipientList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/recipients \ -H "Authorization: Bearer jusa_sk_test_…"
Create or update a recipient
Upserts on external_id, then phone: 201 when made, 200 when updated. Scope: recipients:write.
| Body (RecipientWrite) | Type | About |
|---|---|---|
external_id | string | |
phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
meter | string | An 11-digit ZESA prepaid meter number. |
token_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
name | string | |
payroll_id | string | |
cost_center | string | |
notes | string | |
is_active | boolean | |
is_pinned | boolean | |
network_hint | string | |
daily_cap | string or number | A decimal amount, e.g. "5.00". |
weekly_cap | string or number | A decimal amount, e.g. "5.00". |
monthly_cap | string or number | A decimal amount, e.g. "5.00". |
lifetime_cap | string or number | A decimal amount, e.g. "5.00". |
blocked | boolean | |
blocked_reason | string | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 200 Recipient · 201 Recipient. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/recipients \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"external_id": "user_123", "name": "Tendai Moyo", "phone": "0772123456"}'
Retrieve a recipient
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
Returns: 200 Recipient. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/recipients/{recipient_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Update a recipient
Scope: recipients:write.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
| Body (RecipientWrite) | Type | About |
|---|---|---|
external_id | string | |
phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
meter | string | An 11-digit ZESA prepaid meter number. |
token_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
name | string | |
payroll_id | string | |
cost_center | string | |
notes | string | |
is_active | boolean | |
is_pinned | boolean | |
network_hint | string | |
daily_cap | string or number | A decimal amount, e.g. "5.00". |
weekly_cap | string or number | A decimal amount, e.g. "5.00". |
monthly_cap | string or number | A decimal amount, e.g. "5.00". |
lifetime_cap | string or number | A decimal amount, e.g. "5.00". |
blocked | boolean | |
blocked_reason | string | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 200 Recipient. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/recipients/{recipient_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"external_id": "user_123", "name": "Tendai Moyo", "phone": "0772123456"}'
Deactivate or erase a recipient
Deactivates. With erase=true, erases their data and keeps the history masked; refused (409 erasure_deferred) while a send is in flight or a schedule is active. Scope: recipients:write.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
erase | query | true: erase the person's data (deferred while anything is in flight). |
Returns: 200 DeletedRecipient or Recipient. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/recipients/{recipient_id} \
-H "Authorization: Bearer jusa_sk_test_…"
A recipient's earnings pots
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
Returns: 200 RecipientBalance. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/recipients/{recipient_id}/balance \
-H "Authorization: Bearer jusa_sk_test_…"
List a recipient's sends
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 SendList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/recipients/{recipient_id}/sends \
-H "Authorization: Bearer jusa_sk_test_…"
A recipient's spend against their caps
Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
Returns: 200 RecipientSpend. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/recipients/{recipient_id}/spend \
-H "Authorization: Bearer jusa_sk_test_…"
A person's statement
Everything sent to them in a period (this month by default; YYYY-MM-DD, inclusive), totalled by currency and outcome, with each send (up to 500). Scope: recipients:read.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
from | query | The start (inclusive). |
to | query | The end (inclusive). |
Returns: 200 RecipientStatement. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/recipients/{recipient_id}/statement \
-H "Authorization: Bearer jusa_sk_test_…"
Import recipients
Up to 5,000, as JSON rows or CSV. Scope: recipients:write.
| Body (RecipientBulk) | Type | About |
|---|---|---|
recipients | array of any | Recipient rows (as POST /recipients); a bad row is reported in its place. |
csv | string |
Returns: 200 RecipientImport. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/recipients/bulk \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Programs
Reward programs: budget, caps, fraud rules.
List programs
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 ProgramList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/programs \ -H "Authorization: Bearer jusa_sk_test_…"
Create a program
The completions HMAC secret is shown once, here. A reserve program sets its whole budget aside (402 when the credit is short). Scope: programs:manage.
| Body (ProgramCreate) | Type | About |
|---|---|---|
name required | string | |
description | string | |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
default_amount required | string or number | A decimal amount, e.g. "5.00". |
budget required | string or number | A decimal amount, e.g. "5.00". |
funding | "draw" | "reserve" | |
choices | array of "airtime" | "data" | "zesa" | |
status | "draft" | "active" | |
sources | array of "api" | "completions" | "links" | |
one_per_phone | boolean | |
per_recipient | object | |
require_network | string | |
throttle_per_hour | integer | |
min_account_age | string or integer | Seconds, or "30m", "24h", "3d". |
hold | object | |
on_zesa_down | "queue" | "fail" | |
max_hold | string or integer | Seconds, or "30m", "24h", "3d". |
sms_message | string | |
branding | object | |
lawful_basis | string | |
sensitive_data | boolean | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
geofence | object |
Returns: 201 Program. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Household survey Q4", "currency": "USD", "default_amount": "0.50", "budget": "500.00", "funding": "reserve", "per_recipient": {"lifetime": 1}}'
Retrieve a program
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
Returns: 200 Program. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/programs/{program_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Update a program
Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
| Body (ProgramUpdate) | Type | About |
|---|---|---|
name | string | |
description | string | |
default_amount | string or number | A decimal amount, e.g. "5.00". |
budget | string or number | A decimal amount, e.g. "5.00". |
choices | array of "airtime" | "data" | "zesa" | |
sources | array of "api" | "completions" | "links" | |
one_per_phone | boolean | |
per_recipient | object | |
require_network | string | |
throttle_per_hour | integer | |
min_account_age | string or integer | Seconds, or "30m", "24h", "3d". |
hold | object | |
on_zesa_down | "queue" | "fail" | |
max_hold | string or integer | Seconds, or "30m", "24h", "3d". |
sms_message | string | |
branding | object | |
lawful_basis | string | |
sensitive_data | boolean | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
geofence | object |
Returns: 200 Program. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/programs/{program_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Close a program
The same as POST …/close. Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
Returns: 200 Program. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/programs/{program_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Activate a draft program
Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
Returns: 200 Program. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/activate \
-H "Authorization: Bearer jusa_sk_test_…"
Close a program
Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
Returns: 200 Program. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/close \
-H "Authorization: Bearer jusa_sk_test_…"
Report a completion (signed)
For survey tools that can sign a webhook but hold no key: Jusa-Signature: t=<unix>,v1=<hex HMAC-SHA256(program secret, "{t}.{raw body}")>, within 5 minutes. completion_id (or ref) is required; a repeat returns the first reward (200).
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
| Body (Completion) | Type | About |
|---|---|---|
completion_id | string | |
ref | string | Alias of completion_id. |
recipient | string or object | rcp_… or ext:<external_id>. |
phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
target | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
meter | string | An 11-digit ZESA prepaid meter number. |
token_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
amount | string or number | A decimal amount, e.g. "5.00". |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 200 Reward · 201 Reward. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/completions \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Take budget back
Without an amount: everything no escrow or unclaimed link has earmarked. Scope: programs:manage. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
| Body (ProgramDefund) | Type | About |
|---|---|---|
amount | string or number | A decimal amount, e.g. "5.00". |
Returns: 200 Program. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/defund \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'
Add budget (and reserved credit)
Scope: programs:manage. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
| Body (ProgramFund) | Type | About |
|---|---|---|
amount required | string or number | A decimal amount, e.g. "5.00". |
Returns: 200 Program. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/fund \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"amount": "5.00"}'
Pause a program
Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
Returns: 200 Program. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/pause \
-H "Authorization: Bearer jusa_sk_test_…"
Resume a paused program
Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
Returns: 200 Program. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/resume \
-H "Authorization: Bearer jusa_sk_test_…"
Roll the completions secret
The old secret keeps working for 24 hours. Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
Returns: 200 Program. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/roll-secret \
-H "Authorization: Bearer jusa_sk_test_…"
Rewards
The seed primitive: reward a person for something they did.
List rewards
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
program | query | prg_… |
recipient | query | rcp_… or ext:<external_id>. |
completion_id | query | The completion it answers. |
external_id | query | Your id for the person. |
expand[] | query | Inline a related object in place of its id (repeat for more): recipient, program or batch on a send; recipient, program or send on a reward. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 RewardList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/rewards \ -H "Authorization: Bearer jusa_sk_test_…"
Reward someone
The seed primitive: caps, fraud rules and budget under one lock, then a send or a link. The same completion_id again returns the first reward (200). A refusal writes nothing, so it can be retried. Scope: rewards:write. Moves money: an Idempotency-Key is required.
| Body (RewardCreate) | Type | About |
|---|---|---|
program required | string | |
recipient required | string or object | rcp_… or ext:<external_id>. |
amount | string or number | A decimal amount, e.g. "5.00". |
delivery | "direct" | "link" | "choice" | |
type | "airtime" | "data" | "zesa" | |
product | string | |
bundle_id | string | |
meter | string | An 11-digit ZESA prepaid meter number. |
token_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
completion_id required | string | Required: one reward per completion, for good. |
hold | boolean | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
link | object | |
reason | string |
Returns: 200 Reward · 201 Reward · 202 AgentApproval. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/rewards \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"program": "prg_\u2026", "recipient": {"external_id": "resp-8812", "phone": "0771230000"}, "completion_id": "8812", "metadata": {"survey": "form-hh-q4"}}'
Retrieve a reward
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
reward_id required | path | rwd_… |
expand[] | query | Inline a related object in place of its id (repeat for more): recipient, program or batch on a send; recipient, program or send on a reward. |
Returns: 200 Reward. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/rewards/{reward_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Release an escrowed reward
Optionally for less. Scope: rewards:write. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
reward_id required | path | rwd_… |
| Body (RewardRelease) | Type | About |
|---|---|---|
amount | string or number | A decimal amount, e.g. "5.00". |
Returns: 200 Reward. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/rewards/{reward_id}/release \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'
Void a reward
Its budget goes back to the program. Scope: rewards:write.
| Parameter | In | About |
|---|---|---|
reward_id required | path | rwd_… |
Returns: 200 Reward. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/rewards/{reward_id}/void \
-H "Authorization: Bearer jusa_sk_test_…"
Reward links
Links and QR codes a person claims.
List reward links
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
label | query | Only links with this label. |
program | query | prg_… |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 RewardLinkList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/reward-links \ -H "Authorization: Bearer jusa_sk_test_…"
Create reward links
One link, or a list when count is over 1 (up to 500). Each has a url, a token and a QR code. Scope: rewards:write.
| Body (RewardLinkCreate) | Type | About |
|---|---|---|
program required | string | |
count | integer | |
recipient | string or object | rcp_… or ext:<external_id>. |
amount | string or number | A decimal amount, e.g. "5.00". |
label | string | |
expires_at | integer or string | Unix seconds or an ISO 8601 time. |
bind | "none" | "otp" | |
phone_hint | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
choices | array of "airtime" | "data" | "zesa" | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 201 RewardLink or RewardLinkList. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/reward-links \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"program": "prg_\u2026", "count": 10, "bind": "otp", "label": "enumerator-7"}'
Retrieve a reward link
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
link_id required | path | lnk_… |
Returns: 200 RewardLink. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/reward-links/{link_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Revoke a reward link
Scope: rewards:write.
| Parameter | In | About |
|---|---|---|
link_id required | path | lnk_… |
Returns: 200 RewardLink. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/reward-links/{link_id}/revoke \
-H "Authorization: Bearer jusa_sk_test_…"
Reviews
Rewards held by a fraud rule, and your blocklist.
List blocked numbers and meters
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
target | query | A phone (any format) or meter. |
Returns: 200 BlockedTargetList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/blocklist \ -H "Authorization: Bearer jusa_sk_test_…"
Block a number or meter
Live mode only. Scope: account:manage.
| Body (BlockCreate) | Type | About |
|---|---|---|
target required | string | |
reason | string |
Returns: 201 BlockedTarget. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/blocklist \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"target": "string"}'
Retrieve a block
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
entry_id required | path | blk_… |
Returns: 200 BlockedTarget. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/blocklist/{entry_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Unblock
Scope: account:manage.
| Parameter | In | About |
|---|---|---|
entry_id required | path | blk_… |
Returns: 200 BlockedTarget. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/blocklist/{entry_id} \
-H "Authorization: Bearer jusa_sk_test_…"
List fraud reviews
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
program | query | prg_… |
Returns: 200 ReviewList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/reviews \ -H "Authorization: Bearer jusa_sk_test_…"
Retrieve a review
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
review_id required | path | rev_… |
Returns: 200 Review. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/reviews/{review_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Approve a held reward
Re-checks the caps and budget under lock, then sends. Scope: programs:manage. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
review_id required | path | rev_… |
| Body (ReviewDecision) | Type | About |
|---|---|---|
note | string |
Returns: 200 Review. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/reviews/{review_id}/approve \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'
Reject a held reward
Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
review_id required | path | rev_… |
| Body (ReviewDecision) | Type | About |
|---|---|---|
note | string |
Returns: 200 Review. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/reviews/{review_id}/reject \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Rules
Earnings pots, streaks, referrals and geofenced check-ins.
List activities
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
name | query | Only activities with this name. |
recipient | query | rcp_… or ext:<external_id>. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 ActivityList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/activities \ -H "Authorization: Bearer jusa_sk_test_…"
Record an activity
Feeds the program rules; matches says what it triggered. activity_id dedupes for good (200). Scope: rewards:write.
| Body (ActivityCreate) | Type | About |
|---|---|---|
recipient required | string or object | rcp_… or ext:<external_id>. |
name required | string | |
activity_id required | string | |
occurred_at | integer or string | Unix seconds or an ISO 8601 time. |
value | string or number | A decimal amount, e.g. "5.00". |
program | string | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 200 Activity · 201 Activity. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/activities \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"recipient": "ext:user_123", "name": "lesson.completed", "activity_id": "log-9981"}'
Retrieve an activity
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
activity_id required | path | act_… |
Returns: 200 Activity. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/activities/{activity_id} \
-H "Authorization: Bearer jusa_sk_test_…"
List check-ins
Scope: rewards:read. Publishable keys may call it (from a browser).
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
Returns: 200 CheckinList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/checkins \ -H "Authorization: Bearer jusa_sk_test_…"
Check in to claim a geofenced link
Publishable keys may call it from the browser. Publishable keys may call it (from a browser).
| Body (CheckinCreate) | Type | About |
|---|---|---|
link required | string | |
lat required | number or string | |
lng required | number or string | |
accuracy_m | number or string | |
attestation | string | |
phone required | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
choice | "airtime" | "data" | "zesa" | |
meter | string | An 11-digit ZESA prepaid meter number. |
bundle_id | string | |
code | string | |
challenge_token | string | |
challenge_answer | string |
Returns: 201 Checkin. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/checkins \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"link": "string", "lat": 1, "lng": 1, "phone": "0772123456"}'
List a program's rules
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
active | query | Active or not. |
Returns: 200 ProgramRuleList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/rules \
-H "Authorization: Bearer jusa_sk_test_…"
Set a program's rules
The whole set: rules left out are switched off. lottery and prize_draw are refused. Scope: programs:manage.
| Parameter | In | About |
|---|---|---|
program_id required | path | prg_… |
| Body (ProgramRules) | Type | About |
|---|---|---|
rules required | array of object |
Returns: 200 ProgramRuleList. Errors: the envelope.
curl -X PUT https://jusa.localhost.co.zw/dev/v1/programs/{program_id}/rules \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"rules": []}'
List referrals
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
program | query | prg_… |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 ReferralList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/referrals \ -H "Authorization: Bearer jusa_sk_test_…"
Record a referral
Scope: rewards:write.
| Body (ReferralCreate) | Type | About |
|---|---|---|
program required | string | |
referrer required | string or object | rcp_… or ext:<external_id>. |
referee required | string or object | rcp_… or ext:<external_id>. |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
Returns: 200 Referral · 201 Referral. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/referrals \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"program": "string", "referrer": "string", "referee": "string"}'
Retrieve a referral
Scope: rewards:read.
| Parameter | In | About |
|---|---|---|
referral_id required | path | ref_… |
Returns: 200 Referral. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/referrals/{referral_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Qualify a referral
Pays the direct referrer (depth 1), in escrow by default. Scope: rewards:write. Moves money: an Idempotency-Key is required.
| Parameter | In | About |
|---|---|---|
referral_id required | path | ref_… |
Returns: 200 Referral. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/referrals/{referral_id}/qualify \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)"
Claims
What a publishable key may do with a reward link.
Read a reward link by its token
For publishable keys (the widget). Publishable keys may call it (from a browser).
| Parameter | In | About |
|---|---|---|
token required | path | The link's token. |
Returns: 200 RewardLinkClaim. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/claims/{token} \
-H "Authorization: Bearer jusa_sk_test_…"
Claim a reward link
Publishable keys may call it (from a browser).
| Parameter | In | About |
|---|---|---|
token required | path | The link's token. |
| Body (Claim) | Type | About |
|---|---|---|
phone required | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
choice | "airtime" | "data" | "zesa" | |
meter | string | An 11-digit ZESA prepaid meter number. |
bundle_id | string | |
code | string | |
challenge_token | string | |
challenge_answer | string |
Returns: 200 RewardLinkClaim · 201 RewardLinkClaim. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/claims/{token} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"phone": "0772123456"}'
Text the claim code (OTP-bound links)
Publishable keys may call it (from a browser).
| Parameter | In | About |
|---|---|---|
token required | path | The link's token. |
| Body (ClaimCodeRequest) | Type | About |
|---|---|---|
phone required | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
challenge_token | string | |
challenge_answer | string |
Returns: 200 ClaimCode. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/claims/{token}/code \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"phone": "0772123456"}'
Agent approvals
An agent key's request waiting for a person.
List agent approvals
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
action | query | Only this action (send or reward; for the audit log, e.g. api_key.created). |
Returns: 200 AgentApprovalList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/approvals \ -H "Authorization: Bearer jusa_sk_test_…"
Retrieve an agent approval
| Parameter | In | About |
|---|---|---|
approval_id required | path | apr_… |
Returns: 200 AgentApproval. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/approvals/{approval_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Approve an agent's request
Dashboard only: an owner or admin, with their payment PIN.
| Parameter | In | About |
|---|---|---|
approval_id required | path | apr_… |
| Body (ApprovalDecision) | Type | About |
|---|---|---|
pin | string | Your payment PIN (dashboard only). |
Returns: 200 AgentApproval. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/approvals/{approval_id}/approve \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Reject an agent's request
Dashboard only.
| Parameter | In | About |
|---|---|---|
approval_id required | path | apr_… |
Returns: 200 AgentApproval. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/approvals/{approval_id}/reject \
-H "Authorization: Bearer jusa_sk_test_…"
MCP
The Model Context Protocol server for AI agents.
The MCP server (JSON-RPC 2.0)
Agent keys only. Tools: send_airtime, buy_zesa, confirm_meter, create_reward, get_balance, get_send, list_products. 202 with no body answers a notification; a message that is not JSON-RPC is a JSON-RPC error with status 400.
Returns: 200 JsonRpcResponse · 202 no body · 400 JsonRpcResponse. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/mcp \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Jusa Credit
Your prepaid balance: transactions, top-ups, statements.
Jusa Credit balances
Scope: credit:read.
Returns: 200 Credit. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/credit \ -H "Authorization: Bearer jusa_sk_test_…"
Low-balance and shortfall alerts
Scope: credit:read.
Returns: 200 CreditAlerts. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/credit/alerts \ -H "Authorization: Bearer jusa_sk_test_…"
Set the alerts
Scope: account:manage.
| Body (CreditAlertsUpdate) | Type | About |
|---|---|---|
low_balance | object | |
shortfall_projection | boolean | |
shortfall_days | integer | |
channels | array of "webhook" | "email" | "sms" |
Returns: 200 CreditAlerts. Errors: the envelope.
curl -X PUT https://jusa.localhost.co.zw/dev/v1/credit/alerts \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
List credit set aside
Scope: credit:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
currency | query | Only this currency. |
Returns: 200 CreditReservationList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/credit/reservations \ -H "Authorization: Bearer jusa_sk_test_…"
List top-ups
Scope: credit:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
method | query | GET, POST, … |
status | query | Only objects in this status. |
Returns: 200 TopupList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/credit/topups \ -H "Authorization: Bearer jusa_sk_test_…"
Top up Jusa Credit
pesepay: send a person to redirect_url; the card fee is paid by the payer. bank_transfer: our bank details and a reference to quote. Any amount; spendable once paid. Scope: credit:topup. Moves money: an Idempotency-Key is required.
| Body (TopupCreate) | Type | About |
|---|---|---|
method | "pesepay" | "bank_transfer" | |
amount required | string or number | A decimal amount, e.g. "5.00". |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
description | string |
Returns: 201 Topup. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/credit/topups \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"method": "pesepay", "amount": "100.00", "currency": "USD"}'
Retrieve a top-up
Scope: credit:read.
| Parameter | In | About |
|---|---|---|
topup_id required | path | top_… |
Returns: 200 Topup. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/credit/topups/{topup_id} \
-H "Authorization: Bearer jusa_sk_test_…"
List Jusa Credit transactions
Scope: credit:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
currency | query | Only this currency. |
type | query | Only this type. |
source | query | What it came from: snd_, top_, rwd_, bch_ or rsv_. |
Returns: 200 CreditTransactionList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/credit/transactions \ -H "Authorization: Bearer jusa_sk_test_…"
Retrieve a transaction
Scope: credit:read.
| Parameter | In | About |
|---|---|---|
transaction_id required | path | txn_… |
Returns: 200 CreditTransaction. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/credit/transactions/{transaction_id} \
-H "Authorization: Bearer jusa_sk_test_…"
A monthly statement
Scope: credit:read.
| Parameter | In | About |
|---|---|---|
period | query | YYYY-MM; this month by default. |
currency | query | Only this currency. |
Returns: 200 Statement. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/statements \ -H "Authorization: Bearer jusa_sk_test_…"
A monthly statement as CSV
Scope: credit:read.
| Parameter | In | About |
|---|---|---|
period | query | YYYY-MM; this month by default. |
currency | query | Only this currency. |
Returns: 200 CSV. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/statements.csv \ -H "Authorization: Bearer jusa_sk_test_…"
A monthly statement as PDF
Scope: credit:read.
| Parameter | In | About |
|---|---|---|
period | query | YYYY-MM; this month by default. |
currency | query | Only this currency. |
Returns: 200 no body. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/statements.pdf \ -H "Authorization: Bearer jusa_sk_test_…"
Checkout
Your user pays by card; you get the attribution.
List checkout sessions
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
client_reference | query | Your own reference. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 CheckoutSessionList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/checkout-sessions \ -H "Authorization: Bearer jusa_sk_test_…"
Create a checkout session
Your user pays by card on Pesepay (Jusa is the merchant of record and the card fee is the payer's); the send is booked to you. A repeated client_reference returns the first session (200). Scope: sends:write. Moves money: an Idempotency-Key is required.
| Body (CheckoutSessionCreate) | Type | About |
|---|---|---|
type | "airtime" | "data" | "zesa" | |
product | string | |
target required | string | |
amount required | string or number | A decimal amount, e.g. "5.00". |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
bundle_id | string | |
token_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
customer_phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
success_url required | string | |
cancel_url required | string | |
client_reference | string | |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
sms_message | string |
Returns: 200 CheckoutSession · 201 CheckoutSession. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/checkout-sessions \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"type": "zesa", "target": "37261502217", "amount": "20.00", "currency": "USD", "token_phone": "0772123456", "success_url": "https://example.com/thanks", "cancel_url": "https://example.com/basket"}'
Retrieve a checkout session
Scope: sends:read.
| Parameter | In | About |
|---|---|---|
session_id required | path | cs_… |
Returns: 200 CheckoutSession. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/checkout-sessions/{session_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Fix the meter while it waits
Scope: sends:write.
| Parameter | In | About |
|---|---|---|
session_id required | path | cs_… |
| Body (CheckoutMeter) | Type | About |
|---|---|---|
meter required | string | An 11-digit ZESA prepaid meter number. |
Returns: 200 CheckoutSession. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/checkout-sessions/{session_id}/meter \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"meter": "37261502217"}'
Workforce
Employer allocation and GL, as the Jusa app has it.
What this key may do in Workforce
Scope: workforce:admin.
Returns: 200 WorkforceAccess. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/access \ -H "Authorization: Bearer jusa_sk_test_…"
Allocate airtime or ZESA
Scope: workforce:admin.
| Body (WorkforceAllocate) | Type | About |
|---|---|---|
kind required | "airtime" | "zesa" | |
product_ref | string | |
cost_center_id | integer or string | A numeric id. |
title | string | |
custom_sms | string | |
idempotency_key | string | 8 to 64 of A-Z a-z 0-9 _ -. A repeat returns the send it made; the Idempotency-Key header does the same. |
acknowledge_over_budget | boolean or string | true or false. |
lines required | array of object |
Returns: 200 WorkforceSendResult. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/allocate \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"kind": "airtime", "product_ref": "airtime-usd", "cost_center_id": 3, "idempotency_key": "stipend-2026-10", "lines": [{"recipient_id": 12, "amount": "2.00"}]}'
Preview an allocation
Scope: workforce:admin.
| Body (WorkforceAllocate) | Type | About |
|---|---|---|
kind required | "airtime" | "zesa" | |
product_ref | string | |
cost_center_id | integer or string | A numeric id. |
title | string | |
custom_sms | string | |
idempotency_key | string | 8 to 64 of A-Z a-z 0-9 _ -. A repeat returns the send it made; the Idempotency-Key header does the same. |
acknowledge_over_budget | boolean or string | true or false. |
lines required | array of object |
Returns: 200 WorkforcePreview. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/allocate/preview \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"kind": "airtime", "product_ref": "airtime-usd", "cost_center_id": 3, "idempotency_key": "stipend-2026-10", "lines": [{"recipient_id": 12, "amount": "2.00"}]}'
Past allocations
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
from | query | The start (inclusive). |
to | query | The end (inclusive). |
status | query | Only objects in this status. |
cost_center | query | cc_… (Workforce: a department's id, or none for sends booked to none). |
page | query | Workforce pages from 1. |
page_size | query | Workforce rows per page (batches up to 50, the ledger up to 100). |
Returns: 200 WorkforceBatchPage. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/batches \ -H "Authorization: Bearer jusa_sk_test_…"
One allocation
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
reference required | path | A Workforce batch reference. |
Returns: 200 WorkforceBatch. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/batches/{reference} \
-H "Authorization: Bearer jusa_sk_test_…"
Resend the failed lines
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
reference required | path | A Workforce batch reference. |
| Body (WorkforceResend) | Type | About |
|---|---|---|
item_ids | array of integer or string | Only these lines (default: every line that can go again). |
idempotency_key | string | 8 to 64 of A-Z a-z 0-9 _ -. A repeat returns the send it made; the Idempotency-Key header does the same. |
acknowledge_over_budget | boolean or string | true or false. |
Returns: 200 WorkforceSendResult. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/batches/{reference}/resend \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Preview resending the failed lines
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
reference required | path | A Workforce batch reference. |
| Body (WorkforceResend) | Type | About |
|---|---|---|
item_ids | array of integer or string | Only these lines (default: every line that can go again). |
idempotency_key | string | 8 to 64 of A-Z a-z 0-9 _ -. A repeat returns the send it made; the Idempotency-Key header does the same. |
acknowledge_over_budget | boolean or string | true or false. |
Returns: 200 WorkforcePreview. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/batches/{reference}/resend/preview \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Departments
Scope: workforce:admin.
Returns: 200 WorkforceDepartmentList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/cost-centers \ -H "Authorization: Bearer jusa_sk_test_…"
Add a department
Scope: workforce:admin.
| Body (WorkforceDepartmentWrite) | Type | About |
|---|---|---|
name | string | |
code | string | The GL code. |
monthly_budget | string or number | A decimal amount, e.g. "5.00". |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
is_active | boolean or string | On a change: false archives it. |
Returns: 201 WorkforceDepartment. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/cost-centers \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Field sales", "code": "6100", "monthly_budget": "250.00", "currency": "USD"}'
Change a department
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
cost_center_id required | path | cc_… |
| Body (WorkforceDepartmentWrite) | Type | About |
|---|---|---|
name | string | |
code | string | The GL code. |
monthly_budget | string or number | A decimal amount, e.g. "5.00". |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
is_active | boolean or string | On a change: false archives it. |
Returns: 200 WorkforceDepartment. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/workforce/cost-centers/{cost_center_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Field sales", "code": "6100", "monthly_budget": "250.00", "currency": "USD"}'
A department's people
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
cost_center_id required | path | cc_… |
Returns: 200 WorkforceDepartmentPeople. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/cost-centers/{cost_center_id}/people \
-H "Authorization: Bearer jusa_sk_test_…"
Move people into a department
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
cost_center_id required | path | cc_… |
| Body (WorkforcePeopleChange) | Type | About |
|---|---|---|
add | array of integer or string | People to move into it. |
remove | array of integer or string | People to take out. |
new | array of object | People to add to the register and to it. |
Returns: 200 WorkforceDepartmentPeople. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/cost-centers/{cost_center_id}/people \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"add": [12], "new": [{"name": "Rudo Moyo", "phone": "0772123456"}]}'
A department's spend
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
cost_center_id required | path | cc_… |
from | query | The start (inclusive). |
to | query | The end (inclusive). |
Returns: 200 WorkforceDepartmentSpend. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/cost-centers/{cost_center_id}/spend \
-H "Authorization: Bearer jusa_sk_test_…"
The GL journal
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
from | query | The start (inclusive). |
to | query | The end (inclusive). |
cost_center | query | cc_… (Workforce: a department's id, or none for sends booked to none). |
format | query | json (default); csv for reports and the Workforce files (the ledger also takes csv-json, the file inside JSON); pdf for a receipt. |
Returns: 200 WorkforceJournal or CSV. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/journal \ -H "Authorization: Bearer jusa_sk_test_…"
The ledger
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
from | query | The start (inclusive). |
to | query | The end (inclusive). |
currency | query | Only this currency. |
kind | query | Only this kind of entry (topup, debit, refund, adjustment). |
q | query | Entries whose reference contains this. |
format | query | json (default); csv for reports and the Workforce files (the ledger also takes csv-json, the file inside JSON); pdf for a receipt. |
page | query | Workforce pages from 1. |
page_size | query | Workforce rows per page (batches up to 50, the ledger up to 100). |
Returns: 200 WorkforceLedger or WorkforceExport or CSV. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/ledger \ -H "Authorization: Bearer jusa_sk_test_…"
Meters on file
Scope: workforce:admin.
Returns: 200 WorkforceMeters. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/meters \ -H "Authorization: Bearer jusa_sk_test_…"
What Workforce can send
Scope: workforce:admin.
Returns: 200 WorkforceProducts. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/products \ -H "Authorization: Bearer jusa_sk_test_…"
Add a person
Scope: workforce:admin.
| Body (WorkforcePersonCreate) | Type | About |
|---|---|---|
name required | string | |
phone required | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
payroll_id | string | |
cost_center_id | integer or string | A numeric id. |
monthly_cap | string or number | A decimal amount, e.g. "5.00". |
Returns: 201 WorkforcePersonCreated. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/recipients \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Rudo Moyo", "phone": "0772123456", "cost_center_id": 3}'
Change a person
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
| Body (WorkforcePersonUpdate) | Type | About |
|---|---|---|
name | string | |
phone | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
payroll_id | string | |
cost_center_id | integer or string | A numeric id. |
monthly_cap | string or number | A decimal amount, e.g. "5.00". |
notes | string |
Returns: 200 WorkforcePersonUpdated. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/workforce/recipients/{recipient_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
A person's statement
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
recipient_id required | path | rcp_… or ext:<external_id>. |
from | query | The start (inclusive). |
to | query | The end (inclusive). |
Returns: 200 WorkforceStatement. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/recipients/{recipient_id}/statement \
-H "Authorization: Bearer jusa_sk_test_…"
Add a plan
Scope: workforce:admin.
| Body (WorkforcePlanCreate) | Type | About |
|---|---|---|
name required | string | |
frequency required | "daily" | "weekly" | "monthly" | |
hour | integer or string | |
minute | integer or string | |
day_of_week | integer or string | weekly: 0 (Monday) to 6. |
day_of_month | integer or string | monthly: 1 to 28. |
group_id | integer or string | A numeric id. |
product_ref | string | An airtime product; plans do not send ZESA. |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
amount_per_recipient required | string or number | A decimal amount, e.g. "5.00". |
custom_sms | string | |
cost_center_id | integer or string | The department it is booked to and sends to. |
Returns: 201 WorkforcePlan. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/schedules \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"name": "Month-end airtime", "frequency": "monthly", "day_of_month": 25, "hour": 9, "amount_per_recipient": "5.00", "cost_center_id": 3}'
Change a plan
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
| Body (WorkforcePlanUpdate) | Type | About |
|---|---|---|
name | string | |
frequency | "daily" | "weekly" | "monthly" | |
hour | integer or string | |
minute | integer or string | |
day_of_week | integer or string | weekly: 0 (Monday) to 6. |
day_of_month | integer or string | monthly: 1 to 28. |
group_id | integer or string | A numeric id. |
product_ref | string | An airtime product; plans do not send ZESA. |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
amount_per_recipient | string or number | A decimal amount, e.g. "5.00". |
custom_sms | string | |
cost_center_id | integer or string | The department it is booked to and sends to. |
is_active | boolean or string | false pauses it. |
Returns: 200 WorkforcePlan. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/workforce/schedules/{schedule_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Remove a plan
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
Returns: 200 WorkforcePlanStopped. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/workforce/schedules/{schedule_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Run a plan now
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
| Body (WorkforcePlanRunNow) | Type | About |
|---|---|---|
idempotency_key | string | 8 to 64 of A-Z a-z 0-9 _ -. A repeat returns the send it made; the Idempotency-Key header does the same. |
again | boolean or string | Send again when it already went out today. |
acknowledge_over_budget | boolean or string | true or false. |
Returns: 200 WorkforcePlanRun. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/schedules/{schedule_id}/run-now \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Preview running a plan
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
schedule_id required | path | sch_… |
Returns: 200 WorkforcePreview. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/workforce/schedules/{schedule_id}/run-now/preview \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
The period summary
Scope: workforce:admin.
| Parameter | In | About |
|---|---|---|
from | query | The start (inclusive). |
to | query | The end (inclusive). |
Returns: 200 WorkforceSummary. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/workforce/summary \ -H "Authorization: Bearer jusa_sk_test_…"
Family
MyFamilyTime (needs OAuth user tokens; not available yet).
MyFamilyTime (not available yet)
Every /family route answers 501 not_yet_available: it needs OAuth user tokens. Use recipients and schedules for your own users.
Returns: an error. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/family \ -H "Authorization: Bearer jusa_sk_test_…"
MyFamilyTime (not available yet)
Every /family route answers 501 not_yet_available: it needs OAuth user tokens. Use recipients and schedules for your own users.
Returns: an error. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/family \ -H "Authorization: Bearer jusa_sk_test_…"
MyFamilyTime (not available yet)
Every /family route answers 501 not_yet_available: it needs OAuth user tokens. Use recipients and schedules for your own users.
Returns: an error. Errors: the envelope.
curl -X PUT https://jusa.localhost.co.zw/dev/v1/family \ -H "Authorization: Bearer jusa_sk_test_…"
MyFamilyTime (not available yet)
Every /family route answers 501 not_yet_available: it needs OAuth user tokens. Use recipients and schedules for your own users.
Returns: an error. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/family \ -H "Authorization: Bearer jusa_sk_test_…"
MyFamilyTime (not available yet)
Every /family route answers 501 not_yet_available: it needs OAuth user tokens. Use recipients and schedules for your own users.
Returns: an error. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/family \ -H "Authorization: Bearer jusa_sk_test_…"
Reports
Spend, success rate, the GL journal and CSV exports.
List exports
Scope: reports:read.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
Returns: 200 ExportList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/exports \ -H "Authorization: Bearer jusa_sk_test_…"
Export to CSV
Built in the background; GET it for a signed url (an hour). Tokens are always masked. Scope: reports:read.
| Body (ExportCreate) | Type | About |
|---|---|---|
type required | "sends" | "rewards" | "transactions" | "deliveries" | |
from | integer or string | Unix seconds or an ISO 8601 time. |
to | integer or string | Unix seconds or an ISO 8601 time. |
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
status | string |
Returns: 202 Export. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/exports \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"type": "sends", "from": "2026-09-01T00:00:00Z"}'
Retrieve an export
Scope: reports:read.
| Parameter | In | About |
|---|---|---|
export_id required | path | exp_… |
Returns: 200 Export. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/exports/{export_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Download an export
No key: the signed url is the credential.
| Parameter | In | About |
|---|---|---|
export_id required | path | exp_… |
token | query | The signed download token from the export's url. |
Returns: 200 CSV. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/exports/{export_id}/download
GL journal
Scope: reports:read.
| Parameter | In | About |
|---|---|---|
from | query | The start (inclusive). |
to | query | The end (inclusive). |
cost_center | query | cc_… (Workforce: a department's id, or none for sends booked to none). |
format | query | json (default); csv for reports and the Workforce files (the ledger also takes csv-json, the file inside JSON); pdf for a receipt. |
Returns: 200 Report or CSV. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/reports/journal \ -H "Authorization: Bearer jusa_sk_test_…"
Spend report
Delivered sends paid from Jusa Credit at face value; group_by day, network, product, program, cost_center or recipient. Scope: reports:read.
| Parameter | In | About |
|---|---|---|
from | query | The start (inclusive). |
to | query | The end (inclusive). |
currency | query | Only this currency. |
group_by | query | How rows are grouped. |
Returns: 200 Report. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/reports/spend \ -H "Authorization: Bearer jusa_sk_test_…"
Success-rate report
delivered ÷ (delivered + failed); group_by network or product. Scope: reports:read.
| Parameter | In | About |
|---|---|---|
from | query | The start (inclusive). |
to | query | The end (inclusive). |
currency | query | Only this currency. |
group_by | query | How rows are grouped. |
Returns: 200 Report. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/reports/success-rate \ -H "Authorization: Bearer jusa_sk_test_…"
Webhooks
Endpoints, events and deliveries.
List events
The catch-up feed, 30 days. type takes a family: send.* Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
type | query | Only this type. |
object | query | Events about this object, e.g. snd_… |
Returns: 200 EventList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/events \ -H "Authorization: Bearer jusa_sk_test_…"
Retrieve an event
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
event_id required | path | evt_… |
Returns: 200 Event. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/events/{event_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Read a ZESA token from its link
The token_url in a send.delivered event: no key (the url is the credential), once, within 15 minutes.
| Parameter | In | About |
|---|---|---|
token required | path | The link's token. |
Returns: 200 SendToken. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/send-tokens/{token}
List webhook deliveries
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
endpoint | query | we_… |
event | query | evt_… |
type | query | Only this type. |
status | query | Only objects in this status. |
Returns: 200 WebhookDeliveryList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/webhook-deliveries \ -H "Authorization: Bearer jusa_sk_test_…"
Retrieve a delivery
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
delivery_id required | path | whd_… |
Returns: 200 WebhookDelivery. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/webhook-deliveries/{delivery_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Redeliver
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
delivery_id required | path | whd_… |
Returns: 202 WebhookDelivery. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/webhook-deliveries/{delivery_id}/redeliver \
-H "Authorization: Bearer jusa_sk_test_…"
List webhook endpoints
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
limit | query | Rows per page. |
starting_after | query | An object id: the page after it (next_cursor). |
ending_before | query | An object id: the page before it. |
created[gte] | query | Unix seconds or ISO 8601. |
created[lte] | query | Unix seconds or ISO 8601. |
created[gt] | query | Unix seconds or ISO 8601. |
created[lt] | query | Unix seconds or ISO 8601. |
status | query | Only objects in this status. |
metadata[key] | query | metadata[<key>]=<value>: an exact match on one key. |
Returns: 200 WebhookEndpointList. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/webhook-endpoints \ -H "Authorization: Bearer jusa_sk_test_…"
Add a webhook endpoint
https only, public addresses only. The signing secret is shown here. Up to 16 per mode. Scope: webhooks:manage.
| Body (WebhookEndpointCreate) | Type | About |
|---|---|---|
url required | string | |
enabled_events | array of string | "*", a family like "send.*", or types. |
description | string | |
api_version | string | |
include_tokens | boolean | |
token_links | boolean | Needs tokens:read. On by default when the caller has it. |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
format | "jusa" | "legacy_airtime" |
Returns: 201 WebhookEndpoint. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/webhook-endpoints \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/jusa/webhooks", "enabled_events": ["send.*", "reward.*"]}'
Retrieve a webhook endpoint
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
endpoint_id required | path | we_… |
Returns: 200 WebhookEndpoint. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/webhook-endpoints/{endpoint_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Update a webhook endpoint
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
endpoint_id required | path | we_… |
| Body (WebhookEndpointUpdate) | Type | About |
|---|---|---|
url | string | |
enabled_events | array of string | "*", a family like "send.*", or types. |
description | string | |
api_version | string | |
include_tokens | boolean | |
token_links | boolean | Needs tokens:read. On by default when the caller has it. |
metadata | object | Up to 20 keys (letters, digits, _ . -, up to 40 characters) with values up to 500 characters. |
format | "jusa" | "legacy_airtime" | |
status | "enabled" | "disabled" |
Returns: 200 WebhookEndpoint. Errors: the envelope.
curl -X PATCH https://jusa.localhost.co.zw/dev/v1/webhook-endpoints/{endpoint_id} \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Delete a webhook endpoint
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
endpoint_id required | path | we_… |
Returns: 200 WebhookEndpoint. Errors: the envelope.
curl -X DELETE https://jusa.localhost.co.zw/dev/v1/webhook-endpoints/{endpoint_id} \
-H "Authorization: Bearer jusa_sk_test_…"
Replay events
Every stored event in a time range, again. Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
endpoint_id required | path | we_… |
| Body (WebhookReplayCreate) | Type | About |
|---|---|---|
from required | integer or string | Unix seconds or an ISO 8601 time. |
to | integer or string | Unix seconds or an ISO 8601 time. |
types | array of string |
Returns: 202 WebhookReplay. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/webhook-endpoints/{endpoint_id}/replay \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"from": 1}'
Roll the signing secret
The old secret signs alongside for the grace (two v1= values). Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
endpoint_id required | path | we_… |
| Body (WebhookRollSecret) | Type | About |
|---|---|---|
grace | "0" | "1h" | "24h" |
Returns: 200 WebhookEndpoint. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/webhook-endpoints/{endpoint_id}/roll-secret \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Send a test event
Scope: webhooks:manage.
| Parameter | In | About |
|---|---|---|
endpoint_id required | path | we_… |
| Body (WebhookTest) | Type | About |
|---|---|---|
type | string |
Returns: 202 WebhookDelivery. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/webhook-endpoints/{endpoint_id}/test \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Test mode
Helpers for test keys only.
Approve (test mode)
Moves money: an Idempotency-Key is required. Test keys only: a live key gets 403 test_mode_only.
| Parameter | In | About |
|---|---|---|
approval_id required | path | apr_… |
Returns: 200 AgentApproval. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/approvals/{approval_id}/approve \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Idempotency-Key: $(uuidgen)"
Reject (test mode)
Test keys only: a live key gets 403 test_mode_only.
| Parameter | In | About |
|---|---|---|
approval_id required | path | apr_… |
Returns: 200 AgentApproval. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/approvals/{approval_id}/reject \
-H "Authorization: Bearer jusa_sk_test_…"
Pay a test checkout
Scope: sends:write. Test keys only: a live key gets 403 test_mode_only.
| Parameter | In | About |
|---|---|---|
session_id required | path | cs_… |
| Body (TestCheckoutPay) | Type | About |
|---|---|---|
outcome | "paid" | "failed" |
Returns: 200 CheckoutSession. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/checkout-sessions/{session_id}/pay \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Play staff on a held test checkout
Scope: sends:write. Test keys only: a live key gets 403 test_mode_only.
| Parameter | In | About |
|---|---|---|
session_id required | path | cs_… |
| Body (TestCheckoutReview) | Type | About |
|---|---|---|
action required | "send" | "credit" | |
note | string |
Returns: 200 CheckoutSession. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/checkout-sessions/{session_id}/review \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"action": "send"}'
Add test Jusa Credit
Scope: credit:topup. Test keys only: a live key gets 403 test_mode_only.
| Body (TestCreditCreate) | Type | About |
|---|---|---|
currency | "USD" | "ZWG" | "usd" | "zwg" | USD or ZWG (either case). |
amount required | string or number | A decimal amount, e.g. "5.00". |
Returns: 201 TestCredit. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/credit \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"currency": "USD", "amount": "500.00"}'
Fire a sample event
Scope: webhooks:manage. Test keys only: a live key gets 403 test_mode_only.
| Body (TestTrigger) | Type | About |
|---|---|---|
type required | string | An event type, e.g. send.delivered. |
Returns: 201 Event. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/events/trigger \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"type": "string"}'
Wipe test mode
Sends, rewards, batches, ledger and events go; keys, endpoints and recipients stay; credit is reset. Scope: account:manage. Test keys only: a live key gets 403 test_mode_only.
Returns: 200 TestReset. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/reset \ -H "Authorization: Bearer jusa_sk_test_…"
Claim a link, skipping the code
Scope: rewards:write. Test keys only: a live key gets 403 test_mode_only.
| Parameter | In | About |
|---|---|---|
link_id required | path | lnk_… |
| Body (TestLinkClaim) | Type | About |
|---|---|---|
phone required | string | A Zimbabwean mobile: 07XXXXXXXX, 2637XXXXXXXX or +2637XXXXXXXX. |
choice | "airtime" | "data" | "zesa" | |
meter | string | An 11-digit ZESA prepaid meter number. |
bundle_id | string |
Returns: 200 Reward · 201 Reward. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/reward-links/{link_id}/claim \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"phone": "0772123456"}'
Move a test send on
Without to: the next scripted step now. A move a live send could not make is 409. Scope: sends:write. Test keys only: a live key gets 403 test_mode_only.
| Parameter | In | About |
|---|---|---|
send_id required | path | snd_… (or the bare hex of a legacy order). |
| Body (TestAdvance) | Type | About |
|---|---|---|
to | "delivered" | "failed" | "late_delivered" |
Returns: 200 Send. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/sends/{send_id}/advance \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{}'
Take ZESA down or up (test mode)
Scope: sends:write. Test keys only: a live key gets 403 test_mode_only.
| Body (TestZesa) | Type | About |
|---|---|---|
state required | "up" | "down" |
Returns: 200 ZesaAvailability. Errors: the envelope.
curl -X POST https://jusa.localhost.co.zw/dev/v1/test/zesa/availability \
-H "Authorization: Bearer jusa_sk_test_…" \
-H "Content-Type: application/json" \
-d '{"state": "up"}'
Utility
Health.
Check the API is up
No key needed.
Returns: 200 Health. Errors: the envelope.
curl https://jusa.localhost.co.zw/dev/v1/health