Webhooks

Webhooks

Setting Up Webhooks

When creating your link_session_token with the POST /link-session endpoint, you can specify a callback URL with the webhook field:

"webhook": "https://yourdomain.com/moneykit_webhook"

MoneyKit currently send three types of webhooks:

After an end user successfully links their financial institution, webhooks for that link will be sent to the URL you provided.

Tip

The webhook body will always include the link_id of the link that was updated.

Webhook Security

The webhook payload is signed and will include a MoneyKit-Signature header that can be used to authenticate the webhook. Authentication is optional but encouraged; if you don't authenticate the webhook, you could be open to attack by an impostor sending you fake notifications.

{
"moneykit-signature": "eyJhbGciOiJFUzI1NiIsImtpZCI6IkEtN1ZQRHFURDhFUUlnVGRRVXA3MUtfeHNvS08zSWVzV0xuZ2p4X2giLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FwaS5tb25leWtpdC5jb20iLCJpYXQiOjE2OTgxNTA0MzMsInJlcXVlc3RfYm9keV9zaGEyNTYiOiI2ODk4Yzc2YzA5ZDEwNWQxMjM4OTcxMTAwNGIyYjkxODZjYTE0ZjBkMjJlZDJhZjkzOTIzMWJiMjc4MmYxZGQ0In0.TAT0jQxQnHoBrAcSijtFsYpme35WD5RGsKWOrgzUOCaEiY3CSflkVXO9xWVyqh0-k-98_1MFHbMxpNExrOXaJg"
}

MoneyKit offers a JSON Web Key Set (JWKS) you can fetch and cache in order to verify the signature in the webhook header. The JWKS is rotated regularly, so you should refetch whenever the kid on the incoming webhook does not match your cached JWKS kid.

Note

Note that only one key returned from the JWKS endpoint is meant to be used for verification.

  • The kid in the webhook JWT header will match up with the key id of one of the keys returned from the JWKS endpoint.
  • Depending on the language, some JWT packages will handle this for you. You can pass in the Key Set with the token when decoding (instead of a single key).
  • To verify the webhook, first extract the MoneyKit-Signature from the webhook header, then validate the signature with your cached JWKS using your preferred JWT library.

In case of failures, webhooks will be resent automatically for up to two hours, on an exponential backoff basis, until your endpoint acknowledges receipt. Each webhook has a unique webhook_idempotency_key so that you can avoid accidental reprocessing.

Link State Changed Webhook

  • You will receive this webhook when the state of a link has changed.

  • The possible states are:

    LinkState
    CONNECTING = "connecting";
    AWAITING_TOKEN_EXCHANGE = "awaiting_token_exchange";
    CONNECTED = "connected";
    DELETED = "deleted";
    ERROR = "error";
  • If the state is ERROR, you will be provided an error type:

    LinkError
    SYSTEM_ERROR = "system_error";
    PROVIDER_ERROR = "provider_error";
    INSTITUTION_ERROR = "institution_error";
    USER_ERROR = "user_error";
    AUTH_EXPIRED = "auth_expired";
    INCOMPLETE = "incomplete";
  • Here are two examples of the Link State Changed webhook:

    JSON
    {
    "webhook_event": "link.state_changed",
    "webhook_major_version": 1,
    "webhook_minor_version": 0,
    "webhook_idempotency_key": "52e5d1896805459e899e803f2f3a8446",
    "webhook_timestamp": "2023-10-24T12:18:22.432381",
    "link_id": "mk_a2vfuwDx8qRh77uwfCwT9X",
    "link_tags": [],
    "state": "connected",
    "error": None,
    "error_message": None
    }

    {
    "webhook_event": "link.state_changed",
    "webhook_major_version": 1,
    "webhook_minor_version": 0,
    "webhook_idempotency_key": "ab127b5c1b2340e18ed30ce246f619e0",
    "webhook_timestamp": "2023-10-24T12:27:13.177113",
    "link_id": "mk_RPGN6BqZa4iWXRz5n328Ab",
    "link_tags": [],
    "state": "error",
    "error": "system_error",
    "error_message": "No accounts found"
    }

  • Any link_tags you provide when creating your link_session_token with the POST /link-session endpoint will be included in the Link State Changed webhook body:

    "link_tags": [
    "user_type:admin"
    ],

Product Update Webhook

You will receive this webhook after aggregation for a product has been completed.

There are three scenarios in which this will happen:

  1. Soon after an end user completes linking their accounts.

  2. After requesting a product refresh using the product refresh endpoint.

  3. After a scheduled, periodic aggregation.

Here is an example:

{
"webhook_event": "link.product_refresh",
"webhook_major_version": 1,
"webhook_minor_version": 0,
"webhook_idempotency_key": "2HgPyn8H8CdaYhtLd3sdkf",
"webhook_timestamp": "2023-11-06T10:31:39.515578",
"link_id": "mk_BwujtU5hcym7te8AYaHSxC",
"link_tags": [],
"product": "account_numbers",
"state": "connected",
"state_changed_at": "2023-11-06T10:31:39.475714+00:00",
"error_message": null
}

Transactions Available Webhook

Note

MoneyKit automatically enriches new transactions after they are received.

You will receive this when new transactions are available for a link. Note that it is possible for the transactions product to send a Product Update webhook, notifying you that an update was complete, but there may be no new transactions. But if there are new transactions, then you will receive a Transactions Available webhook.

Here is an example:

{
"webhook_event": "transactions.updates_available",
"webhook_major_version": 1,
"webhook_minor_version": 0,
"webhook_idempotency_key": "9682969d4b6b4a92ad4dea214ea6aa2c",
"webhook_timestamp": "2023-10-24T12:29:22.344868",
"link_id": "mk_EtHQBP4swwN5CKrjnrqAPJ",
}

Note that MoneyKit automatically enriches new transactions after they are received, so when you receive this webhook, it is possible that the new transactions are not yet enriched. You will receive a separate Product Update webhook for the "enrichment" product, when that update process is complete.

Testing Webhooks in Sandbox

MoneyKit makes it easy to test webhooks on demand in the sandbox environment.

The Test Link Event webhook allows you to trigger webhooks for any of your sandbox links.