{"openapi":"3.0.3","info":{"title":"Roamzy Agent API","version":"1.0.0","description":"Public REST surface for AI agents and 3rd-party integrators. One global eSIM. Per-MB billing. USDT-only payments. See https://roamzy.io/llms.txt for human-readable agent guidance.","contact":{"name":"Roamzy support","url":"https://t.me/roamzy_support_bot","email":"support@roamzy.io"},"license":{"name":"Proprietary","url":"https://roamzy.io/legal/terms"}},"servers":[{"url":"https://roamzy.io","description":"Production"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","bearerFormat":"rk_live_<base64url>","description":"API token issued in the user dashboard. Format `rk_live_<32-char-base64url>`. Shown once on creation; only SHA-256 hash is stored server-side."}},"schemas":{"Country":{"type":"object","required":["slug","name","iso_alpha_2","flag","rate_usdt_per_mb","rate_usdt_per_gb","is_premium","zone"],"properties":{"slug":{"type":"string","example":"esim-spain"},"name":{"type":"string","example":"Spain"},"iso_alpha_2":{"type":"string","minLength":2,"maxLength":2,"example":"ES"},"flag":{"type":"string","example":"🇪🇸"},"rate_usdt_per_mb":{"type":"number","example":0.0014},"rate_usdt_per_gb":{"type":"number","example":1.4336},"is_premium":{"type":"boolean","example":false},"zone":{"type":"string","example":"EU 33 REG"}}},"ErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"string","example":"invalid_token"},"detail":{"type":"string"}}},"Status":{"type":"object","properties":{"api_version":{"type":"string","example":"v1"},"time":{"type":"string","format":"date-time"},"agents_paused":{"type":"boolean","description":"When true, every token-authed endpoint returns 503. Read endpoints (no auth) still work."},"purchases_paused":{"type":"boolean","description":"When true, /api/v1/orders returns 503. Read endpoints still work."},"anon_paused":{"type":"boolean","description":"When true, /api/v1/anon-session returns 503 (no new anonymous signups). Existing tokens unaffected. Implied by agents_paused. Mig 0046."},"reason":{"type":"string","nullable":true,"description":"Human-readable explanation set by operator during incident response."}}}}},"paths":{"/api/v1/health":{"get":{"summary":"Liveness probe","description":"Returns 200 when the API process is alive. Does NOT prove DB connectivity — see /api/v1/status for agent-relevant state.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"api_version":{"type":"string"},"time":{"type":"string","format":"date-time"}}}}}}}}},"/api/v1/status":{"get":{"summary":"Agent pause / kill-switch state","description":"Agents MUST poll this before purchase attempts. Backoff if `purchases_paused` is true. Never cached (cache-control: no-store).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}}}}}},"/api/v1/catalog":{"get":{"summary":"Country catalog with per-MB rates","description":"Returns all 193 countries with current pricing. CORS: *. Cached 5 min at the edge.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"api_version":{"type":"string"},"prices_version":{"type":"string"},"currency":{"type":"string","enum":["USDT"]},"payment":{"type":"object","properties":{"currencies":{"type":"array","items":{"type":"string"}},"providers":{"type":"array","items":{"type":"string"}},"fiat_supported":{"type":"boolean"}}},"coverage":{"type":"object","properties":{"total_countries":{"type":"integer"}}},"countries":{"type":"array","items":{"$ref":"#/components/schemas/Country"}}}}}}}}}},"/api/v1/countries/{slug}":{"get":{"summary":"Single-country snapshot","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"esim-spain"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"api_version":{"type":"string"},"country":{"allOf":[{"$ref":"#/components/schemas/Country"},{"type":"object","properties":{"page_url":{"type":"string","format":"uri"}}}]}}}}}},"404":{"description":"Country not in catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/estimate":{"post":{"summary":"Project cost for { country, MB budget }","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["country_slug","mb_estimated"],"properties":{"country_slug":{"type":"string","example":"esim-spain"},"mb_estimated":{"type":"number","minimum":0,"maximum":1000000,"example":500}}}}}},"responses":{"200":{"description":"Estimate computed","content":{"application/json":{"schema":{"type":"object","properties":{"api_version":{"type":"string"},"country":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}}},"input":{"type":"object","properties":{"mb_estimated":{"type":"number"}}},"result":{"type":"object","properties":{"amount_usdt":{"type":"number","description":"Projected cost for the requested MB at current rate"},"rate_usdt_per_mb":{"type":"number"},"min_topup_usdt":{"type":"number","description":"Anti-dust threshold; lowest accepted top-up"},"must_top_up_at_least":{"type":"number","description":"max(min_topup_usdt, amount_usdt)"}}}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Country not in catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/me":{"get":{"summary":"Current user info (auth required)","security":[{"bearer":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","nullable":true,"description":"Masked (a***r@domain.com) to limit PII exposure"},"display_name":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}},"token":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix_hint":{"type":"string","example":"rk_live_abc1"},"allow_purchase":{"type":"boolean"}}}}}}}},"401":{"description":"Missing or invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Service paused or user blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/anon-session":{"post":{"summary":"Create anonymous user + API token (no signup required) — mig 0046","description":"Agent-first flow: client gets a Bearer api_token and a one-time claim_token without prior OAuth. Tighter limits ($30/day, $100/mo, $50 big-txn, 1 active eSIM) until the user attaches this anon account to a real Google/Telegram/email identity via the claim_url. Rate-limited 5/IP/day + 1/IP/min.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"user_agent_hint":{"type":"string","maxLength":200,"description":"Optional free-form client hint (e.g. \"claude-desktop\"). Logged for telemetry."}}}}}},"responses":{"201":{"description":"Anonymous session created. SAVE api_token and claim_url — they cannot be retrieved later.","content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string"},"api_token":{"type":"string","description":"Bearer token (rk_live_<32-char>). SHOWN ONCE."},"claim_token":{"type":"string","description":"Magic-link claim secret (rc_<32-char>). SHOWN ONCE."},"claim_url":{"type":"string","format":"uri","description":"URL to surface to the user for permanent-account attachment."},"limits":{"type":"object","properties":{"daily_usdt":{"type":"number","example":30},"monthly_usdt":{"type":"number","example":100},"cool_off_usdt":{"type":"number","example":30},"big_txn_usdt":{"type":"number","example":50},"max_active_esims":{"type":"integer","example":1}}},"claim_expires_at":{"type":"string","nullable":true,"description":"NULL — claim tokens never auto-expire (operator-approved 2026-05-20)."},"hint":{"type":"string"}}}}}},"429":{"description":"IP rate-limit (5/day or 1/min)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"agents.all.paused or agents.anon.paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/orders":{"post":{"summary":"Create eSIM order with USDT payment intent (auth + purchase scope)","description":"Reserves an eSIM and creates a NowPayments invoice. Runs spending-limit gate (daily/monthly/cool-off/big-txn). Returns pay_url for the user to complete payment.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["country_slug","amount_usdt"],"properties":{"country_slug":{"type":"string","example":"esim-spain"},"amount_usdt":{"type":"integer","minimum":1,"maximum":1000,"example":20},"pay_currency":{"type":"string","example":"usdttrc20","description":"Optional pay currency (default usdttrc20)"}}}}}},"responses":{"200":{"description":"Order created — user pays at pay_url. msisdn is the user-facing eSIM identifier; all other IDs are internal plumbing.","content":{"application/json":{"schema":{"type":"object","properties":{"msisdn":{"type":"string","nullable":true,"description":"USER-FACING eSIM phone number (e.g. \"2040400002\"). Show this to the human; hide the other IDs."},"order_id":{"type":"string","description":"Internal — pass to follow-up endpoints only."},"intent_id":{"type":"string","description":"Internal — use as :id in GET /api/v1/orders/:id. Do NOT show to user."},"esim_id":{"type":"string","description":"Internal — use as :id in GET /api/v1/esims/:id. Do NOT show to user."},"display_id":{"type":"string","description":"Internal short ID. Do NOT show to user."},"pay_url":{"type":"string","format":"uri","description":"NowPayments hosted invoice. Surface to user."},"amount_usdt":{"type":"number"},"status":{"type":"string","example":"waiting"},"user_facing":{"type":"object","description":"Pre-formatted UX hint — agents can copy identifier_value + action_url straight into user-visible text.","properties":{"identifier_label":{"type":"string","example":"Your eSIM number"},"identifier_value":{"type":"string","nullable":true,"description":"The MSISDN — same value as the top-level `msisdn` field."},"action_label":{"type":"string","example":"Pay with USDT"},"action_url":{"type":"string","format":"uri"},"hint":{"type":"string"}}},"next_steps":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Spending limit exceeded (daily/monthly/cool-off)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Purchase scope missing or big-txn confirmation required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Country not in catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"eSIM cap reached or reservation conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Purchases paused or NowPayments unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/orders/{id}":{"get":{"summary":"Poll order status","description":"Returns current status of an intent: waiting | confirming | confirmed | finished | failed | expired | refunded. msisdn is the user-facing identifier — surface that to the human, keep the other IDs internal.","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK — includes msisdn (user-facing) plus internal IDs for follow-up calls"},"401":{"description":"Missing or invalid token"},"404":{"description":"Order not found (or not yours)"}}}},"/api/v1/esims":{"get":{"summary":"List user eSIMs","security":[{"bearer":[]}],"responses":{"200":{"description":"OK — array of eSIMs with status, balance, msisdn, iccid"},"401":{"description":"Missing or invalid token"}}}},"/api/v1/esims/{id}":{"get":{"summary":"eSIM detail with activation surfaces","description":"Returns eSIM info plus the activation block (qr_image_url primary, qr_payload, lpa_url) when status is active or paused.","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"esim":{"type":"object","properties":{"id":{"type":"string"},"display_id":{"type":"string"},"status":{"type":"string"},"msisdn":{"type":"string","nullable":true},"iccid":{"type":"string"},"balance_usdt":{"type":"number"}}},"activation":{"type":"object","nullable":true,"properties":{"qr_image_url":{"type":"string","format":"uri","description":"PNG URL — primary, render inline in chat for the user to scan with phone camera"},"qr_payload":{"type":"string","description":"Raw QR text for clients that render their own QR"},"lpa_url":{"type":"string","description":"Universal-activation URL for one-tap install on iOS 17.4+/Android 14+ when agent and target device match"}}}}}}}},"401":{"description":"Missing or invalid token"},"404":{"description":"eSIM not found (or not yours)"}}}},"/api/v1/faq.json":{"get":{"summary":"Agent-oriented FAQ (17 Q&As)","description":"Machine-readable FAQ for agents: token lifecycle, error codes, retry policy, QR vs LPA, edge cases.","responses":{"200":{"description":"OK — JSON with `questions` array"}}}}}}