Plaid · AsyncAPI Specification

Plaid Webhooks

Version 2020-09-14

AsyncAPI 2.6 specification for the Plaid webhook surface. Plaid delivers asynchronous notifications via HTTP POST to the URL registered on an Item (`webhook` parameter in `/link/token/create`) or configured per-product in the Plaid Dashboard. Each webhook carries a JSON body identified by the `webhook_type` and `webhook_code` fields and is signed with an ES256 JWT in the `Plaid-Verification` HTTP header. Webhooks delivered with a non-200 response are retried with exponential backoff for up to 24 hours starting at 30 seconds. Sources: - https://plaid.com/docs/api/webhooks/ - https://plaid.com/docs/api/webhooks/webhook-verification/ - Product-specific webhook reference pages on plaid.com/docs/api/products/* - Plaid OpenAPI spec (openapi/plaid-openapi-original.yml)

View Spec View on GitHub Bank AccountsFinancialAsyncAPIWebhooksEvents

Channels

item/error
publish onItemError
Fired when an error is encountered with an Item. Resolvable via Link update mode.
item/loginRepaired
publish onItemLoginRepaired
Item exited ITEM_LOGIN_REQUIRED state without the user going through update mode in your app.
item/newAccountsAvailable
publish onItemNewAccountsAvailable
Plaid detected a new account on an Item created/updated with Account Select v2.
item/pendingExpiration
publish onItemPendingExpiration
An Item's access consent is expiring in 7 days (EU/UK PSD2 consent windows).
item/userPermissionRevoked
publish onItemUserPermissionRevoked
End user revoked Item access via my.plaid.com or the institution's OAuth consent portal.
item/userAccountRevoked
publish onItemUserAccountRevoked
End user revoked access to a specific account on the data provider's portal (currently Chase).
item/webhookUpdateAcknowledged
publish onItemWebhookUpdateAcknowledged
Item webhook URL successfully updated; delivered to the newly specified URL.
transactions/syncUpdatesAvailable
publish onTransactionsSyncUpdatesAvailable
Fresh transaction changes available; fetch via /transactions/sync.
transactions/initialUpdate
publish onTransactionsInitialUpdate
Initial 30-day transaction pull complete. Legacy /transactions/get listener.
transactions/historicalUpdate
publish onTransactionsHistoricalUpdate
Historical transaction backfill complete (up to 2 years). Legacy /transactions/get listener.
transactions/defaultUpdate
publish onTransactionsDefaultUpdate
New transactions detected. Legacy /transactions/get listener.
transactions/transactionsRemoved
publish onTransactionsRemoved
Previously returned transactions have been removed.
transactions/recurringTransactionsUpdate
publish onRecurringTransactionsUpdate
Recurring transaction streams added or updated; refetch /transactions/recurring/get.
auth/defaultUpdate
publish onAuthDefaultUpdate
Account or routing numbers have changed; refetch /auth/get and stop using old TANs.
auth/automaticallyVerified
publish onAuthAutomaticallyVerified
Item verified via automated micro-deposits.
auth/verificationExpired
publish onAuthVerificationExpired
Automated micro-deposit verification failed to complete within seven days.
auth/smsMicrodepositsVerification
publish onAuthSmsMicrodepositsVerification
Final status of a same-day micro-deposits verification session.
auth/productPermissionsRequired
publish onAuthProductPermissionsRequired
ACCESS_NOT_GRANTED hit for Auth; put the user through update mode with `auth` in products.
auth/bankTransfersEventsUpdate
publish onBankTransfersEventsUpdate
New ACH events available for micro-deposit verification tracking; fetch via /bank_transfer/event/sync.
identity/defaultUpdate
publish onIdentityDefaultUpdate
Identity data change detected on an Item.
identity/productPermissionsRequired
publish onIdentityProductPermissionsRequired
ACCESS_NOT_GRANTED hit for Identity; put the user through update mode with `identity` in products.
holdings/defaultUpdate
publish onHoldingsDefaultUpdate
New or updated holdings detected on an investment account (typically after market close).
investmentsTransactions/defaultUpdate
publish onInvestmentsDefaultUpdate
New investment transactions detected.
investmentsTransactions/historicalUpdate
publish onInvestmentsHistoricalUpdate
Historical investments transactions extraction complete.
liabilities/defaultUpdate
publish onLiabilitiesDefaultUpdate
New or updated liabilities detected on a Liabilities Item.
assets/productReady
publish onAssetsProductReady
Asset Report ready; call /asset_report/get to retrieve.
assets/error
publish onAssetsError
Asset Report generation failed (ASSET_REPORT_ERROR).
income/productReady
publish onIncomeProductReady
Plaid has finished calculating income for an Item.
income/incomeVerification
publish onIncomeVerification
Income verification processing status change (Document Income flow).
income/incomeVerificationRiskSignals
publish onIncomeVerificationRiskSignals
Risk signals processed for documents uploaded via Document Income.
income/bankIncomeComplete
publish onBankIncomeComplete
A CRA-enabled bank income report has completed (success or failure); call /credit/bank_income/get.
income/bankIncomeRefreshUpdate
publish onBankIncomeRefreshUpdate
A change in user income has been detected; call /credit/bank_income/refresh.
income/bankIncomeRefreshComplete
publish onBankIncomeRefreshComplete
A refreshed bank income report has finished generating.
income/incomeVerificationRefreshReconnectNeeded
publish onIncomeVerificationRefreshReconnectNeeded
Payroll Income refresh failed because the user must re-connect their payroll account.
transfer/eventsUpdate
publish onTransferEventsUpdate
New transfer events available; fetch via /transfer/event/sync.
transfer/recurringNewTransfer
publish onRecurringNewTransfer
A new transfer of a recurring transfer has been originated.
transfer/recurringTransferSkipped
publish onRecurringTransferSkipped
A scheduled recurring transfer could not be created on its planned date.
transfer/recurringCancelled
publish onRecurringCancelled
A recurring transfer was cancelled by Plaid.
statements/refreshComplete
publish onStatementsRefreshComplete
A statement refresh extraction (success or failure) triggered by /statements/refresh has completed.
link/sessionFinished
publish onLinkSessionFinished
A Link session ended with a final status of SUCCESS or EXITED. Used by Layer flows.
link/itemAddResult
publish onLinkItemAddResult
User successfully added a Plaid Item during a Link session; carries the public token.
link/events
publish onLinkEvents
Summary of events emitted during a Link session.
identityVerification/statusUpdated
publish onIdentityVerificationStatusUpdated
An identity verification status changed (dashboard or API trigger).
identityVerification/stepUpdated
publish onIdentityVerificationStepUpdated
A step in the identity verification process was completed.
identityVerification/retried
publish onIdentityVerificationRetried
An identity verification was retried (dashboard or API trigger).
beacon/userStatusUpdated
publish onBeaconUserStatusUpdated
A Beacon User status changed (dashboard action or Beacon Network report).
beacon/reportCreated
publish onBeaconReportCreated
A Beacon User was first reported to the Beacon Network.
beacon/reportUpdated
publish onBeaconReportUpdated
An existing Beacon Report was modified or removed from the Network.
beacon/reportSyndicationCreated
publish onBeaconReportSyndicationCreated
A Beacon Network report matched one of your Beacon Users.
beacon/duplicateDetected
publish onBeaconDuplicateDetected
A Beacon User created within your organization matched one of your existing users.
screening/statusUpdated
publish onScreeningStatusUpdated
An individual watchlist screening status changed (manually or via ongoing monitoring).
entityScreening/statusUpdated
publish onEntityScreeningStatusUpdated
An entity watchlist screening status changed (manually or via ongoing monitoring).

