> ## Documentation Index
> Fetch the complete documentation index at: https://docs.noyo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Respond to important events in near-real-time

Receiving webhook events is particularly useful for listening to asynchronous events such as creation/resolution of differences and discrepancies and changes to group status.

To start using webhooks, you'll need to register the endpoints you want to use in the Noyo web application. After you register them, Noyo can push real-time event data to your application's webhook endpoint as they happen. Noyo uses HTTPS to send webhook events to your app as a JSON payload. Webhooks are only available in production environments and are not available in sandbox.

# Events

| Event name                                     | Event key                                                  |
| ---------------------------------------------- | ---------------------------------------------------------- |
| **Snapshot status changed**                    | `member_snapshot.status_change`                            |
| **Snapshot Fulfillment Summary updated**       | `member_snapshot_carrier_fulfillment_status.status_change` |
| **Difference created**                         | `difference.created`                                       |
| **Difference marked as a discrepancy**         | `difference.discrepancy`                                   |
| **Difference resolved**                        | `difference.resolved`                                      |
| **Difference replaced**                        | `difference.replaced`                                      |
| **Group Created**                              | `group.created`                                            |
| **Group Connection Request created**           | `group_connection_request.created`                         |
| **Group Connection Request status changed**    | `group_connection_request.status_change`                   |
| **Group Disconnection Request created**        | `group_disconnection_request.created`                      |
| **Group Disconnection Request status changed** | `group_disconnection_request.status_change`                |

## Payload Structure

The webhook HTTP payload will include information in both its HTTP headers and its request body. Event payloads do not contain any PII or PHI. Noyo recommends using the corresponding [Get difference](/api-reference/tracking/get-differences) when you receive any `.created` webhook.

### Headers

The payload will be sent with the following HTTP headers:

```json theme={"system"}
{
    "Content-Type": "application/json",
    "x-noyo-signature": "ffd3bc72dea695cf2f9cf01c8956af048c293abd0112267edc1ef28178c567ef",
    "x-noyo-timestamp": "2024-05-08T21:27:45.039587+00:00",
    "x-cloud-trace-context": "54d5ec040d178633b8594fcda9d0edc6/7396771996713220078"
}
```

### Common fields

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

```json theme={"system"}
{
    "data": {
        "id": "eb73184b-ae3c-4411-aa82-3c3bd877cf78"
    },
    "event": {
        "id": "3573d31d-15ab-42a3-aece-8f1e3a5c986f",
        "created": 1714670277,
        "type": "difference.created"
    }
}
```

Some events may include additional fields.

## Using webhooks to track enrollment changes

Our [The Tracking API](/docs/members/tracking/the-tracking-api) and its webhooks enable you to build field-level change tracking, so you have complete transparency from the time Noyo initially processes the member snapshot to when we confirm the change was made successfully by the carrier. You can also listen for unexpected data discrepancies that could interrupt coverage for your members.

