Finch offers webhooks to inform you of changes to data models in a push-notification fashion, rather than you having to rely exclusively on pulling data from our API. A webhook URL is a HTTPS endpoint configured by your application to receive requests from Finch.

Webhooks are available for customers in our Scale tier.

Webhook Registration

Webhook endpoints should use HTTPS and expect to receive POST requests with the following headers:

{
  "Content-Type": "application/json",
  "Finch-Event-Id": "msg_2SFMDibF3lmRw8DzX4t1JjiEZQl",
  "Finch-Signature": "v1,8rFENj/WpNAMx+Kh5R1NLQunmpaBx4vOntjJdbGKbvM=",
  "Finch-Timestamp": "1688737757"
}

You can create webhooks via the Finch Developer Dashboard.

webhooksCreate.png

After registering a webhook, you will be provided with a webhook secret. This secret can be used to validate that the webhooks you receive were sent by Finch.

webhooksSecret

This secret will only be displayed once, so we recommend you store it as soon as you receive it. See the Webhook Verification section for more details.

Webhook Payload Structure

Common Fields

Each webhook event contains the following fields in the response body:

Field NameTypeDescription
company_idstring<uuid>Unique Finch id of the company for which data has been updated.
account_idstring<uuid>Unique Finch id of the employer account that was used to make this connection. If an employer completes authorization through Finch multiple times with different accounts or API tokens, those connections will be associated with different account_ids.
event_typestringThe type of webhook being delivered.
dataobjectMore information about the associated event. The structure of this object will vary per event type.

Finch provides three general types of webhook events: account updates, job completions, and data updates.

Account Updates

Account update events contain information about account connections, such as when a connection has been established or when a connection has entered an error state. This type of webhook has the following unique schema:

Field NameTypeDescription
event_typestringAlways account.updated.
data.statusstringThe status of the account. This follows our standard connection status schema. Options are pending, processing, connected, error_permissions, error_reauth, error_no_acount_setup.
data.authentication_methodstringThe method of authentication used to connect this account. Options follow the standard Finch authentication types: credential, api_token, oauth, and assisted.

Example:

{
  "company_id": "720be419-0293-4d32-a707-32179b0827ab",
  "account_id": "fa872170-b49d-4fb5-aa39-fb1515db0925",
  "event_type": "account.updated",
  "data": {
    "status": "connected"
    "authentication_method": "assisted"
  }
}

Job Completion

Job completion events fire when a job finishes running, whether the final state is a success or an error. This type of webhook has the following data schema:

Field NameTypeDescription
event_typestringFollows the schema job.{job_type}.complete. {job_type} can be any valid Finch job type such as data_sync_all, benefit_create, or benefit_enroll.
data.job_idstring<uuid>The id of the job which has completed.
data.job_urlstringThe url to query the result of the job.

Example:

{
  "company_id": "720be419-0293-4d32-a707-32179b0827ab",
  "account_id": "fa872170-b49d-4fb5-aa39-fb1515db0925",
  "event_type": "job.benefit_enroll.completed",
  "data": {
    "job_id": "10f249d5-c974-4ce3-979a-31164323a34f",
    "job_url": "https://api.tryfinch.com/jobs/10f249d5-c974-4ce3-979a-31164323a34f"
  }
}

Data Changes

Data change events fire when any data for a connection changes after Finch’s initial data sync. These could be created, updated, or deleted events on any of our endpoints. This type of event has the following schema:

Field NameTypeDescription
event_typestringFollows the schema {endpoint}.{created|updated|deleted}. {endpoint} can be any Finch endpoint such as company, directory, individual, etc. You can see the full up-to-date list of events in by registering an endpoint in our developer dashboard.
dataobjectThe data object schema will change depending on the endpoint.

The possible data schemas per endpoint are as follows:

Company

{
  "company_id": "720be419-0293-4d32-a707-32179b0827ab",
  "account_id": "fa872170-b49d-4fb5-aa39-fb1515db0925",
  "event_type": "company.updated",
  "data": null
}

*Company events can only be updated.

Directory

{
  "company_id": "720be419-0293-4d32-a707-32179b0827ab",
  "account_id": "fa872170-b49d-4fb5-aa39-fb1515db0925",
  "event_type": "directory.created",
  "data": {
    "individual_id": "59dcedb1-75a4-446c-8ece-4f9ccd53a1f9"
  }
}

*Note that one event will be created for each individual that has changed. For example, if 10 new individuals are added, 10 created events will be sent.

Employment

{
  "company_id": "720be419-0293-4d32-a707-32179b0827ab",
  "account_id": "fa872170-b49d-4fb5-aa39-fb1515db0925",
  "event_type": "employment.created",
  "data": {
    "individual_id": "9987ecd1-6c6e-4d97-81ae-4d0248dbdb3d"
  }
}

