Webhook events
Recover webhooks your endpoint missed
/beta/webhook_events/list
List webhook events
/beta/webhook_events/list returns webhook events for your account from the last 7
days, regardless of delivery outcome. Results are ordered by sent_time, oldest
first, and paginated with a cursor so you can recover deliveries your endpoint
missed. Each event includes a webhook_message_id that stays the same if that
event shows up again on a later poll, so you can skip events you have already
handled.
TRANSACTIONS webhooks are not returned. Use
/transactions/sync
to recover transaction updates.
Filtering is optional. For webhook_types, webhook_codes, item_ids, and
delivery_statuses, values within a field match with OR; different fields combine
with AND. For example, webhook_types: ["ITEM", "AUTH"] matches events of either
type.
To page through events:
- On the first request, omit
cursor. You can setstart_timeto a time within the last 7 days, or omitstart_timeto start at the oldest retained event. - On later requests, send the previous response's
next_cursorascursor. If you also sendstart_time, it is ignored;cursortakes precedence, even whenstart_timehas changed. - Save
next_cursoreven whenhas_moreisfalse, and send that cursor on the next poll so you only receive events newer than the ones you have already seen.
A request fails with 400 in these cases:
WEBHOOK_EVENTS_START_TIME_OUT_OF_RANGE(INVALID_INPUT) is returned whencursoris omitted andstart_timeis earlier than the 7-day retention window. Retry with astart_timewithin the last 7 days, or omitstart_time.WEBHOOK_EVENTS_CURSOR_EXPIRED(INVALID_INPUT) is returned when the cursor is older than the 7-day retention window and can no longer be resolved. Start again with astart_timewithin the last 7 days. Events older than that window are no longer available.INVALID_FIELD(INVALID_REQUEST) is returned whencursoris not a properly formatted string, or when the request is otherwise invalid.
This endpoint is in beta and may change in backwards-incompatible ways before it is generally available. Send feedback or bug reports to building@plaid.com.
Request fields
Your Plaid API client_id. The client_id is required and may be provided either in the PLAID-CLIENT-ID header or as part of a request body.
Your Plaid API secret. The secret is required and may be provided either in the PLAID-SECRET header or as part of a request body.
Opaque cursor from a prior /beta/webhook_events/list response next_cursor. Use this
on subsequent requests to continue forward. If start_time is also provided,
it is ignored; cursor takes precedence.
512 ISO-8601 timestamp. Returns webhook events with sent_time greater than or equal to
this value. When cursor is provided, this value is ignored, even if it has changed
or falls outside the 7-day retention window. Otherwise, it must not be earlier than
the 7-day retention window. Omit both cursor and start_time to begin from the
oldest retained event.
date-time Page size. Default 100, maximum 100.
1 100 100 Filter by webhook type. Multiple values are OR'd. Combined with other filters using AND.
Values are case-sensitive and match the webhook types Plaid sends (SCREAMING_SNAKE, for
example ITEM or AUTH).
Filter by webhook code. Multiple values are OR'd. Combined with other filters using AND.
Values are case-sensitive and match the webhook codes Plaid sends (SCREAMING_SNAKE, for
example ERROR).
Filter to specific Items. Multiple values are OR'd. Combined with other filters using AND. Values are case-sensitive and match the Item IDs Plaid sends.
Filter by delivery status. Returns webhook events whose latest delivery state matches any of the supplied values. Combined with other filters using AND.
PENDING, DELIVERED, FAILED// Provide a cursor you've previously stored, or leave it
// undefined on the first call.
let cursor = loadSavedCursor();
let hasMore = true;
while (hasMore) {
const request: BetaWebhookEventsListRequest = {
cursor: cursor,
count: 100,
};
const response = await plaidClient.betaWebhookEventsList(request);
const data = response.data;
for (const event of data.webhook_events) {
if (!alreadyProcessed(event.webhook_message_id)) {
handleWebhookEvent(event);
}
}
hasMore = data.has_more;
// Persist next_cursor even when has_more is false, then reuse
// it on the next poll so you only receive newer events.
cursor = data.next_cursor;
saveCursor(cursor);
}
Response fields
Webhook events for your account.
Stable, opaque ID for this webhook event. The same value is returned if the event appears again on a later poll. Quote it when contacting support.
The webhook type.
The webhook code.
The Item ID associated with the webhook, if any.
The time Plaid sent the webhook.
date-time The JSON body Plaid sent to the webhook destination.
The destination URL Plaid sent the webhook to.
Latest delivery state for this webhook.
PENDING: Plaid has not recorded a successful response and may still retry.
Treat PENDING as unknown rather than undelivered: a delivery may have
succeeded without the outcome being recorded.
DELIVERED: the destination returned a 2xx response.
FAILED: all delivery attempts were exhausted without a 2xx response.
PENDING, DELIVERED, FAILEDThe most recent HTTP status code returned by the destination, if any.
Number of delivery attempts Plaid has made for this webhook.
The most recent delivery attempt timestamp. Null if Plaid has not yet attempted delivery.
date-time Indicates whether another page of webhook events is available.
Cursor to pass as cursor on a later /beta/webhook_events/list request to continue
forward. Persist and reuse this value even when has_more is false so the next poll
only returns newer events.
A unique identifier for the request, which can be used for troubleshooting. This identifier, like all Plaid identifiers, is case sensitive.
{
"webhook_events": [
{
"webhook_message_id": "whmsg_4tQ9pNzL2vBxYK",
"webhook_type": "ITEM",
"webhook_code": "PENDING_EXPIRATION",
"item_id": "abc123",
"sent_time": "2026-07-21T14:32:00Z",
"payload": {
"webhook_type": "ITEM",
"webhook_code": "PENDING_EXPIRATION"
},
"destination_url": "https://client.example.com/webhook",
"delivery_status": "DELIVERED",
"last_http_status_code": 200,
"attempt_count": 1,
"last_attempt_time": "2026-07-21T14:32:01Z"
}
],
"has_more": true,
"next_cursor": "eyJzZW50X3RpbWUiOiIyMDI2LTA3LTIxVDE0OjMyOjAwWiIsIndlYmhvb2tfbWVzc2FnZV9pZCI6IndobXNnXzR0UTlwTnpMMnZCeFlLIn0",
"request_id": "4zlKapIkTm8p5KM"
}