# OrionPay — Full LLM Context This is the inline-form documentation for OrionPay's public API. It mirrors the OpenAPI 3.1 spec and is suitable for pasting into LLM context windows. For an index version: https://pay.orion.moe/llms.txt For the canonical machine-readable spec: https://payapi.orion.moe/openapi.json For the Streamable HTTP MCP server: https://payapi.orion.moe/mcp Authentication: send your API key in the X-API-Key header (or Authorization: Bearer ). Two key types: product keys (scoped to one product) and personal keys (productId=null, full account). ## Endpoints ## PIX ### POST /api/v1/pix/generate **Operation ID**: `pix_generate` **MCP tool**: `pix_generate` **Auth**: api-key **Rate limit**: none — No rate limit (critical payment generation). Generates a PIX QR code for a product purchase. The product is identified by the API key (product key). On payment, a webhook is delivered (`payment.success`) and the buyer receives an access token. Payer identification (cpf OR euid) is required since 2026-06-30 for totals ≥ R$10.00; totals strictly below R$10.00 are exempt since 2026-07-21. **Request body** (`GeneratePixRequest`): ``` amount?: number — BRL amount. Optional when product has a fixed price. description?: string — Optional description shown to payer. name!: string — Payer full name. email!: string — Payer email. phone?: string — Optional payer phone (E.164 or Brazilian). cpf?: string — Payer CPF (11 digits) or CNPJ (14 digits) — REQUIRED since 2026-06-30 for amounts ≥ R$10.00 unless euid is sent (amounts strictly below R$10.00 are exempt since 2026-07-21). Punctuation accepted; checksum-validated. euid?: string — Payer DePix EUID — alternative to cpf as the payer identification. ipAddress?: string — Optional payer IP for fraud scoring. ``` ### GET /api/v1/pix/status/{id} **Operation ID**: `pix_status` **MCP tool**: `pix_status` **Auth**: api-key **Rate limit**: 60 / min — 60 requests per minute per ID per API key. Polls the status of a PIX payment. The `id` parameter accepts purchaseId (numeric), transactionId (numeric), or eulenDepositId (UUID). Webhook delivery is preferred over polling. **Parameters**: - `id`! (path, string) — purchaseId, transactionId, or eulenDepositId (UUID). ## Integration ### GET /integration/verify-access **Operation ID**: `integration_verify_access` **MCP tool**: `integration_verify_access` **Auth**: api-key **Rate limit**: 60 / min — 60 requests per minute per token/email per API key. Checks whether a buyer (by email or accessToken) has paid access to the product associated with the API key. **Parameters**: - `email`? (query, string) - `accessToken`? (query, string) ### GET /integration/buyers **Operation ID**: `integration_list_buyers` **MCP tool**: `integration_list_buyers` **Auth**: api-key **Rate limit**: 60 / min Returns all buyers of the product associated with the API key. Filter by status optional. **Parameters**: - `status`? (query, string enum: PENDING|PAID|EXPIRED|CANCELED|REFUNDED) ### GET /integration/buyers/{purchaseId} **Operation ID**: `integration_get_buyer` **MCP tool**: `integration_get_buyer` **Auth**: api-key **Rate limit**: 60 / min Returns details for a specific purchase belonging to the API key's product. **Parameters**: - `purchaseId`! (path, integer) ### GET /integration/content **Operation ID**: `integration_get_content` **MCP tool**: `integration_get_content` **Auth**: api-key **Rate limit**: 60 / min Returns the product content (files, lessons, etc.) accessible to the buyer who owns the supplied accessToken. **Parameters**: - `accessToken`! (query, string) ### POST /integration/grant-access **Operation ID**: `integration_grant_access` **MCP tool**: `integration_grant_access` **Auth**: api-key Grants access to a buyer without a payment (e.g. for refunds resolution, comp tickets, or migrations). An access token is generated. **Request body** (`GrantAccessRequest`): ``` buyerEmail!: string buyerName?: string price?: number — Optional recorded price (no charge is made). expiresAt?: string — Optional access expiry. ``` ### DELETE /integration/revoke-access/{purchaseId} **Operation ID**: `integration_revoke_access` **MCP tool**: `integration_revoke_access` **Auth**: api-key Revokes a buyer's access. The access token is invalidated. **Parameters**: - `purchaseId`! (path, integer) ### POST /api/v1/integration/deposit/pix **Operation ID**: `integration_deposit_pix` **MCP tool**: `integration_deposit_pix` **Auth**: api-key **Rate limit**: 10 / min Generates a PIX QR code for a product deposit. The payer's CPF (or euid) is REQUIRED since 2026-06-30 for amounts ≥ R$10.00 (provider enforcement; amounts strictly below R$10.00 are exempt since 2026-07-21); requests with neither, when required, get 400. The merchant's identification (merchantId) is resolved automatically from the API key owner and must not be sent. **Request body** (`CreatePixDepositRequest`): ``` cpf?: string — Payer CPF (11 digits) or CNPJ (14 digits), digits only — REQUIRED since 2026-06-30 for amounts ≥ R$10.00 unless euid is sent (amounts strictly below R$10.00 are exempt since 2026-07-21); checksum-validated. The merchant's identification (merchantId) is added automatically server-side; do not send it. euid?: string — Payer DePix EUID — alternative to cpf as the payer identification. buyerName?: string buyerEmail?: string amount?: number ``` ## Personal ### GET /api/v1/personal/balance **Operation ID**: `personal_balance` **MCP tool**: `personal_balance` **Auth**: personal-api-key **Rate limit**: 60 / hour Returns BRL balance and all crypto balances (Liquid Network, Bitcoin, Polygon, etc.). ### GET /api/v1/personal/limits **Operation ID**: `personal_limits` **MCP tool**: `personal_limits` **Auth**: personal-api-key **Rate limit**: 30 / hour Returns the user's daily/monthly/per-transaction withdrawal limits and remaining usage. ### GET /api/v1/personal/transactions **Operation ID**: `personal_transactions` **MCP tool**: `personal_transactions` **Auth**: personal-api-key **Rate limit**: 60 / hour Returns the user's transaction history (deposits, withdrawals, swaps), most recent first. **Parameters**: - `limit`? (query, integer) - `offset`? (query, integer) - `type`? (query, string enum: DEPOSIT|WITHDRAWAL|SWAP) ### POST /api/v1/personal/withdraw/pix **Operation ID**: `personal_withdraw_pix` **MCP tool**: `personal_withdraw_pix` **Auth**: personal-api-key **Rate limit**: 10 / hour Queues a PIX withdrawal from the user's BRL wallet. Requires API withdrawals to be enabled (see /enable-withdrawals). A 7-day hold applies to deposits while API withdrawals are enabled. **Request body** (`PixWithdrawRequest`): ``` amount!: number — BRL amount. pixKey!: string — PIX key (CPF/CNPJ/email/phone/random). pixKeyType?: string (CPF|CNPJ|EMAIL|PHONE|EVP) — Optional; auto-detected if omitted. description?: string taxNumber?: string — Tax number of the recipient (CPF/CNPJ). euid?: string — Optional Eulen user identifier override. ``` ### POST /api/v1/personal/withdraw/crypto **Operation ID**: `personal_withdraw_crypto` **MCP tool**: `personal_withdraw_crypto` **Auth**: personal-api-key **Rate limit**: 10 / hour Queues a crypto withdrawal. Supports L-BTC, BTC-PEGOUT (Bitcoin mainnet via peg-out), DePix, USDt, USDC. Network parameter routes via SideShift for cross-chain. **Request body** (`CryptoWithdrawRequest`): ``` amount!: number — Amount in the asset's native unit. toAddress!: string — Destination wallet address. assetTicker!: string (L-BTC|BTC-PEGOUT|DePix|USDt|USDC) — BTC-PEGOUT: peg-out to Bitcoin mainnet (min 0.00025 BTC). network?: string (liquid|bitcoin|ethereum|tron|bsc|solana|ton|avalanche|polygon|optimism) — Optional network override (e.g. for SideShift cross-chain routing). memo?: string — Required for TON. ``` ### GET /api/v1/personal/withdraw/status/{id} **Operation ID**: `personal_withdraw_status` **MCP tool**: `personal_withdraw_status` **Auth**: personal-api-key **Rate limit**: 120 / hour Returns the status, settlement details, and txHash of a withdrawal by transaction ID. **Parameters**: - `id`! (path, integer) ### POST /api/v1/personal/swap/quote **Operation ID**: `personal_swap_quote` **MCP tool**: `personal_swap_quote` **Auth**: personal-api-key **Rate limit**: 120 / hour Returns a quote for swapping between supported assets (DePix, L-BTC, USDt, EURx). Quote is valid for ~30 seconds. **Request body** (`SwapRequest`): ``` fromCurrency!: string (DePix|L-BTC|USDt|EURx) toCurrency!: string (DePix|L-BTC|USDt|EURx) amount!: number ``` ### POST /api/v1/personal/swap/execute **Operation ID**: `personal_swap_execute` **MCP tool**: `personal_swap_execute` **Auth**: personal-api-key **Rate limit**: 20 / hour Executes a swap. Re-quotes internally before locking funds; if the rate moved beyond tolerance the swap is rejected. **Request body** (`SwapRequest`): ``` fromCurrency!: string (DePix|L-BTC|USDt|EURx) toCurrency!: string (DePix|L-BTC|USDt|EURx) amount!: number ``` ### GET /api/v1/personal/withdraw-enabled **Operation ID**: `personal_withdraw_enabled` **MCP tool**: `personal_withdraw_enabled` **Auth**: personal-api-key **Rate limit**: 60 / hour Returns whether API-driven withdrawals are enabled and the deposit hold period. ### POST /api/v1/personal/enable-withdrawals **Operation ID**: `personal_enable_withdrawals` **Auth**: personal-api-key **Rate limit**: 5 / hour Enables withdrawal endpoints for this account. Imposes a 7-day hold on every deposit thereafter (anti-fraud). Requires explicit acceptance of the terms. **Request body** (`EnableWithdrawalsRequest`): ``` acceptTerms!: boolean — Must be true; acknowledges the 7-day hold on all deposits while API withdrawals are enabled. ``` ## Webhooks Outbound HTTPS POST to URLs configured at /dashboard/api-webhooks. Each delivery includes `X-OrionPay-Signature: sha256=` (HMAC-SHA256 of raw body), `X-OrionPay-Event`, and `X-OrionPay-Delivery` (UUID for idempotency). Respond 2xx to ACK. ### Event: `payment.success` **Payload**: ``` event?: string productId?: integer purchaseId?: integer transactionId?: integer buyerEmail?: string buyerName?: string amount?: number paidAt?: string accessToken?: string timestamp?: integer — Unix milliseconds. ``` ### Event: `purchase.created` **Payload**: ``` event?: string productId?: integer purchaseId?: integer buyerEmail?: string amount?: number status?: string (PENDING) timestamp?: integer ``` ### Event: `access.granted` **Payload**: ``` event?: string productId?: integer purchaseId?: integer buyerEmail?: string grantedManually?: boolean accessToken?: string expiresAt?: string timestamp?: integer ``` ### Event: `payment.refunded_med` **Payload**: ``` event?: string productId?: integer purchaseId?: integer transactionId?: integer buyerEmail?: string amount?: number refundedAt?: string reason?: string — MED (fraud challenge) reason from BACEN. refundReasonDetail?: string (med_chargeback) — Always med_chargeback when present — the refund was triggered by the MED the payer filed. timestamp?: integer ``` ### Event: `payment.refunded` **Payload**: ``` event?: string transactionId?: integer — Dashboard test deliveries send a mock string id (`mock_`) instead. amount?: number reference?: string status?: string (refunded) type?: string currency?: string (BRL) reason?: string (eulen_refund) — PIX-provider refund. When the refunded payment carries a MED dispute, refundReasonDetail says med_chargeback (MED reversals of already-credited held amounts still fire the dedicated payment.refunded_med event). refundReasonDetail?: string (cpf_mismatch|cnpj_mismatch|med_chargeback|spam_like|user_blocked|payer_refused|indeterminate_payer|no_typed_cpf|cpf_exempt|no_payer_data) — Optional. Inferred via a cascade of checks: med_chargeback (the payer filed a MED — Bacen's fraud-dispute mechanism — and the payment was returned during the hold window) → document cross-check (diverging CPF → cpf_mismatch, diverging CNPJ → cnpj_mismatch; never applies below the CPF-exemption amount: since 06/08/2026 a document typed for a total strictly under R$10.00 is validated and recorded but NOT forwarded to the provider, so the QR is not locked to it and any account can pay — mismatch refunds cannot happen there) → spam_like (provider anti-spam: 2 or more completed deposits from the same payer in the ~30 minutes before the refunded one — refused attempts themselves do not count) → user_blocked (payer blocked at the provider) → payer_refused (catch-all: provider risk engine refused, no reason declared). Between 28/07/2026 21:39 UTC and 01/08/2026 01:07 UTC the provider did not disclose the paying account on refunds (its refund pipeline echoed back the document the QR was locked to); refunds in that closed window classify as indeterminate_payer and carry no payer fields at all. The real payer identity has been disclosed again since 01/08/2026, so refunds from then on classify normally. For the mismatch reasons only, the payload also carries payerName/payerTaxNumber so the merchant can compare against the document typed at checkout; for the other reasons (except indeterminate_payer) the payload carries only this enum plus payerEuid. Legacy values possible in historical deliveries only: no_typed_cpf, cpf_exempt, no_payer_data, and payer_company (equivalent to cnpj_mismatch). payerEuid?: string — Pseudonymous Eulen end-user id (EUID) of the bank account that actually paid, e.g. `EU015562310201680`. Present for every refund reason whenever known, except indeterminate_payer (during the closed 28/07/2026-01/08/2026 window the provider echoed the locked identity instead of the payer, so no payer field is trustworthy on those deliveries). Stable per payer: use it to correlate repeat refunded payers and block them at your checkout without ever seeing the payer's identity. payerName?: string — Bank-account holder that actually paid. Present only when refundReasonDetail is cpf_mismatch or cnpj_mismatch. payerTaxNumber?: string — Document of the paying account, always masked for CPFs (`***.999.999-**`, Eulen format — the full CPF is never sent); full 14-digit CNPJ for company/PSP accounts (public registry data). Present only when refundReasonDetail is cpf_mismatch or cnpj_mismatch. rejectionReasons?: array — Raw reason codes declared by the PIX provider, e.g. ["PAYER_MISMATCH"] (the paying account's document differs from the one the QR was locked to) or ["BLOCKED_USER"]. The same field payment.refund_pending carries. Live since 01/08/2026 — older refunds arrive without it. The vocabulary is the provider's and may grow, so treat unknown codes as generic risk refusals. Unlike refundReasonDetail this is the provider's own word, not our inference; it never widens the payer fields disclosed above. eulenStatus?: string refundedAt?: string credited?: boolean — Present (always false) when the refund happened before any balance was credited; absent when a previously credited held amount was reversed. timestamp?: string purchaseId?: integer — Present only for product checkout refunds. productId?: integer — Present only for product checkout refunds. productTitle?: string — Present only for product checkout refunds. buyerEmail?: string — Present only for product checkout refunds. buyerName?: string — Present only for product checkout refunds. ``` ### Event: `payment.refund_pending` **Payload**: ``` event?: string transactionId?: integer — Dashboard test deliveries send a mock string id (`mock_`) instead. amount?: number reference?: string status?: string (refund_pending) type?: string currency?: string (BRL) reason?: string (eulen_will_refund) rejectionReasons?: array — Raw reason codes declared by the PIX provider, e.g. ["PAYER_MISMATCH"] (the paying account's document differs from the one the QR was locked to) or ["BLOCKED_USER"]. Optional — older notices arrive without it. The vocabulary is the provider's and may grow, so treat unknown codes as generic risk refusals. expectedRefundAt?: string — Optional. When the provider declared a hold before returning the money (typically 3 days), the date it expects to refund. payerEuid?: string — Pseudonymous Eulen end-user id (EUID) of the account that paid, when known. Same semantics as in payment.refunded: stable per payer, safe to use for blocking repeat offenders at your checkout. eulenStatus?: string (will_refund) credited?: boolean — Always false — no balance was ever credited for this payment. accessRevoked?: boolean — True when OrionPay revoked buyer access to the product because of this notice (the purchase moves to CANCELLED). False for account deposits and for purchases that had nothing to revoke. timestamp?: string purchaseId?: integer — Present only for product checkout payments. productId?: integer — Present only for product checkout payments. productTitle?: string — Present only for product checkout payments. buyerEmail?: string — Present only for product checkout payments. buyerName?: string — Present only for product checkout payments. ``` ### Event: `med.created` **Payload**: ``` event?: string transactionId?: integer purchaseId?: integer buyerEmail?: string amount?: number contestedAt?: string timestamp?: integer ``` ### Event: `webhook.test` **Payload**: ``` event?: string message?: string timestamp?: integer ``` ### Event: `withdrawal.completed` **Payload**: ``` event?: string transactionId?: integer asset?: string amount?: number toAddress?: string txHash?: string completedAt?: string timestamp?: integer ``` ### Event: `withdrawal.settled` **Payload**: ``` event?: string transactionId?: integer asset?: string amount?: number toAddress?: string txHash?: string settledAt?: string timestamp?: integer ``` ### Event: `withdrawal.failed` **Payload**: ``` event?: string transactionId?: integer asset?: string amount?: number reason?: string failedAt?: string timestamp?: integer ``` ### Event: `swap.completed` **Payload**: ``` event?: string transactionId?: integer fromCurrency?: string toCurrency?: string fromAmount?: number toAmount?: number rate?: number completedAt?: string timestamp?: integer ``` ### Event: `swap.failed` **Payload**: ``` event?: string transactionId?: integer fromCurrency?: string toCurrency?: string fromAmount?: number reason?: string failedAt?: string timestamp?: integer ```