{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Webhooks","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"webhooks","__idx":0},"children":["Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Webhooks allow EasyPost Connect to notify your system when relevant business events occur, without requiring your application to repeatedly poll the API."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When an event occurs, EasyPost Connect sends an HTTPS ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," request to the webhook URL configured in your webhook subscription."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use webhooks to react to events such as:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["a job reaching a relevant final or action-required status;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["a sending event reaching a relevant business status;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["a generated file becoming available for download;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["an incoming Peppol document being received."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The exact webhook event payloads are documented in the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/public-api-production/openapi/webhook-events"},"children":["Webhook Events"]}," section of the API reference."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"end-to-end-flow","__idx":1},"children":["End-to-End Flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A typical integration flow is:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Configure a workflow in EasyPost Connect. In the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://eservices.easypost.eu/workflows"},"children":["EasyPost Connect Portal Workflows section"]},", 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."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/public-api-production/openapi/jobs/jobscontroller_createsinglefilejob"},"children":["Public API"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Monitor the job and related sending events."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Receive webhook notifications when relevant business events occur."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Retrieve additional data or files through the Public API when needed."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"webhook-subscriptions","__idx":2},"children":["Webhook Subscriptions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A webhook subscription defines where and when EasyPost Connect should send webhook notifications."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A subscription includes:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Item"},"children":["Item"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["URL"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["HTTPS endpoint that receives webhook ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," requests."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Events"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["List of event types the subscription should receive."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Secret"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Secret used to verify webhook signatures."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Name"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Internal name used to identify the subscription."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Webhook subscriptions can be managed through the Public API."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"testing-a-webhook-subscription","__idx":3},"children":["Testing a Webhook Subscription"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the webhook ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/public-api-production/openapi/webhook-subscriptions/webhookscontroller_testwebhook"},"children":["test endpoint"]}," to send a test event to your configured webhook URL."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"delivery-model","__idx":4},"children":["Delivery Model"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["EasyPost Connect provides ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["at-least-once delivery"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This means:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["the same event may be delivered more than once;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["events may not arrive in the exact order in which they occurred;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["your system must be able to safely handle duplicate events;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["your system should not rely on webhook delivery order."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each webhook event has a unique event ID. Use this ID for idempotency and deduplication."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Recommended approach:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Read the event ID from the request."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Check whether the event ID has already been processed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If already processed, return a successful ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," response."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If not processed, store the event ID and process the event."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Return a successful ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," response once the event has been accepted."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"event-types","__idx":5},"children":["Event Types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The available webhook event types are documented in the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Webhook Events"]}," section of the API reference."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["At a high level, EasyPost Connect can emit the following types of events:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Event type"},"children":["Event type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Purpose"},"children":["Purpose"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["job.status.changed"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Sent when a job reaches a webhook-relevant status."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sending.event.status.changed"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Sent when a sending event reaches a webhook-relevant business status."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sending.event.file.ready"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Sent when a file related to a sending event is available for download."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["peppol.document.received"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Sent when an incoming Peppol document is received or updated."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For each event type, refer to the API reference for the full payload schema, required fields, examples, and possible enum values."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"webhook-request-format","__idx":6},"children":["Webhook Request Format"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Webhook notifications are sent as HTTPS ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," requests with a JSON body."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each request includes signature-related headers that allow your system to verify that the request was sent by EasyPost Connect."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Headers include:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Header"},"children":["Header"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Connect-Signature"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Signature used to verify the webhook request."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Connect-Timestamp"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Timestamp used when generating the signature."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Connect-Event-Id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unique event ID. Use this value for idempotency and deduplication."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The JSON payload structure depends on the event type. See the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Webhook Events"]}," section of the API reference for the exact schemas."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"signature-verification","__idx":7},"children":["Signature Verification"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each webhook subscription has a secret. The secret is returned once when the subscription is created."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your webhook receiver should verify every request before processing it."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To verify a webhook request:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Read the raw request body exactly as received."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Read the value of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Connect-Timestamp"]}," header."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Build the signed payload using the following format:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"<timestamp>.<raw JSON body>\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"ol","attributes":{"start":4},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Compute the HMAC-SHA256 signature using the webhook secret."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Compare the computed signature with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Connect-Signature"]}," header."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use a constant-time comparison to avoid timing attacks."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Do not parse and re-serialize the JSON body before verifying the signature. Formatting changes may invalidate the signature."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"expected-response-from-your-endpoint","__idx":8},"children":["Expected Response from Your Endpoint"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your webhook endpoint should return a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," HTTP status code when the event has been accepted successfully."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any response outside the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," range is considered a failed delivery and may be retried."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Scenario"},"children":["Scenario"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Recommended response"},"children":["Recommended response"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Event accepted successfully"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["204 No Content"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Duplicate event already processed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["204 No Content"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Temporary internal error"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["500 Internal Server Error"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Endpoint temporarily unavailable"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["503 Service Unavailable"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Invalid signature"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401 Unauthorized"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["400 Bad Request"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Even if an event is a duplicate, return a successful ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," response once the duplicate has been safely ignored."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"retry-behavior","__idx":9},"children":["Retry Behavior"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["EasyPost Connect retries webhook deliveries when the receiver does not return a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," response."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Retry policy:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Rule"},"children":["Rule"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Retry interval"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Every 1 hour"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Retry duration"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Up to 24 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["After retry window"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The event is moved to a dead-letter queue and is no longer actively delivered"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Because non-",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," responses trigger retries, your endpoint should avoid returning errors for events that have already been safely received or deduplicated."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"recommended-receiver-logic","__idx":10},"children":["Recommended Receiver Logic"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A robust webhook receiver should:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Receive the webhook request."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Read the raw request body."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Verify the signature."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Read the event ID."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Check whether the event ID was already processed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If already processed, return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["204 No Content"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Store the event ID."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Queue the event for internal processing."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["204 No Content"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"file-related-events","__idx":11},"children":["File-Related Events"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Some webhook events indicate that a file is available, for example an ePOD or submission report."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The webhook notification does not contain the file itself. Instead, use the relevant Public API endpoint ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://easypost-connect-api.redocly.app/public-api-production/openapi/peppol-incoming-documents/peppolcontroller_retrievedownloadurl"},"children":["Retrieve Peppol document download URL"]}," or ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/public-api-production/openapi/sendingevents/sendingeventscontroller_retrievedownloadurl"},"children":["Retrieve Pre-signed URL to download Sending Event files"]}," to request a download URL for the file."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Download URLs are temporary. Your system should retrieve and store the file according to your own retention requirements."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"peppol-document-events","__idx":12},"children":["Peppol Document Events"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When an incoming Peppol document is received or updated, EasyPost Connect can notify your system through a webhook event."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the identifiers in the event payload to retrieve the document or its download URL through the Public API."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Refer to the Peppol Incoming Documents section of the API reference for the relevant endpoints."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"common-mistakes-to-avoid","__idx":13},"children":["Common Mistakes to Avoid"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Avoid the following implementation mistakes:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["processing a webhook before verifying the signature;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["parsing and re-serializing the JSON body before signature verification;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["returning an error for duplicate events that were already safely processed;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["assuming each event is delivered exactly once;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["assuming events always arrive in chronological order;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["performing slow processing before returning a response;"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["exposing a webhook endpoint that only works from a browser or requires an interactive login."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting","__idx":14},"children":["Troubleshooting"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If webhook delivery fails, check the following:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Issue"},"children":["Issue"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What to check"},"children":["What to check"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No request received"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify the subscription URL, DNS, firewall, and network access."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Signature validation fails"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Confirm that the raw request body is used and that the correct secret is configured."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Duplicate events received"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure event ID deduplication is implemented."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Events are retried"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Check that your endpoint returns a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2xx"]}," response after accepting the event."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["File cannot be downloaded"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Confirm that the correct file download endpoint is used and that the download URL has not expired."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/public-api-production/openapi/webhook-subscriptions/webhookscontroller_testwebhook"},"children":["Test webhook subscription"]}," endpoint to send a test event to your configured webhook URL and validate endpoint availability and signature handling before relying on production events."]}]},"headings":[{"value":"Webhooks","id":"webhooks","depth":1},{"value":"End-to-End Flow","id":"end-to-end-flow","depth":2},{"value":"Webhook Subscriptions","id":"webhook-subscriptions","depth":2},{"value":"Testing a Webhook Subscription","id":"testing-a-webhook-subscription","depth":2},{"value":"Delivery Model","id":"delivery-model","depth":2},{"value":"Event Types","id":"event-types","depth":2},{"value":"Webhook Request Format","id":"webhook-request-format","depth":2},{"value":"Signature Verification","id":"signature-verification","depth":2},{"value":"Expected Response from Your Endpoint","id":"expected-response-from-your-endpoint","depth":2},{"value":"Retry Behavior","id":"retry-behavior","depth":2},{"value":"Recommended Receiver Logic","id":"recommended-receiver-logic","depth":2},{"value":"File-Related Events","id":"file-related-events","depth":2},{"value":"Peppol Document Events","id":"peppol-document-events","depth":2},{"value":"Common Mistakes to Avoid","id":"common-mistakes-to-avoid","depth":2},{"value":"Troubleshooting","id":"troubleshooting","depth":2}],"frontmatter":{"seo":{"title":"Webhooks"}},"lastModified":"2026-06-11T12:18:59.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}