Skip to main content

Webhook Events

Webhooks allow your system to automatically receive real-time updates from Bullring Finance when specific events occur. You can register a webhook URL to listen for event notifications and trigger your own workflows. Bullring Finance can notify your server in real time about changes to subaccount status, payment confirmations, and withdrawal progress. To start receiving webhooks, register your endpoint via your Dashboard or the Webhook API.

How It Works

When an event occurs, Bullring Finance sends an HTTP POST request to your registered URL with a JSON payload describing the event. Your endpoint must return a 2xx status code to acknowledge receipt. All webhook requests are signed using a shared secret. You should verify the request signature before processing.

Verifying signature

All webhook requests from Bullring will include a header key X-BULLRING-SIGNATURE, which contains the timestamp and a signed hash string in the format:
X-BULLRING-SIGNATURE: t={timestamp},v1={hash_string}
A sample example of a header would be:
{
  ...
  X-BULLRING-SIGNATURE: "t=1492774577,v1=ansdoj213e98jqd928u3eudh239eu2j9d2jd8ejd238eu23ei2d9j23e8u23eue3,",
  ...
}
To verify the signature:
  • Get the value of the X-BULLRING-SIGNATURE header.
  • Split the string by ,.
  • Then, split each part by =.
  • Extract the value at index 1 from each.
From the example above, you would get:
timestamp     // 1492774577
hashString    // ansdoj213e98jqd928u3eudh239eu2j9d2jd8ejd238eu23ei2d9j23e8u23eue3

To verify the signature, create a hash using:
  • The extracted timestamp
  • The request payload (body)
  • The secret (available on your Bullring dashboard after creating a webhook)
Then compare the generated hash with the received hash with equality ==. Since Bullring uses the SHA256 hashing algorithm with base64 encoding, here’s how to verify the signature using Node.js:
Verification Example

import { createHmac } from "crypto";

const receivedHash = hashString;
const payload = JSON.stringify({ ... }); // The request body
const key = "your_secret_key"; // From dashboard

const hash = createHmac("sha256", key)
  .update(`${timestamp},${payload}`)
  .digest("base64");

const isVerified = receivedHash === hash;

Take note of the , between the timestamp and the payload ${timestamp},${payload} when creating the hash.

Subaccount Events

  • subaccount.status.created
  • subaccount.status.approved
  • subaccount.status.declined
  • subaccount.status.review
These events are triggered when a new store (subaccount) is created or goes through KYB review stages.

Payment Events

  • payment.status.paid
  • payment.status.unpaid
These events notify you when an invoice is paid or expires without being paid.

Withdrawal Events

  • withdrawal.status.not_initiated
  • withdrawal.status.in_progress
  • withdrawal.status.completed
  • withdrawal.status.failed
These events allow you to track the status of your store’s fiat payouts.

Webhook Payload Format

Each webhook will be sent as a POST request to your registered endpoint with the following structure:
{
  "event": "event.name",
  "data": {
    "...": "event-specific payload"
  }
}

Example Payloads

Subaccount Event: subaccount.status.created

{
  "event": "subaccount.status.created",
  "data": {
    "id": "47a79c3d-9a2b-4998-898b-8eb7fad01526",
    "email": "[email protected]",
    "status": "created"
  }
}

Subaccount Event: subaccount.status.approved

{
  "event": "subaccount.status.approved",
  "data": {
    "id": "47a79c3d-9a2b-4998-898b-8eb7fad01526",
    "status": "approved"
  }
}

Payment Event: payment.status.paid

{
  "event": "payment.status.paid",
  "data": {
    "id": "06506c54-8df0-4d5f-bdc0-50dbe29fb84e",
    "amount": 1000,
    "currency": "brl",
    "status": "paid"
  }
}

Payment Event: payment.status.unpaid

{
  "event": "payment.status.unpaid",
  "data": {
    "id": "06506c54-8df0-4d5f-bdc0-50dbe29fb84e",
    "amount": 1000,
    "currency": "brl",
    "status": "unpaid"
  }
}

Withdrawal Event: withdrawal.status.completed

{
  "event": "withdrawal.status.completed",
  "data": {
    "id": "449eca18-9c77-472c-9e82-b08a18637759",
    "status": "completed",
    "amount": "496.50",
    "currency": "BRL"
  }
}
Make sure your server is prepared to respond with a 2xx status code to acknowledge receipt.