Billing & credits
Geopera bills in credits: a prepaid balance you spend on orders, processing, and
other external_spend operations. Credits are pegged at 100 credits = A$1. You can
see prices in your local currency, but the wallet and settlement are in AUD.
This guide covers the customer money path: check your balance, top up (including the
3-D Secure case), and automate top-ups. All payment operations require the billing:write scope; reads require billing:read.
| Operation | Side-effect | Scope | Use |
|---|---|---|---|
billing.credits.balance | read | billing:read | Current credit balance |
billing.credits.transactions | read | billing:read | Ledger history |
payment_methods.create_setup_intent | compute | billing:write | Start attaching a card |
payment_methods.attach | compute | billing:write | Persist a confirmed card |
payment_methods.set_default | compute | billing:write | Choose the off-session card |
billing.topup | spend | billing:write | Charge the card → grant credits |
billing.set_auto_topup | compute | billing:write | Auto top-up below a threshold |
Check the balance
curl -s https://api.geopera.com/v1/op/billing.credits.balance \
-H "Authorization: Bearer $TOKEN"Reads have idiomatic GET routes, so this is a plain GET. The ledger behind it is
itemised — every reservation, charge, refund, and grant — and reachable via billing.credits.transactions.
Attach a card
Top-ups charge a saved card off-session, so attach one first. The flow uses Stripe’s SetupIntent so card details never touch Geopera:
payment_methods.create_setup_intent→ returns aclient_secret.- Confirm the SetupIntent client-side with Stripe.js (the card data goes straight to Stripe).
payment_methods.attachwith the confirmed PaymentMethod id to persist it.payment_methods.set_defaultto make it the card used for off-session charges.
curl -s -X POST https://api.geopera.com/v1/op/payment_methods.create_setup_intent \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}'{ "client_secret": "seti_..._secret_...", "setup_intent_id": "seti_..." }Top up credits
billing.topup charges the org’s default card and grants credits. Specify the amount
one of two ways:
aud_cents— the dollar amount to charge (minimum10000= A$100), orcredits— the credit count to grant (the charge is derived from it).
A +5% volume bonus applies above A$20,000. Send an Idempotency-Key (threaded to
Stripe) so a retry charges and grants at most once.
curl -s -X POST https://api.geopera.com/v1/op/billing.topup \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: top_8a1f...-unique" \
-d '{ "aud_cents": 50000 }'{
"topup_attempt_id": "ta_5c2b...",
"payment_intent_id": "pi_3Q...",
"status": "succeeded",
"credits_granted": 50000,
"new_balance": 73500,
"bonus_credits": 0
}The 3-D Secure retry
If the bank requires a Strong Customer Authentication (3-D Secure) challenge, the top-up
can’t complete off-session. You get a 402 Payment Required whose body carries a client_secret at the top level:
{
"title": "Authentication Required",
"status": 402,
"detail": "Card requires authentication (3DS) — customer must complete on-session",
"client_secret": "pi_3Q..._secret_...",
"requires_action": true
}Handle it like this:
- Take
client_secretand complete the challenge client-side with Stripe.js (stripe.confirmCardPayment/handleCardAction). - Once the bank confirms, retry
billing.topupwith the sameIdempotency-Key— the now-authenticated PaymentIntent settles and credits are granted exactly once.
Other 402 cases: no default card (Payment Method Required — attach one) and card declined (Card Declined — try another card). A Stripe outage surfaces as 502, and an unconfigured billing backend as 503.
Automate top-ups
Keep the balance above a floor without manual intervention:
curl -s -X POST https://api.geopera.com/v1/op/billing.set_auto_topup \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"threshold_credits": 5000,
"topup_amount_credits": 50000,
"daily_charge_cap_aud": 500
}'When the balance drops below threshold_credits, the platform charges the default card
to grant topup_amount_credits, never exceeding daily_charge_cap_aud in a day.
Enabling auto top-up requires a default card.
How spend operations charge
Every external_spend operation (placing an order, accepting a tasking quote, creating
a processing job) reserves credits up front, then settles on the outcome:
- Reserve at placement — your available balance drops immediately.
- Capture on success — the reservation becomes a charge.
- Refund / void on cancel or failure — the reservation is returned.
This reserve-then-settle model is why an order or job never silently over- or under-charges, and why a cancelled order returns your credits. If you have no credits, free organizations can instead authorize a card hold at checkout (captured on delivery, voided on failure) — see billing modes in Ordering archive imagery.
Enterprise invoicing
Enterprise organizations don’t prepay a wallet — their spend accrues to a monthly
invoice instead of reserving credits. Spend operations behave identically from the
caller’s perspective (billingMode: "enterprise" in the response); the difference is in
settlement. Approval gates for large purchases (billing.approvals.*) are available for
organizations that opt into them.