## Introduction

Bloom enables **Webhooks** for you to get notified of specific Enablement Services events.

There are multiple event types you can integrate to your systems through simple HTTP Webhooks.

## Event Types

### Connection

| Event | Description |
| --- | --- |
| **`connection.closed`** | A connection has been closed by the user and will no longer sync to receive transactions. |
| **`connection.created`** | A connection has been created. |
| **`connection.refresh_required`** | A connection needs to be re-authenticated. |
| **`connection.disconnected`** | A connection has been disconnected and will no longer sync to receive transactions. A new processor token will need to be imported to begin receiving new transactions. |
| **`connection.error_provider`** | The connection provider is reporting an error with the connection. This error will resolve itself over time during retries - the connection will either transfer to `ACTIVE` or `DISCONNECTED` status. |
| **`connection.error_service`** | An unexpected error occurred when trying to sync the connection. This error will resolve itself over time during retries - the connection will either transfer to `ACTIVE` or `DISCONNECTED` status. |
| **`connection.error_institution`** | The connection provider is returning an error with the connected institution. This error will resolve itself over time during retries - the connection will either transfer to `ACTIVE` or `DISCONNECTED` status. |
| **`connection.activated`** | A connection has transitioned to an active state and will continue to sync and receive transactions. |

### Tradeline

| Event | Description |
| --- | --- |
| **`tradeline.closed_by_user`** | A tradeline has been closed by the user. |
| **`tradeline.closed_inactivity`** | A tradeline has been closed due to inactivity. |
| **`tradeline.created`** | A tradeline has been created. |
| **`tradeline.opened`** | A tradeline has been opened. |
| **`tradeline.deleted`** | A tradeline has been deleted from the user's credit report. |
| **`tradeline.transactions_added`** (DEPRECATED) | New transactions have been added to a tradeline. |

### User

| Event | Description | 
| --- | --- | 
| **`user.closed`** | A user has been closed. |
| **`user.created`** | A user has been created. |
| **`user.synced`** | A user's transaction history has been synced. |
| **`user.updated`** | A user has been updated. |

## How to Add an Endpoint

In order to add an endpoint you'll need to do two things:

1. Get your dashboard URL.
2. Add an endpoint in the dashboard.

### Get Your Dashboard URL

Use the [webhookDashboardUrl](https://developers.bloomcredit.io/page/enablement_gql_ref#query-webhookDashboardUrl) query. You'll need to authenticate using [Organization Scope](https://developers.bloomcredit.io/docs/enablement-services-authentication#organization-scope) authentication. You will receive a URL as the output. This link will be good for 7 days.

GraphQL

```graphql
query DashboardURL {
  webhookDashboardUrl
}

# Returns: https://app.svix.com/login?primaryColorLight=1481e6&primaryColorDark=1481e6#key=<some-key>
```

### Add An Endpoint

Adding an endpoint is as simple as providing a URL that you control and selecting the event types that you want to listen to.

> 💡
>
> ### You can test an endpoint by using the "Svix Play" button to have a unique URL generated for you.

## Signature Verification

Webhook signatures let you verify that webhook messages are actually sent by us and not a malicious actor.

For a more detailed explanation, check out this article on [why you should verify webhooks](https://docs.svix.com/receiving/verifying-payloads/why).

Our webhook partner Svix offers a set of useful libraries that make verifying webhooks simple. Here is an example using Javascript:

JavaScript

```javascript
import { Webhook } from "svix";

const secret = "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw";

// These were all sent from the server
const headers = {
"svix-id": "msg_p5jXN8AQM9LWM0D4loKWxJek",
"svix-timestamp": "1614265330",
"svix-signature": "v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=",
};
const payload = '{"test": 2432232314}';

const wh = new Webhook(secret);
// Throws on error, returns the verified content on success
const payload = wh.verify(payload, headers);
```

For more instructions and examples of how to verify signatures, check out their [webhook verification documentation](https://docs.svix.com/receiving/verifying-payloads/how).

## Retries

### Automated Retries

We attempts to deliver each webhook message based on a retry schedule with exponential backoff.

Each message is attempted based on the following schedule, where each period is started following the failure of the preceding attempt:

| Attempt # | Time Since Last Attempt | Time Since First Attempt | Example |
| --- | --- | --- | --- |
| 1 | Immediately | 0 min | 2025-01-01T00:00:00Z |
| 2 | 5 seconds | 5 sec | 2022-01-01T00:00:05Z |
| 3 | 5 minutes | ~5 min | 2022-01-01T00:05:05Z |
| 4 | 30 minutes | ~35 min | 2022-01-01T00:35:05Z |
| 5 | 2 hours | ~2 hr 35 min | 2022-01-01T02:35:05Z |
| 6 | 5 hours | ~7 hr 35 min | 2022-01-01T07:35:05Z |
| 7 | 10 hours | ~17 hr 35 min | 2022-01-01T17:35:05Z |
| 8 | 10 hours | ~27 hr 35 min | 2022-01-02T03:35:05Z |

If an endpoint is removed or disabled, delivery attempts to the endpoint will be disabled as well.

For example, an attempt that fails three times before eventually succeeding will be delivered roughly 35 minutes and 5 seconds following the first attempt.

After the 8th attempt, we will not attempt to deliver the webhook again.

### Manual retries

You can also use the application portal to manually retry each message at any time, or automatically retry ("Recover") all failed messages starting from a given date.

## Troubleshooting & Failure Recovery

### Troubleshooting Tips

There are some common reasons why your webhook endpoint is failing:

#### Not using the raw payload body

This is the most common issue. When generating the signed content, we use the raw string body of the message payload.

If you convert JSON payloads into strings using methods like stringify, different implementations may produce different string representations of the JSON object, which can lead to discrepancies when verifying the signature. It's crucial to verify the payload exactly as it was sent, byte-for-byte or string-for-string, to ensure accurate verification.

#### Missing the secret key

From time to time we see people using the wrong secret key. Remember that keys are unique to endpoints.

#### Sending the wrong response codes

When we receive a response with a 2xx status code, we interpret that as a successful delivery even if you indicate a failure in the response payload. Make sure to use the right response status codes so we know when messages are supposed to succeed vs fail.

#### Responses timing out

We will consider any message that fails to send a response within {timeout duration} a failed message. If your endpoint is also processing complicated workflows, it may timeout and result in failed messages.

We suggest having your endpoint simply receive the message and add it to a queue to be processed asynchronously so you can respond promptly and avoid getting timed out.

### Failure Recovery

#### Re-enable a disabled endpoint

If all attempts to a specific endpoint fail for a period of 5 days, the endpoint will be disabled. To re-enable a disabled endpoint, go to the webhook dashboard, find the endpoint from the list and select "Enable Endpoint".

#### Recovering/Resending failed messages

If your service has downtime or if your endpoint was misconfigured, you probably want to recover any messages that failed during the downtime.

If you want to replay a single event, you can find the message from the UI and click the options menu next to any of the attempts.

From there, click "resend" to have the same message sent to your endpoint again.

If you need to recover from a service outage and want to replay all the events since a given time, you can do so from the Endpoint page. On an endpoint's details page, click "Options > Recover Failed Messages".

From there, you can choose a time window to recover from.

For a more granular recovery - for example, if you know the exact timestamp that you want to recover from - you can click the options menu on any message from the endpoint page.

From there, you can click "Replay..." and choose to "Replay all failed messages since this time."
