Webhooks

Webhooks are used to notify your platform about events on finmid side. finmid sends these notifications to an endpoint hosted in your environment configured to receive and process them.

What is a webhook?

A webhook is an asynchronous notification in the shape of an HTTP request. Webhooks are a well known practice used by many services. We use webhooks to notify your platform asynchronously about events on finmid side (e.g. a Business has agreed to share Personally Identifiable Information). This allows you to react to certain events and avoids the need for polling.

How to start receiving webhooks?

To start receiving webhooks, an endpoint has to be configured on both your and finmid ends. You need to provide a URL to which webhooks will be sent. This must be an HTTPS URL that is under your control or trusted by you. Additionally, we need certain security parameters from you to protect your endpoint. There are two ways to configure a webhook endpoint on finmid's end.

๐Ÿ“˜

Setting up an endpoint via customer support

Please contact [email protected] and provide the following data points:

  • url โ€” URL of your webhook endpoint (only HTTPS URLs)
  • basic_auth_username โ€” HTTP basic authentication username, for webhook authentication
  • basic_auth_password โ€” HTTP basic authentication password, for webhook authentication
  • signature_secret โ€” HMAC signature secret, for verifying the webhook payload

๐Ÿ’ก

You can configure multiple webhook endpoints: each of them will be receiving webhook notifications.

Protecting you webhook endpoint

Webhooks will be sent to your endpoint over the public internet. Therefore we only support HTTPS endpoint URLs. Custom self-signed certificates are not supported. We provide two important security features which will help you to protect your endpoint:

โš 

It is strongly recommended to utilize the above security features to protect your endpoint.

Authentication

Webhooks utilize the HTTP basic authentication method. This method has very good support and is easy to implement while providing a sufficient level of protection.

This is just one example how easy the implementation is for an Express.js based app:

const app = require("express")();
const basicAuth = require("express-basic-auth");

app.use(
    basicAuth({
        users: { admin: "supersecret" },
    })
);

Payload verification

All webhooks provide a X-Payload-Signature header. It contains a HMAC-SHA-256 signature of the payload which was generated using the signature secret of the webhook endpoint. Please note that the header value is encoded as Base64.

You can check the signature like so:

  1. Read the signature from HTTP header X-Payload-Signature
  2. Base64-decode the signature value (if necessary)
  3. Generate your signature from the webhook payload using signature_secret
  4. Compare signature from webhook header with your generated signature. The webhook is verified if signatures match.

Pseudo code example of the above steps:

var signature = request.header("X-Payload-Signature");
var payload = request.body();

// Assume generatedSignature is a Base64-encoded signature value already
var generatedSignature = HMAC.createSha256(payload, env.SIGNATURE_SECRET);

if (signature === generatedSignature) {
    // Webhook verified
}

Additional security tips

Your webhook endpoint already has quite good protection if you have implemented the above security features. However, you might consider the following additional tips:

  • Choose a random, hard to guess webhook endpoint URL. This will make it hard for attackers to identify your webhook endpoint.
  • Don't expose sensitive error information with your webhook endpoint. Make sure to log errors internally and only return HTTP 404 Not Found in case of an error. Even if an attacker identifies your webhook endpoint, they might get distracted by the not found error and stop their efforts.

Webhook delivery guarantees

In the event of an error when posting webhooks to your endpoint, finmid will attempt to deliver your webhooks for up to one day, with exponential backoff.

Payload structure

This is a breakdown as to how finmidโ€™s webhooks are structured, at a global level, and event-specific level.

General payload

The webhook payload might contain multiple events. This allows us to reduce HTTP traffic.
Our webhooks all follow the same general payload structure, which can be found below.

Fields labelled as event specific are detailed in the next section.

{
  "webhook_id": "e8cce989-0c48-4036-9bf2-1f0206398743",
  "timestamp": "2022-02-10T13:48:48.892708722Z",
  "events": [
    {
      "event_id": "ebda8288-8889-45e8-b672-0741e3553754",
      "timestamp": "2022-02-10T13:42:35.282908722Z",
      "type": // Event Specific,
      "data": {
        // Event Specific
      }
    },
    {
      "event_id": "4907a0f7-b53f-4fe7-90bd-bded64a55707",
      // ...
    }
  ]
}

Generic Fields

FieldTypeDescription
webhook_idstring
timestampstringCorresponds to when the webhook was created.
eventslist of event objects

Event Object Fields

FieldTypeDescription
event_idstring
timestampstringCorresponds to when the event occured, and should be used as the source of truth to reconstruct event timelines.
typestringType of the event
dataobjectEvent-related data

Types of Events & Payloads

This is a list of all the types of events we currently send. Do be aware that new events may be added.

Events are distinguished by the โ€œtypeโ€ field in the payload, and additional data is provided in the โ€œdataโ€ field.

Webhooks can be tested via the post Simulate Offer acceptance API.


capital_offer.created

This webhook is published when a financing Offer has been created by finmid and is available to share with a Business. Platforms can request Offer status for a relevant Business after this webhook is published.

{
    "event_id": "ebda8288-8889-45e8-b672-0741e3553754",
    "timestamp": "2022-02-10T13:42:35.282908722Z",
    "type": "capital_offer.created",
    "data": {
        "business_id": "business-id",
        "max_amount": 123.45,
        "currency": "EUR"
    }
}

Data Fields:

FieldTypeFormatDescription
business_idstring^[a-zA-Z0-9_.\-]{1,100}$ID of the Business for which the Offer has been created
max_amountnumberdecimalThe maximum Funding amount available in the current Offer
currencystringEUR, DKK, SEK, PLNISO 4217 currency code in which Business operates, e.g. does sales. Currency will be associated with Sales statements and performance data

capital_funding.created

This webhook is published when a new financing Offer has been accepted by a Business and a relevant Funding has been created.

{
    "event_id": "ebda8288-8889-45e8-b672-0741e3553754",
    "timestamp": "2022-02-10T13:42:35.282908722Z",
    "type": "capital_funding.created",
    "data": {
        "business_id": "business-id",
        "funding_id": "funding-id"
    }
}

Data Fields:

FieldTypeFormatDescription
business_idstring^[a-zA-Z0-9_.\-]{1,100}$ID of the Business for which the Offer has been created
funding_idstringUUIDID of the Funding that has been created after the Offer has been accepted by the Business

kyb_data_consent.granted

This webhook is triggered when Business grants finmid consent to collect its Personally Identifiable Information (PII).
After the webhook is published, finmid expects to receive the Business's PI data for further processing.

{
    "event_id": "ebda8288-8889-45e8-b672-0741e3553754",
    "timestamp": "2022-02-10T13:42:35.282908722Z",
    "type": "kyb_data_consent.granted",
    "data": {
        "business_id": "business-id"
    }
}

Data Fields:

FieldTypeFormatDescription
business_idstring^[a-zA-Z0-9_.\-]{1,100}$ID of the Business that has granted data sharing consent