The webhook adapter exposes a generic JSON HTTP interface so that internal systems, scripts, and pipelines can send messages to your heypi agent over plain HTTP — no chat platform required. It is async-first, supports callback URLs for fire-and-forget workflows, and includes a sync mode for short blocking requests. The adapter registers routes on heypi’s shared Node HTTP listener and is inbound-only: scheduled jobs cannot target webhook adapters.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/hunvreus/heypi/llms.txt
Use this file to discover all available pages before exploring further.
Setup
The shared HTTP listener binds to
127.0.0.1:3000 by default. Set http.host: "0.0.0.0" only when an external reverse proxy, API gateway, or firewall handles public exposure.Routes
Whenname is "internal", the following routes are registered:
| Method | Path | Purpose |
|---|---|---|
POST | /webhook/internal | Start a new thread or send a threadless message |
POST | /webhook/internal/messages | Alias for the above |
POST | /webhook/internal/threads/:threadId/messages | Follow up on an existing thread |
GET | /webhook/internal/threads/:threadId/runs/:runId | Poll for run status |
name is omitted, the default is webhook and the prefix becomes /webhook/webhook. When running multiple webhook adapters in the same app, give each a unique name.
Authentication
Every request must include the shared secret in one of two ways:Limits
| Setting | Default | Description |
|---|---|---|
maxBodyBytes | 1,000,000 | Maximum request body size (bytes) |
maxInFlight | 32 | Maximum concurrent in-flight runs |
maxInFlight cap is in-process only — it is not a distributed rate limiter.
Request Body Fields
Every message request body may include:| Field | Type | Required | Description |
|---|---|---|---|
text | string | ✅ | The message text to process. Must be non-empty after trimming. |
user | string | — | Caller identity (used in logs and for cancel authorization). |
threadId | string | — | Existing thread to follow up on. Omit to start a new thread. |
eventId | string | — | Caller-supplied idempotency key; defaults to the generated runId. |
data | unknown | — | Arbitrary extra data forwarded to the agent handler. |
replyUrl | string | — | Callback URL for the completed run result (requires replyHosts). |
sync | boolean | — | Wait for the result in the same HTTP response (see Sync mode). |
timeoutMs | number | — | Per-request sync timeout in milliseconds (capped at 30 000). |
Start a Thread
OmitthreadId to create a new server-side thread. Heypi generates and returns an opaque threadId. Store it on the caller side for follow-ups.
202 Accepted):
Follow Up on a Thread
Send a follow-up message to an existing thread using thethreadId from the initial response:
Check Run Status
Poll for the result of a specific run:Async, Callback, and Sync Modes
- Async (default)
- Callback (replyUrl)
- Sync
All message endpoints return
202 Accepted immediately while the agent turn runs in the background. Poll the status endpoint to retrieve the final result.Approval Integration
When a turn is waiting for a human approval, the run response includes structured approval data that your caller can use to build UI buttons or display a confirmation prompt:user that started the active run, plus any configured approval.approvers.
Full Example
The following is the completeexamples/webhook-notes entry point — a minimal note-taking agent exposed over the webhook adapter:
Security Notes
- Never expose the webhook port directly to the public internet without a proxy.
- Rotate
secretimmediately if it is ever leaked or appears in logs. replyHostsis an exact-hostname allowlist. Wildcard or suffix matching is not supported — list each host explicitly.- Webhook is inbound-only. It does not implement
adapter.send(), so scheduled jobs and heartbeat jobs cannot target webhook adapters as a delivery channel.
Common Failures
401 Unauthorized
401 Unauthorized
400 text is required
400 text is required
The request body is missing the
text field or it is empty after trimming. Every message request must include a non-empty text string.400 replyUrl host is not allowed
400 replyUrl host is not allowed
The hostname in
replyUrl is not listed in replyHosts. Add the hostname to the replyHosts array in your adapter config and redeploy.429 too many in-flight webhook runs
429 too many in-flight webhook runs
The process has reached
maxInFlight concurrent runs. Either increase maxInFlight, reduce concurrency in the caller, or scale horizontally.404 run not found
404 run not found
The
threadId or runId does not exist in the current state store. This can happen if the state directory was deleted, the app was restarted with a different state.root, or the IDs were typed incorrectly.