Download OpenAPI specification:
API documentation
This is the auto-generated endpoint reference. For narrative sections — Authentication, Idempotency, Error taxonomy, Throttling policy, Side-effect index — see the hand-maintained companion at /api/docs.
{- "status": "ready",
- "lpaString": "string",
- "qrDataUri": "string",
- "planName": "string",
- "countryName": "string",
- "dataAmount": "string",
- "validityDays": 0
}| name required | string Customer name |
| environment required | string Enum: "live" "sandbox" Customer environment |
| kind required | string Enum: "WHOLESALE" "VOUCHER" Commercial product the customer is onboarded under. Required and immutable. WHOLESALE: customer-keyed API places wallet-funded orders + activates / tops-up eSIMs. VOUCHER: admin mints code batches against this customer; end-travellers redeem at /r/:code. Pricing for both kinds resolves from customer_plan_prices.price; the kind governs which endpoints the customer can call and how invoice lines are shaped (no provisioning-fee split on voucher lines). |
| country required | string Buyer country as ISO 3166-1 alpha-2, uppercase. Required and IMMUTABLE after create. Drives the VAT/PDV rate applied at invoice generation (via country_vat_rates lookup); unlisted countries fall back to 0% + label "VAT" (matches Serbian izvoz-usluga export exemption). Common values: RS (Serbia), HR (Croatia), DE (Germany), US (United States). A relocated reseller must be created as a new customer to avoid retroactively changing the tax basis of historical invoices. |
| addressLine1 required | string Buyer street address, line 1. Required on the printed invoice. |
| addressLine2 | string Buyer street address, line 2 (apartment / suite / floor). |
| city required | string Buyer city. Required on the printed invoice. |
| postalCode required | string Buyer postal code. Required on the printed invoice. |
| taxId required | string Buyer tax ID (PIB for Serbia, VAT number for EU, etc.). Required at create per invoice design §13.2 — Serbian law requires the buyer PIB on every valid račun, and we are B2B-only, so every legitimate customer has a tax identifier of some kind. If a buyer has no jurisdiction-level tax ID at all (rare edge case), pass a placeholder here and update the row before generating a real invoice. |
| registrationId | string Buyer business registration id (MB in Serbia; equivalent elsewhere). Optional. |
{- "name": "Acme Corp",
- "environment": "live",
- "kind": "WHOLESALE",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx"
}{- "id": "string",
- "name": "string",
- "environment": "live",
- "kind": "WHOLESALE",
- "balance": "150.00",
- "createdAt": "2019-08-24T14:15:22Z",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx",
- "apiKey": "string"
}[- {
- "id": "string",
- "name": "string",
- "environment": "live",
- "kind": "WHOLESALE",
- "balance": "150.00",
- "createdAt": "2019-08-24T14:15:22Z",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx"
}
]{- "id": "string",
- "name": "string",
- "environment": "live",
- "kind": "WHOLESALE",
- "balance": "150.00",
- "createdAt": "2019-08-24T14:15:22Z",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx"
}{- "id": "string",
- "name": "string",
- "environment": "live",
- "kind": "WHOLESALE",
- "balance": "150.00",
- "createdAt": "2019-08-24T14:15:22Z",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx",
- "apiKey": "string"
}| name required | string Customer name |
| environment required | string Enum: "live" "sandbox" Customer environment |
| kind required | string Enum: "WHOLESALE" "VOUCHER" Commercial product the customer is onboarded under. Required and immutable. WHOLESALE: customer-keyed API places wallet-funded orders + activates / tops-up eSIMs. VOUCHER: admin mints code batches against this customer; end-travellers redeem at /r/:code. Pricing for both kinds resolves from customer_plan_prices.price; the kind governs which endpoints the customer can call and how invoice lines are shaped (no provisioning-fee split on voucher lines). |
| country required | string Buyer country as ISO 3166-1 alpha-2, uppercase. Required and IMMUTABLE after create. Drives the VAT/PDV rate applied at invoice generation (via country_vat_rates lookup); unlisted countries fall back to 0% + label "VAT" (matches Serbian izvoz-usluga export exemption). Common values: RS (Serbia), HR (Croatia), DE (Germany), US (United States). A relocated reseller must be created as a new customer to avoid retroactively changing the tax basis of historical invoices. |
| addressLine1 required | string Buyer street address, line 1. Required on the printed invoice. |
| addressLine2 | string Buyer street address, line 2 (apartment / suite / floor). |
| city required | string Buyer city. Required on the printed invoice. |
| postalCode required | string Buyer postal code. Required on the printed invoice. |
| taxId required | string Buyer tax ID (PIB for Serbia, VAT number for EU, etc.). Required at create per invoice design §13.2 — Serbian law requires the buyer PIB on every valid račun, and we are B2B-only, so every legitimate customer has a tax identifier of some kind. If a buyer has no jurisdiction-level tax ID at all (rare edge case), pass a placeholder here and update the row before generating a real invoice. |
| registrationId | string Buyer business registration id (MB in Serbia; equivalent elsewhere). Optional. |
{- "name": "Acme Corp",
- "environment": "live",
- "kind": "WHOLESALE",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx"
}{- "id": "string",
- "name": "string",
- "environment": "live",
- "kind": "WHOLESALE",
- "balance": "150.00",
- "createdAt": "2019-08-24T14:15:22Z",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx",
- "apiKey": "string"
}[- {
- "id": "string",
- "name": "string",
- "environment": "live",
- "kind": "WHOLESALE",
- "balance": "150.00",
- "createdAt": "2019-08-24T14:15:22Z",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx"
}
]{- "id": "string",
- "name": "string",
- "environment": "live",
- "kind": "WHOLESALE",
- "balance": "150.00",
- "createdAt": "2019-08-24T14:15:22Z",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx"
}{- "id": "string",
- "name": "string",
- "environment": "live",
- "kind": "WHOLESALE",
- "balance": "150.00",
- "createdAt": "2019-08-24T14:15:22Z",
- "country": "RS",
- "addressLine1": "Kneza Miloša 12",
- "addressLine2": "Sprat 4",
- "city": "Beograd",
- "postalCode": "11000",
- "taxId": "108xxxxxx",
- "registrationId": "21xxxxxx",
- "apiKey": "string"
}| customerId required | string Customer ID to credit |
| amount required | string Amount to credit in decimal string format |
| referenceId required | string Idempotency key — prevents duplicate credits on retry |
{- "customerId": "string",
- "amount": "100.00",
- "referenceId": "01930000-0000-7000-8000-000000000001"
}{- "customerId": "string",
- "credited": "string",
- "referenceId": "string",
- "applied": true
}[- {
- "id": "string",
- "esimfxProductId": "string",
- "esimfxImsiProfile": "string",
- "name": "string",
- "description": "string",
- "upstreamCost": "string",
- "duration": 30,
- "durationUnit": "DAY",
- "dataAmount": 10,
- "dataAmountUnit": "GB",
- "coverage": "US",
- "destination": "string",
- "compatibleTopupProductIds": [
- "string"
], - "disabled": true,
- "disabledReason": "MANUAL",
- "removedFromUpstream": true,
- "createdAt": "2019-08-24T14:15:22Z"
}
]{- "status": "idle",
- "startedAt": "2019-08-24T14:15:22Z",
- "finishedAt": "2019-08-24T14:15:22Z",
- "result": {
- "created": 0,
- "updated": 0,
- "failed": [
- "string"
], - "warnings": [
- "string"
]
}, - "error": { }
}| disabledReason required | string Enum: "MANUAL" "NEGATIVE_MARGIN" "DUPLICATE" Re-enable all currently-disabled plans whose |
{- "disabledReason": "MANUAL"
}{- "enabled": 0,
- "disabledReason": "MANUAL"
}| id required | string |
| disabled required | boolean Whether the plan is disabled |
{- "disabled": true
}{- "id": "string",
- "esimfxProductId": "string",
- "esimfxImsiProfile": "string",
- "name": "string",
- "description": "string",
- "upstreamCost": "string",
- "duration": 30,
- "durationUnit": "DAY",
- "dataAmount": 10,
- "dataAmountUnit": "GB",
- "coverage": "US",
- "destination": "string",
- "compatibleTopupProductIds": [
- "string"
], - "disabled": true,
- "disabledReason": "MANUAL",
- "removedFromUpstream": true,
- "createdAt": "2019-08-24T14:15:22Z"
}Bypasses the upstream check and atomically credits the wallet + flips status to REFUNDED. Use when ops has confirmed out-of-band that the upstream subscription is terminated. The reason is recorded in the audit log.
| id required | string |
| reason required | string [ 1 .. 500 ] characters Non-empty rationale for the manual override; recorded in the audit trail. |
{- "reason": "eSIMfx support confirmed termination via ticket #12345"
}{- "orderId": "string",
- "status": "REFUND_PENDING",
- "refundRequestedAt": "2026-05-13T12:00:00.000Z",
- "refundedAt": "2026-05-13T12:00:01.234Z",
- "refundedAmount": "14.50"
}Flips status to REFUND_FAILED without crediting the wallet. Use when the refund is irrecoverable (e.g. confirmed fraud, duplicate, or upstream never had the order). The reason is recorded in the audit log.
| id required | string |
| reason required | string [ 1 .. 500 ] characters Non-empty rationale for the manual override; recorded in the audit trail. |
{- "reason": "eSIMfx support confirmed termination via ticket #12345"
}{- "orderId": "string",
- "status": "REFUND_PENDING",
- "refundRequestedAt": "2026-05-13T12:00:00.000Z",
- "refundedAt": "2026-05-13T12:00:01.234Z",
- "refundedAmount": "14.50"
}| customerId required | string Customer owning the eSIM. |
| planId required | string Plan to issue. |
| email required | string Recipient email for the install link. |
required | object (SourceRef) |
| locale | string Enum: "en" "de" Email + install-page locale. Defaults to 'en'. 'de' falls back to en until Phase 2 fills the DE template. |
| expiresInDays | number [ 1 .. 360 ] Install token lifetime in days. Defaults to 360. Capped at 360 by InstallTokenService.sign() to track the eSIMfx upstream activate_by window. |
| travelerName | string Optional first name surfaced in the email greeting. |
{- "customerId": "string",
- "planId": "string",
- "email": "string",
- "sourceRef": {
- "kind": "admin_order",
- "id": "string"
}, - "locale": "en",
- "expiresInDays": 1,
- "travelerName": "string"
}{- "esimId": "string",
- "installToken": "string",
- "installUrl": "string"
}| customerId required | string Customer to invoice. |
| periodStart required | string Inclusive UTC-midnight start of the invoicing window. Bare-date ISO string ('YYYY-MM-DD'). Datetimes / timezones / calendar-invalid dates rejected. |
| periodEnd required | string Exclusive UTC-midnight end of the invoicing window. Bare-date ISO string ('YYYY-MM-DD'). |
| locale | string Enum: "en" "sr" PDF language. 'en' (English, default) or 'sr' (Serbian Latin script). Controls all label text, date format (ISO YYYY-MM-DD vs DD.MM.YYYY.), money decimal separator (period vs comma), and line-item description prefixes. Font (Noto Sans) is Unicode-safe for both. |
object Manual override of the USD-to-RSD exchange rate. Auto-fetches from kurs.resenje.org (NBS mirror) at invoice-generation time when omitted for RS-country buyers. When supplied it is used verbatim regardless of buyer country -- useful for audit replay, mirror-outage fallback, and adding an RSD conversion to a non-RS invoice on the operator's discretion. Provide all three subfields together. |
{- "customerId": "string",
- "periodStart": "2026-05-01",
- "periodEnd": "2026-06-01",
- "locale": "en",
- "exchangeRate": {
- "rateDate": "2026-07-16",
- "rateUsdToRsd": "102.3293",
- "source": "NBS_KURS"
}
}{- "title": "Gone",
- "status": 410,
- "detail": "install token expired",
- "code": "GONE",
- "errors": [
- { }
], - "retryAfterSeconds": 30
}[- {
- "planId": "string",
- "esimfxProductId": "string",
- "name": "string",
- "disabled": true,
- "disabledReason": { },
- "removedFromUpstream": true,
- "upstreamCost": "string",
- "price": "string"
}
]| customerId required | string Customer UUID who will be invoiced for each redemption. |
| planId required | string Plan UUID to issue against each redemption. |
| count required | number [ 1 .. 2000 ] Number of codes to generate. Hard cap 2,000 to keep the bundle in-memory; larger jobs must split or move to DO Spaces (deferred). |
| expiresAt | object Batch expiry (ISO 8601, UTC). |
| label required | string <= 120 characters Human-readable batch label. PUBLIC — shown to end-travellers in the /r/[code] redemption-page footer. Do NOT include PII, internal identifiers, or domain-shaped strings. Period ( |
| createdBy required | string <= 120 characters Operator identifier (email or name) for the audit trail. REQUIRED. Persisted on voucher_batches.created_by AND echoed onto every audit_events row this batch produces. Must contain at least one non-whitespace character — a whitespace-only " " would otherwise satisfy |
| topupPlanId | string UUID of the companion TOPUP plan. When set, this batch is a BUNDLE: each code generates a paired TOPUP code (PENDING_BINDING) that becomes redeemable after the paired ESIM code is redeemed. Must be in the eSIM plan's |
{- "customerId": "string",
- "planId": "string",
- "count": 1,
- "expiresAt": { },
- "label": "VisaCo Croatia 2026-06",
- "createdBy": "string",
- "topupPlanId": "string"
}{- "batchId": "string",
- "codeCount": 0,
- "kind": "STANDARD",
- "topupPlanId": "string"
}| customerId | string Filter by customer UUID. Omit to list across all customers. |
| status | string Enum: "ACTIVE" "REVOKED" Filter by batch status. ACTIVE includes batches whose individual codes may have been revoked; REVOKED is set only when the WHOLE batch was revoked. |
| limit | number [ 1 .. 200 ] Default: 50 Page size (1-200). Defaults to 50. |
| offset | number >= 0 Default: 0 Row offset (>=0). Defaults to 0. |
{- "items": [
- {
- "batchId": "string",
- "customerId": "string",
- "planId": "string",
- "kind": "STANDARD",
- "topupPlanId": "string",
- "label": "string",
- "codeCount": 0,
- "expiresAt": { },
- "status": "ACTIVE",
- "createdBy": "string",
- "createdAt": "string"
}
], - "total": 0
}| id required | string |
{- "batch": {
- "batchId": "string",
- "customerId": "string",
- "planId": "string",
- "kind": "STANDARD",
- "topupPlanId": "string",
- "label": "string",
- "codeCount": 0,
- "expiresAt": { },
- "status": "ACTIVE",
- "createdBy": "string",
- "createdAt": "string"
}, - "stats": {
- "issued": 0,
- "redeeming": 0,
- "redeemed": 0,
- "revoked": 0,
- "expired": 0,
- "pendingBinding": 0,
- "refunded": 0
}
}| id required | string |
| status | string Enum: "ISSUED" "REDEEMING" "REDEEMED" "REVOKED" "EXPIRED" "PENDING_BINDING" "REFUNDED" Filter by code status. Common operator paths: ?status=ISSUED to find a still-redeemable code for single-code revoke (backlog task #31 motivator), ?status=REDEEMED for invoice/audit triage. |
| pairKind | string Enum: "ESIM" "TOPUP" Filter by bundle half. ESIM = starter half, TOPUP = companion half. STANDARD-batch rows carry |
| limit | number [ 1 .. 200 ] Default: 50 Page size (1-200). Defaults to 50. |
| offset | number >= 0 Default: 0 Row offset (>=0). Defaults to 0. |
{- "items": [
- {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "pairKind": "ESIM",
- "pairId": "string",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "priceMinor": "string",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z"
}
], - "total": 0
}| id required | string |
| reason | object <= 500 characters Optional revoke reason. If null / empty / whitespace, |
| revokedBy required | string <= 120 characters Operator identifier (email or name) for the audit trail. REQUIRED. Stored only on the audit_events row; voucher_codes itself has no |
{- "reason": { },
- "revokedBy": "string"
}{- "batchId": "string",
- "revokedCodeCount": 0
}| id required | string |
{- "title": "Gone",
- "status": 410,
- "detail": "install token expired",
- "code": "GONE",
- "errors": [
- { }
], - "retryAfterSeconds": 30
}| id required | string |
| reason | object <= 500 characters Optional revoke reason. If null / empty / whitespace, |
| revokedBy required | string <= 120 characters Operator identifier (email or name) for the audit trail. REQUIRED. Stored only on the audit_events row; voucher_codes itself has no |
{- "reason": { },
- "revokedBy": "string"
}{- "codeId": "string"
}| pairId required | string |
{- "pairId": "string",
- "batchId": "string",
- "customerId": "string",
- "esim": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "topup": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "recentAuditEvents": [
- {
- "action": "string",
- "at": "2019-08-24T14:15:22Z",
- "customerId": "string",
- "after": { }
}
]
}| codeId required | string |
{- "pairId": "string",
- "batchId": "string",
- "customerId": "string",
- "esim": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "topup": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "recentAuditEvents": [
- {
- "action": "string",
- "at": "2019-08-24T14:15:22Z",
- "customerId": "string",
- "after": { }
}
]
}string Look up by traveller email (redeemed_email OR bound_email). The most-recent match is returned when an email appears on multiple codes. | |
| code | string Look up by human-displayable voucher code string. |
| codeId | string Look up by voucher_codes.id (UUIDv7). Accepted for programmatic clients; operators normally use |
| fresh | boolean Bypass the 60s upstream eSIMfx snapshot cache. Defaults to false. Set when triaging suspected upstream/local divergence. |
{- "voucherCode": {
- "id": "string",
- "code": "string",
- "status": "ISSUED",
- "pairKind": "string",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "pairId": "string",
- "batchId": "string",
- "customerId": "string",
- "refundedAt": "2019-08-24T14:15:22Z",
- "chargebackedAt": "2019-08-24T14:15:22Z"
}, - "batch": {
- "id": "string",
- "label": "string",
- "kind": "STANDARD",
- "planId": "string",
- "topupPlanId": "string",
- "status": "string",
- "expiresAt": "2019-08-24T14:15:22Z"
}, - "esim": {
- "id": "string",
- "iccid": "string",
- "status": "string",
- "installTokenExpiresAt": "2019-08-24T14:15:22Z",
- "redeemedAt": "2019-08-24T14:15:22Z"
}, - "upstream": {
- "iccid": "string",
- "status": "string",
- "fetchedAt": "2019-08-24T14:15:22Z",
- "cached": true
}, - "esimHalf": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "topupHalf": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "recentAuditEvents": [
- {
- "action": "string",
- "at": "2019-08-24T14:15:22Z",
- "customerId": "string",
- "after": { }
}
]
}| code required | string Human-displayable voucher code string. Must be the ESIM-half on bundle codes — TOPUP-halves have no install link. |
string Override destination email. Defaults to the on-disk | |
| locale | string Render locale for the install email template. Defaults to the eSIM row's stored locale, else |
| reason required | string Free-form operator-provided reason (recorded in the audit event). Surfaced in support tooling so the support agent can see why an admin resent. |
| resentBy required | string Operator handle (admin email / system actor). Recorded in the |
{- "code": "string",
- "email": "string",
- "locale": "string",
- "reason": "string",
- "resentBy": "string"
}{- "installUrl": "string",
- "sentTo": "string",
- "emailDelivered": true
}| code required | string Voucher code string (ESIM-half, TOPUP-half, or standalone). |
| disputeId required | string Stripe / payment-processor dispute identifier. Free-form text — recorded in audit so support can correlate the chargeback ticket. |
| reason required | string Free-form chargeback reason (fraud, friendly-fraud, etc). Recorded verbatim in the audit row. |
| actor required | string Operator handle (admin email / system actor) — recorded as |
{- "code": "string",
- "disputeId": "string",
- "reason": "string",
- "actor": "string"
}{- "code": "string",
- "esimId": "string",
- "terminatedAt": "2019-08-24T14:15:22Z",
- "cascaded": true
}| code required | string Voucher code string. ESIM-half refund cascades to the paired TOPUP-half. |
| reason required | string Free-form refund reason (defective device, customer dispute, etc.). Recorded verbatim on voucher_codes.refund_reason + audit. |
| refundedBy required | string Operator handle (admin email / system actor). Recorded as refunded_by on the voucher_codes row + audit. |
| notifyTraveller required | boolean Send a "your voucher has been refunded" email to the on-disk redeemed_email (bundle cascade sends one email per pair). Required — the operator must make an explicit choice per call. Set |
{- "code": "string",
- "reason": "string",
- "refundedBy": "string",
- "notifyTraveller": true
}{- "code": "string",
- "refundedAt": "2019-08-24T14:15:22Z",
- "cascaded": true,
- "cascadeKind": "refund"
}| code required | string Voucher code string (ESIM-half or standalone). TOPUP-half codes are rejected with 422. |
| reason required | string Free-form reissue reason (profile fault, scan failure, etc). Recorded in audit. |
| reissuedBy required | string Operator handle (admin email / system actor). Recorded in audit. |
| locale | string Render locale for the install email template. Defaults to the eSIM row's stored locale, else |
string Destination override for the new install email. Defaults to the on-disk redeemed_email. Use when support previously corrected the address via resend-install-email and the reissue should NOT fall back to the original (stale) one. |
{- "code": "string",
- "reason": "string",
- "reissuedBy": "string",
- "locale": "string",
- "email": "string"
}{- "code": "string",
- "esimId": "string",
- "iccid": "string",
- "installUrl": "string",
- "sentTo": "string",
- "emailDelivered": true
}{- "items": [
- {
- "reason": "hard_bounce",
- "suppressedAt": "2026-07-11T04:29:26.870Z",
- "firstNotificationId": "265d09b0-2099-5fc5-9cbb-6b7cb1b33d0b"
}
], - "total": 43,
- "limit": 100,
- "offset": 0
}{ }{- "deleted": true
}{ }{- "summary": {
- "removed": 42,
- "skipped": 1
}
}| customerId required | string Customer ID to credit |
| amount required | string Amount to credit in decimal string format |
| referenceId required | string Idempotency key — prevents duplicate credits on retry |
{- "customerId": "string",
- "amount": "100.00",
- "referenceId": "01930000-0000-7000-8000-000000000001"
}{- "customerId": "string",
- "credited": "string",
- "referenceId": "string",
- "applied": true
}[- {
- "id": "string",
- "name": "string",
- "description": "string",
- "price": "9.99",
- "duration": 30,
- "durationUnit": "DAY",
- "dataAmount": 10,
- "dataAmountUnit": "GB",
- "coverage": "US",
- "provisioningFee": "0.50"
}
][- {
- "id": "string",
- "esimfxProductId": "string",
- "esimfxImsiProfile": "string",
- "name": "string",
- "description": "string",
- "upstreamCost": "string",
- "duration": 30,
- "durationUnit": "DAY",
- "dataAmount": 10,
- "dataAmountUnit": "GB",
- "coverage": "US",
- "destination": "string",
- "compatibleTopupProductIds": [
- "string"
], - "disabled": true,
- "disabledReason": "MANUAL",
- "removedFromUpstream": true,
- "createdAt": "2019-08-24T14:15:22Z"
}
]{- "status": "idle",
- "startedAt": "2019-08-24T14:15:22Z",
- "finishedAt": "2019-08-24T14:15:22Z",
- "result": {
- "created": 0,
- "updated": 0,
- "failed": [
- "string"
], - "warnings": [
- "string"
]
}, - "error": { }
}| disabledReason required | string Enum: "MANUAL" "NEGATIVE_MARGIN" "DUPLICATE" Re-enable all currently-disabled plans whose |
{- "disabledReason": "MANUAL"
}{- "enabled": 0,
- "disabledReason": "MANUAL"
}| id required | string |
| disabled required | boolean Whether the plan is disabled |
{- "disabled": true
}{- "id": "string",
- "esimfxProductId": "string",
- "esimfxImsiProfile": "string",
- "name": "string",
- "description": "string",
- "upstreamCost": "string",
- "duration": 30,
- "durationUnit": "DAY",
- "dataAmount": 10,
- "dataAmountUnit": "GB",
- "coverage": "US",
- "destination": "string",
- "compatibleTopupProductIds": [
- "string"
], - "disabled": true,
- "disabledReason": "MANUAL",
- "removedFromUpstream": true,
- "createdAt": "2019-08-24T14:15:22Z"
}Sandbox customers receive dummy eSIMs with no real provisioning.
| planId required | string ID of the plan to purchase |
{- "planId": "string"
}{- "id": "string",
- "planId": "string",
- "iccid": "string",
- "operationType": "NEW",
- "status": "PENDING",
- "subscriptionStatus": "PENDING",
- "salePrice": "9.99",
- "planPrice": "5.99",
- "provisioningFee": "0.50",
- "createdAt": "2019-08-24T14:15:22Z",
- "refundRequestedAt": "string",
- "refundedAt": "string",
- "refundedAmount": "14.50"
}[- {
- "id": "string",
- "planId": "string",
- "iccid": "string",
- "operationType": "NEW",
- "status": "PENDING",
- "subscriptionStatus": "PENDING",
- "salePrice": "9.99",
- "planPrice": "5.99",
- "provisioningFee": "0.50",
- "createdAt": "2019-08-24T14:15:22Z",
- "refundRequestedAt": "string",
- "refundedAt": "string",
- "refundedAmount": "14.50"
}
]{- "id": "string",
- "planId": "string",
- "iccid": "string",
- "operationType": "NEW",
- "status": "PENDING",
- "subscriptionStatus": "PENDING",
- "salePrice": "9.99",
- "planPrice": "5.99",
- "provisioningFee": "0.50",
- "createdAt": "2019-08-24T14:15:22Z",
- "refundRequestedAt": "string",
- "refundedAt": "string",
- "refundedAmount": "14.50"
}Accepts the refund and returns 202. The body reports REFUNDED if upstream termination completes inline within ~3s; otherwise it reports REFUND_PENDING and a background worker drives the commit. Poll GET /orders/:id until status becomes REFUNDED (success) or REFUND_FAILED (60-day window missed or admin-forced). Sandbox refunds skip real upstream termination.
| id required | string |
{- "orderId": "string",
- "status": "REFUND_PENDING",
- "refundRequestedAt": "2026-05-13T12:00:00.000Z",
- "refundedAt": "2026-05-13T12:00:01.234Z",
- "refundedAmount": "14.50"
}Bypasses the upstream check and atomically credits the wallet + flips status to REFUNDED. Use when ops has confirmed out-of-band that the upstream subscription is terminated. The reason is recorded in the audit log.
| id required | string |
| reason required | string [ 1 .. 500 ] characters Non-empty rationale for the manual override; recorded in the audit trail. |
{- "reason": "eSIMfx support confirmed termination via ticket #12345"
}{- "orderId": "string",
- "status": "REFUND_PENDING",
- "refundRequestedAt": "2026-05-13T12:00:00.000Z",
- "refundedAt": "2026-05-13T12:00:01.234Z",
- "refundedAmount": "14.50"
}Flips status to REFUND_FAILED without crediting the wallet. Use when the refund is irrecoverable (e.g. confirmed fraud, duplicate, or upstream never had the order). The reason is recorded in the audit log.
| id required | string |
| reason required | string [ 1 .. 500 ] characters Non-empty rationale for the manual override; recorded in the audit trail. |
{- "reason": "eSIMfx support confirmed termination via ticket #12345"
}{- "orderId": "string",
- "status": "REFUND_PENDING",
- "refundRequestedAt": "2026-05-13T12:00:00.000Z",
- "refundedAt": "2026-05-13T12:00:01.234Z",
- "refundedAmount": "14.50"
}Activates a PENDING subscription for the given ICCID. Sandbox activations return a simulated ACTIVE status.
| iccid required | string |
{- "title": "Gone",
- "status": 410,
- "detail": "install token expired",
- "code": "GONE",
- "errors": [
- { }
], - "retryAfterSeconds": 30
}Sandbox eSIMs always show ACTIVE status.
| id required | string |
{- "id": "string",
- "iccid": "string",
- "esimQr": "string",
- "status": "PROVISIONED",
- "createdAt": "2019-08-24T14:15:22Z",
- "liveStatus": {
- "iccid": "string",
- "esimQr": "string",
- "status": "string"
}
}Sandbox returns simulated usage data.
| id required | string |
{- "iccid": "string",
- "usedAmount": 0,
- "totalAmount": 0,
- "amountUnit": "string",
- "status": "string",
- "activationTime": "string",
- "expiry": "string"
}Sandbox returns all plans with customer pricing (no upstream filter).
| id required | string |
[- {
- "planId": "string",
- "name": "string",
- "duration": 0,
- "durationUnit": "string",
- "amount": 0,
- "amountUnit": "string",
- "coverage": [
- "string"
], - "price": "string",
- "provisioningFee": "0.00"
}
]Sandbox top-ups simulate upstream without real provisioning.
| id required | string |
| planId required | string ID of the top-up plan to apply |
{- "planId": "string"
}{- "id": "string",
- "planId": "string",
- "iccid": "string",
- "operationType": "NEW",
- "status": "PENDING",
- "subscriptionStatus": "PENDING",
- "salePrice": "9.99",
- "planPrice": "5.99",
- "provisioningFee": "0.50",
- "createdAt": "2019-08-24T14:15:22Z",
- "refundRequestedAt": "string",
- "refundedAt": "string",
- "refundedAmount": "14.50"
}| customerId required | string Customer owning the eSIM. |
| planId required | string Plan to issue. |
| email required | string Recipient email for the install link. |
required | object (SourceRef) |
| locale | string Enum: "en" "de" Email + install-page locale. Defaults to 'en'. 'de' falls back to en until Phase 2 fills the DE template. |
| expiresInDays | number [ 1 .. 360 ] Install token lifetime in days. Defaults to 360. Capped at 360 by InstallTokenService.sign() to track the eSIMfx upstream activate_by window. |
| travelerName | string Optional first name surfaced in the email greeting. |
{- "customerId": "string",
- "planId": "string",
- "email": "string",
- "sourceRef": {
- "kind": "admin_order",
- "id": "string"
}, - "locale": "en",
- "expiresInDays": 1,
- "travelerName": "string"
}{- "esimId": "string",
- "installToken": "string",
- "installUrl": "string"
}| customerId required | string Customer to invoice. |
| periodStart required | string Inclusive UTC-midnight start of the invoicing window. Bare-date ISO string ('YYYY-MM-DD'). Datetimes / timezones / calendar-invalid dates rejected. |
| periodEnd required | string Exclusive UTC-midnight end of the invoicing window. Bare-date ISO string ('YYYY-MM-DD'). |
| locale | string Enum: "en" "sr" PDF language. 'en' (English, default) or 'sr' (Serbian Latin script). Controls all label text, date format (ISO YYYY-MM-DD vs DD.MM.YYYY.), money decimal separator (period vs comma), and line-item description prefixes. Font (Noto Sans) is Unicode-safe for both. |
object Manual override of the USD-to-RSD exchange rate. Auto-fetches from kurs.resenje.org (NBS mirror) at invoice-generation time when omitted for RS-country buyers. When supplied it is used verbatim regardless of buyer country -- useful for audit replay, mirror-outage fallback, and adding an RSD conversion to a non-RS invoice on the operator's discretion. Provide all three subfields together. |
{- "customerId": "string",
- "periodStart": "2026-05-01",
- "periodEnd": "2026-06-01",
- "locale": "en",
- "exchangeRate": {
- "rateDate": "2026-07-16",
- "rateUsdToRsd": "102.3293",
- "source": "NBS_KURS"
}
}{- "title": "Gone",
- "status": 410,
- "detail": "install token expired",
- "code": "GONE",
- "errors": [
- { }
], - "retryAfterSeconds": 30
}[- {
- "planId": "string",
- "esimfxProductId": "string",
- "name": "string",
- "disabled": true,
- "disabledReason": { },
- "removedFromUpstream": true,
- "upstreamCost": "string",
- "price": "string"
}
]| month required | string Example: month=2026-03 Month in YYYY-MM format |
| format | string Default: "json" Enum: "json" "csv" "pdf" Export format |
| sections | string Example: sections=revenue,profitability Comma-separated sections to include |
{- "month": "string",
- "generatedAt": "string",
- "revenue": {
- "grossRevenue": "string",
- "totalUpstreamCost": "string",
- "grossMargin": "string",
- "marginPercent": "string",
- "refundedAmount": "string",
- "refundCount": 0,
- "netRevenue": "string",
- "orderCounts": {
- "new": 0,
- "topup": 0
}, - "daily": [
- {
- "date": "string",
- "grossRevenue": "string",
- "upstreamCost": "string",
- "grossMargin": "string",
- "refundedAmount": "string",
- "refundCount": 0,
- "orders": 0,
- "upstreamRefundedAmount": "0.88",
- "netUpstreamCost": "0.50",
- "netMargin": "0.00"
}
], - "upstreamRefundedAmount": "1.76",
- "netUpstreamCost": "1.88",
- "netMargin": "1.62"
}, - "profitability": {
- "plans": [
- {
- "planId": "string",
- "planName": "string",
- "orderCount": 0,
- "refundCount": 0,
- "grossRevenue": "string",
- "refundedAmount": "string",
- "netRevenue": "string",
- "grossMargin": "string",
- "unitMargin": "string",
- "upstreamRefundedAmount": "1.76",
- "netUpstreamCost": "1.88",
- "netMargin": "1.62"
}
]
}, - "customers": {
- "customers": [
- {
- "customerId": "string",
- "customerName": "string",
- "totalSpent": "string",
- "orderCount": 0,
- "refundCount": 0,
- "refundedAmount": "string",
- "netRevenue": "string"
}
]
}
}| month required | string Example: month=2026-03 Month in YYYY-MM format |
| format | string Default: "json" Enum: "json" "csv" "pdf" Export format |
| sections | string Example: sections=revenue,profitability Comma-separated sections to include |
{- "month": "string",
- "generatedAt": "string",
- "revenue": {
- "grossRevenue": "string",
- "totalUpstreamCost": "string",
- "grossMargin": "string",
- "marginPercent": "string",
- "refundedAmount": "string",
- "refundCount": 0,
- "netRevenue": "string",
- "orderCounts": {
- "new": 0,
- "topup": 0
}, - "daily": [
- {
- "date": "string",
- "grossRevenue": "string",
- "upstreamCost": "string",
- "grossMargin": "string",
- "refundedAmount": "string",
- "refundCount": 0,
- "orders": 0,
- "upstreamRefundedAmount": "0.88",
- "netUpstreamCost": "0.50",
- "netMargin": "0.00"
}
], - "upstreamRefundedAmount": "1.76",
- "netUpstreamCost": "1.88",
- "netMargin": "1.62"
}, - "profitability": {
- "plans": [
- {
- "planId": "string",
- "planName": "string",
- "orderCount": 0,
- "refundCount": 0,
- "grossRevenue": "string",
- "refundedAmount": "string",
- "netRevenue": "string",
- "grossMargin": "string",
- "unitMargin": "string",
- "upstreamRefundedAmount": "1.76",
- "netUpstreamCost": "1.88",
- "netMargin": "1.62"
}
]
}, - "customers": {
- "customers": [
- {
- "customerId": "string",
- "customerName": "string",
- "totalSpent": "string",
- "orderCount": 0,
- "refundCount": 0,
- "refundedAmount": "string",
- "netRevenue": "string"
}
]
}
}| code required | string |
{- "kind": "issued",
- "code": "string",
- "batchLabel": "string",
- "expiresAt": "string",
- "planSummary": {
- "countryCode": "string",
- "countryName": "string",
- "dataAmount": "string",
- "validityDays": 0
}
}| code required | string |
| email required | string Traveller email where the install link will be delivered. VouchersService normalises this to lower-case + trimmed before persistence and idempotency comparison. |
| locale | string Enum: "en" "de" Locale for the install email + install page. Defaults to 'en'. 'de' falls back to en until Phase 2 fills the DE template (mirrors OriginationService default). |
{- "email": "string",
- "locale": "en"
}{- "kind": "esim",
- "installToken": "string",
- "installUrl": "string",
- "topupOrderId": "string",
- "dataAmount": "string",
- "dataAmountUnit": "string",
- "validityDays": 0
}| code required | string |
{- "status": "active",
- "plan": "string",
- "data": {
- "remainingMb": 0,
- "totalMb": 0
}, - "expiresAt": "string",
- "usageUpdatedAt": "string"
}| customerId required | string Customer UUID who will be invoiced for each redemption. |
| planId required | string Plan UUID to issue against each redemption. |
| count required | number [ 1 .. 2000 ] Number of codes to generate. Hard cap 2,000 to keep the bundle in-memory; larger jobs must split or move to DO Spaces (deferred). |
| expiresAt | object Batch expiry (ISO 8601, UTC). |
| label required | string <= 120 characters Human-readable batch label. PUBLIC — shown to end-travellers in the /r/[code] redemption-page footer. Do NOT include PII, internal identifiers, or domain-shaped strings. Period ( |
| createdBy required | string <= 120 characters Operator identifier (email or name) for the audit trail. REQUIRED. Persisted on voucher_batches.created_by AND echoed onto every audit_events row this batch produces. Must contain at least one non-whitespace character — a whitespace-only " " would otherwise satisfy |
| topupPlanId | string UUID of the companion TOPUP plan. When set, this batch is a BUNDLE: each code generates a paired TOPUP code (PENDING_BINDING) that becomes redeemable after the paired ESIM code is redeemed. Must be in the eSIM plan's |
{- "customerId": "string",
- "planId": "string",
- "count": 1,
- "expiresAt": { },
- "label": "VisaCo Croatia 2026-06",
- "createdBy": "string",
- "topupPlanId": "string"
}{- "batchId": "string",
- "codeCount": 0,
- "kind": "STANDARD",
- "topupPlanId": "string"
}| customerId | string Filter by customer UUID. Omit to list across all customers. |
| status | string Enum: "ACTIVE" "REVOKED" Filter by batch status. ACTIVE includes batches whose individual codes may have been revoked; REVOKED is set only when the WHOLE batch was revoked. |
| limit | number [ 1 .. 200 ] Default: 50 Page size (1-200). Defaults to 50. |
| offset | number >= 0 Default: 0 Row offset (>=0). Defaults to 0. |
{- "items": [
- {
- "batchId": "string",
- "customerId": "string",
- "planId": "string",
- "kind": "STANDARD",
- "topupPlanId": "string",
- "label": "string",
- "codeCount": 0,
- "expiresAt": { },
- "status": "ACTIVE",
- "createdBy": "string",
- "createdAt": "string"
}
], - "total": 0
}| id required | string |
{- "batch": {
- "batchId": "string",
- "customerId": "string",
- "planId": "string",
- "kind": "STANDARD",
- "topupPlanId": "string",
- "label": "string",
- "codeCount": 0,
- "expiresAt": { },
- "status": "ACTIVE",
- "createdBy": "string",
- "createdAt": "string"
}, - "stats": {
- "issued": 0,
- "redeeming": 0,
- "redeemed": 0,
- "revoked": 0,
- "expired": 0,
- "pendingBinding": 0,
- "refunded": 0
}
}| id required | string |
| status | string Enum: "ISSUED" "REDEEMING" "REDEEMED" "REVOKED" "EXPIRED" "PENDING_BINDING" "REFUNDED" Filter by code status. Common operator paths: ?status=ISSUED to find a still-redeemable code for single-code revoke (backlog task #31 motivator), ?status=REDEEMED for invoice/audit triage. |
| pairKind | string Enum: "ESIM" "TOPUP" Filter by bundle half. ESIM = starter half, TOPUP = companion half. STANDARD-batch rows carry |
| limit | number [ 1 .. 200 ] Default: 50 Page size (1-200). Defaults to 50. |
| offset | number >= 0 Default: 0 Row offset (>=0). Defaults to 0. |
{- "items": [
- {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "pairKind": "ESIM",
- "pairId": "string",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "priceMinor": "string",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z"
}
], - "total": 0
}| id required | string |
| reason | object <= 500 characters Optional revoke reason. If null / empty / whitespace, |
| revokedBy required | string <= 120 characters Operator identifier (email or name) for the audit trail. REQUIRED. Stored only on the audit_events row; voucher_codes itself has no |
{- "reason": { },
- "revokedBy": "string"
}{- "batchId": "string",
- "revokedCodeCount": 0
}| id required | string |
{- "title": "Gone",
- "status": 410,
- "detail": "install token expired",
- "code": "GONE",
- "errors": [
- { }
], - "retryAfterSeconds": 30
}| id required | string |
| reason | object <= 500 characters Optional revoke reason. If null / empty / whitespace, |
| revokedBy required | string <= 120 characters Operator identifier (email or name) for the audit trail. REQUIRED. Stored only on the audit_events row; voucher_codes itself has no |
{- "reason": { },
- "revokedBy": "string"
}{- "codeId": "string"
}| pairId required | string |
{- "pairId": "string",
- "batchId": "string",
- "customerId": "string",
- "esim": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "topup": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "recentAuditEvents": [
- {
- "action": "string",
- "at": "2019-08-24T14:15:22Z",
- "customerId": "string",
- "after": { }
}
]
}| codeId required | string |
{- "pairId": "string",
- "batchId": "string",
- "customerId": "string",
- "esim": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "topup": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "recentAuditEvents": [
- {
- "action": "string",
- "at": "2019-08-24T14:15:22Z",
- "customerId": "string",
- "after": { }
}
]
}string Look up by traveller email (redeemed_email OR bound_email). The most-recent match is returned when an email appears on multiple codes. | |
| code | string Look up by human-displayable voucher code string. |
| codeId | string Look up by voucher_codes.id (UUIDv7). Accepted for programmatic clients; operators normally use |
| fresh | boolean Bypass the 60s upstream eSIMfx snapshot cache. Defaults to false. Set when triaging suspected upstream/local divergence. |
{- "voucherCode": {
- "id": "string",
- "code": "string",
- "status": "ISSUED",
- "pairKind": "string",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "pairId": "string",
- "batchId": "string",
- "customerId": "string",
- "refundedAt": "2019-08-24T14:15:22Z",
- "chargebackedAt": "2019-08-24T14:15:22Z"
}, - "batch": {
- "id": "string",
- "label": "string",
- "kind": "STANDARD",
- "planId": "string",
- "topupPlanId": "string",
- "status": "string",
- "expiresAt": "2019-08-24T14:15:22Z"
}, - "esim": {
- "id": "string",
- "iccid": "string",
- "status": "string",
- "installTokenExpiresAt": "2019-08-24T14:15:22Z",
- "redeemedAt": "2019-08-24T14:15:22Z"
}, - "upstream": {
- "iccid": "string",
- "status": "string",
- "fetchedAt": "2019-08-24T14:15:22Z",
- "cached": true
}, - "esimHalf": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "topupHalf": {
- "codeId": "string",
- "code": "string",
- "status": "ISSUED",
- "redeemedAt": "2019-08-24T14:15:22Z",
- "redeemedEmail": "string",
- "esimId": "string",
- "iccid": "string",
- "subscriptionStatus": "PROVISIONED",
- "boundIccid": "string",
- "boundEmail": "string",
- "boundAt": "2019-08-24T14:15:22Z",
- "topupOrderId": "string",
- "chargebackedAt": "2019-08-24T14:15:22Z",
- "refundedAt": "2019-08-24T14:15:22Z",
- "revokedAt": "2019-08-24T14:15:22Z",
- "revokeReason": "string"
}, - "recentAuditEvents": [
- {
- "action": "string",
- "at": "2019-08-24T14:15:22Z",
- "customerId": "string",
- "after": { }
}
]
}| code required | string Human-displayable voucher code string. Must be the ESIM-half on bundle codes — TOPUP-halves have no install link. |
string Override destination email. Defaults to the on-disk | |
| locale | string Render locale for the install email template. Defaults to the eSIM row's stored locale, else |
| reason required | string Free-form operator-provided reason (recorded in the audit event). Surfaced in support tooling so the support agent can see why an admin resent. |
| resentBy required | string Operator handle (admin email / system actor). Recorded in the |
{- "code": "string",
- "email": "string",
- "locale": "string",
- "reason": "string",
- "resentBy": "string"
}{- "installUrl": "string",
- "sentTo": "string",
- "emailDelivered": true
}| code required | string Voucher code string (ESIM-half, TOPUP-half, or standalone). |
| disputeId required | string Stripe / payment-processor dispute identifier. Free-form text — recorded in audit so support can correlate the chargeback ticket. |
| reason required | string Free-form chargeback reason (fraud, friendly-fraud, etc). Recorded verbatim in the audit row. |
| actor required | string Operator handle (admin email / system actor) — recorded as |
{- "code": "string",
- "disputeId": "string",
- "reason": "string",
- "actor": "string"
}{- "code": "string",
- "esimId": "string",
- "terminatedAt": "2019-08-24T14:15:22Z",
- "cascaded": true
}| code required | string Voucher code string. ESIM-half refund cascades to the paired TOPUP-half. |
| reason required | string Free-form refund reason (defective device, customer dispute, etc.). Recorded verbatim on voucher_codes.refund_reason + audit. |
| refundedBy required | string Operator handle (admin email / system actor). Recorded as refunded_by on the voucher_codes row + audit. |
| notifyTraveller required | boolean Send a "your voucher has been refunded" email to the on-disk redeemed_email (bundle cascade sends one email per pair). Required — the operator must make an explicit choice per call. Set |
{- "code": "string",
- "reason": "string",
- "refundedBy": "string",
- "notifyTraveller": true
}{- "code": "string",
- "refundedAt": "2019-08-24T14:15:22Z",
- "cascaded": true,
- "cascadeKind": "refund"
}| code required | string Voucher code string (ESIM-half or standalone). TOPUP-half codes are rejected with 422. |
| reason required | string Free-form reissue reason (profile fault, scan failure, etc). Recorded in audit. |
| reissuedBy required | string Operator handle (admin email / system actor). Recorded in audit. |
| locale | string Render locale for the install email template. Defaults to the eSIM row's stored locale, else |
string Destination override for the new install email. Defaults to the on-disk redeemed_email. Use when support previously corrected the address via resend-install-email and the reissue should NOT fall back to the original (stale) one. |
{- "code": "string",
- "reason": "string",
- "reissuedBy": "string",
- "locale": "string",
- "email": "string"
}{- "code": "string",
- "esimId": "string",
- "iccid": "string",
- "installUrl": "string",
- "sentTo": "string",
- "emailDelivered": true
}{- "items": [
- {
- "reason": "hard_bounce",
- "suppressedAt": "2026-07-11T04:29:26.870Z",
- "firstNotificationId": "265d09b0-2099-5fc5-9cbb-6b7cb1b33d0b"
}
], - "total": 43,
- "limit": 100,
- "offset": 0
}{ }{- "deleted": true
}{ }{- "summary": {
- "removed": 42,
- "skipped": 1
}
}