Webhooks
The SnapTrade API can be configured to send you webhook notifications when certain events happen. API-key integrations receive the API-key webhook schema, while OAuth applications receive the versioned oauth_v1 schema described below.
To get started with webhooks, visit the webhook tab of the SnapTrade Dashboard to configure a webhook listener.
OAuth Application Webhooks
OAuth applications can receive connection and account events for SnapTrade Personal users. An event is delivered when all of the following are true:
- The OAuth app is registered under the SnapTrade customer that owns the configured webhook listener.
- The user has an active authorization for the app.
- The authorization includes the
webhookscope.
Request the scope alongside read:
scope=read webhook
Existing authorizations do not automatically gain a newly requested scope. A user who previously granted only read must go through the OAuth authorization flow again and approve webhook before the app can receive events for that user.
OAuth apps registered under a customer reuse that customer's webhook URL, consumer key, and custom headers. If the customer has multiple OAuth apps, use oauthClientId in the payload to identify which app the notification targets. A Personal user who has granted webhook access to multiple apps can cause one notification to be delivered to each eligible app.
OAuth Webhook Schema
OAuth application webhooks use schemaVersion: "oauth_v1":
| Field | Description |
|---|---|
schemaVersion | The payload contract version. OAuth webhooks currently use oauth_v1. |
webhookId | The unique ID of this webhook notification. |
oauthClientId | The OAuth client ID of the application receiving the notification. |
eventTimestamp | When the event occurred, formatted as an ISO 8601 timestamp. |
userId | The SnapTrade Personal user UUID. This matches sub.snaptrade_user_id returned during OAuth token exchange. |
eventType | The event that occurred. |
connectionId | The brokerage connection UUID, when the event relates to a connection. This replaces brokerageAuthorizationId from the API-key schema. |
brokerageId | The brokerage UUID, when available. |
accountId | The brokerage account UUID, when the event relates to an account. |
connectionAttemptedResult | The result of a connection attempt, when applicable. |
details | Additional event-specific data, when available. |
The OAuth schema does not include the API-key schema's clientId, brokerageAuthorizationId, or webhookSecret fields. Continue to verify authenticity using the Signature request header.
Verifying Webhook Authenticity
Note: Webhook secrets are deprecated.
You can verify the authenticity of any SnapTrade webhook by using the Signature header contained in the webhook headers, and comparing that to the expected signature generated using your consumer key. Note that the consumer key is different from the webhook secret that is being deprecated.
The Signature header contains an HMAC SHA256 hash of the request body, using your consumer key as the key.
Here's an example implementation of a Flask webhook handler that verifies the authenticity of incoming webhooks:
Aside from signature verification:
- SnapTrade supports custom webhook request headers (which are useful if your webhook handler is protected by Cloudflare)
- SnapTrade does not support IP whitelisting at this time
Handling Undeliverable Webhooks
When your webhook handler responds to our requests with a status code that is not 200, 201, 202, or 204, we mark that webhook as undelivered in our system.
We will attempt to resend an undeliverable webhook notification with an exponential backoff (starting at 30 minutes) until it is either delivered successfully, or 3 retry attempts have been made.
Webhook Types
The different webhook event types are outlined below.
USER_REGISTERED
Sent when a new user is successfully registered through the /registerUser/ endpoint.
Example payload is below:
USER_DELETED
Sent when a user is successfully deleted through the /deleteUser/ endpoint.
Example payload is below:
CONNECTION_ATTEMPTED
Sent when a user a user attempts to make a brokerage connection, will also report the result of the attempt.
Example payload is below:
Possible values for the connectionAttemptedResult field are:
SUCCESSAUTH_EXPIREDINVALID_AUTH_CODEAUTH_NOT_IN_PROGRESSDIFFERENT_ACCOUNTUNCAUGHT_ERRORINVALID_CREDENTIALSINVALID_MFA_CODENO_DATA
CONNECTION_ADDED
Sent when a new brokerage connection is created using the SnapTrade connection portal.
Example payload is below.
CONNECTION_DELETED
Sent when a user deletes an existing connection using the SnapTrade connection portal.
Example payload is below:
CONNECTION_BROKEN
Sent when a user's connection is broken (also referred to as disabled) for some reason, usually an inability to handshake with the brokerage's API. To resolve this state, see Fix Disabled Connections.
Example payload is below:
CONNECTION_FIXED
Sent when a broken (disabled) connection is fixed.
Example payload is below:
CONNECTION_UPDATED
Sent when a brokerage connection is updated.
Example payload is below:
CONNECTION_FAILED
Sent when a user’s attempt to connect to a brokerage has failed.
Example payload is below:
NEW_ACCOUNT_AVAILABLE
Sent when a new account is detected through a brokerage connection.
Example payload is below:
ACCOUNT_TRANSACTIONS_INITIAL_UPDATE
Sent when we complete the initial transactions sync after a new account is connected. The duration needed for the first sync can vary by brokerage and by how many historical transactions the user has, but usually takes between 1-60 seconds.
Example payload is below:
ACCOUNT_TRANSACTIONS_UPDATED
Sent when account transactions are incrementally updated. After the initial sync has already completed, account will be checked for new transactions daily. If new transactions are found they will be saved and this webhook will be sent. Only ACCOUNT_TRANSACTIONS_INITIAL_UPDATE gets sent upon first connection.
Example payload is below:
ACCOUNT_REMOVED
Sent when an account is removed from a connection.
Example payload is below:
ACCOUNT_HOLDINGS_UPDATED
Gets sent when holdings for an account has been updated. Updated does not necessarily mean that the holdings have changed, instead it means that updating holdings with new data has been attempted. In the rare case that a holdings update fails (for example when a brokerage's API is down for maintenance), the webhook will still be sent and details on what failed will be included as part of the details field in the body.
This webhook will be sent when we run our daily account syncs, as well as if a manual account refresh has been requested via: https://docs.snaptrade.com/reference/Connections/Connections_refreshBrokerageAuthorization
Account Holdings in this context refers to:
- Positions
- Balances
- Orders
- Total Account Value
- Account Detail
Historical transactions will be polled for, but will not fetch transactions from the current day
Example payload is below:
TRADE_DETECTION
Gets sent seconds after a trade has been executed in a specific account. Requires an account specific subscription. Please contact support@snaptrade.com (or reach out on discord/slack) for more information about pricing and setting up subscriptions. Only available on certain brokerages.
Returns a list of orders placed
Example payload is below:
TRADE_UPDATE
Beta webhook that gets sent when the status of trade placed through SnapTrade changes. For example, will be sent if a trade gets cancelled, executed, partitally filled, etc. Please contact support@snaptrade.com (or reach out on discord/slack) to try out this experimental webhook. Only trades placed through SnapTrade will be monitored for status updates.
Returns a the current order detail in the webhook body
Example payload is below: