Paying Pix
Send payments to Brazilian bank accounts via Pix
This endpoint allows you to make payments from USDT to any given Pix key, which will reflect the balance in seconds into any bank account in Brazil.
Payments are processed through our prepaid system with direct bank settlement for optimal speed and reliability.
Request Body
| Name | Type | Description |
|---|---|---|
| pix_key | string | Required. The Pix key of the recipient. Can be a CPF/CNPJ, email, phone number, or random key. |
| currency | string | Required. Either 'BRL' or 'USDT'. Determines how the amount is interpreted. |
| amount | float | Required. Amount to be sent. Minimum value is 0.5 (validated as USDT equivalent regardless of the currency field). |
| from_address | string | Required. The sender wallet address. Must be a whitelisted address registered in your merchant wallets. |
| payer_name | string | Required. Full name of the End User. |
| payer_id_type | string | Required. Type of the End User's identity document, one of the Document Types. |
| payer_document | string | Required. Number of the End User's identity document. |
| payer_country | string | Required, except with a CPF or CNPJ. Country that issued the End User's identity document, as an ISO 3166-1 alpha-2 or alpha-3 code ("AR" or "ARG"). |
| external_id | string | Recommended. Your internal reference ID for this transaction. Must be unique for each payment attempt. Used to prevent duplicates and track transactions. If omitted, idempotency and deduplication via external_id will not be available. |
| transfer_description | string | Optional. Free-text note appended to the recipient's Pix receipt, after the End User fields (see Receipt Description). The final receipt is truncated to 140 characters if needed. |
Currency Handling: If the payout currency is set to BRL, kamiPay sends exactly the BRL amount requested. If the payout currency is set to USDT, kamiPay calculates the BRL amount at payout creation time using the current exchange rate and sends that resulting BRL amount.
The minimum payment amount is 0.5 (validated as USDT equivalent). Sending an amount lower than 0.5 will result in a 400 error.
End User
Every payment identifies its End User: the person or company on whose behalf the payment is sent (your customer, or your own company). The End User is not the recipient of the Pix, who is identified by the pix_key.
Send the End User in the four payer_* fields, following these rules:
- All four fields are required, with one exception:
payer_countrycan be omitted with aCPForCNPJ(see below). A blank or whitespace-only value counts as missing, and letter case is ignored in all four. payer_countryis the country that issued the document, not the End User's nationality or country of residence: a Venezuelan citizen identified with an Argentine DNI is sent asARGandDNI.- With a
CPForCNPJ,payer_countryis optional, and the document is taken as issued by Brazil. If you send a country with one, it must beBRorBRA. payer_id_typeis a document type that the country issues, as listed in Document Types.payer_documentis a valid document of that type. Spaces, dots, dashes and slashes are ignored, so20-12345678-6and20123456786are the same CUIT. Where the table below lists a check digit, it is verified.payer_namehas at least 2 characters and at least one letter, not counting leading or trailing spaces.
The End User fields are also shown on the recipient's Pix receipt (see Receipt Description).
Document Types
| Issuing country | payer_id_type | Document format |
|---|---|---|
Argentina (AR / ARG) | DNI | 7 or 8 digits. |
Argentina (AR / ARG) | CUIT, CUIL | 11 digits, the last one a check digit. |
Chile (CL / CHL) | RUT | 7 or 8 digits followed by a check digit (0 to 9, or K). |
Uruguay (UY / URY) | CI | 7 or 8 digits, the last one a check digit. |
Uruguay (UY / URY) | RUT | 12 digits. |
Bolivia (BO / BOL) | CI | 4 to 10 digits, optionally followed by a 2-character complement, the department of issue (such as LP), or both. |
Bolivia (BO / BOL) | NIT | 7 to 13 digits, not starting with 0. |
Mexico (MX / MEX) | CURP | 18 characters in the official format, with the birth date as YYMMDD. The last one is a check digit. |
Mexico (MX / MEX) | INE | The 18-character elector key printed on the credential (6 letters, 8 digits, H or M, 3 digits), or the 9 to 13 digits of its CIC or OCR. |
Mexico (MX / MEX) | RFC | 12 characters for a company, 13 for an individual, in the official format, with the date of incorporation or birth as YYMMDD. |
Brazil (BR / BRA) | CPF | 11 digits, the last two check digits. |
Brazil (BR / BRA) | CNPJ | 14 characters, the last two check digits. The first 12 can include letters. |
Venezuela (VE / VEN) | CI | 5 to 9 digits, optionally preceded by V or E. |
Colombia (CO / COL) | CI | 5 to 10 digits. |
Paraguay (PY / PRY) | CI | 5 to 10 digits. |
Ecuador (EC / ECU) | CI | 5 to 10 digits. |
Peru (PE / PER) | DNI | 8 digits, without the check character printed next to them on the card. |
Peru (PE / PER) | RUC | 11 digits, the last one a check digit. |
Peru (PE / PER) | CE, CPP | 9 digits. |
| Any country | PASSPORT | 6 to 20 letters (A to Z) or digits. |
| Any country | OTHER | Any document not listed above, with at least one character. Send only OTHER as the type and the document number as it appears on the document. |
An End User whose document was issued by a country not listed above can only be identified with a PASSPORT, or with OTHER when it has no passport.
Receipt Description
Every Pix payment carries a short description that the recipient sees on their Pix receipt. kamiPay composes this message from the End User fields and your transfer_description, concatenated in a fixed order:
Pagamento via {your company name} {payer_name} {payer_id_type} {payer_document} {payer_country} {transfer_description}The message always begins with Pagamento via {your company name} (your registered company name), followed by the End User fields in the order above. transfer_description is optional: if you don't send it, or send it blank, it is simply skipped, with no extra spaces. So is a payer_country omitted for a CPF or CNPJ.
Example
Sending the End User plus a note:
{
"payer_name": "Juan Perez",
"payer_id_type": "DNI",
"payer_document": "12345678",
"payer_country": "ARG",
"transfer_description": "Order #4821"
}produces:
Pagamento via {your company name} Juan Perez DNI 12345678 ARG Order #4821Length handling
The composed message is hard-truncated to 140 characters (the Pix protocol limit for this field). There is no error for long input — the receipt is silently cut to fit.
Because the fields are concatenated in a fixed order, the leading fields are the most protected from truncation: your company name is never lost, while a long transfer_description (the last field) is the first to be cut.
The message is in Portuguese because the Pix recipient is always in Brazil. Only transfer_description is optional: the End User is always part of the message, so the receipt always says who the payment is sent for.
Example Request
const url = `${baseURL}/v1/payments/payToPixKey`;
const body = {
"currency": "BRL",
"amount": 4.10,
"pix_key": "user@example.com", // CPF/CNPJ/email/Phone/pixkey
"from_address": "0x1c0aCF853xxxxx9488fEdce1ab4",
"external_id": "payment-unique-id-001", // Must be unique for each attempt
// End User: the person or company on whose behalf the payment is sent
"payer_name": "Juan Perez",
"payer_id_type": "DNI",
"payer_document": "12345678",
"payer_country": "ARG", // country that issued the document
// Optional note for the recipient's receipt (see "Receipt Description");
// the final receipt is truncated to 140 chars
// "transfer_description": "Order #4821"
};
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${access_token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body)
});import requests
import json
url = f"{base_url}/v1/payments/payToPixKey"
body = {
"currency": "BRL",
"amount": 4.10,
"pix_key": "user@example.com", # CPF/CNPJ/email/Phone/pixkey
"from_address": "0x1c0aCF853xxxxx9488fEdce1ab4",
"external_id": "payment-unique-id-001", # Must be unique for each attempt
# End User: the person or company on whose behalf the payment is sent
"payer_name": "Juan Perez",
"payer_id_type": "DNI",
"payer_document": "12345678",
"payer_country": "ARG", # country that issued the document
# Optional note for the recipient's receipt (see "Receipt Description");
# the final receipt is truncated to 140 chars
# "transfer_description": "Order #4821"
}
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
}
response = requests.post(url, json=body, headers=headers)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
url := fmt.Sprintf("%s/v1/payments/payToPixKey", baseURL)
// Create request body
requestBody, _ := json.Marshal(map[string]interface{}{
"currency": "BRL",
"amount": 4.10,
"pix_key": "user@example.com", // CPF/CNPJ/email/Phone/pixkey
"from_address": "0x1c0aCF853xxxxx9488fEdce1ab4",
"external_id": "payment-unique-id-001", // Must be unique for each attempt
// End User: the person or company on whose behalf the payment is sent
"payer_name": "Juan Perez",
"payer_id_type": "DNI",
"payer_document": "12345678",
"payer_country": "ARG", // country that issued the document
// Optional note for the recipient's receipt (see "Receipt Description");
// the final receipt is truncated to 140 chars
// "transfer_description": "Order #4821"
})
// Create request
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", access_token))
req.Header.Add("Content-Type", "application/json")
// Make the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
}Response
Payment Successfully Processed
{
"status": "ok",
"kamipay_id": "txc_01jrazd7a9fqpp0bym8km1dbd6",
"data": {
"status": "broadcasted",
"usdt_amount": 0.875934
},
"external_id": "testing123"
}Always use kamipay_id and external_id for transaction tracking. The external_id field is only present in the response if it was provided in the request.
Payment Created (Processing)
Returned when the external_id matches an existing transaction that is still being processed or has already been completed.
{
"status": "created",
"kamipay_id": "txc_01jkg5vktaer5abc123kjgrsn2",
"data": null,
"external_id": "idempotency-1"
}Bad Request
The response format for 400 errors varies depending on the validation that failed.
Invalid PIX key format:
{
"detail": {
"msg": "Description of the format error",
"reformated_key": null,
"name": null
}
}Invalid amount (below minimum):
{
"detail": "Wrong amount USDt:0.3. Minimum USDt:0.5"
}Invalid currency:
{
"detail": "Currency should be BRL, USDT"
}Invalid or non-whitelisted from_address:
{
"detail": "Incorrect Address: 0xInvalidAddress123"
}Missing pix_key:
{
"detail": "pix_key is required"
}Unauthorized
Invalid or expired token:
{
"detail": "Could not validate credentials"
}Valid token but insufficient permissions:
{
"detail": "Token Profile Not Authorized for this Endpoint"
}Conflict (Previous Transaction Cancelled)
Returned when the external_id matches an existing transaction that was previously cancelled. To retry, you must use a new external_id, since re-sending the same one will return this same 409 response.
{
"status": "canceled",
"kamipay_id": "txc_01jkg5vktaer5abc123kjgrsn2",
"data": null,
"external_id": "idempotency-1"
}Validation Error
Returned when the request body is invalid, for example when the End User is missing or invalid. detail has one entry per invalid field; for a missing field, input is the whole request body.
For example, a request with a CUIT issued by Chile and without payer_name returns:
{
"detail": [
{
"type": "value_error",
"loc": ["body", "payer_id_type"],
"msg": "payer_id_type CUIT is not valid for country CHL",
"input": "CUIT"
},
{
"type": "missing",
"loc": ["body", "payer_name"],
"msg": "Field required",
"input": {
"currency": "BRL",
"amount": 4.1,
"pix_key": "user@example.com",
"from_address": "0x1c0aCF853xxxxx9488fEdce1ab4",
"external_id": "payment-unique-id-001",
"payer_id_type": "CUIT",
"payer_document": "20-12345678-6",
"payer_country": "CHL"
}
}
]
}The End User errors you can get, each with one example of its msg (the field, document type or country named in it varies):
type | Example msg |
|---|---|
missing | Field required |
value_error | payer_country must be an ISO 3166-1 alpha-2 or alpha-3 code |
value_error | payer_country must be BRA for a CPF |
value_error | payer_id_type must be one of CE, CI, CNPJ, CPF, CPP, CUIL, CUIT, CURP, DNI, INE, NIT, OTHER, PASSPORT, RFC, RUC, RUT |
value_error | payer_id_type CUIT is not valid for country CHL |
value_error | payer_document is not a valid CUIT |
value_error | payer_name must have at least 2 characters and at least one letter |
To handle these errors in code, rely on loc and type, not on msg. Some fields can only be checked once the fields they depend on are valid, so fixing one can reveal an error in another.
A request rejected with this error creates no payment, so you can fix it and send it again with the same external_id.
Payment Failed (Transaction Cancelled)
The payment was attempted but failed at the gateway level. The transaction is permanently cancelled.
{
"status": "canceled",
"kamipay_id": "txc_01jrazd7a9fqpp0bym8km1dbd6",
"data": null,
"external_id": "testing123"
}Critical: This status indicates the transaction could NOT be processed and should be considered failed. If you want to retry the payment, you must use a new external_id.
Internal Server Error
{
"detail": "An unexpected error occurred."
}Service Unavailable
Returned when the payment gateway is under maintenance.
{
"detail": "Service is temporarily unavailable due to maintenance"
}Transaction Identifiers
| Identifier | Description |
|---|---|
kamipay_id | Primary identifier provided by kamiPay to monitor a specific transaction until completion. Always use this for transaction tracking. |
external_id | Your transaction identifier. Must be unique for each payment attempt. Essential for preventing duplicates, tracking in webhooks, and status endpoints. Only present in responses if it was provided in the request. |
Important: Always use kamipay_id and external_id as your primary tracking mechanisms.
Response Statuses Returned by This Endpoint
This endpoint can return the following status values in the response body:
| Status | HTTP Code | Description |
|---|---|---|
ok | 200 | Payment was successfully processed. The data object contains the transaction details. |
created | 201 | A transaction with this external_id already exists and is either in progress or completed. No new transaction is created. |
canceled | 409 | A transaction with this external_id was previously cancelled. You must use a new external_id to retry. |
canceled | 424 | The payment was attempted but failed at the gateway level. The transaction is permanently cancelled. You must use a new external_id to retry. |
For detailed transaction status tracking (e.g., intermediate processing states, final settlement confirmation), use the status endpoint or webhooks. This endpoint only returns the initial synchronous result of the payment attempt.
Payment Processing
Payments are processed through our prepaid system, which provides:
- Fast settlement: Direct bank transfers settled in batches
- Consistent identifiers: Same tracking system (
kamipay_idandexternal_id) regardless of underlying processing method
Error Handling & Retries
Status Code 424 - Critical Failure: When receiving a 424 status code, the transaction has failed permanently. To retry the payment, you must generate a new external_id. Using the same external_id will result in duplicate prevention mechanisms blocking the request.
Retry Guidelines
- Successful payments (200): No retry needed
- Processing payments (201): Do not retry. Monitor status via webhooks or status endpoint
- Failed payments (424): Generate a new
external_idbefore retrying - Conflicts (409): The previous transaction with this
external_idwas cancelled. You must use a newexternal_idto retry, as re-sending the same one will return409again - Validation errors (422): Fix the fields listed in
detailbefore retrying. No payment was created, so you can retry with the sameexternal_id - Server errors (500/503): Safe to retry with the same
external_idafter a short delay
Handling Parallel Requests
Status: Created (201)
When making requests while a previous transaction with the same external_id is still processing:
- Returns 201 status code
- The existing transaction is still being processed; no new transaction is created
- The
datafield will benull - Monitor via status endpoint or webhooks
Status: Cancelled (409/424)
- 409: A previous transaction with this
external_idwas cancelled. Use a newexternal_idto retry - 424: The current transaction failed to process at the gateway level. Use a new
external_idto retry