Welcome to the Cashout API documentation. This guide provides a comprehensive overview of how to use our PIX transfer system to send money from your accounts to recipients via the Brazilian instant payment network.
Table of Contents
- What is Cashout?
- Approval Workflows
- PIX Key Types and Formats
- Account Types for Manual Transfers
- Authentication
- Idempotency
- Transaction Status Flow
- Available Endpoints
- Common Business Rules
- Getting Started
What is Cashout?
Cashout is the process of transferring money from your account to external recipients using the PIX instant payment system. PIX is Brazil's instant payment platform that enables real-time transfers 24/7, including weekends and holidays.
With our Cashout API, you can:
- Send money using PIX keys (email, phone, CPF/CNPJ, or random key)
- Transfer funds using complete bank account details
- Use PIX copy-and-paste codes (BR Codes) for payments
- Implement single-step or two-step approval workflows
- Ensure safe retries with idempotency keys
Approval Workflows
Our API supports two different approval workflows to suit your business needs:
Two-Step Approval (Create + Approve)
Use Case: Organizations that require manual review, compliance checks, or dual authorization before processing transfers.
Flow:
- Create Transaction - Use
/create-cashoutor/create-cashout-manualto create a transaction with statusNEW - Review - Perform any necessary validation or approval processes
- Approve Transaction - Use
/approve-cashoutto approve and initiate the transfer (status changes toPROCESSING)
Benefits:
- Clear audit trail with separate creation and approval steps
- Time to review transactions before processing
- Supports different user roles (creator vs approver)
- Compliance-friendly for organizations with strict controls
Endpoints:
- POST /create-cashout - Create with PIX key
- POST /create-cashout-manual - Create with manual account details
- POST /approve-cashout - Approve pending transaction
One-Step Approval (Self-Approve)
Use Case: Trusted operations where immediate processing is required without separate approval.
Flow:
- Create and Approve - Single API call creates the transaction and immediately processes it (status goes from
NEWtoPROCESSING) - Background Processing - Transfer is initiated automatically
Benefits:
- Faster processing for trusted operations
- Simplified integration with fewer API calls
- Ideal for automated systems or low-risk transfers
Important: Always use idempotency keys with self-approve endpoints to prevent duplicate transfers!
Endpoints:
- POST /create-cashout-self-approve - PIX key with auto-approval
- POST /create-cashoutmanual-self-approve - Manual details with auto-approval
- POST /create-copy-and-paste-transfer - BR Code with auto-approval
- POST /create-manual-transfer - Web-based transfer with MFA
PIX Key Types and Formats
PIX keys are identifiers that simplify transfers by eliminating the need to provide complete bank account details. Our API supports all PIX key types:
1. Email
- Format: Standard email format
- Example:
[email protected] - Validation: Must be a valid email address
- Case Sensitivity: Case-insensitive
2. Phone Number
- Format: Must include country code with
+prefix - Example:
+5511999999999 - Pattern:
+55(Brazil) +11(area code) +9(mobile) +XXXXXXXX(number) - Validation:
- Must start with
+55for Brazilian numbers - Mobile numbers: 11 digits after country code (includes the digit 9)
- Landline numbers: 10 digits after country code
- Must start with
- Important: Always include the
+prefix and country code
3. CPF (Individual Tax ID)
- Format: 11 digits, numbers only (no formatting)
- Example:
11122233344 - Validation: Must be a valid CPF number
- Note: Do not include dots or dashes (
.or-)
4. CNPJ (Company Tax ID)
- Format: 14 digits, numbers only (no formatting)
- Example:
11222333000144 - Validation: Must be a valid CNPJ number
- Note: Do not include dots, dashes, or slashes
5. EVP (Random Key)
- Format: UUID v4 format
- Example:
123e4567-e89b-12d3-a456-426614174000 - Description: Randomly generated key created by the recipient's bank
- Validation: Must be a valid UUID format with hyphens
PIX Key Format Summary Table
| Key Type | Format | Example | Notes |
|---|---|---|---|
| [email protected] | [email protected] | Case-insensitive | |
| Phone | +55AAXXXXXXXXX | +5511999999999 | Must include +55 prefix |
| CPF | 11 digits | 11122233344 | Numbers only, no formatting |
| CNPJ | 14 digits | 11222333000144 | Numbers only, no formatting |
| EVP | UUID v4 | 123e4567-e89b-12d3-a456-426614174000 | Random key with hyphens |
Account Types for Manual Transfers
When creating cashout transactions using manual recipient account details (instead of PIX keys), you must specify the account type and account model:
Account Type Values
The recipient_account_type field accepts these values:
| Value | Description | Use Case |
|---|---|---|
| CURRENT_ACCOUNT | Current Account | Standard checking account for daily transactions |
| PAYMENT_ACCOUNT | Payment Account | Digital payment account (e.g., fintech or digital banks) |
| SAVING_ACCOUNT | Savings Account | Savings account with limited transaction frequency |
Account Model
The recipient_account_model field must always be set to the fixed value:
"recipient_account_model": "Movement"
Important: This is a required field with a fixed value when using manual account details.
Example
{
"recipient_account_type": "CURRENT_ACCOUNT",
"recipient_account_model": "Movement"
}
Applies to these endpoints:
Authentication
All Cashout API endpoints require two authentication mechanisms:
1. Bearer Token (Authorization Header)
Authenticate your requests using a JWT access token:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
How to obtain:
- Use the authentication endpoint to obtain an access token
- Tokens have an expiration time and must be refreshed when expired
2. HMAC Signature (hmac Header)
Ensure request integrity using HMAC-SHA256 signature:
hmac: 57373705c83bc5efe41001790c54642e670088c0c87d56bc8f990f2260c7740b...
How it works:
- HMAC (Hash-based Message Authentication Code) combines your secret key with the request body
- Generates a cryptographic hash that verifies the request hasn't been tampered with
- Both headers are required for all requests
Important: Never share your HMAC secret key. Store it securely and use it only server-side.
Idempotency
Idempotency is a mechanism to safely retry API requests without risk of creating duplicate transactions. This is critically important for financial operations.
What is Idempotency?
When you include an idempotency-key header in your request, the API guarantees that retrying the same request (with the same key and body) will not create duplicate transactions.
How It Works
idempotency-key: 550e8400-e29b-41d4-a716-446655440000
Scenarios:
-
First Request (HTTP 200 - New Transaction Created):
- Idempotency key is new (doesn't exist in database)
- API creates a new transaction and returns details
- Response includes a new
transaction_id - Webhooks will be sent when transaction completes
-
Retry with Same Key + Same Body (HTTP 200 - Existing Transaction Returned):
- Idempotency key already exists in database with the same request body
- API returns the existing transaction (no new transaction created)
- Response has the same
transaction_idas the first request - Status reflects current database state: Can be
PROCESSING,SUCCESS,ERROR,CANCELLED, orREFUNDED - Important: No new webhooks are sent - this is a replay of the original request
- Safe to retry without risk of duplicate transfers
- Always check status field - Don't assume the transaction succeeded
-
Same Key + Different Body (HTTP 409 Conflict):
- Idempotency key already exists but with a different request body
- API rejects the request to prevent data inconsistency
- Returns error response with details
- Action required: Generate a new idempotency key for the new transaction
Example Error Response:
{
"worked": false,
"detail": "Idempotency key already used with a different request",
"message": "Idempotency key already used with a different request",
"data": null
}
Key Format
- Format: UUID version 4 (RFC 4122)
- Example:
550e8400-e29b-41d4-a716-446655440000 - Generation: Use a cryptographically secure UUID v4 generator
- Scope: Unique per account (not globally unique)
- Case: Case-insensitive, but lowercase is recommended
When to Use
| Endpoint Type | Idempotency Key |
|---|---|
| Two-Step (Create) | Recommended - Prevents duplicate creation |
| One-Step (Self-Approve) | Strongly Recommended - Prevents duplicate transfers and financial losses |
| Approve | Not needed - Status validation prevents duplicates |
Best Practices
- Generate on Client: Create the UUID before making the request
- Store with Transaction: Save both the idempotency key and
transaction_idfrom the response - Check Transaction ID on Retry: If you retry a request, compare the returned
transaction_idwith your stored value:- Same transaction_id = This is the existing transaction (no new transfer will be processed)
- Different transaction_id = This is a new transaction (new transfer will be processed)
- One Key per Transaction: Never reuse idempotency keys for different transactions
- Retry with Same Key: Always use the same idempotency key when retrying the exact same request
- Handle 409 Conflicts: If you receive HTTP 409, it means you're trying to use an existing key with different data - generate a new idempotency key
- Permanent Storage: Idempotency keys are stored indefinitely in the system
Practical Example: Handling Retries
First Request - New Transaction Created
POST /create-cashout-self-approve
idempotency-key: 550e8400-e29b-41d4-a716-446655440000
Request Body:
{
"source_account_branch_identifier": "0001",
"source_account_number": "123456",
"amount": 100.00,
"key": "111*****999"
}
Response (HTTP 200):
{
"worked": true,
"transaction_id": 1134,
"status": "PROCESSING",
"amount": 100.00,
"fee": 0.0,
"key": "111*****999",
"from_accout": "123456",
"code_transaction": null,
"recipient_name": null,
"recipient_account_id": null,
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"erro_descriptor": ""
}
β
Action: Store transaction_id: 1134 and idempotency_key in your database for tracking.
Retry Request - Existing Transaction Returned
Network error occurs, you retry with the same idempotency key and same body:
POST /create-cashout-self-approve
idempotency-key: 550e8400-e29b-41d4-a716-446655440000
Request Body: (exactly the same as before)
{
"source_account_branch_identifier": "0001",
"source_account_number": "123456",
"amount": 100.00,
"key": "111*****999"
}
Response (HTTP 200) - Example 1: Successful Transaction
{
"worked": true,
"transaction_id": 1134,
"status": "SUCCESS",
"amount": 100.00,
"fee": 0.0,
"key": "111*****999",
"from_accout": "123456",
"code_transaction": "E12345678901234567890123456789AB",
"recipient_instution": "12345678",
"recipient_account_id": "654321",
"recipient_branch_id": "0001",
"recipient_legal_id": "111*****999",
"recipient_name": "RECIPIENT NAME",
"recipient_account_type": "CURRENT_ACCOUNT",
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"erro_descriptor": ""
}
Response (HTTP 200) - Example 2: Cancelled Transaction
{
"worked": true,
"transaction_id": 1134,
"status": "CANCELLED",
"amount": 100.00,
"fee": 0.0,
"key": "111*****999",
"from_accout": "123456",
"code_transaction": null,
"recipient_instution": null,
"recipient_account_id": null,
"recipient_branch_id": null,
"recipient_legal_id": null,
"recipient_name": null,
"recipient_account_type": null,
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"erro_descriptor": "Transaction cancelled - Account limit exceeded"
}
Response (HTTP 200) - Example 3: Error Transaction
{
"worked": true,
"transaction_id": 1134,
"status": "ERROR",
"amount": 100.00,
"fee": 0.0,
"key": "111*****999",
"from_accout": "123456",
"code_transaction": null,
"recipient_instution": null,
"recipient_account_id": null,
"recipient_branch_id": null,
"recipient_legal_id": null,
"recipient_name": null,
"recipient_account_type": null,
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"erro_descriptor": "Payment processing error - Insufficient funds"
}
Response (HTTP 200) - Example 4: Refunded Transaction
{
"worked": true,
"transaction_id": 1134,
"status": "REFUNDED",
"amount": 100.00,
"fee": 0.0,
"key": "111*****999",
"from_accout": "123456",
"code_transaction": "E12345678901234567890123456789AB",
"recipient_instution": "12345678",
"recipient_account_id": "654321",
"recipient_branch_id": "0001",
"recipient_legal_id": "111*****999",
"recipient_name": "RECIPIENT NAME",
"recipient_account_type": "CURRENT_ACCOUNT",
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"erro_descriptor": "Transaction was refunded by recipient"
}
{
"worked": true,
"transaction_id": 1134,
"status": "CANCELLED",
"amount": 100.00,
"fee": 0.0,
"key": "111*****999",
"from_accout": "123456",
"code_transaction": null,
"recipient_instution": null,
"recipient_account_id": null,
"recipient_branch_id": null,
"recipient_legal_id": null,
"recipient_name": null,
"recipient_account_type": null,
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"erro_descriptor": "Transaction cancelled - Account limit exceeded"
}
π How to Identify This is the Same Transaction:
Compare the transaction_id from both responses:
- First request:
transaction_id: 1134 - Retry request:
transaction_id: 1134β SAME ID!
β This means: No new transaction was created. You are receiving the current status of the existing transaction from the database.
Key Observations:
- Same
transaction_id(1134) - This is your primary indicator that no duplicate was created - Status reflects database state - Can be
PROCESSING,SUCCESS,ERROR,CANCELLED, orREFUNDEDdepending on transaction outcome - Always check the
statusfield - Don't assume the transaction succeeded - Check
erro_descriptor- If status isERROR,CANCELLED, orREFUNDED, this field contains the reason - Fields populated on success -
code_transaction,recipient_name, etc. only have values when status isSUCCESS - Same
idempotency_key- Confirms this is the replay of your original request
Important: The response always reflects the real-time state of the transaction in the database, not a cached response.
Conflict Request - Different Body with Same Key
You try to create a different transaction using the same idempotency key:
POST /create-cashout-self-approve
idempotency-key: 550e8400-e29b-41d4-a716-446655440000
Request Body: (DIFFERENT from original - different amount)
{
"source_account_branch_identifier": "0001",
"source_account_number": "123456",
"amount": 200.00,
"key": "111*****999"
}
Response (HTTP 409 Conflict):
{
"worked": false,
"detail": "Idempotency key already used with a different request",
"message": "Idempotency key already used with a different request",
"data": null
}
β This means: The idempotency key is already associated with a different transaction (different request body).
Action Required:
- Generate a new idempotency key for this new transaction
- Never reuse idempotency keys for different transactions
- Each unique transaction must have its own unique idempotency key
Implementation Example
// First attempt
const idempotencyKey = "550e8400-e29b-41d4-a716-446655440000";
const requestBody = {
source_account_branch_identifier: "0001",
source_account_number: "123456",
amount: 100.00,
key: "111*****999"
};
// Store in your database before making the request
await db.saveTransaction({
idempotency_key: idempotencyKey,
request_body: requestBody,
status: "PENDING"
});
const response1 = await createCashout(idempotencyKey, requestBody);
// { transaction_id: 1134, status: "PROCESSING", ... }
// Update your database with the transaction_id
await db.updateTransaction({
idempotency_key: idempotencyKey,
transaction_id: response1.transaction_id,
status: response1.status
});
// Network error or timeout - retry with SAME idempotency key
const response2 = await createCashout(idempotencyKey, requestBody);
// { transaction_id: 1134, status: "SUCCESS", ... }
// Compare transaction_id
const storedTransaction = await db.getTransactionByIdempotencyKey(idempotencyKey);
if (response2.transaction_id === storedTransaction.transaction_id) {
// β
SAME TRANSACTION - Safe replay
console.log("Same transaction returned - no duplicate created");
// Update status with current information
await db.updateTransaction({
transaction_id: response2.transaction_id,
status: response2.status,
code_transaction: response2.code_transaction,
recipient_name: response2.recipient_name
});
// Do NOT wait for a new webhook - use current status
if (response2.status === "SUCCESS") {
console.log("Transaction completed successfully!");
} else if (response2.status === "ERROR") {
console.error("Transaction error:", response2.erro_descriptor);
} else if (response2.status === "CANCELLED") {
console.error("Transaction cancelled:", response2.erro_descriptor);
} else if (response2.status === "REFUNDED") {
console.warn("Transaction was refunded:", response2.erro_descriptor);
} else if (response2.status === "PROCESSING") {
console.log("Transaction still being processed...");
}
} else {
// β DIFFERENT TRANSACTION (this should not happen with same key+body)
console.error("Unexpected: different transaction_id returned!");
}
How Idempotency Works Internally
Our API validates idempotency directly against the database:
-
First Request:
- Idempotency key doesn't exist in database
- New transaction is created and stored
- Response is returned with new
transaction_idand initial status (NEWorPROCESSING)
-
Retry with Same Key + Same Body:
- System finds existing transaction with the idempotency key
- Compares stored request body with incoming request body
- Bodies match β Returns existing transaction with current database state
- Important: Status reflects real-time transaction outcome:
PROCESSING- Still being processedSUCCESS- Transfer completed successfullyERROR- Payment processing error (checkerro_descriptor)CANCELLED- Transaction cancelled due to business rules or restrictions (checkerro_descriptor)REFUNDED- Transaction was completed but later refunded (checkerro_descriptor)
-
Same Key + Different Body:
- System finds existing transaction with the idempotency key
- Compares stored request body with incoming request body
- Bodies don't match β Returns HTTP 409 Conflict
Key Differences:
- β Permanent storage: Idempotency keys are stored indefinitely (not just 24 hours)
- β Real-time status: Retries return the current transaction state from database, not a cached response
- β Database-based: Direct comparison with stored transactions, not cache lookup
- β Live transaction state: Status can change between retries as the transaction progresses
Critical Points for Clients
- Always compare
transaction_idbetween requests to identify if you received a new or existing transaction - Always check the
statusfield - Don't assume success! The transaction may have been cancelled, encountered an error, or been refunded - Status can vary - The same transaction can return different statuses depending on when you retry:
- First call:
PROCESSING - Retry 1 minute later:
SUCCESS,ERROR,CANCELLED, orREFUNDED
- First call:
- Check
erro_descriptoron failures - If status isERROR,CANCELLED, orREFUNDED, this field explains why - Don't wait for duplicate webhooks - Retried requests don't trigger new webhook notifications
- Use the returned data - When you receive an existing transaction, its status reflects the real database state
- Store idempotency keys permanently - Keep them in your database for audit and reconciliation purposes
Transaction Status Flow
Understanding transaction statuses helps you track and manage transfers:
βββββββββββββββ
β NEW β β Transaction created (Two-Step only)
ββββββββ¬βββββββ
β
β Approval (manual or automatic)
β
βββββββββββββββ
β PROCESSING β β Transfer being executed
ββββββββ¬βββββββ
β
ββββ Success βββββββββββ βββββββββββ
β β SUCCESS β β Transfer completed successfully
β ββββββ¬βββββ
β β
β β Refund requested
β β
β ββββββββββββ
β β REFUNDED β β Money returned to sender
β ββββββββββββ
β
ββββ Payment Error ββββββ βββββββββββ
β β ERROR β β Payment processing failed
β βββββββββββ
β
ββββ Business Rules βββββ ββββββββββββ
βCANCELLED β β Cancelled due to restrictions
ββββββββββββ
Status Descriptions
| Status | Description | Next Steps |
|---|---|---|
| NEW | Transaction created, waiting for approval | Call /approve-cashout to process |
| PROCESSING | Transfer is being executed by the payment system | Wait for final status (async) |
| SUCCESS | Transfer completed successfully | No action needed (money sent) |
| ERROR | Payment processing error occurred | Check erro_descriptor field for error details (e.g., insufficient funds, invalid recipient, payment system unavailable). Create a new transaction if needed |
| CANCELLED | Transaction cancelled due to business rules or restrictions | Check erro_descriptor field for cancellation reason (e.g., account limits, blocked operations, compliance restrictions). Create a new transaction if needed |
| REFUNDED | Transaction was successful but later refunded | Check erro_descriptor field for refund reason. Money was returned to the sender account |
Available Endpoints
Two-Step Approval Endpoints
| Method | Endpoint | Description | Documentation |
|---|---|---|---|
| POST | /create-cashout | Create transaction using PIX key | View Docs |
| POST | /create-cashout-manual | Create transaction with manual account details | View Docs |
| POST | /approve-cashout | Approve a pending transaction | View Docs |
| POST | /approve-cashout-manual | Approve a pending manual transaction | View Docs |
One-Step Self-Approve Endpoints
| Method | Endpoint | Description | Documentation |
|---|---|---|---|
| POST | /create-cashout-self-approve | Create and auto-approve with PIX key | View Docs |
| POST | /create-cashoutmanual-self-approve | Create and auto-approve with manual details | View Docs |
| POST | /create-copy-and-paste-transfer | Create and auto-approve with BR Code | View Docs |
| POST | /create-manual-transfer | Web-based transfer with MFA validation | View Docs |
Common Business Rules
These rules apply to all Cashout endpoints:
Amount Validation
- Must be greater than zero
- Maximum of 2 decimal places
- Example valid amounts:
100.00,50.50,1000 - Example invalid amounts:
0,-10.00,100.999
Account Identification
- Branch Identifier: 4-digit code (e.g.,
0001) - Account Number: Numeric string (e.g.,
123456) - Both are required to identify the source account
Recipient Account Details (Manual Transfers)
When not using a PIX key, you must provide:
- Recipient name
- Recipient CPF or CNPJ
- Recipient bank ISPB code (8 digits)
- Recipient account number and digit
- Recipient branch number
- Account type (Current, Savings, or Payment)
HMAC Validation
- All request bodies must be validated with HMAC signature
- Invalid HMAC results in
400 Bad Requestwith response:{"worked": false, "detail": "HMAC invalid", "message": "HMAC invalid", "data": null}
Getting Started
Quick Start Guide
-
Choose Your Workflow:
- Need approval workflow? Use
/create-cashout+/approve-cashout - Need immediate processing? Use
/create-cashout-self-approve
- Need approval workflow? Use
-
Prepare Authentication:
- Obtain your Bearer token
- Generate HMAC signature for your request body
-
Generate Idempotency Key:
// JavaScript example const { v4: uuidv4 } = require('uuid'); const idempotencyKey = uuidv4(); -
Format Your PIX Key:
- Phone:
+5511999999999(with+and country code) - Email:
[email protected] - CPF:
11122233344(numbers only)
- Phone:
-
Make Your First Request:
POST /create-cashout-self-approve Authorization: Bearer <your_token> hmac: <computed_hmac> idempotency-key: 550e8400-e29b-41d4-a716-446655440000 Content-Type: application/json { "source_account_branch_identifier": "0001", "source_account_number": "123456", "amount": 100.00, "key": "+5511999999999" } -
Handle the Response:
- Check
worked: truefor success - Store the
transaction_idfor tracking - Check
statusfield (NEWorPROCESSING)
- Check
Example Use Cases
Use Case 1: Automated Payouts
- Use self-approve endpoints for faster processing
- Always include idempotency keys
- Monitor transaction status asynchronously
Use Case 2: Manual Review Required
- Use two-step approval endpoints
- Implement approval UI for authorized users
- Maintain audit trail of who created vs who approved
Use Case 3: Customer-Initiated Transfers
- Use
/create-manual-transferfor web applications - Require MFA validation for security
- Allow customers to choose between PIX key or manual details
Error Handling
All endpoints return standard HTTP status codes:
| Status Code | Meaning | Action |
|---|---|---|
| 200 | Success | Process response data |
| 400 | Bad Request | Check request body, parameters, or HMAC signature |
| 401 | Unauthorized | Verify Bearer token |
| 409 | Conflict | Idempotency key conflict - use new key |
Support and Additional Resources
For detailed information about specific endpoints, refer to their individual documentation:
- Create Cashout (PIX Key)
- Create Cashout (Manual Details)
- Approve Cashout Manual
- Approve Cashout Pix Key
- Create Cashout Self-Approve (PIX Key)
- Create Cashout Self-Approve (Manual)
- Copy and Paste Transfer
Last Updated: May 2026
API Version: v3
