Skip to Content
🚀 Trusted by travel businesses worldwide - Roamify eSIM API

eSIM API - Refunds

Table of Contents

How Refund Requests Work

A refund request asks Roamify support to refund one eSIM of one of your orders. You submit a request, Roamify support reviews it, and the refund happens only after support approves it. Submitting a request never moves money by itself, and it does not revoke the eSIM.

  • Every request is decided by Roamify support. A request for an eSIM that was already installed or has used data is accepted and reviewed, not refused up front.
  • The only request refused immediately is one for an eSIM that was already refunded (422).
  • An eSIM can have one open request at a time. A second request while one is PENDING or PROCESSING is rejected with 409 and the refundId of the open request.
  • The refund is paid back the way the order was paid. For an order paid with your balance, the amount is credited back to your balance.
  • In the partner portal, only team members with the ADMIN or OPERATION role can submit and cancel requests. Other roles can only track them. API requests are made with your API key.
💡

Track a request with Get a Refund Request, or configure a webhook URL to receive a Refund event when a request is refunded or rejected.

Request a Refund in the Partner Portal

You can also file and track requests in the Roamify partner portal. It uses the same rules as the API.

  • Inventory (unassigned bundles): choose Request refund on a bundle. A side sheet opens with the eSIM ID already filled in. Pick a reason, add notes if you like, and submit.
  • eSIMs: open View eSIM on an eSIM and choose Request refund. The sheet opens with the ICCID already filled in. Pick a reason and submit.
  • Refund Requests page: lists every request with its status, lets you search by ICCID, cancel a pending request and open its details. New refund request lets you submit up to 50 ICCIDs at once, or one eSIM ID for an eSIM that has no ICCID yet.

The reason is required. When the reason is OTHER, the notes are required too (at least 10 characters).

Create a Refund Request

To request a refund for one eSIM, send a POST request to the /api/refund endpoint.

You are required to provide the following parameters in the request body:

  • productType: What is refunded. This is a required field of type string. Only esim is supported for now. Any other value is answered with 422 REFUND_PRODUCT_TYPE_NOT_SUPPORTED.
  • iccid or esimId: Which eSIM. Provide exactly one of them, both of type string. Use iccid for an eSIM that was redeemed. Use esimId for an eSIM that has no ICCID yet, such as a bundle that was never assigned.
  • reason: Why you are asking for the refund. This is a required field of type string. See Refund Reasons.
  • notes: Free text for the support team. This is an optional field of type string of up to 1000 characters. It is required, with at least 10 characters, when reason is OTHER.

Usage - request a refund

curl --request POST \ --url 'https://api-dev.getroamify.com/api/refund' \ --header 'Authorization: Bearer {{token}}' \ --header 'Content-Type: application/json' \ --data '{ "productType": "esim", "iccid": "8935303411619613", "reason": "UNABLE_TO_INSTALL", "notes": "The customer could not scan the QR code on an iPhone 12." }'

Response

A new request answers 201 with the refund request object and a tracking object that tells you where to poll it.

{ "refundId": "9f3c1c0e-6a52-4b8e-9d0a-0d3f1e2a7c11", "status": "PENDING", "productType": "esim", "item": { "esimId": "74b73655-0fa7-49e4-bab6-eb80b5522104", "iccid": "8935303411619613", "orderId": "ee9357c2-0000-0000-0000-000000000000", "countryName": "Bahrain", "optionName": "5 GB - 3 Days" }, "reason": "UNABLE_TO_INSTALL", "notes": "The customer could not scan the QR code on an iPhone 12.", "amount": null, "currency": null, "balanceCreditUsd": null, "transaction": null, "rejectionReason": null, "rejectionNote": null, "createdAt": 1762570000, "updatedAt": 1762570000, "processedAt": null, "cancelledAt": null, "history": [ { "status": "PENDING", "at": 1762570000 } ], "tracking": { "method": "GET", "path": "/api/refund?refundId=9f3c1c0e-6a52-4b8e-9d0a-0d3f1e2a7c11" } }

Create Refund Requests in Bulk

To request refunds for several eSIMs in one call, send a POST request to the /api/refund/bulk endpoint.

  • productType: As above. It applies to every entry.
  • refunds: An array of between 1 and 20 entries. Each entry has the same iccid or esimId (exactly one), reason and notes fields as a single request.
⚠️

A bad entry fails the whole call with 400, and nothing is created. Fix the entry and send the call again. Entries that are valid but refused individually (for example an eSIM that already has an open request) are reported in the results instead.

curl --request POST \ --url 'https://api-dev.getroamify.com/api/refund/bulk' \ --header 'Authorization: Bearer {{token}}' \ --header 'Content-Type: application/json' \ --data '{ "productType": "esim", "refunds": [ { "iccid": "8935303411619613", "reason": "NO_CONNECTIVITY" }, { "esimId": "74b73655-0fa7-49e4-bab6-eb80b5522104", "reason": "OTHER", "notes": "Customer cancelled the trip." } ] }'

The call answers 200 with one result per entry, in the order you sent them:

{ "summary": { "requested": 2, "created": 1, "failed": 1, "duplicates": 0 }, "results": [ { "index": 0, "iccid": "8935303411619613", "status": "CREATED", "refund": { "refundId": "9f3c1c0e-6a52-4b8e-9d0a-0d3f1e2a7c11", "status": "PENDING" } }, { "index": 1, "esimId": "74b73655-0fa7-49e4-bab6-eb80b5522104", "status": "FAILED", "error": { "code": "REFUND_REQUEST_ALREADY_EXISTS", "message": "A refund request is already open for this eSIM", "refundId": "2b1d7e40-0000-0000-0000-000000000000" } } ] }