![Events and webhooks in two scenarios: a completed change and an unexpected discrepancy](https://static.noyo.com/img/85ad564-Frame_58.png)

### Member Snapshot status changed

```json JSON theme={"system"}
{
    "event": {
        "id": "fbec86ad-ced9-48e5-9f77-4a3a00b522aa",
        "type": "member_snapshot.status_change",
        "created": 1719344385
    },
    "data": {
        "id": "ea1b3077-b4f9-461b-80be-28023bc8ea19",
        "status": "completed"
    }
}
```

### Member Snapshot Fulfillment Summary updated

Read more about fulfillment summary [here](/docs/members/snapshot-workflow/snapshot-status).

```json theme={"system"}
{
    "event": {
        "created": 1719858674,
        "id": "beb4e1b4-f1bd-4cbe-a361-a61bec3203e8",
        "type": "member_snapshot_carrier_fulfillment_status.status_change"
    },
    "data": {
        "carrier_id": "9a0a7437-4097-4251-9e71-85384c0eb1c9",
        "member_snapshot_id": "815e92bc-33c1-4ed3-b658-4dc0e56cc38a",
        "status": "sent"
    }
}
```

### Difference created

For any snapshot sent to Noyo that contains changes, a `difference.created` event will be sent almost instantly after the snapshot is initially processed. This means you can save individual tracking identifiers for all field-level changes that are in-process.

Note that discrepancies found during carrier syncs will also initiate a `difference.created` event, so Noyo suggests calling [Get difference](/api-reference/tracking/get-differences) to populate all relevant details of the difference, such as its source.

```json theme={"system"}
{
    "data": {
        "id": "eb73184b-ae3c-4411-aa82-3c3bd877cf78"
    },
    "event": {
        "id": "3573d31d-15ab-42a3-aece-8f1e3a5c986f",
        "created": 1714670277,
        "type": "difference.created"
    }
}
```

### Difference marked discrepancy

If we detect that a processing change has not been made by the carrier, or we find a data discrepancy during a sync, Noyo will send a `difference.discrepancy` event.

```json theme={"system"}
{
    "data": {
        "id": "eb73184b-ae3c-4411-aa82-3c3bd877cf78"
    },
    "event": {
        "id": "98144317-718a-45cd-be96-0a400ecbc32e",
        "created": 1714670277,
        "type": "difference.discrepancy"
    }
}
```

### Difference replaced

In cases where an open difference is replaced by another, Noyo will send an event that you can use to replace the association in your system.

```json theme={"system"}
{
    "data": {
        "id": "f50b03bb-c8ab-4dd9-92dc-5dcf77011e0c"
    },
    "event": {
        "id": "2ba88657-8bcc-4380-abc9-bbff85326ab2",
        "created": 1714670515,
        "type": "difference.replaced"
    }
}
```

### Difference resolved

When a change is completed successfully by the carrier or a discrepancy is resolved (whether due to a subsequent snapshot, a sync, or a manual portal update, Noyo will send a `difference.resolved` event.

<Warning>
  Once a difference is resolved, it cannot be reopened
</Warning>

```json theme={"system"}
{
    "data": {
        "id": "f50b03bb-c8ab-4dd9-92dc-5dcf77011e0c"
    },
    "event": {
        "id": "6b1b42fc-fc79-4a28-b94a-a63d3c9a611b",
        "created": 1714670515,
        "type": "difference.resolved"
    }
}
```

### Group Connection Request created

```json theme={"system"}
{
    "event": {
        "id": "1586f9b4-63f1-4c20-a965-6677beb04396",
        "type": "group_connection_request.created",
        "created": 1719343992
    },
    "data": {
        "id": "2cfcf643-540e-45b7-9007-912ed53edc42",
        "status": "processing"
    }
}
```

### Group Connection Request status changed

```json theme={"system"}
{
    "event": {
        "id": "2586f9b4-63f1-4c20-a965-6677beb04396",
        "type": "group_connection_request.status_change",
        "created": 1719343992
    },
    "data": {
        "id": "2cfcf643-540e-45b7-9007-912ed53edc42",
        "status": "action_required"
    }
}
```

### Group Disconnection Request created

```json theme={"system"}
{
    "event": {
        "id": "3586f9b4-63f1-4c20-a965-6677beb04396",
        "type": "group_disconnection_request.created",
        "created": 1719343992
    },
    "data": {
        "id": "4cfcf643-540e-45b7-9007-912ed53edc42",
        "status": "action_required"
    }
}
```

### Group Disconnection Request status changed

```json theme={"system"}
{
    "event": {
        "id": "4586f9b4-63f1-4c20-a965-6677beb04396",
        "type": "group_disconnection_request.status_change",
        "created": 1719343992
    },
    "data": {
        "id": "4cfcf643-540e-45b7-9007-912ed53edc42",
        "status": "processing"
    }
}
```

# Subscriptions

### Create a subscription

You can create webhook subscriptions in the Noyo web application by going to Developer > [Webhooks](https://app.noyo.com/developer/webhooks) and pressing "New subscription".

* Under "Destination", type the URL where you'd like to receive webhooks
* Under "Secret", type a string to use as a `secret` key. You should choose a random string of text with high entropy. You can use the webhook secret to limit incoming requests to only those originating from Noyo. You may optionally provide an API key name to assist in future key rotation

# Testing

<Warning>
  Webhooks are not available for sandbox environments
</Warning>

### Send a test event for a subscription

You can send a test request to any subscription through the Noyo app.

The payload will resemble the following:

```json theme={"system"}
{
    "data": {
        "message": "Hello world!"
    },
    "event": {
        "type": "test.hello_world",
        "created": 1715203664,
        "id": "beda9ea3-5136-4b7b-93be-37bf3fcc010a"
    }
}
```

# Best Practices

## Event delivery and ordering

**New event types will be continuously added; it is up to you to decide which events you want to process and how, but your implementation of the event listener must be able to handle new event types without breaking.**

We guarantee *at-least-once delivery* of events, meaning webhook endpoints may occasionally receive duplicate events. All event requests include an event ID, which may be used to deduplicate webhook events.

We *do not* guarantee delivery of events in the order they happen. You should also use the Noyo REST APIs to fetch any missing data.

## Verification

**Authenticate all requests to your webhook endpoint.**

Noyo includes an HMAC signature with each outgoing webhook request in an `x-noyo-signature` HTTP header. Endpoints should verify each request by: computing an expected HMAC signature for the payload and verifying that the value contained in the `x-noyo-signature` header provided by the sender matches the expected signature. [Read more](/docs/introduction/webhooks/webhook-verification).

## Responding to webhooks

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

### Subscription health and retries

* When an event fails to deliver, its queue will handle retrying with backoff 15 times over 3 days.
* When a webhook fails delivery, its subscription is moved into `unhealthy` status
  * If an event succeeds, the status is changed back to `active`
  * If an event comes in when a subscription has been unhealthy for 7 days, and that event fails, the subscription is changed to "failed." The subscription's queue is paused, which means that no other events will be attempted
