Which event do I need?#
item/error. There is no separate event for an authentication problem: every
execution that finishes in an error state — including one that failed at the
login step — is delivered as item/error. The full event list is in
Webhook; nothing else in it is
auth-specific.
Register it like any other event:
POST https://api.pluggy.ai/webhooks
X-API-KEY: {apiKey}
Content-Type: application/json
{
"url": "https://example.com/pluggy",
"event": "item/error"
}There is no per-product wildcard. If you want every Item event on one endpoint,
register all instead of one webhook per event.
Telling the failures apart#
The notification carries an error object, and its code is the same value the
Item exposes as executionStatus:
{
"event": "item/error",
"eventId": "d876fd7c-e9bd-4c4c-bd46-cc96c62aac29",
"itemId": "d161a74a-8bc8-4093-88de-724312969b0d",
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Invalid credentials"
},
"triggeredBy": "SYNC",
"clientUserId": "your-user-id"
}The codes that mean "the connection could not authenticate" are:
error.code | What it means | What the end user has to do |
|---|---|---|
INVALID_CREDENTIALS | The credentials were rejected by the institution. | Re-enter credentials. The Item's status becomes LOGIN_ERROR and it is no longer auto-synced until new credentials are provided. |
INVALID_CREDENTIALS_MFA | The second login step failed: wrong or expired MFA token. | Start a new connection attempt and submit a fresh token. |
USER_INPUT_TIMEOUT | The MFA token was never submitted in time. | Start again and submit the token within the window. |
ALREADY_LOGGED_IN | The institution refused a new session because one is already active. | Close the open session at the institution, then retry. |
ACCOUNT_LOCKED | The account is locked at the institution. | Contact the institution to unlock it. |
ACCOUNT_CREDENTIALS_RESET | The institution is forcing a credentials reset (expired password, new security policy). | Reset the password at the institution, then reconnect. |
ACCOUNT_NEEDS_ACTION | The institution is blocking data collection until the user does something (accept new terms, complete a profile). | Resolve it at the institution, then retry. |
USER_AUTHORIZATION_NOT_GRANTED | Device authorisation was not granted to the connector. | Authorise the device in the bank app and reconnect. |
USER_AUTHORIZATION_REVOKED | The user revoked data sharing at the institution. | Re-consent — this creates a new connection. |
USER_NOT_SUPPORTED | The kind of account the user is connecting is not supported for that connector. | Use a supported account type. |
Every value, including the non-authentication ones, is described in Item lifecycle.
`USER_AUTHORIZATION_PENDING` also arrives as `item/error`
It is an intermediate state, not a failure: the user has to authorise on their device or at the institution, and Pluggy resumes the collection by itself a few minutes later. Treat this code as "waiting", not as "broken", or you will ask the user to reconnect a connection that was about to succeed.
The `error` object can be absent
When the failure happened inside Pluggy while storing already-collected data, the
item/error event still fires but carries no error object — that internal code
is not exposed on client-facing surfaces. Handle a missing error without
crashing, and read the Item's executionStatus with
GET /items/{id}API when you need the detail.
What is not an error#
Two events look like failures and are not:
item/waiting_user_input— the connector is asking for an MFA token. The login succeeded; the execution is suspended until the token is sent withPOST /items/{id}/mfaAPI.item/waiting_user_action— the user must act on their device (approve in the bank app, scan a QR code).
Neither ends the execution, so neither should trigger a "your bank connection failed" message to your user.
Getting the itemId#
itemId is in the payload of every item/* event, and so is the clientUserId
you set when creating the Connect Token — those two
fields are all you need to map the failure to one of your users, with no extra
API call.
If you never stored the itemId in the first place, see
I lost an itemId. How do I find it again? — listing
items is opt-in per team, so the reliable path is to persist the itemId from
the widget's onSuccess callback or the item/created webhook.
Reacting to it#
- Look up your user through
clientUserId(or your ownitemIdmapping). - If the code is one of the credential errors above, prompt the user to
reconnect. Re-sending the same credentials to an Item in
LOGIN_ERRORwithout the user re-entering them will fail again. - Update the existing Item rather than creating a new one — see
Updating an Item. A new connection means
a new
itemIdand a fresh history.
Answer the webhook with a 2xx within 10 seconds and process asynchronously;
a slow handler counts as a failure and is retried. The retry schedule is in
Webhook.
