{ "openapi": "3.1.0", "info": { "title": "YuvexPay API", "description": "The YuvexPay API allows you to accept payments, process withdrawals, manage products and customers programmatically. All API access is over HTTPS and uses JSON request/response bodies.", "version": "1.0.0", "contact": { "name": "YuvexPay Support", "email": "support@yuvexpay.com", "url": "https://docs.yuvexpay.com" } }, "servers": [ { "url": "https://api.yuvexpay.com", "description": "Same base URL serves both sandbox and production. Send a `ypk_test_*` key for sandbox or a `ypk_live_*` key for production." } ], "security": [ { "bearerAuth": [] } ], "tags": [ { "name": "Authentication", "description": "Inspect the API key in use" }, { "name": "Payments", "description": "Create and manage payments" }, { "name": "Withdrawals", "description": "Create and manage withdrawals" }, { "name": "Products", "description": "Manage product catalog" }, { "name": "Customers", "description": "Manage customer records" }, { "name": "Documents", "description": "Generate receipts and statements" }, { "name": "Balance", "description": "Inspect the current account balance" } ], "paths": { "/v1/auth/self": { "get": { "operationId": "getApiKeySelf", "summary": "Inspect the current API key", "description": "Return metadata about the API key currently being used: id, name, environment, scopes, IP allowlist, last-used timestamp and expiration. Useful for verifying key configuration without storing it client-side.", "tags": [ "Authentication" ], "responses": { "200": { "description": "API key metadata.", "content": { "application/json": { "schema": { "type": "object", "properties": { "apiKey": { "$ref": "#/components/schemas/ApiKeySelf" } } }, "example": { "apiKey": { "id": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "name": "Backend integration", "description": null, "environment": "PRODUCTION", "prefix": "ypk_live_ab12cd34ef", "scopes": [ "payments:write", "payments:read", "withdrawals:write", "withdrawals:read" ], "ipAllowlist": [ "203.0.113.10" ], "rateLimitPerMinute": 600, "expiresAt": null, "lastUsedAt": "2026-05-03T14:21:08.443Z", "createdAt": "2026-04-12T09:00:00.000Z" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/payments": { "post": { "operationId": "createPayment", "summary": "Create a payment", "description": "Create a new payment. Supports PIX, direct credit card charges for headless single-method CARD flows, hosted card checkout, and boleto. Requires an idempotency key.", "tags": [ "Payments" ], "parameters": [ { "$ref": "#/components/parameters/IdempotencyKey" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePaymentRequest" }, "example": { "amount": 49.9, "methods": [ "PIX" ], "currency": "BRL", "mode": "headless", "description": "Order #1234", "externalId": "order-1234", "expiresInMinutes": 30, "customer": { "name": "Pedro Álvares Cabral", "email": "user@example.com", "phone": "+5511999999999", "document": "12345678900" }, "metadata": { "order_id": "1234", "source": "website" } }, "examples": { "headlessPix": { "summary": "Headless PIX payment", "value": { "amount": 49.9, "methods": [ "PIX" ], "currency": "BRL", "mode": "headless", "description": "Order #1234", "externalId": "order-1234", "expiresInMinutes": 30, "customer": { "name": "Pedro Álvares Cabral", "email": "user@example.com", "phone": "+5511999999999", "document": "12345678900" }, "metadata": { "order_id": "1234", "source": "website" } } }, "headlessCardDirect": { "summary": "Headless direct credit CARD payment", "value": { "amount": 149.9, "methods": [ "CARD" ], "currency": "BRL", "mode": "headless", "description": "Order #5678", "externalId": "order-5678", "customer": { "name": "Pedro Álvares Cabral", "email": "user@example.com", "phone": "+5511999999999", "document": "12345678900" }, "card": { "number": "4111111111111111", "expiryMonth": "12", "expiryYear": "2030", "ccv": "123", "installments": 1, "remoteIp": "198.51.100.23", "holderInfo": { "postalCode": "01310930", "addressNumber": "100", "phone": "+5511999999999" } } } } } } } }, "responses": { "201": { "description": "Payment created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePaymentResponse" }, "examples": { "headlessPix": { "summary": "Headless PIX payment", "value": { "payment": { "id": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "txId": "PAY3f2a8b9c4e6d1f5a7b3c8d2e9f4a6b1c", "amount": 49.9, "feeAmount": 2.99, "netAmount": 46.91, "status": "NEW", "paymentMethod": "PIX", "currency": "BRL", "description": "Order #1234", "expiresAt": "2026-05-03T15:00:00.000Z", "createdAt": "2026-05-03T14:30:00.000Z", "methodData": { "type": "PIX", "pixCopyPaste": "00020126...", "qrCodeBase64": "data:image/png;base64,...", "qrCodeUrl": null } } } }, "headlessCardDirect": { "summary": "Headless direct credit CARD payment", "value": { "payment": { "id": "8f3c0a85-7c29-4e3a-9e07-5b1a7b6d1234", "txId": "PAYa1b2c3d4e5f60718293a4b5c6d7e8f90", "amount": 149.9, "feeAmount": 8.25, "netAmount": 141.65, "status": "PROCESSING", "paymentMethod": "CARD", "currency": "BRL", "description": "Order #5678", "expiresAt": "2026-05-03T15:00:00.000Z", "createdAt": "2026-05-03T14:30:00.000Z", "methodData": { "type": "CARD", "captureType": "DIRECT", "cardSubtype": "CREDIT", "brand": "VISA", "last4Digits": "1111", "installments": 1 } } } }, "hostedCheckout": { "summary": "Hosted multi-method checkout", "value": { "payment": { "id": "fa1c0a85-7c29-4e3a-9e07-5b1a7b6d1234", "txId": "PAY9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e", "amount": 49.9, "feeAmount": 2.99, "netAmount": 46.91, "status": "PENDING_METHOD_SELECTION", "methods": [ "PIX", "CARD" ], "currency": "BRL", "checkoutUrl": "https://checkout.yuvexpay.com/checkout/fa1c0a85-7c29-4e3a-9e07-5b1a7b6d1234", "expiresAt": "2026-05-03T15:00:00.000Z", "isSandbox": true, "createdAt": "2026-05-03T14:30:00.000Z" } } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "409": { "$ref": "#/components/responses/IdempotencyConflict" }, "429": { "$ref": "#/components/responses/RateLimited" } } }, "get": { "operationId": "listPayments", "summary": "List payments", "description": "Retrieve a paginated list of payments with optional filters.", "tags": [ "Payments" ], "parameters": [ { "name": "status", "in": "query", "schema": { "$ref": "#/components/schemas/PaymentStatus" }, "description": "Filter by payment status." }, { "name": "method", "in": "query", "schema": { "$ref": "#/components/schemas/PaymentMethod" }, "description": "Filter by payment method." }, { "name": "startDate", "in": "query", "schema": { "type": "string", "format": "date-time" }, "description": "Start of date range (ISO 8601)." }, { "name": "endDate", "in": "query", "schema": { "type": "string", "format": "date-time" }, "description": "End of date range (ISO 8601)." }, { "name": "minAmount", "in": "query", "schema": { "type": "number" }, "description": "Minimum payment amount." }, { "name": "maxAmount", "in": "query", "schema": { "type": "number" }, "description": "Maximum payment amount." }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Payments listed.", "content": { "application/json": { "schema": { "type": "object", "properties": { "payments": { "type": "array", "items": { "$ref": "#/components/schemas/Payment" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/payments/{paymentId}": { "get": { "operationId": "getPayment", "summary": "Get a payment", "description": "Retrieve a single payment by its ID.", "tags": [ "Payments" ], "parameters": [ { "name": "paymentId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The payment ID." } ], "responses": { "200": { "description": "Payment found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "payment": { "$ref": "#/components/schemas/Payment" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/payments/txid/{txId}": { "get": { "operationId": "getPaymentByTxId", "summary": "Get payment by transaction ID", "description": "Retrieve a payment using the YuvexPay transaction ID (`txId`) instead of the UUID.", "tags": [ "Payments" ], "parameters": [ { "name": "txId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "The YuvexPay transaction ID." } ], "responses": { "200": { "description": "Payment found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "payment": { "$ref": "#/components/schemas/Payment" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/payments/{paymentId}/boleto": { "get": { "operationId": "getPaymentBoleto", "summary": "Download boleto PDF", "description": "Download the boleto PDF for a payment whose method is BOLETO. The response is the PDF binary; clients should treat the body as `application/pdf`.", "tags": [ "Payments" ], "parameters": [ { "name": "paymentId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The payment ID." } ], "responses": { "200": { "description": "Boleto PDF.", "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/payments/{paymentId}/refund": { "post": { "operationId": "refundPayment", "summary": "Refund a payment", "description": "Issue a full or partial refund for a paid payment. Requires an idempotency key.", "tags": [ "Payments" ], "parameters": [ { "name": "paymentId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The payment ID to refund." }, { "$ref": "#/components/parameters/IdempotencyKey" } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RefundPaymentRequest" }, "example": { "amount": 25, "reason": "Customer requested refund" } } } }, "responses": { "200": { "description": "Refund created.", "content": { "application/json": { "schema": { "type": "object", "properties": { "refund": { "$ref": "#/components/schemas/Refund" } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { "$ref": "#/components/responses/IdempotencyConflict" } } } }, "/v1/payments/{paymentId}/refunds": { "get": { "operationId": "listRefunds", "summary": "List refunds", "description": "Retrieve all refunds for a specific payment.", "tags": [ "Payments" ], "parameters": [ { "name": "paymentId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The payment ID." } ], "responses": { "200": { "description": "Refunds listed.", "content": { "application/json": { "schema": { "type": "object", "properties": { "refunds": { "type": "array", "items": { "$ref": "#/components/schemas/Refund" } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/withdrawals": { "post": { "operationId": "createWithdrawal", "summary": "Create a withdrawal", "description": "Create a PIX withdrawal to transfer BRL funds from your YuvexPay balance. Requires an idempotency key.\n\nThe PIX key type (CPF, CNPJ, e-mail, phone, or random key) is detected automatically from `destination.pixKey` — you do not need to declare it. `INVALID_PIX_KEY` covers format only. Whether the key exists on the PIX network is not checked before acceptance: depending on when the network rejects, a well-formed key that belongs to nobody surfaces either as a synchronous `400 PROVIDER_ERROR` on this call or later as a `WITHDRAWAL_FAILED` webhook.\n\nThe creditor document sent to the PIX network is resolved in three steps, in order: (1) `destination.recipientDocument` when you send it; (2) the PIX key itself when `destination.pixKey` is a CPF or CNPJ key; (3) your own verified tax id — company CNPJ for business accounts, owner CPF for individual accounts. Step 3 is a fallback, not a default: if you pay a third party on an e-mail, phone or random key and omit `destination.recipientDocument`, the network is told your own document is the creditor, and the payout may be rejected downstream by the receiving institution.\n\nWithdrawals may be blocked outside business hours or if your account has restrictions.\n\n`creditorDocumentSource` is a dashboard-only field and is not accepted here: sending it returns `400 CREDITOR_DOCUMENT_SOURCE_NOT_SUPPORTED` rather than being silently ignored. Use `destination.recipientDocument` to put a specific document on the payout.", "tags": [ "Withdrawals" ], "parameters": [ { "$ref": "#/components/parameters/IdempotencyKey" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWithdrawalRequest" }, "examples": { "pix": { "summary": "Third-party payout with the holder document", "value": { "amount": 500, "method": "PIX", "currency": "BRL", "destination": { "type": "pix", "pixKey": "ana@example.com", "recipientDocument": "529.982.247-25" }, "description": "Weekly payout" } }, "pixDocumentKey": { "summary": "CPF key — the creditor document is derived from the key", "value": { "amount": 500, "method": "PIX", "currency": "BRL", "destination": { "type": "pix", "pixKey": "52998224725" } } }, "pixOwnAccount": { "summary": "Payout to your own account — no document needed", "value": { "amount": 500, "method": "PIX", "currency": "BRL", "destination": { "type": "pix", "pixKey": "0f1e2d3c-4b5a-6978-8796-a5b4c3d2e1f0" } } } } } } }, "responses": { "201": { "description": "Withdrawal created.", "content": { "application/json": { "schema": { "type": "object", "properties": { "withdrawal": { "$ref": "#/components/schemas/WithdrawalCreated" } } } } } }, "400": { "description": "Validation failed, or the PIX network rejected the payout deterministically. Withdrawal-specific codes: `INVALID_PIX_KEY`, `INVALID_RECIPIENT_DOCUMENT`, `RECIPIENT_DOCUMENT_MISMATCH`, `CREDITOR_DOCUMENT_SOURCE_NOT_SUPPORTED`, `WITHDRAWAL_BELOW_MINIMUM`, `WITHDRAWAL_LIMIT_EXCEEDED`, `WITHDRAWAL_BELOW_PROVIDER_MINIMUM`, `WITHDRAWAL_PIX_KEY_BLOCKED`, `BLOCKLIST_DENIED`, `INSUFFICIENT_BALANCE`, `WITHDRAWAL_RECIPIENT_DOCUMENT_MISMATCH`, `PROVIDER_ERROR`. Note that a missing `X-Idempotency-Key` is rejected before the error envelope is applied and returns the flat shape `{ statusCode, error, message, code }` with code `IDEMPOTENCY_KEY_REQUIRED`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "invalidRecipientDocument": { "summary": "recipientDocument is not a valid CPF or CNPJ", "value": { "error": { "code": "INVALID_RECIPIENT_DOCUMENT", "message": "O CPF/CNPJ do destinatário é inválido. Envie um CPF de 11 dígitos ou um CNPJ de 14 dígitos." } } }, "recipientDocumentMismatch": { "summary": "recipientDocument disagrees with a CPF or CNPJ pixKey", "value": { "error": { "code": "RECIPIENT_DOCUMENT_MISMATCH", "message": "O CPF/CNPJ do destinatário não corresponde à chave PIX informada." } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "description": "Withdrawals blocked for this account. Codes: `WITHDRAWALS_BLOCKED`, `VERIFICATION_REQUIRED`, `CREDITOR_DOCUMENT_UNAVAILABLE`, `SANDBOX_COMPANY_PRODUCTION_BLOCKED`, `CREDITOR_DOCUMENT_OVERRIDE_FORBIDDEN`, `WITHDRAWAL_PROVIDER_OVERRIDE_FORBIDDEN`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "$ref": "#/components/responses/IdempotencyConflict" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "description": "Code `CREDITOR_DOCUMENT_UNREADABLE`: your tax id is on file but could not be read, so no creditor document could be resolved. No money moved. Unlike a generic `INTERNAL_ERROR` this is deterministic — retrying the identical request fails the same way. Send `destination.recipientDocument` to unblock the payout, or contact support.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "The withdrawal never reached the PIX network and no money moved. Codes: `WITHDRAWAL_PROVIDER_UNAVAILABLE`, `WITHDRAWAL_TEMPORARILY_UNAVAILABLE`, `WITHDRAWAL_PROCESSING_FAILED`. Safe to retry with a new idempotency key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "get": { "operationId": "listWithdrawals", "summary": "List withdrawals", "description": "Retrieve a paginated list of withdrawals with optional filters.", "tags": [ "Withdrawals" ], "parameters": [ { "name": "status", "in": "query", "schema": { "$ref": "#/components/schemas/WithdrawalStatus" }, "description": "Filter by withdrawal status." }, { "name": "method", "in": "query", "schema": { "type": "string", "enum": [ "PIX" ] }, "description": "Filter by withdrawal method. Public withdrawals currently support PIX only." }, { "name": "currency", "in": "query", "schema": { "type": "string", "enum": [ "BRL" ] }, "description": "Filter by currency. Public withdrawals currently support BRL only." }, { "name": "startDate", "in": "query", "schema": { "type": "string", "format": "date-time" }, "description": "Start of date range (ISO 8601)." }, { "name": "endDate", "in": "query", "schema": { "type": "string", "format": "date-time" }, "description": "End of date range (ISO 8601)." }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Withdrawals listed.", "content": { "application/json": { "schema": { "type": "object", "properties": { "withdrawals": { "type": "array", "items": { "$ref": "#/components/schemas/Withdrawal" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/withdrawals/limits": { "get": { "operationId": "getWithdrawalLimits", "summary": "Get withdrawal limits", "description": "Retrieve your current withdrawal limits, usage and fee. A reduced \"nightly\" limit applies outside the `dayStartHour`–`dayEndHour` window (BRT); `isNightTime` tells you which one `currentLimit` currently reflects. Day and night share one `used` counter for the whole Brazilian calendar day.", "tags": [ "Withdrawals" ], "responses": { "200": { "description": "Limits retrieved.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WithdrawalLimits" }, "example": { "currentLimit": 5000, "used": 1200, "remaining": 3800, "isNightTime": false, "fee": 2.49, "dayStartHour": 6, "dayEndHour": 22, "minAmount": 10, "perTransactionLimit": 5000 } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/withdrawals/{withdrawalId}": { "get": { "operationId": "getWithdrawal", "summary": "Get a withdrawal", "description": "Retrieve a single withdrawal by its ID.", "tags": [ "Withdrawals" ], "parameters": [ { "name": "withdrawalId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The withdrawal ID." } ], "responses": { "200": { "description": "Withdrawal found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "withdrawal": { "$ref": "#/components/schemas/Withdrawal" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/balance": { "get": { "operationId": "getBalance", "summary": "Get account balance", "description": "Return the current BRL balance for the company associated with the API key. The endpoint takes no parameters — the company is always derived from the API key. The three balance buckets are independent and additive: `available` (withdrawable now), `frozen` (reserved for in-flight withdrawals, refunds, or operational holds — not currently withdrawable), and `held` (confirmed but pending settlement). The total account balance is `available + frozen + held`. A sandbox API key always returns zeros, since sandbox transactions do not accumulate to a real balance.", "tags": [ "Balance" ], "responses": { "200": { "description": "Balance retrieved.", "content": { "application/json": { "schema": { "type": "object", "properties": { "balance": { "$ref": "#/components/schemas/Balance" } }, "required": [ "balance" ] }, "examples": { "noFrozenFunds": { "summary": "Production balance with nothing frozen", "value": { "balance": { "available": 13.45, "frozen": 0, "held": 10.2, "total": 23.65, "currency": "BRL", "environment": "PRODUCTION", "updatedAt": "2026-05-06T12:00:00.000Z" } } }, "withFrozenFunds": { "summary": "Production balance with funds frozen for an in-flight withdrawal", "value": { "balance": { "available": 6.47, "frozen": 6.99, "held": 10.2, "total": 23.66, "currency": "BRL", "environment": "PRODUCTION", "updatedAt": "2026-05-06T12:00:00.000Z" } } }, "sandboxKey": { "summary": "Sandbox API key — always returns zeros", "value": { "balance": { "available": 0, "frozen": 0, "held": 0, "total": 0, "currency": "BRL", "environment": "SANDBOX", "updatedAt": null } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/products": { "post": { "operationId": "createProduct", "summary": "Create a product", "description": "Create a new product in your catalog. Products can be referenced when creating payments to pre-fill amount and description.", "tags": [ "Products" ], "parameters": [ { "$ref": "#/components/parameters/IdempotencyKey" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateProductRequest" }, "example": { "name": "Premium Plan", "description": "Monthly premium subscription", "amount": 99.9, "externalId": "plan-premium", "metadata": { "tier": "premium", "billing": "monthly" } } } } }, "responses": { "201": { "description": "Product created.", "content": { "application/json": { "schema": { "type": "object", "properties": { "product": { "$ref": "#/components/schemas/Product" } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "409": { "$ref": "#/components/responses/IdempotencyConflict" } } }, "get": { "operationId": "listProducts", "summary": "List products", "description": "Retrieve a paginated list of products with optional search and filters.", "tags": [ "Products" ], "parameters": [ { "name": "search", "in": "query", "schema": { "type": "string" }, "description": "Search products by name." }, { "name": "isActive", "in": "query", "schema": { "type": "string", "enum": [ "true", "false" ] }, "description": "Filter by active status." }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Products listed.", "content": { "application/json": { "schema": { "type": "object", "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/products/{productId}": { "get": { "operationId": "getProduct", "summary": "Get a product", "description": "Retrieve a single product by its ID.", "tags": [ "Products" ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The product ID." } ], "responses": { "200": { "description": "Product found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "product": { "$ref": "#/components/schemas/Product" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } }, "patch": { "operationId": "updateProduct", "summary": "Update a product", "description": "Update one or more fields of an existing product.", "tags": [ "Products" ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The product ID." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateProductRequest" } } } }, "responses": { "200": { "description": "Product updated.", "content": { "application/json": { "schema": { "type": "object", "properties": { "product": { "$ref": "#/components/schemas/Product" }, "message": { "type": "string" } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteProduct", "summary": "Deactivate a product", "description": "Soft-delete a product by setting it as inactive. The product data is preserved but it can no longer be used in new payments.", "tags": [ "Products" ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The product ID." } ], "responses": { "200": { "description": "Product deactivated.", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/products/{productId}/image": { "post": { "operationId": "uploadProductImage", "summary": "Upload product image", "description": "Upload (or replace) the image for a product. Send a single file as `multipart/form-data`. Accepted formats: PNG, JPEG. Maximum 10 MB.", "tags": [ "Products" ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The product ID." } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "file": { "type": "string", "format": "binary", "description": "Image file (PNG or JPEG, max 10 MB)." } }, "required": [ "file" ] } } } }, "responses": { "200": { "description": "Image uploaded.", "content": { "application/json": { "schema": { "type": "object", "properties": { "product": { "$ref": "#/components/schemas/Product" } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteProductImage", "summary": "Delete product image", "description": "Remove the image associated with a product.", "tags": [ "Products" ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The product ID." } ], "responses": { "200": { "description": "Image removed.", "content": { "application/json": { "schema": { "type": "object", "properties": { "product": { "$ref": "#/components/schemas/Product" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/products/external/{externalId}": { "get": { "operationId": "getProductByExternalId", "summary": "Get product by external ID", "description": "Retrieve a product using your own external ID instead of the YuvexPay UUID.", "tags": [ "Products" ], "parameters": [ { "name": "externalId", "in": "path", "required": true, "schema": { "type": "string", "maxLength": 100 }, "description": "Your external product ID." } ], "responses": { "200": { "description": "Product found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "product": { "$ref": "#/components/schemas/Product" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/customers": { "post": { "operationId": "createCustomer", "summary": "Create a customer", "description": "Create a new customer record. Customers can be associated with payments for tracking and recurring billing.", "tags": [ "Customers" ], "parameters": [ { "$ref": "#/components/parameters/IdempotencyKey" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCustomerRequest" }, "example": { "name": "Pedro Álvares Cabral", "email": "user@example.com", "document": "12345678900", "documentType": "CPF", "phone": "+5511999999999", "externalId": "cust-001", "metadata": { "plan": "premium" } } } } }, "responses": { "201": { "description": "Customer created.", "content": { "application/json": { "schema": { "type": "object", "properties": { "customer": { "$ref": "#/components/schemas/Customer" } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "409": { "$ref": "#/components/responses/IdempotencyConflict" } } }, "get": { "operationId": "listCustomers", "summary": "List customers", "description": "Retrieve a paginated list of customers with optional search and filters.", "tags": [ "Customers" ], "parameters": [ { "name": "search", "in": "query", "schema": { "type": "string" }, "description": "Search by name, email, or document." }, { "name": "isActive", "in": "query", "schema": { "type": "string", "enum": [ "true", "false" ] }, "description": "Filter by active status." }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Customers listed.", "content": { "application/json": { "schema": { "type": "object", "properties": { "customers": { "type": "array", "items": { "$ref": "#/components/schemas/Customer" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/customers/{customerId}": { "get": { "operationId": "getCustomer", "summary": "Get a customer", "description": "Retrieve a single customer by their ID, including metadata.", "tags": [ "Customers" ], "parameters": [ { "name": "customerId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "The customer's prefixed identifier (e.g., `cus_ab12cd34ef`). This is the merchant-facing slug, not the internal UUID." } ], "responses": { "200": { "description": "Customer found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "customer": { "$ref": "#/components/schemas/CustomerDetail" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } }, "patch": { "operationId": "updateCustomer", "summary": "Update a customer", "description": "Update one or more fields of an existing customer.", "tags": [ "Customers" ], "parameters": [ { "name": "customerId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "The customer's prefixed identifier (e.g., `cus_ab12cd34ef`). This is the merchant-facing slug, not the internal UUID." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateCustomerRequest" } } } }, "responses": { "200": { "description": "Customer updated.", "content": { "application/json": { "schema": { "type": "object", "properties": { "customer": { "$ref": "#/components/schemas/Customer" }, "message": { "type": "string" } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteCustomer", "summary": "Deactivate a customer", "description": "Soft-delete a customer by setting them as inactive. Customer data is preserved.", "tags": [ "Customers" ], "parameters": [ { "name": "customerId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "The customer's prefixed identifier (e.g., `cus_ab12cd34ef`). This is the merchant-facing slug, not the internal UUID." } ], "responses": { "200": { "description": "Customer deactivated.", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/documents/payments/{paymentId}/receipt": { "get": { "operationId": "getPaymentReceipt", "summary": "Get payment receipt", "description": "Render a receipt for a payment. Requires the `documents:read` scope. The payment must be `PAID` or `REFUNDED`; any other status returns `400 RECEIPT_NOT_AVAILABLE`. The lookup is scoped to the environment of the key: a production key resolves only production payments and a sandbox key only sandbox ones, so an id from the other environment returns `404` even when it belongs to your own account.", "tags": [ "Documents" ], "parameters": [ { "name": "paymentId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The payment ID." }, { "name": "format", "in": "query", "schema": { "type": "string", "enum": [ "html", "text" ], "default": "html" }, "description": "Output format." } ], "responses": { "200": { "description": "The rendered receipt. The response body is the document itself — HTML with `Content-Type: text/html`, or plain text with `Content-Type: text/plain`. It is not JSON, and it is not a PDF.", "content": { "text/html": { "schema": { "type": "string" } }, "text/plain": { "schema": { "type": "string" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "400": { "description": "The payment is not `PAID` or `REFUNDED`. Code: `RECEIPT_NOT_AVAILABLE`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/v1/documents/withdrawals/{withdrawalId}/receipt": { "get": { "operationId": "getWithdrawalReceipt", "summary": "Get withdrawal receipt", "description": "Render a receipt for a completed withdrawal. Requires the `documents:read` scope. The withdrawal must be in status `COMPLETED`; any other status returns `400 RECEIPT_NOT_AVAILABLE`. The lookup is scoped to the environment of the key: a production key resolves only production withdrawals and a sandbox key only sandbox ones, so an id from the other environment returns `404` even when it belongs to your own account.", "tags": [ "Documents" ], "parameters": [ { "name": "withdrawalId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The withdrawal ID." }, { "name": "format", "in": "query", "schema": { "type": "string", "enum": [ "html", "text" ], "default": "html" }, "description": "Output format." } ], "responses": { "200": { "description": "The rendered receipt. The response body is the document itself — HTML with `Content-Type: text/html`, or plain text with `Content-Type: text/plain`. It is not JSON, and it is not a PDF.", "content": { "text/html": { "schema": { "type": "string" } }, "text/plain": { "schema": { "type": "string" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "400": { "description": "The withdrawal is not `COMPLETED`. Code: `RECEIPT_NOT_AVAILABLE`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/v1/documents/statements": { "get": { "operationId": "getStatement", "summary": "Get account statement", "description": "Generate an account statement for a date range. Maximum range is 90 days. The statement is built from the real-money ledger, which sandbox transactions never touch, so a sandbox key always receives an empty statement with zero balances.", "tags": [ "Documents" ], "parameters": [ { "name": "startDate", "in": "query", "required": true, "schema": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }, "description": "Start date in `YYYY-MM-DD` format.", "example": "2026-01-01" }, { "name": "endDate", "in": "query", "required": true, "schema": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }, "description": "End date in `YYYY-MM-DD` format.", "example": "2026-01-31" }, { "name": "format", "in": "query", "schema": { "type": "string", "enum": [ "html", "text" ], "default": "html" }, "description": "Output format." } ], "responses": { "200": { "description": "Statement generated.", "content": { "application/json": { "schema": { "type": "object", "properties": { "content": { "type": "string", "description": "The statement content in the requested format." } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } } }, "webhooks": { "PAYMENT_CONFIRMED": { "post": { "summary": "Payment confirmed", "description": "A card or boleto payment was authorized by the issuer. The funds are a held receivable, not yet a balance credit.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "txId": { "type": "string", "description": "Payment transaction id." }, "status": { "type": "string", "enum": [ "CONFIRMED" ], "description": "Always `CONFIRMED`." }, "method": { "type": "string", "description": "Payment method, e.g. `CARD` or `BOLETO`." }, "source": { "type": "string", "description": "Present and equal to `reconciler` when the confirmation came from reconciliation rather than the provider callback." } } }, "example": { "txId": "PAY3F2A8B9C4E6D1F5A7B3C8D2E9F4A6B1C", "status": "CONFIRMED", "method": "CARD" } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "PAYMENT_PAID": { "post": { "summary": "Payment paid", "description": "Funds were received and the net amount was credited to your balance.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Payment id." }, "txId": { "type": "string", "description": "Payment transaction id." }, "status": { "type": "string", "enum": [ "PAID" ], "description": "Always `PAID`." }, "amount": { "type": "number", "description": "Gross amount in BRL." }, "paidAt": { "type": [ "string", "null" ], "format": "date-time", "description": "Settlement timestamp. `null` when not recorded." }, "endToEndId": { "type": [ "string", "null" ], "description": "PIX end-to-end identifier, safe for bank reconciliation. `null` when the network did not supply one." }, "payer": { "oneOf": [ { "$ref": "#/components/schemas/WebhookPayerBlock" }, { "type": "null" } ], "description": "Payer block, or `null` when every field would be null." } } }, "example": { "id": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "txId": "PAY3F2A8B9C4E6D1F5A7B3C8D2E9F4A6B1C", "status": "PAID", "amount": 49.9, "paidAt": "2026-06-06T12:00:00.000Z", "endToEndId": "E000000022026060612000abc1234def", "payer": { "name": "Maria Silva", "document": "39053344705", "documentType": "CPF", "institutionName": "Banco Example S.A.", "institutionIspb": "00000000" } } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "PAYMENT_EXPIRED": { "post": { "summary": "Payment expired", "description": "The payment expired before being completed. For boleto this is not always terminal — a boleto paid on or shortly after its due date can settle late, producing a `PAYMENT_PAID` afterwards for the same charge.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Payment id. Present only on some expiry paths." }, "txId": { "type": "string", "description": "Payment transaction id." }, "status": { "type": "string", "enum": [ "EXPIRED" ], "description": "Always `EXPIRED`." }, "amount": { "type": "number", "description": "Gross amount in BRL. Present only on some expiry paths." } } }, "example": { "txId": "PAY3F2A8B9C4E6D1F5A7B3C8D2E9F4A6B1C", "status": "EXPIRED" } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "PAYMENT_CANCELLED": { "post": { "summary": "Payment cancelled", "description": "The payment was cancelled or definitively failed before completing.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Payment id. Present only on some cancellation paths." }, "txId": { "type": "string", "description": "Payment transaction id." }, "status": { "type": "string", "enum": [ "CANCELLED" ], "description": "Always `CANCELLED`." }, "amount": { "type": "number", "description": "Gross amount in BRL. Present only on some cancellation paths." }, "reason": { "type": "string", "description": "Provider-supplied reason, when available." } } }, "example": { "txId": "PAY3F2A8B9C4E6D1F5A7B3C8D2E9F4A6B1C", "status": "CANCELLED", "reason": "payment_failed" } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "PAYMENT_REFUNDED": { "post": { "summary": "Payment refunded", "description": "A refund completed for this payment. Note this body identifies the payment as `paymentId` and carries no `txId`.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "paymentId": { "type": "string", "format": "uuid", "description": "Payment id." }, "refundId": { "type": "string", "format": "uuid", "description": "Refund id." }, "amount": { "type": "number", "description": "Refunded amount in BRL." }, "source": { "type": "string", "description": "Present when the refund was driven by a PIX MED; names the settling provider." } } }, "example": { "paymentId": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "refundId": "2c7f1a90-55b8-4a1e-9d33-6f0b2e4c7a11", "amount": 49.9 } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "PAYMENT_REFUND_FAILED": { "post": { "summary": "Payment refund failed", "description": "A refund attempt failed. This body identifies the payment as `paymentId` and carries no `txId`.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "paymentId": { "type": "string", "format": "uuid", "description": "Payment id." }, "refundId": { "type": "string", "format": "uuid", "description": "Refund id." }, "reason": { "type": "string", "description": "Failure reason, safe to surface to operators." } } }, "example": { "paymentId": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "refundId": "2c7f1a90-55b8-4a1e-9d33-6f0b2e4c7a11", "reason": "Refund failed at provider [E42]" } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "PAYMENT_CHARGEBACK": { "post": { "summary": "Payment chargeback", "description": "A chargeback was opened or processed. These two fields are the entire body — there is no id, amount or reason.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "txId": { "type": "string", "description": "Payment transaction id." }, "status": { "type": "string", "enum": [ "CHARGEBACK" ], "description": "Always `CHARGEBACK`." } } }, "example": { "txId": "PAY3F2A8B9C4E6D1F5A7B3C8D2E9F4A6B1C", "status": "CHARGEBACK" } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "MED_RECEIVED": { "post": { "summary": "MED received", "description": "A PIX MED (special refund) was opened by the payer's bank.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "medId": { "type": "string", "format": "uuid", "description": "MED id." }, "paymentId": { "type": "string", "format": "uuid", "description": "Payment id." }, "providerMedId": { "type": "string", "description": "Provider MED id, when supplied." }, "amount": { "type": "number", "description": "Disputed amount in BRL." }, "frozenAmount": { "type": "number", "description": "Amount frozen on your balance in BRL." }, "shortfall": { "type": "number", "description": "Amount that could not be frozen because of insufficient balance." } } }, "example": { "medId": "b41d7e02-9c3f-4a58-8e12-0d5a6f9b3c74", "paymentId": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "amount": 49.9, "frozenAmount": 30, "shortfall": 19.9 } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "MED_RESOLVED": { "post": { "summary": "MED resolved", "description": "A PIX MED reached a terminal outcome.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "medId": { "type": "string", "format": "uuid", "description": "MED id." }, "paymentId": { "type": "string", "format": "uuid", "description": "Payment id." }, "resolution": { "type": "string", "enum": [ "ACCEPTED_BY_USER", "REJECTED_BY_USER", "ACCEPTED_BY_PSP", "REJECTED_BY_PSP", "CANCELLED_BY_PSP" ], "description": "How the MED ended. Switch on this non-exhaustively — new outcomes can be added." }, "amount": { "type": "number", "description": "Disputed amount in BRL. Absent on the provider-lifecycle paths, which omit the key entirely." }, "refundId": { "type": "string", "format": "uuid", "description": "Refund id, when the resolution produced a refund." }, "shortfall": { "type": "number", "description": "Unfrozen shortfall, when applicable." }, "source": { "type": "string", "description": "Names the path that resolved the MED when it was not a direct merchant action, e.g. `onlyup`, `unoip`, `merchant_refund` or `-reconciler`." } }, "required": [ "medId", "paymentId", "resolution" ] }, "example": { "medId": "b41d7e02-9c3f-4a58-8e12-0d5a6f9b3c74", "paymentId": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "amount": 49.9, "resolution": "ACCEPTED_BY_USER" } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "WITHDRAWAL_REQUESTED": { "post": { "summary": "Withdrawal requested", "description": "A withdrawal was created and handed to the PIX network.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "withdrawalId": { "type": "string", "format": "uuid", "description": "Withdrawal id." }, "txId": { "type": "string", "description": "Withdrawal transaction id." }, "netAmount": { "type": "number", "description": "Amount sent to the recipient in BRL, excluding the fee." }, "status": { "type": "string", "enum": [ "PENDING" ], "description": "Always `PENDING`." } } }, "example": { "withdrawalId": "9a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", "txId": "WIT1746280868443K3F9Q", "netAmount": 100, "status": "PENDING" } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "WITHDRAWAL_SENT": { "post": { "summary": "Withdrawal sent", "description": "The recipient received the money. `failureReason` is absent from this body entirely, not `null`.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "withdrawalId": { "type": "string", "format": "uuid", "description": "Withdrawal id." }, "txId": { "type": "string", "description": "Withdrawal transaction id." }, "netAmount": { "type": "number", "description": "Amount sent to the recipient in BRL, excluding the fee." }, "status": { "type": "string", "enum": [ "COMPLETED" ], "description": "Always `COMPLETED`." }, "endToEndId": { "type": [ "string", "null" ], "description": "PIX end-to-end identifier. `null` when the network did not assign one." }, "recipient": { "oneOf": [ { "$ref": "#/components/schemas/WebhookRecipientBlock" }, { "type": "null" } ], "description": "Recipient block, or `null` when every field would be null." } } }, "example": { "withdrawalId": "9a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", "txId": "WIT1746280868443K3F9Q", "netAmount": 100, "status": "COMPLETED", "endToEndId": "E000000022026060612050def5678abc", "recipient": { "name": "João Souza", "document": "52998224725", "institutionName": "Banco Example S.A.", "institutionIspb": "00000000", "branch": "0001", "account": "123456" } } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "WITHDRAWAL_FAILED": { "post": { "summary": "Withdrawal failed", "description": "The network rejected the transfer and the funds were returned to your balance. This also carries provider-driven reversals.\n\nThe HTTP body is this object exactly — there is no envelope and no `type` field. The event name is delivered in the `X-Webhook-Event` header. Most optional fields arrive as an explicit `null`; a few keys are dropped from the JSON entirely when unset, and those are marked as such below. Read values with a null-safe accessor rather than testing key presence.", "tags": [ "Webhooks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "withdrawalId": { "type": "string", "format": "uuid", "description": "Withdrawal id." }, "txId": { "type": "string", "description": "Withdrawal transaction id." }, "netAmount": { "type": "number", "description": "Amount that was attempted, in BRL." }, "status": { "type": "string", "enum": [ "FAILED" ], "description": "Always `FAILED`." }, "endToEndId": { "type": [ "string", "null" ], "description": "PIX end-to-end identifier. `null` when the network did not assign one." }, "recipient": { "oneOf": [ { "$ref": "#/components/schemas/WebhookRecipientBlock" }, { "type": "null" } ], "description": "Recipient block, or `null` when every field would be null." }, "failureReason": { "type": "string", "description": "Merchant-safe failure description. Never `null` — the key is absent when the network supplied no usable reason text." } } }, "example": { "withdrawalId": "9a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d", "txId": "WIT1746280868443K3F9Q", "netAmount": 100, "status": "FAILED", "endToEndId": null, "recipient": null, "failureReason": "A chave PIX informada não existe ou já não está ativa. Confira a chave e tente de novo." } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } } }, "components": { "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "description": "YuvexPay API key. Include as `Authorization: Bearer ypk___` where `` is `test` (sandbox) or `live` (production). Create and manage keys in the dashboard under **Settings > API Keys**." } }, "parameters": { "IdempotencyKey": { "name": "X-Idempotency-Key", "in": "header", "required": true, "schema": { "type": "string", "maxLength": 100 }, "description": "A unique string to ensure the request is processed only once. Must be unique per request. Valid for 24 hours." }, "Page": { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 }, "description": "Page number for pagination." }, "Limit": { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }, "description": "Number of items per page (1-100)." } }, "schemas": { "PaymentMethod": { "type": "string", "enum": [ "PIX", "CARD", "BOLETO" ], "description": "Available public payment methods." }, "Currency": { "type": "string", "enum": [ "BRL" ], "description": "Supported currencies." }, "PaymentStatus": { "type": "string", "enum": [ "NEW", "PENDING_METHOD_SELECTION", "PROCESSING", "CONFIRMED", "PAID", "CANCELLED", "EXPIRED", "REFUNDED", "PARTIAL_REFUND", "CHARGEBACK", "MED_FROZEN" ], "description": "Possible payment statuses." }, "WithdrawalStatus": { "type": "string", "enum": [ "PENDING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED" ], "description": "Possible withdrawal statuses." }, "TransactionStatus": { "type": "string", "enum": [ "PENDING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "REFUNDED" ], "description": "Generic transaction status." }, "WebhookEventType": { "type": "string", "enum": [ "PAYMENT_CONFIRMED", "PAYMENT_PAID", "PAYMENT_EXPIRED", "PAYMENT_CANCELLED", "PAYMENT_REFUNDED", "PAYMENT_REFUND_FAILED", "PAYMENT_CHARGEBACK", "MED_RECEIVED", "MED_RESOLVED", "WITHDRAWAL_REQUESTED", "WITHDRAWAL_SENT", "WITHDRAWAL_FAILED" ], "description": "Subscribable webhook event types. This value is delivered in the `X-Webhook-Event` header, never in the request body." }, "CreatePaymentRequest": { "type": "object", "properties": { "productId": { "type": "string", "format": "uuid", "description": "Link this payment to an existing product. If set, `amount` is optional and defaults to the product price." }, "customerId": { "type": "string", "format": "uuid", "description": "Link this payment to an existing customer. For CARD and BOLETO, the linked customer must have name and CPF/CNPJ." }, "amount": { "type": "number", "minimum": 0.01, "maximum": 1000000, "description": "Payment amount. Required unless `productId` is provided." }, "methods": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentMethod" }, "minItems": 1, "default": [ "PIX" ], "description": "Accepted payment methods. Headless payments must use exactly one method." }, "currency": { "$ref": "#/components/schemas/Currency", "default": "BRL" }, "mode": { "type": "string", "enum": [ "hosted", "headless" ], "default": "headless", "description": "hosted redirects the payer to a YuvexPay checkout page. headless returns method data directly for a single payment method. Headless CARD requires the card payload for direct credit card capture. Hosted or multi-method CARD flows continue through checkout/provider invoice URLs, where debit remains available." }, "returnUrl": { "type": "string", "format": "uri", "description": "URL to redirect the payer after payment (hosted mode)." }, "completionUrl": { "type": "string", "format": "uri", "description": "URL to redirect the payer after successful payment completion." }, "description": { "type": "string", "maxLength": 500, "description": "Payment description shown to the payer." }, "externalId": { "type": "string", "maxLength": 100, "description": "Your own reference ID (e.g., order number)." }, "expiresInMinutes": { "type": "integer", "minimum": 5, "maximum": 10080, "default": 60, "description": "Minutes until the payment expires (5-10080, i.e. up to 7 days)." }, "enforcePayerDocument": { "type": "boolean", "default": false, "description": "PIX only. Opt-in request that the charge be paid from an account whose CPF/CNPJ matches the customer's document (same-document payment). Best-effort: when the provider cannot enforce it, the charge is still created and paid normally." }, "customer": { "type": "object", "description": "Inline customer data. For CARD and BOLETO, include at least name and CPF/CNPJ unless you use customerId.", "properties": { "name": { "type": "string", "description": "Payer name. Required for CARD and BOLETO when not using customerId." }, "document": { "type": "string", "description": "CPF or CNPJ. Required for CARD and BOLETO when not using customerId." }, "email": { "type": "string", "format": "email" }, "phone": { "type": "string", "description": "Payer phone number in national or international format." } } }, "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Arbitrary key-value pairs attached to the payment." }, "passFeeToPayer": { "type": "boolean", "description": "Whether the YuvexPay fee should be added on top of `amount` and charged to the payer instead of being deducted from the merchant's net. When omitted, the company-level `passFeeToPayerDefault` setting applies. Note: payments with a resolved amount below R$1,00 are always charged to the payer regardless of this value (forced passthrough to keep merchant net positive). The final decision is reflected back as `feePassedToPayer` on the Payment object." }, "card": { "type": "object", "description": "Direct credit card charge payload. Allowed only for headless single-method CARD payments and required in that flow.", "properties": { "number": { "type": "string", "description": "Primary account number (PAN), 13-19 digits." }, "expiryMonth": { "type": "string", "description": "Expiration month as 1-2 digits." }, "expiryYear": { "type": "string", "description": "Expiration year as 4 digits." }, "ccv": { "type": "string", "description": "Card security code (3-4 digits)." }, "installments": { "type": "integer", "minimum": 1, "maximum": 12, "default": 1, "description": "Installment count for direct credit card charges." }, "remoteIp": { "type": "string", "description": "Customer IP address required by the acquiring flow." }, "holderInfo": { "type": "object", "description": "Supplemental cardholder billing data. `postalCode` and `addressNumber` are required.", "properties": { "name": { "type": "string" }, "email": { "type": "string", "format": "email" }, "document": { "type": "string", "description": "CPF or CNPJ for the cardholder." }, "postalCode": { "type": "string" }, "addressNumber": { "type": "string" }, "addressComplement": { "type": "string" }, "phone": { "type": "string" }, "mobilePhone": { "type": "string" } }, "required": [ "postalCode", "addressNumber" ] } }, "required": [ "number", "expiryMonth", "expiryYear", "ccv", "remoteIp", "holderInfo" ] }, "boleto": { "type": "object", "description": "Boleto-specific options, applied only when `BOLETO` is one of the `methods`. Fine, interest, and discount mirror the boleto configuration registered with the bank and are settled against the amount the payer actually pays.", "properties": { "dueDate": { "type": "string", "format": "date", "description": "Boleto due date (vencimento) as `YYYY-MM-DD`. Must be today or a future date within one year. Defaults to 3 business days out when omitted. The boleto can still be paid after this date unless the account is configured to reject post-due payment." }, "fine": { "type": "object", "description": "Fine (multa) charged once when the boleto is paid after the due date.", "properties": { "value": { "type": "number", "description": "Fine amount (must be greater than 0), interpreted per `type`." }, "type": { "type": "string", "enum": [ "FIXED", "PERCENTAGE" ], "default": "PERCENTAGE", "description": "Whether `value` is a fixed BRL amount or a percentage of the boleto value." } }, "required": [ "value" ] }, "interest": { "type": "object", "description": "Monthly interest (juros) accrued while the boleto is overdue.", "properties": { "value": { "type": "number", "description": "Monthly interest percentage (must be greater than 0)." } }, "required": [ "value" ] }, "discount": { "type": "object", "description": "Discount granted when the boleto is paid on or before the discount deadline.", "properties": { "value": { "type": "number", "description": "Discount amount (must be greater than 0), interpreted per `type`." }, "type": { "type": "string", "enum": [ "FIXED", "PERCENTAGE" ], "default": "PERCENTAGE", "description": "Whether `value` is a fixed BRL amount or a percentage of the boleto value." }, "dueDateLimitDays": { "type": "integer", "minimum": 0, "maximum": 60, "default": 0, "description": "Number of days before the due date up to which the discount applies. 0 means the discount applies only until the due date." } }, "required": [ "value" ] } } } } }, "RefundPaymentRequest": { "type": "object", "properties": { "amount": { "type": "number", "minimum": 0.01, "description": "Refund amount. Omit for a full refund." }, "reason": { "type": "string", "maxLength": 500, "description": "Reason for the refund." } } }, "CreateWithdrawalRequest": { "type": "object", "required": [ "amount", "destination" ], "properties": { "amount": { "type": "number", "minimum": 10, "maximum": 1000000, "multipleOf": 0.01, "description": "The NET amount in BRL — exactly what the recipient receives. The platform fee is added on top, never deducted: your balance is debited `amount + feeAmount` (returned as `grossAmount`), and that gross is what counts against `INSUFFICIENT_BALANCE`, the daily/nightly limits and the per-transaction cap. The R$10.00 minimum is compared against `amount` (the net); it is the floor the platform never goes below, and the value that actually applies to you is `minAmount` from `GET /v1/withdrawals/limits`. The 1,000,000 ceiling is a request-validation bound, not your limit — the per-transaction cap that applies to your account is returned by `GET /v1/withdrawals/limits`." }, "method": { "type": "string", "enum": [ "PIX" ], "default": "PIX", "description": "Public withdrawals currently support PIX only." }, "currency": { "type": "string", "enum": [ "BRL" ], "default": "BRL" }, "destination": { "description": "Where to send the funds.", "type": "object", "title": "PIX destination", "required": [ "type", "pixKey" ], "properties": { "type": { "type": "string", "enum": [ "pix" ] }, "pixKey": { "type": "string", "minLength": 1, "description": "PIX key. Accepts CPF, CNPJ, e-mail, Brazilian phone (`+55XXXXXXXXXXX`), or a random EVP/UUID. The type is detected automatically; you do not need to send `pixKeyType`. The key is normalized before use: CPF and CNPJ keys are reduced to digits and must pass their check digits, phone keys are normalized to `+55` E.164, and e-mail and EVP keys are trimmed and lowercased. A key that matches none of those shapes is rejected with `INVALID_PIX_KEY`." }, "recipientDocument": { "type": "string", "minLength": 11, "maxLength": 18, "description": "CPF or CNPJ of the PIX key holder. Whitespace and punctuation are stripped server-side; the result must be a valid CPF (11 digits) or CNPJ (14 digits), check digits included. Send it whenever the destination key belongs to someone other than you. Omitting it on a third-party e-mail, phone or random key makes the network see your own document as creditor. If `pixKey` is itself a CPF or CNPJ key, this field must agree with it or the request is rejected with `RECIPIENT_DOCUMENT_MISMATCH`." } } }, "description": { "type": "string", "maxLength": 500, "description": "Internal description for this withdrawal." } } }, "CreateProductRequest": { "type": "object", "required": [ "name" ], "properties": { "externalId": { "type": "string", "maxLength": 100, "description": "Your own product ID." }, "name": { "type": "string", "minLength": 1, "maxLength": 200, "description": "Product name." }, "description": { "type": "string", "maxLength": 1000, "description": "Product description." }, "amount": { "type": "number", "minimum": 0.01, "maximum": 1000000, "description": "Default price." }, "metadata": { "type": "object", "additionalProperties": { "type": "string" } } } }, "UpdateProductRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "amount": { "type": [ "number", "null" ], "minimum": 0.01, "maximum": 1000000, "description": "Set to `null` to remove the default price." }, "isActive": { "type": "boolean" }, "metadata": { "type": "object", "additionalProperties": { "type": "string" } } } }, "CreateCustomerRequest": { "type": "object", "properties": { "externalId": { "type": "string", "maxLength": 100, "description": "Your own customer ID." }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "email": { "type": "string", "format": "email", "maxLength": 255 }, "document": { "type": "string", "maxLength": 20, "description": "CPF or CNPJ number." }, "documentType": { "type": "string", "enum": [ "CPF", "CNPJ" ] }, "phone": { "type": "string", "maxLength": 20 }, "metadata": { "type": "object", "additionalProperties": { "type": "string" } } } }, "UpdateCustomerRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "email": { "type": "string", "format": "email", "maxLength": 255 }, "document": { "type": "string", "maxLength": 20 }, "documentType": { "type": "string", "enum": [ "CPF", "CNPJ" ] }, "phone": { "type": "string", "maxLength": 20 }, "isActive": { "type": "boolean" }, "metadata": { "type": "object", "additionalProperties": { "type": "string" } } } }, "Payment": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string", "description": "YuvexPay transaction ID." }, "amount": { "type": "number", "description": "Merchant-requested base amount, in BRL. This is the seller's intended receivable." }, "feeAmount": { "type": "number", "description": "YuvexPay platform fee, in BRL. When `feePassedToPayer` is true, this equals `payerFeeAmount` and is charged on top of `amount`. When false, this is deducted from `amount` to produce `netAmount`." }, "netAmount": { "type": "number", "description": "Merchant's net receivable, in BRL. Equals `amount` when `feePassedToPayer` is true; equals `amount - feeAmount` otherwise." }, "feePassedToPayer": { "type": "boolean", "description": "Resolved decision (after applying the per-payment override, company default, and sub-R$1 forced passthrough) on whether the fee was added on top for the payer." }, "payerFeeAmount": { "type": [ "number", "null" ], "description": "Fee amount added to the payer's total charge. Null when `feePassedToPayer` is false. When true, payer-charged total = `amount + payerFeeAmount`." }, "status": { "$ref": "#/components/schemas/PaymentStatus" }, "paymentMethod": { "$ref": "#/components/schemas/PaymentMethod" }, "currency": { "$ref": "#/components/schemas/Currency" }, "description": { "type": "string" }, "metadata": { "type": "object", "additionalProperties": { "type": "string" } }, "expiresAt": { "type": "string", "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "methodData": { "type": "object", "description": "Payment method-specific data (QR code for PIX, barcode for boleto, etc.).", "properties": { "type": { "$ref": "#/components/schemas/PaymentMethod" } }, "additionalProperties": true }, "paidAt": { "type": [ "string", "null" ], "format": "date-time", "description": "Timestamp when the payment was confirmed paid. Null until paid." }, "payer": { "type": [ "object", "null" ], "description": "Payer (debtor) identity captured from the PIX network once the charge is paid. Provider-agnostic. Null for historical records or open-payer charges where no payer data was captured.", "properties": { "name": { "type": [ "string", "null" ], "description": "Payer full name." }, "document": { "type": [ "string", "null" ], "description": "Payer CPF or CNPJ, as digits. Some PIX participants report the document masked (for example `***405566**`); when that is all the network gave us, the masked string is what this field carries — a full document wins whenever one is on file. `null` when no document of either kind was captured, which for an open-payer charge is common. Match on `*` before parsing the value as a CPF or CNPJ." }, "documentType": { "type": [ "string", "null" ], "enum": [ "CPF", "CNPJ", null ], "description": "Payer document type. `null` when the network did not say which kind of document it reported, which is the usual case alongside a masked `document`." }, "institutionName": { "type": [ "string", "null" ], "description": "Payer bank/institution name." }, "institutionIspb": { "type": [ "string", "null" ], "description": "Payer bank ISPB (Banco Central participant identifier)." } } } } }, "Refund": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string" }, "amount": { "type": "number" }, "reason": { "type": "string" }, "status": { "$ref": "#/components/schemas/TransactionStatus" }, "processedAt": { "type": "string", "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" } } }, "Withdrawal": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string", "description": "Transaction identifier: `WIT` followed by a millisecond timestamp and 5 uppercase alphanumerics, e.g. `WIT1746280868443K3F9Q`. Treat it as an opaque string.", "example": "WIT1746280868443K3F9Q" }, "grossAmount": { "type": "number" }, "feeAmount": { "type": "number" }, "netAmount": { "type": "number" }, "pixKey": { "type": "string", "description": "The destination PIX key, masked. The full key is never returned by the API once the withdrawal is created." }, "pixKeyType": { "type": "string", "description": "Type of PIX key: `CPF`, `CNPJ`, `EMAIL`, `PHONE` or `EVP` (random key)." }, "status": { "$ref": "#/components/schemas/WithdrawalStatus" }, "isSandbox": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" }, "paymentMethod": { "type": "string", "enum": [ "PIX" ] }, "currency": { "type": "string", "enum": [ "BRL" ] }, "processedAt": { "type": [ "string", "null" ], "format": "date-time" }, "errorMessage": { "type": [ "string", "null" ], "description": "Human-readable failure message. Treat as opaque text and surface only to internal operators." } } }, "WithdrawalLimits": { "type": "object", "properties": { "currentLimit": { "type": "number", "description": "Daily (or nightly, when `isNightTime` is true) limit in BRL." }, "used": { "type": "number", "description": "Amount already consumed today, in BRL. Counts `amount + feeAmount` of each withdrawal, and is a single counter for the whole Brazilian calendar day — the nightly limit is compared against a total that already includes daytime volume." }, "remaining": { "type": "number", "description": "Remaining capacity for the current window, in BRL. Because the fee counts towards the limit, the largest `amount` you can request is `remaining - fee`." }, "isNightTime": { "type": "boolean", "description": "Whether reduced night limits are active." }, "fee": { "type": "number", "description": "Withdrawal fee, a flat amount in BRL charged on top of `amount`. Read it from here rather than hardcoding it: it is configurable per account." }, "dayStartHour": { "type": "integer", "description": "Hour when daytime limits begin (BRT)." }, "dayEndHour": { "type": "integer", "description": "Hour when nighttime limits begin (BRT)." }, "minAmount": { "type": "number", "description": "Minimum withdrawal amount in BRL that applies to your account, compared against `amount` (the net) rather than against `amount + fee`. Never below the `R$ 10.00` platform floor, but it can be higher. Unrelated to the routing provider minimum, which surfaces as `WITHDRAWAL_BELOW_PROVIDER_MINIMUM`." }, "perTransactionLimit": { "type": [ "number", "null" ], "description": "Largest single withdrawal your account may request, in BRL, or `null` when no account-level cap applies. Like the daily limit it is compared against `amount + fee`, so the largest `amount` you can request is `perTransactionLimit - fee`. A second, lower ceiling can apply at the PIX provider that routes the payout; it depends on live routing and is not returned here, but it is still evaluated synchronously and comes back as a `400 WITHDRAWAL_LIMIT_EXCEEDED` or `400 WITHDRAWAL_BELOW_PROVIDER_MINIMUM` on the create call." } } }, "Product": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "description": { "type": "string" }, "amount": { "type": "number" }, "externalId": { "type": "string" }, "isActive": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" } } }, "Customer": { "type": "object", "properties": { "customerId": { "type": "string", "description": "Merchant-facing customer slug (e.g., `cus_ab12cd34ef`). Use this as the `{customerId}` path parameter in subsequent calls." }, "name": { "type": "string" }, "email": { "type": "string" }, "document": { "type": "string" }, "phone": { "type": "string" }, "externalId": { "type": "string" }, "isActive": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" } } }, "CustomerDetail": { "allOf": [ { "$ref": "#/components/schemas/Customer" }, { "type": "object", "properties": { "metadata": { "type": "object", "additionalProperties": { "type": "string" } }, "updatedAt": { "type": "string", "format": "date-time" } } } ] }, "Pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" }, "total": { "type": "integer" }, "totalPages": { "type": "integer" } } }, "Error": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string", "description": "Machine-readable error code." }, "message": { "type": "string", "description": "Human-readable error message." }, "details": { "type": "object", "description": "Additional error context." } } } } }, "FlatError": { "type": "object", "description": "The flat error envelope used by low-level guards that reply before the API error envelope is applied — today the idempotency middleware. The machine-readable code is the top-level `code`; `error` is only the HTTP reason phrase.", "properties": { "statusCode": { "type": "integer", "description": "HTTP status code, repeated in the body." }, "error": { "type": "string", "description": "HTTP reason phrase, for example `Conflict` or `Bad Request`. Not a machine-readable code." }, "message": { "type": "string", "description": "Human-readable error message." }, "code": { "type": "string", "description": "Machine-readable error code." } } }, "Balance": { "type": "object", "description": "Account balance for the company associated with the API key. The four buckets `available`, `frozen`, and `held` are independent and additive — `total = available + frozen + held`.", "properties": { "available": { "type": "number", "description": "Amount available for immediate PIX withdrawal, in BRL." }, "frozen": { "type": "number", "description": "Funds reserved/frozen for in-flight withdrawals, refunds, or operational holds. Not currently withdrawable, but still part of the account balance until the underlying operation reaches a terminal state. Independent from `available`." }, "held": { "type": "number", "description": "Confirmed amount pending settlement (for example, card or boleto payments confirmed but not yet liquidated). Will move to `available` once settled." }, "total": { "type": "number", "description": "Total account balance: `available + frozen + held`." }, "currency": { "type": "string", "enum": [ "BRL" ], "description": "Always `BRL`. Multi-currency is not yet supported on the public API." }, "environment": { "type": "string", "enum": [ "SANDBOX", "PRODUCTION" ], "description": "Environment of the API key used to make this call. Sandbox keys always return zeros." }, "updatedAt": { "type": [ "string", "null" ], "format": "date-time", "description": "When the balance row was last updated. `null` if the company has never had any balance activity." } }, "required": [ "available", "frozen", "held", "total", "currency", "environment", "updatedAt" ] }, "ApiKeySelf": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Internal API key id." }, "name": { "type": "string", "description": "Human-readable name set in the dashboard." }, "description": { "type": [ "string", "null" ] }, "environment": { "type": "string", "enum": [ "SANDBOX", "PRODUCTION" ], "description": "Environment the key is bound to." }, "prefix": { "type": "string", "description": "The non-secret prefix of the key (`ypk__`). Safe to log." }, "scopes": { "type": "array", "items": { "type": "string" }, "description": "Permissions granted to the key." }, "ipAllowlist": { "type": "array", "items": { "type": "string" }, "description": "List of allowed IPs/CIDRs. Empty means no restriction." }, "rateLimitPerMinute": { "type": [ "integer", "null" ], "description": "Per-key rate limit in requests per minute, or null for the account default." }, "expiresAt": { "type": [ "string", "null" ], "format": "date-time", "description": "Expiration timestamp, or null if the key does not expire." }, "lastUsedAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" } } }, "HeadlessPaymentCreateResponse": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string" }, "amount": { "type": "number", "description": "Merchant-requested base amount. Seller receivable when `feePassedToPayer` is true." }, "feeAmount": { "type": "number", "description": "YuvexPay platform fee." }, "netAmount": { "type": "number", "description": "Merchant's net receivable." }, "feePassedToPayer": { "type": "boolean", "description": "Resolved decision on whether the fee was added on top for the payer." }, "payerFeeAmount": { "type": [ "number", "null" ], "description": "Fee added to payer's total when `feePassedToPayer` is true; null otherwise." }, "isSandbox": { "type": "boolean" }, "status": { "$ref": "#/components/schemas/PaymentStatus" }, "paymentMethod": { "$ref": "#/components/schemas/PaymentMethod" }, "currency": { "$ref": "#/components/schemas/Currency" }, "description": { "type": [ "string", "null" ] }, "expiresAt": { "type": "string", "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "methodData": { "type": "object", "additionalProperties": true, "properties": { "type": { "$ref": "#/components/schemas/PaymentMethod" } }, "description": "Method-specific data. PIX returns QR data. Direct CARD returns captureType: DIRECT plus masked credit card data. Invoice-style CARD/BOLETO returns provider invoice links.", "example": { "type": "CARD", "captureType": "DIRECT", "cardSubtype": "CREDIT", "brand": "VISA", "last4Digits": "1111", "installments": 1 } } } }, "HostedPaymentCreateResponse": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string" }, "amount": { "type": "number", "description": "Merchant-requested base amount. Seller receivable when `feePassedToPayer` is true." }, "feeAmount": { "type": "number", "description": "YuvexPay platform fee." }, "netAmount": { "type": "number", "description": "Merchant's net receivable." }, "feePassedToPayer": { "type": "boolean", "description": "Resolved decision on whether the fee was added on top for the payer." }, "payerFeeAmount": { "type": [ "number", "null" ], "description": "Fee added to payer's total when `feePassedToPayer` is true; null otherwise." }, "status": { "$ref": "#/components/schemas/PaymentStatus" }, "methods": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentMethod" } }, "currency": { "$ref": "#/components/schemas/Currency" }, "checkoutUrl": { "type": "string", "format": "uri" }, "expiresAt": { "type": "string", "format": "date-time" }, "isSandbox": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" } } }, "CreatePaymentResponse": { "type": "object", "properties": { "payment": { "oneOf": [ { "$ref": "#/components/schemas/HeadlessPaymentCreateResponse" }, { "$ref": "#/components/schemas/HostedPaymentCreateResponse" } ] } }, "required": [ "payment" ] }, "WebhookPayerBlock": { "type": "object", "description": "Payer block. `null` when every field would be null.", "properties": { "name": { "type": [ "string", "null" ], "description": "Payer name as reported by the settling institution." }, "document": { "type": [ "string", "null" ], "description": "Payer CPF or CNPJ, as digits, or the masked string the settling PIX provider reported (for example `***405566**`) when no full document is on file. `null` when neither was captured. Match on `*` before parsing the value as a CPF or CNPJ." }, "documentType": { "type": [ "string", "null" ], "description": "`CPF` or `CNPJ`." }, "institutionName": { "type": [ "string", "null" ], "description": "Payer institution name." }, "institutionIspb": { "type": [ "string", "null" ], "description": "Payer institution ISPB code." } } }, "WebhookRecipientBlock": { "type": "object", "description": "Recipient block. `null` when every field would be null. There is no `documentType` here, unlike `payer`.", "properties": { "name": { "type": [ "string", "null" ], "description": "Recipient name as reported by the settling institution." }, "document": { "type": [ "string", "null" ], "description": "Recipient CPF or CNPJ. Populated when the withdrawal was created with `destination.recipientDocument`, or with a CPF or CNPJ `destination.pixKey`, or when the settling provider reported the receiving account. `null` otherwise." }, "institutionName": { "type": [ "string", "null" ], "description": "Recipient institution name." }, "institutionIspb": { "type": [ "string", "null" ], "description": "Recipient institution ISPB code." }, "branch": { "type": [ "string", "null" ], "description": "Recipient branch, when the provider reports it." }, "account": { "type": [ "string", "null" ], "description": "Recipient account, when the provider reports it." } } }, "WithdrawalCreated": { "type": "object", "description": "The withdrawal as returned by `POST /v1/withdrawals`. This response is deliberately narrower than the object returned by the read endpoints: it omits `pixKey`, `pixKeyType`, `processedAt` and `errorMessage`, which are only meaningful once the network has acted.", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string", "description": "Transaction identifier: `WIT` followed by a millisecond timestamp and 5 uppercase alphanumerics, e.g. `WIT1746280868443K3F9Q`. Treat it as an opaque string.", "example": "WIT1746280868443K3F9Q" }, "grossAmount": { "type": "number" }, "feeAmount": { "type": "number" }, "netAmount": { "type": "number" }, "paymentMethod": { "type": "string", "enum": [ "PIX" ] }, "currency": { "type": "string", "enum": [ "BRL" ] }, "status": { "$ref": "#/components/schemas/WithdrawalStatus" }, "isSandbox": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" } } } }, "responses": { "ValidationError": { "description": "Validation error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "VALIDATION_ERROR", "message": "Invalid request body", "details": { "amount": "Must be greater than 0.01" } } } } } }, "Unauthorized": { "description": "Missing or invalid authentication token.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "INVALID_API_KEY", "message": "Invalid or revoked API key" } } } } }, "Forbidden": { "description": "The API key is valid but does not have the required scope for this endpoint.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "INSUFFICIENT_SCOPE", "message": "Required scope missing: balance:read" } } } } }, "NotFound": { "description": "Resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "NOT_FOUND", "message": "Resource not found" } } } } }, "IdempotencyConflict": { "description": "Idempotency conflict, raised by the idempotency middleware. `IDEMPOTENCY_PAYLOAD_MISMATCH` means the key was already used with a different request body, at any point in the 24-hour window, including by a request still in flight. `IDEMPOTENCY_CONFLICT` means a request with the same key and the same body is still being processed. Both use the **flat** envelope: `error` is the HTTP reason phrase and the machine-readable code is the top-level `code`. Note that `IDEMPOTENCY_CONFLICT` raised later by the withdrawal service on an idempotency-key collision uses the nested `Error` envelope instead.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FlatError" }, "examples": { "payloadMismatch": { "summary": "The key was reused with a different body", "value": { "statusCode": 409, "error": "Conflict", "message": "This idempotency key was already used with a different request payload", "code": "IDEMPOTENCY_PAYLOAD_MISMATCH" } }, "inFlight": { "summary": "The same request is still being processed", "value": { "statusCode": 409, "error": "Conflict", "message": "A request with this idempotency key is already being processed", "code": "IDEMPOTENCY_CONFLICT" } } } } } }, "RateLimited": { "description": "Rate limit exceeded.", "headers": { "X-RateLimit-Limit": { "schema": { "type": "integer" }, "description": "Request limit per window." }, "X-RateLimit-Remaining": { "schema": { "type": "integer" }, "description": "Remaining requests in window." }, "X-RateLimit-Reset": { "schema": { "type": "integer" }, "description": "Seconds until the window resets." } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Too many requests" } } } } } } } }