> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getdial.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getdial.ai/_mcp/server.

# Local URL target

> Fan Dial events out to a local HTTP endpoint with optional HMAC signing or bearer-token auth.

A **local URL target** is the HTTP-webhook side of Dial's fan-out. You register a loopback URL with the listen daemon and, for every account event, the daemon `POST`s the event JSON to that URL. Long-lived services on the same machine (an agent runtime, a workflow engine, a queue) pick the event up like any other webhook.

## When to use it

* The receiver is a process that's already running and listens on a loopback port.
* You want HMAC signing or a bearer token to authenticate Dial's POSTs.
* You're wiring into a product that already understands webhooks — most agent frameworks fall here.

If the receiver is a script you'd normally spawn from a shell, the [CLI command target](/integrations/methods/cli-command-target) is simpler and avoids running an HTTP server.

## Prerequisites

```bash
dial listen install
```

The listen daemon owns the fan-out queue and is what makes the outgoing POSTs. See [Listen service](/documentation/cli/listen-service).

## Loopback only

The URL **must** be `http://` or `https://` and resolve to a local loopback host — `127.0.0.1`, `0.0.0.0`, `localhost`, `::1`, or `[::1]`. Public URLs are rejected. This is intentional: Dial events stay on the machine the daemon runs on; cross-network delivery is a job for our forthcoming durable webhooks.

## Register

```bash
dial local-target add url http://127.0.0.1:8787/dial-events
```

For each event, the daemon issues:

```
POST http://127.0.0.1:8787/dial-events
Content-Type: application/json

<event_json>
```

See [`dial local-target add url`](/documentation/cli/commands#local-target-add-url) for the full reference.

## Authenticate the receiver

By default the POST has no auth — fine on loopback for casual setups, but most receivers want at least one of:

### HMAC signing

```bash
dial local-target add url http://127.0.0.1:8787/dial-events \
  --secret "$(cat ~/.config/my-receiver/dial.secret)" \
  --signature-header "X-My-Receiver-Signature"
```

The daemon computes `HMAC-SHA256(secret, body)` per request and puts the hex digest in the header named by `--signature-header` (defaults to `X-Dial-Signature`). The receiver recomputes the digest from the raw body and rejects mismatches. Good fit for systems that expect a per-subscription HMAC, like [Hermes Agent](/integrations/agent-clients/hermes-agent).

### Bearer token

```bash
dial local-target add url http://127.0.0.1:8787/dial-events \
  --bearer "$(cat ~/.config/my-receiver/dial.token)"
```

The daemon sends `Authorization: Bearer <token>` on every POST. Good fit for systems that gate the endpoint with a shared static token, like [OpenClaw](/integrations/agent-clients/openclaw).

You can also combine `--bearer` with `--secret` if the receiver wants both.

## Delivery semantics

* **2xx** — treated as delivered.
* **Non-2xx, connection error, or timeout** — the daemon retries the request **once**. If the retry also fails, the event is logged and dropped; nothing is queued for later.
* **Timeout** — `--timeout <seconds>` per attempt, defaulting to `5`.
* **Concurrency** — requests to the same target run independently; the daemon doesn't serialize them, so make your handler safe to run twice in close succession (the once-retry can produce that).
* The event stream the daemon consumes is **presence-based** (not at-least-once), so an event the daemon never received cannot be recovered from the server side. For guaranteed delivery, register an [at-least-once webhook](/documentation/platform/webhooks).

## Verify, list, remove

```bash
dial local-target list
# url  http://127.0.0.1:8787/dial-events
# cmd  /usr/local/bin/handle-dial-event

dial local-target remove http://127.0.0.1:8787/dial-events
```

No daemon restart is required for any of these — the fan-out registry updates on the fly.

## See also

#### [CLI command target](/integrations/methods/cli-command-target)

Run a local executable per event instead of POSTing JSON.

#### [Stream account events](/documentation/platform/stream-account-events)

The upstream stream that the listen daemon is fanning out from.