{
  "openapi": "3.1.0",
  "info": {
    "title": "Godwit SIM API",
    "version": "0.1.0",
    "description": "The API behind Godwit SIM, a prepaid travel data eSIM store.\n\nAI agents buy without an account: GET /plans needs no key, POST /checkout with no key returns an order secret (osk_...) once and, on the first order, a buyer key (agsk_...) once, and GET /orders/{id} with the order secret returns the eSIM and a travelerMessage to forward. The same flow is an MCP server at /mcp.\n\nOperations that take a phone without an order secret are the tool API of Godwit SIM's own AI assistant and need its private key.\n\nEvery money amount is an integer in the currency's minor unit and is the total the buyer pays.\n\nAgent guide: https://godwitsim.com/agents.md. API quickstart: https://godwitsim.com/developers.md. Plans and prices: https://godwitsim.com/plans.md."
  },
  "servers": [
    {
      "url": "https://api.godwitsim.com"
    }
  ],
  "security": [
    {
      "toolKey": []
    }
  ],
  "tags": [
    {
      "name": "tools",
      "description": "Called by the Lorikeet concierge as API tools."
    },
    {
      "name": "links"
    },
    {
      "name": "agents",
      "description": "Called by AI agents buying for a traveller, with an order secret or buyer key instead of an account."
    }
  ],
  "paths": {
    "/plans": {
      "get": {
        "operationId": "listPlans",
        "tags": [
          "tools",
          "agents"
        ],
        "security": [
          {
            "toolKey": []
          },
          {}
        ],
        "summary": "Plans for a destination, priced for the customer's market",
        "description": "With the tool key, phone is required and the response carries the\nnumber's gift, device on file and onUs. Without a key, anyone may price\nplans by phone or by market (or currency); the response then has no\ngift, deviceOnFile or onUs, and is limited to 10 requests a minute per\naddress. A key that is not the tool key gets 401. Without phone,\nmarket or currency, prices are in US dollars.\n",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": true,
            "description": "ISO 3166-1 alpha-2 code of the destination, for example JP.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}$"
            }
          },
          {
            "name": "phone",
            "in": "query",
            "required": false,
            "description": "The customer's number. Sets the currency (+61 AUD, +44 GBP, +1 USD) and the California surcharge. Required with the tool key, optional without.",
            "schema": {
              "$ref": "#/components/schemas/Phone"
            }
          },
          {
            "name": "market",
            "in": "query",
            "description": "Without a key and without phone. AU (AUD), UK (GBP) or US (USD, the default).",
            "schema": {
              "$ref": "#/components/schemas/Market"
            }
          },
          {
            "name": "currency",
            "in": "query",
            "description": "Without a key and without phone, instead of market.",
            "schema": {
              "$ref": "#/components/schemas/Currency"
            }
          },
          {
            "name": "billingState",
            "in": "query",
            "description": "Without a key and without phone. A US billing state of CA adds the California surcharge.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Plans, cheapest first within each data size.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/PlanList"
                    },
                    {
                      "$ref": "#/components/schemas/PublicPlanList"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/checkout": {
      "post": {
        "operationId": "createCheckout",
        "tags": [
          "tools",
          "agents"
        ],
        "security": [
          {
            "toolKey": []
          },
          {
            "buyerKey": []
          },
          {}
        ],
        "summary": "Create a Stripe Checkout for a plan and return a short payment link",
        "description": "Two callers. The concierge sends the tool key and CheckoutRequest. An\nagent sends AgentCheckoutRequest with no key, or with its buyer key to\ngroup the order with its others. An agent order takes no phone and\ntexts nothing: the response holds orderSecret once, and buyerKey once\nwhen no buyer key was sent. Agent checkouts are limited to 10 a minute\nper buyer key and per address. An unknown buyer key gets 401\ninvalid_buyer_key; never retry it as a new buyer.\n\nSend an Idempotency-Key header (or idempotencyKey in the body, 1 to\n200 printable ASCII characters; without a buyer key, a random value\nof at least 22 letters, digits, - or _, such as a UUID) to make\nretries safe. For 24 hours\nthe same key from the same buyer key, or without one from the same\naddress, returns the first response again with 200 and\nIdempotent-Replayed: true, including its orderSecret and buyerKey,\nwhich Godwit keeps encrypted for that window. The same key with a\ndifferent request gets 409 idempotency_conflict; while the first\nrequest is still running, 409 idempotency_in_progress.\n\nIf the number holds a comp gift that covers the plan, no Checkout is\ncreated: the order is paid by the gift, the eSIM is bought and the\ninstall text sent before the response returns, and the response has\ngifted true and a message to relay instead of checkoutUrl.\n\nWith channel voice, the backend texts checkoutUrl to the phone number\nby SMS and reports it in delivery. A failed text does not fail the\ncheckout.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/CheckoutRequest"
                  },
                  {
                    "$ref": "#/components/schemas/AgentCheckoutRequest"
                  }
                ]
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Agent checkouts only. Replays the first response for 24 hours.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An agent retry with the same Idempotency-Key. The first response again.",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "const": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentCheckout"
                }
              }
            }
          },
          "201": {
            "description": "Checkout created (send checkoutUrl as plain text), or the order was a gift (relay message). An agent gets AgentCheckout.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/Checkout"
                    },
                    {
                      "$ref": "#/components/schemas/GiftedCheckout"
                    },
                    {
                      "$ref": "#/components/schemas/AgentCheckout"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "idempotency_conflict or idempotency_in_progress (agent checkouts with an Idempotency-Key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/orders/by-phone": {
      "get": {
        "operationId": "listOrdersByPhone",
        "tags": [
          "tools"
        ],
        "summary": "Orders placed from a phone number, newest first",
        "parameters": [
          {
            "name": "phone",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Phone"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Orders. Never includes installation details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/orders": {
      "get": {
        "operationId": "listAgentOrders",
        "tags": [
          "agents"
        ],
        "security": [
          {
            "buyerKey": []
          }
        ],
        "summary": "The buyer key's orders, newest first, up to 200",
        "description": "Limited, with GET /orders/{id}, to 60 reads a minute per key and per address.",
        "responses": {
          "200": {
            "description": "Orders. Status only; read each with GET /orders/{id} for the eSIM.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentOrderList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/orders/{id}": {
      "get": {
        "operationId": "getAgentOrder",
        "tags": [
          "agents"
        ],
        "security": [
          {
            "orderSecret": []
          },
          {
            "buyerKey": []
          },
          {}
        ],
        "summary": "An agent order. Status only without a key; the eSIM with its order secret or buyer key",
        "description": "Poll every few seconds after payment until status is delivered. With\nthe order secret or the buyer key that bought it, the response has\naccess full, the eSIM (install links, SM-DP+ address, activation code,\nQR payload), usage, top-up options and travelerMessage: plain text\nunder 600 characters in the words Godwit texts its own customers.\nForward it as is. Any other key gets 401 invalid_order_secret or\ninvalid_buyer_key, never a status-only answer. Orders placed through\nthe concierge are not found here. Reads are limited to 60 a minute\nper key and per address.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9-]{1,64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The order.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AgentOrderStatus"
                    },
                    {
                      "$ref": "#/components/schemas/AgentOrder"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/orders/{id}/claim": {
      "post": {
        "operationId": "claimAgentOrder",
        "tags": [
          "agents"
        ],
        "security": [],
        "summary": "Attach the traveller's phone to an agent order",
        "description": "Takes the order secret in the body. Attaches the phone to the order and\nits eSIM and texts the install details to it on the channel; before\ndelivery, delivery texts them. Godwit's text support, resends and\nrefunds by phone then work for the eSIM. Repeating a claim for the\nsame phone and channel sends nothing new. A claim for another phone\nmoves the order to it, at most 3 phones per order (409\nclaim_limit_reached). Limited to 10 a minute per address and 5 an\nhour per order. Only +61, +44, and US and Canadian +1 numbers. A claim\ndoes not apply a Godwit gift held by the number.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9-]{1,64}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClaimRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Claimed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "order_closed (the order expired, was refunded or credited, or is disputed) or claim_limit_reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/orders/{id}/topup": {
      "post": {
        "operationId": "createAgentTopup",
        "tags": [
          "agents"
        ],
        "security": [
          {
            "orderSecret": []
          },
          {
            "buyerKey": []
          }
        ],
        "summary": "Pay for more data on a delivered agent eSIM",
        "description": "Send the eSIM order's secret or the buyer key that bought it, and a\npackageId from the order's topupOptions. Returns a Stripe Checkout\nlike an agent checkout, for a new top-up order. No new key is issued:\nthe eSIM order's secret and the buyer key open the top-up order too.\nThe data goes onto the same eSIM after payment; nothing is texted and\nthere is nothing to install. Counts against the checkout limit of 10\na minute per buyer key and per address.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The eSIM order's id.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9-]{1,64}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentTopupRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Top-up checkout created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentTopup"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "topup_unavailable (the order is not a delivered eSIM order, or Airalo does not offer that package for it now).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhook": {
      "get": {
        "operationId": "getAgentWebhook",
        "tags": [
          "agents"
        ],
        "security": [
          {
            "buyerKey": []
          }
        ],
        "summary": "The buyer key's low-data callback",
        "responses": {
          "200": {
            "description": "The callback, or url null.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentWebhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "put": {
        "operationId": "putAgentWebhook",
        "tags": [
          "agents"
        ],
        "security": [
          {
            "buyerKey": []
          }
        ],
        "summary": "Register one low-data callback for the buyer key",
        "description": "Godwit posts esim.low_data when an eSIM the buyer bought has used 75% or\n90% of its data: JSON with type, eventId, orderId, iccid, level and\nremainingPercent, never a key. eventId is stable for the event: drop\na post whose eventId you have seen. Each post carries\nGodwit-Signature t=<unix seconds>,v1=<hex HMAC-SHA256 of signingSecret\nover t + \".\" + raw body>. Reject a t more than 5 minutes from your\nclock: the signature alone does not stop a replay. One attempt, 5 seconds, redirects not\nfollowed. The URL must be public HTTPS on port 443. signingSecret is\nreturned once; registering again replaces the URL and the secret.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentWebhookRegistered"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "delete": {
        "operationId": "deleteAgentWebhook",
        "tags": [
          "agents"
        ],
        "security": [
          {
            "buyerKey": []
          }
        ],
        "summary": "Remove the buyer key's callback",
        "responses": {
          "200": {
            "description": "Removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "deleted"
                  ],
                  "properties": {
                    "deleted": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/esims/{iccid}/usage": {
      "get": {
        "operationId": "getUsage",
        "tags": [
          "tools"
        ],
        "summary": "Data left, validity and top-up options for an eSIM",
        "parameters": [
          {
            "$ref": "#/components/parameters/Iccid"
          },
          {
            "name": "phone",
            "in": "query",
            "required": true,
            "description": "The requester's number. Must be the number on file.",
            "schema": {
              "$ref": "#/components/schemas/Phone"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Usage as Airalo reports it. Airalo caches usage for up to 20 minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/esims/{iccid}/verification-code": {
      "post": {
        "operationId": "issueVerificationCode",
        "tags": [
          "tools"
        ],
        "summary": "Text a one-time code to the number on file",
        "description": "Needed before /resend when the request comes from another number or\nchannel. The code expires after 10 minutes and allows 5 attempts. At\nmost 3 codes are issued per eSIM in 24 hours.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Iccid"
          }
        ],
        "responses": {
          "202": {
            "description": "Code sent by SMS to the number on file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationCodeSent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/esims/{iccid}/resend": {
      "post": {
        "operationId": "resendInstall",
        "tags": [
          "tools"
        ],
        "summary": "Resend the installation message",
        "description": "From the number and channel on file, resends at once. From anywhere\nelse, needs a code from /verification-code and then sends to the\nrequester.\nWith channel voice the details go by SMS to the phone number, and a\ncall from the number on file counts as SMS on file.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Iccid"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResendRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Installation message sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "verification_required (issue a code first), invalid_code (with\nattemptsRemaining), code_expired or too_many_attempts.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "order_closed. The order was refunded or disputed, so its eSIM is not resent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/esims/{iccid}/topup": {
      "post": {
        "operationId": "createTopup",
        "tags": [
          "tools"
        ],
        "summary": "Create a Stripe Checkout for a top-up package",
        "description": "With channel voice, the backend texts checkoutUrl by SMS and reports it in delivery, as /checkout does.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Iccid"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TopupRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkout"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          }
        }
      }
    },
    "/orders/{id}/refund": {
      "post": {
        "operationId": "refundOrder",
        "tags": [
          "tools",
          "agents"
        ],
        "security": [
          {
            "toolKey": []
          },
          {
            "orderSecret": []
          },
          {
            "buyerKey": []
          }
        ],
        "summary": "Refund or credit an order under the refund policy",
        "description": "The concierge sends the tool key and RefundRequest with the order's\nphone. An agent sends its order secret or buyer key and\nAgentRefundRequest. Agent orders follow the same policy and budget but\nget money only, never Godwit credit: where the policy gives credit the\nanswer is 409 credit_only. The concierge cannot refund an agent order\nuntil a phone claims it. Agent refunds are limited to 5 an hour per\norder.\n\nThe backend decides, not the caller:\nnot installed gives a full refund; a fault Airalo has verified gives a\nfull refund; after installation the customer gets a credit. A fault\nreason after installation files a request with Airalo and returns\npending_provider_review; the backend refunds when Airalo approves.\nRefunds and credits count against a weekly budget.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9-]{1,64}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/RefundRequest"
                  },
                  {
                    "$ref": "#/components/schemas/AgentRefundRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision taken and carried out.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RefundDecision"
                }
              }
            }
          },
          "202": {
            "description": "Fault reported to Airalo; no money moved yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RefundDecision"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "refund_budget_exceeded (escalate to a person), order_not_refundable, order_provisioning (retry in a minute), refund_in_progress, or credit_only (agent orders).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/gifts/by-phone": {
      "get": {
        "operationId": "getGiftByPhone",
        "tags": [
          "tools"
        ],
        "summary": "Whether a number has a gift waiting, and only what the customer may be told",
        "description": "Returns the unredeemed, unexpired gift the number's next order would\nuse. The backend decides eligibility at checkout; a plan above the\ngift's value is paid for as normal.\n",
        "parameters": [
          {
            "name": "phone",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Phone"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The gift, or hasGift false.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentGift"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/r/{code}": {
      "get": {
        "operationId": "followShortLink",
        "tags": [
          "links"
        ],
        "security": [],
        "summary": "Short-link redirector for payment and install links",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the Stripe Checkout or eSIM install URL.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "404": {
            "description": "Unknown code.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "mcp",
        "tags": [
          "agents"
        ],
        "security": [],
        "summary": "MCP server (streamable HTTP, stateless)",
        "description": "The agent API as Model Context Protocol tools. Add\n{\"mcpServers\":{\"godwit-sim\":{\"url\":\"https://api.godwitsim.com/mcp\"}}} to\nan MCP client. No headers or sign-up. Tools that read or change an\norder take orderSecret or buyerKey as arguments, because MCP carries\nno Authorization header. WAVE_API_KEY is never accepted here.\n\nTools: searchDestinations, listPlans, getPlan, listRecommendations,\ncreateOrder, getOrder, listOrders, getUsage, listTopupPackages,\ncreateTopup, claimOrder and requestRefund. Their input and output\nschemas are built from this document when the server starts: the\nrequest bodies and parameters of the matching routes, plus\nDestinationSearch, PlanRequest and RecommendationRequest.\n\nStateless JSON responses: no session, no server-sent event stream, so\nGET and DELETE get 405. Bodies are limited to 64 KiB. Limits are per\naddress on each instance: 120 requests and 60 tool calls a minute,\nplus each tool's REST limit (shared with REST).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "A JSON-RPC 2.0 message."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "A notification or response was accepted."
          },
          "400": {
            "description": "Not a valid MCP message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "406": {
            "description": "Accept must allow application/json and text/event-stream.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "413": {
            "description": "The body is over 64 KiB.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "mcpStream",
        "tags": [
          "agents"
        ],
        "security": [],
        "summary": "Not offered; the server sends no server-initiated messages",
        "responses": {
          "405": {
            "description": "Method not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/mcp/server-card.json": {
      "get": {
        "operationId": "mcpServerCard",
        "tags": [
          "agents"
        ],
        "security": [],
        "summary": "Describes the MCP server, its tools and auth model, for discovery",
        "responses": {
          "200": {
            "description": "The server card.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpServerCard"
                }
              }
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "health",
        "security": [],
        "summary": "Liveness check",
        "responses": {
          "200": {
            "description": "Up.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "toolKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "WAVE_API_KEY, set as a header on each Lorikeet API tool."
      },
      "orderSecret": {
        "type": "http",
        "scheme": "bearer",
        "description": "An order secret (osk_...), returned once by an agent checkout. Opens that order only."
      },
      "buyerKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A buyer key (agsk_...), returned once by an agent's first checkout. Opens every order it bought, lists them and manages the webhook."
      }
    },
    "parameters": {
      "Iccid": {
        "name": "iccid",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[0-9]{18,22}F?$"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid input.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or wrong credentials. Agents see buyer_key_required, invalid_buyer_key or invalid_order_secret.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found, or not on file for this phone number.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "The request conflicts with the current state.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unprocessable": {
        "description": "unsupported_market (only +61, +44 and +1 numbers can buy) or plan_unavailable.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "rate_limited. Wait for the Retry-After header's seconds.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Too many codes issued.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Phone": {
        "type": "string",
        "pattern": "^\\+[1-9][0-9]{7,14}$",
        "examples": [
          "+61412345678"
        ]
      },
      "Currency": {
        "type": "string",
        "enum": [
          "AUD",
          "USD",
          "GBP"
        ]
      },
      "Market": {
        "type": "string",
        "enum": [
          "AU",
          "UK",
          "US"
        ],
        "description": "AU prices in AUD, UK in GBP, US in USD."
      },
      "Channel": {
        "type": "string",
        "enum": [
          "sms",
          "whatsapp",
          "voice"
        ],
        "description": "The channel the customer is using. A phone call cannot carry a link, so\non voice every text for the request (payment link, install details) goes\nby SMS to the phone number in the request.\n"
      },
      "LinkDelivery": {
        "type": "object",
        "required": [
          "channel",
          "status"
        ],
        "description": "Present only when channel was voice. The payment link was texted to the caller.",
        "properties": {
          "channel": {
            "const": "sms"
          },
          "status": {
            "type": "string",
            "enum": [
              "sent",
              "failed"
            ],
            "description": "failed means the text did not go out. checkoutUrl is still valid; do not read it aloud."
          }
        }
      },
      "Money": {
        "type": "object",
        "required": [
          "currency",
          "amountMinor",
          "display"
        ],
        "properties": {
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "amountMinor": {
            "type": "integer",
            "minimum": 0
          },
          "display": {
            "type": "string",
            "examples": [
              "A$14.00"
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "attemptsRemaining": {
            "type": "integer"
          },
          "retryAfterSeconds": {
            "type": "integer"
          }
        }
      },
      "Plan": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PublicPlan"
          },
          {
            "type": "object",
            "required": [
              "onUs"
            ],
            "properties": {
              "onUs": {
                "type": "boolean",
                "description": "True when the number's comp gift covers this plan",
                "so create_checkout returns gifted instead of a payment link.": null
              }
            }
          }
        ]
      },
      "PublicPlanList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "countryCode",
          "currency",
          "plans"
        ],
        "description": "Prices without the tool key. No gift or device, which belong to a number's owner.",
        "properties": {
          "countryCode": {
            "type": "string"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "plans": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/PublicPlan"
                },
                {
                  "not": {
                    "required": [
                      "onUs"
                    ]
                  }
                }
              ]
            }
          }
        }
      },
      "PublicPlan": {
        "type": "object",
        "required": [
          "planId",
          "countryCode",
          "country",
          "dataMb",
          "dataLabel",
          "days",
          "price",
          "surcharge",
          "total",
          "taxNote"
        ],
        "properties": {
          "planId": {
            "type": "string",
            "description": "Airalo package id."
          },
          "countryCode": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "dataMb": {
            "type": "integer"
          },
          "dataLabel": {
            "type": "string",
            "examples": [
              "3 GB"
            ]
          },
          "days": {
            "type": "integer"
          },
          "price": {
            "$ref": "#/components/schemas/Money"
          },
          "surcharge": {
            "description": "California 911/988 surcharge, charged as a separate line. Null outside California.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Money"
              },
              {
                "type": "null"
              }
            ]
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          },
          "taxNote": {
            "type": "string",
            "examples": [
              "Includes GST"
            ]
          }
        }
      },
      "PlanList": {
        "type": "object",
        "required": [
          "countryCode",
          "currency",
          "gift",
          "deviceOnFile",
          "plans"
        ],
        "properties": {
          "countryCode": {
            "type": "string"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "gift": {
            "description": "The unredeemed, unexpired gift the number holds, or null. The same gift get_gift returns.",
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "mode",
                  "recipientName",
                  "capUsdCents"
                ],
                "properties": {
                  "mode": {
                    "$ref": "#/components/schemas/GiftMode"
                  },
                  "recipientName": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The name staff gave for the customer."
                  },
                  "capUsdCents": {
                    "type": "integer",
                    "description": "The most the gift covers",
                    "against the plan's USD price. Use onUs on each plan rather than comparing prices.": null
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "deviceOnFile": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "ios",
              "android",
              null
            ],
            "description": "The phone the customer last bought for, or null if the number has never given one."
          },
          "plans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Plan"
            }
          }
        }
      },
      "CheckoutRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "phone",
          "planId",
          "ticketId",
          "channel"
        ],
        "properties": {
          "phone": {
            "$ref": "#/components/schemas/Phone"
          },
          "planId": {
            "type": "string"
          },
          "ticketId": {
            "type": "string",
            "description": "The Lorikeet conversation the customer is in."
          },
          "channel": {
            "$ref": "#/components/schemas/Channel"
          },
          "device": {
            "type": "string",
            "enum": [
              "ios",
              "android"
            ],
            "description": "Omit if unknown; the install message then carries both links."
          }
        }
      },
      "AgentCheckoutRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "planId"
        ],
        "description": "An agent's checkout. No phone, ticket or channel; attach a phone later with POST /orders/{id}/claim.",
        "properties": {
          "planId": {
            "type": "string",
            "description": "A planId from GET /plans."
          },
          "market": {
            "$ref": "#/components/schemas/Market"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "billingState": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$",
            "description": "Two-letter US state of the billing address. CA adds the California surcharge to US-dollar orders."
          },
          "device": {
            "type": "string",
            "enum": [
              "ios",
              "android"
            ],
            "description": "Omit if unknown; travelerMessage then carries both links."
          },
          "agentHint": {
            "type": "string",
            "maxLength": 200,
            "description": "Which agent, and for whom (for example \"TripBot for a corporate travel desk\"). Stored for attribution; never sent to the traveller."
          },
          "idempotencyKey": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "The Idempotency-Key header",
            "for clients that cannot set headers.": null
          }
        }
      },
      "TopupRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "phone",
          "packageId",
          "ticketId",
          "channel"
        ],
        "properties": {
          "phone": {
            "$ref": "#/components/schemas/Phone"
          },
          "packageId": {
            "type": "string",
            "description": "A packageId from the usage response's topupOptions."
          },
          "ticketId": {
            "type": "string"
          },
          "channel": {
            "$ref": "#/components/schemas/Channel"
          }
        }
      },
      "LineItem": {
        "type": "object",
        "required": [
          "description",
          "amount"
        ],
        "properties": {
          "description": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Money"
          }
        }
      },
      "Checkout": {
        "type": "object",
        "required": [
          "orderId",
          "gifted",
          "checkoutUrl",
          "total",
          "lineItems",
          "expiresAt"
        ],
        "properties": {
          "orderId": {
            "type": "string"
          },
          "gifted": {
            "const": false
          },
          "checkoutUrl": {
            "type": "string",
            "format": "uri",
            "description": "Short link that redirects to Stripe Checkout."
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "delivery": {
            "$ref": "#/components/schemas/LinkDelivery"
          }
        }
      },
      "AgentCheckout": {
        "type": "object",
        "required": [
          "orderId",
          "gifted",
          "checkoutUrl",
          "total",
          "lineItems",
          "expiresAt",
          "orderUrl",
          "orderSecret",
          "buyerKey",
          "nextStep"
        ],
        "properties": {
          "orderId": {
            "type": "string"
          },
          "gifted": {
            "const": false
          },
          "checkoutUrl": {
            "type": "string",
            "format": "uri",
            "description": "Open in a browser and pay by card or Link."
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "orderUrl": {
            "type": "string",
            "format": "uri",
            "description": "GET this with the order secret or buyer key. Not a credential."
          },
          "orderSecret": {
            "type": "string",
            "pattern": "^osk_[A-Za-z0-9_-]{43}$",
            "description": "Shown once. Opens this order. Never send it to the traveller."
          },
          "buyerKey": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^agsk_[A-Za-z0-9_-]{43}$",
            "description": "Shown once, on the first checkout without a buyer key; otherwise null. Never send it to the traveller."
          },
          "nextStep": {
            "type": "string"
          }
        }
      },
      "GiftedCheckout": {
        "type": "object",
        "required": [
          "orderId",
          "gifted",
          "total",
          "message"
        ],
        "properties": {
          "orderId": {
            "type": "string"
          },
          "gifted": {
            "const": true
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          },
          "message": {
            "type": "string",
            "description": "Tell the customer this. Do not send a payment link."
          }
        }
      },
      "GiftCohort": {
        "type": "string",
        "enum": [
          "sales",
          "recruiting",
          "investor",
          "staff"
        ]
      },
      "GiftMode": {
        "type": "string",
        "enum": [
          "comp",
          "pay_and_refund"
        ],
        "description": "comp skips payment at checkout; pay_and_refund takes payment and refunds it once the eSIM is delivered."
      },
      "AgentGift": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "hasGift",
          "gift"
        ],
        "properties": {
          "hasGift": {
            "type": "boolean"
          },
          "gift": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "mode",
                  "cohort",
                  "recipientName",
                  "expiresAt"
                ],
                "properties": {
                  "mode": {
                    "$ref": "#/components/schemas/GiftMode"
                  },
                  "cohort": {
                    "$ref": "#/components/schemas/GiftCohort"
                  },
                  "recipientName": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The name staff gave for the customer. Greet them by it."
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "OrderStatus": {
        "type": "string",
        "enum": [
          "pending_payment",
          "expired",
          "paid",
          "held",
          "provisioned",
          "delivered",
          "provider_review",
          "refunded",
          "credited",
          "disputed"
        ]
      },
      "OrderSummary": {
        "type": "object",
        "required": [
          "orderId",
          "kind",
          "status",
          "planId",
          "planName",
          "iccid",
          "total",
          "createdAt"
        ],
        "properties": {
          "orderId": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "esim",
              "topup"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "planId": {
            "type": "string"
          },
          "planName": {
            "type": "string"
          },
          "iccid": {
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OrderList": {
        "type": "object",
        "required": [
          "orders"
        ],
        "properties": {
          "orders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderSummary"
            }
          }
        }
      },
      "AgentOrderStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "access",
          "orderId",
          "kind",
          "status",
          "planName",
          "total",
          "createdAt",
          "nextStep"
        ],
        "properties": {
          "access": {
            "const": "statusOnly"
          },
          "orderId": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "esim",
              "topup"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "planName": {
            "type": "string"
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "nextStep": {
            "type": "string"
          }
        }
      },
      "AgentOrder": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "access",
          "orderId",
          "kind",
          "status",
          "planName",
          "total",
          "createdAt",
          "nextStep",
          "claimedBy",
          "esim",
          "travelerMessage",
          "usage",
          "topupOptions"
        ],
        "properties": {
          "access": {
            "const": "full"
          },
          "orderId": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "esim",
              "topup"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "planName": {
            "type": "string"
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "nextStep": {
            "type": "string"
          },
          "claimedBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "The masked phone that claimed the order",
            "or null.": null
          },
          "esim": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AgentEsim"
              },
              {
                "type": "null"
              }
            ]
          },
          "travelerMessage": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 599,
            "description": "Forward as is. Never holds a key."
          },
          "usage": {
            "description": "Null before delivery or while Airalo has no reading. Refreshed at most once a minute.",
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "status",
                  "totalMb",
                  "remainingMb",
                  "usedMb",
                  "expiresAt",
                  "isUnlimited"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "not_active",
                      "in_use",
                      "expired",
                      "revoked",
                      "unknown"
                    ]
                  },
                  "totalMb": {
                    "type": "integer"
                  },
                  "remainingMb": {
                    "type": "integer"
                  },
                  "usedMb": {
                    "type": "integer"
                  },
                  "expiresAt": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "isUnlimited": {
                    "type": "boolean"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "topupOptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TopupOption"
            }
          }
        }
      },
      "AgentEsim": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "iccid",
          "appleInstallUrl",
          "androidInstallUrl",
          "smdpAddress",
          "activationCode",
          "confirmationCode",
          "qrPayload",
          "apn"
        ],
        "properties": {
          "iccid": {
            "type": "string"
          },
          "appleInstallUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "One-tap install on iOS 17.4 and later."
          },
          "androidInstallUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "smdpAddress": {
            "type": "string"
          },
          "activationCode": {
            "type": "string"
          },
          "confirmationCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "qrPayload": {
            "type": "string",
            "description": "The LPA string a QR code encodes."
          },
          "apn": {
            "type": "object",
            "required": [
              "ios",
              "android",
              "any"
            ],
            "properties": {
              "ios": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "android": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "any": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "AgentOrderList": {
        "type": "object",
        "required": [
          "orders"
        ],
        "properties": {
          "orders": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "orderId",
                "kind",
                "status",
                "planName",
                "total",
                "createdAt",
                "claimed"
              ],
              "properties": {
                "orderId": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "esim",
                    "topup"
                  ]
                },
                "status": {
                  "$ref": "#/components/schemas/OrderStatus"
                },
                "planName": {
                  "type": "string"
                },
                "total": {
                  "$ref": "#/components/schemas/Money"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "claimed": {
                  "type": "boolean"
                }
              }
            }
          }
        }
      },
      "ClaimRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "orderSecret",
          "phone",
          "channel"
        ],
        "properties": {
          "orderSecret": {
            "type": "string",
            "description": "The order's osk_ secret. A buyer key does not claim."
          },
          "phone": {
            "$ref": "#/components/schemas/Phone"
          },
          "channel": {
            "type": "string",
            "enum": [
              "sms",
              "whatsapp"
            ]
          }
        }
      },
      "ClaimResult": {
        "type": "object",
        "required": [
          "orderId",
          "status",
          "claimedBy",
          "channel",
          "installText"
        ],
        "properties": {
          "orderId": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "claimedBy": {
            "type": "string",
            "description": "The phone",
            "masked.": null
          },
          "channel": {
            "type": "string",
            "enum": [
              "sms",
              "whatsapp"
            ]
          },
          "installText": {
            "type": "string",
            "enum": [
              "sent",
              "already_sent",
              "on_delivery"
            ],
            "description": "on_delivery means the order is not delivered yet; the install text goes when it is."
          }
        }
      },
      "AgentWebhook": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "events": {
            "type": "array",
            "items": {
              "const": "esim.low_data"
            }
          }
        }
      },
      "AgentWebhookRegistered": {
        "type": "object",
        "required": [
          "url",
          "signingSecret",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "signingSecret": {
            "type": "string",
            "description": "Shown once. Verify Godwit-Signature with it."
          },
          "events": {
            "type": "array",
            "items": {
              "const": "esim.low_data"
            }
          }
        }
      },
      "AgentRefundRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "reason"
        ],
        "properties": {
          "reason": {
            "$ref": "#/components/schemas/RefundReason"
          },
          "notes": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "AgentTopupRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "packageId"
        ],
        "properties": {
          "packageId": {
            "type": "string",
            "description": "A packageId from the eSIM order's topupOptions."
          }
        }
      },
      "AgentTopup": {
        "type": "object",
        "required": [
          "orderId",
          "gifted",
          "checkoutUrl",
          "total",
          "lineItems",
          "expiresAt",
          "orderUrl",
          "nextStep"
        ],
        "properties": {
          "orderId": {
            "type": "string",
            "description": "The new top-up order. The eSIM order's secret and the buyer key open it."
          },
          "gifted": {
            "const": false
          },
          "checkoutUrl": {
            "type": "string",
            "format": "uri",
            "description": "Open in a browser and pay by card or Link."
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "orderUrl": {
            "type": "string",
            "format": "uri"
          },
          "nextStep": {
            "type": "string"
          }
        }
      },
      "McpServerCard": {
        "type": "object",
        "required": [
          "name",
          "title",
          "description",
          "version",
          "serverUrl",
          "transport",
          "remotes",
          "authentication",
          "tools"
        ],
        "description": "After AgentSIM's card. MCP server cards are a draft (SEP-2127), not part of a released MCP specification.",
        "properties": {
          "name": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "serverUrl": {
            "type": "string",
            "format": "uri"
          },
          "transport": {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "const": "streamable-http"
              }
            }
          },
          "remotes": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "type",
                "url"
              ],
              "properties": {
                "type": {
                  "const": "streamable-http"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "supportedProtocolVersions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "websiteUrl": {
            "type": "string",
            "format": "uri"
          },
          "documentationUrl": {
            "type": "string",
            "format": "uri"
          },
          "llmsTxtUrl": {
            "type": "string",
            "format": "uri"
          },
          "authentication": {
            "type": "object",
            "required": [
              "required",
              "note"
            ],
            "properties": {
              "required": {
                "const": false
              },
              "note": {
                "type": "string"
              }
            }
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "description"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "TopupOption": {
        "type": "object",
        "required": [
          "packageId",
          "dataMb",
          "dataLabel",
          "days",
          "price",
          "surcharge",
          "total"
        ],
        "properties": {
          "packageId": {
            "type": "string"
          },
          "dataMb": {
            "type": "integer"
          },
          "dataLabel": {
            "type": "string"
          },
          "days": {
            "type": "integer"
          },
          "price": {
            "$ref": "#/components/schemas/Money"
          },
          "surcharge": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Money"
              },
              {
                "type": "null"
              }
            ]
          },
          "total": {
            "$ref": "#/components/schemas/Money"
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "iccid",
          "status",
          "totalMb",
          "remainingMb",
          "usedMb",
          "expiresAt",
          "isUnlimited",
          "topupOptions"
        ],
        "properties": {
          "iccid": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "not_active",
              "in_use",
              "expired",
              "revoked",
              "unknown"
            ]
          },
          "totalMb": {
            "type": "integer"
          },
          "remainingMb": {
            "type": "integer"
          },
          "usedMb": {
            "type": "integer"
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "isUnlimited": {
            "type": "boolean"
          },
          "topupOptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TopupOption"
            }
          }
        }
      },
      "VerificationCodeSent": {
        "type": "object",
        "required": [
          "sentTo",
          "expiresAt"
        ],
        "properties": {
          "sentTo": {
            "type": "string",
            "description": "Masked number on file",
            "for example +61•••••678.": null
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ResendRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "phone",
          "channel"
        ],
        "properties": {
          "phone": {
            "$ref": "#/components/schemas/Phone"
          },
          "channel": {
            "$ref": "#/components/schemas/Channel"
          },
          "code": {
            "type": "string",
            "pattern": "^[0-9]{6}$"
          },
          "device": {
            "type": "string",
            "enum": [
              "ios",
              "android"
            ]
          }
        }
      },
      "ResendResult": {
        "type": "object",
        "required": [
          "status",
          "sentTo"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "sent"
            ]
          },
          "sentTo": {
            "type": "string"
          }
        }
      },
      "RefundReason": {
        "type": "string",
        "enum": [
          "change_of_mind",
          "trip_cancelled",
          "installation_failure",
          "no_coverage",
          "incompatible_device",
          "other"
        ]
      },
      "RefundRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "phone",
          "reason"
        ],
        "properties": {
          "phone": {
            "$ref": "#/components/schemas/Phone"
          },
          "reason": {
            "$ref": "#/components/schemas/RefundReason"
          },
          "notes": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "RefundDecision": {
        "type": "object",
        "required": [
          "orderId",
          "outcome",
          "rule",
          "amount"
        ],
        "properties": {
          "orderId": {
            "type": "string"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "refunded",
              "credited",
              "pending_provider_review"
            ]
          },
          "rule": {
            "type": "string",
            "enum": [
              "not_installed",
              "provider_verified_fault",
              "installed_credit_only",
              "fault_reported"
            ]
          },
          "amount": {
            "$ref": "#/components/schemas/Money"
          }
        }
      }
    }
  }
}