Messages

ItemErrorWebhook
ITEM / ERROR
Fired when an error is encountered with an Item.
ItemLoginRepairedWebhook
ITEM / LOGIN_REPAIRED
NewAccountsAvailableWebhook
ITEM / NEW_ACCOUNTS_AVAILABLE
PendingExpirationWebhook
ITEM / PENDING_EXPIRATION
UserPermissionRevokedWebhook
ITEM / USER_PERMISSION_REVOKED
UserAccountRevokedWebhook
ITEM / USER_ACCOUNT_REVOKED
WebhookUpdateAcknowledgedWebhook
ITEM / WEBHOOK_UPDATE_ACKNOWLEDGED
SyncUpdatesAvailableWebhook
TRANSACTIONS / SYNC_UPDATES_AVAILABLE
InitialUpdateWebhook
TRANSACTIONS / INITIAL_UPDATE
HistoricalUpdateWebhook
TRANSACTIONS / HISTORICAL_UPDATE
DefaultUpdateWebhook
TRANSACTIONS / DEFAULT_UPDATE
TransactionsRemovedWebhook
TRANSACTIONS / TRANSACTIONS_REMOVED
RecurringTransactionsUpdateWebhook
TRANSACTIONS / RECURRING_TRANSACTIONS_UPDATE
AuthDefaultUpdateWebhook
AUTH / DEFAULT_UPDATE
AutomaticallyVerifiedWebhook
AUTH / AUTOMATICALLY_VERIFIED
VerificationExpiredWebhook
AUTH / VERIFICATION_EXPIRED
HostedMMDVerificationWebhook
AUTH / SMS_MICRODEPOSITS_VERIFICATION
ProductPermissionsRequiredAuthWebhook
AUTH / PRODUCT_PERMISSIONS_REQUIRED
BankTransfersEventsUpdateWebhook
BANK_TRANSFERS / BANK_TRANSFERS_EVENTS_UPDATE
IdentityDefaultUpdateWebhook
IDENTITY / DEFAULT_UPDATE
ProductPermissionsRequiredIdentityWebhook
IDENTITY / PRODUCT_PERMISSIONS_REQUIRED
HoldingsDefaultUpdateWebhook
HOLDINGS / DEFAULT_UPDATE
InvestmentsDefaultUpdateWebhook
INVESTMENTS_TRANSACTIONS / DEFAULT_UPDATE
InvestmentsHistoricalUpdateWebhook
INVESTMENTS_TRANSACTIONS / HISTORICAL_UPDATE
LiabilitiesDefaultUpdateWebhook
LIABILITIES / DEFAULT_UPDATE
AssetsProductReadyWebhook
ASSETS / PRODUCT_READY
AssetsErrorWebhook
ASSETS / ERROR
ItemProductReadyWebhook
INCOME / PRODUCT_READY
IncomeVerificationStatusWebhook
INCOME / INCOME_VERIFICATION
IncomeVerificationRiskSignalsStatusWebhook
INCOME / INCOME_VERIFICATION_RISK_SIGNALS
BankIncomeCompleteWebhook
INCOME / BANK_INCOME_COMPLETE
BankIncomeRefreshUpdateWebhook
INCOME / BANK_INCOME_REFRESH_UPDATE
BankIncomeRefreshCompleteWebhook
INCOME / BANK_INCOME_REFRESH_COMPLETE
IncomeVerificationRefreshReconnectNeededWebhook
INCOME / INCOME_VERIFICATION_REFRESH_RECONNECT_NEEDED
TransferEventsUpdateWebhook
TRANSFER / TRANSFER_EVENTS_UPDATE
RecurringNewTransferWebhook
TRANSFER / RECURRING_NEW_TRANSFER
RecurringTransferSkippedWebhook
TRANSFER / RECURRING_TRANSFER_SKIPPED
RecurringCancelledWebhook
TRANSFER / RECURRING_CANCELLED
StatementsRefreshCompleteWebhook
STATEMENTS / STATEMENTS_REFRESH_COMPLETE
LinkSessionFinishedWebhook
LINK / SESSION_FINISHED (Layer)
ItemAddResultWebhook
LINK / ITEM_ADD_RESULT
LinkEventsWebhook
LINK / EVENTS
IdentityVerificationStatusUpdatedWebhook
IDENTITY_VERIFICATION / STATUS_UPDATED
IdentityVerificationStepUpdatedWebhook
IDENTITY_VERIFICATION / STEP_UPDATED
IdentityVerificationRetriedWebhook
IDENTITY_VERIFICATION / RETRIED
BeaconUserStatusUpdatedWebhook
BEACON / USER_STATUS_UPDATED
BeaconReportCreatedWebhook
BEACON / REPORT_CREATED
BeaconReportUpdatedWebhook
BEACON / REPORT_UPDATED
BeaconReportSyndicationCreatedWebhook
BEACON / REPORT_SYNDICATION_CREATED
BeaconDuplicateDetectedWebhook
BEACON / DUPLICATE_DETECTED
ScreeningStatusUpdatedWebhook
SCREENING / STATUS_UPDATED
EntityScreeningStatusUpdatedWebhook
ENTITY_SCREENING / STATUS_UPDATED

