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

# HTTP Delivery

> Delivery semantics, signatures, retries, timeouts, and callback URLs.

## Delivery model

SafeFetch sends your action body to `url` using the configured `method` and headers.

* Any `2xx` response marks the action `completed`.
* Non-`2xx`, network errors, and timeouts trigger retries until `retries` is exhausted.

## Headers SafeFetch sends

<ParamField path="X-SafeFetch-Signature" type="string" required>
  HMAC signature in format `sha256=<hex>`.
</ParamField>

<ParamField path="X-SafeFetch-Action-Id" type="string" required>
  Action identifier.
</ParamField>

<ParamField path="X-SafeFetch-Attempt" type="integer" required>
  1-based attempt number.
</ParamField>

<ParamField path="Content-Type" type="string" required>
  Always `application/json`.
</ParamField>

## HMAC verification

Use your API key as the secret and the exact JSON body as input.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { createHmac, timingSafeEqual } from 'node:crypto';

  function verifySafeFetchSignature(rawBody: string, signatureHeader: string, apiKey: string): boolean {
    const expected = `sha256=${createHmac('sha256', apiKey).update(rawBody).digest('hex')}`;
    const a = Buffer.from(expected);
    const b = Buffer.from(signatureHeader || '');
    if (a.length !== b.length) return false;
    return timingSafeEqual(a, b);
  }
  ```

  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_safefetch_signature(raw_body: bytes, signature_header: str, api_key: str) -> bool:
      expected = "sha256=" + hmac.new(api_key.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
      return hmac.compare_digest(expected, signature_header or "")
  ```
</CodeGroup>

## Timeout behavior

<Warning>
  Default HTTP request timeout is 30 seconds (`DISPATCH_TIMEOUT_MS=30000`). Timeout is treated as a failed attempt and retried.
</Warning>

## Retry behavior for non-2xx

SafeFetch retries with exponential backoff and caps delay at 1 hour.

## Callback URLs

If `callback` is set on an action, SafeFetch sends a POST notification on terminal outcomes:

* `completed`: includes response code/body/duration
* `failed`: includes `last_error`
