Build

Recipes

Every use case we know of, with the calls it takes.

Every use case we know of, with the calls it takes. Tags say when something depends on a later phase or on a legal sign-off; everything untagged works today. Code is Python with the jusa SDK; every call has the same shape in Node and in the reference.

Reward a respondent for finishing a survey

For KoboToolbox or ODK back-ends, SurveyCTO, research agencies. seed

The seed use case. One program holds the budget and the rules; each completed survey is one reward, keyed on the submission so it is paid once, forever.

  1. Create a program with a budget, a default amount and per_recipient caps (or in the dashboard).
  2. When your survey tool reports a completion, POST /rewards with the respondent and completion_id = the submission id.
  3. Listen for reward.delivered and reward.failed; a failure has already returned the budget.
program = jusa.Program.create(name="Household survey Q4", currency="USD",
    default_amount="0.50", budget="500.00", funding="reserve",
    choices=["airtime"], per_recipient={"lifetime": 1}, idempotency_key="prog-q4")

reward = jusa.Reward.create(program=program.id,
    recipient={"external_id": s["respondent_id"], "phone": s["phone"]},
    completion_id=s["submission_id"], metadata={"survey": s["form_id"]},
    idempotency_key=f"survey-{s['submission_id']}")

Calls: programs.create · rewards.create · programs.create_completion

Claim cards and QR codes for offline surveys

For NGOs with enumerators.

Print a link or QR per respondent. They claim it on their own phone later; an OTP binding stops a forwarded link being claimed by someone else.

  1. Mint a batch of links with a label per enumerator and an expiry.
  2. Hand out the url or the qr_png_url; the hosted page at /r/{token} does the rest.
  3. Watch reward_link.claimed and pull a spend report grouped by program.
links = jusa.RewardLink.create(program="prg_…", count=200, label="enumerator-7",
    bind="otp", expires_at="2026-12-31T23:59:59Z")
for link in links.data:
    print(link.url, link.qr_png_url)

Calls: reward_links.create · reward_links.list · reports.spend · exports.create

A micro-task earnings pot

For Premise-style task apps. P3

Each task earns into a pot; the pot pays out as airtime once it reaches a threshold.

  1. Set a threshold rule on the program: earn per activity, pay_at the threshold.
  2. POST an activity for each finished task; activity_id dedupes it for good.
  3. Show the person their pot with /recipients/{id}/balance.
jusa.Program.set_rules("prg_…", rules=[
    {"kind": "threshold", "activity": "task.done", "earn": "0.25", "pay_at": "1.00"}])
jusa.Activity.create(recipient="ext:user_123", name="task.done", activity_id=task_id)
jusa.Recipient.balance("ext:user_123")

Calls: programs.set_rules · activities.create · recipients.balance

Reimburse SMS or USSD poll costs

For Pollsters.

Pay back what answering a poll cost each participant, in one batch at the end of the day.

  1. Collect the numbers that answered.
  2. Preview, then create one batch with a line each.
items = [{"target": p, "amount": "0.10", "client_reference": f"poll-7-{p}"} for p in phones]
jusa.Batch.preview(items=items)
batch = jusa.Batch.create(items=items, title="Poll 7 reimbursement",
                          idempotency_key="poll-7-reimburse")

Calls: batches.preview · batches.create · batches.retrieve

Reward in-app actions: sign-up, KYC, first transaction

For Fintech and e-commerce apps.

Reward what a user does, once per user, with sign-up rewards held in escrow until they stick.

  1. A program per action, with one_per_phone and per_recipient caps.
  2. Reward with your own external_id so caps follow the person across numbers.
  3. Hold sign-up rewards (hold=true) and release them after your own checks.
reward = jusa.Reward.create(program="prg_signup", completion_id=f"signup-{user.id}",
    recipient={"external_id": str(user.id), "phone": user.phone}, hold=True)
# … after 7 days of real use:
jusa.Reward.release(reward.id, idempotency_key=f"release-{reward.id}")

