API reference
← DashboardBase URL: https://4xx.io. All endpoints accept and return JSON. Authenticate with a secret key in the Authorization header:Authorization: Bearer sk_test_…
Getting started in 3 calls
- Generate a test secret key in API keys.
- Configure a Praxis Cashier processor in test mode under Processors. No real credentials needed — the sandbox uses deterministic test cards.
- Create a payment with the curl snippet below and open the returned
session.urlto pay it.
POST /api/public/v1/payments
Create a payment. Returns either a hosted-page URL or a 3DS redirect.
curl https://4xx.io/api/public/v1/payments \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount_minor": 1999,
"currency": "GBP",
"capture": "automatic",
"customer": { "email": "alice@example.com", "country": "GB" },
"return_url": "https://your-site.example/return"
}'Response (201):
{
"id": "pay_...",
"object": "payment",
"status": "pending",
"amount_minor": 1999,
"currency": "GBP",
"session": {
"id": "...",
"client_secret": "cs_...",
"url": "https://4xx.io/pay/.../?cs=cs_..."
},
"next_action": null,
"request_id": "req_..."
}GET /api/public/v1/payments/:id
curl https://4xx.io/api/public/v1/payments/pay_... \
-H "Authorization: Bearer sk_test_..."POST /api/public/v1/payments/:id/refund
curl https://4xx.io/api/public/v1/payments/pay_.../refund \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "amount_minor": 500, "reason": "Customer request" }'Test cards (Praxis sandbox)
| PAN | Outcome |
|---|---|
4111 1111 1111 1111 | Success — captured |
4000 0000 0000 0002 | Declined (card_declined) |
4000 0000 0000 3220 | 3DS challenge required, then success |
4000 0000 0000 0341 | Success, then chargeback on inbound webhook |
Use any future expiry, any 3-digit CVC, and any cardholder name.
Webhooks
Register a webhook endpoint under Webhooks to receive lifecycle events. We POST a JSON body and sign it asx-4xx-signature: t=<ts>,v1=<hmac_sha256(secret, ts + "." + body)>.
Event types:
payment.created,payment.requires_action,payment.authorized,payment.captured,payment.failedpayment.refunded,payment.partially_refundedpayment.chargebacked
Error envelope
{
"error": {
"type": "invalid_request_error",
"code": "amount_too_small",
"message": "Amount must be at least 50.",
"request_id": "req_..."
}
}HTTP status codes: 400 bad request, 401 unauthenticated, 403 insufficient scope, 404 not found, 409 invalid state, 500 server error.