Webhooks

How do I use Webhooks?

Overview

Receive real-time notifications about events in your organization using Cybrid's webhook system. Manage webhooks through the Organization API.

Configure your applications to receive events as they occur. Your back-end systems can then execute actions based on these events.

To enable webhook events, register one or more webhook endpoints. Cybrid posts JSON payloads containing a SubscriptionEvent object to your endpoints via HTTPS.

Webhooks notify you of asynchronous events: completed trades, settled transfers, and finished identity verifications.

Authenticate with the Organization API

Use the Organization API

All webhook operations are in the Organization API, not the Bank API. Use Organization API endpoints for webhook operations.

Configure Authentication Scopes

Interact with webhook subscriptions using an OAuth2 access token with organization-level scopes.

Required Scopes:

ScopeDescription
subscriptions:readList and retrieve subscription details
subscriptions:writeCreate subscriptions
subscriptions:executeFull subscription management
subscription_events:readView webhook events
subscription_events:executeFull event management

Get an Organization Token

Create an Organization Application in the IDP service, then request an OAuth2 token using client credentials flow:

POST https://id.{environment}.cybrid.app/oauth/token
Content-Type: application/json

{
  "grant_type": "client_credentials",
  "client_id": "client_id",
  "client_secret": "client_secret",
  "scope": "subscriptions:execute subscription_events:read"
}
{
  "access_token": "access_token",
  "token_type": "Bearer",
  "expires_in": 3600
}

Use the access token in all Organization API requests:

Authorization: Bearer {access_token}
⚠️

Common Authentication Errors

  • 401 Unauthorized: Invalid or expired token
  • 403 Forbidden: Token lacks required scopes
  • Using a bank token instead of organization token will not work

Subscribe to Webhook Events

Cybrid sends event data when activity occurs in your account. Each event creates a new SubscriptionEvent object. A single API request may create multiple events. For example, creating a new transfer generates transfer.storing, transfer.reviewing and transfer.completed events.

Register webhook endpoints in your organization to enable automatic delivery of SubscriptionEvent objects via POST requests. Your app can then run back-end actions (for example, informing the customer a trade has been settled).

Trade Events

Events related to cryptocurrency trading operations:

Event TypeDescriptionUse Case
trade.storingCreating tradeInitial state tracking
trade.pendingAwaiting executionMonitor pending trades
trade.settlingSettlingTrack settlement progress
trade.completedCompletedFinal confirmation
trade.failedFailedHandle failures
trade.cancelledCancelledTrack cancellations

Transfer Events

Events for money movement operations:

Event TypeDescriptionUse Case
transfer.storingCreating transferInitial state tracking
transfer.pendingPendingMonitor pending transfers
transfer.holdingOn holdTrack held transfers
transfer.reviewingUnder reviewCompliance workflows
transfer.completedCompletedFinal confirmation
transfer.failedFailedHandle failures

Identity Verification Events

Events for customer KYC/identity verification:

Event TypeDescriptionUse Case
identity_verification.storingCreating verificationInitial tracking
identity_verification.pendingIn progressMonitor progress
identity_verification.reviewingRequires manual reviewCompliance workflows
identity_verification.waitingAwaiting customer actionCustomer notifications
identity_verification.expiredExpiredRe-verification needed
identity_verification.completedCompletedCustomer onboarding

To capture error events, listen for trade.failed, transfer.failed and/or identity_verification.expired.

Create a Webhook Subscription

Set up your webhook endpoint

Create a webhook endpoint handler to receive event data POST requests.

Set up an HTTPS endpoint function that accepts webhook requests with a POST method. For sandbox setup you can use an HTTP endpoint.

⚠️

Production webhook endpoints must use HTTPS

For production environments, your webhook URL MUST use HTTPS. HTTP URLs will be rejected with an InvalidHttpsUrlException. For sandbox/testing, HTTP URLs are allowed for local development.

Configure your endpoint function to handle POST requests with a JSON payload consisting of a SubscriptionEvent object.

