{
  "openapi": "3.1.0",
  "info": {
    "title": "eSIMCenter Partner API",
    "version": "3.0.0",
    "summary": "Catalog, purchase, orders, wallet and webhooks for eSIMCenter partners.",
    "description": "Partner API v3. Authenticate with the `X-API-Key` header (the key is accepted nowhere else). Every response uses the same envelope and `code` is the real HTTP status. Branch on `error`, a stable machine code matching `^[a-z0-9_]+$`, never on `title`.\n\n**Sandbox first.** A key starting with `esk_test_` only works on the Sandbox server, never moves money and never reaches a provider; every response it produces carries `\"livemode\": false`. A live key (`esk_` + 40 hex) only works on the Live server. Moving to production is a change of server URL and key, nothing else.\n\nThe older address `https://esimcenter.com/api/v3` keeps working and accepts both key kinds, so existing integrations do not break.\n\nFull guide: https://esimcenter.com/pages/api",
    "contact": {
      "name": "eSIMCenter",
      "url": "https://esimcenter.com/contact"
    }
  },
  "servers": [
    {
      "url": "https://sandbox-api.esimcenter.com/v3",
      "description": "Sandbox (esk_test_ keys only). A live key here answers 401 live_key_on_sandbox_host."
    },
    {
      "url": "https://api.esimcenter.com/v3",
      "description": "Live (esk_ keys only). A sandbox key here answers 401 sandbox_key_on_live_host."
    }
  ],
  "security": [
    { "ApiKeyAuth": [] }
  ],
  "tags": [
    { "name": "Catalog", "description": "Countries and plans. Read-only; identical in both modes." },
    { "name": "Purchase", "description": "The only endpoint that spends money (live) or creates a fake order (sandbox)." },
    { "name": "Orders", "description": "Order list, order detail with activation data, and data usage." },
    { "name": "Account", "description": "Wallet, statement, revenue share, pricing and top-up." },
    { "name": "Webhooks", "description": "Read your webhook subscriptions and delivery log, and test an address. Subscriptions are created in the developer panel." },
    { "name": "Sandbox", "description": "Endpoints and fields that exist only for sandbox keys." }
  ],
  "paths": {
    "/catalog/countries": {
      "get": {
        "operationId": "catalogCountries",
        "tags": ["Catalog"],
        "summary": "List countries and regions",
        "description": "Every destination we sell, one row per country code, sorted by code. Regions appear in the `region` field and can be passed to `/catalog/products` as a country value. Rate tier `catalog`: 900 requests per minute per key.",
        "responses": {
          "200": {
            "description": "Country list. The example is trimmed to 3 of 230 countries.",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/XRateLimitLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/XRateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/XRateLimitReset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "count": { "type": "integer" },
                        "countries": { "type": "array", "items": { "$ref": "#/components/schemas/Country" } }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "count": 230,
                  "countries": [
                    {
                      "country_code": "AD",
                      "title": "Andorra",
                      "slug": "andorra",
                      "image": "https://esimcenter.com/cdn/flags/ad.png",
                      "region": "europe"
                    },
                    {
                      "country_code": "AE",
                      "title": "United Arab Emirates",
                      "slug": "united-arab-emirates",
                      "image": "https://esimcenter.com/cdn/flags/ae.png",
                      "region": "middle-east"
                    },
                    {
                      "country_code": "AG",
                      "title": "Antigua And Barbuda",
                      "slug": "antigua-and-barbuda",
                      "image": "https://esimcenter.com/cdn/flags/ag.png",
                      "region": "americas"
                    }
                  ],
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/catalog/products": {
      "get": {
        "operationId": "catalogProducts",
        "tags": ["Catalog"],
        "summary": "List the plans of one destination",
        "description": "The same set, in the same order, that a customer sees on the website country page. By default one winning plan per country, days and data group is returned and filters run after that choice; pass `all_variants=1` to filter across every variant. At most 500 plans per request; `truncated` is `true` when more matched. Every invalid filter value is refused with `invalid_<name>` instead of being ignored. Rate tier `catalog`: 900 requests per minute per key.",
        "parameters": [
          { "name": "country", "in": "query", "required": true, "description": "ISO country code (de, tr, us) or region code/slug (europe, global, middle-east). Errors: country_required, invalid_country, country_not_found.", "schema": { "type": "string" }, "example": "tr" },
          { "name": "type", "in": "query", "description": "Optional catalog group filter, passed through to the selector the website uses.", "schema": { "type": "string" } },
          { "name": "include_small", "in": "query", "description": "1 also returns packages under 1 GB. Off by default.", "schema": { "type": "string", "enum": ["0", "1"], "default": "0" }, "example": "1" },
          { "name": "all_variants", "in": "query", "description": "0: one winner per data/day group. 1: every variant.", "schema": { "type": "string", "enum": ["0", "1"], "default": "0" }, "example": "1" },
          { "name": "usage_metered", "in": "query", "description": "1 only plans whose cost follows measured usage, 0 only the others, empty both. Error: invalid_usage_metered.", "schema": { "type": "string", "enum": ["0", "1"] }, "example": "1" },
          { "name": "unlimited", "in": "query", "description": "1 only unlimited plans, 0 only capped plans, empty both. Error: invalid_unlimited.", "schema": { "type": "string", "enum": ["0", "1"] }, "example": "0" },
          { "name": "network_type", "in": "query", "description": "Advertised network generation, case-insensitive. Error: invalid_network_type.", "schema": { "type": "string", "enum": ["4G", "5G"] }, "example": "5G" },
          { "name": "min_data_mb", "in": "query", "description": "Minimum bundled data in MB (1024 = 1 GB). Unlimited plans report amount 0, so any min_data_mb excludes them. Error: invalid_min_data_mb.", "schema": { "type": "integer", "minimum": 0, "maximum": 10485760 }, "example": 3072 },
          { "name": "max_data_mb", "in": "query", "description": "Maximum bundled data in MB. Error: invalid_max_data_mb.", "schema": { "type": "integer", "minimum": 0, "maximum": 10485760 }, "example": 10240 },
          { "name": "day", "in": "query", "description": "Exact validity in days. Error: invalid_day.", "schema": { "type": "integer", "minimum": 1, "maximum": 3650 }, "example": 7 },
          { "name": "min_day", "in": "query", "description": "Minimum validity in days. Error: invalid_min_day.", "schema": { "type": "integer", "minimum": 1, "maximum": 3650 }, "example": 7 },
          { "name": "max_day", "in": "query", "description": "Maximum validity in days. Error: invalid_max_day.", "schema": { "type": "integer", "minimum": 1, "maximum": 3650 }, "example": 30 },
          { "name": "min_price", "in": "query", "description": "Minimum price you pay, in USD. Error: invalid_min_price.", "schema": { "type": "number", "minimum": 0, "maximum": 100000 }, "example": 1 },
          { "name": "max_price", "in": "query", "description": "Maximum price you pay, in USD. Error: invalid_max_price.", "schema": { "type": "number", "minimum": 0, "maximum": 100000 }, "example": 15 },
          { "name": "sort", "in": "query", "description": "Sort field. Plans with no per-GB value are placed last in both directions when sorting by price_per_gb. Error: invalid_sort.", "schema": { "type": "string", "enum": ["price", "price_per_gb", "data", "day", "title"], "default": "price" }, "example": "price_per_gb" },
          { "name": "order", "in": "query", "description": "Sort direction. Error: invalid_order.", "schema": { "type": "string", "enum": ["asc", "desc"], "default": "asc" }, "example": "asc" },
          { "name": "limit", "in": "query", "description": "How many plans to return. Error: invalid_limit.", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 500 }, "example": 3 },
          { "name": "offset", "in": "query", "description": "How many matched plans to skip. Error: invalid_offset.", "schema": { "type": "integer", "minimum": 0, "maximum": 999999, "default": 0 }, "example": 20 }
        ],
        "responses": {
          "200": {
            "description": "Plans. `total` is the count before filters, `matched` after, `count` the rows in this response.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "country_code": { "type": "string" },
                        "country": { "$ref": "#/components/schemas/Country" },
                        "include_small": { "type": "boolean" },
                        "all_variants": { "type": "boolean" },
                        "total": { "type": "integer" },
                        "matched": { "type": "integer" },
                        "count": { "type": "integer" },
                        "truncated": { "type": "boolean" },
                        "limit": { "type": "integer" },
                        "offset": { "type": "integer" },
                        "sort": { "type": "string" },
                        "order": { "type": "string" },
                        "filters": { "type": ["object", "array"], "description": "Echo of the filters that were applied." },
                        "products": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "country_code": "tr",
                  "country": {
                    "country_code": "TR",
                    "title": "Turkey",
                    "slug": "turkey",
                    "image": "https://esimcenter.com/cdn/flags/tr.png",
                    "region": ""
                  },
                  "include_small": false,
                  "all_variants": false,
                  "total": 132,
                  "matched": 49,
                  "count": 3,
                  "truncated": true,
                  "limit": 3,
                  "offset": 0,
                  "sort": "price_per_gb",
                  "order": "asc",
                  "filters": {
                    "min_data_mb": 3072,
                    "max_price": 15,
                    "unlimited": "0"
                  },
                  "products": [
                    {
                      "id": "esc_e7c9a2a773dcccba",
                      "title": "Turkey 3GB 15Days Premium",
                      "country_code": "tr",
                      "country_title": "Turkey",
                      "country_image": "https://esimcenter.com/cdn/flags/tr.png",
                      "region": "country",
                      "continent": "",
                      "data": "3 GB",
                      "amount": 3072,
                      "day": 15,
                      "is_unlimited": "",
                      "price": 1.66,
                      "sale_price": 1.66,
                      "price_per_gb": 0.55,
                      "operator_title": "",
                      "network_type": "5G",
                      "coverage_codes": [
                        "TR"
                      ],
                      "coverage_names_en": [
                        "Turkey"
                      ],
                      "fup_kind": "unknown",
                      "fup_daily_mb": 0,
                      "fup_speed_kbps": 0,
                      "fair_use_text": "",
                      "is_usage_metered": 0
                    },
                    {
                      "id": "esc_f6a4a80ceb7c06f4",
                      "title": "Turkey 20GB 30Days",
                      "country_code": "tr",
                      "country_title": "Turkey",
                      "country_image": "https://esimcenter.com/cdn/flags/tr.png",
                      "region": "country",
                      "continent": "",
                      "data": "20 GB",
                      "amount": 20480,
                      "day": 30,
                      "is_unlimited": "",
                      "price": 13.48,
                      "sale_price": 13.48,
                      "price_per_gb": 0.67,
                      "operator_title": "",
                      "network_type": "5G",
                      "coverage_codes": [
                        "TR"
                      ],
                      "coverage_names_en": [
                        "Turkey"
                      ],
                      "fup_kind": "unknown",
                      "fup_daily_mb": 0,
                      "fup_speed_kbps": 0,
                      "fair_use_text": "",
                      "is_usage_metered": 0
                    },
                    {
                      "id": "esc_bea698fef9e1b5fa",
                      "title": "Turkey 10GB 30Days",
                      "country_code": "tr",
                      "country_title": "Turkey",
                      "country_image": "https://esimcenter.com/cdn/flags/tr.png",
                      "region": "country",
                      "continent": "",
                      "data": "10 GB",
                      "amount": 10240,
                      "day": 30,
                      "is_unlimited": "",
                      "price": 6.76,
                      "sale_price": 6.76,
                      "price_per_gb": 0.68,
                      "operator_title": "",
                      "network_type": "5G",
                      "coverage_codes": [
                        "TR"
                      ],
                      "coverage_names_en": [
                        "Turkey"
                      ],
                      "fup_kind": "unknown",
                      "fup_daily_mb": 0,
                      "fup_speed_kbps": 0,
                      "fair_use_text": "",
                      "is_usage_metered": 0
                    }
                  ],
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/catalog/product/{id}": {
      "get": {
        "operationId": "catalogProduct",
        "tags": ["Catalog"],
        "summary": "Get one plan",
        "description": "One plan under a `product` key, same fields as the list. Works for variants that grouping hides from the list. Accepts only the opaque alias (`esc_` + 16 hex); a raw internal id and an unknown alias both answer 404 `product_not_found`. Rate tier `catalog`: 900 requests per minute per key.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "description": "Opaque product alias, `esc_` + 16 lowercase hex characters.", "schema": { "$ref": "#/components/schemas/ProductId" }, "example": "esc_0dde4d49725f9e60" }
        ],
        "responses": {
          "200": {
            "description": "The plan.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "product": { "$ref": "#/components/schemas/Product" } } }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "product": {
                    "id": "esc_0dde4d49725f9e60",
                    "title": "Turkey 1GB 7Days",
                    "country_code": "tr",
                    "country_title": "Turkey",
                    "country_image": "https://esimcenter.com/cdn/flags/tr.png",
                    "region": "country",
                    "continent": "",
                    "data": "1 GB",
                    "amount": 1024,
                    "day": 7,
                    "is_unlimited": "",
                    "price": 1.43,
                    "sale_price": 1.43,
                    "price_per_gb": 1.43,
                    "operator_title": "",
                    "network_type": "5G",
                    "coverage_codes": [
                      "TR"
                    ],
                    "coverage_names_en": [
                      "Turkey"
                    ],
                    "fup_kind": "unknown",
                    "fup_daily_mb": 0,
                    "fup_speed_kbps": 0,
                    "fair_use_text": "",
                    "is_usage_metered": 0
                  },
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/purchase/buy": {
      "post": {
        "operationId": "purchaseBuy",
        "tags": ["Purchase"],
        "summary": "Buy an eSIM",
        "description": "Live key: checks the price, charges your wallet (prepaid) or credit line (postpaid) and orders the eSIM. Sandbox key: same input checks and your real partner price, then creates a sandbox order instead of charging you (balance stays 1000.00).\n\nThe response never carries the activation payload; read `esim` from `GET /order/detail/{id}`.\n\n`200 delivered`: the eSIM exists. `202 deferred`: paid, arriving later, do NOT buy again; wait for the `order.delivered` webhook or poll `order/detail`. `200 failed`: the order will not be delivered.\n\nSend your own unique `idempotency_key` per intended purchase. The same key with the same product returns the recorded result and moves no money; with another product it is 409. To retry a failed order, send a NEW key.\n\nRate tier `purchase`: 150 requests per minute per key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": ["product_id"],
                "properties": {
                  "product_id": { "$ref": "#/components/schemas/ProductId" },
                  "idempotency_key": { "type": "string", "maxLength": 191, "description": "Strongly recommended. At most 191 bytes after trimming; no quotes, backslash, < >, control characters or 4-byte characters. A UUID fits. Errors: idempotency_key_too_long, invalid_idempotency_key." },
                  "sandbox_scenario": { "type": "string", "enum": ["success", "deferred", "failed"], "description": "Sandbox keys only; live keys ignore it. Empty means success. Any other value is 400 invalid_sandbox_scenario." },
                  "sandbox_usage": { "type": "string", "enum": ["not_active", "active", "depleted", "expired"], "default": "not_active", "description": "Sandbox keys only. Decides what `order/usage/{iccid}` reports for the fake eSIM of this order." }
                }
              },
              "examples": {
                "live": { "summary": "Plain purchase", "value": { "product_id": "esc_1da490735cdc625a", "idempotency_key": "1f7c0c9e-7b3a-4a6e-9f11-9a5b2d7c4e01" } },
                "sandboxDeferred": { "summary": "Sandbox: deferred delivery, half used", "value": { "product_id": "esc_1da490735cdc625a", "idempotency_key": "1f7c0c9e-7b3a-4a6e-9f11-9a5b2d7c4e02", "sandbox_scenario": "deferred", "sandbox_usage": "active" } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Order created. `delivery` is `delivered` or `failed`.",
            "content": { "application/json": {
              "schema": { "$ref": "#/components/schemas/BuyResponse" },
              "example": {
                "result": "ok",
                "code": "200",
                "id": "2470",
                "order_id": 2470,
                "billing_mode": "prepaid",
                "balance": 3.94,
                "delivery": "delivered",
                "delivery_reason": "",
                "provision": "ok",
                "order": {
                  "id": "2470",
                  "product_id": "esc_1da490735cdc625a",
                  "product_title": "Turkey 100MB 7Days",
                  "product_price": "1.06",
                  "sale_price": "1.06",
                  "coupon_code": "",
                  "product_type": "esim",
                  "date": "2026-09-28 13:06:09",
                  "status": "1",
                  "api_sale_id": "B26092810060019",
                  "api_sale_code": "26092810060022"
                },
                "title": "Successful",
                "api_version": 3
              }
            } }
          },
          "202": {
            "description": "Paid, delivery deferred (`delivery: deferred`). Do not buy again.",
            "content": { "application/json": {
              "schema": { "$ref": "#/components/schemas/BuyResponse" },
              "example": {
                "result": "ok",
                "code": "202",
                "id": "2471",
                "order_id": 2471,
                "billing_mode": "postpaid",
                "balance": 0,
                "delivery": "deferred",
                "delivery_reason": "awaiting_profile",
                "provision": "ok",
                "order": {
                  "id": "2471",
                  "product_id": "esc_e4c66b73d7954ed2",
                  "product_title": "Algeria 100MB 7Days",
                  "product_price": "1.06",
                  "sale_price": "1.06",
                  "coupon_code": "",
                  "product_type": "esim",
                  "date": "2026-09-28 13:06:58",
                  "status": "4",
                  "api_sale_id": "B26092810070001",
                  "api_sale_code": "sale2471"
                },
                "title": "Successful",
                "api_version": 3
              }
            } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "description": "`insufficient_balance` (prepaid; carries `balance`, `required`) or `not_postpaid`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "`credit_limit_exceeded` (postpaid; carries `outstanding`, `credit_limit`, `available`, `required`), `partner_account_suspended`, or an authentication refusal.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "409": { "description": "`charge_in_progress` (retry with the same key), `charge_incomplete` (needs manual repair, retrying does not fix it) or `idempotency_key_conflict`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/order/get": {
      "get": {
        "operationId": "orderGet",
        "tags": ["Orders"],
        "summary": "List your orders",
        "description": "Your orders, newest first, under the singular key `order`. Sandbox keys see only their own sandbox orders; failed sandbox orders are left out, as failed live orders are. Rate tier `read`: 360 requests per minute per key.",
        "parameters": [
          { "name": "limit", "in": "query", "description": "Page size.", "schema": { "type": "integer", "default": 10 }, "example": 3 },
          { "name": "start", "in": "query", "description": "1-based page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 }, "example": 1 }
        ],
        "responses": {
          "200": {
            "description": "Orders.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "order": { "type": "array", "items": { "$ref": "#/components/schemas/OrderListItem" } } } }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "order": [
                    {
                      "id": "2471",
                      "product_id": "esc_e4c66b73d7954ed2",
                      "product_title": "Algeria 100MB 7Days",
                      "title_display": "100 MB · 7 days",
                      "product_price": "1.06",
                      "sale_price": "1.06",
                      "product_type": "esim",
                      "date": "2026-09-28 13:06:58",
                      "status": "1",
                      "api_sale_id": "B26092810070001",
                      "api_sale_code": "26092810070001",
                      "esim": {
                        "qrcode_url": "",
                        "iccid": "<redacted>",
                        "lpa": "<redacted>",
                        "smdpAddress": "<redacted>",
                        "activationCode": "<redacted>",
                        "matching_id": null,
                        "direct_apple_installation_url": null
                      },
                      "data": "100 MB",
                      "day": "7",
                      "is_unlimited": "",
                      "country_code": "dz",
                      "country_image": "https://esimcenter.com/cdn/flags/dz.png",
                      "country_name": "Algeria",
                      "fair_use_text": "",
                      "coupon_code": ""
                    }
                  ],
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/order/detail/{id}": {
      "get": {
        "operationId": "orderDetail",
        "tags": ["Orders"],
        "summary": "Get one order with activation data",
        "description": "One order in full. The `esim` block carries the activation details (`lpa`, `smdpAddress`, `activationCode`, `matching_id`); treat it as a secret. An id that is not yours answers `404 order_not_found`, the same as an id that does not exist. Rate tier `read`: 360 requests per minute per key.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "description": "Order id (the `id` / `order_id` returned by `purchase/buy`).", "schema": { "type": "integer", "minimum": 1 }, "example": 2470 }
        ],
        "responses": {
          "200": {
            "description": "The order.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "order": { "type": "object", "additionalProperties": true },
                        "esim": { "type": "object", "additionalProperties": true, "description": "Activation details: qrcode_url, iccid, lpa, smdpAddress, activationCode, matching_id, confirmation_code, apn and related fields." },
                        "operator_meta": { "type": "object", "additionalProperties": true }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "order": {
                    "id": "2470",
                    "product_id": "esc_1da490735cdc625a",
                    "product_title": "Turkey 100MB 7Days",
                    "title_display": "100 MB · 7 days",
                    "product_price": "1.06",
                    "sale_price": "1.06",
                    "coupon_code": "",
                    "date": "2026-09-28 13:06:09",
                    "status": "1",
                    "api_sale_id": "B26092810060019",
                    "api_sale_code": "26092810060022",
                    "data": "100 MB",
                    "day": "7",
                    "is_unlimited": false,
                    "voice": "",
                    "text": "",
                    "short_info": "Turkey 100MB 7Days",
                    "fair_use_text": "",
                    "fup_kind": "unknown",
                    "fup_daily_mb": 0,
                    "fup_speed_kbps": 0,
                    "operator_title": "",
                    "operator_image": "https://esimcenter.com/cdn/assets/images/providers/<redacted>.jpg",
                    "country_code": "tr",
                    "country_image": "https://esimcenter.com/cdn/flags/tr.png",
                    "country_name": "Turkey"
                  },
                  "esim": {
                    "qrcode": null,
                    "qrcode_url": "",
                    "iccid": "<redacted>",
                    "lpa": "<redacted>",
                    "matching_id": null,
                    "smdpAddress": "<redacted>",
                    "activationCode": "<redacted>",
                    "confirmation_code": null,
                    "msisdn": null,
                    "apn": "internet",
                    "apn_type": null,
                    "apn_value": null,
                    "is_roaming": null
                  },
                  "operator_meta": {
                    "activation_policy": null,
                    "plan_type": "data",
                    "esim_type": "Prepaid",
                    "is_roaming": null,
                    "is_kyc_verify": null,
                    "rechargeability": null,
                    "apn_type": null,
                    "apn_value": null,
                    "info": null,
                    "other_info": null,
                    "warning": null
                  },
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/order/usage/{iccid}": {
      "get": {
        "operationId": "orderUsage",
        "tags": ["Orders"],
        "summary": "Get data usage of an eSIM",
        "description": "Remaining data. Cache it for a minute or two. `404 usage_not_available` means the ICCID is not yours, has no usage record yet, or the provider did not answer; it is not an integration error. With a sandbox key only the ICCID of your own delivered sandbox order works, and the figures follow the `sandbox_usage` chosen at purchase: `not_active` (default) 1024 MB total and 0 used, `active` 1024 total and 512 used, `depleted` 1024 of 1024 used, `expired` with an expiry date in the past. Rate tier `read`: 360 requests per minute per key.",
        "parameters": [
          { "name": "iccid", "in": "path", "required": true, "description": "ICCID of the eSIM. Sandbox ICCIDs start with 89000.", "schema": { "type": "string" }, "example": "8900000000000000001" }
        ],
        "responses": {
          "200": {
            "description": "Usage.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "usage": { "$ref": "#/components/schemas/Usage" } } }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "usage": {
                    "iccid": "<redacted>",
                    "sale_id": 2470,
                    "title": "Turkey 100MB 7Days",
                    "status": "not_active",
                    "tone": "ok",
                    "total_mb": 100,
                    "used_mb": 0,
                    "remaining_mb": 100,
                    "percent_remaining": 100,
                    "is_unlimited": false,
                    "fair_use_text": "",
                    "activated_at": null,
                    "expires_at": null,
                    "days_left": null,
                    "fetched_at": "2026-09-28 14:00:03"
                  },
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account/balance": {
      "get": {
        "operationId": "accountBalance",
        "tags": ["Account"],
        "summary": "Get wallet balance",
        "description": "Spendable balance in USD. Sandbox keys always see 1000.00, and purchases do not reduce it. On invoice terms read `account/statement` instead. Rate tier `read`: 360 requests per minute per key.",
        "responses": {
          "200": {
            "description": "Balance.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "balance": { "type": "number" } } }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "balance": 0,
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account/history": {
      "get": {
        "operationId": "accountHistory",
        "tags": ["Account"],
        "summary": "List wallet movements",
        "description": "Wallet movements, newest first, each with the balance that resulted. Sandbox keys get an empty ledger. A read failure is `500 history_read_failed`, which means unknown, not empty. Rate tier `read`: 360 requests per minute per key.",
        "parameters": [
          { "name": "limit", "in": "query", "description": "How many movements. Values above 500 are clamped.", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 50 }, "example": 3 },
          { "name": "start", "in": "query", "description": "1-based page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 }, "example": 1 }
        ],
        "responses": {
          "200": {
            "description": "Movements.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "ledger": { "type": "array", "items": { "$ref": "#/components/schemas/LedgerItem" } },
                        "balance": { "type": "number" },
                        "balances": { "type": "object", "additionalProperties": true, "description": "Split behind `balance`: cash, promo and debt." }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "ledger": [],
                  "balance": 0,
                  "balances": {
                    "balance": 0,
                    "promo": 0,
                    "cash": 0,
                    "debt": 0
                  },
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/account/statement": {
      "get": {
        "operationId": "accountStatement",
        "tags": ["Account"],
        "summary": "Get invoice-terms statement",
        "description": "Billing mode, credit limit, outstanding debt, remaining credit (`available` may be negative) and credit-line movements. `404 partner_account_not_found` when no billing account was ever opened for you. Sandbox keys see a prepaid account with zeros and an empty ledger. Rate tier `read`: 360 requests per minute per key.",
        "parameters": [
          { "name": "limit", "in": "query", "description": "How many movements per page. Default 50, at most 500.", "schema": { "type": "integer", "default": 50 }, "example": 3 },
          { "name": "start", "in": "query", "description": "1-based page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 }, "example": 1 },
          { "name": "type", "in": "query", "description": "Optional movement type filter: `charge`, `payment`, `adjust`, `refund` or `profit_share`. Any other value is ignored.", "schema": { "type": "string", "enum": ["charge", "payment", "adjust", "refund", "profit_share"] }, "example": "charge" }
        ],
        "responses": {
          "200": {
            "description": "Statement.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "billing_mode": { "type": "string", "enum": ["prepaid", "postpaid"] },
                        "credit_limit": { "type": "string", "description": "Decimal string, 2 places." },
                        "outstanding": { "type": "string", "description": "Decimal string, 2 places." },
                        "available": { "type": "string", "description": "Decimal string, 2 places; negative when over the limit." },
                        "profit_share_enabled": { "type": "integer", "enum": [0, 1] },
                        "status": { "type": "integer" },
                        "currency": { "type": "string", "examples": ["USD"] },
                        "ledger": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
                        "count": { "type": "integer" }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "billing_mode": "postpaid",
                  "credit_limit": "5.00",
                  "outstanding": "1.06",
                  "available": "3.94",
                  "profit_share_enabled": 1,
                  "status": 1,
                  "currency": "USD",
                  "ledger": [
                    {
                      "id": "1",
                      "type": "charge",
                      "amount": "1.06",
                      "balance_after": "1.06",
                      "note": "eSIM purchase #2471",
                      "date": "2026-09-28 13:06:58"
                    }
                  ],
                  "count": 1,
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account/profitshare": {
      "get": {
        "operationId": "accountProfitshare",
        "tags": ["Account"],
        "summary": "List revenue share on unused data",
        "description": "Revenue share lines plus page-independent totals. `profit_share_enabled: 0` is not an error; history stays visible. Sandbox keys get `profit_share_enabled: 0`, an empty list and `total_share` 0.00. Rate tier `read`: 360 requests per minute per key.",
        "parameters": [
          { "name": "limit", "in": "query", "description": "How many lines per page. Default 50, at most 500.", "schema": { "type": "integer", "default": 50 }, "example": 3 },
          { "name": "start", "in": "query", "description": "1-based page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 }, "example": 1 },
          { "name": "status", "in": "query", "description": "Optional status filter: `shadow`, `settled` or `none`. Any other value is ignored.", "schema": { "type": "string", "enum": ["shadow", "settled", "none"] }, "example": "settled" }
        ],
        "responses": {
          "200": {
            "description": "Revenue share.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "profit_share_enabled": { "type": "integer", "enum": [0, 1] },
                        "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
                        "count": { "type": "integer" },
                        "total_share": { "type": "string" },
                        "total_unused_mb": { "type": "integer" },
                        "currency": { "type": "string", "examples": ["USD"] }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "profit_share_enabled": 1,
                  "items": [],
                  "count": 0,
                  "total_share": "0.00",
                  "total_unused_mb": 0,
                  "currency": "USD",
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account/pricing": {
      "get": {
        "operationId": "accountPricing",
        "tags": ["Account"],
        "summary": "Get your pricing summary",
        "description": "Your contract numbers: margin share, total share cap, number of active special price rules, and a measured average discount. `average_discount_pct` is a measurement, not a promise: `null` means it could not be measured, `0` means measured with no discount. Sandbox keys get the same live answer. Rate tier `read`: 360 requests per minute per key.",
        "responses": {
          "200": {
            "description": "Pricing summary.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "pricing": {
                          "type": "object",
                          "additionalProperties": true,
                          "properties": {
                            "margin_share_pct": { "type": "number" },
                            "total_share_cap_pct": { "type": "number" },
                            "active_rule_count": { "type": "integer" },
                            "average_discount_pct": { "type": ["number", "null"] },
                            "is_average": { "type": "boolean" },
                            "sample_size": { "type": "integer" },
                            "sample_scope": { "type": "string" },
                            "currency": { "type": "string" }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account/topup": {
      "post": {
        "operationId": "accountTopup",
        "tags": ["Account"],
        "summary": "Start a wallet top-up",
        "description": "Live key: creates a Stripe payment intent; you complete the payment with Stripe's own SDK using the returned `client_secret`, then call `topupConfirm` with the `payment_key`. Sandbox key: returns a fake intent (`id` starting `sbx_topup_`, `client_secret` starting `sbx_secret_`, `status: requires_confirmation`, `livemode: false`); no Stripe call is made and the balance stays 1000.00. Rate tier `money`: 30 requests per minute per key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": ["amount"],
                "properties": {
                  "amount": { "type": "number", "exclusiveMinimum": 0, "description": "Amount credited to the wallet, in USD. Outside the accepted range: 400 amount_out_of_range (carries min and max)." }
                }
              },
              "example": {
                "amount": 50
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment intent. Live fields include `client_secret` and `payment_key`; the full shape is not fixed here.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "additionalProperties": true, "properties": { "client_secret": { "type": "string" }, "payment_key": { "type": "string" } } }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account/topupConfirm": {
      "post": {
        "operationId": "accountTopupConfirm",
        "tags": ["Account"],
        "summary": "Confirm a wallet top-up",
        "description": "Verifies the payment and credits the wallet. Idempotent: confirming twice credits once. Sandbox key: returns `succeeded` without touching any wallet. Rate tier `money`: 30 requests per minute per key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": ["payment_key"],
                "properties": {
                  "payment_key": { "type": "string", "description": "The `payment_key` returned by `topup`. Sandbox keys return keys that start with `sbx_topup_`." }
                }
              },
              "example": {
                "payment_key": "sbx_topup_0123456789abcdef01234567_5000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Top-up confirmed. The full shape is not fixed here.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "additionalProperties": true }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account/sandboxReset": {
      "post": {
        "operationId": "accountSandboxReset",
        "tags": ["Sandbox"],
        "summary": "Delete your sandbox orders",
        "description": "Sandbox keys only. Deletes the sandbox orders and delivery records of your account so you can start a test run from a clean state. A live key gets `400 sandbox_only_action` and nothing is touched. Rate tier `purchase`: 150 requests per minute per key.",
        "responses": {
          "200": {
            "description": "Reset done.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "deleted_orders": { "type": "integer" } } }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "deleted_orders": 4,
                  "livemode": false
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/webhook/list": {
      "get": {
        "operationId": "webhookList",
        "tags": ["Webhooks"],
        "summary": "List your webhook subscriptions",
        "description": "Your subscriptions of the key's own mode: sandbox keys see only sandbox subscriptions, live keys only live ones. The signing secret is never returned, only `secret_mask`. Subscriptions are created in the developer panel. Rate tier `read`: 360 requests per minute per key.",
        "responses": {
          "200": {
            "description": "Subscriptions.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "subscriptions": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookSubscription" } },
                        "count": { "type": "integer" },
                        "events": { "type": "array", "items": { "type": "string" }, "description": "All event names you can subscribe to, for example order.created, order.paid, order.delivered, order.deferred, order.failed, order.test." }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "subscriptions": [
                    {
                      "id": "10",
                      "url": "https://example.com/webhooks/esimcenter",
                      "secret_mask": "a1b2c3...",
                      "events_list": [
                        "order.created",
                        "order.paid",
                        "order.delivered",
                        "order.deferred",
                        "order.failed",
                        "order.test"
                      ],
                      "status": "1",
                      "fail_count": "0",
                      "date": "2026-09-28 12:44:26",
                      "updated_at": "2026-09-28 13:39:09"
                    }
                  ],
                  "count": 1,
                  "events": [
                    "order.created",
                    "order.paid",
                    "order.delivered",
                    "order.deferred",
                    "order.failed",
                    "order.test"
                  ],
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/webhook/deliveries": {
      "get": {
        "operationId": "webhookDeliveries",
        "tags": ["Webhooks"],
        "summary": "Read the webhook delivery log",
        "description": "Without `id` the latest deliveries (without body). With `id` one delivery with the exact body we sent in `payload` and the `signature_rule`. Sandbox keys see only deliveries of sandbox subscriptions. Rate tier `read`: 360 requests per minute per key.",
        "parameters": [
          { "name": "id", "in": "query", "description": "Delivery id. When present, the single-delivery mode is used and the other filters are ignored.", "schema": { "type": "integer", "minimum": 1 }, "example": 27 },
          { "name": "status", "in": "query", "description": "Delivery status filter. The accepted values are returned in `statuses`; others answer 400 invalid_status.", "schema": { "type": "string" }, "example": "sent" },
          { "name": "event", "in": "query", "description": "Event filter, one of `events`; others answer 400 invalid_event.", "schema": { "type": "string" }, "example": "order.paid" },
          { "name": "subscription_id", "in": "query", "description": "Digits only; otherwise 400 invalid_subscription_id.", "schema": { "type": "string", "pattern": "^\\d{1,20}$" }, "example": "10" },
          { "name": "limit", "in": "query", "description": "1 to 200. Otherwise 400 invalid_limit.", "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }, "example": 3 }
        ],
        "responses": {
          "200": {
            "description": "Deliveries (list mode) or one delivery with `payload` (single mode).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "count": { "type": "integer" },
                        "limit": { "type": "integer" },
                        "statuses": { "type": "array", "items": { "type": "string" } },
                        "events": { "type": "array", "items": { "type": "string" } },
                        "deliveries": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookDelivery" } },
                        "delivery": { "$ref": "#/components/schemas/WebhookDelivery" },
                        "payload": { "type": "string", "description": "Single mode: the body sent, byte for byte." },
                        "payload_pruned": { "type": "boolean", "description": "True when the stored body was already removed by the retention job." },
                        "signature_rule": { "type": "string" }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "count": 2,
                  "limit": 3,
                  "statuses": [
                    "pending",
                    "sent",
                    "failed",
                    "dlq"
                  ],
                  "events": [
                    "order.created",
                    "order.paid",
                    "order.delivered",
                    "order.deferred",
                    "order.failed",
                    "order.test"
                  ],
                  "deliveries": [
                    {
                      "id": "27",
                      "subscription_id": "10",
                      "event_id": "order.test:1001:1790594355",
                      "event_type": "order.test",
                      "sale_id": "0",
                      "status": "sent",
                      "attempts": "1",
                      "max_attempts": "5",
                      "http_code": "200",
                      "duration_ms": "17",
                      "response_body": "{\"ok\":true,\"received_bytes\":263,\"event\":\"order.test\"}",
                      "error": "",
                      "sent_at": "2026-09-28 14:20:05",
                      "date": "2026-09-28 14:19:15"
                    },
                    {
                      "id": "26",
                      "subscription_id": "10",
                      "event_id": "order.test:1001:1790594353",
                      "event_type": "order.test",
                      "sale_id": "0",
                      "status": "sent",
                      "attempts": "1",
                      "max_attempts": "5",
                      "http_code": "200",
                      "duration_ms": "12",
                      "response_body": "{\"ok\":true,\"received_bytes\":263,\"event\":\"order.test\"}",
                      "error": "",
                      "sent_at": "2026-09-28 14:20:04",
                      "date": "2026-09-28 14:19:13"
                    }
                  ],
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/webhook/test": {
      "post": {
        "operationId": "webhookTest",
        "tags": ["Webhooks"],
        "summary": "Queue an order.test event",
        "description": "Queues a signed `order.test` event to your registered subscriptions of the key's mode. Delivery happens asynchronously; read the result from `webhook/deliveries`. Sandbox keys: only sandbox subscriptions, event id starts with `sbx_`; when your sandbox queue is full the answer is 429 `sandbox_queue_full`. Rate tier `outbound`: 18 requests per minute per key.",
        "requestBody": {
          "required": false,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "subscription_id": { "type": "integer", "description": "Target one subscription. Omit to target all your active subscriptions. Not yours: 404 subscription_not_found." }
                }
              },
              "example": {
                "subscription_id": 10
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event queued.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "event": { "type": "string" },
                        "event_id": { "type": "string" },
                        "queued": { "type": "integer" },
                        "subscriptions": { "type": "integer" },
                        "delivery_mode": { "type": "string", "examples": ["queued"] }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "event": "order.test",
                  "event_id": "order.test:1001:1790594918",
                  "queued": 1,
                  "subscriptions": 1,
                  "delivery_mode": "queued",
                  "title": "Successful",
                  "api_version": 3
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "409": { "description": "`no_active_subscription`: the subscription exists but is paused or disabled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/webhook/probe": {
      "post": {
        "operationId": "webhookProbe",
        "tags": ["Webhooks"],
        "summary": "Test an address without saving it",
        "description": "Sends one signed request to the given address right now and returns the HTTP status, latency and a trimmed response. Nothing is saved. A fresh single-use secret is generated and returned as `probe_secret` so you can test your signature check. Internal and private addresses are refused with `400 probe_blocked` and no request is sent. Same behavior for both key kinds; the `livemode` field in the sent body follows the key's mode. Rate tier `outbound`: 18 requests per minute per key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": { "type": "string", "maxLength": 500, "description": "https address to test. Errors: url_required, url_too_long, probe_blocked." },
                  "event": { "type": "string", "default": "order.test", "description": "Event name to simulate; one of the events returned by `webhook/list`. Otherwise 400 invalid_event." }
                }
              },
              "example": {
                "url": "https://esimcenter.com/",
                "event": "order.paid"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A request was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "request_sent": { "type": "boolean" },
                        "saved": { "type": "boolean", "examples": [false] },
                        "url": { "type": "string" },
                        "event": { "type": "string" },
                        "event_id": { "type": "string" },
                        "http_status": { "type": "integer" },
                        "accepted": { "type": "boolean", "description": "True when the receiver answered 2xx." },
                        "duration_ms": { "type": "integer" },
                        "response_body": { "type": "string" },
                        "error": { "type": "string" },
                        "payload": { "type": "string" },
                        "timestamp": { "type": "integer" },
                        "signature": { "type": "string" },
                        "probe_secret": { "type": "string" },
                        "signature_rule": { "type": "string" }
                      }
                    }
                  ]
                },
                "example": {
                  "result": "ok",
                  "code": "200",
                  "request_sent": true,
                  "saved": false,
                  "url": "https://esimcenter.com/",
                  "event": "order.paid",
                  "event_id": "probe.order.paid:1001:1790594918",
                  "http_status": 200,
                  "accepted": true,
                  "duration_ms": 120,
                  "response_body": "<!DOCTYPE html>\n<html lang=\"en\" dir=\"ltr\">\n<head><meta charset=\"utf-8\">…",
                  "payload": "{\"order_id\":0,\"status\":\"test\",\"delivery\":\"delivered\",\"delivery_reason\":\"\",\"iccid\":\"\",\"qr_code\":\"\",\"occurred_at\":\"2026-09-28T14:28:38+03:00\",\"event\":\"order.paid\",\"event_id\":\"probe.order.paid:1001:1790594918\"}",
                  "timestamp": 1790594918,
                  "signature": "<redacted>",
                  "probe_secret": "<redacted>",
                  "signature_rule": "X-Signature = 'sha256=' + HMAC_SHA256(timestamp + '.' + payload, probe_secret)",
                  "title": "Successful",
                  "api_version": 3,
                  "error": ""
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "405": { "$ref": "#/components/responses/MethodNotAllowed" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Live keys look like `esk_` + 40 hex and work only on the Live server. Sandbox keys look like `esk_test_` + 40 hex and work only on the Sandbox server. The key is accepted only in this header; in the URL or body it answers 401 api_key_header_required."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request itself is wrong; do not retry it unchanged. `error` holds the machine code, for example `country_required`, `invalid_country`, `invalid_sort`, `product_id_required`, `invalid_product`, `amount_required`, `invalid_sandbox_scenario`, `sandbox_unsupported_action`, `sandbox_only_action`.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unauthorized": {
        "description": "Authentication refused. `error` is one of `api_key_required` (header missing), `api_key_header_required` (key sent in URL or body), `invalid_api_key` (unknown, revoked or expired), `live_key_on_sandbox_host` (a live key was sent to the Sandbox server) or `sandbox_key_on_live_host` (a sandbox key was sent to the Live server).",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                { "$ref": "#/components/schemas/Error" },
                {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": ["api_key_required", "api_key_header_required", "invalid_api_key", "live_key_on_sandbox_host", "sandbox_key_on_live_host"]
                    }
                  }
                }
              ]
            },
            "examples": {
              "liveKeyOnSandbox": { "value": { "result": "fail", "error": "live_key_on_sandbox_host", "code": "401", "title": "Unsuccessful", "api_version": 3 } },
              "sandboxKeyOnLive": { "value": { "result": "fail", "error": "sandbox_key_on_live_host", "code": "401", "title": "Unsuccessful", "api_version": 3 } }
            }
          }
        }
      },
      "Forbidden": {
        "description": "`partner_not_found`, `partner_disabled`, `ip_not_allowed` (the key is locked to other source addresses), `topup_not_available`, `credit_limit_exceeded` or `partner_account_suspended`.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Not found, for example `unknown_endpoint`, `unknown_method`, `product_not_found`, `country_not_found`, `order_not_found`, `usage_not_available`, `partner_account_not_found`, `no_subscription`, `subscription_not_found`, `delivery_not_found`, `topup_not_found`.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "MethodNotAllowed": {
        "description": "Wrong HTTP verb (`method_not_allowed`); the body carries `allow`.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Over the per-minute ceiling of the endpoint's tier. Wait `retry_after` seconds (also in the `Retry-After` header). In the sandbox, `sandbox_queue_full` is also a 429 on `webhook/test`.",
        "headers": {
          "Retry-After": { "schema": { "type": "integer" }, "description": "Seconds to wait." }
        },
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                { "$ref": "#/components/schemas/Error" },
                {
                  "type": "object",
                  "properties": {
                    "tier": { "type": "string", "enum": ["catalog", "read", "purchase", "money", "outbound"] },
                    "limit_per_minute": { "type": "integer" },
                    "retry_after": { "type": "integer" }
                  }
                }
              ]
            }
          }
        }
      },
      "ServerError": {
        "description": "A read failed or a row could not be written, for example `order_read_failed`, `history_read_failed`, `webhook_read_failed`, `sandbox_webhook_read_failed`, `sale_insert_failed`. Treat read failures as unknown, not as empty. Retrying is safe for reads; retry a purchase with the same `idempotency_key`.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "headers": {
      "XRateLimitLimit": { "description": "Per-minute ceiling of this endpoint's tier.", "schema": { "type": "integer" } },
      "XRateLimitRemaining": { "description": "Requests left in the sliding window.", "schema": { "type": "integer" } },
      "XRateLimitReset": { "description": "Seconds until the window rolls.", "schema": { "type": "integer" } }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "description": "Fields every response carries.",
        "properties": {
          "result": { "type": "string", "enum": ["ok", "fail"] },
          "code": { "type": "string", "description": "The real HTTP status, as a string." },
          "title": { "type": "string", "description": "Human label; may be reworded. Do not branch on it." },
          "api_version": { "type": "integer", "examples": [3] },
          "livemode": { "type": "boolean", "description": "Present and `false` on every response produced by a sandbox key, errors included. Absent on responses served by the live path (catalog, pricing, probe)." }
        },
        "required": ["result", "code"],
        "additionalProperties": true
      },
      "Error": {
        "type": "object",
        "properties": {
          "result": { "type": "string", "enum": ["fail"] },
          "error": { "type": "string", "pattern": "^[a-z0-9_]+$", "description": "Stable machine code." },
          "code": { "type": "string" },
          "title": { "type": "string" },
          "api_version": { "type": "integer", "examples": [3] },
          "livemode": { "type": "boolean" }
        },
        "required": ["result", "error", "code"],
        "additionalProperties": true,
        "examples": [
          { "result": "fail", "error": "product_not_found", "code": "404", "title": "Unsuccessful", "api_version": 3 }
        ]
      },
      "ProductId": {
        "type": "string",
        "pattern": "^esc_[0-9a-f]{16}$",
        "description": "Opaque, stable, irreversible product alias. Raw internal ids are refused.",
        "examples": ["esc_0dde4d49725f9e60"]
      },
      "Country": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "country_code": { "type": "string", "examples": ["TR"] },
          "title": { "type": "string" },
          "slug": { "type": "string" },
          "image": { "type": "string", "format": "uri", "description": "Absolute flag URL on our own domain." },
          "region": { "type": "string" }
        }
      },
      "Product": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "id": { "$ref": "#/components/schemas/ProductId" },
          "title": { "type": "string" },
          "country_code": { "type": "string" },
          "country_title": { "type": "string" },
          "country_image": { "type": "string", "format": "uri" },
          "region": { "type": "string", "examples": ["country", "regional"] },
          "continent": { "type": "string" },
          "data": { "type": "string", "description": "Human string, for example `1 GB`." },
          "amount": { "type": "integer", "description": "Data in MB. 0 for unlimited plans." },
          "day": { "type": "integer" },
          "is_unlimited": { "type": "string", "description": "Empty string, or `1` for unlimited plans." },
          "price": { "type": "number", "description": "USD you pay. Same number as sale_price." },
          "sale_price": { "type": "number" },
          "price_per_gb": { "type": "number" },
          "operator_title": { "type": "string", "description": "May be empty." },
          "operator_image": { "type": "string" },
          "network_type": { "type": "string", "examples": ["5G"] },
          "coverage_codes": { "type": "array", "items": { "type": "string" } },
          "coverage_names_en": { "type": "array", "items": { "type": "string" } },
          "fup_kind": { "type": "string", "examples": ["unknown"] },
          "fup_daily_mb": { "type": "integer" },
          "fup_speed_kbps": { "type": "integer" },
          "fair_use_text": { "type": "string" },
          "is_usage_metered": { "type": "integer", "enum": [0, 1] }
        }
      },
      "BuyResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/Envelope" },
          {
            "type": "object",
            "properties": {
              "id": { "type": "string" },
              "order_id": { "type": "integer" },
              "billing_mode": { "type": "string", "enum": ["prepaid", "postpaid"] },
              "balance": { "type": "number" },
              "delivery": { "type": "string", "enum": ["delivered", "deferred", "failed"] },
              "delivery_reason": {
                "type": "string",
                "description": "Closed set of machine codes: awaiting_payment, queued, awaiting_profile, provider_throttled, provider_balance, provider_error, retry_exhausted, manual_reconcile, inflight_wait. Empty when delivered. Ignore values you do not know.",
                "examples": ["", "awaiting_profile"]
              },
              "provision": { "type": ["string", "null"], "description": "`ok` or `fail` when the provider was called on this request; null on an idempotent replay." },
              "order": { "type": "object", "additionalProperties": true },
              "warnings": { "type": "array", "items": { "type": "string" }, "description": "Sandbox only, for example `sandbox_queue_full`." }
            }
          }
        ]
      },
      "OrderListItem": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "id": { "type": "string" },
          "product_id": { "$ref": "#/components/schemas/ProductId" },
          "product_title": { "type": "string" },
          "title_display": { "type": "string" },
          "product_price": { "type": "string" },
          "sale_price": { "type": "string" },
          "product_type": { "type": "string" },
          "date": { "type": ["string", "null"], "description": "YYYY-MM-DD HH:MM:SS, UTC. A date that is not set is null." },
          "status": { "type": "string", "description": "`1` delivered, `2` failed, `4` being prepared." },
          "esim": { "type": "object", "additionalProperties": true },
          "data": { "type": "string" },
          "day": { "type": "string" },
          "is_unlimited": { "type": ["string", "boolean"] },
          "country_code": { "type": "string" },
          "country_image": { "type": "string" },
          "country_name": { "type": "string" },
          "fair_use_text": { "type": "string" },
          "coupon_code": { "type": "string" }
        }
      },
      "Usage": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "iccid": { "type": "string" },
          "sale_id": { "type": "integer" },
          "title": { "type": "string" },
          "status": { "type": "string", "examples": ["not_active", "active", "depleted", "expired"] },
          "tone": { "type": "string" },
          "total_mb": { "type": "integer" },
          "used_mb": { "type": "integer" },
          "remaining_mb": { "type": "integer" },
          "percent_remaining": { "type": "number" },
          "is_unlimited": { "type": "boolean" },
          "fair_use_text": { "type": "string" },
          "activated_at": { "type": ["string", "null"] },
          "expires_at": { "type": ["string", "null"] },
          "days_left": { "type": ["integer", "null"] },
          "fetched_at": { "type": "string" }
        }
      },
      "LedgerItem": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "id": { "type": "string" },
          "type": { "type": "string", "description": "topup, gift, spend, refund, adjust, expire, debt, profit_share." },
          "bucket": { "type": "string", "enum": ["cash", "promo"] },
          "amount": { "type": ["string", "number"], "description": "Negative for a purchase." },
          "balance_after": { "type": ["string", "number"] },
          "expires_at": { "type": ["string", "null"] },
          "note": { "type": "string" },
          "date": { "type": "string" }
        }
      },
      "WebhookSubscription": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "id": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "secret_mask": { "type": "string", "description": "Masked signing secret; the secret itself is never returned." },
          "events_list": { "type": "array", "items": { "type": "string" } },
          "status": { "type": "string" },
          "fail_count": { "type": "string" },
          "date": { "type": "string" },
          "updated_at": { "type": "string" }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "id": { "type": "string" },
          "subscription_id": { "type": "string" },
          "event_id": { "type": "string", "description": "Deduplicate on this. Sandbox event ids start with sbx_." },
          "event_type": { "type": "string" },
          "sale_id": { "type": "string", "description": "0 for test events and for sandbox orders." },
          "status": { "type": "string" },
          "attempts": { "type": "string" },
          "max_attempts": { "type": "string" },
          "http_code": { "type": "string" },
          "duration_ms": { "type": "string" },
          "response_body": { "type": "string" },
          "error": { "type": "string" },
          "sent_at": { "type": ["string", "null"] },
          "date": { "type": "string" }
        }
      }
    }
  }
}