Calls: rewards.create · rewards.release · rewards.void

Streak rewards

For Edtech, health and fitness apps. P3

Pay when someone does the thing N times in D days.

  1. Set a streak rule: activity, count, days, amount.
  2. POST an activity each time; the reward is made when the streak completes.
jusa.Program.set_rules("prg_…", rules=[
    {"kind": "streak", "activity": "lesson.completed", "count": 5, "days": 7, "amount": "1.00"}])
jusa.Activity.create(recipient="ext:learner-9", name="lesson.completed", activity_id=lesson_log_id)

Calls: programs.set_rules · activities.create

A two-sided referral chain

For Any app. P3

Pay the referrer (and optionally the referee) once the referee does something that counts. Depth 1, held in escrow by default.

  1. Set a referral rule with a qualifying_activity.
  2. Record the referral when they sign up.
  3. The referee's qualifying activity (or POST …/qualify) pays out.
jusa.Referral.create(program="prg_…",
    referrer={"external_id": "u-1", "phone": "0772123456"},
    referee={"external_id": "u-2", "phone": "0772654321"})

Calls: programs.set_rules · referrals.create · referrals.qualify

Loyalty points to airtime, data or ZESA

For Retailers, fuel stations, supermarkets. data: P2

Let members spend points on airtime or a ZESA token at face value.

  1. Show the catalog; for ZESA, quote first so the member sees the units.
  2. Send with the quote: it carries a confirmed meter.
quote = jusa.Quote.create(product="zesa-usd", amount="10.00", target="37261502217")
print(quote.zesa.units_now, quote.meter.customer_name)
jusa.Send.create(quote=quote.id, token_phone="0772123456",
                 idempotency_key=f"redeem-{redemption.id}")

Calls: products.list · quotes.create · sends.create · meters.retrieve

Radio or brand giveaways with an audit trail

For Media. prize draws: P3, needs a licence

First-come links for listeners, with an export the auditor can check. Prize draws stay off until counsel signs them off.

  1. Mint the links; read them out or post them.
  2. Export the rewards for the audit.
jusa.RewardLink.create(program="prg_…", count=50, label="breakfast-show")
export = jusa.Export.create(type="rewards", from_="2026-10-01")

Calls: reward_links.create · exports.create · exports.retrieve

Conditional adherence incentives

For Clinics, research trials. needs counsel: health data

Hold a reward and release it when a visit is confirmed. Sensitive-data programs stay off until Jusa approves them.

  1. A sensitive_data program with a lawful_basis, pseudonymous external_ids.
  2. Hold rewards; release or void them from your clinical system.
reward = jusa.Reward.create(program="prg_trial", completion_id=f"visit-{visit.id}",
    recipient={"external_id": participant.code, "phone": participant.phone}, hold=True)

Calls: rewards.create · rewards.release · rewards.void

Community health worker allowances

For Health NGOs. data: P2

A monthly allowance to every CHW, billed to the right cost centre.

  1. Recipients in a group, a cost centre with a GL code.
  2. A monthly schedule to the group.
cc = jusa.CostCenter.create(name="CHW programme", gl_code="6100")
group = jusa.Group.create(name="CHWs", cost_center=cc.id)
jusa.Schedule.create(name="CHW airtime", cadence="monthly", day_of_month=1, hour=8, minute=0,
    group=group.id, product="airtime-usd", amount="5.00")

Calls: recipients.bulk · groups.create · cost_centers.create · schedules.create · reports.spend

In-kind ZESA assistance

For WFP-style programmes.

ZESA tokens to households in a batch, credit reserved up front, dry-run in test mode first.

  1. Each line carries the meter and a token_phone.
  2. Run it in test mode, then live.
jusa.Batch.create(items=[
    {"type": "zesa", "meter": "37261502217", "token_phone": "0772123456", "amount": "10.00",
     "client_reference": "hh-0001"},
], title="October assistance", idempotency_key="oct-assistance")

Calls: batches.preview · batches.create · statements.retrieve