Return a successful status code (2xx) quickly, before any complex logic that could cause a timeout. For example, you must return a 200 response before updating a transfer in your system.

⚠️

Endpoints must return a 2xx prior to any complex logic

Your endpoint must respond with 2 seconds to avoid timeouts.

ℹ️

Registered webhook endpoints must be publicly accessible URLs

You can register up to 5 webhook endpoints per organization.

Register your webhook endpoint

After testing your webhook endpoint function, register the endpoint's accessible URL using the Subscription API so Cybrid knows where to deliver events.

Endpoint: POST /api/subscriptions

Request Parameters:

FieldTypeRequiredDescription
typestringYesMust be webhook (only supported type)
namestringYesDescriptive name (max 128 characters)
urlstringYesYour HTTPS endpoint URL
environmentstringYesproduction or sandbox

Request Example:

POST /api/subscriptions
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "name": "Production Trade Notifications",
  "type": "webhook",
  "url": "https://your-domain.com/webhooks/cybrid",
  "environment": "production"
}
{
  "guid": "subscription_guid",
  "organization_guid": "organization_guid",
  "name": "Production Trade Notifications",
  "url": "https://your-domain.com/webhooks/cybrid",
  "environment": "production",
  "type": "webhook",
  "state": "storing",
  "signing_key": "c3ac436b171923eb688a6bf364f89effd90427f4d58018e25558af3ecd506bc5",
  "created_at": "2026-01-24T12:00:00Z"
}
⚠️

Save the signing_key immediately

Note the signing_key as it is only returned once and is needed to secure your webhook endpoint. The signing key is generated server-side (64-character hex string) and encrypted before storage. You cannot retrieve it later.

ℹ️

Subscription activation

A subscription becomes active only after the endpoint is available and responds to requests.

Understand the Webhook Payload

When an event occurs, Cybrid sends an HTTP POST to your webhook URL.

Headers

Content-Type: application/json
X-Cybrid-Signature: {hmac_signature}

SubscriptionEvent Object

The following is an example of a SubscriptionEvent:

{
  "guid": "cf16b78e233464229c7eda5e979b25a8",
  "organization_guid": "ad9007e80f35ac6d343d43496cad2744",
  "bank_guid": "b3a9d531c26892954661735e1b7482b7",
  "event_type": "transfer.storing",
  "object_guid": "8f5ee194e5aa254feef3054bd9543939",
  "environment": "sandbox"
}

Payload Fields

FieldDescription
guidUnique ID for this webhook event. Can also be used as an idempotency key.
organization_guidUnique identifier for the organization that owns the object. This allows the same webhook endpoint to be registered with multiple organizations.
bank_guidUnique identifier for the bank that owns the object associated with the event.
event_typeA tuple separated by . containing the type of the object (ie: trade, transfer or identity_verification) and the type of event generated by the Cybrid platform.
object_guidIdentifier for the object generating the event.
environmentproduction or sandbox. You may receive both sandbox and production mode event delivery requests to your endpoints.

Verify Webhook Signatures

Every webhook payload includes an X-Cybrid-Signature header containing an HMAC signature. You MUST verify this signature to ensure the webhook came from Cybrid.

When Cybrid sends data to registered webhook endpoints, the payload is authenticated with a hash-based message authentication code (HMAC). The key used to create the HMAC is the signing_key provided on subscription registration (see section above). Verify it by running the algorithm yourself with the payload and the key to re-create the HMAC. The HMAC is created with the HMAC_SHA256 algorithm, then encoded in hex. The HMAC is attached to the request in the X-Cybrid-Signature header.

Signature Verification Examples

const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');
const PORT = 3333;
// replace SIGNING_KEY with the key received when the webhook endpoint was created
const SIGNING_KEY = "c3ac436b171923eb688a6bf364f89effd90427f4d58018e25558af3ecd506bc5"
const ALGORITHM = 'sha256'
const SIGNATURE_HEADER = 'X-Cybrid-Signature'
const app = express();
app.use(bodyParser.json({
    verify: (req, res, buf) => {
      req.rawBody = buf;
    }
  }));
