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

# Signature verification

> There is no signature. Here's what to do instead.

No HMAC header, no `Tied-Signature`, no shared secret of any kind. A delivery is a plain `POST` with a JSON body and nothing added beyond content-type headers.

If you have integrated with a provider that signs its webhooks and expect to verify one the same way here, do not write code that waits for a header which will never arrive.

<Warning>
  This means **anyone who knows or guesses your webhook URL can POST a fake payload to it**, and your endpoint has no built-in way to tell it apart from a real one. Treat the URL itself as the secret, not the payload.
</Warning>

## What to do instead

<Steps>
  <Step title="Put a secret in the URL you register, not just a path">
    Register a URL with a long, random, unguessable token as a path segment or query parameter — `https://yourapp.com/webhooks/tied/f83a1e...` rather than `https://yourapp.com/webhooks/tied`. Reject anything that arrives without the exact token. This is the actual substitute for a missing signature: the secret lives in the URL instead of in a header.
  </Step>

  <Step title="Don't rely on IP allowlisting as your only control">
    If you want to restrict inbound connections by source IP as a secondary layer, check with Tied support first for whatever egress ranges currently apply to your instance — they aren't published in this reference, and infrastructure that isn't contractually static shouldn't be treated as if it were. Use it as a second layer behind the URL secret, not a replacement for it.
  </Step>

  <Step title="Treat the payload as a notification, not a source of truth">
    Because nothing proves authenticity or integrity in transit, don't take a consequential action — refunding a charge, escalating a ticket, closing an account — directly off the fields in a webhook body. Use the event as a trigger to call back into the API and read the current state of the resource with your own token, then act on what the API tells you.
  </Step>
</Steps>

## Why this matters more than it might for other APIs

Combined with there being [no retry on delivery failure](/api-reference/webhooks/testing), a webhook here is best treated as a **hint that something happened**, not an event log you can trust and replay. Build your integration so that:

* A forged payload can't do anything on its own — it can only prompt a `GET` that then either finds real corroborating state or doesn't.
* A missed delivery isn't silently fatal — pair webhooks with an occasional reconciliation poll for anything business-critical, since there's no redelivery to fall back on if your endpoint was down for the one attempt Tied made.

## Next

<CardGroup cols={2}>
  <Card title="Payload schemas" icon="braces" href="/api-reference/webhooks/payloads">
    What's actually in the body you can't fully trust.
  </Card>

  <Card title="Retries and testing" icon="refresh-cw" href="/api-reference/webhooks/testing">
    Why reconciliation polling matters: there's exactly one delivery attempt.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.