Webhook#
In this guide we will cover the webhooks that are sent by Pluggy to create a reactive integration to changes. This will cover both data and payment integrations.
A Webhook is a tool that allows you to receive a notification for a certain event. It allows you to set up an HTTPS URL in our platform for certain events, and you will receive a JSON POST event at that URL, with specific event data.
To subscribe to Webhook events, you can either:
- Create a Webhook instance directly, or
- Specify the
webhookUrlparameter (with a valid HTTPS URL), when:- creating an Item,
- creating a Connect Token, or
- creating a Payment Request
If you create a Webhook instance directly, you'll be subscribing to all the cases of events of the specified event type. Instead, if you do it by specifying the webhookUrl when creating an Item, or creating a Connect Token, you'll be notified about all the events, but only related to that specific resource. Which is, either to that Item specifically, or to the related item(s) created using that Connect Token.
Data Events#
We support registering for the following events when consuming Pluggy's data endpoints.
| Event | Description |
|---|---|
item/created | An item was created and finished connecting successfully. |
item/updated | An item was updated and synced successfully. |
item/deleted | An item was deleted successfully. |
item/error | An item has encountered errors in its execution. The case of USER_AUTHORIZATION_PENDING will also trigger this event. |
item/waiting_user_input | An item is blocked waiting for user input to continue. |
item/waiting_user_action | An item is blocked waiting for the user to approve an action on their device (e.g., authorize access in the banking app or scan a QR code). |
item/login_succeeded | An item has successfully logged in to the provider and it's collecting the data. |
connector/status_updated | A connector has changed status (ONLINE/UNSTABLE/OFFLINE). This event informs the affected connector ID and updated status. Check the Connectors endpoint to see all connectors and their IDs. |
transactions/deleted | IDs of deleted transactions after merge data on item update. |
transactions/created | Receive this webhook event, and use the createdTransactionsLink page to access all the transactions available and insert them in your data source. |
transactions/updated | IDs of updated transactions after merge data on item update. After receiving this webhook, it's recommended to get the full transactions data using the transactions endpoint with the ids parameter. |
Note
Transactions webhooks are only fired when there is a data change. If there aren't any transactions, it won't fire a transactions/created event.
Payment Events#
We support registering for the following events when consuming Pluggy's payment endpoints.
Payment Intent#
| Event | Description |
|---|---|
payment_intent/created | ID of payment intent created by the user, with paymentRequestId. |
payment_intent/completed | ID of payment intent completed successfully by the user, with paymentRequestId. |
payment_intent/waiting_payer_authorization | ID of payment intent when it needs additional authorization. |
payment_intent/error | ID of payment intent when it has an error during the flow, with paymentRequestId. |
payment_request/updated | Payment request status has changed. |
Scheduled Payment#
| Event | Description |
|---|---|
scheduled_payment/created | IDs of the scheduled payment authorization. |
scheduled_payment/completed | A single payment of the authorization has been made. |
scheduled_payment/error | Payment not completed, finished with error. |
scheduled_payment/canceled | Payment canceled by the user or the client. |
Automatic PIX Payment#
| Event | Description |
|---|---|
automatic_pix_payment/created | A payment was scheduled for an automatic PIX payment request. |
automatic_pix_payment/completed | A scheduled payment associated with an automatic PIX payment request was completed successfully. |
automatic_pix_payment/error | A scheduled payment associated to an automatic PIX payment request ended with error. |
automatic_pix_payment/canceled | A scheduled payment associated to an automatic PIX payment request was cancelled. |
Smart Transfer#
| Event | Description |
|---|---|
smart_transfer_preauthorization/completed | A smart transfer preauthorization was approved by a user. |
smart_transfer_preauthorization/error | There was an error during the smart transfer preauthorization approval. For example, when the user rejects the consent. |
smart_transfer_payment/completed | A smart transfer payment was completed. |
smart_transfer_payment/error | There was an error settling the smart transfer payment. For example, when the account doesn't have enough balance. |
To register for a specific event, you will have to send the desired event to listen only to those webhook events.
Alternatively, you can just use the option all to receive all events associated.
We only accept HTTPS URLs. Localhost URLs are not allowed; you will have to provide an HTTPS URL using ngrok or other tools to provide public, secure URLs.
Tip
To test this functionality, you can use RequestCatcher, which is a tool that will just receive our notification and show you the payload. You can easily create a catcher with just a name in seconds.
Payload parameters#
When doing the POST request, all webhooks will send the following parameters in JSON format:
- event: event's name (
item/created,item/updated,item/error, etc.) - eventId: identifier of the event itself. It should be the same for one event when it is sent to many endpoints.
- triggeredBy: who triggered the event (for all events except
item/deleted,connector/status_updatedandtransactions/deleted). Possible values:USER: an end user triggered the event with a Connect Token (for example, from Pluggy Connect)CLIENT: a client triggered the event with an API Key (for example, by running PATCH on an Item)SYNC: Auto-sync triggered the eventINTERNAL: It was triggered by someone from Pluggy's support team
Depending on the event type, the entity ID is sent. For example, for items, itemId. In transactions, transactionIds, and in connectors, connectorId.
clientUserId is only sent on item/* events#
clientUserId -- the identifier you set when creating the Connect Token -- is included in the payload of every item/* event: item/created, item/updated, item/error, item/deleted, item/login_succeeded, item/waiting_user_input and item/waiting_user_action.
It is not included in transactions/* events. Those payloads carry itemId, accountId and the transaction fields, but no clientUserId. To attribute a transactions event to an end user, keep your own itemId -> user mapping (which you already have from the item/created event or the widget's onSuccess callback) and resolve it from itemId.
If `clientUserId` arrives as `null` on `item/*` events
The usual cause is that it was sent at the root of the POST /connect_token body instead of inside options. The API discards unknown root properties and still returns 200, so the value never reaches the Item. See Configuring a Connect Token. You can backfill affected Items with PATCH /items/API.
Item examples#
item/created:
{
"event": "item/created",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "a5c763cb-0952-457b-9936-630f79c5b016",
"triggeredBy": "USER",
"clientUserId": "client-user-id"
}item/updated:
{
"event": "item/updated",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "a5c763cb-0952-457b-9936-630f79c5b016",
"triggeredBy": "USER",
"clientUserId": "client-user-id"
}item/deleted:
{
"event": "item/deleted",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "a5c763cb-0952-457b-9936-630f79c5b016",
"triggeredBy": "USER",
"clientUserId": "client-user-id"
}item/error:
{
"event": "item/error",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "d161a74a-8bc8-4093-88de-724312969b0d",
"error": {
"code": "USER_INPUT_TIMEOUT",
"message": "User requested input had expired",
"parameter": "token"
},
"triggeredBy": "USER",
"clientUserId": "client-user-id"
}item/waiting_user_input:
{
"event": "item/waiting_user_input",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "d038aaa6-35f2-4f06-8b8a-c464a4a61fc2",
"triggeredBy": "USER",
"clientUserId": "client-user-id"
}item/waiting_user_action:
{
"event": "item/waiting_user_action",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "d038aaa6-35f2-4f06-8b8a-c464a4a61fc2",
"triggeredBy": "USER",
"clientUserId": "client-user-id"
}item/login_succeeded:
{
"event": "item/login_succeeded",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "a5c763cb-0952-457b-9936-630f79c5b016",
"triggeredBy": "USER",
"clientUserId": "client-user-id"
}Connector examples#
connector/status_updated:
{
"id": "201",
"event": "connector/status_updated",
"eventId": "4552bee0-b87f-48b5-896b-c23113839319",
"connectorId": "201",
"data": {
"status": "UNSTABLE"
}
}Transaction examples#
transactions/deleted:
{
"event": "transactions/deleted",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "a5c763cb-0952-457b-9936-630f79c5b016",
"accountId": "8a6e2c17-2817-40bb-b03d-546febc6a60a",
"transactionIds": [
"5a14feae-eaa7-423a-820c-6b83837c35b7",
"786c7d98-6085-4879-9c7f-2255260e2436"
]
}transactions/updated:
{
"event": "transactions/updated",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "a5c763cb-0952-457b-9936-630f79c5b016",
"accountId": "8a6e2c17-2817-40bb-b03d-546febc6a60a",
"transactionIds": [
"5a14feae-eaa7-423a-820c-6b83837c35b7",
"786c7d98-6085-4879-9c7f-2255260e2436"
]
}transactions/created:
{
"itemId": "de7bbf5a-abf2-47e4-94b1-586b36758423",
"event": "transactions/created",
"id": "de7bbf5a-abf2-47e4-94b1-586b36758423",
"eventId": "4e69d62d-b7c8-4f01-b591-a1d8a94710b9",
"accountId": "0d5a0de2-9c82-4ea2-af50-31643a632a33",
"transactionsCount": 332,
"transactionsMinDate": "2025-02-12T15:00:01.000Z",
"transactionsCreatedAtFrom": "2025-02-13T17:21:53.719Z",
"createdTransactionsLink": "https://api.pluggy.ai/transactions?accountId=0d5a0de2-9c82-4ea2-af50-31643a632a33&createdAtFrom=2025-02-13T17:21:53.719Z"
}Payment Intent examples#
payment_intent/created:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"paymentIntentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"event": "payment_intent/created",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0"
}payment_intent/completed:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"paymentIntentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"event": "payment_intent/completed",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"referenceId": "E33371172200009110200U70a27b2698"
}payment_intent/error:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"paymentIntentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"event": "payment_intent/error",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"referenceId": "E33371172200009110200U70a27b2698",
"error": {
"code": "REJECTED_BY_USER",
"description": "The consent was rejected by the user.",
"detail": "O usuário rejeitou a autorização do consentimento"
}
}Payment Request examples#
payment_request/updated:
{
"event": "payment_request/updated",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6",
"clientId": "client-123",
"status": "CANCELED"
}Scheduled Payments examples#
scheduled_payment/created:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"scheduledPaymentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"event": "scheduled_payment/created"
}scheduled_payment/completed:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"scheduledPaymentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"event": "scheduled_payment/completed"
}scheduled_payment/error:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"scheduledPaymentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"event": "scheduled_payment/error",
"error": {
"title": "Title of the error",
"code": "Error Code",
"description": "Error description"
}
}scheduled_payment/canceled:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"scheduledPaymentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"event": "scheduled_payment/canceled"
}Automatic PIX Payments examples#
automatic_pix_payment/created:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"automaticPixPaymentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"endToEndId": "E37943755202506111319U0da92d1b7e",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"event": "automatic_pix_payment/created"
}automatic_pix_payment/completed:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"automaticPixPaymentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"endToEndId": "E37943755202506111319U0da92d1b7e",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"event": "automatic_pix_payment/completed"
}automatic_pix_payment/error:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"automaticPixPaymentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"endToEndId": "E37943755202506111319U0da92d1b7e",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"event": "automatic_pix_payment/error",
"error": {
"title": "Title of the error",
"code": "Error Code",
"description": "Error description"
}
}automatic_pix_payment/canceled:
{
"paymentRequestId": "624cb41e-2b76-42ac-8021-ce6b6b4ace6b",
"automaticPixPaymentId": "8429f45a-f541-4fc3-8439-cbc8d9dc9952",
"endToEndId": "E37943755202506111319U0da92d1b7e",
"eventId": "71e0b4db-4e8c-401d-8ad8-5ab3ecd1f3d0",
"event": "automatic_pix_payment/canceled"
}Smart Transfers examples#
smart_transfer_preauthorization/completed:
{
"clientId": "65a8c757-ef2c-4bc3-94ed-99218f6a0ee8",
"event": "smart_transfer_preauthorization/completed",
"eventId": "8013240b-7c0a-409e-bf00-7fc586cc9196",
"smartTransferPreauthorizationId": "f84e6e06-e0f9-4ab3-89f2-da062be65e7c",
"status": "COMPLETED"
}smart_transfer_preauthorization/error:
{
"clientId": "65a8c757-ef2c-4bc3-94ed-99218f6a0ee8",
"event": "smart_transfer_preauthorization/error",
"eventId": "54ceeb25-b9f7-4b90-8124-0cff66d1b2c3",
"smartTransferPreauthorizationId": "3dd45002-52aa-44e8-a206-f6b99a293d9a",
"status": "REJECTED",
"error": {
"code": "REJECTED_BY_USER",
"description": "The consent was rejected by the user.",
"detail": "O usuário rejeitou a autorização do consentimento"
}
}smart_transfer_payment/completed:
{
"clientId": "65a8c757-ef2c-4bc3-94ed-99218f6a0ee8",
"event": "smart_transfer_payment/completed",
"eventId": "0dfa6e5d-aee4-42b6-bd3b-e27e9c591339",
"smartTransferPreauthorizationId": "f84e6e06-e0f9-4ab3-89f2-da062be65e73",
"smartTransferPaymentId": "2afc828e-0dc2-4195-a774-145f7d7fc46c",
"status": "PAYMENT_COMPLETED"
}smart_transfer_payment/error:
{
"clientId": "65a8c757-ef2c-4bc3-94ed-99218f6a0ee6",
"event": "smart_transfer_payment/error",
"eventId": "59ff7a4a-d804-499b-a31c-7865014ba0ef",
"smartTransferPreauthorizationId": "f84e6e06-e0f9-4ab3-89f2-da062be65e73",
"smartTransferPaymentId": "b546ab61-f1cd-4cd4-b5bf-0073d9a9ee3a",
"status": "PAYMENT_REJECTED",
"error": {
"code": "INSUFFICIENT_BALANCE",
"description": "The account doesn't have enough balance to perform the payment.",
"detail": "Saldo insuficiente."
}
}Handling notifications#
When we send you a webhook notification, one of two things happens:
- Your API responds with a
2XXstatus within 10 seconds → success - Your API responds with any other status, or does not respond within 10 seconds → failure
Retry schedule#
We attempt to deliver each webhook up to 3 times:
| Attempt | When |
|---|---|
| 1st | Immediately |
| 2nd | ~15 minutes after the 1st attempt fails |
| 3rd | ~2 hours after the 2nd attempt fails |
If the 3rd attempt fails, the notification is not delivered again automatically.
Responses we do not retry#
Some responses tell us the request will never succeed as sent. In those cases we stop after the first attempt and schedule no retries:
400 · 401 · 403 · 404 · 405
Every other non-2XX outcome is treated as temporary and gets the full 3-attempt schedule above, including 408, 429, any 5XX, connection errors and timeouts.
Caution
If your endpoint answers 400 while it is temporarily broken — a bug parsing the payload, a deploy that leaves your handler rejecting valid requests — those notifications are dropped after a single attempt and will not come back on their own. Return a 5XX for temporary or unexpected server-side problems, and reserve 4XX for requests you will never accept.
Exception: time-sensitive item events
item/waiting_user_input and item/login_succeeded are time-sensitive: they get 3 attempts spaced about 6 minutes apart, and never use the 15-minute / 2-hour schedule. After the 3rd attempt they are not retried.
If every attempt fails#
You can retry a notification manually from the Dashboard. A manual retry starts a fresh cycle of up to 3 attempts, ~15 minutes apart. The rule above still applies: if your endpoint keeps answering 400, 401, 403, 404 or 405, the manual retry also stops after one attempt — so fix the endpoint first, then retry.
Responding to notifications#
It's mandatory that your API returns 2XX right after receiving a notification, and then does its processing after responding to Pluggy. This way, if your processing takes more than 10 seconds, we will not interpret it as a failure, therefore avoiding unwanted retries of the same notification.
For all items notifications, we expect that the first thing you do when processing the event is to do a GET /items/{id} to recover the latest information related to the event, instead of processing from the event's payload data.
Whitelisting Pluggy's IPs
If you want to add extra security measures to provide IP filtering to our requests, you should whitelist the following IPs:
52.67.145.81
Webhook headers#
When creating a webhook, you can specify a headers object to send specific headers in your webhook notifications. This can be useful, for instance, if your webhook URL is secured with an API Key. You can add headers to your webhook like this:
{
"url": "example.com",
"event": "all",
"headers": {
"Authorization": "My API key",
"X-CLIENT-ID": "Some secret extra information"
}
}API Only
Webhook headers currently can only be configured via API, since they may contain sensitive data to be exposed in our dashboard.
For more information see Webhook in our API reference.
