{"openapi":"3.1.0","info":{"title":"SimPig API","version":"0.1.0","description":"Travel eSIMs without an app. Every endpoint answers on the host it is called on and takes the Supabase Auth access token as a bearer token (the session endpoints take the HttpOnly cookie instead). Errors use one envelope: `{ error, code, reason, detail, issues }`."},"servers":[{"url":"/","description":"The current host: simpig.com (shop), me.simpig.com (dashboard), auth.simpig.com (sign-in) or admin.simpig.com"}],"tags":[{"name":"Auth","description":"Sessions: email code (default), password or passkey; recovery; the signed-in person’s own security."},{"name":"Catalog","description":"Destinations and plans (public, cached at the edge)."},{"name":"Checkout","description":"Start an order and hand off to Stripe Checkout."},{"name":"Orders","description":"The customer’s orders (receipts) and one order’s progress."},{"name":"eSIMs","description":"The customer’s eSIMs: status, usage, install details, top-ups."},{"name":"Pools","description":"The Pig Pen: a pool of mates on one trip, a discount that grows with them."},{"name":"Admin","description":"Staff (second factor required)."},{"name":"Agent","description":"Buying for a person without a session: pay link, one-time order token, install details. Also at /api/mcp as MCP tools."},{"name":"Webhooks","description":"Stripe and eSIM Access call these; nothing else should."},{"name":"Meta","description":"Service endpoints."}],"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","example":"that plan just sold out of the catalogue. pick another."},"code":{"type":"string","enum":["validation","unauthorized","forbidden","not_found","conflict","rate_limited","server_error"],"example":"validation"},"reason":{"type":"string","example":"PLAN_NOT_AVAILABLE","description":"Database or API message code."},"detail":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"}]}},"message":{"type":"string"}},"required":["path","message"]}}},"required":["error","code","issues"]}},"parameters":{}},"paths":{"/api/v1/auth/otp":{"post":{"tags":["Auth"],"summary":"Email a 6-digit sign-in code","description":"Supabase Auth emails the code; a new address becomes an account. The answer is the same whether the address existed or not.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email"}},"required":["email"]}}}},"responses":{"200":{"description":"Sent (or not: no difference is shown).","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean","enum":[true]}},"required":["sent"]}}}},"403":{"description":"Not from a SimPig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many codes asked for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Supabase Auth unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/otp/verify":{"post":{"tags":["Auth"],"summary":"Sign in with the emailed code","description":"Exchanges the code with Supabase Auth. The refresh token goes into an HttpOnly cookie scoped to /api/v1/auth; the response carries the access token for the page to keep in memory. The customer row is created on first sign-in (api.customers_ensure).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email"},"code":{"type":"string","pattern":"^\\d{6}$"}},"required":["email","code"]}}}},"responses":{"200":{"description":"Signed in.","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"integer"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]}},"required":["id","email"]}},"required":["accessToken","expiresAt","user"]}}}},"401":{"description":"Wrong or expired code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not from a SimPig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Supabase Auth unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/session":{"post":{"tags":["Auth"],"summary":"A fresh access token from the session cookie","description":"Rotates the refresh token with Supabase Auth and sets the new one in the cookie. 401 `NO_SESSION` when there is no usable cookie.","responses":{"200":{"description":"A fresh access token.","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"integer"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]}},"required":["id","email"]}},"required":["accessToken","expiresAt","user"]}}}},"401":{"description":"No session, or the refresh token was refused (cookie cleared).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not from a SimPig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Supabase Auth unreachable (the page keeps its current token).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["Auth"],"summary":"Sign out everywhere","description":"Clears the session cookie and asks Supabase Auth to revoke every refresh token of the user. `everywhere` is false when Supabase could not be told.","parameters":[{"schema":{"type":"string"},"required":false,"name":"authorization","in":"header"}],"responses":{"200":{"description":"Signed out.","content":{"application/json":{"schema":{"type":"object","properties":{"everywhere":{"type":"boolean"}},"required":["everywhere"]}}}},"403":{"description":"Not from a SimPig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/me":{"get":{"tags":["Auth"],"summary":"Who is signed in","description":"api.me() on the shop surface: the customer row, or 403 CUSTOMER_MISSING when sign-in did not finish.","responses":{"200":{"description":"The caller.","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":["string","null"],"format":"uuid"},"email":{"type":["string","null"]},"surface":{"type":["string","null"],"enum":["shop","admin","auth","worker",null]},"customerId":{"type":["string","null"],"format":"uuid"},"displayName":{"type":["string","null"]},"nudgeLowData":{"type":["boolean","null"]},"staffRole":{"type":["string","null"],"enum":["owner","support","finance",null]},"isStaff":{"type":"boolean"}},"required":["userId","email","surface","customerId","displayName","nudgeLowData","staffRole","isStaff"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"No customer row yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Auth"],"summary":"Update display name or the low-data nudge","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"displayName":{"type":"string","maxLength":80},"nudgeLowData":{"type":"boolean"}}}}}},"responses":{"200":{"description":"The caller, updated.","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":["string","null"],"format":"uuid"},"email":{"type":["string","null"]},"surface":{"type":["string","null"],"enum":["shop","admin","auth","worker",null]},"customerId":{"type":["string","null"],"format":"uuid"},"displayName":{"type":["string","null"]},"nudgeLowData":{"type":["boolean","null"]},"staffRole":{"type":["string","null"],"enum":["owner","support","finance",null]},"isStaff":{"type":"boolean"}},"required":["userId","email","surface","customerId","displayName","nudgeLowData","staffRole","isStaff"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/login":{"post":{"tags":["Auth"],"summary":"Sign in with email and password","description":"For people who chose a password (the email code is the default). The refresh token goes into the HttpOnly cookie; the customer row is created on first sign-in.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email"},"password":{"type":"string","minLength":1,"maxLength":1024}},"required":["email","password"]}}}},"responses":{"200":{"description":"Signed in.","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"integer"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]}},"required":["id","email"]}},"required":["accessToken","expiresAt","user"]}}}},"401":{"description":"Wrong email or password.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Account disabled, or not from a simpig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Supabase Auth unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/account":{"post":{"tags":["Auth"],"summary":"Whether an email has an account, and a password","description":"The shop’s checkout asks this to decide what to ask next: a password (an account with one), or nothing (no account, or one an assistant made). Same answer the one-door sign-in would give; same-site only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email"}},"required":["email"]}}}},"responses":{"200":{"description":"The address’s state.","content":{"application/json":{"schema":{"type":"object","properties":{"exists":{"type":"boolean"},"hasPassword":{"type":"boolean"}},"required":["exists","hasPassword"]}}}},"403":{"description":"Not from a SimPig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Malformed email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/enter":{"post":{"tags":["Auth"],"summary":"Sign in, or sign up: one form","description":"Email and password. The password is tried first (`signed_in`). When Supabase refuses it: an address with no account gets one, with one confirmation link emailed (`confirm_email`, the password rules apply); an account without a password yet (one an assistant created) is emailed a link to set one (`set_password_link`); otherwise 401 `INVALID_CREDENTIALS`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email"},"password":{"type":"string","minLength":1,"maxLength":1024}},"required":["email","password"]}}}},"responses":{"200":{"description":"Signed in, or an email is on its way.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["signed_in","confirm_email","set_password_link"]},"session":{"type":["object","null"],"properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"integer"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]}},"required":["id","email"]}},"required":["accessToken","expiresAt","user"]}},"required":["status","session"]}}}},"401":{"description":"Wrong password for an account that has one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Account disabled, or not from a simpig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A new account needs a stronger password.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Supabase Auth unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/signup":{"post":{"tags":["Auth"],"summary":"Create an account with email and password","description":"Supabase emails one confirmation link (it lands on the auth host at /confirmed); the answer is `confirm_email` whether or not the address already had an account. When the project does not require confirmation the answer is `signed_in` with the session.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email"},"password":{"type":"string","maxLength":1024}},"required":["email","password"]}}}},"responses":{"200":{"description":"A confirmation link is on its way, or the session.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["confirm_email","signed_in"]},"session":{"type":["object","null"],"properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"integer"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]}},"required":["id","email"]}},"required":["accessToken","expiresAt","user"]}},"required":["status","session"]}}}},"403":{"description":"Not from a simpig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Password rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many sign-ups.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Supabase Auth unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/forgot-password":{"post":{"tags":["Auth"],"summary":"Send a password recovery email","description":"Always answers 204, whether or not the address has an account. The link lands on the auth host (`/reset-password`).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email"}},"required":["email"]}}}},"responses":{"204":{"description":"If the address has an account, an email is on its way."},"403":{"description":"Not from a simpig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/session/from-link":{"post":{"tags":["Auth"],"summary":"Adopt the session from a recovery link","description":"Supabase's recovery email lands on the auth host with tokens in the URL fragment. The page hands them here once: the access token is verified, the refresh token goes into the cookie, and the fragment is cleared.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string","minLength":20,"maxLength":4096},"refreshToken":{"type":"string","minLength":8,"maxLength":512}},"required":["accessToken","refreshToken"]}}}},"responses":{"200":{"description":"Session adopted.","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"integer"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]}},"required":["id","email"]}},"required":["accessToken","expiresAt","user"]}}}},"401":{"description":"The link has expired or was already used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not from a simpig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Supabase Auth unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/security":{"get":{"tags":["Auth"],"summary":"The signed-in person's ways in","description":"Whether a password is set, whether this session may set one without the current password (it came from an email code or link within the hour), their authenticator apps and passkeys.","responses":{"200":{"description":"Their security status.","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":["string","null"]},"aal":{"type":"string","enum":["aal1","aal2"]},"hasPassword":{"type":"boolean"},"canSetPassword":{"type":"boolean"},"codeRequired":{"type":"boolean"},"factors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"friendlyName":{"type":["string","null"]},"createdAt":{"type":["string","null"]}},"required":["id","type","status","friendlyName","createdAt"]}},"passkeys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"friendlyName":{"type":["string","null"]},"createdAt":{"type":"string"},"lastUsedAt":{"type":["string","null"]}},"required":["id","friendlyName","createdAt","lastUsedAt"]}}},"required":["email","aal","hasPassword","canSetPassword","codeRequired","factors","passkeys"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/password":{"post":{"tags":["Auth"],"summary":"Set a password without the current one","description":"Only a session opened from an email code or recovery link within the last hour may do this (`PASSWORD_LINK_REQUIRED` otherwise). After a recovery link (`endSessions: true`) every session ends, this one included.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"password":{"type":"string","maxLength":1024},"endSessions":{"type":"boolean"}},"required":["password","endSessions"]}}}},"responses":{"204":{"description":"Password set."},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The session did not come from an email code or link, or it is older than an hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Password rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/password/change":{"post":{"tags":["Auth"],"summary":"Change the password, with the current one","description":"Every other session of the person is then signed out; this one stays.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"currentPassword":{"type":"string","minLength":1,"maxLength":1024},"password":{"type":"string","maxLength":1024}},"required":["currentPassword","password"]}}}},"responses":{"204":{"description":"Changed."},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The current password is wrong, the new one is the same, or it is too weak.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/mfa/totp":{"post":{"tags":["Auth"],"summary":"Start adding an authenticator app","description":"Staff need one: the admin surface refuses a session without a second factor. Answers the QR code and the secret; the factor counts once a code is verified.","responses":{"200":{"description":"Scan the QR code, then verify a code.","content":{"application/json":{"schema":{"type":"object","properties":{"factorId":{"type":"string"},"qrCode":{"type":"string"},"secret":{"type":"string"}},"required":["factorId","qrCode","secret"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Too many factors.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/mfa/verify":{"post":{"tags":["Auth"],"summary":"Verify a code from the authenticator app","description":"Finishes adding the app, or raises the session to `aal2`: the new refresh token goes into the cookie and the response carries the new access token.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"factorId":{"type":"string","format":"uuid"},"code":{"type":"string","pattern":"^\\d{6}$"}},"required":["factorId","code"]}}}},"responses":{"200":{"description":"Verified.","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"integer"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]}},"required":["id","email"]}},"required":["accessToken","expiresAt","user"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Wrong or expired code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/mfa/factors/{id}":{"delete":{"tags":["Auth"],"summary":"Remove an authenticator app","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Removed."},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Verify a code first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"No such factor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/passkeys/options":{"post":{"tags":["Auth"],"summary":"Start adding a passkey","description":"WebAuthn creation options for the browser. Passkeys work on the auth host, the relying-party origin configured in Supabase.","responses":{"200":{"description":"Options for navigator.credentials.create.","content":{"application/json":{"schema":{"type":"object","properties":{"challengeId":{"type":"string","format":"uuid"},"options":{"type":"object","additionalProperties":{}}},"required":["challengeId","options"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Passkeys are off, or too many.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/passkeys":{"post":{"tags":["Auth"],"summary":"Finish adding a passkey","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"challengeId":{"type":"string","format":"uuid"},"credential":{"type":"object","additionalProperties":{}}},"required":["challengeId","credential"]}}}},"responses":{"204":{"description":"Added."},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Try again, or the passkey exists already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/passkeys/{id}":{"delete":{"tags":["Auth"],"summary":"Remove a passkey","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Removed."},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"No such passkey.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/passkey/options":{"post":{"tags":["Auth"],"summary":"Sign in with a passkey: the options","description":"No email is asked: the browser offers the passkeys it holds for this site.","responses":{"200":{"description":"Options for navigator.credentials.get.","content":{"application/json":{"schema":{"type":"object","properties":{"challengeId":{"type":"string","format":"uuid"},"options":{"type":"object","additionalProperties":{}}},"required":["challengeId","options"]}}}},"403":{"description":"Not from a simpig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Passkeys are off.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/passkey/sign-in":{"post":{"tags":["Auth"],"summary":"Sign in with a passkey","description":"A passkey with user verification counts as both factors: the session is never asked for a code.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"challengeId":{"type":"string","format":"uuid"},"credential":{"type":"object","additionalProperties":{}}},"required":["challengeId","credential"]}}}},"responses":{"200":{"description":"Signed in.","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"integer"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]}},"required":["id","email"]}},"required":["accessToken","expiresAt","user"]}}}},"401":{"description":"Passkey not recognised.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Account disabled, or not from a simpig page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/catalog/destinations":{"get":{"tags":["Catalog"],"summary":"Every destination with something to sell","responses":{"200":{"description":"Destinations, most popular first.","content":{"application/json":{"schema":{"type":"object","properties":{"destinations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["country","region","global"]},"continent":{"type":["string","null"]},"slug":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"memberCodes":{"type":"array","items":{"type":"string"}},"popularity":{"type":"integer"},"planCount":{"type":"integer"},"fromPriceMinor":{"type":["integer","null"]},"currency":{"type":"string"}},"required":["code","name","kind","continent","slug","aliases","memberCodes","popularity","planCount","fromPriceMinor","currency"]}}},"required":["destinations"]}}}}}}},"/api/v1/catalog/plans/{destination}":{"get":{"tags":["Catalog"],"summary":"The plans for one destination","description":"A country lists its own plans first, then the regional and global plans that cover it (`isRegional`).","parameters":[{"schema":{"type":"string","minLength":2,"maxLength":48,"pattern":"^[A-Za-z0-9_-]+$","example":"japan","description":"A destination slug (japan, bali-indonesia, europe) or code (JP, EU-42)."},"required":true,"description":"A destination slug (japan, bali-indonesia, europe) or code (JP, EU-42).","name":"destination","in":"path"}],"responses":{"200":{"description":"The destination and its plans.","content":{"application/json":{"schema":{"type":"object","properties":{"destination":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["country","region","global"]},"slug":{"type":"string"},"memberCodes":{"type":"array","items":{"type":"string"}},"facts":{"type":["object","null"],"properties":{"language":{"type":"string"},"landmark":{"type":"string"},"attraction":{"type":"string"},"hello":{"type":"string"},"helloSay":{"type":"string"},"insta":{"type":"string"},"instaSay":{"type":"string"},"tip":{"type":"string"},"worth":{"type":"object","properties":{"photos":{"type":"string"},"facetime":{"type":"string"},"video":{"type":"string"},"dms":{"type":"string"},"maps":{"type":"string"},"music":{"type":"string"}},"required":["photos","facetime","video","dms","maps","music"]}},"required":["language","landmark","attraction","hello","helloSay","insta","instaSay","tip"]}},"required":["code","name","kind","slug","memberCodes"]},"plans":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"destinationKind":{"type":"string","enum":["country","region","global"]},"isRegional":{"type":"boolean"},"volumeBytes":{"type":"number"},"durationDays":{"type":"integer"},"dataType":{"type":"integer"},"activeType":{"type":["integer","null"]},"supportsTopup":{"type":"boolean"},"speed":{"type":["string","null"]},"priceMinor":{"type":"integer"},"currency":{"type":"string"},"compareAtMinor":{"type":["integer","null"]},"badge":{"type":["string","null"]},"isFeatured":{"type":"boolean"},"coverageCodes":{"type":"array","items":{"type":"string"}},"operators":{"type":"array","items":{"type":"object","properties":{"name":{"type":["string","null"]},"network":{"type":["string","null"]}},"required":["name","network"]}}},"required":["productId","packageCode","title","subtitle","destinationCode","destinationName","destinationKind","isRegional","volumeBytes","durationDays","dataType","activeType","supportsTopup","speed","priceMinor","currency","compareAtMinor","badge","isFeatured","coverageCodes","operators"]}}},"required":["destination","plans"]}}}},"404":{"description":"No such destination, or nothing to sell there.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Malformed destination.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/catalog/trip":{"get":{"tags":["Catalog"],"summary":"Plans for a trip through several countries","description":"Pass the ISO country codes as `to=JP,KR,TH` (up to 8). `covering` lists every plan whose coverage includes all of them; `singles` lists each country’s own plans so a combination can be built when nothing covers the whole trip. Unknown codes are dropped; 404 when none is a country we sell.","parameters":[{"schema":{"type":"string","minLength":2,"maxLength":24,"example":"JP,KR,TH","description":"Comma-separated ISO 3166-1 alpha-2 codes."},"required":true,"description":"Comma-separated ISO 3166-1 alpha-2 codes.","name":"to","in":"query"}],"responses":{"200":{"description":"The trip’s plans.","content":{"application/json":{"schema":{"type":"object","properties":{"destinations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["code","name","slug"]}},"covering":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"destinationKind":{"type":"string","enum":["country","region","global"]},"isRegional":{"type":"boolean"},"volumeBytes":{"type":"number"},"durationDays":{"type":"integer"},"dataType":{"type":"integer"},"activeType":{"type":["integer","null"]},"supportsTopup":{"type":"boolean"},"speed":{"type":["string","null"]},"priceMinor":{"type":"integer"},"currency":{"type":"string"},"compareAtMinor":{"type":["integer","null"]},"badge":{"type":["string","null"]},"isFeatured":{"type":"boolean"},"coverageCodes":{"type":"array","items":{"type":"string"}},"operators":{"type":"array","items":{"type":"object","properties":{"name":{"type":["string","null"]},"network":{"type":["string","null"]}},"required":["name","network"]}}},"required":["productId","packageCode","title","subtitle","destinationCode","destinationName","destinationKind","isRegional","volumeBytes","durationDays","dataType","activeType","supportsTopup","speed","priceMinor","currency","compareAtMinor","badge","isFeatured","coverageCodes","operators"]}},"singles":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"plans":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"destinationKind":{"type":"string","enum":["country","region","global"]},"isRegional":{"type":"boolean"},"volumeBytes":{"type":"number"},"durationDays":{"type":"integer"},"dataType":{"type":"integer"},"activeType":{"type":["integer","null"]},"supportsTopup":{"type":"boolean"},"speed":{"type":["string","null"]},"priceMinor":{"type":"integer"},"currency":{"type":"string"},"compareAtMinor":{"type":["integer","null"]},"badge":{"type":["string","null"]},"isFeatured":{"type":"boolean"},"coverageCodes":{"type":"array","items":{"type":"string"}},"operators":{"type":"array","items":{"type":"object","properties":{"name":{"type":["string","null"]},"network":{"type":["string","null"]}},"required":["name","network"]}}},"required":["productId","packageCode","title","subtitle","destinationCode","destinationName","destinationKind","isRegional","volumeBytes","durationDays","dataType","activeType","supportsTopup","speed","priceMinor","currency","compareAtMinor","badge","isFeatured","coverageCodes","operators"]}}},"required":["code","plans"]}}},"required":["destinations","covering","singles"]}}}},"404":{"description":"None of the codes is a country on sale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Malformed list of codes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/catalog/plan/{productId}":{"get":{"tags":["Catalog"],"summary":"One plan","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"productId","in":"path"}],"responses":{"200":{"description":"The plan.","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"destinationKind":{"type":"string","enum":["country","region","global"]},"isRegional":{"type":"boolean"},"volumeBytes":{"type":"number"},"durationDays":{"type":"integer"},"dataType":{"type":"integer"},"activeType":{"type":["integer","null"]},"supportsTopup":{"type":"boolean"},"speed":{"type":["string","null"]},"priceMinor":{"type":"integer"},"currency":{"type":"string"},"compareAtMinor":{"type":["integer","null"]},"badge":{"type":["string","null"]},"isFeatured":{"type":"boolean"},"coverageCodes":{"type":"array","items":{"type":"string"}},"operators":{"type":"array","items":{"type":"object","properties":{"name":{"type":["string","null"]},"network":{"type":["string","null"]}},"required":["name","network"]}}},"required":["productId","packageCode","title","subtitle","destinationCode","destinationName","destinationKind","isRegional","volumeBytes","durationDays","dataType","activeType","supportsTopup","speed","priceMinor","currency","compareAtMinor","badge","isFeatured","coverageCodes","operators"]}},"required":["plan"]}}}},"404":{"description":"No such plan, or it is not on sale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Malformed id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/pulse":{"get":{"tags":["Catalog"],"summary":"The shop’s pulse","description":"Live, aggregate numbers: eSIMs live now and in which countries, data gobbled all-time, top destinations, the latest activations at country level, eSIMs live per hour over the last day. Refreshed every 30 seconds.","responses":{"200":{"description":"The pulse.","content":{"application/json":{"schema":{"type":"object","properties":{"at":{"type":"string"},"liveCount":{"type":"integer"},"readyCount":{"type":"integer"},"gobbledBytes":{"type":"number"},"onSale":{"type":"integer"},"liveByCountry":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"count":{"type":"integer"}},"required":["code","count"]}},"topSource":{"type":"string","enum":["orders","wanted"]},"top":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"n":{"type":"number"}},"required":["code","name","n"]}},"recent":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"volumeBytes":{"type":["number","null"]},"at":{"type":"string"}},"required":["code","name","volumeBytes","at"]}},"liveByHour":{"type":"array","items":{"type":"integer"}}},"required":["at","liveCount","readyCount","gobbledBytes","onSale","liveByCountry","topSource","top","recent","liveByHour"]}}}}}}},"/api/v1/checkout":{"post":{"tags":["Checkout"],"summary":"Start an order and get the Stripe Checkout URL","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"qty":{"type":"integer","minimum":1,"maximum":5,"default":1},"topupEsimId":{"type":"string","format":"uuid"},"poolCode":{"type":"string","pattern":"^[A-HJ-NP-Z2-9]{6}$"}},"required":["productId"]}}}},"responses":{"200":{"description":"Navigate to `checkoutUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"orderId":{"type":"string","format":"uuid"},"orderRef":{"type":"string"},"checkoutUrl":{"type":"string","format":"uri"}},"required":["orderId","orderRef","checkoutUrl"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"PLAN_NOT_AVAILABLE, INVALID_QUANTITY, ESIM_NOT_FOUND, TOPUP_NOT_ALLOWED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many orders started this hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"PAYMENTS_NOT_CONFIGURED, or Stripe unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/orders":{"get":{"tags":["Orders"],"summary":"My orders","description":"Orders that got past checkout (pending_payment ones are not listed), newest first, without their eSIMs.","responses":{"200":{"description":"Orders.","content":{"application/json":{"schema":{"type":"object","properties":{"orders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ref":{"type":"string"},"status":{"type":"string","enum":["pending_payment","paid","provisioning","fulfilled","partially_fulfilled","failed","refunded","cancelled","expired"]},"kind":{"type":"string","enum":["purchase","topup"]},"currency":{"type":"string"},"subtotalMinor":{"type":"integer"},"discountMinor":{"type":"integer"},"taxMinor":{"type":"integer"},"totalMinor":{"type":"integer"},"paidMinor":{"type":["integer","null"]},"captureState":{"type":"string","enum":["auto","authorised","captured","released","expired"]},"perk":{"type":["string","null"],"enum":["simpig_paid","pool","saved_bacon","heist",null]},"createdAt":{"type":"string"},"paidAt":{"type":["string","null"]},"fulfilledAt":{"type":["string","null"]},"failedReason":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"qty":{"type":"integer"},"unitPriceMinor":{"type":"integer"},"kind":{"type":"string","enum":["base","topup"]},"targetEsimId":{"type":["string","null"],"format":"uuid"},"volumeBytes":{"type":["number","null"]},"durationDays":{"type":["integer","null"]},"providerStatus":{"type":"string","enum":["pending","ordered","allocated","failed"]}},"required":["id","productId","packageCode","title","destinationCode","destinationName","qty","unitPriceMinor","kind","targetEsimId","volumeBytes","durationDays","providerStatus"]}},"esims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["string","null"]},"esimTranNo":{"type":"string"},"iccid":{"type":["string","null"]},"packageCode":{"type":"string"},"productTitle":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"esimStatus":{"type":"string"},"smdpStatus":{"type":"string"},"state":{"type":"string","enum":["ready","installed","active","out_of_data","expired","cancelled","revoked","suspended","unknown"]},"activeType":{"type":["integer","null"]},"totalVolumeBytes":{"type":["number","null"]},"usedBytes":{"type":"number"},"remainingBytes":{"type":"number"},"usageCheckedAt":{"type":["string","null"]},"activateTime":{"type":["string","null"]},"expiredTime":{"type":["string","null"]},"totalDuration":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"speed":{"type":["string","null"]},"supportsTopup":{"type":"boolean"},"topupCount":{"type":"integer"},"install":{"type":"object","properties":{"ac":{"type":["string","null"]},"qrCodeUrl":{"type":["string","null"]},"shortUrl":{"type":["string","null"]},"smdpAddress":{"type":["string","null"]},"matchingId":{"type":["string","null"]},"confirmationCode":{"type":["string","null"]},"iosInstallUrl":{"type":["string","null"]},"androidInstallUrl":{"type":["string","null"]},"apn":{"type":["string","null"]}},"required":["ac","qrCodeUrl","shortUrl","smdpAddress","matchingId","confirmationCode","iosInstallUrl","androidInstallUrl","apn"]},"orderRef":{"type":"string"},"orderItemId":{"type":"string","format":"uuid"},"packages":{"type":"array","items":{"type":"object","properties":{"orderType":{"type":"string"},"packageCode":{"type":["string","null"]},"volumeBytes":{"type":["number","null"]},"durationValue":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["orderType","packageCode","volumeBytes","durationValue","durationUnit","createdAt"]}},"lastSyncedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","label","esimTranNo","iccid","packageCode","productTitle","destinationCode","destinationName","esimStatus","smdpStatus","state","totalVolumeBytes","usedBytes","remainingBytes","usageCheckedAt","activateTime","expiredTime","totalDuration","durationUnit","speed","supportsTopup","topupCount","install","orderRef","orderItemId","packages","lastSyncedAt","createdAt"]}}},"required":["id","ref","status","kind","currency","subtotalMinor","discountMinor","taxMinor","totalMinor","createdAt","paidAt","fulfilledAt","failedReason","items","esims"]}}},"required":["orders"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/orders/{ref}":{"get":{"tags":["Orders"],"summary":"One order with its eSIMs","parameters":[{"schema":{"type":"string","minLength":4,"maxLength":16,"pattern":"^[A-Za-z0-9-]+$","example":"SP-7K3M9Q"},"required":true,"name":"ref","in":"path"}],"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ref":{"type":"string"},"status":{"type":"string","enum":["pending_payment","paid","provisioning","fulfilled","partially_fulfilled","failed","refunded","cancelled","expired"]},"kind":{"type":"string","enum":["purchase","topup"]},"currency":{"type":"string"},"subtotalMinor":{"type":"integer"},"discountMinor":{"type":"integer"},"taxMinor":{"type":"integer"},"totalMinor":{"type":"integer"},"paidMinor":{"type":["integer","null"]},"captureState":{"type":"string","enum":["auto","authorised","captured","released","expired"]},"perk":{"type":["string","null"],"enum":["simpig_paid","pool","saved_bacon","heist",null]},"createdAt":{"type":"string"},"paidAt":{"type":["string","null"]},"fulfilledAt":{"type":["string","null"]},"failedReason":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"qty":{"type":"integer"},"unitPriceMinor":{"type":"integer"},"kind":{"type":"string","enum":["base","topup"]},"targetEsimId":{"type":["string","null"],"format":"uuid"},"volumeBytes":{"type":["number","null"]},"durationDays":{"type":["integer","null"]},"providerStatus":{"type":"string","enum":["pending","ordered","allocated","failed"]}},"required":["id","productId","packageCode","title","destinationCode","destinationName","qty","unitPriceMinor","kind","targetEsimId","volumeBytes","durationDays","providerStatus"]}},"esims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["string","null"]},"esimTranNo":{"type":"string"},"iccid":{"type":["string","null"]},"packageCode":{"type":"string"},"productTitle":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"esimStatus":{"type":"string"},"smdpStatus":{"type":"string"},"state":{"type":"string","enum":["ready","installed","active","out_of_data","expired","cancelled","revoked","suspended","unknown"]},"activeType":{"type":["integer","null"]},"totalVolumeBytes":{"type":["number","null"]},"usedBytes":{"type":"number"},"remainingBytes":{"type":"number"},"usageCheckedAt":{"type":["string","null"]},"activateTime":{"type":["string","null"]},"expiredTime":{"type":["string","null"]},"totalDuration":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"speed":{"type":["string","null"]},"supportsTopup":{"type":"boolean"},"topupCount":{"type":"integer"},"install":{"type":"object","properties":{"ac":{"type":["string","null"]},"qrCodeUrl":{"type":["string","null"]},"shortUrl":{"type":["string","null"]},"smdpAddress":{"type":["string","null"]},"matchingId":{"type":["string","null"]},"confirmationCode":{"type":["string","null"]},"iosInstallUrl":{"type":["string","null"]},"androidInstallUrl":{"type":["string","null"]},"apn":{"type":["string","null"]}},"required":["ac","qrCodeUrl","shortUrl","smdpAddress","matchingId","confirmationCode","iosInstallUrl","androidInstallUrl","apn"]},"orderRef":{"type":"string"},"orderItemId":{"type":"string","format":"uuid"},"packages":{"type":"array","items":{"type":"object","properties":{"orderType":{"type":"string"},"packageCode":{"type":["string","null"]},"volumeBytes":{"type":["number","null"]},"durationValue":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["orderType","packageCode","volumeBytes","durationValue","durationUnit","createdAt"]}},"lastSyncedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","label","esimTranNo","iccid","packageCode","productTitle","destinationCode","destinationName","esimStatus","smdpStatus","state","totalVolumeBytes","usedBytes","remainingBytes","usageCheckedAt","activateTime","expiredTime","totalDuration","durationUnit","speed","supportsTopup","topupCount","install","orderRef","orderItemId","packages","lastSyncedAt","createdAt"]}}},"required":["id","ref","status","kind","currency","subtotalMinor","discountMinor","taxMinor","totalMinor","createdAt","paidAt","fulfilledAt","failedReason","items","esims"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not one of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Malformed reference.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/orders/{ref}/receipt":{"get":{"tags":["Orders"],"summary":"The receipt for a paid order","description":"The order with the business details and tax line from the shop settings and the payment reference. 404 until the order is paid.","parameters":[{"schema":{"type":"string","pattern":"^[0-9A-Za-z]{8}$"},"required":true,"name":"ref","in":"path"}],"responses":{"200":{"description":"The receipt.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ref":{"type":"string"},"status":{"type":"string","enum":["pending_payment","paid","provisioning","fulfilled","partially_fulfilled","failed","refunded","cancelled","expired"]},"kind":{"type":"string","enum":["purchase","topup"]},"currency":{"type":"string"},"subtotalMinor":{"type":"integer"},"discountMinor":{"type":"integer"},"taxMinor":{"type":"integer"},"totalMinor":{"type":"integer"},"paidMinor":{"type":["integer","null"]},"captureState":{"type":"string","enum":["auto","authorised","captured","released","expired"]},"perk":{"type":["string","null"],"enum":["simpig_paid","pool","saved_bacon","heist",null]},"createdAt":{"type":"string"},"paidAt":{"type":["string","null"]},"fulfilledAt":{"type":["string","null"]},"failedReason":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"qty":{"type":"integer"},"unitPriceMinor":{"type":"integer"},"kind":{"type":"string","enum":["base","topup"]},"targetEsimId":{"type":["string","null"],"format":"uuid"},"volumeBytes":{"type":["number","null"]},"durationDays":{"type":["integer","null"]},"providerStatus":{"type":"string","enum":["pending","ordered","allocated","failed"]}},"required":["id","productId","packageCode","title","destinationCode","destinationName","qty","unitPriceMinor","kind","targetEsimId","volumeBytes","durationDays","providerStatus"]}},"esims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["string","null"]},"esimTranNo":{"type":"string"},"iccid":{"type":["string","null"]},"packageCode":{"type":"string"},"productTitle":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"esimStatus":{"type":"string"},"smdpStatus":{"type":"string"},"state":{"type":"string","enum":["ready","installed","active","out_of_data","expired","cancelled","revoked","suspended","unknown"]},"activeType":{"type":["integer","null"]},"totalVolumeBytes":{"type":["number","null"]},"usedBytes":{"type":"number"},"remainingBytes":{"type":"number"},"usageCheckedAt":{"type":["string","null"]},"activateTime":{"type":["string","null"]},"expiredTime":{"type":["string","null"]},"totalDuration":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"speed":{"type":["string","null"]},"supportsTopup":{"type":"boolean"},"topupCount":{"type":"integer"},"install":{"type":"object","properties":{"ac":{"type":["string","null"]},"qrCodeUrl":{"type":["string","null"]},"shortUrl":{"type":["string","null"]},"smdpAddress":{"type":["string","null"]},"matchingId":{"type":["string","null"]},"confirmationCode":{"type":["string","null"]},"iosInstallUrl":{"type":["string","null"]},"androidInstallUrl":{"type":["string","null"]},"apn":{"type":["string","null"]}},"required":["ac","qrCodeUrl","shortUrl","smdpAddress","matchingId","confirmationCode","iosInstallUrl","androidInstallUrl","apn"]},"orderRef":{"type":"string"},"orderItemId":{"type":"string","format":"uuid"},"packages":{"type":"array","items":{"type":"object","properties":{"orderType":{"type":"string"},"packageCode":{"type":["string","null"]},"volumeBytes":{"type":["number","null"]},"durationValue":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["orderType","packageCode","volumeBytes","durationValue","durationUnit","createdAt"]}},"lastSyncedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","label","esimTranNo","iccid","packageCode","productTitle","destinationCode","destinationName","esimStatus","smdpStatus","state","totalVolumeBytes","usedBytes","remainingBytes","usageCheckedAt","activateTime","expiredTime","totalDuration","durationUnit","speed","supportsTopup","topupCount","install","orderRef","orderItemId","packages","lastSyncedAt","createdAt"]}},"receipt":{"type":"object","properties":{"number":{"type":"string"},"issuedAt":{"type":"string"},"businessName":{"type":"string"},"businessAbn":{"type":["string","null"]},"businessAddress":{"type":["string","null"]},"businessEmail":{"type":"string"},"customerEmail":{"type":"string"},"taxLabel":{"type":"string"},"taxRateBps":{"type":"integer"},"taxMinor":{"type":"integer"},"pricesIncludeTax":{"type":"boolean"},"paymentMethod":{"type":"string"},"paymentRef":{"type":["string","null"]}},"required":["number","issuedAt","businessName","businessAbn","businessAddress","businessEmail","customerEmail","taxLabel","taxRateBps","taxMinor","pricesIncludeTax","paymentMethod","paymentRef"]}},"required":["id","ref","status","kind","currency","subtotalMinor","discountMinor","taxMinor","totalMinor","createdAt","paidAt","fulfilledAt","failedReason","items","esims","receipt"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No paid order with that ref.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/esims":{"get":{"tags":["eSIMs"],"summary":"My eSIMs","description":"Active first, then out of data, installed, ready; everything else after.","responses":{"200":{"description":"eSIMs.","content":{"application/json":{"schema":{"type":"object","properties":{"esims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["string","null"]},"esimTranNo":{"type":"string"},"iccid":{"type":["string","null"]},"packageCode":{"type":"string"},"productTitle":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"esimStatus":{"type":"string"},"smdpStatus":{"type":"string"},"state":{"type":"string","enum":["ready","installed","active","out_of_data","expired","cancelled","revoked","suspended","unknown"]},"activeType":{"type":["integer","null"]},"totalVolumeBytes":{"type":["number","null"]},"usedBytes":{"type":"number"},"remainingBytes":{"type":"number"},"usageCheckedAt":{"type":["string","null"]},"activateTime":{"type":["string","null"]},"expiredTime":{"type":["string","null"]},"totalDuration":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"speed":{"type":["string","null"]},"supportsTopup":{"type":"boolean"},"topupCount":{"type":"integer"},"install":{"type":"object","properties":{"ac":{"type":["string","null"]},"qrCodeUrl":{"type":["string","null"]},"shortUrl":{"type":["string","null"]},"smdpAddress":{"type":["string","null"]},"matchingId":{"type":["string","null"]},"confirmationCode":{"type":["string","null"]},"iosInstallUrl":{"type":["string","null"]},"androidInstallUrl":{"type":["string","null"]},"apn":{"type":["string","null"]}},"required":["ac","qrCodeUrl","shortUrl","smdpAddress","matchingId","confirmationCode","iosInstallUrl","androidInstallUrl","apn"]},"orderRef":{"type":"string"},"orderItemId":{"type":"string","format":"uuid"},"packages":{"type":"array","items":{"type":"object","properties":{"orderType":{"type":"string"},"packageCode":{"type":["string","null"]},"volumeBytes":{"type":["number","null"]},"durationValue":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["orderType","packageCode","volumeBytes","durationValue","durationUnit","createdAt"]}},"lastSyncedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","label","esimTranNo","iccid","packageCode","productTitle","destinationCode","destinationName","esimStatus","smdpStatus","state","totalVolumeBytes","usedBytes","remainingBytes","usageCheckedAt","activateTime","expiredTime","totalDuration","durationUnit","speed","supportsTopup","topupCount","install","orderRef","orderItemId","packages","lastSyncedAt","createdAt"]}}},"required":["esims"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/esims/{id}":{"get":{"tags":["eSIMs"],"summary":"One eSIM","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The eSIM with its install details and usage.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["string","null"]},"esimTranNo":{"type":"string"},"iccid":{"type":["string","null"]},"packageCode":{"type":"string"},"productTitle":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"esimStatus":{"type":"string"},"smdpStatus":{"type":"string"},"state":{"type":"string","enum":["ready","installed","active","out_of_data","expired","cancelled","revoked","suspended","unknown"]},"activeType":{"type":["integer","null"]},"totalVolumeBytes":{"type":["number","null"]},"usedBytes":{"type":"number"},"remainingBytes":{"type":"number"},"usageCheckedAt":{"type":["string","null"]},"activateTime":{"type":["string","null"]},"expiredTime":{"type":["string","null"]},"totalDuration":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"speed":{"type":["string","null"]},"supportsTopup":{"type":"boolean"},"topupCount":{"type":"integer"},"install":{"type":"object","properties":{"ac":{"type":["string","null"]},"qrCodeUrl":{"type":["string","null"]},"shortUrl":{"type":["string","null"]},"smdpAddress":{"type":["string","null"]},"matchingId":{"type":["string","null"]},"confirmationCode":{"type":["string","null"]},"iosInstallUrl":{"type":["string","null"]},"androidInstallUrl":{"type":["string","null"]},"apn":{"type":["string","null"]}},"required":["ac","qrCodeUrl","shortUrl","smdpAddress","matchingId","confirmationCode","iosInstallUrl","androidInstallUrl","apn"]},"orderRef":{"type":"string"},"orderItemId":{"type":"string","format":"uuid"},"packages":{"type":"array","items":{"type":"object","properties":{"orderType":{"type":"string"},"packageCode":{"type":["string","null"]},"volumeBytes":{"type":["number","null"]},"durationValue":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["orderType","packageCode","volumeBytes","durationValue","durationUnit","createdAt"]}},"lastSyncedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","label","esimTranNo","iccid","packageCode","productTitle","destinationCode","destinationName","esimStatus","smdpStatus","state","totalVolumeBytes","usedBytes","remainingBytes","usageCheckedAt","activateTime","expiredTime","totalDuration","durationUnit","speed","supportsTopup","topupCount","install","orderRef","orderItemId","packages","lastSyncedAt","createdAt"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not one of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Malformed id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/esims/{id}/label":{"post":{"tags":["eSIMs"],"summary":"Name this eSIM","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":40}},"required":["label"]}}}},"responses":{"200":{"description":"The eSIM, relabelled.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["string","null"]},"esimTranNo":{"type":"string"},"iccid":{"type":["string","null"]},"packageCode":{"type":"string"},"productTitle":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"esimStatus":{"type":"string"},"smdpStatus":{"type":"string"},"state":{"type":"string","enum":["ready","installed","active","out_of_data","expired","cancelled","revoked","suspended","unknown"]},"activeType":{"type":["integer","null"]},"totalVolumeBytes":{"type":["number","null"]},"usedBytes":{"type":"number"},"remainingBytes":{"type":"number"},"usageCheckedAt":{"type":["string","null"]},"activateTime":{"type":["string","null"]},"expiredTime":{"type":["string","null"]},"totalDuration":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"speed":{"type":["string","null"]},"supportsTopup":{"type":"boolean"},"topupCount":{"type":"integer"},"install":{"type":"object","properties":{"ac":{"type":["string","null"]},"qrCodeUrl":{"type":["string","null"]},"shortUrl":{"type":["string","null"]},"smdpAddress":{"type":["string","null"]},"matchingId":{"type":["string","null"]},"confirmationCode":{"type":["string","null"]},"iosInstallUrl":{"type":["string","null"]},"androidInstallUrl":{"type":["string","null"]},"apn":{"type":["string","null"]}},"required":["ac","qrCodeUrl","shortUrl","smdpAddress","matchingId","confirmationCode","iosInstallUrl","androidInstallUrl","apn"]},"orderRef":{"type":"string"},"orderItemId":{"type":"string","format":"uuid"},"packages":{"type":"array","items":{"type":"object","properties":{"orderType":{"type":"string"},"packageCode":{"type":["string","null"]},"volumeBytes":{"type":["number","null"]},"durationValue":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["orderType","packageCode","volumeBytes","durationValue","durationUnit","createdAt"]}},"lastSyncedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","label","esimTranNo","iccid","packageCode","productTitle","destinationCode","destinationName","esimStatus","smdpStatus","state","totalVolumeBytes","usedBytes","remainingBytes","usageCheckedAt","activateTime","expiredTime","totalDuration","durationUnit","speed","supportsTopup","topupCount","install","orderRef","orderItemId","packages","lastSyncedAt","createdAt"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"ESIM_NOT_FOUND or a label over 40 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/esims/{id}/sync":{"post":{"tags":["eSIMs"],"summary":"Refresh this eSIM from the supplier","description":"Queues a reconcile (at most once a minute per eSIM, 30 an hour per customer) and wakes the worker. Poll GET /esims/{id} for the result.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"`queued` is false when it was refreshed within the last minute.","content":{"application/json":{"schema":{"type":"object","properties":{"queued":{"type":"boolean"},"esimTranNo":{"type":"string"}},"required":["queued","esimTranNo"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"ESIM_NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many refreshes this hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/esims/{id}/topups":{"get":{"tags":["eSIMs"],"summary":"Top-up plans for this eSIM","description":"The supplier lists what this eSIM can take; those plans are registered in our catalogue as top-up-only products and offered at our prices. Empty when the eSIM cannot be topped up or the supplier is unavailable.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Plans to buy with POST /checkout `topupEsimId`.","content":{"application/json":{"schema":{"type":"object","properties":{"esimId":{"type":"string","format":"uuid"},"plans":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"destinationKind":{"type":"string","enum":["country","region","global"]},"isRegional":{"type":"boolean"},"volumeBytes":{"type":"number"},"durationDays":{"type":"integer"},"dataType":{"type":"integer"},"activeType":{"type":["integer","null"]},"supportsTopup":{"type":"boolean"},"speed":{"type":["string","null"]},"priceMinor":{"type":"integer"},"currency":{"type":"string"},"compareAtMinor":{"type":["integer","null"]},"badge":{"type":["string","null"]},"isFeatured":{"type":"boolean"},"coverageCodes":{"type":"array","items":{"type":"string"}},"operators":{"type":"array","items":{"type":"object","properties":{"name":{"type":["string","null"]},"network":{"type":["string","null"]}},"required":["name","network"]}}},"required":["productId","packageCode","title","subtitle","destinationCode","destinationName","destinationKind","isRegional","volumeBytes","durationDays","dataType","activeType","supportsTopup","speed","priceMinor","currency","compareAtMinor","badge","isFeatured","coverageCodes","operators"]}}},"required":["esimId","plans"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not one of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Malformed id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/pools/{code}":{"get":{"tags":["Pools"],"summary":"A pool, as anyone with the link sees it","parameters":[{"schema":{"type":"string","pattern":"^[A-HJ-NP-Z2-9]{6}$","example":"7KQ2XM"},"required":true,"name":"code","in":"path"}],"responses":{"200":{"description":"The pool.","content":{"application/json":{"schema":{"type":"object","properties":{"pool":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":["string","null"]},"status":{"type":"string","enum":["draft","open","settled","cancelled"]},"tripCodes":{"type":"array","items":{"type":"string"}},"destinations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["code","name","slug"]}},"opensAt":{"type":["string","null"]},"closesAt":{"type":["string","null"]},"maxMembers":{"type":"integer"},"tiers":{"type":"array","items":{"type":"object","properties":{"members":{"type":"integer"},"bps":{"type":"integer"}},"required":["members","bps"]}},"paidCount":{"type":"integer"},"tierBps":{"type":"integer"},"members":{"type":"array","items":{"type":"object","properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"joinedAt":{"type":"string"}},"required":["displayName","paid","host","joinedAt"]}},"me":{"type":["object","null"],"properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"orderRef":{"type":["string","null"]},"fullMinor":{"type":["integer","null"]},"finalMinor":{"type":["integer","null"]}},"required":["displayName","paid","host","orderRef","fullMinor","finalMinor"]}},"required":["code","name","status","tripCodes","destinations","opensAt","closesAt","maxMembers","tiers","paidCount","tierBps","members","me"]}},"required":["pool"]}}}},"404":{"description":"No such pool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/pools":{"post":{"tags":["Pools"],"summary":"Open a pool for a trip","description":"The caller is the host and its first member. The pool opens, and the clock starts, when the host’s own payment is authorised.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tripCodes":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{2}$"},"minItems":1,"maxItems":8},"name":{"type":"string","maxLength":24},"displayName":{"type":"string","maxLength":20}},"required":["tripCodes"]}}}},"responses":{"200":{"description":"The pool, in draft until the host pays.","content":{"application/json":{"schema":{"type":"object","properties":{"pool":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":["string","null"]},"status":{"type":"string","enum":["draft","open","settled","cancelled"]},"tripCodes":{"type":"array","items":{"type":"string"}},"destinations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["code","name","slug"]}},"opensAt":{"type":["string","null"]},"closesAt":{"type":["string","null"]},"maxMembers":{"type":"integer"},"tiers":{"type":"array","items":{"type":"object","properties":{"members":{"type":"integer"},"bps":{"type":"integer"}},"required":["members","bps"]}},"paidCount":{"type":"integer"},"tierBps":{"type":"integer"},"members":{"type":"array","items":{"type":"object","properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"joinedAt":{"type":"string"}},"required":["displayName","paid","host","joinedAt"]}},"me":{"type":["object","null"],"properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"orderRef":{"type":["string","null"]},"fullMinor":{"type":["integer","null"]},"finalMinor":{"type":["integer","null"]}},"required":["displayName","paid","host","orderRef","fullMinor","finalMinor"]}},"required":["code","name","status","tripCodes","destinations","opensAt","closesAt","maxMembers","tiers","paidCount","tierBps","members","me"]}},"required":["pool"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"POOLS_OFF, INVALID_INPUT.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many pools today.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/pools/mine":{"get":{"tags":["Pools"],"summary":"The pools the caller is in","responses":{"200":{"description":"Newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"pools":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":["string","null"]},"status":{"type":"string","enum":["draft","open","settled","cancelled"]},"tripCodes":{"type":"array","items":{"type":"string"}},"destinations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["code","name","slug"]}},"opensAt":{"type":["string","null"]},"closesAt":{"type":["string","null"]},"maxMembers":{"type":"integer"},"tiers":{"type":"array","items":{"type":"object","properties":{"members":{"type":"integer"},"bps":{"type":"integer"}},"required":["members","bps"]}},"paidCount":{"type":"integer"},"tierBps":{"type":"integer"},"members":{"type":"array","items":{"type":"object","properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"joinedAt":{"type":"string"}},"required":["displayName","paid","host","joinedAt"]}},"me":{"type":["object","null"],"properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"orderRef":{"type":["string","null"]},"fullMinor":{"type":["integer","null"]},"finalMinor":{"type":["integer","null"]}},"required":["displayName","paid","host","orderRef","fullMinor","finalMinor"]}},"required":["code","name","status","tripCodes","destinations","opensAt","closesAt","maxMembers","tiers","paidCount","tierBps","members","me"]}}},"required":["pools"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/pools/{code}/me":{"get":{"tags":["Pools"],"summary":"A pool with the caller’s own place in it","parameters":[{"schema":{"type":"string","pattern":"^[A-HJ-NP-Z2-9]{6}$","example":"7KQ2XM"},"required":true,"name":"code","in":"path"}],"responses":{"200":{"description":"The pool, `me` filled in when the caller is a member.","content":{"application/json":{"schema":{"type":"object","properties":{"pool":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":["string","null"]},"status":{"type":"string","enum":["draft","open","settled","cancelled"]},"tripCodes":{"type":"array","items":{"type":"string"}},"destinations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["code","name","slug"]}},"opensAt":{"type":["string","null"]},"closesAt":{"type":["string","null"]},"maxMembers":{"type":"integer"},"tiers":{"type":"array","items":{"type":"object","properties":{"members":{"type":"integer"},"bps":{"type":"integer"}},"required":["members","bps"]}},"paidCount":{"type":"integer"},"tierBps":{"type":"integer"},"members":{"type":"array","items":{"type":"object","properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"joinedAt":{"type":"string"}},"required":["displayName","paid","host","joinedAt"]}},"me":{"type":["object","null"],"properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"orderRef":{"type":["string","null"]},"fullMinor":{"type":["integer","null"]},"finalMinor":{"type":["integer","null"]}},"required":["displayName","paid","host","orderRef","fullMinor","finalMinor"]}},"required":["code","name","status","tripCodes","destinations","opensAt","closesAt","maxMembers","tiers","paidCount","tierBps","members","me"]}},"required":["pool"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such pool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/pools/{code}/join":{"post":{"tags":["Pools"],"summary":"Join a pool","description":"Free, and counts for nothing until the caller buys an eSIM for the trip with `poolCode` at checkout.","parameters":[{"schema":{"type":"string","pattern":"^[A-HJ-NP-Z2-9]{6}$","example":"7KQ2XM"},"required":true,"name":"code","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"displayName":{"type":"string","maxLength":20}}}}}},"responses":{"200":{"description":"The pool with the caller in it.","content":{"application/json":{"schema":{"type":"object","properties":{"pool":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":["string","null"]},"status":{"type":"string","enum":["draft","open","settled","cancelled"]},"tripCodes":{"type":"array","items":{"type":"string"}},"destinations":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["code","name","slug"]}},"opensAt":{"type":["string","null"]},"closesAt":{"type":["string","null"]},"maxMembers":{"type":"integer"},"tiers":{"type":"array","items":{"type":"object","properties":{"members":{"type":"integer"},"bps":{"type":"integer"}},"required":["members","bps"]}},"paidCount":{"type":"integer"},"tierBps":{"type":"integer"},"members":{"type":"array","items":{"type":"object","properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"joinedAt":{"type":"string"}},"required":["displayName","paid","host","joinedAt"]}},"me":{"type":["object","null"],"properties":{"displayName":{"type":"string"},"paid":{"type":"boolean"},"host":{"type":"boolean"},"orderRef":{"type":["string","null"]},"fullMinor":{"type":["integer","null"]},"finalMinor":{"type":["integer","null"]}},"required":["displayName","paid","host","orderRef","fullMinor","finalMinor"]}},"required":["code","name","status","tripCodes","destinations","opensAt","closesAt","maxMembers","tiers","paidCount","tierBps","members","me"]}},"required":["pool"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"POOL_NOT_FOUND, POOL_CLOSED, POOL_FULL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/admin/overview":{"get":{"tags":["Admin"],"summary":"Today at a glance","responses":{"200":{"description":"Counts, revenue, supplier balance, last catalogue sync.","content":{"application/json":{"schema":{"type":"object","properties":{"customers":{"type":"integer"},"ordersToday":{"type":"integer"},"revenueTodayMinor":{"type":"integer"},"provisioning":{"type":"integer"},"failed":{"type":"integer"},"esimsActive":{"type":"integer"},"providerBalanceUsdE4":{"type":["integer","null"]},"lastCatalogSync":{"type":["string","null"]},"currency":{"type":"string"}},"required":["customers","ordersToday","revenueTodayMinor","provisioning","failed","esimsActive","providerBalanceUsdE4","lastCatalogSync","currency"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not staff, or no second factor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/admin/orders":{"get":{"tags":["Admin"],"summary":"Recent orders, every customer","description":"shop.order_json with the customer email and id added, newest first.","responses":{"200":{"description":"Orders.","content":{"application/json":{"schema":{"type":"object","properties":{"orders":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["orders"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not staff, or no second factor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/admin/catalog/sync":{"post":{"tags":["Admin"],"summary":"Sync the catalogue from the supplier now","description":"api.me() on the admin surface must succeed first (staff, aal2); then the same sync the hourly housekeeping runs.","responses":{"200":{"description":"What changed.","content":{"application/json":{"schema":{"type":"object","properties":{"syncId":{"anyOf":[{"type":"integer"},{"type":"string"}]},"packagesSeen":{"type":"integer"},"packagesNew":{"type":"integer"},"packagesGone":{"type":"integer"},"productsRepriced":{"type":"integer"}},"required":["syncId","packagesSeen","packagesNew","packagesGone","productsRepriced"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not staff, or no second factor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Supplier not configured or not answering.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/agent/orders":{"post":{"tags":["Agent"],"summary":"Start an order for a person (no sign-in)","description":"For assistants and programs. Name the person’s email and a plan (`productId` from `GET /api/v1/catalog/plans/{destination}`). The answer carries Stripe’s `payUrl` to hand to the person, a `statusUrl` to poll, and an `orderToken` returned only here: it is the only way to read the order back. Once paid and provisioned, the status carries the install details (one-tap links, the LPA activation code for a QR, the manual SM-DP+ entry) and the same are emailed to the person. Send `idempotencyKey` to make retries safe. Rate-limited per IP and per email.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email"},"productId":{"type":"string","format":"uuid"},"qty":{"type":"integer","minimum":1,"maximum":5,"default":1},"callbackUrl":{"type":"string","maxLength":2048,"format":"uri"},"idempotencyKey":{"type":"string","minLength":8,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$"}},"required":["email","productId"]}}}},"responses":{"200":{"description":"The order, the pay link and the one-time token.","content":{"application/json":{"schema":{"type":"object","properties":{"order":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ref":{"type":"string"},"status":{"type":"string","enum":["pending_payment","paid","provisioning","fulfilled","partially_fulfilled","failed","refunded","cancelled","expired"]},"kind":{"type":"string","enum":["purchase","topup"]},"currency":{"type":"string"},"subtotalMinor":{"type":"integer"},"discountMinor":{"type":"integer"},"taxMinor":{"type":"integer"},"totalMinor":{"type":"integer"},"paidMinor":{"type":["integer","null"]},"captureState":{"type":"string","enum":["auto","authorised","captured","released","expired"]},"perk":{"type":["string","null"],"enum":["simpig_paid","pool","saved_bacon","heist",null]},"createdAt":{"type":"string"},"paidAt":{"type":["string","null"]},"fulfilledAt":{"type":["string","null"]},"failedReason":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"qty":{"type":"integer"},"unitPriceMinor":{"type":"integer"},"kind":{"type":"string","enum":["base","topup"]},"targetEsimId":{"type":["string","null"],"format":"uuid"},"volumeBytes":{"type":["number","null"]},"durationDays":{"type":["integer","null"]},"providerStatus":{"type":"string","enum":["pending","ordered","allocated","failed"]}},"required":["id","productId","packageCode","title","destinationCode","destinationName","qty","unitPriceMinor","kind","targetEsimId","volumeBytes","durationDays","providerStatus"]}},"esims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["string","null"]},"esimTranNo":{"type":"string"},"iccid":{"type":["string","null"]},"packageCode":{"type":"string"},"productTitle":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"esimStatus":{"type":"string"},"smdpStatus":{"type":"string"},"state":{"type":"string","enum":["ready","installed","active","out_of_data","expired","cancelled","revoked","suspended","unknown"]},"activeType":{"type":["integer","null"]},"totalVolumeBytes":{"type":["number","null"]},"usedBytes":{"type":"number"},"remainingBytes":{"type":"number"},"usageCheckedAt":{"type":["string","null"]},"activateTime":{"type":["string","null"]},"expiredTime":{"type":["string","null"]},"totalDuration":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"speed":{"type":["string","null"]},"supportsTopup":{"type":"boolean"},"topupCount":{"type":"integer"},"install":{"type":"object","properties":{"ac":{"type":["string","null"]},"qrCodeUrl":{"type":["string","null"]},"shortUrl":{"type":["string","null"]},"smdpAddress":{"type":["string","null"]},"matchingId":{"type":["string","null"]},"confirmationCode":{"type":["string","null"]},"iosInstallUrl":{"type":["string","null"]},"androidInstallUrl":{"type":["string","null"]},"apn":{"type":["string","null"]}},"required":["ac","qrCodeUrl","shortUrl","smdpAddress","matchingId","confirmationCode","iosInstallUrl","androidInstallUrl","apn"]},"orderRef":{"type":"string"},"orderItemId":{"type":"string","format":"uuid"},"packages":{"type":"array","items":{"type":"object","properties":{"orderType":{"type":"string"},"packageCode":{"type":["string","null"]},"volumeBytes":{"type":["number","null"]},"durationValue":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["orderType","packageCode","volumeBytes","durationValue","durationUnit","createdAt"]}},"lastSyncedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","label","esimTranNo","iccid","packageCode","productTitle","destinationCode","destinationName","esimStatus","smdpStatus","state","totalVolumeBytes","usedBytes","remainingBytes","usageCheckedAt","activateTime","expiredTime","totalDuration","durationUnit","speed","supportsTopup","topupCount","install","orderRef","orderItemId","packages","lastSyncedAt","createdAt"]}},"payUrl":{"type":["string","null"],"format":"uri"},"claimExpiresAt":{"type":["string","null"]}},"required":["id","ref","status","kind","currency","subtotalMinor","discountMinor","taxMinor","totalMinor","createdAt","paidAt","fulfilledAt","failedReason","items","esims","payUrl","claimExpiresAt"]},"orderToken":{"type":["string","null"]},"statusUrl":{"type":"string","format":"uri"},"existing":{"type":"boolean"},"next":{"type":"string"}},"required":["order","orderToken","statusUrl","existing","next"]}}}},"422":{"description":"Unknown or unavailable plan, bad quantity or email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many orders from this address or IP in an hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Payments not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/agent/orders/{ref}":{"get":{"tags":["Agent"],"summary":"The order’s progress and, once fulfilled, the install details","description":"Send the order token as `Authorization: Bearer <orderToken>`. An unknown ref, a wrong token and an expired token (14 days) all answer 404.","parameters":[{"schema":{"type":"string","pattern":"^[0-9A-Za-z]{8}$"},"required":true,"name":"ref","in":"path"},{"schema":{"type":"string"},"required":true,"name":"authorization","in":"header"}],"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"type":"object","properties":{"order":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ref":{"type":"string"},"status":{"type":"string","enum":["pending_payment","paid","provisioning","fulfilled","partially_fulfilled","failed","refunded","cancelled","expired"]},"kind":{"type":"string","enum":["purchase","topup"]},"currency":{"type":"string"},"subtotalMinor":{"type":"integer"},"discountMinor":{"type":"integer"},"taxMinor":{"type":"integer"},"totalMinor":{"type":"integer"},"paidMinor":{"type":["integer","null"]},"captureState":{"type":"string","enum":["auto","authorised","captured","released","expired"]},"perk":{"type":["string","null"],"enum":["simpig_paid","pool","saved_bacon","heist",null]},"createdAt":{"type":"string"},"paidAt":{"type":["string","null"]},"fulfilledAt":{"type":["string","null"]},"failedReason":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"productId":{"type":"string","format":"uuid"},"packageCode":{"type":"string"},"title":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"qty":{"type":"integer"},"unitPriceMinor":{"type":"integer"},"kind":{"type":"string","enum":["base","topup"]},"targetEsimId":{"type":["string","null"],"format":"uuid"},"volumeBytes":{"type":["number","null"]},"durationDays":{"type":["integer","null"]},"providerStatus":{"type":"string","enum":["pending","ordered","allocated","failed"]}},"required":["id","productId","packageCode","title","destinationCode","destinationName","qty","unitPriceMinor","kind","targetEsimId","volumeBytes","durationDays","providerStatus"]}},"esims":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["string","null"]},"esimTranNo":{"type":"string"},"iccid":{"type":["string","null"]},"packageCode":{"type":"string"},"productTitle":{"type":"string"},"destinationCode":{"type":"string"},"destinationName":{"type":"string"},"esimStatus":{"type":"string"},"smdpStatus":{"type":"string"},"state":{"type":"string","enum":["ready","installed","active","out_of_data","expired","cancelled","revoked","suspended","unknown"]},"activeType":{"type":["integer","null"]},"totalVolumeBytes":{"type":["number","null"]},"usedBytes":{"type":"number"},"remainingBytes":{"type":"number"},"usageCheckedAt":{"type":["string","null"]},"activateTime":{"type":["string","null"]},"expiredTime":{"type":["string","null"]},"totalDuration":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"speed":{"type":["string","null"]},"supportsTopup":{"type":"boolean"},"topupCount":{"type":"integer"},"install":{"type":"object","properties":{"ac":{"type":["string","null"]},"qrCodeUrl":{"type":["string","null"]},"shortUrl":{"type":["string","null"]},"smdpAddress":{"type":["string","null"]},"matchingId":{"type":["string","null"]},"confirmationCode":{"type":["string","null"]},"iosInstallUrl":{"type":["string","null"]},"androidInstallUrl":{"type":["string","null"]},"apn":{"type":["string","null"]}},"required":["ac","qrCodeUrl","shortUrl","smdpAddress","matchingId","confirmationCode","iosInstallUrl","androidInstallUrl","apn"]},"orderRef":{"type":"string"},"orderItemId":{"type":"string","format":"uuid"},"packages":{"type":"array","items":{"type":"object","properties":{"orderType":{"type":"string"},"packageCode":{"type":["string","null"]},"volumeBytes":{"type":["number","null"]},"durationValue":{"type":["integer","null"]},"durationUnit":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["orderType","packageCode","volumeBytes","durationValue","durationUnit","createdAt"]}},"lastSyncedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","label","esimTranNo","iccid","packageCode","productTitle","destinationCode","destinationName","esimStatus","smdpStatus","state","totalVolumeBytes","usedBytes","remainingBytes","usageCheckedAt","activateTime","expiredTime","totalDuration","durationUnit","speed","supportsTopup","topupCount","install","orderRef","orderItemId","packages","lastSyncedAt","createdAt"]}},"payUrl":{"type":["string","null"],"format":"uri"},"claimExpiresAt":{"type":["string","null"]}},"required":["id","ref","status","kind","currency","subtotalMinor","discountMinor","taxMinor","totalMinor","createdAt","paidAt","fulfilledAt","failedReason","items","esims","payUrl","claimExpiresAt"]},"next":{"type":"string"}},"required":["order","next"]}}}},"401":{"description":"No order token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not your token).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/stripe":{"post":{"tags":["Webhooks"],"summary":"Stripe events","description":"The raw event body, signed with `Stripe-Signature`. Handled: checkout.session.completed, checkout.session.async_payment_succeeded / failed, checkout.session.expired, charge.refunded. Other types are stored and ignored.","parameters":[{"schema":{"type":"string"},"required":false,"name":"stripe-signature","in":"header"}],"responses":{"200":{"description":"Received (and acted on, or a duplicate).","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean","enum":[true]}},"required":["received"]}}}},"400":{"description":"Bad signature or unparseable event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"No webhook secret configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/esimaccess":{"post":{"tags":["Webhooks"],"summary":"eSIM Access notifications","description":"ORDER_STATUS, ESIM_STATUS, SMDP_EVENT, DATA_USAGE, VALIDITY_USAGE, CHECK_HEALTH. Stored once by notifyId; the eSIM named in `content` is reconciled from the signed API.","responses":{"200":{"description":"Received.","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean","enum":[true]}},"required":["received"]}}}}}}},"/api/v1/health":{"get":{"tags":["Meta"],"summary":"Liveness","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}}}}}},"webhooks":{}}