Create Webhooks

Overview

You can use webhooks to receive real-time HTTP callbacks when specific events occur. This enables you to automate workflows, notify your end-users, and keep your platform in sync with BitGo.

As a CaaS provider, you operate at the Organization level and manage numerous child Enterprises (your end-users). This guide covers the two scopes of webhooks you can integrate:

  • Organization Webhooks - Notifications for events that apply to your entire CaaS organization such as organization-level policy changes or new bank accounts.
  • Enterprise Webhooks - Notifications for events specific to one of your end-users such as an end-user's KYC status change or a new bank account added to an end-user's enterprise.

BitGo signs all webhook notifications, enabling you to verify their authenticity.

  • Webhook Secret - When you create a webhook, you can set a secret. BitGo uses this secret to generate an HMAC-SHA256 signature.
  • Signature Header - The signature is sent in the x-signature-sha256 HTTP header.
  • Verification - To verify the payload, compute your own HMAC-SHA256 signature of the raw request body using your secret and compare it to the signature in the header. This is a critical security step.

Enterprise Webhook Types

TypeTriggers When
accessTokenAn access token is made.
bankAccountA bank account is added.

Organization Webhook Types

TypeTriggers When
bankAccountA bank account is added.
enterpriseKycStateEnterprise KYC status changes.
identityStatusA user identity status changes.
pendingapprovalA transaction request is pending approval.
transferA transfer succeeds or fails.
txRequestA transaction request changes status.
txRequestTransactionA transaction request transaction state changes.
userKycStateA user's KYC verification status changes.

Prerequisites

1. Create Organization Webhooks

The following example creates a webhook that notifies you whenever a bank account is added.

Endpoint: Create Organization Webhook

export ORGANIZATION_ID="<YOUR_ORGANIZATION_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export URL="<YOUR_WEBHOOK_URL>"
export LABEL="<YOUR_WEBHOOK_NAME>"

curl -X POST \
  https://app.bitgo-test.com/api/v2/organization/$ORGANIZATION_ID/webhook \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "type": "bankAccount",
    "url": "'"$URL"'",
    "label": "'"$LABEL"'"
  }'
Step Result
{
  "success": "true",
  "data": {
    "id": "690278897328c7fc41e81887d4b76964",
    "label": "test org webhook 1",
    "created": "2025-10-29T20:26:49.286Z",
    "scope" : "organization",
    "organizationId" : "68f6af3f2f29893d5863bc0eaf77b3c6",
    "type": "bankAccount",
    "url": "https://webhook.site/2635918b-018b-4179-82e9-6940a0b851e0",
    "version": 1,
    "state": "active",
    "successiveFailedAttempts": "0",
    "listenToFailureStates": "false",
    "txRequestStates": [],
    "txRequestTransactionStates": []
}

2. (Optional) Create Webhook Secret

Create a webhook secret that enables you to verify webhook notifications. BitGo uses this secret to generate HMAC-SHA256 signatures for all webhook payloads, enabling you to verify that notifications genuinely originate from BitGo and haven't been tampered with.

Important: Store your webhook secret securely. If compromised, rotate it immediately by calling this endpoint again. You can rotate secrets once every 5 minutes.

Note: You must create your webhook secret before performing the action that triggers the notification. Otherwise, the notification won't include a signature and you won't be able to verify it.

Endpoint: Create Webhook Secret

export ORGANIZATION_ID="<YOUR_ORGANIZATION_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"

curl -X POST \
  https://app.bitgo-test.com/api/v2/webhook/secret \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "organizationId": "'"$ORGANIZATION_ID"'"
  }'
Step Result
{
  "secret": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
}

Save this secret securely—you'll need it to verify incoming webhook notifications using the x-signature-sha256 header.

3. (Optional) Verify Webhook Notification

When BitGo sends a webhook notification, it includes an x-signature-sha256 header containing an HMAC-SHA256 signature. You can verify the notification is legitimate by passing the signature from this header along with the payload you received.

Endpoint: Verify Webhook Notification

export WEBHOOK_ID="<YOUR_WEBHOOK_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export SIGNATURE="<X_SIGNATURE_SHA256_HEADER_VALUE>" # Value from the x-signature-sha256 header in the webhook notification
export PAYLOAD="<YOUR_PAYLOAD>" # JSON payload as a string from the webhook notification body

curl -X POST "https://app.bitgo-test.com/api/v2/webhook/$WEBHOOK_ID/verify" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "signature": "'"$SIGNATURE"'",
    "notificationPayload": "'"$PAYLOAD"'"
  }'
Step Result
{
  "webhookId": "wh119ecd15a4adf811f8f552fde21b9d819b4dc9a7f04c51513395816703c73511",
  "isValid": true
}

4. (Optional) Simulate Organization Webhook

You can simulate your webhook with real data from the prior step or with placeholder data (also known as dummy data).

Endpoint: Simulate Organization Webhook

export ORGANIZATION_ID="<YOUR_ORGANIZATION_ID>"
export WEBHOOK_ID="<YOUR_WEBHOOK_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export ACCESS_TOKEN_ID="<YOUR_ACCESS_TOKEN_ID>"

curl -X POST "https://app.bitgo-test.com/api/v2/organization/$ORGANIZATION_ID/webhook/$WEBHOOK_ID/simulate" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
  -d '{
  "bankAccountId": "string"
}'
Step Result
{
  "webhookNotifications": [
    {
      "id": "59cd72485007a239fb00282ed480da1f",
      "accessToken": "txRequest",
      "url": "https://webhook.site/f74addc1-c40a-4fce-879a-2d92b8d491c5",
      "hash": "db924f4cf2347ac5a6b464d3e8dc4a20cffc117eb29f4460645fbc34de171bfb",
      "simulation": true,
      "retries": 0,
      "webhook": "68531e154d28af627819bd3e183a930e",
      "updatedAt": "2025-06-19T20:48:43.525Z",
      "version": 1,
      "allowBlockedHosts": true,
      "payload": "string",
      "response": {
        "code": 0,
        "type": "string",
        "body": "string",
        "error": "string"
      }
    }
  ]
}

See Also