# Webhooks

Webhooks allow EasyPost Connect to notify your system when relevant business events occur, without requiring your application to repeatedly poll the API.

When an event occurs, EasyPost Connect sends an HTTPS `POST` request to the webhook URL configured in your webhook subscription.

Use webhooks to react to events such as:

- a job reaching a relevant final or action-required status;
- a sending event reaching a relevant business status;
- a generated file becoming available for download;
- an incoming Peppol document being received.


The exact webhook event payloads are documented in the [Webhook Events](/public-api-production/openapi/webhook-events) section of the API reference.

## End-to-End Flow

A typical integration flow is:

1. Configure a workflow in EasyPost Connect. In the [EasyPost Connect Portal Workflows section](https://eservices.easypost.eu/workflows) click “Add Workflow” to create a workflow (configuration that will define how a sending will be sent). A workflow defines sending channels, templates, and the data that should be processed and sent.
2. Create a job. A job represents one execution of a workflow. When a job is created, Connect processes the job and may generate one or more sending events. A job can be created using the [Public API](/public-api-production/openapi/jobs/jobscontroller_createsinglefilejob).
3. Monitor the job and related sending events.
4. Receive webhook notifications when relevant business events occur.
5. Retrieve additional data or files through the Public API when needed.


A webhook event should usually be treated as a notification that something changed. Your system can then use the Public API to retrieve the latest state or download the relevant file.

## Webhook Subscriptions

A webhook subscription defines where and when EasyPost Connect should send webhook notifications.

A subscription includes:

| Item | Description |
|  --- | --- |
| URL | HTTPS endpoint that receives webhook `POST` requests. |
| Events | List of event types the subscription should receive. |
| Secret | Secret used to verify webhook signatures. |
| Name | Internal name used to identify the subscription. |


Webhook subscriptions can be managed through the Public API.

The subscription secret is generated by EasyPost Connect and returned only once when the webhook subscription is created. Store it securely. It is required to verify webhook signatures.

## Testing a Webhook Subscription

Use the webhook [test endpoint](/public-api-production/openapi/webhook-subscriptions/webhookscontroller_testwebhook) to send a test event to your configured webhook URL.

A successful test only confirms that EasyPost Connect can reach your endpoint and receive a response. Your production receiver should still implement idempotency, signature verification, and proper error handling.

## Delivery Model

EasyPost Connect provides **at-least-once delivery**.

This means:

- the same event may be delivered more than once;
- events may not arrive in the exact order in which they occurred;
- your system must be able to safely handle duplicate events;
- your system should not rely on webhook delivery order.


Each webhook event has a unique event ID. Use this ID for idempotency and deduplication.

Recommended approach:

1. Read the event ID from the request.
2. Check whether the event ID has already been processed.
3. If already processed, return a successful `2xx` response.
4. If not processed, store the event ID and process the event.
5. Return a successful `2xx` response once the event has been accepted.


## Event Types

The available webhook event types are documented in the **Webhook Events** section of the API reference.

At a high level, EasyPost Connect can emit the following types of events:

| Event type | Purpose |
|  --- | --- |
| `job.status.changed` | Sent when a job reaches a webhook-relevant status. |
| `sending.event.status.changed` | Sent when a sending event reaches a webhook-relevant business status. |
| `sending.event.file.ready` | Sent when a file related to a sending event is available for download. |
| `peppol.document.received` | Sent when an incoming Peppol document is received or updated. |


For each event type, refer to the API reference for the full payload schema, required fields, examples, and possible enum values.

## Webhook Request Format

Webhook notifications are sent as HTTPS `POST` requests with a JSON body.

Each request includes signature-related headers that allow your system to verify that the request was sent by EasyPost Connect.

Headers include:

| Header | Description |
|  --- | --- |
| `X-Connect-Signature` | Signature used to verify the webhook request. |
| `X-Connect-Timestamp` | Timestamp used when generating the signature. |
| `X-Connect-Event-Id` | Unique event ID. Use this value for idempotency and deduplication. |


The JSON payload structure depends on the event type. See the **Webhook Events** section of the API reference for the exact schemas.

## Signature verification

Each webhook subscription has a secret. The secret is returned once when the subscription is created.

Your webhook receiver should verify every request before processing it.

To verify a webhook request:

1. Read the raw request body exactly as received.
2. Read the value of the `X-Connect-Timestamp` header.
3. Build the signed payload using the following format:


```text
<timestamp>.<raw JSON body>
```

1. Compute the HMAC-SHA256 signature using the webhook secret.
2. Compare the computed signature with the `X-Connect-Signature` header.
3. Use a constant-time comparison to avoid timing attacks.


Do not parse and re-serialize the JSON body before verifying the signature. Formatting changes may invalidate the signature.

## Expected Response from Your Endpoint

Your webhook endpoint should return a `2xx` HTTP status code when the event has been accepted successfully.

Any response outside the `2xx` range is considered a failed delivery and may be retried.

| Scenario | Recommended response |
|  --- | --- |
| Event accepted successfully | `200 OK` or `204 No Content` |
| Duplicate event already processed | `200 OK` or `204 No Content` |
| Temporary internal error | `500 Internal Server Error` |
| Endpoint temporarily unavailable | `503 Service Unavailable` |
| Invalid signature | `401 Unauthorized` or `400 Bad Request` |


Even if an event is a duplicate, return a successful `2xx` response once the duplicate has been safely ignored.

## Retry Behavior

EasyPost Connect retries webhook deliveries when the receiver does not return a `2xx` response.

Retry policy:

| Rule | Value |
|  --- | --- |
| Retry interval | Every 1 hour |
| Retry duration | Up to 24 hours |
| After retry window | The event is moved to a dead-letter queue and is no longer actively delivered |


Because non-`2xx` responses trigger retries, your endpoint should avoid returning errors for events that have already been safely received or deduplicated.

## Recommended Receiver Logic

A robust webhook receiver should:

1. Receive the webhook request.
2. Read the raw request body.
3. Verify the signature.
4. Read the event ID.
5. Check whether the event ID was already processed.
6. If already processed, return `204 No Content`.
7. Store the event ID.
8. Queue the event for internal processing.
9. Return `204 No Content`.


For reliability, avoid doing long-running business logic directly inside the webhook HTTP request. Prefer accepting the event, storing it, queueing it internally, and processing it asynchronously.

## File-Related Events

Some webhook events indicate that a file is available, for example an ePOD or submission report.

The webhook notification does not contain the file itself. Instead, use the relevant Public API endpoint [Retrieve Peppol document download URL](https://easypost-connect-api.redocly.app/public-api-production/openapi/peppol-incoming-documents/peppolcontroller_retrievedownloadurl) or [Retrieve Pre-signed URL to download Sending Event files](/public-api-production/openapi/sendingevents/sendingeventscontroller_retrievedownloadurl) to request a download URL for the file.

Download URLs are temporary. Your system should retrieve and store the file according to your own retention requirements.

## Peppol Document Events

When an incoming Peppol document is received or updated, EasyPost Connect can notify your system through a webhook event.

Use the identifiers in the event payload to retrieve the document or its download URL through the Public API.

Refer to the Peppol Incoming Documents section of the API reference for the relevant endpoints.

## Common Mistakes to Avoid

Avoid the following implementation mistakes:

- processing a webhook before verifying the signature;
- parsing and re-serializing the JSON body before signature verification;
- returning an error for duplicate events that were already safely processed;
- assuming each event is delivered exactly once;
- assuming events always arrive in chronological order;
- performing slow processing before returning a response;
- exposing a webhook endpoint that only works from a browser or requires an interactive login.


## Troubleshooting

If webhook delivery fails, check the following:

| Issue | What to check |
|  --- | --- |
| No request received | Verify the subscription URL, DNS, firewall, and network access. |
| Signature validation fails | Confirm that the raw request body is used and that the correct secret is configured. |
| Duplicate events received | Ensure event ID deduplication is implemented. |
| Events are retried | Check that your endpoint returns a `2xx` response after accepting the event. |
| File cannot be downloaded | Confirm that the correct file download endpoint is used and that the download URL has not expired. |


Use the [Test webhook subscription](/public-api-production/openapi/webhook-subscriptions/webhookscontroller_testwebhook) endpoint to send a test event to your configured webhook URL and validate endpoint availability and signature handling before relying on production events.