{"openapi":"3.1.0","info":{"title":"Aspra Public API","version":"2.0.0","description":"The Aspra public API for airtime, vouchers, gift cards, utility bills and cash-in. All money-moving endpoints require a UUID `Idempotency-Key` header. Authenticate with `Authorization: Bearer <client_id>.<secret>` issued from the CRM. Set `X-Aspra-Env: sandbox` to hit the mock provider.  Rate limit: per-client (default 10 rps; configurable per client).  Changelog: https://api.aspra.io/v1/public/openapi.json Sandbox:   add header `X-Aspra-Env: sandbox` to any endpoint.","contact":{"name":"Aspra API support","email":"api@aspra.io"}},"servers":[{"url":"https://api.aspra.io/v1/public"}],"tags":[{"name":"Auth","description":"Credential validation"},{"name":"Catalog","description":"Billers, products, categories"},{"name":"Recharge","description":"Mobile airtime recharge (B2CTOP)"},{"name":"Bill Payment","description":"Utility bill payments (TAQA, FEWA, ELIFE, Du Postpaid, etc.)"},{"name":"Cash In","description":"Wallet top-ups (Du Pay, Botim Money)"},{"name":"Gift Card","description":"Gift / game card PIN issuance"},{"name":"Voucher","description":"Generic voucher PIN issuance (B2CWSL)"},{"name":"Transactions","description":"Read transactions, refunds"},{"name":"Reconciliation","description":"Per-client reconciliation summaries"},{"name":"Webhooks","description":"Subscription CRUD + delivery audit"},{"name":"Travel","description":"Flights, hotels, and bookings via Travelopro. **Beta — preview.** Live calls return 501 until the Phase 2 adapter ships; sandbox (`X-Aspra-Env: sandbox`) returns canned offers and bookings."}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"bad_request"},"message":{"type":"string","example":"Idempotency-Key header required"},"requestId":{"type":"string","description":"Echoed in X-Request-Id header — supply when contacting support."},"details":{}},"required":["code"]}},"required":["error"]},"Money":{"type":"object","properties":{"amountMinor":{"type":"string","description":"Integer minor units as string (bigint-safe)"},"currency":{"type":"string","example":"AED"}},"required":["amountMinor","currency"]},"Quote":{"type":"object","properties":{"amountMinor":{"type":"string"},"feeMinor":{"type":"string"},"totalMinor":{"type":"string"},"currency":{"type":"string"},"fxRate":{"type":"number","nullable":true}}},"RechargeResult":{"type":"object","properties":{"receiptNo":{"type":"string"},"status":{"type":"string","enum":["SUCCESS","FAILED","UNKNOWN"]},"providerTxnId":{"type":"string","nullable":true},"reason":{"type":"string","nullable":true}}},"VoucherResult":{"allOf":[{"$ref":"#/components/schemas/RechargeResult"},{"type":"object","properties":{"pin":{"type":"string","nullable":true}}}]},"GiftCardResult":{"allOf":[{"$ref":"#/components/schemas/RechargeResult"},{"type":"object","properties":{"pin":{"type":"string","nullable":true},"serial":{"type":"string","nullable":true},"expiresAt":{"type":"string","nullable":true}}}]},"BillInquiry":{"type":"object","properties":{"billerCode":{"type":"string"},"accountNumber":{"type":"string"},"accountName":{"type":"string","nullable":true},"outstandingMinor":{"type":"string","nullable":true},"currency":{"type":"string","nullable":true},"dueDate":{"type":"string","nullable":true},"billerRef":{"type":"string","nullable":true}}},"WebhookSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["active","paused"]},"createdAt":{"type":"string","format":"date-time"},"secret":{"type":"string","description":"HMAC secret — returned ONCE on create."}}},"Reconciliation":{"type":"object","properties":{"period":{"type":"object","properties":{"dateFrom":{"type":"string","format":"date-time"},"dateTo":{"type":"string","format":"date-time"}}},"totalCount":{"type":"integer"},"totalVolumeMinor":{"type":"string"},"totalFeesMinor":{"type":"string"},"byStatus":{"type":"object","properties":{"success":{"type":"integer"},"failed":{"type":"integer"},"unknown":{"type":"integer"}}},"byBiller":{"type":"array","items":{"type":"object"}}}}}},"security":[{"bearer":[]}],"paths":{"/auth/token":{"post":{"tags":["Auth"],"summary":"Validate credentials and echo tenant + scopes","description":"Sanity check that the bearer token is valid and returns the active scopes.","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalog":{"get":{"tags":["Catalog"],"summary":"List active products available to the caller","description":"Required scope: `txn:read`.","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/billers":{"get":{"tags":["Catalog"],"summary":"List enabled billers","description":"Filters: country (ISO2), categoryCode, q. Required scope: `txn:read`. Cached 60s. Each item includes a `logoUrl` (nullable) suitable for display in the consumer UI.","parameters":[{"name":"country","in":"query","schema":{"type":"string"}},{"name":"categoryCode","in":"query","schema":{"type":"string"}},{"name":"q","in":"query","schema":{"type":"string"}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/categories":{"get":{"tags":["Catalog"],"summary":"List biller categories","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recharge/quote":{"post":{"tags":["Recharge"],"summary":"Quote a recharge (no money moves)","description":"Required scope: `txn:read`.","parameters":[{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"providerCode":"ipos","prodCode":"B2CTOP","accountNo":"971501234567","amountMinor":"1000","currency":"AED"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recharge/confirm":{"post":{"tags":["Recharge"],"summary":"Confirm a recharge (money moves)","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"providerCode":"ipos","prodCode":"B2CTOP","accountNo":"971501234567","amountMinor":"1000","currency":"AED"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RechargeResult"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bill/inquire":{"post":{"tags":["Bill Payment"],"summary":"Look up an outstanding utility bill","description":"Required scope: `txn:read`. Maps to iPos pinless lookup.","parameters":[{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"billerCode":"TAQA-WATER","accountNumber":"0123456789"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillInquiry"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bill/pay":{"post":{"tags":["Bill Payment"],"summary":"Pay a utility bill","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"billerCode":"TAQA-WATER","accountNumber":"0123456789","amountMinor":"12345","currency":"AED"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RechargeResult"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/cashin/quote":{"post":{"tags":["Cash In"],"summary":"Quote a cash-in (Du Pay / Botim Money)","description":"Required scope: `txn:read`.","parameters":[{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/cashin/confirm":{"post":{"tags":["Cash In"],"summary":"Confirm a cash-in","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RechargeResult"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/giftcard/issue":{"post":{"tags":["Gift Card"],"summary":"Issue a gift / game card PIN","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GiftCardResult"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/voucher/issue":{"post":{"tags":["Voucher"],"summary":"Issue a voucher PIN (B2CWSL pinRequest)","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoucherResult"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/transactions":{"get":{"tags":["Transactions"],"summary":"List recent transactions for the caller","description":"Required scope: `txn:read`.","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/transactions/{id}":{"get":{"tags":["Transactions"],"summary":"Fetch a single transaction","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/transactions/{id}/refund":{"post":{"tags":["Transactions"],"summary":"Best-effort refund / reverse","description":"Required scope: `txn:write`. iPos does not support reverse — returns `{status: \"not_supported\"}`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/balance":{"get":{"tags":["Transactions"],"summary":"Tenant float balance","description":"Required scope: `ledger:read`.","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reconciliation":{"get":{"tags":["Reconciliation"],"summary":"Per-client reconciliation summary (max 90 days)","description":"Required scope: `txn:read`.","parameters":[{"name":"dateFrom","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"dateTo","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"billerCode","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reconciliation"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/subscriptions":{"get":{"tags":["Webhooks"],"summary":"List webhook destinations","description":"Required scope: `webhook:manage`.","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Webhooks"],"summary":"Create a webhook destination (returns secret ONCE)","description":"Required scope: `webhook:manage`.","responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscription"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/subscriptions/{id}":{"patch":{"tags":["Webhooks"],"summary":"Update a webhook destination","description":"Required scope: `webhook:manage`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook destination","description":"Required scope: `webhook:manage`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/deliveries":{"get":{"tags":["Webhooks"],"summary":"List recent webhook deliveries","description":"Required scope: `webhook:manage`.","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/test":{"post":{"tags":["Webhooks"],"summary":"Send a test webhook event","description":"Required scope: `webhook:manage`.","responses":{"201":{"description":"Created"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/search":{"post":{"tags":["Travel"],"summary":"Search flights (beta)","description":"Required scope: `txn:write`. **Beta — preview.** Live mode returns `501 not_implemented` until the Travelopro adapter is wired in Phase 2. Sandbox (`X-Aspra-Env: sandbox`) returns canned offers.","parameters":[{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"origin":"DXB","destination":"BOM","departDate":"2026-06-01","returnDate":"2026-06-08","adults":1,"cabinClass":"economy","currency":"USD"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"Not Implemented (live mode, Phase 1)"}}}},"/travel/flights/book":{"post":{"tags":["Travel"],"summary":"Book a flight (beta)","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. **Beta — preview.** Live mode returns 501 until Phase 2.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"offerId":"sb_off_abc","passengers":[{"type":"ADT","title":"MR","firstName":"Jane","lastName":"Doe","dob":"1990-01-15","gender":"F"}],"contact":{"email":"jane@example.com","phone":"+971501234567"}}}}},"responses":{"201":{"description":"Created"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"Not Implemented (live mode, Phase 1)"}}}},"/travel/flights/price":{"post":{"tags":["Travel"],"summary":"Revalidate / price a flight fare (beta)","description":"Required scope: `txn:write`. **Beta — preview.** Calls Travelopro `/revalidate`. Returns the priced itinerary. If the fare is no longer valid, returns 410 `fare_expired`.","parameters":[{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"sessionId":"sess_TR_abc123","fareSourceCode":"FSC_OUT_001","fareSourceCodeInbound":"FSC_IN_001"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Fare expired"},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/extras":{"post":{"tags":["Travel"],"summary":"Get ancillaries for a fare (beta)","description":"Required scope: `txn:write`. **Beta — preview.** Returns categorized ancillaries: baggage, meals, and per-sector seat maps. Wraps Travelopro `/extra_services`.","parameters":[{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"sessionId":"sess_TR_abc123","fareSourceCode":"FSC_OUT_001"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/fare-rules":{"post":{"tags":["Travel"],"summary":"Get fare rules text (beta)","description":"Required scope: `txn:write`. **Beta — preview.** Wraps Travelopro `/fare_rules`. Returns baggage info plus a list of `{ airline, cityPair, category, text }`.","parameters":[{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"sessionId":"sess_TR_abc123","fareSourceCode":"FSC_OUT_001","fareSourceCodeInbound":"FSC_IN_001"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/book/{pnr}/ticket":{"post":{"tags":["Travel"],"summary":"Issue an e-ticket for a confirmed booking (beta)","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. **Beta — preview.** Wraps Travelopro `/ticket_order`. Body: `{ uniqueId }` (returned by `/book`).","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"uniqueId":"UID_42_99"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/hotels/search":{"post":{"tags":["Travel"],"summary":"Search hotels (beta)","description":"Required scope: `txn:write`. **Beta — preview.** Live mode returns 501 until Phase 2.","parameters":[{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"cityCode":"DXB","checkIn":"2026-06-01","checkOut":"2026-06-04","rooms":[{"adults":2}],"currency":"USD"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"Not Implemented (live mode, Phase 1)"}}}},"/travel/hotels/book":{"post":{"tags":["Travel"],"summary":"Book a hotel (beta)","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. **Beta — preview.** Live mode returns 501 until Phase 2.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"offerId":"sb_hot_abc","rooms":[{"leadGuest":{"type":"ADT","firstName":"Jane","lastName":"Doe","dob":"1990-01-15"}}],"contact":{"email":"jane@example.com","phone":"+971501234567"}}}}},"responses":{"201":{"description":"Created"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"Not Implemented (live mode, Phase 1)"}}}},"/travel/bookings/{id}":{"get":{"tags":["Travel"],"summary":"Look up a travel booking (beta)","description":"Required scope: `txn:write`. **Beta — preview.** Live mode returns 501 until Phase 2.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"Not Implemented (live mode, Phase 1)"}}}},"/travel/bookings/{id}/cancel":{"post":{"tags":["Travel"],"summary":"Cancel a travel booking (beta)","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. **Beta — preview.** Live mode returns 501 until Phase 2.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"Not Implemented (live mode, Phase 1)"}}}},"/travel/flights/{pnr}":{"get":{"tags":["Travel"],"summary":"Trip details for a confirmed booking","description":"Required scope: `txn:read`. Wraps Travelopro `/trip_details`. PNR ownership enforced — caller must own the booking.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"},"description":"Travelopro UniqueID returned at /book"},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/cancel":{"post":{"tags":["Travel"],"summary":"Cancel a flight booking and refund the wallet","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. Wraps Travelopro `/cancel`. On success refunds full `client_charge_minor` to the kapi wallet.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/notes":{"post":{"tags":["Travel"],"summary":"Add a booking note","description":"Required scope: `txn:write`. Wraps Travelopro `/booking_notes`. No money movement.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"note":"Customer requested a meal upgrade."}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/seats":{"post":{"tags":["Travel — Travelport-specific"],"summary":"Get seat map for a Travelport reservation","description":"Required scope: `txn:read`. Travelport-only. Use `?provider=travelport`.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"},"description":"Travelport reservation id"},{"name":"provider","in":"query","required":false,"schema":{"type":"string","enum":["travelport"]}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":false,"content":{"application/json":{"example":{"segmentId":"seg_1"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/ancillaries":{"post":{"tags":["Travel — Travelport-specific"],"summary":"Add an ancillary service to a Travelport reservation","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. Travelport-only.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"provider","in":"query","required":false,"schema":{"type":"string","enum":["travelport"]}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"ancillaryId":"anc_BAG_20KG"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/exchange":{"post":{"tags":["Travel — Travelport-specific"],"summary":"Exchange a Travelport reservation for a new itinerary","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. Travelport-only.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"provider","in":"query","required":false,"schema":{"type":"string","enum":["travelport"]}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"newOfferId":"tp_off_xxx"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/void/quote":{"post":{"tags":["Travel"],"summary":"Void quote (no money movement)","description":"Required scope: `txn:write`. Wraps Travelopro `/void_ticket_quote`.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"requestBody":{"required":true,"content":{"application/json":{"example":{"paxDetails":[{"type":"ADT","firstName":"John","lastName":"Doe","eTicket":"176-1234567890"}]}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/void/execute":{"post":{"tags":["Travel"],"summary":"Void a ticket and refund TotalRefundAmount to wallet","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. Wraps Travelopro `/void_ticket`.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/refund/quote":{"post":{"tags":["Travel"],"summary":"Refund quote (no money movement)","description":"Required scope: `txn:write`. Wraps Travelopro `/refund_quote`.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/refund/execute":{"post":{"tags":["Travel"],"summary":"Refund a ticket and credit wallet","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. Tries Travelopro `/refund` first; falls back to `/refund_ticket` on 404. Refund amount per provider quote (proportional — markup retained).","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/reissue/quote":{"post":{"tags":["Travel"],"summary":"Reissue quote","description":"Required scope: `txn:write`. Wraps Travelopro `/reissue_ticket_quote`.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/reissue/execute":{"post":{"tags":["Travel"],"summary":"Reissue a ticket and debit wallet for fare difference","description":"Required scope: `txn:write`. `Idempotency-Key` REQUIRED. Wraps Travelopro `/reissue_ticket`. Debits fare difference + new markup from the wallet.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Caller-generated UUID. Required on every mutating call. Replays return the same body.","schema":{"type":"string","format":"uuid"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/flights/{pnr}/ptr/{ptrId}":{"get":{"tags":["Travel"],"summary":"PTR (post-ticket request) status","description":"Required scope: `txn:read`. Wraps Travelopro `/search_post_ticket_status`.","parameters":[{"name":"pnr","in":"path","required":true,"schema":{"type":"string"}},{"name":"ptrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Aspra-Env","in":"header","required":false,"description":"Set to `sandbox` to route to the mock provider — no ledger writes, canned success responses.","schema":{"type":"string","enum":["live","sandbox"]}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/airports":{"get":{"tags":["Travel"],"summary":"Cached airport list","description":"Required scope: `txn:read`. Read from `travelopro_airports` cache. Refetches automatically if cache is empty or older than 30 days. Edge cache: `s-maxage=86400`.","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/travel/airlines":{"get":{"tags":["Travel"],"summary":"Cached airline list","description":"Required scope: `txn:read`. Read from `travelopro_airlines` cache. Refetches automatically if cache is empty or older than 30 days. Edge cache: `s-maxage=86400`.","responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden / scope missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}