Webhooks

How do I use Webhooks?

Overview

Receive real-time notifications about events in your organization using Cybrid's webhook system. Manage webhook subscriptions in the Partner Portal, and retrieve events and deliveries 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

Webhook events and deliveries are in the Organization API, not the Bank API. Use Organization API endpoints to retrieve them.

Configure Authentication Scopes

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

Required Scopes:

ScopeDescription
subscription_events:readView webhook events
subscription_events:executeFull event management
⚠️

Subscription management requires the Partner Portal

API-key tokens cannot hold the subscriptions:read, subscriptions:write or subscriptions:execute scopes. If you request them, they are dropped and the token is issued with the remaining scopes. Calls to /api/subscriptions made with an API-key token return 403 with message_code: invalid_scopes. Create, view and delete subscriptions in the Partner Portal instead. Existing subscriptions are unaffected and continue to deliver events.

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": "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 the Partner Portal 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).

Events for bank operations:

Event TypeDescriptionUse Case
bank.createdCreatedNew bank
bank.updatedUpdatedConfiguration change

To capture error events, listen for trade.failed, transfer.failed, plan.failed, execution.failed, external_bank_account.failed, external_bank_account.expired, 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 message_code: invalid_https_url. 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 within 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 in the Partner Portal so Cybrid knows where to deliver events. Users with the Key Admin or Founder role can manage subscriptions.

  1. Sign in to the Partner Portal and select a bank in the environment (sandbox or production) you want to receive events for.
  2. Open the Developers tab, then select Subscriptions.
  3. Click Create Webhook Subscription.
  4. Enter a descriptive name (max 128 characters) and your endpoint URL, then submit the form.

A subscription created in the portal receives every event type.

⚠️

Save the signing key immediately

After the subscription is created, the portal displays its signing key once. Copy it and store it securely; you need it to verify webhook signatures. 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.

Delete your webhook subscription

Delete a subscription from Developers → Subscriptions in the Partner Portal. Deletion is scheduled rather than immediate: the subscription's scheduled_deletion_at is set to 24 hours from the request, and a subscription.deleting event is created. Deliveries continue until that time; the subscription is then removed and a subscription.deleted event is created.

A subscription with protection_enabled: true cannot be deleted, and the Partner Portal offers no delete action for it. The field is read-only; contact [email protected] to protect a subscription or remove its protection.

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": "subscription_event_guid",
  "organization_guid": "organization_guid",
  "bank_guid": "bank_guid",
  "event_type": "transfer.storing",
  "object_guid": "object_guid",
  "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');
        const requestBuffer = Buffer.from(requestSignature || '', 'utf8');
        const expectedBuffer = Buffer.from(expectedSignature, 'utf8');
        const valid_request = requestBuffer.length === expectedBuffer.length
          && crypto.timingSafeEqual(requestBuffer, expectedBuffer);
        // 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 subscription in the Partner Portal.

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
invalid_https_urlURL uses HTTP instead of HTTPS in productionUse HTTPS for production; HTTP allowed for sandbox only
unknown_organizationOrganization GUID doesn't exist or is invalidVerify your organization GUID and authentication
not_implementedWebhook 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 with message_code: invalid_scopes
  • Cause: Token lacks the scope for the endpoint, or an API-key token was used to call /api/subscriptions
  • Fix: Request a token with the required subscription_events scopes; manage subscriptions in the Partner Portal

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. The previous registration keeps delivering for 24 hours after the delete request, so the same event is delivered to both endpoints in the meantime; your webhook handler should handle duplicate events.

Testing

Use Sandbox Environment

  • Create test subscriptions with a sandbox bank selected in the Partner Portal
  • 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

The subscription endpoints back the Partner Portal and cannot be called with an API-key token. Manage subscriptions in the Partner Portal.

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

  • Manage Subscriptions in the Partner Portal - API-key tokens cannot create, view or delete subscriptions
  • Use Organization API - Webhook events and deliveries are in the Organization API, not the Bank API
  • 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
  • Deletion is Delayed - Deleting a subscription schedules removal 24 hours out; subscribe to subscription.storing, subscription.failed, subscription.deleting and subscription.deleted to be told when a subscription is created, fails or is deleted

Did this page help you?