Clinic or antenatal check-in

For Health. P3 needs counsel: health data

A reward link paid only by checking in at the clinic, inside its geofence and hours.

  1. A program with a geofence.
  2. The person checks in from the widget or your app.
jusa.Checkin.create(link="lnk_…", lat=-17.8252, lng=31.0522, accuracy_m=25,
    phone="0772123456", choice="airtime")

Calls: checkins.create · reward_links.create

Learn to earn data

For Edtech. data: P2 rules: P3

Finish lessons, earn data bundles. Live data bundles ship in P2; test mode has a simulated list.

  1. List a data product's bundles.
  2. Reward with type=data and a bundle_id.
bundles = jusa.Product.list_bundles("econet-data-usd")
jusa.Reward.create(program="prg_…", completion_id=f"course-{c.id}",
    recipient={"phone": "0772123456"}, type="data", bundle_id=bundles.data[0].bundle_id)

Calls: products.list_bundles · rewards.create

Exam-season data stipends

For Schools and tutors. data: P2

A weekly stipend through exam season, stopped by deleting the schedule.

  1. A weekly schedule to the class group.
jusa.Schedule.create(name="Exam data", cadence="weekly", day_of_week=0, hour=7, minute=0,
    group="grp_…", product="airtime-usd", amount="2.00")

Calls: schedules.create · schedules.delete

A bursary split across data and ZESA

For Bursary funds. data: P2

Two schedules to the same people: airtime for data, ZESA to their meter.

  1. Recipients carry a meter and a token_phone.
  2. One airtime and one ZESA schedule.
jusa.Schedule.create(name="Bursary ZESA", cadence="monthly", day_of_month=1, hour=8, minute=0,
    recipients=["rcp_…"], product="zesa-usd", amount="10.00")

Calls: recipients.create · schedules.create

Gig and rider allowances

For Delivery and ride apps.

Airtime per completed trip, capped per rider per day.

  1. A per-recipient daily cap on the rider.
  2. Reward each trip, keyed on the trip id.
jusa.Recipient.update("ext:rider-4", daily_cap="3.00")
jusa.Reward.create(program="prg_trips", completion_id=f"trip-{trip.id}",
    recipient="ext:rider-4", amount="0.50")

Calls: recipients.update · rewards.create

Employer ZESA as a benefit

For Employers.

Workforce as it is in the Jusa app: departments, plans, allocation and the GL journal.

  1. Allocate to people by department.
  2. Pull the journal for your GL.
client = jusa.Jusa("jusa_sk_live_…")
client.workforce.journal(from_="2026-09-01", to="2026-09-30", format="csv")

Calls: workforce.allocate · workforce.journal · meters.retrieve

Pay per verified sale

For Field sales teams.

Escrow the commission until the return window closes; void it if the sale comes back.

  1. A program with hold.mode=escrow and auto_release_after.
  2. Void on a return.
jusa.Program.create(name="Sales commission", default_amount="1.00", budget="200.00",
    hold={"mode": "escrow", "auto_release_after": "14d"})

Calls: programs.create · rewards.create · rewards.void

Outage apology credit

For Banks and ISPs.

One batch to every affected customer, safe to retry with one Idempotency-Key.

  1. Build the items from your incident list.
  2. Create the batch with an idempotency key.
jusa.Batch.create(items=[{"target": p, "amount": "1.00"} for p in affected],
    title="Sorry for Tuesday", idempotency_key="incident-2026-10-07")

Calls: batches.create

Diaspora: send ZESA or airtime home

For Remittance apps.

The sender pays by card on a checkout session; you get the attribution and the token. Recurring by card is not possible (there is no stored card): use your own Jusa Credit and a schedule.

  1. Create a checkout session; send the payer to its url.
  2. Hear checkout.session.fulfilled.
session = jusa.CheckoutSession.create(type="zesa", target="37261502217", amount="20.00",
    currency="USD", token_phone="0772123456", success_url="https://app.example/thanks",
    cancel_url="https://app.example/basket", client_reference=f"order-{order.id}")
