This section of the documentation would help you learn how to listen to events when actions occur on your profile
What are webhooks?
As a bank, various activities are constantly happening by the second across many accounts and whenever these actions occur on your profile, we trigger events which your application can listen for.
This is where webhooks come in. A webhook is a URL on your server where we send a JSON payload over HTTP as such events occur. For example, if you implement webhooks, once a payment is completed -- whether successful or failed -- we will immediately notify your server with a payable.completed
event.
Webhooks on Brass
Webhooks are broken into scopes -- scopes define the domain within which an action is happening and scopes contain events.
Let's look at an example, we have the payable
scope which describes activities involving Instant Payments and it has the completed
event which indicates that all actions on the resource are done (nothing else will happen on the Payment) -- when the webhook is fired, it will come with event name payable.completed
.
Enabling webhook notifications
Webhooks are not enabled by default for users. Visit the Update Customer Preferences section of the documentation to see how to enable or disable webhooks.
Supported Events
Below, you'll find the most important scoped events in Brass
Scope | Description |
---|---|
account.created | An account has just been created on your customer profile. This is important when dealing with subaccounts |
account.credited | One of your accounts has just been credited with funds |
account.debited | One of your accounts has just been debited for funds |
payable.auth_concluded | Authorisations have been concluded on the payment. This means it has entered hold-off or has been declined |
payable.completed | Processing for the payment has concluded -- it can either be successful or failed. |
payable.created | A payment has been created on your customer profile |
payable.hold_off | A payment has gone into hold-off mode -- this is the grace period within which it can be cancelled after it has been successfully approved |
payment_request.created | An invoice has been created on your customer profile |
payment_request.status_updated | The status of the invoice has been update (for instance, it was marked as paid) |
Verifying webhook events
It is very important to always verify that events that reach your webhook endpoint actually originated from Brass. You can do so by validating the signature of the webhook.
Brass webhook events are sent with an added header called
X-Brass-Signature
which is theHMAC SHA256
signature of the event payload signed using your webhook secret key.
var crypto = require('crypto');
// webhook secret key set on your profile
var secret = process.env.SECRET_KEY;
// using Express
app.post("/my/webhook/url", function(req, res) {
// retrieve the request's body
const event = req.body;
// validate event
const hash = crypto.createHmac('sha256', secret)
.update(JSON.stringify(event))
.digest('hex');
// return appropriate response if the request hash does not match the header
if (hash != req.headers['X-Brass-Signature']) {
return res.status(401).json({message: "Unauthorized request."});
}
// do something with event
res.send(200);
});
// using Slim 4 framework
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
$app->post("/my/webhook/url", function (Request $request, Response $response) {
// calculate the hash of the request
$calculatedHash = hash_hmac(
'SHA256',
$request->getBody()->getContents(),
$_ENV('BRASS_WEBHOOK_SECRET_KEY')
);
$response->withHeader('Content-Type', 'application/json');
// return appropriate response if calculated hash is not equal to header
if ($calculatedHash !== $request->getHeader('X-Brass-Signature')) {
$data = ['message' => "Unauthorized request"];
$response->getBody()->write(json_encode($data));
return $response->withStatus(401);
}
// do something with the request body
return $response->withStatus(200);
});
Responding to webhook events
You should respond to an event with a 200 OK
. We consider this an acknowledgement by your application. If your application responds with any status outside of the 2xx
range, we will consider it unacknowledged and thus retry 3 more times.
You don't need to send a request body or some other parameter as it would be discarded - we only pay attention to the status code.
If your application is likely to start a long running task in response to the event, Brass may timeout waiting for the response and would ultimately consider the event unacknowledged and queue to be raised later. You can mitigate this by having your application respond immediately with a
200
before it goes on to perform the rest of the task in the background
Webhook Samples
{
"event": "payable.completed",
"data": {
"embeds": [
"authorizations",
"attachments",
"customer",
"counterparties",
"initiated_by",
"payment_request",
"recurring_payment_mandate",
"source_account",
"tags"
],
"id": "pay_4JbTQ4dQZZGSzx3xwH4B1u",
"customer_reference": null,
"title": "We're paying up to the kobo",
"description": "The level of accuracy with this payment is very refreshing",
"currency": "NGN",
"amount": {
"raw": "1567025",
"formatted": "15670.25",
"formatted_display": "₦15,670.25"
},
"fee": {
"raw": "0",
"formatted": "0.00",
"formatted_display": "₦0.00"
},
"status": "success",
"hold_off_ends_at": "2021-06-03T16:04:23+01:00",
"scheduled_for": null,
"paid_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2021-06-02T08:56:45+01:00",
"updated_at": "2021-06-03T16:10:02+01:00",
"display_status": "Success",
"is_recurring": false,
"recurring_payment_mandate_id": null,
"counterparties_count": null,
"failure_reason": null,
"authorizations": {
"data": [
{
"embeds": [
"authorizable",
"customer",
"user"
],
"id": "aut_uYXa1rQGu3xR54VDKRqE0",
"is_required": true,
"status": "approved",
"comment": "Approve",
"responded_at": "2021-06-03T15:04:21+01:00",
"created_at": "2021-06-02T08:56:45+01:00",
"updated_at": "2021-06-03T15:04:21+01:00",
"authorizable_type": "payment",
"user": {
"data": {
"embeds": [
"addresses",
"customers",
"role",
"partner",
"agent"
],
"id": "usr_vwrVsHrHwrynXO9sMrdpx",
"firstname": "Octonauts",
"lastname": "Assemble",
"phone": "+2348123320811",
"password": null,
"email": "[email protected]",
"remember_token": null,
"gender": null,
"photo_url": "https://s3.eu-west-1.amazonaws.com/sandbox-private.getbrass.co/cus_1Gdl3cC14y6nUOoxOPhPcz/bb693e28-a393-48cc-9792-9a3fc1d39297.jpeg",
"email_verified": false,
"email_verified_at": null,
"disabled_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-08-23T11:55:07+01:00",
"updated_at": "2021-05-05T14:15:01+01:00",
"preferences": {
"security": {
"enabled_2fa": true,
"otp_channel": "email"
}
},
"role": {
"data": {
"embeds": [
"permissions"
],
"id": "rol_7h8f07GPpE17GGgrBA2vmN",
"name": "business.owner",
"display_name": "Owner",
"is_active": true,
"is_restricted": true,
"created_at": "2020-03-23T22:41:20+01:00",
"updated_at": "2020-03-23T22:41:20+01:00",
"abilities": {
"dashboard": {
"section": "Dashboard",
"abilities": [
"Full access to all dashboard services",
"Read cash flow, cash-in and cash-out"
],
"lacks_abilities": [],
"is_important": false
},
"account": {
"section": "Account",
"abilities": [
"Access to company accounts",
"Download company statements"
],
"lacks_abilities": [],
"is_important": false
},
"invoices": {
"section": "Invoices",
"abilities": [
"Can prepare all invoices",
"Can read all invoices"
],
"lacks_abilities": [],
"is_important": false
},
"payments": {
"section": "Payments",
"abilities": [
"Can prepare all payments",
"If required, can approve all payments",
"Can read all payments",
"Can cancel all payments"
],
"lacks_abilities": [],
"is_important": true
},
"team_management": {
"section": "Team management",
"abilities": [
"Can manage: add, remove new team members",
"Can change the privileges of other team members",
"Can remove another owner without their permission"
],
"lacks_abilities": [],
"is_important": false
},
"contact_management": {
"section": "Contact management",
"abilities": [
"Can manage: add, remove contacts",
"Can read payments and transactions for all contacts"
],
"lacks_abilities": [],
"is_important": false
},
"company_settings": {
"section": "Company settings",
"abilities": [
"Can update company profile including documents",
"Can update company settings",
"Can reset company account preferences"
],
"lacks_abilities": [],
"is_important": false
}
}
}
}
}
}
},
{
"embeds": [
"authorizable",
"customer",
"user"
],
"id": "aut_58OCgcANODJ21lPHdd39di",
"is_required": false,
"status": "unprocessed",
"comment": null,
"responded_at": null,
"created_at": "2021-06-02T08:56:45+01:00",
"updated_at": "2021-06-02T08:56:45+01:00",
"authorizable_type": "payment",
"user": {
"data": {
"embeds": [
"addresses",
"customers",
"role",
"partner",
"agent"
],
"id": "usr_7b8WPGR2brnzg2GHEwAYn3",
"firstname": "Simi",
"lastname": "Jombo",
"phone": null,
"password": null,
"email": "[email protected]",
"remember_token": null,
"gender": null,
"photo_url": null,
"email_verified": false,
"email_verified_at": null,
"disabled_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-10-01T12:45:00+01:00",
"updated_at": "2020-10-01T12:45:00+01:00",
"preferences": {
"security": {
"enabled_2fa": true,
"otp_channel": "email"
}
},
"role": {
"data": {
"embeds": [
"permissions"
],
"id": "rol_LbhnugthyweEB151E6On4",
"name": "business.admin",
"display_name": "Admin",
"is_active": true,
"is_restricted": true,
"created_at": "2020-05-15T07:04:11+01:00",
"updated_at": "2020-05-15T07:04:11+01:00",
"abilities": {
"dashboard": {
"section": "Dashboard",
"abilities": [
"Full access to all dashboard services",
"Read cash flow, cash-in and cash-out"
],
"lacks_abilities": [],
"is_important": false
},
"account": {
"section": "Account",
"abilities": [
"Access to company accounts",
"Download company statements"
],
"lacks_abilities": [],
"is_important": false
},
"invoices": {
"section": "Invoices",
"abilities": [
"Can prepare all invoices",
"Can read all invoices"
],
"lacks_abilities": [],
"is_important": false
},
"payments": {
"section": "Payments",
"abilities": [
"Can prepare all payments",
"If required, can approve all payments",
"Can read all payments",
"Can cancel all payments"
],
"lacks_abilities": [],
"is_important": true
},
"team_management": {
"section": "Team management",
"abilities": [
"Can manage: add, remove new team members",
"Can change the privileges of other team members"
],
"lacks_abilities": [],
"is_important": false
},
"contact_management": {
"section": "Contact management",
"abilities": [
"Can manage: add, remove contacts",
"Can read payments and transactions for all contacts"
],
"lacks_abilities": [],
"is_important": false
},
"company_settings": {
"section": "Company settings",
"abilities": [
"Can update company profile including documents",
"Can update company settings",
"Can reset company account preferences"
],
"lacks_abilities": [],
"is_important": false
}
}
}
}
}
}
},
{
"embeds": [
"authorizable",
"customer",
"user"
],
"id": "aut_7hiPsSkjFBK6UpBGnOFLr5",
"is_required": false,
"status": "unprocessed",
"comment": null,
"responded_at": null,
"created_at": "2021-06-02T08:56:45+01:00",
"updated_at": "2021-06-02T08:56:45+01:00",
"authorizable_type": "payment",
"user": {
"data": {
"embeds": [
"addresses",
"customers",
"role",
"partner",
"agent"
],
"id": "usr_KHf0B0bPWwBBhOgDckHfc",
"firstname": "Bowale",
"lastname": "Omode",
"phone": null,
"password": null,
"email": "[email protected]",
"remember_token": null,
"gender": null,
"photo_url": null,
"email_verified": false,
"email_verified_at": null,
"disabled_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-10-05T17:10:47+01:00",
"updated_at": "2020-10-05T17:10:47+01:00",
"preferences": {
"security": {
"enabled_2fa": true,
"otp_channel": "email"
}
},
"role": {
"data": {
"embeds": [
"permissions"
],
"id": "rol_LbhnugthyweEB151E6On4",
"name": "business.admin",
"display_name": "Admin",
"is_active": true,
"is_restricted": true,
"created_at": "2020-05-15T07:04:11+01:00",
"updated_at": "2020-05-15T07:04:11+01:00",
"abilities": {
"dashboard": {
"section": "Dashboard",
"abilities": [
"Full access to all dashboard services",
"Read cash flow, cash-in and cash-out"
],
"lacks_abilities": [],
"is_important": false
},
"account": {
"section": "Account",
"abilities": [
"Access to company accounts",
"Download company statements"
],
"lacks_abilities": [],
"is_important": false
},
"invoices": {
"section": "Invoices",
"abilities": [
"Can prepare all invoices",
"Can read all invoices"
],
"lacks_abilities": [],
"is_important": false
},
"payments": {
"section": "Payments",
"abilities": [
"Can prepare all payments",
"If required, can approve all payments",
"Can read all payments",
"Can cancel all payments"
],
"lacks_abilities": [],
"is_important": true
},
"team_management": {
"section": "Team management",
"abilities": [
"Can manage: add, remove new team members",
"Can change the privileges of other team members"
],
"lacks_abilities": [],
"is_important": false
},
"contact_management": {
"section": "Contact management",
"abilities": [
"Can manage: add, remove contacts",
"Can read payments and transactions for all contacts"
],
"lacks_abilities": [],
"is_important": false
},
"company_settings": {
"section": "Company settings",
"abilities": [
"Can update company profile including documents",
"Can update company settings",
"Can reset company account preferences"
],
"lacks_abilities": [],
"is_important": false
}
}
}
}
}
}
}
]
},
"attachments": {
"data": [
{
"embeds": [
"attached_to",
"customer",
"uploaded_by"
],
"id": "att_7e7b9A97DpCwKAXfH2r9HE",
"filename": "https://s3.eu-west-1.amazonaws.com/sandbox-private.getbrass.co/cus_1Gdl3cC14y6nUOoxOPhPcz/07cb4719-4e20-4045-8f5e-71c41beb4f63.jpeg?X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAYOX7CAIIYQ2GESN7%2F20210603%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20210603T151004Z&X-Amz-SignedHeaders=host&X-Amz-Expires=3600&X-Amz-Signature=e4ceadaa00ccd037352187e28937c150cb1e5a09e60de5e5469a1f3ff357e54b",
"note": "begging-meme.jpeg",
"created_at": "2021-06-02T08:56:45+01:00",
"updated_at": "2021-06-02T08:56:45+01:00",
"attached_to_type": "payment",
"uploaded_by": {
"data": {
"embeds": [
"addresses",
"customers",
"role",
"partner",
"agent"
],
"id": "usr_677kDUWkAdGJMrhppCDB8W",
"firstname": "Octonaut",
"lastname": "Pay",
"phone": "+2348123320811",
"password": null,
"email": "[email protected]",
"remember_token": null,
"gender": null,
"photo_url": null,
"email_verified": false,
"email_verified_at": null,
"disabled_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-09-02T10:04:10+01:00",
"updated_at": "2021-05-18T17:00:44+01:00",
"preferences": {
"security": {
"enabled_2fa": true,
"otp_channel": "email"
}
}
}
}
}
]
},
"customer": {
"data": {
"embeds": [
"accounts",
"addresses",
"country",
"customer_type",
"industry",
"preferred_branch",
"partner"
],
"id": "cus_1Gdl3cC14y6nUOoxOPhPcz",
"domain_slug": null,
"name": "Octonauts Ltd",
"registered_name": "Octonauts Ltd",
"description": "Pay your transportation fare with this",
"contact_firstname": "Octonauts",
"contact_lastname": "Assemble",
"contact_phone": "+2348123320811",
"contact_email": "[email protected]",
"channel": "direct",
"logo_url": null,
"is_registered": false,
"is_onboarding_completed": true,
"onboarding_completed_at": "2020-08-27T13:30:33+01:00",
"approved_auto_verification_at": null,
"kyc_verified_at": "2020-08-27T13:31:46+01:00",
"legal_search_started_at": "2020-10-07T13:14:30+01:00",
"kyc_completed_at": null,
"disabled_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-08-23T11:55:07+01:00",
"updated_at": "2021-05-04T16:27:27+01:00",
"customer_id": "001743",
"scoped_customer_id": 54,
"welcome_tracker": [
"welcome-pack"
],
"preferences": {
"webhook_enabled": true,
"webhook_url": "https://enyy64yxbbytl8y.m.pipedream.net",
"cashflow_questionnaire_submitted": false
},
"customer_type": {
"data": {
"embeds": [
"country"
],
"id": "cty_2Far8Fl7n0WoKsXt8gIxzc",
"label": "sole proprietorship",
"display_name": "Sole Proprietorship",
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-03-23T23:14:02+01:00",
"updated_at": "2020-03-23T23:14:02+01:00"
}
}
}
},
"counterparties": {
"data": [
{
"embeds": [
"customer",
"added_by_user",
"counterparty",
"disbursements",
"transactable"
],
"id": "cpt_7KXhpeLKXTRlMJE7Qr34Br",
"status": "unprocessed",
"responded_at": null,
"created_at": "2021-06-02T08:56:45+01:00",
"updated_at": "2021-06-02T08:56:45+01:00",
"counterparty_type": "contact",
"transactable_type": "payment",
"counterparty": {
"data": {
"embeds": [
"customer",
"contact_tag",
"added_by_user",
"recipient_customer_profile",
"bank"
],
"id": "con_1W8RJVzuqqCEYgC8RhzHu1",
"number": "0106857286",
"name": "OCAKES EMMANUEL OBOY",
"alias": "Emmanuel Okeke",
"currency": "NGN",
"phone": null,
"email": null,
"is_beneficiary": true,
"disabled_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2021-03-18T16:58:16+01:00",
"updated_at": "2021-03-18T16:58:16+01:00",
"bank": {
"data": {
"embeds": [
"country"
],
"id": "bnk_1mHfTTp03uzgVyof2L28lC",
"code": "044",
"name": "Access Bank",
"is_visible": true,
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-03-23T22:41:36+01:00",
"updated_at": "2020-03-23T22:41:36+01:00"
}
}
}
}
}
]
},
"initiated_by": {
"data": {
"embeds": [
"addresses",
"customers",
"role",
"partner",
"agent"
],
"id": "usr_677kDUWkAdGJMrhppCDB8W",
"firstname": "Octonaut",
"lastname": "Pay",
"phone": "+2348123320811",
"password": null,
"email": "[email protected]",
"remember_token": null,
"gender": null,
"photo_url": null,
"email_verified": false,
"email_verified_at": null,
"disabled_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-09-02T10:04:10+01:00",
"updated_at": "2021-05-18T17:00:44+01:00",
"preferences": {
"security": {
"enabled_2fa": true,
"otp_channel": "email"
}
}
}
},
"source_account": {
"data": {
"embeds": [
"bank",
"bank_branch",
"customer",
"total_outgoing",
"signatories"
],
"id": "acc_7711L8SDEjw5dN4NqAZpCB",
"number": "0000100024",
"name": "Octonauts Ltd",
"currency": "NGN",
"ledger_balance": {
"raw": "50166464495",
"formatted": "501664644.95",
"formatted_display": "₦501,664,644.95"
},
"available_balance": {
"raw": "49999088051",
"formatted": "499990880.51",
"formatted_display": "₦499,990,880.51"
},
"is_default": true,
"is_virtual": false,
"is_partner_managed": false,
"is_wallet": false,
"wallet_type": null,
"callback_time_minutes": 60,
"locked_reason": null,
"locked_at": null,
"disable_reason": null,
"disabled_at": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2021-03-27T16:43:43+01:00",
"updated_at": "2021-06-03T16:05:08+01:00",
"masked_number": "00*****024",
"preferences": {
"monthly_budget": null
},
"bank": {
"data": {
"embeds": [
"country"
],
"id": "bnk_1Pl1vnSMWep0rd8FNXkLyK",
"code": "100022",
"name": "GoMoney",
"is_visible": true,
"is_trashed": false,
"deleted_at": null,
"created_at": "2021-01-04T09:05:07+01:00",
"updated_at": "2021-01-04T09:05:07+01:00"
}
}
}
},
"tags": {
"data": [
{
"embeds": [
"customer",
"added_by_user"
],
"id": "tag_1cISYnOd1sX2WPH7yqHNDa",
"tag": "Accounting",
"description": null,
"is_trashed": false,
"deleted_at": null,
"created_at": "2020-08-23T11:55:07+01:00",
"updated_at": "2020-08-23T11:55:07+01:00"
}
]
}
}
}