ChoEsim

ChoEsim Partner API: sandbox guide

What the sandbox is

The sandbox is a separate, simulated copy of the API. You use it with a test key that starts with cesim_test_.

A test key goes through the same checks as a live key: authentication, scopes, validation, idempotency, rate limits, wallet rules and webhooks. Your code can use the same paths in both modes.

Test and live are two different keys. Going live means creating a live key in the partner portal, which is possible after your first top-up has been credited. You do not "switch" a test key.

Responses show livemode: false for test data.

Fund the test wallet

A test wallet starts at zero. Add fake money with POST /sandbox/wallet/topup.

curl -s -X POST "$CESIM_BASE/sandbox/wallet/topup" \
  -H "Authorization: Bearer cesim_test_REPLACE_ME" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"amount_minor": 10000}'

Magic values

The sandbox is deterministic. The plan id or the order reference decides what happens. Use these values to test every path.

You sendWhat happens
plan_id: test_plan_ok_1gb_7dThe order is fulfilled immediately.
plan_id: test_plan_slow_1gb_7dThe order is accepted. It stays in processing for 30 seconds, then it is fulfilled.
plan_id: test_plan_unknown_1gb_7dThe order is in processing with status_detail: under_review for 120 seconds, then it is fulfilled.
plan_id: test_plan_fail_1gb_7dThe order fails. The wallet is credited. An order.failed event is sent.
plan_id: test_plan_oos_1gb_7d422 plan_unavailable.
plan_id: test_plan_price_change_1gb_7d409 price_changed on the first attempt for each reference.
plan_id: test_plan_nocancel_1gb_7dThe order is fulfilled. A cancel call returns 409 esim_not_cancellable.
reference starts with sbx-429-429 rate_limited with Retry-After: 2.
reference starts with sbx-insufficient-402 insufficient_balance.

The fulfilled test eSIMs have a fake activation string like LPA:1$sandbox.choesim.invalid$TEST-3F9A2C01. It cannot be installed on a phone.

Simulate each path

Success

Order test_plan_ok_1gb_7d. You get 201 with status: fulfilled.

A delay (30 seconds)

Order test_plan_slow_1gb_7d. You get 202, status: processing and a Location header.

curl -i -X POST "$CESIM_BASE/orders" \
  -H "Authorization: Bearer cesim_test_REPLACE_ME" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reference":"sbx-slow-001","items":[{"plan_id":"test_plan_slow_1gb_7d","quantity":1}]}'

Then poll GET /orders/{id} and wait for fulfilled. Honour Retry-After. If you have a webhook endpoint, you also get order.fulfilled.

The state changes with time. It is computed from the order's created_at, so you do not have to keep a connection open.

If you repeat the same request with the same Idempotency-Key after the 30 seconds, you get 201 with Idempotent-Replayed: true. The status code follows the current state of the order.

The unknown-outcome path (120 seconds)

In real life, a supplier call can time out and ChoEsim cannot tell at once whether the eSIM was bought. The order then shows status: processing and status_detail: under_review. The wallet stays debited until the outcome is proven.

Order test_plan_unknown_1gb_7d to practise this. For 120 seconds the order is processing with under_review, then it becomes fulfilled.

What your code must do:

  1. Treat processing as "not finished". It is not a failure.
  2. Never create a second order. Poll GET /orders/{id}, or repeat the same request with the same Idempotency-Key.
  3. Do not give your customer a refund or a retry until you see failed.
  4. Expect that a real order can stay in under_review longer than 120 seconds. In live mode, ChoEsim staff check it. The sandbox gives no timing promise for live mode.

An order.delayed event may be sent for a slow order. It is informational.

A definite failure

Order test_plan_fail_1gb_7d. The order ends as failed, the wallet is credited, and order.failed is sent. Check GET /wallet/entries to see the credit.

A plan that cannot be bought

Order test_plan_oos_1gb_7d. You get 422 plan_unavailable. Nothing is debited.

A price change

Order test_plan_price_change_1gb_7d with expected_total_minor set. The first attempt for a given reference returns 409 price_changed. Nothing is debited. The error body includes expected_total_minor and current_total_minor.

Use it to test this flow: read current_total_minor, decide whether to accept it, then send the order again with the new total. Because the order was refused before any money was taken, nothing is stored for the Idempotency-Key. You may send the corrected request with the same key, or with a new key. Both work.

The 422 idempotency_key_reuse error applies only when a key is already attached to an order that was created and you send a different body with it.

A cancel that is refused

Order test_plan_nocancel_1gb_7d, then call POST /orders/{id}/cancel. You get 409 esim_not_cancellable.

Rate limit

Send an order whose reference starts with sbx-429-. You get 429 rate_limited with Retry-After: 2. Wait two seconds, then retry.

Not enough money

Send an order whose reference starts with sbx-insufficient-. You get 402 insufficient_balance. Nothing is debited.

Idempotency and duplicates

You can test these without magic values:

Webhooks in the sandbox

A test-mode key sends real webhooks to a test-mode endpoint. Register a real HTTPS endpoint with a test key and you receive real, signed requests.

Test endpoints follow the same URL rules as live ones: https, port 443, public address, no redirects. A localhost URL is rejected. For local development, use a tunnel service that gives you a public HTTPS address.

Webhook events from test mode have livemode: false.

Limits of the sandbox

A real purchase on a live key is a separate process agreed with ChoEsim. Never use the live key to "test".