redirect(session.url)

Calls: checkout_sessions.create · checkout_sessions.retrieve · meters.retrieve

Landlord and tenant split-meter settlement

For Property apps.

Each tenant pays their share by card; the receipt proves what went to the meter.

  1. A checkout session per tenant.
  2. Share the receipt of the delivered send.
receipt = jusa.Send.receipt("snd_…")
print(receipt.units, receipt.customer_name)

Calls: checkout_sessions.create · sends.receipt

Checkout cashback as airtime

For E-commerce. data: P2

A percentage back as airtime after each order, from a program budget.

  1. Reward on order completion, keyed on the order id.
jusa.Reward.create(program="prg_cashback", completion_id=f"order-{order.id}",
    recipient={"phone": order.phone}, amount=f"{order.total * 0.02:.2f}")

Calls: rewards.create

Solar or IoT ZESA auto-buy

For Smart-plug and monitor vendors.

The device reads the token from the send.delivered webhook. token_phone is required: ZETDC needs a number to notify. Cap the device's key.

  1. A restricted key with tokens:read and a daily spend cap.
  2. Send ZESA when the monitor says so; read the token from the webhook.
jusa.Send.create(type="zesa", target=device.meter, amount="5.00",
    token_phone=owner.phone, client_reference=f"auto-{reading.id}")

Calls: keys.create · sends.create · webhook_endpoints.create

Availability-aware buying

For Any ZESA seller.

Check ZESA before promising a token now; queue with max_hold, or fail fast with on_zesa_down=fail.

  1. Read /status or hear zesa.availability.*.
  2. Choose on_zesa_down and max_hold per send.
if jusa.Status.retrieve().zesa.state == "down":
    notify_user("ZESA is down; your token will come when it is back.")
jusa.Send.create(type="zesa", target=meter, amount="10.00", token_phone=phone,
    on_zesa_down="queue", max_hold="6h")

Calls: status.retrieve · status.history · sends.create

Band-aware ZESA quotes

For Budgeting apps.

Show how many units the money buys now and after the monthly reset.

  1. Quote with the meter.
q = jusa.Quote.create(product="zesa-usd", amount="20.00", target="37261502217")
print(q.zesa.units_now, q.zesa.units_after_reset, q.zesa.resets_on)

Calls: quotes.create

AI agent tool calls over MCP

For Assistants and chat bots.

Give an agent an ak key: it sends within a threshold and to known numbers; anything more asks a person, who approves with their payment PIN. Live agents are off until an owner turns them on.

  1. Create an agent key with a threshold.
  2. Point your MCP client at /dev/v1/mcp.
key = jusa.ApiKey.create(kind="ak", label="assistant",
    approval_threshold={"USD": "10.00"})
# MCP server: https://jusa.localhost.co.zw/dev/v1/mcp  (Authorization: Bearer <ak key>)

Calls: keys.create · mcp.call · approvals.list

WhatsApp chatbot commerce

For Bot builders.

Confirm the meter in the chat, send a checkout link, confirm delivery from the webhook.

  1. meters.retrieve for the name to confirm.
  2. A checkout session; reply with its url.
meter = jusa.Meter.retrieve("37261502217")
reply(f"Is this {meter.customer_name} in {meter.address}?")

Calls: meters.retrieve · checkout_sessions.create

Escrowed conditional rewards

For Anyone.

The general primitive: reserve now, decide later. Held credit shows as credit.reserved.

  1. hold=true on the reward.
  2. release (optionally for less) or void.
r = jusa.Reward.create(program="prg_…", completion_id="task-9", recipient="ext:u-9", hold=True)
jusa.Reward.release(r.id, amount="0.40", idempotency_key=f"rel-{r.id}")

Calls: rewards.create · rewards.release · rewards.void · credit.list_reservations

Geofenced event check-in

For Ag shows, churches, retail. P3

