API reference

← Dashboard

Base 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

  1. Generate a test secret key in API keys.
  2. Configure a Praxis Cashier processor in test mode under Processors. No real credentials needed — the sandbox uses deterministic test cards.
  3. Create a payment with the curl snippet below and open the returned session.url to 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)

PANOutcome
4111 1111 1111 1111Success — captured
4000 0000 0000 0002Declined (card_declined)
4000 0000 0000 32203DS challenge required, then success
4000 0000 0000 0341Success, 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:

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.