Skip to content
Sandbox open: magic test cards, hosted checkout and signed webhooks · 55 payment methods in the catalogue, cards to crypto · Built and hosted in the EEA · Read the API reference at /developers

Sandbox open: magic test cards, hosted checkout and signed webhooks

Documentation

API reference

Sandbox endpoints

The four test-mode endpoints: tokenise a magic card, advance an async payment, simulate a webhook, and read the card table from the API.

Updated

Any cp_*_test_ key routes to the simulator. There is no mode parameter: the key is the environment, and a test key cannot reach a live connector however hard you try.

These four endpoints exist in test mode only. A live key gets 403 authorization_error with code test_mode_required.

The card numbers, the outcome overrides and the challenge-page behaviour are on the sandbox and test cards page, rendered from the simulator's own module. This page is the endpoint reference.

Tokenise a card

POST /v1/sandbox/tokens → 201. The only endpoint in the API that accepts card data. Publishable keys may call it, because this is what hosted fields do from the browser.

ParameterRequiredNotes
typenocard (default), apple_pay, google_pay.
numberyesA magic card number. Spaces and dashes are fine.
exp_monthyes1–12.
exp_yearyesAny future year.
cvcyes3 or 4 digits. Any value.
nameno≤ 200 characters.
curl https://cleared-pay.com/api/v1/sandbox/tokens \
  -H "Authorization: Bearer $CLEARED_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"card","number":"4242424242424242","exp_month":12,"exp_year":2029,"cvc":"123"}'
{
  "id": "tok_01JBXQ2M4K7T9V3ZC8HR5N6PWD",
  "object": "token",
  "type": "card",
  "card": {
    "brand": "visa",
    "last4": "4242",
    "bin": "424242",
    "exp_month": 12,
    "exp_year": 2029
  },
  "livemode": false,
  "created": "2026-08-19T10:15:00Z",
  "expires_at": "2026-08-19T10:30:00Z"
}

What is kept: brand, last4, BIN, expiry, a fingerprint, and the outcome the number maps to. What is not: the number and the security code. They exist for the duration of the request and are never written anywhere, including the request log, which redacts card-shaped strings as a second line of defence.

Tokens are single-use and valid for 15 minutes. A number outside the magic-card table is 400 invalid_card_number; a past expiry is 400 card_expired. The endpoint ignores Idempotency-Key — a token is single-use, so replaying one would give you a token you cannot spend.

Advance an async payment

POST /v1/sandbox/advance/:paymentId → 200 with the payment.

curl -X POST https://cleared-pay.com/api/v1/sandbox/advance/pay_01JBXQ6P9R4T2V7XZ1BD5GH8KM \
  -H "Authorization: Bearer $CLEARED_SECRET_KEY"

Resolves a processing payment now instead of waiting out its 30 seconds — useful in a test suite, where waiting is the slowest thing you will do. A payment in any other status is 422 payment_not_advanceable, naming the status it is in.

A processing payment also resolves lazily on read once its resolve time has passed, so GET /v1/payments/:id after 30 seconds gives the same result without this call.

Simulate a webhook

POST /v1/sandbox/simulate-webhook → 201 with the event and its delivery attempts.

ParameterRequiredNotes
typeyesAny event type.
endpointnoA whe_ id. Omitted, every subscribed endpoint receives it.
resourcenoAn id of yours to use as data.object. Omitted, the newest matching resource is used; with none, a clearly synthetic sample.
curl https://cleared-pay.com/api/v1/sandbox/simulate-webhook \
  -H "Authorization: Bearer $CLEARED_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"payment.captured","resource":"pay_01JBXQ2M4K7T9V3ZC8HR5N6PWD"}'

The event is stored like any other — real id, real signature, livemode: false — and delivered inline, so the HTTP result of your receiver comes back in the response rather than in a log you have to go and find:

{
  "id": "evt_01JBXQ9R4V8X6Z2B5DG9KL3NQS",
  "object": "event",
  "type": "payment.captured",
  "api_version": "2026-08-19",
  "livemode": false,
  "created": "2026-08-19T10:17:00Z",
  "data": { "object": { "id": "pay_01JBXQ2M4K7T9V3ZC8HR5N6PWD", "object": "payment", "status": "captured" } },
  "attempts": [
    {
      "id": "wha_01JBXQ9R6X2Z8B4D7FH1MN5QTV",
      "object": "webhook_attempt",
      "event": "evt_01JBXQ9R4V8X6Z2B5DG9KL3NQS",
      "endpoint": "whe_01JBXQ8Q2T6V4X9Z3CF7HJ1LNP",
      "attempt": 1,
      "status": "delivered",
      "http_status": 200,
      "response_body": "ok",
      "error": null,
      "duration_ms": 142,
      "next_retry_at": null,
      "attempted_at": "2026-08-19T10:17:00Z"
    }
  ]
}

A synthetic sample uses obviously fake ids such as pay_SIMULATED0000000000000000, so nothing in your logs can be mistaken for a real resource. If no endpoint is subscribed to the type you asked for, the answer is 422 no_matching_endpoint.

Signatures on a simulated event are real, so this is the right way to test your verification code — including the rotation case: roll the secret, then simulate, and check that your receiver accepts a header carrying two v1= values.

Read the card table

GET /v1/sandbox/test-cards → 200 with the same table the sandbox page renders and the simulator reads, so a test fixture can be generated from it instead of copied.

curl https://cleared-pay.com/api/v1/sandbox/test-cards \
  -H "Authorization: Bearer $CLEARED_SECRET_KEY"
{
  "object": "list",
  "data": [
    {
      "number": "4242 4242 4242 4242",
      "brand": "visa",
      "outcome": "approved",
      "label": "Approved"
    },
    {
      "number": "4000 0000 0000 9995",
      "brand": "visa",
      "outcome": "declined",
      "decline_code": "insufficient_funds",
      "decline_is_soft": true,
      "label": "Declined — insufficient_funds (soft)"
    }
  ],
  "metadata_overrides": {
    "_cp_test_outcome": ["approved", "declined:<decline_code>", "failed", "requires_action"],
    "_cp_test_latency_ms": "integer, delays the response",
    "_cp_test_http_error": "HTTP status to return instead of processing"
  },
  "has_more": false,
  "next_cursor": null
}

Abridged above; the response carries all nine cards. A publishable key may call this one too.

What the sandbox does not simulate

  • Wallets. Apple Pay and Google Pay need a real device flow. The hosted checkout page excludes them in test mode rather than faking a result.
  • Settlement and payouts. Fees are calculated and snapshotted at capture, so the numbers are real; money never moves.
  • Disputes. The event types exist; there is no way to raise one yet.
  • Issuer variety. Every simulated card is issued in EE with funding: "credit". Do not write logic that depends on issuer country until you have live data.