Test the full Southbill API with test keys — real test-mode payments, zero real money.
Two completely separated worlds: live and test.
The Southbill sandbox is a full test environment, not a simulator. A test key talks to a real payment processor in test mode: real checkout pages, real 3-D Secure, real webhooks — only no money moves.
Two separated worlds
|
Live |
Sandbox |
| Key prefix |
sk_live_… |
sk_test_… |
| Account |
your verified account |
an auto-created test account |
| Data |
live tables |
separate sandbox storage |
| Money |
real |
none |
| Fees / payouts |
yes |
never |
| Verification (KYC) emails |
yes |
never |
There is no fallback between the two. A test key can never read, write or refund live data, and a live key never touches sandbox data.
Getting a sandbox
- Open Dashboard → Developers → Sandbox.
- Click Create sandbox account. Your test account is created and verified instantly — no second onboarding.
- Click Create test key. Test keys start with
sk_test_ and stay visible: you can reveal and copy them any time.
Test keys do not count against the API key limit of your plan.
Base URL and auth
https://api.southbill.com/v1
Authorization: Bearer sk_test_…
Everything else — paths, request bodies, errors, idempotency, pagination — is identical to live. Switching to production is only a key change.
Data lifetime
Sandbox records expire 30 days after creation and are deleted automatically. You can also wipe everything from the Sandbox tab.
Signed test events, delivery log and replay.
Webhook endpoints are mode-scoped: an endpoint created with a test key only ever receives test events, and never sees live traffic.
Create a test endpoint
curl https://api.southbill.com/v1/webhook_endpoints \
-H "Authorization: Bearer sk_test_…" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-app.example.com/webhooks","enabled_events":["*"]}'
The response contains the signing secret (whsec_…) — store it, it is shown once.
Verify the signature
Identical to live: the signature header carries a timestamp and an HMAC-SHA256 of timestamp.payload. See Webhooks → Verify signatures.
Events you will see in test
checkout.session.completed, payment_intent.succeeded, payment_intent.payment_failed, checkout.session.refunded, charge.dispute.created, invoice.paid, invoice.partially_paid, invoice.payment_succeeded, invoice.installment.paid, invoice.installment.due, invoice.installment.overdue, subscription.created, subscription.updated, subscription.canceled.
Payout events exist in live only.
Delivery log & replay
curl "https://api.southbill.com/v1/webhook_endpoints/{id}/deliveries" \
-H "Authorization: Bearer sk_test_…"
Failed deliveries are retried automatically with exponential backoff, exactly as in live.
No public URL yet?
Point the endpoint at any HTTPS request bin, or tunnel your local server (for example ngrok http 3000) and use the public HTTPS URL.
One full pass: product, checkout, payment, refund, invoice, subscription.
This walkthrough runs the complete flow with curl. It works the same on macOS, Linux and Windows (CMD or PowerShell).
Set your key first:
export SB=sk_test_… # macOS / Linux
set SB=sk_test_… # Windows CMD
1. Create a product and a price
curl https://api.southbill.com/v1/products -H "Authorization: Bearer $SB" \
-H "Content-Type: application/json" \
-d '{"name":"Test Plan","default_price":{"unit_amount":2500,"currency":"eur"}}'
2. Create a customer
curl https://api.southbill.com/v1/customers -H "Authorization: Bearer $SB" \
-H "Content-Type: application/json" \
-d '{"email":"tester@example.com","name":"Test Buyer"}'
3. Create a checkout session and pay it
curl https://api.southbill.com/v1/checkout/sessions -H "Authorization: Bearer $SB" \
-H "Content-Type: application/json" \
-d '{"amount":2500,"currency":"eur","customer_email":"tester@example.com",
"success_url":"https://example.com/ok","cancel_url":"https://example.com/no"}'
Open the returned url in a browser and pay with 4242 4242 4242 4242.
4. Check the payment
curl "https://api.southbill.com/v1/payments?limit=5" -H "Authorization: Bearer $SB"
5. Refund it
curl https://api.southbill.com/v1/refunds -H "Authorization: Bearer $SB" \
-H "Content-Type: application/json" \
-d '{"payment_intent":"pi_…","amount":1000,"reason":"requested_by_customer"}'
6. Create an invoice with installments
curl https://api.southbill.com/v1/invoices -H "Authorization: Bearer $SB" \
-H "Content-Type: application/json" \
-d '{"customer":"cus_…","currency":"eur","payment_mode":"partial",
"items":[{"description":"Consulting","quantity":1,"unit_amount":120000}],
"installments":[{"amount":60000,"due_date":"2026-10-01"},
{"amount":60000,"due_date":"2026-11-01"}]}'
Then POST /v1/invoices/{id}/send, inspect GET /v1/invoices/{id}/installments, and settle with POST /v1/invoices/{id}/mark_paid.
7. Create a subscription
curl https://api.southbill.com/v1/subscriptions -H "Authorization: Bearer $SB" \
-H "Content-Type: application/json" \
-d '{"customer":"cus_…","price":"price_…"}'
8. Read the event stream
curl "https://api.southbill.com/v1/events?limit=20" -H "Authorization: Bearer $SB"
Every object in these responses has "livemode": false. When the pass is green, swap sk_test_ for sk_live_ — nothing else changes.