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

# Troubleshooting webhooks

> Diagnose and resolve webhook delivery problems.

## Delivery and retries

Zeam delivers each event by `POST` and expects a `2xx` response. A non-`2xx`
response, a timeout, or a connection error is treated as a failure and retried
with backoff. After the maximum number of attempts the delivery is
dead-lettered and no longer retried.

Current defaults (configurable by Zeam):

* Up to **3 attempts** per delivery, then dead-letter.
* **15-second** request timeout per attempt.
* Payloads larger than **128 KiB** are not delivered.

Because retries exist, your endpoint must be idempotent. See
[Security](/webhooks/security).

## Common scenarios

<AccordionGroup>
  <Accordion title="The endpoint cannot be reached">
    Confirm the URL is public, correct, and reachable from the internet, and
    that DNS resolves. Localhost and private addresses are not reachable by
    Zeam. After fixing, send a test event from the portal.
  </Accordion>

  <Accordion title="TLS or certificate errors">
    Use a valid, non-expired certificate from a trusted CA on a standard HTTPS
    port. Self-signed certificates are rejected in production.
  </Accordion>

  <Accordion title="The endpoint returns a non-2xx status">
    Return a `2xx` as soon as you have stored the event, then process it on a
    background worker. Slow processing before acknowledging risks a timeout and
    a retry.
  </Accordion>

  <Accordion title="Events are duplicated">
    Retries can deliver the same event more than once. De-duplicate on the
    `X-Webhook-Id` header and return `2xx` for an event you have already
    processed.
  </Accordion>

  <Accordion title="Events arrive later than expected">
    Delivery is asynchronous and retried with backoff, so events can lag and can
    arrive out of business-process order. Do not depend on timing or order;
    reconcile with the Transactions API.
  </Accordion>

  <Accordion title="A transaction changed state but no webhook arrived">
    Check that you have an **active** registration for that event type (there is
    one per association and event type, and disabling is permanent). The
    delivery may also have been dead-lettered after repeated failures. Poll
    [`GET /v1/transactions/{transactionId}`](/api-reference/introduction) to
    recover the current state.
  </Accordion>

  <Accordion title="The payload cannot be parsed">
    Read the raw request body and parse it as JSON (`Content-Type:
            application/json`). If you verify the signature, do so over the raw bytes
    before parsing.
  </Accordion>

  <Accordion title="Signature validation fails">
    Recompute the HMAC-SHA256 over the exact raw body using your association id
    as the key, and compare against the value after the `sha256=` prefix. A
    common cause is verifying against a re-serialised body. See
    [Security](/webhooks/security).
  </Accordion>

  <Accordion title="Sandbox and production URLs are confused">
    Registrations belong to a single environment. Confirm you registered the
    endpoint in the same environment your traffic runs in, and that each
    environment points at the correct URL.
  </Accordion>
</AccordionGroup>
