In this section, you will learn how to create a preauthorization to do payments without user interaction.
Create a payment recipient#
First, you need to create Payment Recipients. Those will be the allowed destinations to send transfers. All of recipients must belong to the same owner.
curl --location 'https://api.pluggy.ai/payments/recipients' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: ••••••' \
--data '{
"account": {
"type": "CHECKING_ACCOUNT",
"number": "1111111",
"branch": "0001"
},
"paymentInstitutionId": "abfd2a88-bc7b-407f-9fcc-395548ee6840",
"name": "John Doe",
"taxNumber": "11111111111"
}'Check the API docsAPI to know how to use this endpoint. To know the paymentInstitutionId, you need to use this endpointAPI.
This will return the following response:
{
"type": "BANK_ACCOUNT",
"id": "ded2e966-bd40-4e82-b467-d32fe4b4f40e",
"name": "John Doe",
"taxNumber": "11111111111",
"isDefault": false,
"paymentInstitution": {
"id": "abfd2a88-bc7b-407f-9fcc-395548ee6840",
"name": "Banco XP S.A.",
"tradeName": "BCO XP S.A.",
"ispb": "33264668",
"compe": "348",
"createdAt": "2023-12-08T17:52:21.001Z",
"updatedAt": "2023-12-08T17:52:21.001Z"
},
"account": {
"type": "CHECKING_ACCOUNT",
"number": "1111111",
"branch": "0001"
},
"pixKey": null,
"createdAt": "2024-08-01T16:32:29.276Z",
"updatedAt": "2024-08-01T16:32:29.276Z"
}Create the Smart Transfer Preauthorization#
Now, you are ready to create a smart transfer preauthorization. To do that, you need to do the following request:
curl --location 'https://api.pluggy.ai/smart-transfers/preauthorizations' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: ••••••' \
--data '{
"connectorId": 612,
"parameters": {
"cpf": "11111111111"
},
"recipientIds": [
"ded2e966-bd40-4e82-b467-d32fe4b4f40e"
],
"callbackUrls": {
"success": "https://my-success-page.com",
"error": "https://my-error-page.com"
},
"configuration": {
"transactionLimit": 100
},
"linkedJourney": true
}'-
The
connectorIdwill be the one associated with the institution of your debtor account (for example, if you want to create the preauthorization in Nubank, you need to send the id612). -
You can configure the transaction limits sending the
configurationfield (see Configuring transaction limits section). -
linkedJourney (optional, default false): also ask the user, in the same approval at the bank, for permission to read the source account balance. See Source account balance.
For more details about this endpoint, check our API docsAPI.
Important
For PF accounts you can only configure one recipient per authorization. Only PJ accounts can configure multiple recipients for the same authorization.
This will return the following response:
{
"id": "7e3e1dbb-8009-4966-9254-1eaab05ad18b",
"status": "CREATED",
"consentUrl": "https://this-is-the-consent-url.com",
"clientPreauthorizationId": null,
"callbackUrls": null,
"recipients": [
{
"type": "BANK_ACCOUNT",
"id": "ded2e966-bd40-4e82-b467-d32fe4b4f40e",
"name": "John Doe",
"taxNumber": "11111111111",
"isDefault": false,
"paymentInstitution": {
"id": "abfd2a88-bc7b-407f-9fcc-395548ee6840",
"name": "Banco XP S.A.",
"tradeName": "BCO XP S.A.",
"ispb": "33264668",
"compe": "348",
"createdAt": "2023-12-08T17:52:21.001Z",
"updatedAt": "2023-12-08T17:52:21.001Z"
},
"account": {
"type": "CHECKING_ACCOUNT",
"number": "1111111",
"branch": "0001"
},
"pixKey": null,
"createdAt": "2024-07-31T15:56:03.938Z",
"updatedAt": "2024-07-31T15:56:23.123Z"
}
],
"connector": {
"id": 612,
"name": "Nubank",
"primaryColor": "8a0fbe",
"institutionUrl": "https://nuapp.nubank.com.br/open-banking/logo.svg",
"country": "BR",
"type": "PERSONAL_BANK",
"credentials": [
{
"validation": "^\\d{3}\\.?\\d{3}\\.?\\d{3}-?\\d{2}$",
"validationMessage": "CPF deve ter 11 numeros.",
"label": "CPF",
"name": "cpf",
"type": "number",
"placeholder": "",
"optional": false
}
],
"imageUrl": "https://cdn.pluggy.ai/assets/connector-icons/212.svg",
"hasMFA": false,
"oauth": true,
"health": {
"status": "ONLINE",
"stage": null
},
"products": [
"ACCOUNTS",
"TRANSACTIONS",
"IDENTITY",
"CREDIT_CARDS",
"PAYMENT_DATA",
"LOANS",
"INVESTMENTS"
],
"createdAt": "2023-09-01T18:05:09.145Z",
"isSandbox": false,
"isOpenFinance": true,
"updatedAt": "2024-08-01T16:33:57.978Z",
"supportsPaymentInitiation": true,
"supportsScheduledPayments": true,
"supportsSmartTransfers": true
},
"dataConsent": {
"status": "AWAITING_AUTHORISATION",
"rejectionReason": null,
"updatedAt": "2026-10-07T18:43:08.390Z"
},
"createdAt": "2024-08-01T16:39:27.946Z",
"updatedAt": "2024-08-01T16:39:32.448Z"
}After the preauthorization is created, you need to redirect your user to the consentUrl returned in the response. There, the user needs to approve the preauthorization in their payment institution. After the consent is given, the user will be redirected to the success callback url if everything is ok, or to the error callback url if the consent was rejected or if an error happens in the process.
Note: if you don't define a set of callbackUrls, the user will be redirected to a Pluggy's default page.
Now, if you check the preauthorization status using this endpointAPI, you will see it with one of the following statuses:
- COMPLETED: The preauthorization was completed and you are ready to create payments.
- REJECTED: The user rejected the preauthorization in the institution consent flow.
- ERROR: There was an error in the institution consent flow.
In the next section, you will see how to create a payment without user interaction.
Configuring transaction limits#
You can configure the transaction limits sending the configuration object.
| Field | Type | Optional | Description |
|---|---|---|---|
| totalAllowedAmount | number | true | Maximum amount to be reached by the sum of all transactions that use the consent authorized by the customer. |
| transactionLimit | number | true | Maximum amount for each payment transaction associated with this consent. |
| periodicLimits | object | true | Transactional limits per period as determined by the paying user. |
In the periodicLimits object, you can configure the limits per period. The available periods are day, week, month and year, and for each one you can configure:
| Field | Type | Optional | Description |
|---|---|---|---|
| quantityLimit | number | true | Maximum number of transactions allowed to occur in the period. |
| transactionLimit | number | true | Maximum amount to be transacted in the period. |
Example:
{
"configuration": {
"totalAllowedAmount": 100.5,
"transactionLimit": 10,
"periodicLimits": {
"day": {
"quantityLimit": 2,
"transactionLimit": 5
},
"week": {
// week limits
},
"month": {
// month limits
},
"year": {
// year limits
}
}
}
}In the case that some limit is reached, you will receive an appropriate error from the API.
Source account balance#
Smart Transfers are paid from the user's account without a new approval, so a transfer can be rejected for insufficient balance. Create the preauthorization with linkedJourney: true to also ask the user, in the same approval at the bank, for permission to read the source account balance. Nothing else changes in the redirect, the return or the transfers.
Permission status#
The preauthorization carries a dataConsent object while the permission exists. It is null when linkedJourney was not requested, and it can also be null when the balance permission is not available for that institution or environment. In that case the preauthorization is created normally, without the permission.
status | Meaning |
|---|---|
AWAITING_AUTHORISATION | The user has not approved yet. |
AUTHORISED | The balance can be read. |
REJECTED | Rejected, revoked, cancelled or expired. It does not come back; rejectionReason says why. |
The permission ends with the preauthorization. If the user revokes either one from their bank, the balance endpoint answers 403, and the stored status can still read AUTHORISED until the institution reports the change.
dataConsent is refreshed from the institution when you retrieve the preauthorization by id. The list returns the last known status. updatedAt is when Pluggy last saw the status change.
Read the balance#
GET /smart-transfers/preauthorizations/{id}/balance reads the source account balance in real time from the institution. Amounts are in BRL.
{
"balance": 5.48,
"overdraft": {
"contracted": 1000,
"used": 0,
"available": 1000
}
}overdraft is the account's overdraft limit: how much was contracted, how much is in use and how much is still available. It is null when the institution does not share it.
| Status | codeDescription | Meaning |
|---|---|---|
200 | Balance read. | |
400 | SMART_TRANSFER_DATA_CONSENT_NOT_REQUESTED | The preauthorization was created without linkedJourney. |
403 | SMART_TRANSFER_DATA_CONSENT_NOT_AVAILABLE | The permission is pending, rejected or revoked. |
429 | BALANCE_OPEN_FINANCE_RATE_LIMIT | The rate limit at the institution is reached, for example the account's monthly balance quota. |
Every call counts toward the institution's quota
Each call reads the balance from the institution, with no cache. Open Finance limits balance reads per account per month (see Operational Rate Limits). Read the balance right before a transfer, not on a schedule.
Cancel only the permission#
DELETE /smart-transfers/preauthorizations/{id}/data-consent cancels the balance permission and returns the preauthorization with dataConsent.status set to REJECTED. The preauthorization stays active and its transfers keep working. A permission cannot be added to an existing preauthorization: create a new one with linkedJourney.
| Status | codeDescription | Meaning |
|---|---|---|
200 | Permission cancelled, or it had already ended. | |
400 | SMART_TRANSFER_DATA_CONSENT_NOT_REQUESTED | The preauthorization was created without linkedJourney. |
403 | SMART_TRANSFER_DATA_CONSENT_NOT_AVAILABLE | The institution did not accept the cancellation, for example because the user has not approved the permission yet. |
429 | BALANCE_OPEN_FINANCE_RATE_LIMIT | The rate limit at the institution is reached. |
Cancelling a permission that already ended (rejected, revoked, or its preauthorization revoked or rejected) answers 200 with status: REJECTED, so retrying is safe.
