> For AI agents: the complete documentation index is available at [llms.txt](https://docs.snaptrade.com/llms.txt). Markdown versions of documentation pages are available by appending .md to the URL path.

# Webhooks

The SnapTrade API can be configured to send you webhook notifications when certain events happen.

To get started with webhooks, visit the webhook tab of the SnapTrade Dashboard to configure a webhook listener.

# 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:

```python
from base64 import b64encode
from hashlib import sha256
import datetime
import hmac
import json
import os
from flask import Flask, request, jsonify

app = Flask(__name__)

CONSUMER_KEY = os.getenv("SNAPTRADE_CONSUMER_KEY")

@app.route('/', methods=['POST'])
def webhook_listener():
    # Extract the payload
    payload = request.get_json()

    # Extract the Signature header
    signature = request.headers.get('Signature')

    # Verify the signature
    sig_content = json.dumps(payload, separators=(",", ":"), sort_keys=True)
    sig_digest = hmac.new(CONSUMER_KEY.encode(), sig_content.encode(), sha256).digest()
    calculated_signature = b64encode(sig_digest).decode()

    if calculated_signature != signature:
        raise Exception("Signature verification failed!")
    
    # Prevent replay attacks by checking the timestamp and making sure it was sent recently
    timestamp = datetime.datetime.fromisoformat(payload["eventTimestamp"])
    if (datetime.datetime.now(datetime.timezone.utc) - timestamp).total_seconds() > 300:
        raise Exception("Payload is too old.")

    print("Signature verified successfully.")

    # Log the received data
    print("Received payload:", payload)

    # Respond with a success message
    return jsonify({}), 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5123)
```

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:

```json
{
  "webhookId": "dbaee13a-1184-4677-9741-b6845e60ee3a",
  "clientId": "phjBdpKfpN",
  "eventTimestamp": "2022-05-31T11:16:54.658533+00:00",
  "userId": "qQYISR9xcmR8ZtVHNgg1lbYgSQcxafPqPW0ZbE0yaA6ham6n54",
  "eventType": "USER_REGISTERED",
  "webhookSecret": "lgCQWztweEFcHvjdLhHc"
}
```

## USER\_DELETED

Sent when a user is successfully deleted through the `/deleteUser/` endpoint.

Example payload is below:

```json
{
   "userId":"TOD1ACWHxnO9cnpq9XgNmFaRwXRMRu2taaFPpvYi6ng3uD2UN9",
   "clientId":"phjBdpKfpN",
   "eventType":"USER_DELETED",
   "webhookId":"dbaee13a-1184-4677-9741-b6845e60ee3a",
   "webhookSecret":"lgCQWztweEFcHvjdLhHc",
   "eventTimestamp":"2025-01-08T14:32:59.979214+00:00"
}
```

## 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:

```json
{
  "webhookId": "6594d898-7377-453b-989e-03545ebde8bf",
  "clientId": "kqLsLciREm",
  "eventTimestamp": "2022-05-31T11:17:17.804976+00:00",
  "userId": "TOD1ACWHxnO9cnpq9XgNmFaRwXRMRu2taaFPpvYi6ng3uD2UN9",
  "eventType": "CONNECTION_ATTEMPTED",
  "connectionAttemptedResult": "SUCCESS",
  "brokerageId": "c8153291-0761-43a8-8ed0-4642103c2cb8",
  "webhookSecret": "PajFGRsWwxgckkOZwteW"
}
```

Possible values for the `connectionAttemptedResult` field are:

*   `SUCCESS`
*   `AUTH_EXPIRED`
*   `INVALID_AUTH_CODE`
*   `AUTH_NOT_IN_PROGRESS`
*   `DIFFERENT_ACCOUNT`
*   `UNCAUGHT_ERROR`
*   `INVALID_CREDENTIALS`
*   `INVALID_MFA_CODE`
*   `NO_DATA`

## CONNECTION\_ADDED

Sent when a new brokerage connection is created using the SnapTrade connection portal.

Example payload is below.

```json
{
  "webhookId": "7467e5e4-4b59-4514-9d04-d05dbc10dea9",
  "clientId": "cJEQQgtDIb",
  "eventTimestamp": "2022-05-31T11:13:37.269120+00:00",
  "userId": "oWbLZ3OWsO3Nt8DKJnYxM32wLLD6h6SVj0ywkDmypUlkWkqsI5",
  "eventType": "CONNECTION_ADDED",
  "brokerageId": "c8153291-0761-43a8-8ed0-4642103c2cb8",
  "brokerageAuthorizationId": "a0231bd2-9b77-4732-ba78-f9c6af6a1a62",
  "webhookSecret": "ZSDkaLCRJndeZGgeaJnV"
}
```

## CONNECTION\_DELETED

Sent when a user deletes an existing connection using the SnapTrade connection portal.

Example payload is below:

```json
{
  "webhookId": "85a63984-6fb6-45fb-bc3b-18582a89ab3c",
  "clientId": "gxKWQEIXGS",
  "eventTimestamp": "2022-05-31T11:35:33.753881+00:00",
  "userId": "a053epCMsmII9goant5wdQH7CaT0M00HgQiRaxWnK0MncDUF2c",
  "eventType": "CONNECTION_DELETED",
  "brokerageId": "c8153291-0761-43a8-8ed0-4642103c2cb8",
  "brokerageAuthorizationId": "a0231bd2-9b77-4732-ba78-f9c6af6a1a62",
  "webhookSecret": "lbTycRhXwzVRJPovdNSN"
}
```

## 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](https://docs.snaptrade.com/docs/fix-broken-connections).

Example payload is below:

```json
{
  "webhookId": "434d23af-5f89-44f8-9415-5029557e64c7",
  "clientId": "yLNANjmwau",
  "eventTimestamp": "2022-05-31T11:37:50.590720+00:00",
  "userId": "DbjLqtPsj6G1MlDLtZaps4YhtckD0jWOuLQjl6GPW4FmpUa8eo",
  "eventType": "CONNECTION_BROKEN",
  "webhookSecret": "SCNsZYmJcuXRiznplDdL",
  "brokerageAuthorizationId": "dc90a844-367b-4120-8b56-21040bfa6525"
}
```

## CONNECTION\_FIXED

Sent when a broken (disabled) connection is fixed.

Example payload is below:

```json
{
  "webhookId": "ae73c055-2308-4de4-bf0e-d828bbac8c91",
  "clientId": "BVmifPEScc",
  "eventTimestamp": "2022-05-31T11:52:26.803063+00:00",
  "userId": "EKDO7W7YSWEZkOZUN5v1iMGF7ipEmOGjc6bMEm89y6vCrtnebH",
  "eventType": "CONNECTION_FIXED",
  "webhookSecret": "zNKlROkqSEXjXBxfTsAu",
  "brokerageAuthorizationId": "5e942e13-2b39-47b2-b524-858b9d9849ed"
}
```

## CONNECTION\_UPDATED

Sent when a brokerage connection is updated.

Example payload is below:

```json
{
  "webhookId": "57a293e4-f7eb-49ad-92e2-80ac16e01af7",
  "clientId": "IZtBuuKXEA",
  "eventTimestamp": "2022-05-31T11:53:09.437310+00:00",
  "userId": "sXQQcRdQEmGSMizpv45gBlBANBeCWgEn9uSmLlEpa1YsZ7oAW8",
  "eventType": "CONNECTION_UPDATED",
  "webhookSecret": "oHdlCBoExKtosDtTLVDc",
  "brokerageAuthorizationId": "7ed55083-7128-46ff-9317-43cd788f43ff"
}
```

## CONNECTION\_FAILED

Sent when a user’s attempt to connect to a brokerage has failed.

Example payload is below:

```json
{
  "webhookId": "57a293e4-f7eb-49ad-92e2-80ac16e01af7",
  "clientId": "IZtBuuKXEA",
  "eventTimestamp": "2022-05-31T11:53:09.437310+00:00",
  "userId": "sXQQcRdQEmGSMizpv45gBlBANBeCWgEn9uSmLlEpa1YsZ7oAW8",
  "eventType": "CONNECTION_FAILED",
  "webhookSecret": "oHdlCBoExKtosDtTLVDc"
}
```

## NEW\_ACCOUNT\_AVAILABLE

Sent when a new account is detected through a brokerage connection.

Example payload is below:

```json
{
  "webhookId": "6fe03e8c-201a-4c3f-92d0-acb825fc89cf",
  "clientId": "SjWLDemQUF",
  "eventTimestamp": "2022-05-31T12:37:18.630476+00:00",
  "userId": "hmznAjmLezKPaSnLIiNQweSGsf7MIlEvm9dcTao3gwjgBtulLK",
  "eventType": "NEW_ACCOUNT_AVAILABLE",
  "webhookSecret": "HZRnYEhLPgwyUXitkJBd",
  "accountId": "ec20134b-72e7-4e72-9b4c-9c7ddd1c479c",
  "brokerageAuthorizationId": "795de4e6-260e-45b3-815b-8f601c4cf448"
}
```

## 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:

```json
{
  "webhookId": "aa7a7d5a-4a12-40df-a329-feedc738eee9",
  "clientId": "rmCZBUGAXP",
  "eventTimestamp": "2022-05-31T12:39:47.182403+00:00",
  "userId": "7jN22b2AWHMOqOeujjaiU5kKOM8iITt11ENcUMftx6LBg9GTn2",
  "eventType": "ACCOUNT_TRANSACTIONS_INITIAL_UPDATE",
  "webhookSecret": "gQNuZIBwPYUVAsXvCfOA",
  "accountId": "d9f55a48-5dc6-4100-acee-24e5dfd8ec26",
  "brokerageAuthorizationId": "d28affae-f918-4ecd-8128-e5438e861706"
}
```

## 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:

```json
{
  "webhookId": "4eb13e1c-5778-44c2-968d-813d81cb73a1",
  "clientId": "JELZRJPqui",
  "eventTimestamp": "2022-05-31T12:40:05.944481+00:00",
  "userId": "4XTxw0mZhffRCdrgPHBG1VqdklkVJdEz5Uor0hq29hwPYlLS7s",
  "eventType": "ACCOUNT_TRANSACTIONS_UPDATED",
  "webhookSecret": "DDAVBuPPGgqwADXgbcOE",
  "accountId": "6e30be12-4ab0-4ec0-8d4d-0dfdce509e36",
  "brokerageAuthorizationId": "8105b33e-3dd6-4189-b93e-31ccaa9d3b69"
}
```

## ACCOUNT\_REMOVED

Sent when an account is removed from a connection.

Example payload is below:

```json
{
  "webhookId": "60b9ad5f-9e78-43b2-af5a-8ce8412b1cd6",
  "clientId": "PARTNERAPP",
  "eventTimestamp": "2024-05-31T12:40:05.944481+00:00",
  "userId": "4XTxw0mZhffRCdrgPHBG1VqdklkVJdEz5Uor0hq29hwPYlLS7s",
  "eventType": "ACCOUNT_REMOVED",
  "webhookSecret": "DDAVBuPPGgqwADXgbcOE",
  "accountId": "6e30be12-4ab0-4ec0-8d4d-0dfdce509e36",
  "brokerageAuthorizationId": "8105b33e-3dd6-4189-b93e-31ccaa9d3b69"
}
```

## 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:

```json
{
  "userId": "60b9ad5f-8e78-43b2-af5a-8ce8412b1cd6",
  "clientId": "PARTNERAPP",
  "accountId": "45a3g56a-eef6-4904-a68e-d3f90c3e07c5",
  "eventType": "ACCOUNT_HOLDINGS_UPDATED",
  "webhookId": "312a13f3-3929-46dd-a70f-ade6aaefc100",
  "brokerageId": "905872ac-a7b1-4031-a31f-790cba1bfc94",
  "webhookSecret": "yfqvPlWrTFILBcjCERPh",
  "eventTimestamp": "2024-03-01T14:38:13.111991+00:00",
  "brokerageAuthorizationId": "6bb1ahb0-b8c8-4b59-8bf9-7841d7a89c63",
  "details": {
    "total_value": { "success": true, "error": null },
    "positions": { "success": true, "error": null },
    "balances": { "success": true, "error": null },
    "orders": { "success": true, "error": null }
  }
}
```

## 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:

```json
{
    "userId": "60b9ad5f-8e78-43b2-af5a-8ce8412b1cd6",
    "details": {
        "orders": [
            {
                "legs": [
                    {
                        "action": "BUY_TO_OPEN",
                        "leg_id": null,
                        "status": "EXECUTED",
                        "instrument": {
                            "symbol": "AAPL  261218C00240000",
                            "figi_code": null,
                            "asset_type": "OPTION",
                            "description": "AAPL CALL 7/17 240",
                            "exchange_mic_code": "OPRA"
                        },
                        "total_quantity": "1.000000000",
                        "execution_price": "19.2000000000",
                        "filled_quantity": "1.000000000",
                        "canceled_quantity": null
                    }
                ],
                "status": "EXECUTED",
                "order_role": null,
                "order_type": "LIMIT",
                "stop_price": "0.0000000000",
                "limit_price": "19.2000000000",
                "time_placed": "2026-05-08T18:52:02.810000Z",
                "time_executed": "2026-05-08T18:52:02.848000Z",
                "time_in_force": "DAY",
                "trailing_stop": null,
                "price_currency": "USD",
                "execution_price": "19.2000000000",
                "brokerage_order_id": "124912433123",
                "brokerage_group_order_id": null
            }
        ]
    },
    "clientId": "PARTNERAPP",
    "webookId": "6963bc1c-8bca-4896-8faa-e84b82f8f7b2",
    "accountId": "ad4202bb-eaf7-4c7c-a176-e8406612b1ca",
    "eventType": "TRADE_DETECTION",
    "webhookId": "763bc1cd-8aca-4896-8faa-e84b82f8f7b2",
    "brokerageId": "02952334-1d52-436f-ba74-f3e9a98ea635",
    "webhookSecret": "yfqvPlWrTFILBcjCERPh",
    "eventTimestamp": "2026-05-08T18:52:08.123974+00:00",
    "brokerageAuthorizationId": "6bb1ahb1-b8c8-4b59-8bf9-7841d7a89c63"
}
```

## 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:

```json
{
    "userId": "60b9ad5f-8e78-43b2-af5a-8ce8412b1cd6",
    "details": {
        "orders": [
            {
                "legs": [
                    {
                        "action": "BUY_TO_OPEN",
                        "leg_id": null,
                        "status": "EXECUTED",
                        "instrument": {
                            "symbol": "AAPL  261218C00240000",
                            "figi_code": null,
                            "asset_type": "OPTION",
                            "description": "AAPL CALL 7/17 240",
                            "exchange_mic_code": "OPRA"
                        },
                        "total_quantity": "1.000000000",
                        "execution_price": "19.2000000000",
                        "filled_quantity": "1.000000000",
                        "canceled_quantity": null
                    }
                ],
                "status": "EXECUTED",
                "order_role": null,
                "order_type": "LIMIT",
                "stop_price": "0.0000000000",
                "limit_price": "19.2000000000",
                "time_placed": "2026-05-08T18:52:02.810000Z",
                "time_executed": "2026-05-08T18:52:02.848000Z",
                "time_in_force": "DAY",
                "trailing_stop": null,
                "price_currency": "USD",
                "execution_price": "19.2000000000",
                "brokerage_order_id": "124912433123",
                "brokerage_group_order_id": null
            }
        ]
    },
    "clientId": "PARTNERAPP",
    "webookId": "6963bc1c-8bca-4896-8faa-e84b82f8f7b2",
    "accountId": "ad4202bb-eaf7-4c7c-a176-e8406612b1ca",
    "eventType": "TRADE_UPDATE",
    "webhookId": "763bc1cd-8aca-4896-8faa-e84b82f8f7b2",
    "brokerageId": "02952334-1d52-436f-ba74-f3e9a98ea635",
    "webhookSecret": "yfqvPlWrTFILBcjCERPh",
    "eventTimestamp": "2026-05-08T18:52:08.123974+00:00",
    "brokerageAuthorizationId": "6bb1ahb1-b8c8-4b59-8bf9-7841d7a89c63"
}
```
