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

# Inbound webhook

> POST /v1/hooks/:token — trigger a render from any system that can POST JSON, with optional HMAC signing.

<Info>**POST** `https://api.craftkit.dev/v1/hooks/:token`</Info>

Triggers a render from any external system that can POST JSON. The webhook URL and HMAC secret are
auto-generated when a template is created. The body is **the variable data, flat** — no
`{ data: … }` wrapper. Inbound renders share the rendering pipeline with
[POST /v1/templates/:slug/render](/api-reference/render-template): same Zod validation, versioning,
dashboard row, and outgoing webhooks.

## Authorization

Inbound webhooks are **not** authenticated with a project API key. The per-template token in the URL
identifies the template; an optional HMAC signature proves the payload's integrity.

<ParamField header="x-craftkit-signature" type="string">
  Optional. Hex-encoded HMAC-SHA256 of the **raw request body**, keyed by the template's inbound
  secret. When present it is verified; when absent the request is accepted (toggle enforcement per
  template in the dashboard for production traffic).
</ParamField>

## Path parameters

<ParamField path="token" type="string" required>
  The per-template inbound token from the **Use this template → Inbound webhook** panel. Identifies
  and authenticates the target template.
</ParamField>

## Body

The body is the flat variable data — the manifest keys for the target template, with no envelope.
Scalars nest by dot-path; loops are keyed by their dot-free top-level key. Keys absent from the
manifest are stripped. Invalid payloads return `400 invalid_input_data` with the offending fields in
`issues.fieldErrors`.

<ParamField body="(manifest keys)" type="object" required>
  <Expandable title="example">
    <ParamField body="customer" type="object">Scalar values nested by dot-path.</ParamField>
    <ParamField body="items" type="array">A loop, keyed by its top-level manifest key.</ParamField>
    <ParamField body="total" type="number">A top-level scalar.</ParamField>
  </Expandable>
</ParamField>

## Response

`202` when a render is queued.

<ResponseField name="id" type="string">Render id (UUIDv7).</ResponseField>
<ResponseField name="status" type="string">Always `queued` on accept.</ResponseField>
<ResponseField name="pollUrl" type="string">`GET` this with your project bearer token to poll the render.</ResponseField>

## Errors

| Status | code                   | Meaning                                                       |
| ------ | ---------------------- | ------------------------------------------------------------- |
| 400    | `invalid_json`         | Body is not valid JSON.                                       |
| 400    | `invalid_input_data`   | Body did not match the template manifest (`issues` included). |
| 401    | `invalid_signature`    | `x-craftkit-signature` was present but did not match.         |
| 404    | `invalid_token`        | No template matches this inbound token.                       |
| 409    | `no_published_version` | Template has no published version yet.                        |
| 503    | `queue_unavailable`    | Render queue is temporarily unreachable — retry.              |

```bash cURL theme={null}
BODY='{"customer":{"name":"Acme Corp"},"items":[{"name":"Widget","qty":5,"price":9.99}],"total":49.95}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$CRAFTKIT_INBOUND_SECRET" -hex | awk '{print $2}')
curl -X POST "https://api.craftkit.dev/v1/hooks/$CRAFTKIT_INBOUND_TOKEN" \
  -H "x-craftkit-signature: $SIG" \
  -H "Content-Type: application/json" \
  -d "$BODY"
```

```json 202 theme={null}
{
  "id": "0193c2c3-...",
  "status": "queued",
  "pollUrl": "https://api.craftkit.dev/v1/renders/0193c2c3-..."
}
```
