{ "openapi": "3.1.0", "info": { "title": "YuvexPay API", "description": "The YuvexPay API allows you to accept payments, process withdrawals, manage products, coupons 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": "Coupons", "description": "Manage discount coupons" }, { "name": "Documents", "description": "Generate receipts and statements" }, { "name": "Balance", "description": "Inspect the current account balance" }, { "name": "Splits", "description": "Inspect the split partners of the account" }, { "name": "Public", "description": "Public information that needs no API key" } ], "paths": { "/public/pricing": { "get": { "operationId": "getPublicPricing", "summary": "Get standard pricing", "description": "Standard prices for the payment methods currently offered, the withdrawal fee, plan facts and sign-up availability. No authentication. Money values are decimal strings with two places. Responses are cacheable for 5 minutes.", "tags": [ "Public" ], "security": [], "responses": { "200": { "description": "Pricing retrieved.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicPricing" }, "example": { "currency": "BRL", "methods": [ { "method": "PIX", "percent": "4.99", "fixed": "0.30", "minimumFee": null, "minAmount": "0.01", "maxAmount": "100000.00", "settlementDays": 0 }, { "method": "CARD", "percent": "4.99", "fixed": "0.60", "minimumFee": null, "minAmount": "5.00", "maxAmount": "100000.00", "settlementDays": 32 }, { "method": "BOLETO", "percent": null, "fixed": "2.99", "minimumFee": null, "minAmount": "5.00", "maxAmount": "100000.00", "settlementDays": 3 } ], "withdrawal": { "fee": "2.49", "minAmount": "10.00", "settlementDays": 0 }, "plan": { "monthlyFee": "0.00", "setupFee": "0.00", "volumeMinimum": null, "customPricing": true }, "signup": { "open": true, "url": "https://app.yuvexpay.com/sign-up", "accountTypes": [ "INDIVIDUAL", "BUSINESS" ], "sandbox": true }, "updatedAt": "2026-05-06T02:54:13.529Z" } } } } } } }, "/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 and boleto, in headless or hosted mode, and card through the hosted checkout only. Requires an idempotency key.\n\nCard payments are not accepted through the API. A `card` object, `CARD` in `methods` in headless mode (`mode` omitted or `headless`), or any of these fields at the top level of the body: `deviceInfo`, `remoteIp`, `holderInfo`, `authorizeOnly`, `creditCard`, `creditCardHolderInfo`, `creditCardToken`, `installmentCount`, `installmentValue`, `totalValue`, is rejected with `400 CARD_PAYMENTS_NOT_AVAILABLE_VIA_API`, even without a valid API key, and the card data is not stored or logged.\n\nTo charge a card, create the payment with `mode: hosted` and `CARD` in `methods` (`[\"CARD\"]`, or `[\"PIX\", \"CARD\"]` to let the payer choose), with an `amount` of at least R$ 5.00, and redirect the payer to the returned `checkoutUrl`. The checkout collects the card, accepts credit cards in a single installment and debit cards, and runs 3D Secure itself. The result arrives as `PAYMENT_CONFIRMED`, `PAYMENT_PAID`, `PAYMENT_EXPIRED` or `PAYMENT_CANCELLED`; you can also poll `GET /v1/payments/{paymentId}`.\n\nSend `items` to sell several lines in one payment, `couponCode` to apply a coupon, and, on a hosted payment, `itemsQuantityEditable`, `orderBump` and `upsell`. The response carries `items`, `subtotal`, `discount` and `coupon`. A coupon that discounts the whole amount completes the payment as `FREE`, with no charge.", "tags": [ "Payments" ], "parameters": [ { "$ref": "#/components/parameters/IdempotencyKey" }, { "name": "X-Split-Platform-Key", "in": "header", "required": false, "schema": { "type": "string" }, "description": "API key of an approved split platform, sent as the key itself, without `Bearer`, in the environment of the key in `Authorization`: `ypk_live_` in production, `ypk_test_` in sandbox. It lets the platform named in `splits[]` connect the seller on this call, with no dashboard invitation: the relationship is recorded as `ACCEPTED` with the platform's fee cap and the payment is created. The connection works in production and in sandbox under the same fee cap, and only a production connection notifies the seller. The header is read only when exactly one recipient in `splits[]` has no accepted relationship with your company, and ignored otherwise. A key that is unknown, revoked or expired, is from the other environment, lacks the `payments:write` scope, is used from outside its IP allowlist or belongs to your own company returns `403 SPLIT_PLATFORM_KEY_INVALID`. A valid key whose company is not that recipient, or is not an approved platform, connects nobody: the request follows the regular flow and returns `SPLIT_RELATIONSHIP_PENDING`. Send it from a server only. See the Split platforms guide." } ], "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" } } }, "hostedCheckout": { "summary": "Hosted checkout with PIX and card", "value": { "amount": 49.9, "methods": [ "PIX", "CARD" ], "currency": "BRL", "mode": "hosted", "description": "Order #1234", "externalId": "order-1234", "returnUrl": "https://store.example.com/checkout/return", "completionUrl": "https://store.example.com/checkout/done", "customer": { "name": "Pedro Álvares Cabral", "email": "user@example.com", "document": "12345678900" } } }, "headlessPixWithSplit": { "summary": "Headless PIX payment split with two other companies", "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" }, "splits": [ { "recipientCompanyId": "b7f1c3d2-4e5a-4b6c-8d9e-0a1b2c3d4e5f", "type": "PERCENTAGE", "value": 10 }, { "recipientCompanyId": "c9a2d4e6-8b1f-4c3a-9d7e-2f5a8b0c1d3e", "type": "FIXED", "value": 5 } ] } }, "hostedCart": { "summary": "Hosted checkout with line items, a coupon, an order bump and an upsell", "value": { "items": [ { "name": "Camiseta", "unitAmount": 59.9, "quantity": 2, "sku": "TSHIRT-M" }, { "name": "Boné", "unitAmount": 39.9, "quantity": 1, "productId": "8a3f1c2e-6b4d-4e7a-9c1b-2d3e4f5a6b7c" } ], "itemsQuantityEditable": true, "couponCode": "BLACK10", "orderBump": { "name": "Meias", "unitAmount": 19.9, "description": "Par de meias da mesma coleção" }, "upsell": { "name": "Moletom", "unitAmount": 99.9 }, "methods": [ "PIX", "CARD" ], "mode": "hosted", "externalId": "order-1235", "completionUrl": "https://store.example.com/checkout/done" } } } } } }, "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 } } } }, "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" } } }, "headlessPixWithSplit": { "summary": "Headless PIX payment split with two other companies", "value": { "payment": { "id": "2c7d9e14-5b60-4a83-9f21-6d4e8a0b3c57", "txId": "PAY4d7e2f8a1b3c5d6e9f0a1b2c3d4e5f60", "amount": 49.9, "feeAmount": 2.99, "netAmount": 46.91, "splits": [ { "id": "a1e4c7b2-9d38-4f61-8c05-2b7d6e9f4a31", "recipientCompanyId": "b7f1c3d2-4e5a-4b6c-8d9e-0a1b2c3d4e5f", "type": "PERCENTAGE", "value": 10 }, { "id": "d3f8b1a6-27c4-4e09-9a5b-8c1e0d7f6b24", "recipientCompanyId": "c9a2d4e6-8b1f-4c3a-9d7e-2f5a8b0c1d3e", "type": "FIXED", "value": 5 } ], "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 } } } }, "hostedCart": { "summary": "Hosted checkout with line items and a 10% coupon", "value": { "payment": { "id": "7e2b4c1d-9a3f-4d6e-8b5c-1f0a2e3d4c5b", "txId": "PAY6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d", "amount": 143.73, "feeAmount": 1.86, "netAmount": 141.87, "feePassedToPayer": false, "payerFeeAmount": null, "splits": [], "items": [ { "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e", "name": "Camiseta", "unitAmount": 59.9, "quantity": 2, "kind": "ITEM", "productId": null, "sku": "TSHIRT-M" }, { "id": "1c2d3e4f-5a6b-4c7d-9e8f-0a1b2c3d4e5f", "name": "Boné", "unitAmount": 39.9, "quantity": 1, "kind": "ITEM", "productId": "8a3f1c2e-6b4d-4e7a-9c1b-2d3e4f5a6b7c", "sku": null } ], "subtotal": 159.7, "discount": 15.97, "coupon": { "code": "BLACK10" }, "status": "PENDING_METHOD_SELECTION", "methods": [ "PIX", "CARD" ], "currency": "BRL", "checkoutUrl": "https://checkout.yuvexpay.com/checkout/7e2b4c1d-9a3f-4d6e-8b5c-1f0a2e3d4c5b", "expiresAt": "2026-10-31T19:00:00.000Z", "isSandbox": false, "createdAt": "2026-10-31T18:00:00.000Z" } } }, "headlessFree": { "summary": "Headless PIX payment completed as FREE by a 100% coupon", "value": { "payment": { "id": "3d4e5f6a-7b8c-4d9e-8f0a-1b2c3d4e5f6a", "txId": "PAY0f1e2d3c4b5a69788796a5b4c3d2e1f0", "status": "FREE", "amount": 0, "method": "PIX", "paymentMethod": "PIX", "currency": "BRL", "methodData": null, "feeAmount": 0, "netAmount": 0, "feePassedToPayer": false, "payerFeeAmount": null, "description": "Order #1236", "expiresAt": "2026-10-31T19:00:00.000Z", "isSandbox": false, "createdAt": "2026-10-31T18:00:00.000Z", "splits": [], "items": [], "subtotal": 49.9, "discount": 49.9, "coupon": { "code": "FREE100" } } } } } } } }, "400": { "description": "Validation failed, or the request was refused. `CARD_PAYMENTS_NOT_AVAILABLE_VIA_API`: the request carries card data or asks for `CARD` in headless mode; card payments go through the hosted checkout only. It takes precedence over validation and authentication errors, so it is returned even without a valid API key. Split-specific codes: `SPLIT_RECIPIENT_LIMIT_EXCEEDED`, `SPLIT_SELF_RECIPIENT`, `SPLIT_AMOUNT_EXCEEDS_NET`, `SPLIT_RELATIONSHIP_PENDING`, `SPLIT_RELATIONSHIP_REQUEST_LIMIT`, `SPLIT_RELATIONSHIP_REVOKED`, `SPLIT_RELATIONSHIP_DECLINED`, `SPLIT_RECIPIENT_INACTIVE`, `SPLIT_RECIPIENT_NOT_APPROVED`, `SPLIT_ENVIRONMENT_MISMATCH`, `SPLIT_FEE_CAP_EXCEEDED`, `SPLIT_PLATFORM_SUSPENDED`, `SPLIT_PARTNER_LIMIT_EXCEEDED`. `SPLIT_RELATIONSHIP_PENDING` is returned for every recipient that cannot receive a split yet apart from a pair that declined an invitation from your company or was revoked, identical whether the company ID exists or not; `SPLIT_RELATIONSHIP_DECLINED` carries `details.recipients`, the company IDs that declined; `SPLIT_RECIPIENT_INACTIVE`, `SPLIT_RECIPIENT_NOT_APPROVED` and `SPLIT_ENVIRONMENT_MISMATCH` are returned only for a recipient that had already accepted the relationship. `SPLIT_FEE_CAP_EXCEEDED`: a split to an approved split platform is above its fee cap, with `details.recipientCompanyId` and `details.feeCap`. `SPLIT_PLATFORM_SUSPENDED`: the recipient is a platform connected with a fee cap whose approval was withdrawn, with `details.recipientCompanyId`. `SPLIT_PARTNER_LIMIT_EXCEEDED`: a platform connection through `X-Split-Platform-Key` would take your company past its limit of active partners, with `details.limit`. Cart codes: `AMOUNT_ITEMS_MISMATCH`, `amount` is not the sum of `items`, with `details.amount` and `details.itemsTotal` (a string such as `\"159.70\"`); `ITEM_PRODUCT_NOT_FOUND`, an item `productId` is not a product of your account in this environment; `OFFER_PRODUCT_NOT_FOUND`, the same for `orderBump` or `upsell`; `UPSELL_AMOUNT_TOO_HIGH`, the upsell costs more than the payment `amount` after the coupon; `COUPON_INVALID`, `couponCode` cannot be used on this payment, one answer for every reason; `AMOUNT_TOO_HIGH`, the sum of `items` is above R$ 1,000,000.00; `MINIMUM_AMOUNT_FOR_CARD_OR_BOLETO`, the sum of `items`, or a headless boleto amount after the coupon, is below R$ 5.00. `orderBump` or `upsell` in headless mode returns `VALIDATION_ERROR` with `OFFERS_REQUIRE_HOSTED` in the message. `PROVIDER_REJECTED` means the payment was refused and the message is generic; a refused boleto payment is recorded as `CANCELLED`. 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": { "cardPaymentsNotAvailableViaApi": { "summary": "Card data sent to the API", "value": { "error": { "code": "CARD_PAYMENTS_NOT_AVAILABLE_VIA_API", "message": "Pagamentos com cartão não são aceitos pela API. Use o checkout hospedado: crie o pagamento com mode \"hosted\" e envie o pagador para o checkoutUrl." } } }, "providerRejected": { "summary": "The payment was refused", "value": { "error": { "code": "PROVIDER_REJECTED", "message": "Não foi possível processar este pagamento. Verifique os dados informados ou tente outro método de pagamento." } } }, "splitAmountExceedsNet": { "summary": "The splits total more than netAmount", "value": { "error": { "code": "SPLIT_AMOUNT_EXCEEDS_NET", "message": "Splits total 48.00 but netAmount is 46.91, exceeding it by 1.09" } } }, "splitRelationshipPending": { "summary": "The recipient has not accepted the split relationship yet", "value": { "error": { "code": "SPLIT_RELATIONSHIP_PENDING", "message": "Company b7f1c3d2-4e5a-4b6c-8d9e-0a1b2c3d4e5f has not accepted split payments from your company yet" } } }, "splitFeeCapExceeded": { "summary": "A split to an approved platform is above its fee cap (example cap)", "value": { "error": { "code": "SPLIT_FEE_CAP_EXCEEDED", "message": "A divisão para a plataforma excede o teto de 4.00% do valor do pagamento.", "details": { "recipientCompanyId": "b7f1c3d2-4e5a-4b6c-8d9e-0a1b2c3d4e5f", "feeCap": "4.00" } } } }, "amountItemsMismatch": { "summary": "`amount` is not the sum of `items`", "value": { "error": { "code": "AMOUNT_ITEMS_MISMATCH", "message": "O valor informado não corresponde à soma dos itens.", "details": { "amount": 150, "itemsTotal": "159.70" } } } }, "couponInvalid": { "summary": "The coupon cannot be used on this payment", "value": { "error": { "code": "COUPON_INVALID", "message": "Cupom inválido ou indisponível." } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "description": "Payment creation blocked for this account, or the platform key was refused. Codes: `PAYMENT_CREATION_BLOCKED`, `SPLIT_ORIGINATOR_NOT_APPROVED`, `PAYMENT_METHOD_NOT_ENABLED`, `SPLIT_PLATFORM_KEY_INVALID`. `PAYMENT_METHOD_NOT_ENABLED`: a method in `methods` is not enabled for your account (`details.method` names it). If card payments are enabled for your account but card acceptance is temporarily unavailable, it is returned only when `CARD` is the only method; with other methods, the payment is created without `CARD`. `SPLIT_PLATFORM_KEY_INVALID`: the key sent in `X-Split-Platform-Key` was refused. Every reason gets this same answer, with no `details`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "splitPlatformKeyInvalid": { "summary": "The key in X-Split-Platform-Key was refused", "value": { "error": { "code": "SPLIT_PLATFORM_KEY_INVALID", "message": "A chave enviada em X-Split-Platform-Key não é válida para conectar a plataforma a esta empresa." } } } } } } }, "409": { "$ref": "#/components/responses/IdempotencyConflict" }, "429": { "$ref": "#/components/responses/RateLimited" }, "502": { "description": "Code `PROVIDER_TEMPORARILY_UNAVAILABLE`. On a boleto payment the outcome of the charge is not known yet: the payment stays `PROCESSING`, with `methodData: null`, until it is resolved, which can take 15 minutes or more, and then either continues normally or ends `CANCELLED`. This `502` is stored for the `X-Idempotency-Key`: a retry with the same key returns the same `502` for 24 hours and never submits a second charge. To recover the payment, repeat the request once with the same `externalId` and a new `X-Idempotency-Key`: the response is the existing payment, with its `id`, and no new charge is submitted. Then poll `GET /v1/payments/{paymentId}` until it leaves `PROCESSING`. On a PIX payment the charge was not created and the payment is recorded as `CANCELLED`: retry with a new `externalId`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "Codes: `PROVIDER_TEMPORARILY_UNAVAILABLE`, the charge was not created and the payment is recorded as `CANCELLED`, so retry later with a new `externalId`; `SERVICE_UNAVAILABLE`, the requested payment method is temporarily disabled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "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. `FREE` lists the orders a coupon covered in full." }, { "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. A payment can be refunded once, fully or partially: after a partial refund a new refund is rejected with `REFUND_ALREADY_PERFORMED`, and after a full refund with `ALREADY_REFUNDED`. While a refund is being processed, a new one is rejected with `400 REFUND_IN_PROGRESS`. A partial refund additionally requires a settled payment and a processing route that supports partial refunds; otherwise it is rejected with `PARTIAL_REFUND_NOT_SUPPORTED`. When the payment carries splits, the refund is apportioned: every party is debited in proportion to what it received, including the originating company, and partial refunds use the same proportions. `INSUFFICIENT_BALANCE_FOR_REFUND` is evaluated against the originating company's share only — a recipient that has already withdrawn its share goes negative instead of blocking the refund, and the shortfall is never moved onto the originating company. When the refund takes the counted volume of your verified CPF or CNPJ below a rewards prize that YuvexPay approved for it, the processing fee is not returned: your balance is debited the full amount returned to the payer, and the kept fee, debited from the originating company only, can take the available balance negative. A `FREE` payment was never charged and returns `400 INVALID_STATUS`.", "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/payments/{paymentId}/simulate": { "post": { "operationId": "simulatePayment", "summary": "Simulate a payment", "description": "Drive a sandbox payment to a terminal status immediately, whatever its amount and payment method. Sandbox only: a production API key is rejected with `403 FORBIDDEN`, and a production payment id is not visible to a sandbox key, so it returns `404`. `REFUNDED` marks the payment paid if it is not already and then issues a full refund, which follows the sandbox refund matrix: a payment whose amount ends in `.02` fails with `400 PSP_REFUND_FAILED`; `DISPUTED` marks it paid if it is not already and then moves it to `CHARGEBACK`, which is a chargeback and not a MED.", "tags": [ "Payments" ], "parameters": [ { "name": "paymentId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The sandbox payment ID to simulate." }, { "name": "X-Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "maxLength": 100 }, "description": "Optional for this endpoint. Honoured when sent — replay and payload check apply — but omitting it is not rejected." } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulatePaymentRequest" }, "example": { "status": "CANCELLED" } } } }, "responses": { "200": { "description": "The payment after the simulated transition.", "content": { "application/json": { "schema": { "type": "object", "properties": { "payment": { "$ref": "#/components/schemas/Payment" } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "description": "The API key is a production key. Only sandbox API keys can simulate.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "FORBIDDEN", "message": "Only sandbox payments can be simulated" } } } } }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { "$ref": "#/components/responses/IdempotencyConflict" } } } }, "/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/WithdrawalListItem" } }, "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 four balance buckets are independent and additive: `available` (settled funds free to use; a withdrawal is also bounded by `GET /v1/withdrawals/limits`), `frozen` (set aside for in-flight withdrawals, refunds, or operational holds — not currently withdrawable), `held` (confirmed but pending settlement), and `reserved` (a rolling reserve withheld from settled funds and released on a schedule — not currently withdrawable). The total account balance is `available + frozen + held + reserved`. A split credit received from another YuvexPay company follows your own rolling reserve policy, not the originating company's. 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, "reserved": 0, "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, "reserved": 0, "total": 23.66, "currency": "BRL", "environment": "PRODUCTION", "updatedAt": "2026-05-06T12:00:00.000Z" } } }, "withReservedFunds": { "summary": "Production balance with part of settled funds in a rolling reserve", "value": { "balance": { "available": 90, "frozen": 0, "held": 0, "reserved": 10, "total": 100, "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, "reserved": 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. Only `true` or `false`; any other value returns `400`." }, { "$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": { "allOf": [ { "$ref": "#/components/schemas/Customer" }, { "type": "object", "properties": { "approvedCount": { "type": "integer", "description": "Number of the customer's payments with status `PAID` or `CONFIRMED`." }, "approvedAmount": { "type": "number", "description": "Sum of those payments' amounts, in BRL, with 2 decimal places." }, "lastApprovedAt": { "type": "string", "format": "date-time", "nullable": true, "description": "When the latest of those payments was paid, or `null` when there is none." } } } ] } }, "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/coupons": { "post": { "operationId": "createCoupon", "summary": "Create a coupon", "description": "Create a discount coupon. Requires the `coupons:write` scope. Apply it with `couponCode` on `POST /v1/payments`, or let the payer type it on the hosted checkout. Coupons belong to the environment of the key: a `ypk_test_` key manages sandbox coupons and a `ypk_live_` key production coupons, and a payment uses only coupons of its own environment.", "tags": [ "Coupons" ], "parameters": [ { "name": "X-Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "maxLength": 100 }, "description": "Optional on this endpoint. When sent, a `2xx` response is stored for 24 hours and replayed for a retry with the same key and body." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCouponRequest" }, "examples": { "percent": { "summary": "10% off the whole order, one use per payer", "value": { "code": "black10", "type": "PERCENT", "percentBp": 1000, "validFrom": "2026-11-27T00:00:00-03:00", "validUntil": "2026-12-01T00:00:00-03:00", "maxRedemptions": 500, "maxRedemptionsPerBuyer": 1 } }, "fixed": { "summary": "R$ 20.00 off one product", "value": { "code": "BONE20", "type": "FIXED", "amount": 20, "productIds": [ "8a3f1c2e-6b4d-4e7a-9c1b-2d3e4f5a6b7c" ] } } } } } }, "responses": { "201": { "description": "Coupon created.", "content": { "application/json": { "schema": { "type": "object", "properties": { "coupon": { "$ref": "#/components/schemas/Coupon" } }, "required": [ "coupon" ] }, "example": { "coupon": { "id": "4b5c6d7e-8f9a-4b0c-9d1e-2f3a4b5c6d7e", "code": "BLACK10", "type": "PERCENT", "percentBp": 1000, "amount": null, "validFrom": "2026-11-27T03:00:00.000Z", "validUntil": "2026-12-01T03:00:00.000Z", "maxRedemptions": 500, "maxRedemptionsPerBuyer": 1, "productIds": [], "active": true, "redemptionCount": 0, "createdAt": "2026-10-31T18:00:00.000Z", "updatedAt": "2026-10-31T18:00:00.000Z" } } } } }, "400": { "description": "Validation failed. Besides `VALIDATION_ERROR`: `COUPON_VALUE_INVALID`, `percentBp` and `amount` do not match `type`; `COUPON_WINDOW_INVALID`, `validUntil` is not after `validFrom`; `COUPON_PRODUCT_NOT_FOUND`, a `productIds` entry is not a product of your account in this environment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "couponValueInvalid": { "summary": "`amount` sent on a `PERCENT` coupon", "value": { "error": { "code": "COUPON_VALUE_INVALID", "message": "Cupom percentual exige percentBp e não aceita amount." } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "409": { "description": "`COUPON_CODE_TAKEN`: another coupon of your account in this environment that is not deleted has this code. A reused `X-Idempotency-Key` returns the flat idempotency errors instead.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "COUPON_CODE_TAKEN", "message": "Já existe um cupom com este código." } } } } } } }, "get": { "operationId": "listCoupons", "summary": "List coupons", "description": "List the coupons of your account that are not deleted, newest first. Requires the `coupons:read` scope. Coupons belong to the environment of the key: a `ypk_test_` key manages sandbox coupons and a `ypk_live_` key production coupons, and a payment uses only coupons of its own environment.", "tags": [ "Coupons" ], "parameters": [ { "name": "search", "in": "query", "schema": { "type": "string", "maxLength": 32 }, "description": "Return the coupons whose code contains this text, case-insensitive." }, { "name": "active", "in": "query", "schema": { "type": "string", "enum": [ "true", "false" ] }, "description": "Filter by `active`. Only `true` or `false`; any other value returns `400`." }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Coupons listed.", "content": { "application/json": { "schema": { "type": "object", "properties": { "coupons": { "type": "array", "items": { "$ref": "#/components/schemas/Coupon" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" } } } }, "/v1/coupons/{couponId}": { "get": { "operationId": "getCoupon", "summary": "Get a coupon", "description": "Retrieve a coupon. Requires the `coupons:read` scope. A deleted coupon, or one from the other environment, returns `404`.", "tags": [ "Coupons" ], "parameters": [ { "name": "couponId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The coupon ID." } ], "responses": { "200": { "description": "Coupon found.", "content": { "application/json": { "schema": { "type": "object", "properties": { "coupon": { "$ref": "#/components/schemas/Coupon" } }, "required": [ "coupon" ] }, "example": { "coupon": { "id": "4b5c6d7e-8f9a-4b0c-9d1e-2f3a4b5c6d7e", "code": "BLACK10", "type": "PERCENT", "percentBp": 1000, "amount": null, "validFrom": "2026-11-27T03:00:00.000Z", "validUntil": "2026-12-01T03:00:00.000Z", "maxRedemptions": 500, "maxRedemptionsPerBuyer": 1, "productIds": [], "active": true, "redemptionCount": 0, "createdAt": "2026-10-31T18:00:00.000Z", "updatedAt": "2026-10-31T18:00:00.000Z" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } } }, "patch": { "operationId": "updateCoupon", "summary": "Update a coupon", "description": "Update one or more fields of a coupon. Requires the `coupons:write` scope. The change applies from the next time the coupon is applied; a payment that already holds the coupon keeps it.", "tags": [ "Coupons" ], "parameters": [ { "name": "couponId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The coupon ID." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateCouponRequest" }, "example": { "active": false } } } }, "responses": { "200": { "description": "Coupon updated.", "content": { "application/json": { "schema": { "type": "object", "properties": { "coupon": { "$ref": "#/components/schemas/Coupon" } }, "required": [ "coupon" ] } } } }, "400": { "description": "Validation failed. Besides `VALIDATION_ERROR`: `COUPON_VALUE_INVALID`, `percentBp` and `amount` do not match `type`; `COUPON_WINDOW_INVALID`, `validUntil` is not after `validFrom`; `COUPON_PRODUCT_NOT_FOUND`, a `productIds` entry is not a product of your account in this environment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "couponValueInvalid": { "summary": "`amount` sent on a `PERCENT` coupon", "value": { "error": { "code": "COUPON_VALUE_INVALID", "message": "Cupom percentual exige percentBp e não aceita amount." } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { "description": "`COUPON_CODE_TAKEN`: another coupon of your account in this environment that is not deleted has this code.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "COUPON_CODE_TAKEN", "message": "Já existe um cupom com este código." } } } } } } }, "delete": { "operationId": "deleteCoupon", "summary": "Delete a coupon", "description": "Delete a coupon. Requires the `coupons:write` scope. The coupon is deactivated and kept for the payments that used it, but it no longer appears in the list, `GET` returns `404`, and its code can be used by a new coupon. A payment that already holds the coupon keeps its discount.", "tags": [ "Coupons" ], "parameters": [ { "name": "couponId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "The coupon ID." } ], "responses": { "200": { "description": "Coupon deleted.", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string" } } }, "example": { "message": "Coupon deleted" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "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, `FREE` included, 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" } } } }, "400": { "description": "The payment is not `PAID` or `REFUNDED`. Code: `RECEIPT_NOT_AVAILABLE`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/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" } } } }, "400": { "description": "The withdrawal is not `COMPLETED`. Code: `RECEIPT_NOT_AVAILABLE`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/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. Split credits received from other YuvexPay companies, and the proportional debits taken back on a refund or MED, appear here. Besides `GET /v1/balance`, this is the only public record a recipient has of a split: the payment itself belongs to the originating company and is not readable by the recipient. The price of a recovery kit appears as a debit labelled `Kit de recuperação`. The fee of a free order, a payment a coupon covered in full, appears as a debit labelled `Taxa de pedido gratuito`.", "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. Interpreted in the America/Sao_Paulo timezone, inclusive from 00:00.", "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. Interpreted in the America/Sao_Paulo timezone, inclusive through 23:59:59.", "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" } } } }, "/v1/splits/partners": { "get": { "operationId": "listSplitPartners", "summary": "List split partners", "description": "Return the companies your account may split payments with, in both directions, for the environment of the API key. A pair is created in the dashboard: the originating company invites the recipient by company ID and the recipient accepts. There is no API to invite, accept, decline or revoke — accepting is a human act. The one exception is an approved split platform, which connects a seller on the seller's first payment by sending `X-Split-Platform-Key` on `POST /v1/payments`, in production and in sandbox. Use this endpoint to discover which `companyId` values you may send as `recipientCompanyId` in `splits[]`: only a partner whose `status` is `ACCEPTED` is usable. The list carries the live row for each pair, plus the most recent answered row for a pair that has no live one, so a rejected or revoked pair is still visible. Every partner is returned in one response, newest first, unless `limit` is sent: then at most `limit` relationships are read per call, a page can carry fewer partners than `limit`, and you pass `nextCursor` back as `cursor` until it is `null`. Requires the `payments:read` scope.", "tags": [ "Splits" ], "parameters": [ { "name": "status", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "PENDING", "ACCEPTED", "REVOKED", "DECLINED", "CANCELLED" ] }, "description": "Return only partners in this state." }, { "name": "direction", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "OUTGOING", "INCOMING" ] }, "description": "`OUTGOING` lists companies you may split to; `INCOMING` lists companies that split to you." }, { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "description": "Maximum number of relationships read for this page. A page can carry fewer partners than `limit`. Omit it to get every partner in one response. When `cursor` is sent without `limit`, a page reads up to 100 relationships." }, { "name": "cursor", "in": "query", "required": false, "schema": { "type": "string", "format": "uuid" }, "description": "The `nextCursor` of the previous page. Omit it for the first page." } ], "responses": { "200": { "description": "Partners listed.", "content": { "application/json": { "schema": { "type": "object", "properties": { "nextCursor": { "type": [ "string", "null" ], "format": "uuid", "description": "Send it as `cursor` to read the next page. `null` on the last page, and whenever the whole list was returned." }, "partners": { "type": "array", "items": { "$ref": "#/components/schemas/SplitPartner" } } }, "required": [ "nextCursor", "partners" ] }, "examples": { "mixed": { "summary": "One usable partner and one that revoked", "value": { "nextCursor": null, "partners": [ { "id": "0f0e1f5e-1d6a-4a77-9c1b-1c1a9f2a44c1", "companyId": "7b1f2a2c-5f0b-4a3a-9b62-2e9a1f0c3d55", "tradeName": "Ateliê Norte", "direction": "OUTGOING", "status": "ACCEPTED", "feeCap": null, "requestedAt": "2026-09-10T12:00:00.000Z", "acceptedAt": "2026-09-10T18:22:41.000Z" }, { "id": "3c5c5d61-9a21-4f65-b2a1-8a0e5d2c7b90", "companyId": "a41c0f22-6e2d-4a10-8f3b-5d77cbb0a912", "tradeName": "Studio Sul", "direction": "OUTGOING", "status": "REVOKED", "feeCap": null, "requestedAt": "2026-08-02T09:14:00.000Z", "acceptedAt": "2026-08-02T10:01:12.000Z" } ] } }, "platform": { "summary": "A seller connected to an approved platform (example cap)", "value": { "nextCursor": null, "partners": [ { "id": "6a2d8c41-0b7e-4f39-a5d2-9e1c3b7f4a60", "companyId": "b7f1c3d2-4e5a-4b6c-8d9e-0a1b2c3d4e5f", "tradeName": "Checkout Leste", "direction": "OUTGOING", "status": "ACCEPTED", "feeCap": "4.00", "requestedAt": "2026-10-09T13:05:00.000Z", "acceptedAt": "2026-10-09T13:05:00.000Z" } ] } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" } } } } }, "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 a periodic status check rather than a real-time notification." } } }, "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", "enum": [ "payment_failed", "payment_cancelled" ], "description": "Why the payment ended. `payment_failed`: the payment was refused or could not be completed, for example a card that was refused. `payment_cancelled`: the charge was cancelled before it was paid. Absent from the JSON on the cancellation paths that carry `id` and `amount` instead. Switch on it non-exhaustively." } } }, "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`. It is also sent for refunds you did not start, such as a payer's refund request approved in the dashboard or automatically, or a refund YuvexPay made after such a request. The body carries no reason: read `reason` with `GET /v1/payments/{paymentId}/refunds`.\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", "null" ], "description": "Present and `null` when the refund was driven by a PIX MED. Absent on every other refund." } } }, "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": "Always `Reembolso não processado. O valor foi devolvido ao saldo.`" } } }, "example": { "paymentId": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "refundId": "2c7f1a90-55b8-4a1e-9d33-6f0b2e4c7a11", "reason": "Reembolso não processado. O valor foi devolvido ao saldo." } } } }, "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. If the card dispute is won, the payment returns to the status it had before the chargeback and `PAYMENT_PAID` or `PAYMENT_CONFIRMED` is sent again for that status; a payment restored to `REFUNDED` or `PARTIAL_REFUND` sends no webhook.\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." } } } }, "CHECKOUT_ABANDONED": { "post": { "summary": "Checkout abandoned", "description": "The hosted checkout saved the payer's name, e-mail and CPF or CNPJ, and the payment was still unpaid 30 minutes later. The checkout saves them as soon as all three are filled in and valid, or when it opens with details the browser remembered. When the payment was created with a customer that has a name of 2 to 120 characters, a valid e-mail and a CPF or CNPJ with valid check digits, the wait starts instead the first time the checkout is opened while the payment is waiting for a method, with or without `skip_customer=1`. The delay is set by YuvexPay for the whole platform and can change.\n\nOpt-in: delivered only to endpoints that selected this event, never to the others. Sent at most once per payment, never for headless payments or for sandbox payments, and never by a manual resend. The payment can still be paid after this event: read its current status with `GET /v1/payments/{paymentId}` before contacting the payer. `buyer` carries the payer's contact, and the same response carries it in `payer`.\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. All five fields are always present.", "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": [ "PENDING_METHOD_SELECTION", "NEW", "PROCESSING", "EXPIRED", "CANCELLED" ], "description": "The payment's status when the event was sent. The event can arrive after `PAYMENT_EXPIRED` or `PAYMENT_CANCELLED` for the same payment." }, "amount": { "type": "number", "description": "The payment `amount` in BRL, the same value `GET /v1/payments/{paymentId}` returns." }, "buyer": { "type": "object", "description": "The payer's contact. A field the payment does not have comes from the customer record linked to the payment, and is `null` when it is not known there either. Never `null` itself.", "properties": { "name": { "type": [ "string", "null" ], "description": "The name you sent in `customer`, completed with the one the payer typed on the checkout." }, "email": { "type": [ "string", "null" ], "description": "The e-mail the payer typed on the checkout, else the one you sent in `customer`." }, "phone": { "type": [ "string", "null" ], "description": "The phone the payer typed on the checkout, else the one you sent in `customer`." } }, "required": [ "name", "email", "phone" ] } }, "required": [ "id", "txId", "status", "amount", "buyer" ] }, "example": { "id": "5d0f8b6e-3a02-4f5b-9e1c-7c6a4a1b8c9d", "txId": "PAY3F2A8B9C4E6D1F5A7B3C8D2E9F4A6B1C", "status": "PENDING_METHOD_SELECTION", "amount": 49.9, "buyer": { "name": "Maria Souza", "email": "maria@example.com", "phone": null } } } } }, "responses": { "200": { "description": "Return a 2xx status to acknowledge receipt." } } } }, "PAYMENT_FREE": { "post": { "summary": "Payment free", "description": "A coupon discounted the whole amount of the payment and the order completed without a charge. A free order never sends `PAYMENT_PAID`.\n\nOpt-in: delivered only to endpoints that selected this event, never to the others, and never for sandbox payments. A resend from the dashboard or by support repeats the exact body of the original delivery.\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.", "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": [ "FREE" ], "description": "Always `FREE`." }, "amount": { "type": "number", "enum": [ 0 ], "description": "Always `0`." }, "paidAt": { "type": "string", "format": "date-time", "description": "When the order completed. `GET /v1/payments/{paymentId}` returns this time as `completedAt`, with `paidAt: null`." }, "endToEndId": { "type": "null", "description": "Always `null`: no PIX transfer exists." }, "payer": { "oneOf": [ { "$ref": "#/components/schemas/WebhookPayerBlock" }, { "type": "null" } ], "description": "The name and document the payment has, with `institutionName` and `institutionIspb` `null`, or `null` when neither is known." } }, "required": [ "id", "txId", "status", "amount", "paidAt", "endToEndId", "payer" ] }, "example": { "id": "3d4e5f6a-7b8c-4d9e-8f0a-1b2c3d4e5f6a", "txId": "PAY0F1E2D3C4B5A69788796A5B4C3D2E1F0", "status": "FREE", "amount": 0, "paidAt": "2026-10-31T18:05:00.000Z", "endToEndId": null, "payer": { "name": "Maria Silva", "document": "39053344705", "documentType": "CPF", "institutionName": null, "institutionIspb": null } } } } }, "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", "null" ], "description": "Always `null` when present. Kept for payload compatibility; use `medId`." }, "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 some resolution 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", "null" ], "description": "`merchant_refund` when your own refund resolved the MED. `null` or absent otherwise." } }, "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 reversals initiated on the network side.\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": "Generic failure message from a fixed set: `Saque não processado. O valor foi devolvido ao saldo.`, `Saque recusado pela instituição de destino.`, `A chave PIX informada não existe ou já não está ativa. Confira a chave e tente de novo.`, `A conta de destino está bloqueada. Utilize outra chave PIX.`, `A conta de destino foi encerrada. Utilize outra chave PIX.`, `O documento informado não corresponde ao titular da chave PIX informada.`. Never `null` — the key is absent when no reason was recorded." } } }, "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. Successful (`2xx`) responses are stored and replayed for the key. On `POST /v1/payments`, a `502 PROVIDER_TEMPORARILY_UNAVAILABLE` for a boleto payment is stored and replayed too: to recover that payment, repeat the request once with the same `externalId` and a new key. A retry of an earlier card payment request now returns `400 CARD_PAYMENTS_NOT_AVAILABLE_VIA_API` instead of the stored response (`201` or `502`): recover that payment with `GET /v1/payments/txid/{txId}`, or with a create request with the same `externalId`, `mode: \"hosted\"` and `methods: [\"CARD\"]`, no `card` object or card fields, and a new key." }, "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": { "PublicPricing": { "type": "object", "required": [ "currency", "methods", "withdrawal", "plan", "signup", "updatedAt" ], "properties": { "currency": { "type": "string", "enum": [ "BRL" ] }, "methods": { "type": "array", "description": "Payment methods currently offered, with their standard price. PIX is always listed.", "items": { "type": "object", "required": [ "method", "percent", "fixed", "minimumFee", "minAmount", "maxAmount", "settlementDays" ], "properties": { "method": { "type": "string", "enum": [ "PIX", "CARD", "BOLETO" ] }, "percent": { "type": [ "string", "null" ], "pattern": "^\\d+\\.\\d{2}$", "description": "Percentage fee; `\"4.99\"` means 4.99 %. `null` when there is none." }, "fixed": { "type": [ "string", "null" ], "pattern": "^\\d+\\.\\d{2}$", "description": "Fixed fee per payment in BRL. `null` when there is none." }, "minimumFee": { "type": [ "string", "null" ], "pattern": "^\\d+\\.\\d{2}$", "description": "Minimum fee per payment in BRL. `null` when there is none." }, "minAmount": { "type": "string", "pattern": "^\\d+\\.\\d{2}$", "description": "Smallest payment amount in BRL." }, "maxAmount": { "type": "string", "pattern": "^\\d+\\.\\d{2}$", "description": "Largest payment amount in BRL." }, "settlementDays": { "type": "integer", "minimum": 0, "description": "Days until a paid amount becomes available." } } } }, "withdrawal": { "type": [ "object", "null" ], "description": "`null` while withdrawals are not offered.", "required": [ "fee", "minAmount", "settlementDays" ], "properties": { "fee": { "type": "string", "pattern": "^\\d+\\.\\d{2}$", "description": "Fee per PIX withdrawal in BRL." }, "minAmount": { "type": "string", "pattern": "^\\d+\\.\\d{2}$", "description": "Smallest withdrawal amount in BRL." }, "settlementDays": { "type": "integer", "enum": [ 0 ] } } }, "plan": { "type": "object", "required": [ "monthlyFee", "setupFee", "volumeMinimum", "customPricing" ], "properties": { "monthlyFee": { "type": "string", "enum": [ "0.00" ] }, "setupFee": { "type": "string", "enum": [ "0.00" ] }, "volumeMinimum": { "type": "null" }, "customPricing": { "type": "boolean", "enum": [ true ], "description": "Custom prices can be negotiated." } } }, "signup": { "type": "object", "required": [ "open", "url", "accountTypes", "sandbox" ], "properties": { "open": { "type": "boolean", "description": "Whether new accounts can be created right now." }, "url": { "type": "string", "format": "uri" }, "accountTypes": { "type": "array", "items": { "type": "string", "enum": [ "INDIVIDUAL", "BUSINESS" ] } }, "sandbox": { "type": "boolean", "description": "Whether new accounts start with a sandbox." } } }, "updatedAt": { "type": [ "string", "null" ], "format": "date-time", "description": "When the listed prices last changed." } } }, "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", "FREE" ], "description": "Possible payment statuses. `FREE`: a coupon discounted the whole amount and the order completed without a charge; it is terminal and cannot be refunded." }, "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", "CHECKOUT_ABANDONED", "PAYMENT_FREE" ], "description": "Subscribable webhook event types. This value is delivered in the `X-Webhook-Event` header, never in the request body." }, "PaymentSplitInput": { "type": "object", "description": "One split recipient, set at creation. Shares are computed from `amount` (the gross), never from `netAmount` and never from the fee.", "properties": { "recipientCompanyId": { "type": "string", "format": "uuid", "description": "Company id of the YuvexPay account that receives this share. Must be a company other than your own, unique within `splits`, in the same environment as the payment (an approved split platform connected in sandbox is the exception), approved (KYC, plus KYB for a business), and it must have accepted split payments from your company. An approved split platform that is not connected yet can be named when the request carries its key in `X-Split-Platform-Key`. A split to an approved platform is limited by its fee cap (`SPLIT_FEE_CAP_EXCEEDED`)." }, "type": { "type": "string", "enum": [ "PERCENTAGE", "FIXED" ], "description": "Whether `value` is a percentage of `amount` or a fixed BRL amount. Both forms may coexist in the same `splits` array." }, "value": { "type": "number", "minimum": 0.01, "multipleOf": 0.01, "description": "For `PERCENTAGE`, a percentage of `amount` between `0.01` and `100` with at most two decimal places; the resulting share is rounded down to the cent and every remaining cent stays with your company. For `FIXED`, a BRL amount of at least `0.01`." } }, "required": [ "recipientCompanyId", "type", "value" ] }, "PaymentTrackingInput": { "type": "object", "description": "Attribution data for the payment, passed to the conversion plugins enabled for your account, such as GA4, Meta Conversions API, TikTok Events API and UTMify. Every field is optional and values are trimmed. A `gaClientId`, `fbp` or `fbc` that does not match its format is ignored and the payment is created without it. Any other field that is not a string or is longer than its limit fails the request with `400 VALIDATION_ERROR`. On the hosted checkout, the fields you leave empty are filled from the checkout URL's query parameters and from the checkout domain's cookies.", "properties": { "utmSource": { "type": "string", "maxLength": 512, "description": "The `utm_source` parameter of the landing URL." }, "utmMedium": { "type": "string", "maxLength": 512, "description": "The `utm_medium` parameter of the landing URL." }, "utmCampaign": { "type": "string", "maxLength": 512, "description": "The `utm_campaign` parameter of the landing URL." }, "utmTerm": { "type": "string", "maxLength": 512, "description": "The `utm_term` parameter of the landing URL." }, "utmContent": { "type": "string", "maxLength": 512, "description": "The `utm_content` parameter of the landing URL." }, "src": { "type": "string", "maxLength": 512, "description": "The `src` parameter of the landing URL." }, "sck": { "type": "string", "maxLength": 512, "description": "The `sck` parameter of the landing URL." }, "fbclid": { "type": "string", "maxLength": 512, "description": "Meta click id, the `fbclid` parameter of the landing URL. When sent without `fbc`, an `fbc` is built from it for Meta." }, "gclid": { "type": "string", "maxLength": 512, "description": "Google Ads click id, the `gclid` parameter of the landing URL." }, "ttclid": { "type": "string", "maxLength": 1000, "description": "TikTok click id, the `ttclid` parameter of the landing URL." }, "gaClientId": { "type": "string", "description": "GA4 client id, `.`. A raw `_ga` cookie value, `GA1.1..`, is accepted too and stored as `.`. Any other value is ignored." }, "fbp": { "type": "string", "description": "Value of the Meta `_fbp` cookie, `fb...`, up to 512 characters. Any other value is ignored." }, "fbc": { "type": "string", "description": "Value of the Meta `_fbc` cookie, `fb...`, up to 512 characters. Any other value is ignored." } } }, "PaymentItemInput": { "type": "object", "description": "One order line.", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120, "description": "Line name shown to the payer. Trimmed." }, "unitAmount": { "type": "number", "exclusiveMinimum": 0, "maximum": 1000000, "description": "Unit price in BRL, at most 2 decimal places." }, "quantity": { "type": "integer", "minimum": 1, "maximum": 999 }, "productId": { "type": "string", "format": "uuid", "description": "A product of your account in the environment of the key. Any other id returns `400 ITEM_PRODUCT_NOT_FOUND`. Coupons limited to products match lines by this id." }, "sku": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Your own code for the line. Trimmed." } }, "required": [ "name", "unitAmount", "quantity" ] }, "OrderBumpInput": { "type": "object", "description": "Order bump: an extra line the payer can tick on the hosted checkout before picking a method. Ticked, it joins the order as an `ORDER_BUMP` item and the totals, fee and percentage splits are computed again. A coupon limited to products covers it only when its `productId` is in the coupon's `productIds`. Not offered on a payment with a `FIXED` split.", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120, "description": "Offer name shown to the payer. Trimmed." }, "unitAmount": { "type": "number", "exclusiveMinimum": 0, "maximum": 1000000, "description": "Price in BRL, greater than 0, at most 2 decimal places." }, "description": { "type": "string", "maxLength": 500, "description": "Text shown with the offer. Trimmed; empty is the same as absent." }, "productId": { "type": "string", "format": "uuid", "description": "A product of your account in the environment of the key; its image is shown with the offer. Any other id returns `400 OFFER_PRODUCT_NOT_FOUND`." } }, "required": [ "name", "unitAmount" ] }, "UpsellInput": { "type": "object", "description": "Post-purchase upsell: offered on the hosted checkout for 30 minutes after this payment is paid. Accepting it creates a new payment, with `parentPaymentId` set to this payment and one `UPSELL` item, for the same payer and with the same splits. It is paid by PIX or, from the browser that paid this payment by card, in one click with the same card. Not offered on a payment with a `FIXED` split, never on a `FREE` payment, and not when the payer's changes on the checkout took the final `amount` below the upsell price.", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120, "description": "Offer name shown to the payer. Trimmed." }, "unitAmount": { "type": "number", "minimum": 1, "maximum": 1000000, "description": "Price in BRL, from 1.00 up to the `amount` of this payment after the coupon (`400 UPSELL_AMOUNT_TOO_HIGH` above it), at most 2 decimal places. The upsell payment never carries a fee passed to the payer." }, "description": { "type": "string", "maxLength": 500, "description": "Text shown with the offer. Trimmed; empty is the same as absent." }, "productId": { "type": "string", "format": "uuid", "description": "A product of your account in the environment of the key; its image is shown with the offer. Any other id returns `400 OFFER_PRODUCT_NOT_FOUND`." } }, "required": [ "name", "unitAmount" ] }, "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` or `items` is provided. With `items` it can be omitted; when sent, it must equal the sum of `unitAmount` x `quantity` exactly (`400 AMOUNT_ITEMS_MISMATCH`)." }, "methods": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentMethod" }, "minItems": 1, "default": [ "PIX" ], "description": "Accepted payment methods. Headless payments must use exactly one method, `PIX` or `BOLETO`. `CARD` requires `mode: hosted`; headless `CARD` is rejected with `400 CARD_PAYMENTS_NOT_AVAILABLE_VIA_API`." }, "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, PIX or BOLETO. CARD is available in hosted mode only: the YuvexPay checkout collects the card, accepts credit cards in a single installment and debit cards, and runs 3D Secure itself." }, "returnUrl": { "type": "string", "format": "uri", "description": "Hosted mode: the back link the checkout shows while the payment is unpaid, for a payer who leaves without paying. Only an http or https URL of up to 2048 characters is used; metadata.cancelUrl takes precedence. Use completionUrl for the redirect after payment." }, "completionUrl": { "type": "string", "format": "uri", "description": "Hosted mode: where the checkout sends the payer, after a 5-second countdown, once the payment is `PAID`, `CONFIRMED` or `FREE`. The checkout adds `yvx_payment_id`, `yvx_status`, `yvx_amount_cents`, `yvx_reference`, `yvx_ts` and `yvx_signature`, a lowercase hex HMAC-SHA256 of `payment_id.status.amount_cents.reference.ts` keyed with the account's redirect secret; see the payments guide to verify it. While a post-purchase upsell is offered, the checkout shows a button to this URL instead of the countdown. Only an http or https URL of up to 2048 characters is used. The redirect is not proof of payment: confirm with `GET /v1/payments/{paymentId}` or the webhook before fulfilling." }, "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 it cannot be enforced, 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. `cancelUrl`, an `http` or `https` URL of up to 2048 characters, shows a back link to that URL on the hosted checkout while the payment is unpaid; any other value under that key is ignored." }, "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. On a hosted payment the fee is computed again, at the rate of the method the payer picks, card included; the checkout shows each method with its own total. A payment that completes as `FREE` never passes a fee." }, "splits": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentSplitInput" }, "description": "Share part of this payment with other YuvexPay companies when it settles. Optional. The maximum number of entries is the recipient limit configured for your account — three by default, and support can raise it; going over it returns `SPLIT_RECIPIENT_LIMIT_EXCEEDED`. The sum of the splits must not exceed `netAmount`, otherwise the request fails with `SPLIT_AMOUNT_EXCEEDS_NET` and nothing is clamped. The YuvexPay fee is always yours and is never apportioned. Splits are set only at creation: there is no endpoint to add, edit or remove one afterwards." }, "tracking": { "$ref": "#/components/schemas/PaymentTrackingInput" }, "recoveryKit": { "type": "boolean", "description": "Turn the recovery kit on (`true`) or off (`false`) for this payment. When omitted, the account default applies (Ativar por padrão on the Kit de recuperação card in the dashboard, off until turned on). The kit e-mails a payer who filled in the hosted checkout and did not pay: up to 5 e-mails at the delays set for the account, 15 minutes, 1 hour and 24 hours unless changed, plus one about 10 minutes before a PIX code expires, sent only while the payment can still be paid. It starts when the hosted checkout saves the payer's details, or when the checkout is first opened for a payment whose customer already has them, so it does nothing on a headless payment. The kit price, shown in the dashboard, is debited once, when the first e-mail is sent, even if the balance goes negative. Sandbox payments never get a kit: `recoveryKit.enabled` is `false` on them. The result is returned as `recoveryKit` on the Payment object." }, "items": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "$ref": "#/components/schemas/PaymentItemInput" }, "description": "Order lines. The payment amount is the sum of `unitAmount` x `quantity`. The sum must be at most R$ 1,000,000.00 (`400 AMOUNT_TOO_HIGH`), and at least R$ 5.00 when `methods` has `CARD` or `BOLETO` (`400 MINIMUM_AMOUNT_FOR_CARD_OR_BOLETO`)." }, "itemsQuantityEditable": { "type": "boolean", "default": false, "description": "Let the payer change the quantity of each line on the hosted checkout before picking a method; the amount, the coupon discount, the fee and percentage splits are computed again. Ignored without `items` and on a payment with a `FIXED` split." }, "couponCode": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Coupon to apply, case-insensitive. A code that does not exist, is inactive, outside its validity window, out of uses, over the per-payer limit for `customer.email`, gives no discount on this order, or is sent with a `FIXED` split returns `400 COUPON_INVALID`, one answer for every reason. A discount of the whole amount completes the payment as `FREE`: at once for a headless payment, when the payer confirms on the hosted checkout." }, "orderBump": { "oneOf": [ { "$ref": "#/components/schemas/OrderBumpInput" }, { "type": "null" } ], "description": "Hosted only: in headless mode, the default, it returns `400 VALIDATION_ERROR` with `OFFERS_REQUIRE_HOSTED` in the message. See `OrderBumpInput`." }, "upsell": { "oneOf": [ { "$ref": "#/components/schemas/UpsellInput" }, { "type": "null" } ], "description": "Hosted only: in headless mode, the default, it returns `400 VALIDATION_ERROR` with `OFFERS_REQUIRE_HOSTED` in the message. See `UpsellInput`." }, "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." } } }, "SimulatePaymentRequest": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "PAID", "CANCELLED", "EXPIRED", "REFUNDED", "DISPUTED" ], "default": "PAID", "description": "Terminal outcome to apply. Defaults to `PAID` when the body is empty or omitted." } } }, "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" } } } }, "CreateCouponRequest": { "type": "object", "properties": { "code": { "type": "string", "description": "3 to 32 characters from `A-Z`, `0-9`, `_` and `-`, after trimming. Stored uppercase, so `black10` is saved as `BLACK10`. Unique among the coupons of your account in this environment that are not deleted (`409 COUPON_CODE_TAKEN`)." }, "type": { "type": "string", "enum": [ "PERCENT", "FIXED" ], "description": "`PERCENT` requires `percentBp` and `FIXED` requires `amount`; sending the other field, or neither, returns `400 COUPON_VALUE_INVALID`." }, "percentBp": { "type": "integer", "minimum": 1, "maximum": 10000, "description": "`PERCENT` only: the discount in basis points, `1` to `10000` (`10000` is 100%)." }, "amount": { "type": "number", "exclusiveMinimum": 0, "maximum": 1000000, "description": "`FIXED` only: the discount in BRL, at most 2 decimal places." }, "validFrom": { "type": [ "string", "null" ], "format": "date-time", "description": "ISO 8601 date-time with an offset or `Z`. Inclusive." }, "validUntil": { "type": [ "string", "null" ], "format": "date-time", "description": "ISO 8601 date-time with an offset or `Z`. Exclusive. Must be after `validFrom` (`400 COUPON_WINDOW_INVALID`)." }, "maxRedemptions": { "type": [ "integer", "null" ], "minimum": 1, "maximum": 1000000, "description": "Total uses, counting payments holding the coupon and completed ones. Omitted or `null`: no limit." }, "maxRedemptionsPerBuyer": { "type": [ "integer", "null" ], "minimum": 1, "maximum": 1000000, "description": "Uses per payer e-mail, checked when the e-mail is known. Omitted or `null`: no limit." }, "productIds": { "type": "array", "maxItems": 100, "default": [], "items": { "type": "string", "format": "uuid" }, "description": "Products of your account in this environment that the discount covers (`400 COUPON_PRODUCT_NOT_FOUND` otherwise). Empty: the whole order." }, "active": { "type": "boolean", "default": true } }, "required": [ "code", "type" ] }, "UpdateCouponRequest": { "type": "object", "description": "Send only the fields to change. `null` clears a nullable field. Changing `type` without the value of the new type returns `400 COUPON_VALUE_INVALID`, since the value of the old type is cleared.", "properties": { "code": { "type": "string", "description": "3 to 32 characters from `A-Z`, `0-9`, `_` and `-`, after trimming. Stored uppercase, so `black10` is saved as `BLACK10`. Unique among the coupons of your account in this environment that are not deleted (`409 COUPON_CODE_TAKEN`)." }, "type": { "type": "string", "enum": [ "PERCENT", "FIXED" ] }, "percentBp": { "type": [ "integer", "null" ], "minimum": 1, "maximum": 10000 }, "amount": { "type": [ "number", "null" ], "exclusiveMinimum": 0, "maximum": 1000000 }, "validFrom": { "type": [ "string", "null" ], "format": "date-time" }, "validUntil": { "type": [ "string", "null" ], "format": "date-time" }, "maxRedemptions": { "type": [ "integer", "null" ], "minimum": 1, "maximum": 1000000 }, "maxRedemptionsPerBuyer": { "type": [ "integer", "null" ], "minimum": 1, "maximum": 1000000 }, "productIds": { "type": "array", "maxItems": 100, "items": { "type": "string", "format": "uuid" }, "description": "Replaces the whole list." }, "active": { "type": "boolean" } } }, "PaymentSplit": { "type": "object", "description": "A split recorded on a payment. Immutable once the payment is created.", "properties": { "id": { "type": "string", "format": "uuid", "description": "Identifier of this split entry." }, "recipientCompanyId": { "type": "string", "format": "uuid", "description": "Company id of the YuvexPay account that receives this share." }, "type": { "type": "string", "enum": [ "PERCENTAGE", "FIXED" ], "description": "Whether `value` is a percentage of `amount` or a fixed BRL amount." }, "value": { "type": "number", "description": "The percentage or the BRL amount, exactly as it was sent at creation." } }, "required": [ "id", "recipientCompanyId", "type", "value" ] }, "PaymentItem": { "type": "object", "description": "An order line of a payment.", "properties": { "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "unitAmount": { "type": "number", "description": "Unit price in BRL." }, "quantity": { "type": "integer", "minimum": 1, "maximum": 999, "description": "Quantity. The payer may have changed it on the hosted checkout when the payment was created with `itemsQuantityEditable: true`." }, "kind": { "type": "string", "enum": [ "ITEM", "ORDER_BUMP", "UPSELL" ], "description": "`ITEM`: a line you sent in `items`. `ORDER_BUMP`: the order bump the payer ticked. `UPSELL`: the line of a payment created by a post-purchase upsell." }, "productId": { "type": [ "string", "null" ], "format": "uuid" }, "sku": { "type": [ "string", "null" ] } }, "required": [ "id", "name", "unitAmount", "quantity", "kind", "productId", "sku" ] }, "PaymentCoupon": { "type": [ "object", "null" ], "description": "The coupon applied to the payment, or `null`.", "properties": { "code": { "type": "string", "description": "The coupon code, uppercase.", "example": "BLACK10" } }, "required": [ "code" ] }, "Payment": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string", "description": "YuvexPay transaction ID." }, "amount": { "type": "number", "description": "Amount in BRL before any fee passed to the payer: `subtotal` minus `discount`. On a hosted payment it changes before the payer picks a method when the payer changes a quantity, ticks the order bump or applies a coupon. `0` on a `FREE` payment." }, "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. This figure is pre-split: when the payment carries `splits`, the originating company keeps `netAmount` minus the sum of the splits and the rest is credited to the recipients on settlement." }, "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`." }, "splits": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentSplit" }, "description": "Splits recorded on this payment, in the order they were sent. `[]` when the payment has none. Only the originating company can read them: a recipient calling `GET /v1/payments/{paymentId}` for this payment gets `404`." }, "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", "null" ], "description": "Payment method-specific data (QR code for PIX, masked card data for CARD, barcode for boleto). `null` while the payment has no method data: a boleto payment that is `PROCESSING` after a `502`, a card payment that is `PROCESSING` before the outcome of its charge is known, a payment recorded `CANCELLED` or `EXPIRED` before a charge was created, a hosted payment still in `PENDING_METHOD_SELECTION`, or a `FREE` payment. For CARD, `cardFundingType` is `credit` or `debit`, and `null` when the funding type is not known, including older payments.", "properties": { "type": { "$ref": "#/components/schemas/PaymentMethod" }, "cardFundingType": { "type": [ "string", "null" ], "enum": [ "credit", "debit", null ], "description": "CARD only. `credit` when the card was charged as a credit card in a single installment, `debit` when it was charged as a debit card. `null` when the funding type is not known, including older payments." } }, "additionalProperties": true }, "paidAt": { "type": [ "string", "null" ], "format": "date-time", "description": "Timestamp when the payment was confirmed paid. Null until paid, and on a `FREE` payment, which has `completedAt` instead." }, "completedAt": { "type": [ "string", "null" ], "format": "date-time", "description": "When a `FREE` payment completed. `null` on every other payment." }, "payer": { "type": [ "object", "null" ], "description": "Payer (debtor) identity captured from the PIX network once the charge is paid. Before that, `name` and `document` carry what you sent in `customer`, completed with what the payer typed on the hosted checkout. `email` and `phone` carry the payer's contact. `null` when none of the seven fields is known, for example on historical records or open-payer charges. A `payer` that has only `email` or `phone` is possible, so a non-null `payer` does not mean the payment was paid. Webhook `payer` blocks are unchanged and carry no `email` or `phone`.", "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)." }, "email": { "type": [ "string", "null" ], "description": "Payer e-mail: the one the payer typed on the hosted checkout, else the one in `customer`, or of the customer named in `customerId`, when the payment was created. `GET /v1/payments/{paymentId}` and `POST /v1/payments/{paymentId}/simulate` also fall back to the current e-mail of the customer linked to the payment; `GET /v1/payments` and `GET /v1/payments/txid/{txId}` do not. `null` when not known." }, "phone": { "type": [ "string", "null" ], "description": "Payer phone, with the same sources and fallback as `email`. `null` when not known." } } }, "recoveryKit": { "type": "object", "description": "Recovery kit of this payment. See `recoveryKit` on `POST /v1/payments`.", "properties": { "enabled": { "type": "boolean", "description": "The kit is on for this payment. `false` when the payment was created with the kit off, in sandbox, or before the kit existed, and on a payment-link payment whose payer already has a kit running on that link." }, "charged": { "type": "boolean", "description": "The kit price was debited from your balance. It is debited once, when the first e-mail of the kit is sent." }, "emailsSent": { "type": "integer", "minimum": 0, "description": "Recovery e-mails sent for this payment, including the one sent before a PIX code expires." } }, "required": [ "enabled", "charged", "emailsSent" ] }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentItem" }, "description": "Order lines, `[]` when the payment was created with an amount only, until the payer ticks an order bump." }, "subtotal": { "type": "number", "description": "Amount before the coupon, in BRL." }, "discount": { "type": "number", "description": "Coupon discount, in BRL. `0` without a coupon." }, "coupon": { "$ref": "#/components/schemas/PaymentCoupon" }, "parentPaymentId": { "type": [ "string", "null" ], "format": "uuid", "description": "On a payment created by a post-purchase upsell, the payment whose upsell the payer accepted. `null` otherwise." } } }, "Refund": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string" }, "amount": { "type": "number" }, "reason": { "type": [ "string", "null" ], "description": "The reason sent with the refund request. `Pedido de reembolso do comprador` on a refund the payer requested and you approved, or that was approved automatically. `Reembolso feito pela YuvexPay` on a refund YuvexPay made after a payer's refund request: it debits the full amount returned to the payer, YuvexPay keeps the processing fee and the balance can go negative. `null` when none was sent and on refunds that reached YuvexPay from the payment network, such as the return of a PIX MED." }, "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": "Generic message from the same fixed set as the `WITHDRAWAL_FAILED` webhook `failureReason`, or `Saque em processamento.` while the withdrawal is `PENDING` or `PROCESSING`. `null` when no reason was recorded or the withdrawal is `COMPLETED`." }, "providerErrorCode": { "type": [ "string", "null" ], "description": "Rejection code reported by the receiving institution: `AC03`, `AC06`, `AC07`, `AG03`, `AM18`, `RR04`, `SL02`, `BE01`, `CH11`, `PIX_KEY_NOT_FOUND`, `PIX_KEY_OWNER_MISMATCH`, `ACCOUNT_CLOSED` or `ACCOUNT_BLOCKED`. `null` for every other failure and for withdrawals that did not fail." } }, "description": "The withdrawal as returned by `GET /v1/withdrawals/{withdrawalId}`." }, "WithdrawalListItem": { "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" }, "createdAt": { "type": "string", "format": "date-time" }, "paymentMethod": { "type": "string", "enum": [ "PIX" ] }, "currency": { "type": "string", "enum": [ "BRL" ] }, "processedAt": { "type": [ "string", "null" ], "format": "date-time" }, "providerErrorCode": { "type": [ "string", "null" ], "description": "Rejection code reported by the receiving institution: `AC03`, `AC06`, `AC07`, `AG03`, `AM18`, `RR04`, `SL02`, `BE01`, `CH11`, `PIX_KEY_NOT_FOUND`, `PIX_KEY_OWNER_MISMATCH`, `ACCOUNT_CLOSED` or `ACCOUNT_BLOCKED`. `null` for every other failure and for withdrawals that did not fail." } }, "description": "A withdrawal as returned by `GET /v1/withdrawals`. It carries no `errorMessage` and no `isSandbox`; read `GET /v1/withdrawals/{withdrawalId}` for the failure message." }, "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`. When a withdrawal ceiling applies to your account, `remaining` never exceeds what that ceiling still allows, and a request above it fails with `400 INSUFFICIENT_BALANCE`, like one above your available balance." }, "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 minimum of the PIX route that sends the payout, 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 on the PIX route that sends 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" } } }, "Coupon": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "code": { "type": "string", "description": "Uppercase code, 3 to 32 characters from `A-Z`, `0-9`, `_` and `-`.", "example": "BLACK10" }, "type": { "type": "string", "enum": [ "PERCENT", "FIXED" ] }, "percentBp": { "type": [ "integer", "null" ], "minimum": 1, "maximum": 10000, "description": "`PERCENT` only: the discount in basis points, `1500` for 15% and `10000` for 100%. `null` on a `FIXED` coupon." }, "amount": { "type": [ "number", "null" ], "description": "`FIXED` only: the discount in BRL. `null` on a `PERCENT` coupon." }, "validFrom": { "type": [ "string", "null" ], "format": "date-time", "description": "The coupon works from this time on, inclusive. `null`: no start." }, "validUntil": { "type": [ "string", "null" ], "format": "date-time", "description": "The coupon stops working at this time, exclusive. `null`: no end." }, "maxRedemptions": { "type": [ "integer", "null" ], "minimum": 1, "description": "Total uses. `null`: no limit." }, "maxRedemptionsPerBuyer": { "type": [ "integer", "null" ], "minimum": 1, "description": "Uses per payer e-mail. `null`: no limit." }, "productIds": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Products the discount covers. Empty: the whole order." }, "active": { "type": "boolean" }, "redemptionCount": { "type": "integer", "minimum": 0, "description": "Uses taken: payments holding the coupon that are still open, plus the completed ones. A payment that expires or is cancelled gives its use back." }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "code", "type", "percentBp", "amount", "validFrom", "validUntil", "maxRedemptions", "maxRedemptionsPerBuyer", "productIds", "active", "redemptionCount", "createdAt", "updatedAt" ] }, "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`, `held`, and `reserved` are independent and additive — `total = available + frozen + held + reserved`.", "properties": { "available": { "type": "number", "description": "Settled funds free to use, in BRL. The largest withdrawal you can request right now is also bounded by `remaining` and `perTransactionLimit` from `GET /v1/withdrawals/limits`. This can be negative: when a refund or a PIX MED is apportioned across a split, each recipient is debited its share even if it has already withdrawn the money. A negative balance blocks withdrawals until it is covered by new settlements." }, "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." }, "reserved": { "type": "number", "description": "Rolling reserve: a share of settled funds withheld when your account is subject to a reserve policy, released back to `available` on each release date. Not currently withdrawable, and not usable to fund a refund you issue, until released. A split credit received from another YuvexPay company follows your own reserve policy, not the originating company's; when a refund or MED debit lands on a split share that is still reserved, that payment's own reserve hold is released first and the debit is written against it. `0` when no reserve applies." }, "total": { "type": "number", "description": "Total account balance: `available + frozen + held + reserved`." }, "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", "reserved", "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": "Amount in BRL before any fee passed to the payer: `subtotal` minus `discount`." }, "feeAmount": { "type": "number", "description": "YuvexPay platform fee." }, "netAmount": { "type": "number", "description": "Merchant's net receivable, pre-split. When the payment carries `splits`, the originating company keeps `netAmount` minus the sum of the splits." }, "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." }, "splits": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentSplit" }, "description": "Splits recorded on this payment, in the order they were sent. `[]` when the payment has none." }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentItem" }, "description": "Order lines, `[]` when the payment was created with an amount only." }, "subtotal": { "type": "number", "description": "Amount before the coupon, in BRL." }, "discount": { "type": "number", "description": "Coupon discount, in BRL. `0` without a coupon." }, "coupon": { "$ref": "#/components/schemas/PaymentCoupon" }, "isSandbox": { "type": "boolean" }, "status": { "allOf": [ { "$ref": "#/components/schemas/PaymentStatus" } ], "description": "`FREE` when `couponCode` discounted the whole amount: the payment completed on this request, with no charge." }, "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", "null" ], "additionalProperties": true, "properties": { "type": { "$ref": "#/components/schemas/PaymentMethod" }, "cardFundingType": { "type": [ "string", "null" ], "enum": [ "credit", "debit", null ], "description": "CARD only. `credit` when the card was charged as a credit card in a single installment, `debit` when it was charged as a debit card. `null` when the funding type is not known, including older payments." } }, "description": "Method-specific data. PIX returns QR data. BOLETO returns the digitable line and barcode. CARD returns masked card data and `cardFundingType`, only when the request returns an existing card payment. `null` when the request returns an existing boleto payment that is `PROCESSING` after a `502`, one recorded `CANCELLED` before a charge was created, or a payment created as `FREE`.", "example": { "type": "PIX", "pixCopyPaste": "00020126...", "qrCodeBase64": "data:image/png;base64,...", "qrCodeUrl": null } } } }, "HostedPaymentCreateResponse": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "txId": { "type": "string" }, "amount": { "type": "number", "description": "Amount in BRL before any fee passed to the payer: `subtotal` minus `discount`." }, "feeAmount": { "type": "number", "description": "YuvexPay platform fee." }, "netAmount": { "type": "number", "description": "Merchant's net receivable, pre-split. When the payment carries `splits`, the originating company keeps `netAmount` minus the sum of the splits." }, "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." }, "splits": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentSplit" }, "description": "Splits recorded on this payment, in the order they were sent. `[]` when the payment has none." }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentItem" }, "description": "Order lines, `[]` when the payment was created with an amount only." }, "subtotal": { "type": "number", "description": "Amount before the coupon, in BRL." }, "discount": { "type": "number", "description": "Coupon discount, in BRL. `0` without a coupon." }, "coupon": { "$ref": "#/components/schemas/PaymentCoupon" }, "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 a masked string (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 receiving account was reported back after the transfer. `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 it is reported back." }, "account": { "type": [ "string", "null" ], "description": "Recipient account, when it is reported back." } } }, "WithdrawalCreated": { "type": "object", "description": "The withdrawal as returned by `POST /v1/withdrawals`. This response is deliberately narrower than `GET /v1/withdrawals/{withdrawalId}`: it omits `pixKey`, `pixKeyType`, `processedAt`, `errorMessage` and `providerErrorCode`, 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" } } }, "SplitPartner": { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "Identifier of the relationship, not of the company." }, "companyId": { "type": "string", "format": "uuid", "description": "Company ID of the counterparty. This is the value to send as `recipientCompanyId` in `splits[]`." }, "tradeName": { "type": "string", "description": "Trade name of the counterparty." }, "direction": { "type": "string", "enum": [ "OUTGOING", "INCOMING" ], "description": "`OUTGOING` when your company originates the split, `INCOMING` when it receives one." }, "status": { "type": "string", "enum": [ "PENDING", "ACCEPTED", "REVOKED", "DECLINED", "CANCELLED" ], "description": "Only `ACCEPTED` can be named in `splits[]`. `ACCEPTED` is the active state." }, "feeCap": { "type": [ "string", "null" ], "pattern": "^\\d+\\.\\d{2}$", "example": "4.00", "description": "Fee cap recorded on the relationship when the recipient is an approved split platform: the most it may take of each payment, in percent of `amount`, as a string with two decimal places. `null` for an ordinary partner. The cap applied to a payment can be lower than this value: see the Split platforms guide." }, "requestedAt": { "type": "string", "format": "date-time" }, "acceptedAt": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "id", "companyId", "tradeName", "direction", "status", "feeCap", "requestedAt", "acceptedAt" ] } }, "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" } } } } } } } }