Individual

{
  "company_id": "720be419-0293-4d32-a707-32179b0827ab",
  "account_id": "fa872170-b49d-4fb5-aa39-fb1515db0925",
  "event_type": "individual.updated",
  "data": {
    "individual_id": "9987ecd1-6c6e-4d97-81ae-4d0248dbdb3d"
  }
}

Payment

{
  "company_id": "720be419-0293-4d32-a707-32179b0827ab",
  "account_id": "fa872170-b49d-4fb5-aa39-fb1515db0925",
  "event_type": "payment.created",
  "data": {
    "payment_id": "1c5e7bf2-94ce-4041-bf59-98e95677be21",
    "pay_date": "10-23-2023"
  }
}

Pay Statement

{
  "company_id": "720be419-0293-4d32-a707-32179b0827ab",
  "account_id": "fa872170-b49d-4fb5-aa39-fb1515db0925",
  "event_type": "pay_statement.created",
  "data": {
    "payment_id": "1c5e7bf2-94ce-4041-bf59-98e95677be21",
    "individual_id": "9987ecd1-6c6e-4d97-81ae-4d0248dbdb3d"
  }
}

*Note that one event will be created for each pay statement object that has changed. For example a new payrun for 20 individuals will generate 20 unique pay statement events.

Webhook Verification

Finch uses HMAC-SHA256 webhook verification. The following are steps you can use to verify a webhook using the verification header:

  1. Extract the signature from the header. The Finch-Signature header consists of a list of signatures (where the signature content begins after “v1,” and is space delimited) to account for secret rotations; there may be multiple signatures present for cases where a secret was rotated. During the verification process, the signature must match at least one signature in the list to be considered valid.
v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE= v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=
  1. Generate the webhook signature. Using the webhook secret, hash the webhook content in the form {webhook_id}.{webhook_timestamp}.{body} where webhook_id is the Finch-Event-Id, webhook_timestamp is the Finch-Timestamp, and body is the raw body of the request. The signature is sensitive to any changes, so even a small change in the body will cause the signature to be completely different. This means that you should not change the body in any way before verifying. If the signature does not match the value received in the Finch-Signature header, reject the webhook. Note: when performing the signature comparison, it is safest to use a constant-time comparison to avoid potential timing attacks.
const crypto = require('crypto');

const signedContent = `${webhook_id}.${webhook_timestamp}.${body}`
const SECRET = "5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH";

// Need to base64 decode the secret
const secretBytes = new Buffer(SECRET, "base64");
const signature = crypto
  .createHmac('sha256', secretBytes)
  .update(signedContent)
  .digest('base64');
  1. Verify the webhook timestamp If the signature is valid, ensure the timestamp is not greater than five minutes in the past or future. Using outdated webhooks increases susceptibility to replay attacks.

The Finch Backend SDKs encapsulate all of this logic to simplify the webhook verification process.

import Finch from '@tryfinch/finch-api';
...

app.use('/webhooks/finch', bodyParser.text({ type: '*/*' }), function (req, res) {
  const finch = new Finch();
  const payload = finch.webhooks.unwrap(req.body, req.headers, process.env['FINCH_WEBHOOK_SECRET']); // env var used by default; explicit here.
  console.log(payload);
  res.json({ ok: true });
});

Testing Webhooks

You can send a test request to any webhook through the developer dashboard.

Test webhooks

The test webhook will include the same structure as data update webhooks, with the event_type set to test

Retries

Upon failure, Finch will retry according to the following schedule with exponential backoff:

  • Immediately
  • 5 seconds
  • 5 minutes
  • 30 minutes
  • 2 hours

Best Practices for Handling Webhooks

Responding to Webhooks

In order to prevent unnecessary retries, we recommend receiving webhook events and processing the events in separate processes. Upon receiving the event, you should immediately respond with a 200 indicating that the event was successfully delivered.

Event delivery and ordering

  • It is possible that you may occasionally receive the same webhook event more than once. We recommend setting up idempotent event processing by using the Finch-Event-Id.
  • Finch does not guarantee delivery of events in the order they happen. For example, you may receive an update event for an individual before a created event. You should also use the Finch API to occasionally fetch any missing data. For example, you can fetch an individual if you happen to receive an update event first.

Event Mapping

  • Webhooks are delivered with a company_id and an account_id corresponding to the company for which the data changed. You should retrieve these ids from Finch in order to map webhooks updates to the company/account pair the data belongs to. A simple workflow for this is to call the /introspect endpoint after exchanging an auth code for an access token via the /auth/token endpoint. The /introspect endpoint contains the company_id and account_id for the connection a token is associated with. You can save these ids in your system to map incoming webhook events to companies.