Servers

https
production {webhookUrl}
Customer-hosted HTTPS endpoint that receives webhook POSTs from Plaid. Plaid sends webhooks from a published set of static IPs documented at https://plaid.com/docs/api/webhooks/#ip-addresses.

AsyncAPI Specification

Raw ↑
asyncapi: '2.6.0'
info:
  title: Plaid Webhooks
  version: '2020-09-14'
  description: |
    AsyncAPI 2.6 specification for the Plaid webhook surface.

    Plaid delivers asynchronous notifications via HTTP POST to the URL registered on
    an Item (`webhook` parameter in `/link/token/create`) or configured per-product
    in the Plaid Dashboard. Each webhook carries a JSON body identified by the
    `webhook_type` and `webhook_code` fields and is signed with an ES256 JWT in the
    `Plaid-Verification` HTTP header.

    Webhooks delivered with a non-200 response are retried with exponential backoff
    for up to 24 hours starting at 30 seconds.

    Sources:
      - https://plaid.com/docs/api/webhooks/
      - https://plaid.com/docs/api/webhooks/webhook-verification/
      - Product-specific webhook reference pages on plaid.com/docs/api/products/*
      - Plaid OpenAPI spec (openapi/plaid-openapi-original.yml)
  contact:
    name: Plaid Developer Support
    url: https://plaid.com/docs/
  license:
    name: Proprietary - see Plaid Master Services Agreement
    url: https://plaid.com/legal/

defaultContentType: application/json

servers:
  production:
    url: '{webhookUrl}'
    protocol: https
    description: |
      Customer-hosted HTTPS endpoint that receives webhook POSTs from Plaid.
      Plaid sends webhooks from a published set of static IPs documented at
      https://plaid.com/docs/api/webhooks/#ip-addresses.
    variables:
      webhookUrl:
        default: https://example.com/plaid/webhook
        description: The fully-qualified webhook URL registered with Plaid.
    security:
      - plaidJwt: []

channels:
  # ---------------- ITEM ----------------
  item/error:
    description: Fired when an error is encountered with an Item. Resolvable via Link update mode.
    publish:
      operationId: onItemError
      message:
        $ref: '#/components/messages/ItemErrorWebhook'
  item/loginRepaired:
    description: Item exited ITEM_LOGIN_REQUIRED state without the user going through update mode in your app.
    publish:
      operationId: onItemLoginRepaired
      message:
        $ref: '#/components/messages/ItemLoginRepairedWebhook'
  item/newAccountsAvailable:
    description: Plaid detected a new account on an Item created/updated with Account Select v2.
    publish:
      operationId: onItemNewAccountsAvailable
      message:
        $ref: '#/components/messages/NewAccountsAvailableWebhook'
  item/pendingExpiration:
    description: An Item's access consent is expiring in 7 days (EU/UK PSD2 consent windows).
    publish:
      operationId: onItemPendingExpiration
      message:
        $ref: '#/components/messages/PendingExpirationWebhook'
  item/userPermissionRevoked:
    description: End user revoked Item access via my.plaid.com or the institution's OAuth consent portal.
    publish:
      operationId: onItemUserPermissionRevoked
      message:
        $ref: '#/components/messages/UserPermissionRevokedWebhook'
  item/userAccountRevoked:
    description: End user revoked access to a specific account on the data provider's portal (currently Chase).
    publish:
      operationId: onItemUserAccountRevoked
      message:
        $ref: '#/components/messages/UserAccountRevokedWebhook'
  item/webhookUpdateAcknowledged:
    description: Item webhook URL successfully updated; delivered to the newly specified URL.
    publish:
      operationId: onItemWebhookUpdateAcknowledged
      message:
        $ref: '#/components/messages/WebhookUpdateAcknowledgedWebhook'

  # ---------------- TRANSACTIONS ----------------
  transactions/syncUpdatesAvailable:
    description: Fresh transaction changes available; fetch via /transactions/sync.
    publish:
      operationId: onTransactionsSyncUpdatesAvailable
      message:
        $ref: '#/components/messages/SyncUpdatesAvailableWebhook'
  transactions/initialUpdate:
    description: Initial 30-day transaction pull complete. Legacy /transactions/get listener.
    publish:
      operationId: onTransactionsInitialUpdate
      message:
        $ref: '#/components/messages/InitialUpdateWebhook'
  transactions/historicalUpdate:
    description: Historical transaction backfill complete (up to 2 years). Legacy /transactions/get listener.
    publish:
      operationId: onTransactionsHistoricalUpdate
      message:
        $ref: '#/components/messages/HistoricalUpdateWebhook'
  transactions/defaultUpdate:
    description: New transactions detected. Legacy /transactions/get listener.
    publish:
      operationId: onTransactionsDefaultUpdate
      message:
        $ref: '#/components/messages/DefaultUpdateWebhook'
  transactions/transactionsRemoved:
    description: Previously returned transactions have been removed.
    publish:
      operationId: onTransactionsRemoved
      message:
        $ref: '#/components/messages/TransactionsRemovedWebhook'
  transactions/recurringTransactionsUpdate:
    description: Recurring transaction streams added or updated; refetch /transactions/recurring/get.
    publish:
      operationId: onRecurringTransactionsUpdate
      message:
        $ref: '#/components/messages/RecurringTransactionsUpdateWebhook'

  # ---------------- AUTH ----------------
  auth/defaultUpdate:
    description: Account or routing numbers have changed; refetch /auth/get and stop using old TANs.
    publish:
      operationId: onAuthDefaultUpdate
      message:
        $ref: '#/components/messages/AuthDefaultUpdateWebhook'
  auth/automaticallyVerified:
    description: Item verified via automated micro-deposits.
    publish:
      operationId: onAuthAutomaticallyVerified
      message:
        $ref: '#/components/messages/AutomaticallyVerifiedWebhook'
  auth/verificationExpired:
    description: Automated micro-deposit verification failed to complete within seven days.
    publish:
      operationId: onAuthVerificationExpired
      message:
        $ref: '#/components/messages/VerificationExpiredWebhook'
  auth/smsMicrodepositsVerification:
    description: Final status of a same-day micro-deposits verification session.
    publish:
      operationId: onAuthSmsMicrodepositsVerification
      message:
        $ref: '#/components/messages/HostedMMDVerificationWebhook'
  auth/productPermissionsRequired:
    description: ACCESS_NOT_GRANTED hit for Auth; put the user through update mode with `auth` in products.
    publish:
      operationId: onAuthProductPermissionsRequired
      message:
        $ref: '#/components/messages/ProductPermissionsRequiredAuthWebhook'
  auth/bankTransfersEventsUpdate:
    description: New ACH events available for micro-deposit verification tracking; fetch via /bank_transfer/event/sync.
    publish:
      operationId: onBankTransfersEventsUpdate
      message:
        $ref: '#/components/messages/BankTransfersEventsUpdateWebhook'

  # ---------------- IDENTITY ----------------
  identity/defaultUpdate:
    description: Identity data change detected on an Item.
    publish:
      operationId: onIdentityDefaultUpdate
      message:
        $ref: '#/components/messages/IdentityDefaultUpdateWebhook'
  identity/productPermissionsRequired:
    description: ACCESS_NOT_GRANTED hit for Identity; put the user through update mode with `identity` in products.
    publish:
      operationId: onIdentityProductPermissionsRequired
      message:
        $ref: '#/components/messages/ProductPermissionsRequiredIdentityWebhook'

  # ---------------- HOLDINGS ----------------
  holdings/defaultUpdate:
    description: New or updated holdings detected on an investment account (typically after market close).
    publish:
      operationId: onHoldingsDefaultUpdate
      message:
        $ref: '#/components/messages/HoldingsDefaultUpdateWebhook'

  # ---------------- INVESTMENTS_TRANSACTIONS ----------------
  investmentsTransactions/defaultUpdate:
    description: New investment transactions detected.
    publish:
      operationId: onInvestmentsDefaultUpdate
      message:
        $ref: '#/components/messages/InvestmentsDefaultUpdateWebhook'
  investmentsTransactions/historicalUpdate:
    description: Historical investments transactions extraction complete.
    publish:
      operationId: onInvestmentsHistoricalUpdate
      message:
        $ref: '#/components/messages/InvestmentsHistoricalUpdateWebhook'

  # ---------------- LIABILITIES ----------------
  liabilities/defaultUpdate:
    description: New or updated liabilities detected on a Liabilities Item.
    publish:
      operationId: onLiabilitiesDefaultUpdate
      message:
        $ref: '#/components/messages/LiabilitiesDefaultUpdateWebhook'

  # ---------------- ASSETS ----------------
  assets/productReady:
    description: Asset Report ready; call /asset_report/get to retrieve.
    publish:
      operationId: onAssetsProductReady
      message:
        $ref: '#/components/messages/AssetsProductReadyWebhook'
  assets/error:
    description: Asset Report generation failed (ASSET_REPORT_ERROR).
    publish:
      operationId: onAssetsError
      message:
        $ref: '#/components/messages/AssetsErrorWebhook'

  # ---------------- INCOME ----------------
  income/productReady:
    description: Plaid has finished calculating income for an Item.
    publish:
      operationId: onIncomeProductReady
      message:
        $ref: '#/components/messages/ItemProductReadyWebhook'
  income/incomeVerification:
    description: Income verification processing status change (Document Income flow).
    publish:
      operationId: onIncomeVerification
      message:
        $ref: '#/components/messages/IncomeVerificationStatusWebhook'
  income/incomeVerificationRiskSignals:
    description: Risk signals processed for documents uploaded via Document Income.
    publish:
      operationId: onIncomeVerificationRiskSignals
      message:
        $ref: '#/components/messages/IncomeVerificationRiskSignalsStatusWebhook'
  income/bankIncomeComplete:
    description: A CRA-enabled bank income report has completed (success or failure); call /credit/bank_income/get.
    publish:
      operationId: onBankIncomeComplete
      message:
        $ref: '#/components/messages/BankIncomeCompleteWebhook'
  income/bankIncomeRefreshUpdate:
    description: A change in user income has been detected; call /credit/bank_income/refresh.
    publish:
      operationId: onBankIncomeRefreshUpdate
      message:
        $ref: '#/components/messages/BankIncomeRefreshUpdateWebhook'
  income/bankIncomeRefreshComplete:
    description: A refreshed bank income report has finished generating.
    publish:
      operationId: onBankIncomeRefreshComplete
      message:
        $ref: '#/components/messages/BankIncomeRefreshCompleteWebhook'
  income/incomeVerificationRefreshReconnectNeeded:
    description: Payroll Income refresh failed because the user must re-connect their payroll account.
    publish:
      operationId: onIncomeVerificationRefreshReconnectNeeded
      message:
        $ref: '#/components/messages/IncomeVerificationRefreshReconnectNeededWebhook'

  # ---------------- TRANSFER ----------------
  transfer/eventsUpdate:
    description: New transfer events available; fetch via /transfer/event/sync.
    publish:
      operationId: onTransferEventsUpdate
      message:
        $ref: '#/components/messages/TransferEventsUpdateWebhook'
  transfer/recurringNewTransfer:
    description: A new transfer of a recurring transfer has been originated.
    publish:
      operationId: onRecurringNewTransfer
      message:
        $ref: '#/components/messages/RecurringNewTransferWebhook'
  transfer/recurringTransferSkipped:
    description: A scheduled recurring transfer could not be created on its planned date.
    publish:
      operationId: onRecurringTransferSkipped
      message:
        $ref: '#/components/messages/RecurringTransferSkippedWebhook'
  transfer/recurringCancelled:
    description: A recurring transfer was cancelled by Plaid.
    publish:
      operationId: onRecurringCancelled
      message:
        $ref: '#/components/messages/RecurringCancelledWebhook'

  # ---------------- SIGNAL ----------------
  # Plaid Signal is request/response only — no documented webhook codes were found
  # on the public reference pages at the time of this writing.

  # ---------------- STATEMENTS ----------------
  statements/refreshComplete:
    description: A statement refresh extraction (success or failure) triggered by /statements/refresh has completed.
    publish:
      operationId: onStatementsRefreshComplete
      message:
        $ref: '#/components/messages/StatementsRefreshCompleteWebhook'

  # ---------------- LAYER / LINK ----------------
  link/sessionFinished:
    description: A Link session ended with a final status of SUCCESS or EXITED. Used by Layer flows.
    publish:
      operationId: onLinkSessionFinished
      message:
        $ref: '#/components/messages/LinkSessionFinishedWebhook'
  link/itemAddResult:
    description: User successfully added a Plaid Item during a Link session; carries the public token.
    publish:
      operationId: onLinkItemAddResult
      message:
        $ref: '#/components/messages/ItemAddResultWebhook'
  link/events:
    description: Summary of events emitted during a Link session.
    publish:
      operationId: onLinkEvents
      message:
        $ref: '#/components/messages/LinkEventsWebhook'

  # ---------------- IDENTITY VERIFICATION ----------------
  identityVerification/statusUpdated:
    description: An identity verification status changed (dashboard or API trigger).
    publish:
      operationId: onIdentityVerificationStatusUpdated
      message:
        $ref: '#/components/messages/IdentityVerificationStatusUpdatedWebhook'
  identityVerification/stepUpdated:
    description: A step in the identity verification process was completed.
    publish:
      operationId: onIdentityVerificationStepUpdated
      message:
        $ref: '#/components/messages/IdentityVerificationStepUpdatedWebhook'
  identityVerification/retried:
    description: An identity verification was retried (dashboard or API trigger).
    publish:
      operationId: onIdentityVerificationRetried
      message:
        $ref: '#/components/messages/IdentityVerificationRetriedWebhook'

  # ---------------- MONITOR (Beacon) ----------------
  beacon/userStatusUpdated:
    description: A Beacon User status changed (dashboard action or Beacon Network report).
    publish:
      operationId: onBeaconUserStatusUpdated
      message:
        $ref: '#/components/messages/BeaconUserStatusUpdatedWebhook'
  beacon/reportCreated:
    description: A Beacon User was first reported to the Beacon Network.
    publish:
      operationId: onBeaconReportCreated
      message:
        $ref: '#/components/messages/BeaconReportCreatedWebhook'
  beacon/reportUpdated:
    description: An existing Beacon Report was modified or removed from the Network.
    publish:
      operationId: onBeaconReportUpdated
      message:
        $ref: '#/components/messages/BeaconReportUpdatedWebhook'
  beacon/reportSyndicationCreated:
    description: A Beacon Network report matched one of your Beacon Users.
    publish:
      operationId: onBeaconReportSyndicationCreated
      message:
        $ref: '#/components/messages/BeaconReportSyndicationCreatedWebhook'
  beacon/duplicateDetected:
    description: A Beacon User created within your organization matched one of your existing users.
    publish:
      operationId: onBeaconDuplicateDetected
      message:
        $ref: '#/components/messages/BeaconDuplicateDetectedWebhook'

  # ---------------- WATCHLIST SCREENING ----------------
  screening/statusUpdated:
    description: An individual watchlist screening status changed (manually or via ongoing monitoring).
    publish:
      operationId: onScreeningStatusUpdated
      message:
        $ref: '#/components/messages/ScreeningStatusUpdatedWebhook'
  entityScreening/statusUpdated:
    description: An entity watchlist screening status changed (manually or via ongoing monitoring).
    publish:
      operationId: onEntityScreeningStatusUpdated
      message:
        $ref: '#/components/messages/EntityScreeningStatusUpdatedWebhook'

components:
  securitySchemes:
    plaidJwt:
      type: httpApiKey
      in: header
      name: Plaid-Verification
      description: |
        Plaid signs every webhook with a JSON Web Token (JWT) carried in the `Plaid-Verification`
        HTTP header.

        Verification procedure:
          1. Decode the JWT header and confirm `alg` is `ES256`.
          2. Extract the `kid` (key ID) from the JWT header.
          3. Fetch the corresponding verification key from `/webhook_verification_key/get`.
          4. Verify the JWT signature using the returned ECDSA P-256 public key (JWK).
          5. Verify the `iat` (issued-at) claim is within 5 minutes of the current time.
          6. Compute SHA-256 of the raw request body and confirm it matches the
             `request_body_sha256` claim in the JWT payload.

        Reference: https://plaid.com/docs/api/webhooks/webhook-verification/

  messages:
    # -------- ITEM --------
    ItemErrorWebhook:
      name: ItemErrorWebhook
      title: ITEM / ERROR
      summary: Fired when an error is encountered with an Item.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ItemErrorWebhook'
    ItemLoginRepairedWebhook:
      name: ItemLoginRepairedWebhook
      title: ITEM / LOGIN_REPAIRED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ItemLoginRepairedWebhook'
    NewAccountsAvailableWebhook:
      name: NewAccountsAvailableWebhook
      title: ITEM / NEW_ACCOUNTS_AVAILABLE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/NewAccountsAvailableWebhook'
    PendingExpirationWebhook:
      name: PendingExpirationWebhook
      title: ITEM / PENDING_EXPIRATION
      contentType: application/json
      payload:
        $ref: '#/components/schemas/PendingExpirationWebhook'
    UserPermissionRevokedWebhook:
      name: UserPermissionRevokedWebhook
      title: ITEM / USER_PERMISSION_REVOKED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/UserPermissionRevokedWebhook'
    UserAccountRevokedWebhook:
      name: UserAccountRevokedWebhook
      title: ITEM / USER_ACCOUNT_REVOKED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/UserAccountRevokedWebhook'
    WebhookUpdateAcknowledgedWebhook:
      name: WebhookUpdateAcknowledgedWebhook
      title: ITEM / WEBHOOK_UPDATE_ACKNOWLEDGED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/WebhookUpdateAcknowledgedWebhook'

    # -------- TRANSACTIONS --------
    SyncUpdatesAvailableWebhook:
      name: SyncUpdatesAvailableWebhook
      title: TRANSACTIONS / SYNC_UPDATES_AVAILABLE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/SyncUpdatesAvailableWebhook'
    InitialUpdateWebhook:
      name: InitialUpdateWebhook
      title: TRANSACTIONS / INITIAL_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/InitialUpdateWebhook'
    HistoricalUpdateWebhook:
      name: HistoricalUpdateWebhook
      title: TRANSACTIONS / HISTORICAL_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/HistoricalUpdateWebhook'
    DefaultUpdateWebhook:
      name: DefaultUpdateWebhook
      title: TRANSACTIONS / DEFAULT_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/DefaultUpdateWebhook'
    TransactionsRemovedWebhook:
      name: TransactionsRemovedWebhook
      title: TRANSACTIONS / TRANSACTIONS_REMOVED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/TransactionsRemovedWebhook'
    RecurringTransactionsUpdateWebhook:
      name: RecurringTransactionsUpdateWebhook
      title: TRANSACTIONS / RECURRING_TRANSACTIONS_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/RecurringTransactionsUpdateWebhook'

    # -------- AUTH --------
    AuthDefaultUpdateWebhook:
      name: AuthDefaultUpdateWebhook
      title: AUTH / DEFAULT_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/AuthDefaultUpdateWebhook'
    AutomaticallyVerifiedWebhook:
      name: AutomaticallyVerifiedWebhook
      title: AUTH / AUTOMATICALLY_VERIFIED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/AutomaticallyVerifiedWebhook'
    VerificationExpiredWebhook:
      name: VerificationExpiredWebhook
      title: AUTH / VERIFICATION_EXPIRED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/VerificationExpiredWebhook'
    HostedMMDVerificationWebhook:
      name: HostedMMDVerificationWebhook
      title: AUTH / SMS_MICRODEPOSITS_VERIFICATION
      contentType: application/json
      payload:
        $ref: '#/components/schemas/HostedMMDVerificationWebhook'
    ProductPermissionsRequiredAuthWebhook:
      name: ProductPermissionsRequiredAuthWebhook
      title: AUTH / PRODUCT_PERMISSIONS_REQUIRED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ProductPermissionsRequiredAuthWebhook'
    BankTransfersEventsUpdateWebhook:
      name: BankTransfersEventsUpdateWebhook
      title: BANK_TRANSFERS / BANK_TRANSFERS_EVENTS_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BankTransfersEventsUpdateWebhook'

    # -------- IDENTITY --------
    IdentityDefaultUpdateWebhook:
      name: IdentityDefaultUpdateWebhook
      title: IDENTITY / DEFAULT_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/IdentityDefaultUpdateWebhook'
    ProductPermissionsRequiredIdentityWebhook:
      name: ProductPermissionsRequiredIdentityWebhook
      title: IDENTITY / PRODUCT_PERMISSIONS_REQUIRED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ProductPermissionsRequiredIdentityWebhook'

    # -------- HOLDINGS / INVESTMENTS --------
    HoldingsDefaultUpdateWebhook:
      name: HoldingsDefaultUpdateWebhook
      title: HOLDINGS / DEFAULT_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/HoldingsDefaultUpdateWebhook'
    InvestmentsDefaultUpdateWebhook:
      name: InvestmentsDefaultUpdateWebhook
      title: INVESTMENTS_TRANSACTIONS / DEFAULT_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/InvestmentsDefaultUpdateWebhook'
    InvestmentsHistoricalUpdateWebhook:
      name: InvestmentsHistoricalUpdateWebhook
      title: INVESTMENTS_TRANSACTIONS / HISTORICAL_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/InvestmentsHistoricalUpdateWebhook'

    # -------- LIABILITIES --------
    LiabilitiesDefaultUpdateWebhook:
      name: LiabilitiesDefaultUpdateWebhook
      title: LIABILITIES / DEFAULT_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/LiabilitiesDefaultUpdateWebhook'

    # -------- ASSETS --------
    AssetsProductReadyWebhook:
      name: AssetsProductReadyWebhook
      title: ASSETS / PRODUCT_READY
      contentType: application/json
      payload:
        $ref: '#/components/schemas/AssetsProductReadyWebhook'
    AssetsErrorWebhook:
      name: AssetsErrorWebhook
      title: ASSETS / ERROR
      contentType: application/json
      payload:
        $ref: '#/components/schemas/AssetsErrorWebhook'

    # -------- INCOME --------
    ItemProductReadyWebhook:
      name: ItemProductReadyWebhook
      title: INCOME / PRODUCT_READY
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ItemProductReadyWebhook'
    IncomeVerificationStatusWebhook:
      name: IncomeVerificationStatusWebhook
      title: INCOME / INCOME_VERIFICATION
      contentType: application/json
      payload:
        $ref: '#/components/schemas/IncomeVerificationStatusWebhook'
    IncomeVerificationRiskSignalsStatusWebhook:
      name: IncomeVerificationRiskSignalsStatusWebhook
      title: INCOME / INCOME_VERIFICATION_RISK_SIGNALS
      contentType: application/json
      payload:
        $ref: '#/components/schemas/IncomeVerificationRiskSignalsStatusWebhook'
    BankIncomeCompleteWebhook:
      name: BankIncomeCompleteWebhook
      title: INCOME / BANK_INCOME_COMPLETE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BankIncomeCompleteWebhook'
    BankIncomeRefreshUpdateWebhook:
      name: BankIncomeRefreshUpdateWebhook
      title: INCOME / BANK_INCOME_REFRESH_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BankIncomeRefreshUpdateWebhook'
    BankIncomeRefreshCompleteWebhook:
      name: BankIncomeRefreshCompleteWebhook
      title: INCOME / BANK_INCOME_REFRESH_COMPLETE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BankIncomeRefreshCompleteWebhook'
    IncomeVerificationRefreshReconnectNeededWebhook:
      name: IncomeVerificationRefreshReconnectNeededWebhook
      title: INCOME / INCOME_VERIFICATION_REFRESH_RECONNECT_NEEDED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/IncomeVerificationRefreshReconnectNeededWebhook'

    # -------- TRANSFER --------
    TransferEventsUpdateWebhook:
      name: TransferEventsUpdateWebhook
      title: TRANSFER / TRANSFER_EVENTS_UPDATE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/TransferEventsUpdateWebhook'
    RecurringNewTransferWebhook:
      name: RecurringNewTransferWebhook
      title: TRANSFER / RECURRING_NEW_TRANSFER
      contentType: application/json
      payload:
        $ref: '#/components/schemas/RecurringNewTransferWebhook'
    RecurringTransferSkippedWebhook:
      name: RecurringTransferSkippedWebhook
      title: TRANSFER / RECURRING_TRANSFER_SKIPPED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/RecurringTransferSkippedWebhook'
    RecurringCancelledWebhook:
      name: RecurringCancelledWebhook
      title: TRANSFER / RECURRING_CANCELLED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/RecurringCancelledWebhook'

    # -------- STATEMENTS --------
    StatementsRefreshCompleteWebhook:
      name: StatementsRefreshCompleteWebhook
      title: STATEMENTS / STATEMENTS_REFRESH_COMPLETE
      contentType: application/json
      payload:
        $ref: '#/components/schemas/StatementsRefreshCompleteWebhook'

    # -------- LAYER / LINK --------
    LinkSessionFinishedWebhook:
      name: LinkSessionFinishedWebhook
      title: LINK / SESSION_FINISHED (Layer)
      contentType: application/json
      payload:
        $ref: '#/components/schemas/LinkSessionFinishedWebhook'
    ItemAddResultWebhook:
      name: ItemAddResultWebhook
      title: LINK / ITEM_ADD_RESULT
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ItemAddResultWebhook'
    LinkEventsWebhook:
      name: LinkEventsWebhook
      title: LINK / EVENTS
      contentType: application/json
      payload:
        $ref: '#/components/schemas/LinkEventsWebhook'

    # -------- IDENTITY VERIFICATION --------
    IdentityVerificationStatusUpdatedWebhook:
      name: IdentityVerificationStatusUpdatedWebhook
      title: IDENTITY_VERIFICATION / STATUS_UPDATED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/IdentityVerificationStatusUpdatedWebhook'
    IdentityVerificationStepUpdatedWebhook:
      name: IdentityVerificationStepUpdatedWebhook
      title: IDENTITY_VERIFICATION / STEP_UPDATED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/IdentityVerificationStepUpdatedWebhook'
    IdentityVerificationRetriedWebhook:
      name: IdentityVerificationRetriedWebhook
      title: IDENTITY_VERIFICATION / RETRIED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/IdentityVerificationRetriedWebhook'

    # -------- MONITOR (Beacon) --------
    BeaconUserStatusUpdatedWebhook:
      name: BeaconUserStatusUpdatedWebhook
      title: BEACON / USER_STATUS_UPDATED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BeaconUserStatusUpdatedWebhook'
    BeaconReportCreatedWebhook:
      name: BeaconReportCreatedWebhook
      title: BEACON / REPORT_CREATED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BeaconReportCreatedWebhook'
    BeaconReportUpdatedWebhook:
      name: BeaconReportUpdatedWebhook
      title: BEACON / REPORT_UPDATED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BeaconReportUpdatedWebhook'
    BeaconReportSyndicationCreatedWebhook:
      name: BeaconReportSyndicationCreatedWebhook
      title: BEACON / REPORT_SYNDICATION_CREATED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BeaconReportSyndicationCreatedWebhook'
    BeaconDuplicateDetectedWebhook:
      name: BeaconDuplicateDetectedWebhook
      title: BEACON / DUPLICATE_DETECTED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/BeaconDuplicateDetectedWebhook'

    # -------- WATCHLIST SCREENING --------
    ScreeningStatusUpdatedWebhook:
      name: ScreeningStatusUpdatedWebhook
      title: SCREENING / STATUS_UPDATED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ScreeningStatusUpdatedWebhook'
    EntityScreeningStatusUpdatedWebhook:
      name: EntityScreeningStatusUpdatedWebhook
      title: ENTITY_SCREENING / STATUS_UPDATED
      contentType: application/json
      payload:
        $ref: '#/components/schemas/EntityScreeningStatusUpdatedWebhook'

  schemas:
    WebhookEnvironment:
      type: string
      enum: [sandbox, production]
      description: The Plaid environment the webhook originated from.

    PlaidError:
      type: object
      nullable: true
      description: |
        Standard Plaid error envelope. Populated when an asynchronous operation fails.
      properties:
        error_type: { type: string }
        error_code: { type: string }
        error_code_reason: { type: string, nullable: true }
        error_message: { type: string }
        display_message: { type: string, nullable: true }
        status: { type: integer, nullable: true }
        documentation_url: { type: string, nullable: true }
        suggested_action: { type: string, nullable: true }
        request_id: { type: string, nullable: true }

    # ---------- ITEM ----------
    ItemErrorWebhook:
      type: object
      additionalProperties: true
      required: [webhook_type, webhook_code, item_id, error, environment]
      properties:
        webhook_type: { type: string, const: ITEM }
        webhook_code: { type: string, const: ERROR }
        item_id: { type: string }
        user_id: { type: string }
        error: { $ref: '#/components/schemas/PlaidError' }
        environment: { $ref: '#/components/schemas/WebhookEnvironment' }

    ItemLoginRepairedWebhook:
      type: object
      additionalProperties: true
      required: [webhook_type, webhook_code, item_id, environment]
      properties:
        webhook_type: { type: string, const: ITEM }
        webhook_code: { type: string, const: LOGIN_REPAIRED }
        item_id: { type: string }
        user_id: { type: string }
        environment: { $ref: '#/components/schemas/WebhookEnvironment' }

    NewAccountsAvailableWebhook:
      type: object
      additionalProperties: true
      required: [webhook_type, webhook_code, item_id]
      properties:
        webhook_type: { type: string, const: ITEM }
        webhook_code: { type: string, const: NEW_ACCOUNTS_AVAILABLE }
        item_id: { type: string }
        user_id: { type: string }
        error: { $ref: '#/components/schemas/PlaidError' }
        environment: { $ref: '#/components/schemas/WebhookEnvironment' }

    PendingExpirationWebhook:
      type: object
      additionalProperties: true
      required: [webhook_type, webhook_code, item_id, consent_expiration_time, environment]
      properties:
        webhook_type: { type: string, const: ITEM }
        webhook_code: { type: string, const: PENDING_EXPIRATION }
        item_id: { type: string }
        user_id: { type: string }
        consent_expiration_time:
          type: string
          format: dat

# --- truncated at 32 KB (58 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/plaid/refs/heads/main/asyncapi/plaid-webhooks--asyncapi-original.yml