Each result has a status of:

  • CREATED: the request was created. refund holds it.
  • FAILED: the request was refused. error holds the code and message, plus reasons (for REFUND_NOT_ELIGIBLE) or the open refundId (for REFUND_REQUEST_ALREADY_EXISTS).
  • DUPLICATE: the same iccid or esimId appeared earlier in the call. Only the first is created, and duplicateOf is the index of the entry that was kept.

Get a Refund Request

To get one request, send a GET request to the /api/refund endpoint with exactly one of these query parameters:

  • refundId: the request.
  • esimId: the newest request for that eSIM.
  • iccid: the newest request for that ICCID.
curl --request GET \ --url 'https://api-dev.getroamify.com/api/refund?refundId=9f3c1c0e-6a52-4b8e-9d0a-0d3f1e2a7c11' \ --header 'Authorization: Bearer {{token}}'

It answers 200 with the refund request object, or 404 REFUND_REQUEST_NOT_FOUND.

List Refund Requests

To list your requests, newest first, send a GET request to the /api/refunds endpoint. All query parameters are optional:

  • status: Only requests with this status.
  • iccid or esimId: Only requests for that eSIM. Send at most one of them.
  • limit: Page size, from 1 to 100.
  • cursor: The nextCursor of the previous page.
curl --request GET \ --url 'https://api-dev.getroamify.com/api/refunds?status=PENDING&limit=20' \ --header 'Authorization: Bearer {{token}}'
{ "items": [ { "refundId": "9f3c1c0e-6a52-4b8e-9d0a-0d3f1e2a7c11", "status": "PENDING" } ], "nextCursor": null }

When nextCursor is not null, send it back as cursor to get the next page.

Cancel a Refund Request

To withdraw a request, send a PUT request to /api/refund/{refundId}/cancel. Only a request that is still PENDING can be cancelled. Once support has acted on it, the call answers 409 REFUND_INVALID_STATUS.

curl --request PUT \ --url 'https://api-dev.getroamify.com/api/refund/9f3c1c0e-6a52-4b8e-9d0a-0d3f1e2a7c11/cancel' \ --header 'Authorization: Bearer {{token}}'

It answers 200 with the cancelled request, whose status is CANCELLED. You can submit a new request for the same eSIM afterwards.

The Refund Request Object

FieldTypeDescription
refundIdstringThe ID of the request.
statusstringSee Refund Statuses.
productTypestringAlways esim for now.
itemobjectThe eSIM: esimId, orderId, and when known iccid (only once the eSIM was redeemed), referenceId, countryName and optionName.
reasonstringSee Refund Reasons.
notesstringYour notes.
amountnumber | nullThe amount refunded, in currency. null until the request is REFUNDED.
currencystring | nullThe currency of amount.
balanceCreditUsdnumber | nullThe amount credited to your balance in USD, for orders paid with your balance.
transactionobject | nullThe refund of the payment: id, paymentMethod, paymentStatus and refundedAt.
rejectionReasonstring | nullWhy support rejected it: DATA_USED, ESIM_ACTIVATED, OUTSIDE_REFUND_WINDOW, ESIM_EXPIRED, NETWORK_CONDITIONS_ONLY, INSUFFICIENT_INFORMATION, DUPLICATE_REQUEST or OTHER.
rejectionNotestring | nullSupport’s explanation, when given.
createdAt, updatedAt, processedAt, cancelledAtnumber | nullUnix timestamps in seconds.
historyarrayEvery status the request went through, as { status, at }.

Refund Reasons

ReasonUse it when
UNABLE_TO_INSTALLThe eSIM could not be installed or activated on the device.
NO_CONNECTIVITYThe eSIM is installed but there is no data connection at the destination.
WRONG_PACKAGEThe wrong destination or data plan was purchased.
DUPLICATE_PURCHASEThe eSIM was purchased more than once by mistake.
TRIP_CANCELLEDThe trip was cancelled or changed.
DEVICE_NOT_COMPATIBLEThe customer’s device does not support eSIM or is locked.
CUSTOMER_REQUESTThe customer changed their mind.
OTHERAnything else. notes of at least 10 characters are required.

Refund Statuses

StatusMeaning
PENDINGSubmitted. Roamify support has not acted on it yet. You can cancel it.
PROCESSINGApproved. The refund is being processed.
REFUNDEDThe refund is complete and the eSIM was deactivated.
REJECTEDSupport rejected the request. See rejectionReason and rejectionNote.
CANCELLEDYou cancelled the request before review.

Errors

Every error has the form { "message": "...", "status": "failed", "code": "..." }.

HTTPcodeMeaning
400INVALID_PARAMETERA parameter is missing or invalid. For example both or neither of iccid and esimId, more than 20 bulk entries, or an invalid limit.
400REFUND_INVALID_REASONreason is not one of the supported reasons.
400REFUND_NOTES_REQUIREDreason is OTHER without notes of at least 10 characters.
404ESIM_NOT_FOUNDNo eSIM of your account has that iccid or esimId.
404REFUND_REQUEST_NOT_FOUNDNo request matches the refundId, esimId or iccid.
409REFUND_REQUEST_ALREADY_EXISTSThe eSIM already has an open request. The body carries its refundId.
409REFUND_INVALID_STATUSOnly a PENDING request can be cancelled.
422REFUND_NOT_ELIGIBLEThe eSIM cannot be refunded. reasons lists why, for example ESIM_ALREADY_REFUNDED.
422REFUND_PRODUCT_TYPE_NOT_SUPPORTEDOnly esim is supported.
500REFUND_CREATE_FAILED, REFUND_GET_FAILED, REFUND_CANCEL_FAILED, REFUND_SERVICE_NOT_CONFIGUREDA temporary failure. Try again later.

Authentication errors (401, 403, 404, 429) carry a code as well. See Authentication.