app.use(express.json());
app.post('/webhook', async (req, res) => {
    try {
        const requestSignature = req.get(SIGNATURE_HEADER);
        const expectedSignature = crypto.createHmac(ALGORITHM, SIGNING_KEY)
          .update(req.rawBody)
          .digest('hex');
        let valid_request = requestSignature === expectedSignature
        // respond with 200
        if (valid_request) {
            res.status(200).send("OK");
            console.log(`received payload ${req.body}`)
            // perform actions related to the event
        } else {
            res.status(403).send("INVALID SIGNATURE");
        }
    } catch (err) {
        res.status(500).send(err.toString());
    }
})
app.listen(PORT, () => {
    console.log(`Server running on ${PORT}`);
});
⚠️

Security Best Practice

Always use constant-time comparison when verifying signatures to prevent timing attacks.

Webhook Delivery and Retry Logic

Delivery Workflow

  1. Event Occurs - A trade completes, transfer succeeds, etc.
  2. Event Created - SubscriptionEvent record created
  3. Delivery Attempted - HTTP POST sent to your webhook URL
  4. Retry Logic - If delivery fails, exponential backoff retry

Retry Strategy

Cybrid attempts to deliver a given event to your webhook endpoint for up to 7 days with an exponential backoff.

AttemptDelayTotal Time
1Immediate0s
22x base delay~2s
34x base delay~6s
48x base delay~14s
5+Capped at max delayUp to 7 days

Configuration:

  • Base delay: Exponential starting point (e.g., 2 seconds)
  • Max delay: Cap on exponential growth (prevents extremely long delays)
  • Timeout: ~7 days total retry period
  • Request timeout: 10 seconds per HTTP request

Use the SubscriptionDelivery API to view failing deliveries and when the next attempt will occur. If your endpoint subscription has been disabled or deleted when Cybrid attempts a retry, future retries of that event are prevented. If a delivery has failed, trigger a new retry cycle by creating a new delivery using SubscriptionDelivery API.

Delivery States

StateDescription
storingInitial state, delivery queued
failingRetries in progress (after ~10s of failures)
completedSuccessfully delivered (2xx response)
failedAll retries exhausted or subscription deleted

Success Criteria

A webhook delivery is considered successful if your endpoint returns:

  • HTTP 2xx status code (200, 201, 202, 204, etc.)

Any other response (4xx, 5xx, timeout, network error) triggers retry logic.

Disable Behaviour

If your endpoint has not returned a successful response for multiple days (approximately 7 days), Cybrid will automatically mark the endpoint as failed and skip future deliveries. To re-enable the endpoint, create a new registration using the Subscription API.

Subscription State Tracking:

  • deliveries_failing_since: Set on first delivery failure, cleared when delivery succeeds
  • If failing for 7 days, subscription moves to failed state

Common Errors and Troubleshooting

Subscription Creation Errors

ErrorCauseFix
InvalidHttpsUrlExceptionURL uses HTTP instead of HTTPS in productionUse HTTPS for production; HTTP allowed for sandbox only
InvalidOrganizationOrganization GUID doesn't exist or is invalidVerify your organization GUID and authentication
ApiNotImplementedWebhook API not enabled for your organizationContact Cybrid to enable webhook access

Delivery Failures

Timeout Errors (timeout)

  • Cause: Your endpoint didn't respond within 10 seconds
  • Fix:
    • Optimize endpoint performance
    • Return 2xx immediately, process async
    • Check for network issues

Unreachable Errors (unreachable)

  • Cause: DNS resolution failed, connection refused, network error
  • Fix:
    • Verify URL is accessible from internet
    • Check firewall rules
    • Validate DNS configuration

Failed After Retries

  • Cause: Repeated failures for ~7 days
  • Result: Subscription marked as failed, deliveries stop
  • Fix:
    • Resolve endpoint issues
    • Delete failed subscription
    • Create new subscription

