Skip to content
Preview. The Partners API is not live yet, so details on this page can change before launch.

Integration patterns

These patterns cover the decisions most travel agencies and travel desks face when they add eSIMs to a booking flow. None of them is required. Each one avoids a problem that is hard to fix later.

Place the eSIM order only once your own booking is certain, and tie the two together with one reference.

  1. The customer picks a package. You show the price you have decided to charge.
  2. The customer pays you, or the booking is approved on account.
  3. You save the booking with a state such as esim_pending.
  4. You call POST /v1/orders with customer_ref set to your booking number and Idempotency-Key set to the same value.
  5. On completed, you save the order id and the ICCIDs, deliver the eSIM and mark the booking esim_issued.

Save your booking before you call the API. If your server stops between the call and the save, you still know an order may exist, and the idempotency key lets you ask again safely.

Check the price before you order. Call GET /v1/packages/{id} if your cached copy is old, and compare the order’s total with what you expected. An order is charged at the price at the moment you place it.

Most orders are completed in the response. POST /v1/orders waits up to about 20 seconds for the eSIMs, so set your HTTP client timeout to at least 30 seconds. An order that is not finished by then comes back as processing. Its esims list holds the eSIMs issued so far and can be empty. Do not treat that as a failure and do not order again.

  1. Save the order id and show the customer a “your eSIM is being prepared” state.
  2. Wait for the order.completed or order.failed webhook. order.completed is also sent for a partially_completed order, so read status in the event.
  3. As a backstop, fetch GET /v1/orders/{id} every few seconds at first, then less often.
  4. When the order is final, deliver the eSIMs or tell the customer it failed.

An order that makes no progress for 15 minutes is settled automatically: the eSIMs that were not issued are marked as failed and refunded, and the matching event is sent. If you retry the POST with the same Idempotency-Key while a live order is processing, you get the order as it is now.

Handle every final status:

Status What to do
completed Deliver every eSIM in esims.
partially_completed Deliver the eSIMs in esims. There are fewer than quantity. refunded shows what went back to your balance for the rest. Place a new order, with a new idempotency key, for the units that are missing.
failed Nothing was issued and refunded shows what went back to your balance. Tell the customer, or try again with a new idempotency key.
cancelled The order was cancelled in the Business portal or by eSIMify support, and refunded shows the amount returned. Treat its eSIMs as not usable. No webhook is sent: you see it when you fetch the order.

You can practise this in the sandbox with the test_processing and test_order_failed triggers. In the sandbox a replay of a processing order returns the first response again, so fetch the order instead.

customer_ref is your handle on everything eSIMify holds for a sale. It is copied from the order to each eSIM and appears in events.

  • Find an order from a booking: GET /v1/orders?customer_ref=booking-84512.
  • Find the eSIMs of a booking: GET /v1/esims?customer_ref=booking-84512.
  • Daily check: list yesterday’s orders with created_after and created_before, and compare the count and the sum of total less refunded with your own records. created_after includes its own instant and created_before does not, so two windows that share a boundary never miss or repeat an order. Top-ups are not in this list: read them with GET /v1/esims/{iccid}/topups.
  • Orders you did not place: orders your staff place in the Business portal appear in GET /v1/orders and send events too. Ignore the ones whose customer_ref you do not recognise.
  • Missed webhooks: list GET /v1/events and process any event id you have not stored. Events are kept for 30 days. Live eSIM events are stored only while you have an enabled live endpoint, so check eSIM state with GET /v1/esims as well.

Keep customer_ref unique for each sale and free of personal data. A booking number is ideal. A name or an email address is not.

The catalogue changes slowly, and each key has a rate limit. Do not call the API on every page view.

  • Copy the catalogue into your own database at least once a day. Page through GET /v1/packages with limit=100 until has_more is false. A full copy is not a snapshot: if the catalogue changes while you page, a package can be missed or seen twice, so key your copy on id.
  • Serve your destination pages and search from your copy.
  • Store price as a decimal, with its currency.
  • Before you place an order, the order itself is the final check: a package that is no longer on sale gives package_not_found. Refresh your copy when you see it.
  • The id listed for a destination, data size and validity can change. An id you stored keeps working while that package is on sale. Read data_mb and validity_days from the package, not from its name.
  • Use GET /v1/countries to build the destination list, and packages_count to hide destinations with nothing to sell.

An order is refused with 402 insufficient_balance when your balance does not cover it. Avoid meeting that error in front of a customer.

  • Set a low-balance threshold in the Business portal under Settings. Without one, balance.low is never sent.
  • Subscribe to the balance.low webhook and alert the person who can add funds. It is sent once each time your balance crosses the threshold.
  • Read balance_after on each order and raise your own alert at a level that suits your volume. It is the balance right after the order was charged. If part of the order is refunded, your balance is higher by refunded.
  • For a large batch, check GET /v1/balance first and compare it with the sum of the prices.
  • When a 402 does happen, keep the booking in esim_pending, alert your team, and send the same request again once funds have been added in the portal under Wallet. Use the same idempotency key.

Networks fail. A retry is only safe when it cannot buy twice.

  • Send an Idempotency-Key on every POST, and derive it from your own data, not from a random value made at send time.
  • On a timeout, a dropped connection, 429, 500 or 503, send exactly the same request again with the same key.
  • After an order or top-up with the status failed, the same key returns that failed result again. To try the purchase again, use a new key.
  • On 409 request_in_progress, the first request is still running. Wait a moment and send it again.
  • Wait longer between each attempt: for example 1, 2, 4 and 8 seconds. On 429, wait for Retry-After.
  • Give up after a few attempts, leave the booking in esim_pending and alert your team. The key stays valid for at least 24 hours, so a later retry is still safe within that time.
  • A replayed response has the header Idempotent-Replayed: true and status 200. It is the first response as it was sent, so fetch the order if you need its current state.

See Idempotency for the full rules.

One order can hold up to 50 eSIMs of the same package. A large order takes longer and is more likely to come back as processing. For a group that needs different packages, place one order for each package. Give each order its own idempotency key, such as booking-84512-th and booking-84512-ae, and the same customer_ref if you want to find them together.