> ## Documentation Index
> Fetch the complete documentation index at: https://docs.esimstory.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Request a refund

> Ask for one eSIM to be refunded

Opens a refund request for one eSIM. The answer comes back straight away as
`202 Accepted` with the request in `pending`. The decision follows later, as a
[refund webhook](https://docs.esimstory.com/endpoints/webhooks.md#refund-events).

Every request is reviewed by our team, against the eSIM's status and what you
report in `note`. Review can take several business days, because a refund is
only granted once the eSIM is confirmed unusable.

> [!NOTE]
>
> The refund policy is set by your agreement with us (clause 6.3 and Schedule 1),
> not by this endpoint. In short: before activation, an eSIM that cannot be used
> is refunded in full once verified; after activation, only a service fault on
> our side is refunded or replaced; a change of mind or changed travel plans is
> never refunded. An approved refund is settled as a credit on your next
> invoice.

## Path parameter

**topup\_id** `string` *required*

The unit's `topup_id`, exactly as returned in the order response and repeated
in its webhook.

## Request body

**reason** `string` *required*

Why the eSIM is being refunded. One of:

| `reason` | Use when |
| - | - |
| `device_not_supported` | The customer's device does not support eSIM, or is carrier-locked |
| `not_received` | The customer never received the eSIM |
| `activation_failed` | The profile installed but will not connect at the destination |
| `service_fault` | The eSIM connected but the service did not work as sold |
| `other` | None of the above — explain in `note` |

There is no code for a change of mind: those are not refunded. We may add
codes; we will not remove one without notice in the
[changelog](https://docs.esimstory.com/changelog.md).

**note** `string`

What the customer reported, up to 1,000 characters. The more specific, the
faster the review — device model, what they tried, any error shown. We may
still ask you for screenshots or settings by email.

```bash Endpoint theme={null}
POST /v1/api_partner/esims/20250129123456123456789012/refund
```

```json Body theme={null}
{
  "reason": "activation_failed",
  "note": "iPhone 13, installed before departure. No service in Tokyo with roaming on; restarted twice."
}
```

## Response

**`202 Accepted`** — the request was opened and is `pending`.

**`200 OK`** — a request for this `topup_id` already exists. It is returned
unchanged, whatever the new body says. There is one request per eSIM; to follow
up on a rejected one, contact support.

Both carry the request:

**topup\_id** `string`

The eSIM the request is for.

**external\_order\_id** `string`

The `external_order_id` of the order the eSIM came from.

**status** `string`

`pending`, `approved` or `rejected`.

**reason** `string`

The `reason` you sent.

**note** `string | null`

The `note` you sent.

**rejection\_reason** `string | null`

Set when `status` is `rejected`, otherwise `null`:

| `rejection_reason` | Meaning |
| - | - |
| `esim_activated` | The eSIM connected to a network or used data |
| `activation_expired` | The activation window passed without the eSIM being used |
| `policy_not_met` | Reviewed, and the refund policy does not cover it — support will have been in touch |

**requested\_at** `string`

When the request was opened, ISO 8601 UTC.

**decided\_at** `string | null`

When it was approved or rejected, ISO 8601 UTC.

```json 202 theme={null}
{
  "topup_id": "20250129123456123456789012",
  "external_order_id": "ORDER-123456",
  "status": "pending",
  "reason": "activation_failed",
  "note": "iPhone 13, installed before departure. No service in Tokyo with roaming on; restarted twice.",
  "rejection_reason": null,
  "requested_at": "2026-10-28T03:12:45Z",
  "decided_at": null
}
```

## After you send it

Hold off on telling the customer the refund is done until the decision arrives.
`pending` means received, nothing more. Your [webhook](https://docs.esimstory.com/endpoints/webhooks.md#refund-events)
receives `refund.approved` or `refund.rejected`; without a webhook, read the
request back with [Get a refund request](https://docs.esimstory.com/endpoints/get-refund.md).

An approved refund does not cancel anything on the customer's device. Treat the
eSIM as void once you refund your customer.

## In sandbox

Sandbox has no reviewer, so it decides straight away, and the webhook follows
within seconds:

| `note` starts with | Decision |
| - | - |
| anything else, or no `note` | `approved` |
| `SANDBOX-REJECT` | `rejected`, with `rejection_reason: "policy_not_met"` |

Production never decides on the note. A request there whose note begins
`SANDBOX-REJECT` is reviewed like any other.

## Errors

Every error except `429` carries this shape:

```json theme={null}
{
  "error": {
    "code": "not_issued",
    "message": "This eSIM has not been issued yet."
  }
}
```

| Status | `code` | Cause |
| - | - | - |
| `401` | `unauthorized` | Signature headers missing, expired or wrong. See [Authentication](https://docs.esimstory.com/authentication/overview.md) |
| `404` | `not_found` | No eSIM with that `topup_id` on your account |
| `422` | `invalid_request` | `reason` missing or not one of the codes, or `note` too long |
| `422` | `not_issued` | The eSIM has not been issued yet — wait for its webhook |
| `422` | `already_refunded` | The eSIM was already refunded |
| `429` | — | Over 30 refund requests a minute. See [Rate limits](https://docs.esimstory.com/conventions.md#rate-limits) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.