Links that pay only at the venue, during the event.

  1. A program with a geofence and a window.
  2. Check-ins from the widget.
jusa.Program.create(name="Ag show", default_amount="0.50", budget="100.00", sources=["links"],
    geofence={"lat": -17.8252, "lng": 31.0522, "radius_m": 300})

Calls: programs.create · checkins.create

Farmer extension incentives

For EcoFarmer-scale programmes.

Reward attendance and adoption, keyed on your farmer ids; batches for the season-end bonus.

  1. Reward per attendance.
  2. A batch at season end.
jusa.Reward.create(program="prg_extension", completion_id=f"attend-{session_id}-{farmer.id}",
    recipient={"external_id": farmer.id, "phone": farmer.phone})

Calls: rewards.create · batches.create

Programmatic family allowance

For Bank standing orders. OAuth family: P3

For your own customers' families: recipients plus a payday schedule. /family needs OAuth user tokens and answers 501 until then.

  1. The family members as recipients.
  2. A payday schedule.
jusa.Schedule.create(name="Gogo's airtime", cadence="payday", hour=9, minute=0,
    recipients=["rcp_…"], product="airtime-usd", amount="5.00")

Calls: recipients.create · schedules.create

Bill-shock rescue

For Telco-adjacent fintech.

You spot a zero balance in your own systems (Jusa cannot see it) and send US$0.50, capped per person per day.

  1. A per-recipient daily cap.
  2. One send per rescue, keyed on your alert.
jusa.Send.create(target=customer.phone, amount="0.50", product="airtime-usd",
    recipient=f"ext:{customer.id}", client_reference=f"rescue-{alert.id}")

Calls: sends.create · recipients.update

Emergency power for medical devices

For Health IoT. needs counsel: health data

A monitor triggers a ZESA reward from the patient's own program: the program's budget is the cap, and on_zesa_down=fail on the program so a caregiver hears at once. (A send's program is attribution only: it draws on no budget.)

  1. A program per patient: choices ["zesa"], a budget, on_zesa_down="fail".
  2. A direct reward per alarm, keyed on it; listen to zesa.availability.* and reward.failed.
jusa.Program.create(name=f"Patient {patient.id}", currency="USD", default_amount="5.00",
    budget="60.00", choices=["zesa"], on_zesa_down="fail")
jusa.Reward.create(program="prg_patient_12", completion_id=f"alarm-{alarm.id}", type="zesa",
    recipient={"external_id": patient.id, "meter": patient.meter, "token_phone": carer.phone})

Calls: programs.create · rewards.create · status.retrieve

Crowdsourced price or outage reports

For Civic tech. P3

Each verified report is an activity; a threshold rule pays out.

  1. A threshold rule.
  2. An activity per verified report.
jusa.Activity.create(recipient="ext:reporter-3", name="report.verified", activity_id=report.id)

Calls: activities.create · rewards.create

Data perk on ticket scan

For Promoters. data: P2

A link printed on the ticket, bound to the buyer's phone.

  1. A link per ticket, bind=otp with the buyer's phone_hint.
jusa.RewardLink.create(program="prg_gig", recipient={"phone": ticket.phone},
    bind="otp", phone_hint=ticket.phone, label=ticket.code)

Calls: reward_links.create

Fundraising thank-you airtime

For Churches and schools.

Thank each donor with a little airtime after their gift.

  1. Reward per donation, keyed on the gift.
jusa.Reward.create(program="prg_thanks", completion_id=f"gift-{gift.id}",
    recipient={"phone": gift.phone}, amount="0.50")

Calls: rewards.create

Payday ZESA nudge

For HR platforms.

ZESA units are banded and reset monthly: a quote shows what the money buys before and after the reset, so staff choose when to buy.

  1. Quote on payday.
  2. A payday schedule for those who opt in.
q = jusa.Quote.create(product="zesa-usd", amount="30.00", target=employee.meter)
send_nudge(employee, q.zesa.units_now, q.zesa.units_after_reset)

Calls: quotes.create · schedules.create