# Custom Headers

Send the credential your webhook endpoint requires with every Linked API delivery – up to 10 custom headers per webhook, retries and replays included.

> The API Reference is also the SDK and CLI reference. Consecutive code fences marked `tab=HTTP`, `tab=Node`, `tab=Python` and `tab=CLI` are the same call on each surface (a fence marked `tab=HTTP,CLI` is the same text for both), and a block between `:::surface <names>` and `:::` applies only to the surfaces it names. Identifiers in the text are written as in Node and in HTTP JSON (camelCase); the Python SDK spells methods, parameters and result attributes in snake_case (`sendMessage` → `send_message`, `reconnectionLink` → `reconnection_link`), while names inside HTTP JSON stay camelCase.

If your endpoint requires authentication, put its credential here and Linked API sends it with every delivery – retries, test events and replays included. Up to 10 headers per webhook.

Add them in the [dashboard](https://app.linkedapi.io/webhook) under **Advanced**, or through [`webhook.setHeader`](/docs/admin-webhooks#webhook-setheader). In the dashboard a saved header shows its name with the value masked; **Replace value** opens an empty field, because the value cannot be read back.

:::surface http
Each call of [`webhook.setHeader`](/docs/admin-webhooks#webhook-setheader) adds or replaces one header; [`webhook.deleteHeader`](/docs/admin-webhooks#webhook-deleteheader) removes one and [`webhook.setHeaders`](/docs/admin-webhooks#webhook-setheaders) replaces or clears them all:
:::

:::surface node
In the Node SDK the header endpoints are methods of `admin.webhooks`: `setHeader()` adds or replaces one header, `deleteHeader()` removes one and `setHeaders()` replaces or clears them all:
:::

:::surface python
In the Python SDK the header endpoints are methods of `admin.webhooks`: `set_header()` adds or replaces one header, `delete_header()` removes one and `set_headers()` replaces or clears them all:
:::

:::surface cli
The CLI keeps header values out of your terminal history and logs – they never go on the command line:

- `set-header` reads the value from stdin with `--value-stdin`, removing one trailing newline, or from the environment variable named by `--value-env`; exactly one is required.
- `set --headers-stdin` and `set-headers --stdin` read a JSON object of names and values from stdin.
- An empty value is refused with exit code `5` before anything is sent, whether it comes from stdin, the environment variable or the JSON object, and so is an environment variable that is not set. Remove a header with `delete-header`, or clear them all with `set-headers --clear` or `{}`.
:::

```bash tab=HTTP
curl -X POST https://api.linkedapi.io/admin/webhook.setHeader \
  -H "Content-Type: application/json" \
  -H "linked-api-token: linked_your_token_here" \
  -d '{ "id": "whs-...", "name": "Authorization", "value": "Bearer your_endpoint_token" }'
```

```typescript tab=Node
await admin.webhooks.setHeader({
  id: "whs-...",
  name: "Authorization",
  value: "Bearer your_endpoint_token",
});
```

```python tab=Python
from linkedapi import SetWebhookHeaderParams

admin.webhooks.set_header(
    SetWebhookHeaderParams(
        id="whs-...",
        name="Authorization",
        value="Bearer your_endpoint_token",
    )
)
```

```bash tab=CLI
cat endpoint-token.txt | linkedin admin webhook set-header whs-... Authorization --value-stdin
linkedin admin webhook set-header whs-... Authorization --value-env ENDPOINT_TOKEN
```

A webhook with no headers configured sends exactly what it always sent: `content-type: application/json`, plus the signature headers if signing is on.

**Values are write-only.** Reading the webhook returns `headerNames` – the names you configured – and never the values. That is also why headers are set one at a time: replacing the whole map would force you to re-enter every credential you were not changing.

Limits, all enforced when you save:

| Rule | Value |
| --- | --- |
| headers per webhook | 10 |
| value length | 1024 characters |
| all headers, serialized | 4096 bytes |
| name | an RFC 7230 token |
| value | tab and printable ASCII only |

Reserved names, matched case-insensitively and rejected: `content-type`, `content-length`, `host`, `connection`, `transfer-encoding`, `expect`, `upgrade`, and any name beginning `linked-api-`. Also reserved: `get`, `post`, `put`, `patch`, `delete`, `head` and `common` – our HTTP client treats those as its own configuration keys rather than as headers, so one of them would be sent under the wrong name or dropped. Two names differing only in case are rejected for the same reason.

> Header values are stored unencrypted, and our API responses never return them. One exception is worth knowing before you choose what to put here: if a request body is **invalid JSON**, the parser's error quotes about ten characters around the fault, and that text is both returned in the `400` and written to our logs. The usual way to hit it is a quoting slip in `curl` that sends the value unquoted. So prefer a credential scoped to webhook ingestion, and rotate it on your side as you would any shared secret. We never follow redirects, so your credential cannot be forwarded to another host by a `3xx` from your endpoint.