Authentication Errors

Wrong Token Type

  • Symptom: 403 Forbidden
  • Cause: Using bank token instead of organization token
  • Fix: Get organization-scoped OAuth token

Missing Scopes

  • Symptom: 403 Forbidden
  • Cause: Token lacks subscriptions:execute scope
  • Fix: Request token with proper scopes

Follow Best Practices

Endpoint Implementation

Return 2xx Quickly

@app.route('/webhooks/cybrid', methods=['POST'])
def webhook_handler():
    # Verify signature first
    if not verify_signature(request):
        return '', 401

    # Queue for async processing
    queue.enqueue(process_webhook, request.json)

    # Return success immediately
    return '', 200

Implement Idempotency

  • Store guid from webhook payload
  • Skip processing duplicate events
  • Use database unique constraints

Verify Signatures

  • Always validate X-Cybrid-Signature header
  • Reject webhooks with invalid signatures
  • Use constant-time comparison

Event Ordering

Cybrid may deliver events out of order from their generation sequence. For example, creating a transfer generates the following events:

  • transfer.storing
  • transfer.reviewing
  • transfer.completed

Your endpoint shouldn't expect delivery of these events in this order, and needs to handle delivery accordingly. You can also use the API to fetch the updated object and react according to the latest state of the object.

Handle Duplicate Events

Webhook endpoints may occasionally receive the same event more than once. Log processed event GUIDs and skip events you've already processed.

Handle Events Asynchronously

Configure your handler to process incoming events with an asynchronous queue. Processing events synchronously may cause scalability issues. Any large spike in webhook deliveries may overwhelm your endpoint hosts. Asynchronous queues allow you to process the concurrent events at a rate your system can support.

Monitoring and Alerting

Track Delivery States

  • Monitor subscription_deliveries via API
  • Alert on failing or failed states

Monitor Endpoint Health

  • Log all webhook requests
  • Track response times
  • Alert on elevated error rates

Set Up Alerts

  • Subscription enters failed state
  • deliveries_failing_since is set for > 1 hour
  • High delivery failure rates

Roll Endpoint Signing Secrets Periodically

To keep your webhook handler safe, we recommend that you roll secrets periodically, or when you suspect a compromised secret. The process of rolling a webhook signing secret consists of creating a new registration (with a new signing key), updating your webhook handler and deleting the previous registration. This process may lead to the same event being delivered multiple times but your webhook handler should handle duplicate events.

Testing

Use Sandbox Environment

  • Test with environment: sandbox
  • HTTP URLs allowed for local testing

Verify Retry Logic

  • Temporarily break your endpoint
  • Observe retry behavior
  • Fix endpoint and confirm recovery

Test Event Types

  • Create test trades, transfers, verifications
  • Confirm correct event types received
  • Validate payload structure

API Reference Summary

Subscription Endpoints

MethodEndpointDescription
POST/api/subscriptionsCreate subscription
GET/api/subscriptionsList subscriptions
GET/api/subscriptions/{guid}Get subscription
DELETE/api/subscriptions/{guid}Delete subscription

Event Endpoints

MethodEndpointDescription
GET/api/subscription_eventsList events
GET/api/subscription_events/{guid}Get event

Delivery Endpoints

MethodEndpointDescription
GET/api/subscription_deliveriesList deliveries
GET/api/subscription_deliveries/{guid}Get delivery

Key Takeaways

  • Use Organization API - Webhooks are in the Organization API, not the Bank API
  • Get Organization Token - Use OAuth2 with subscriptions:execute scope
  • HTTPS Required - Production webhooks must use HTTPS URLs
  • Verify Signatures - Always validate X-Cybrid-Signature header with HMAC-SHA256
  • Handle Retries - Return 2xx quickly; process async to avoid timeouts
  • Monitor Health - Watch for failing states and deliveries_failing_since timestamps
  • Be Idempotent - Use guid to handle duplicate deliveries
  • Save Signing Key - The signing key is only returned once during subscription creation; store it securely