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.

OperationSide-effectScopeUse
billing.credits.balancereadbilling:readCurrent credit balance
billing.credits.transactionsreadbilling:readLedger history
payment_methods.create_setup_intentcomputebilling:writeStart attaching a card
payment_methods.attachcomputebilling:writePersist a confirmed card
payment_methods.set_defaultcomputebilling:writeChoose the off-session card
billing.topupspendbilling:writeCharge the card → grant credits
billing.set_auto_topupcomputebilling:writeAuto top-up below a threshold

Check the balance

bash
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:

  1. payment_methods.create_setup_intent → returns a client_secret.
  2. Confirm the SetupIntent client-side with Stripe.js (the card data goes straight to Stripe).
  3. payment_methods.attach with the confirmed PaymentMethod id to persist it.
  4. payment_methods.set_default to make it the card used for off-session charges.
bash
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 '{}'
json
{ "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 (minimum 10000 = A$100), or
  • credits — 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.

bash
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 }'
json
{
  "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:

json
{
  "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:

  1. Take client_secret and complete the challenge client-side with Stripe.js (stripe.confirmCardPayment / handleCardAction).
  2. Once the bank confirms, retry billing.topup with the same Idempotency-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:

bash
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.