Webhook functionality

Webhooks are the perfect way to create a scalable application by allowing you to get asynchronous updates on changes in the API. A webhook will be sent to your server url that you updated in the webhook-config pertaining to your organization. Upon receiving a webhook, you should have your server respond back with a 200 response within 3 seconds to notify our server that you received the webhook successfully. The webhook config persists across all product stacks and APIs.


Consuming Webhooks

Because there is a limited response time, webhooks should be consumed by adding them to your own database table and immediately responding to the webhook server with a 200 code. From there, you can iterate on the webhooks from your own DB and also have a record of all the webhooks you've received.

Webhooks will be retried at increasing time intervals if our webhook server does not receive a 200 response from you in time.

Setting up your Webhook-Config

Every Organization is created with a webhook-config that is initially disabled. In order to enable the webhooks-config and select what events you want notifications for, you'll need to make a PATCH call:

PATCH /api/organization/v1/current-organization/webhook-config

  "url": "https://webhook.site/a5k730Q4", //Your receiving webhook server
  "enabled": true,
  "sharedSecret": "A7B6Fgl2KFI921gJ", //set a shared secret for webhook notification encryption
  "webhookTypes": [
    "payment", "kyc"
  ] // webhook types are "payment","transaction", "nft", "kyc", "document", "custodialAccount", "externalAccount", "identity", "asset", "wallet"

It's important to note that you'll only receive webhooks for the types that you have added to the webhookTypes array.

Webhook structure

Here's an example of what a received webhook looks like:

  "organizationId": "cbbb36e0-f507-40e8-a839-c8982de5c2da", //your integrator's organization
  "action": "string", //
  "id": "b3b3318e-fb35-49f6-a115-416f4e76c26c", //webhook id
  "resourceId": "9c32c0d8-3e73-4896-8a7a-766c7428ac54", //API object's id to be referenced
  "resourceType": "Payment", //API object that was either created or updated
  "createdAtUtc": "2022-03-28T11:14:11.3704804+00:00", //webhook creation time
  "changes": null //depending on the product suite, will either show "null" or key:value updates. Even a "null" value can have updates in the API but not present on the webhook object.

Whenever you receive a webhook, you should follow up with a GET call in the api, targeting the resourceType/resourceId in the URL. You'll then be able to register the new object information in your DB/Front End, or make note of any changes to an existing object.

Webhook security

You might have noticed when updating your webhook-config that it asks for a sharedSecret. The shared secret is hashed out with the contents of the webhook to create an encrypted value that is sent in the header of the webhook. This gives you confidence that the webhook did indeed come from our Fortress server. The verification formula looks like so:

  • Base64Encode(Hmac-Sha256(sharedSecret, webhookPayload))

The value of the encryption is to the key x-fortress-webhook-hmac in the webhook header. Here's a sample curl response from a sent webhook.

curl -X 'POST' 'https://webhook.site/4de14c2c-9e4b-4a5a-b667-64a69f6da52f' 
-H 'connection: close' 
-H 'content-length: 266' 
-H 'content-type: application/json; charset=utf-8' 
-H 'traceparent: 00-317ed7516d09f1d424c95356ab4b377c-f194513ac07d6453-00' 
-H 'request-id: |317ed7516d09f1d424c95356ab4b377c.f194513ac07d6453.' 
-H 'request-context: appId=cid-v1:ec7b73c7-a50e-4e10-9be2-e81e0dd8abc2' 
-H 'x-fortress-webhook-hmac: iM+cYc+/bpcBU2He+SBR+mIT1ngSre5cCx+rKSp+Nhk=' 
-H 'host: webhook.site' 
-d $'{"organizationId":"083a1c85-0aed-4159-bf04-8b2a3f878f19","action":"create","id":"a7d247e2-9caf-42f0-b2a0-7cf55e09b954","resourceId":"cb822ebb-d273-4fd3-a0b4-622103fd6ab4","resourceType":"Transaction","createdAtUtc":"2022-05-25T21:21:55.0782343+00:00","changes":null}'