# Webhooks

Source: https://www.hyperagent.com/docs/concepts/agents/invocations/webhooks

> For AI agents: the documentation index is at https://www.hyperagent.com/llms.txt and the complete documentation in one file at https://www.hyperagent.com/llms-full.txt. Any docs page is also served as Markdown by appending `.md` to its URL.

Trigger an agent with an HTTP POST: configure the prompt and thread, secure the endpoint, and understand the asynchronous response.

Some work should start the moment an event happens somewhere else: a pull request opens, a form is submitted, a CI job fails, or a CRM record changes. A **webhook endpoint** turns that event into an agent run. The system that saw the event sends an HTTP POST, and the agent starts with your instructions and the payload in hand.

POST a payload to the endpoint URL and the agent runs on it. A shared secret
authenticates the caller, and Hyperagent acknowledges the request before the
run finishes.

## How webhooks work [#how-webhooks-work]

GitHub, your CRM, a form service, or your own backend sends the event body
to a URL created for this agent.

Authenticated endpoints require a shared secret in the request header.
Public endpoints trade that protection for broader compatibility.

Your standing prompt tells the agent what to do. Hyperagent appends the raw
request body and starts the run in the configured thread.

A successful call returns `202 Accepted` with a run ID. The work continues
asynchronously and its result lands in the run's thread.

## Create and call a webhook [#create-and-call-a-webhook]

On the agent's **Invocations** tab, open **Webhook Endpoints**. Each endpoint has its own URL, prompt, secret, and thread strategy, so one agent can support several event sources without mixing their configuration.

### Create the endpoint [#create-the-endpoint]

Add an optional name, write the prompt every event should follow, choose a thread strategy, and decide whether the caller must authenticate.

### Save the secret [#save-the-secret]

Authenticated endpoints reveal their secret once. Copy it into the calling system before closing the confirmation.

### Copy the URL [#copy-the-url]

The endpoint card shows the complete URL and a ready-to-run curl example.

### Send a POST [#send-a-post]

Add the secret header when required, send a body no larger than 1 MB, and treat `202 Accepted` as confirmation that the run was queued.

## Webhook configuration options [#webhook-configuration-options]



### Endpoint URL [#endpoint-url]

Every endpoint accepts one method at one URL:

```text
POST /api/webhooks/{endpointId}/receive
```

The endpoint ID is a long random identifier created with the endpoint, which makes the URL impractical to guess. Only POST is accepted. The complete URL appears on the endpoint card with a copy button and curl example.



## Secure the endpoint [#secure-the-endpoint]

Authenticated endpoints use a shared secret. The caller sends it in a header, and Hyperagent checks it before creating a run.

### Send the secret [#send-the-secret]

```bash
curl -X POST https://your-workspace.hyperagent.com/api/webhooks/{endpointId}/receive \
  -H "X-Hyperagent-Webhook-Secret: {secret}" \  # [!code highlight]
  -H "Content-Type: application/json" \
  -d '{"event": "pull_request.opened", "number": 42}'
```

Hyperagent generates the secret from 32 random bytes when the endpoint is created. The UI shows it once with the warning **Save this secret now—it won't be shown again** and tells you to send it as `X-Hyperagent-Webhook-Secret`.

The server stores only a SHA-256 hash, never the original secret. For each call, it hashes the submitted header and uses a timing-safe comparison against the stored value so a caller can't learn the secret from response timing.

Copy it into the calling system before closing the dialog. A lost secret can't
be recovered; it must be rotated.

### Rotate a secret [#rotate-a-secret]

Select **Rotate secret** on the endpoint card to generate a new 32-byte secret. It is shown once, and the old secret stops working immediately. Only the endpoint owner can rotate it. Update the calling system as soon as you rotate.

Rotate when the secret may have leaked or on the schedule required by your security practice.

### Understand the security boundary [#understand-the-security-boundary]

Hyperagent verifies that the submitted secret matches. It does not verify an
HMAC signature of the request body.

A captured valid request can be sent again and will start another run. There
are no timestamps or nonces in the authentication check.

Any host with the URL and secret can call the endpoint. Source-IP
restrictions aren't part of this authentication model.

This model works when the caller can hold a secret in a header. If your integration requires signed bodies, replay protection, or source-IP restrictions, those checks need to happen before the request reaches this endpoint.

### Unauthenticated endpoints [#unauthenticated-endpoints]

Enable &#x2A;*Allow unauthenticated calls (no secret required)** only when the caller can't send a custom header and you accept a public endpoint.

If the URL is shared, placed in client-side code, or otherwise leaked, third
parties can trigger runs and incur charges on your account. Use this only for
public integrations where that risk is acceptable.

Hyperagent adds two protections to unauthenticated endpoints:

* A separate **pre-parse rate limit** rejects excess anonymous calls before the body is read.
* Runs receive lower trust, which **suppresses memory auto-accept**. Memories proposed during those runs aren't saved automatically. Authenticated calls are treated as owner-facing and don't carry that restriction.

Only the owner can select **Add a secret** on the endpoint card. Doing so immediately returns the endpoint to header-based authentication.



### Rate limits [#rate-limits]

Two independent sliding windows protect each endpoint. Both default to **60 calls per minute** and are keyed to the endpoint, not the caller's IP:

* The **invocation limit** applies to authenticated and unauthenticated endpoints. It runs after the size check but before a run is created.
* The additional **unauthenticated limit** applies only when no secret is required. It runs before the body is read, making anonymous floods cheaper to reject.

Crossing either window returns `429` with a `Retry-After` header. The first rejection in a window is also recorded as a skipped invocation in the endpoint's history.

## Understand the request and response [#understand-the-request-and-response]

### Request body [#request-body]

The body can contain any data your caller sends, up to **1 MB**. There is no required schema. Hyperagent appends the raw body to the configured prompt inside a boundary the agent can distinguish from your instructions:

```text
{your configured prompt}

<webhook-payload>
{the raw request body}
</webhook-payload>
```

The request body isn't parsed or validated on the way in; it reaches the agent as text, exactly as sent. Any `</webhook-payload>` sequence inside the body is neutralized before delivery so incoming data can't close the wrapper and present itself as instructions.

### Successful response [#successful-response]

A successful request returns immediately with **202 Accepted**:

```json
{
  "runId": "clw3x8k2p0001abcd1234wxyz", // [!code highlight]
  "status": "accepted"
}
```

The response means the run has been queued, not completed. The run ID identifies it in [Recent invocations](#review-recent-invocations); it isn't a public polling handle.

### Status codes [#status-codes]

Every non-202 response explains why the call didn't start a run:

| Code | Meaning                                                                                                                                                                             |
| ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 202  | Accepted. The run is queued; the body contains `runId` and `status: "accepted"`.                                                                                                    |
| 401  | The secret header is missing on an endpoint that requires it. An unknown endpoint also returns this when no header was sent, so a wrong URL can't be distinguished from a real one. |
| 403  | The secret is present but wrong, or the endpoint's secret hasn't been generated yet.                                                                                                |
| 404  | No endpoint with that ID exists, returned when a header was sent.                                                                                                                   |
| 413  | The payload is larger than 1 MB.                                                                                                                                                    |
| 422  | The endpoint is paused, or the agent is archived or otherwise unavailable.                                                                                                          |
| 429  | A rate limit was exceeded. The response includes `Retry-After`.                                                                                                                     |
| 503  | Webhook triggering is disabled for the deployment.                                                                                                                                  |

### Fire-and-forget delivery [#fire-and-forget-delivery]

There is no public endpoint for fetching the finished result. The run ID helps the owner find the run in Hyperagent, and the output lives in the run's thread—not in the HTTP response and not somewhere the caller can poll.

If the caller can act as an MCP client and needs the agent's response
directly, use the agent's [MCP
server](https://www.hyperagent.com/docs/concepts/agents/invocations/mcp-server) instead. Keep the
webhook when an immediate acknowledgment is enough, and have the agent deliver
the outcome another way: comment on the pull request, post to Slack, update
your database, or call an API.

## Choose a thread strategy [#choose-a-thread-strategy]

Every webhook run happens in a thread. The strategy decides whether events remain independent or build on the same history.

Each event gets a clean thread with the agent's instructions, skills, and
memories but no history from previous events. Use it for independent work,
such as triaging each pull request separately.

Every event is appended to one thread you choose, so the agent sees what
earlier events left there. The target must already belong to this agent.

New threads are named `Webhook: {agentName}` by default. Add a **Thread naming hint** to derive a more useful title from the payload—for example, `Name it after the PR number`. Leave it empty to keep the default.

## Operate and monitor an endpoint [#operate-and-monitor-an-endpoint]

Each endpoint keeps its own configuration and invocation history, so you can debug or pause one integration without affecting the agent's other event sources.

### Review Recent invocations [#review-recent-invocations]

Expand **Recent invocations**, shown with the current run count on the endpoint card, to see its latest runs, most recent first. This is the first place to look when the caller received `202` but no outcome appeared where expected.

| Field             | What it tells you                                                    |
| ----------------- | -------------------------------------------------------------------- |
| **Status**        | `running`, `success`, `error`, or `skipped`.                         |
| **Started at**    | When the run began.                                                  |
| **Completed at**  | When it finished, when available.                                    |
| **Duration**      | How long the run took.                                               |
| **Error message** | Why it failed or was skipped, when a reason exists.                  |
| **Thread**        | The thread where the run worked and where its complete output lives. |

A `skipped` invocation means Hyperagent declined to start the run, most often because of a rate limit or because the endpoint owner is no longer in the workspace. An `error` means the run started and then failed; its thread contains the full context.



### Pause, resume, or delete [#pause-resume-or-delete]

Pausing preserves the endpoint's URL, secret, configuration, and history while stopping new runs. Calls return `422` with `Endpoint is paused`. Resume the endpoint when you're ready to accept calls again.

Deleting an endpoint is immediate and permanent. The endpoint and its invocation records are removed, and the URL stops working. Deleting the agent removes all of its endpoints too.

### Ownership on team agents [#ownership-on-team-agents]

Every endpoint has one runtime owner, and its runs use that person's account and billing. All members of a shared agent can see its endpoints, but only the owner can change an endpoint's prompt, thread strategy, or secret.

The next call is skipped with an owner-not-a-member note, and the endpoint
pauses automatically. Another member can activate it to take ownership.
Archiving the agent keeps its endpoints but makes calls return `422` until the
agent is available again.

## Troubleshooting and FAQs [#troubleshooting-and-faqs]

No. The endpoint returns `202 Accepted` with a run ID before the run starts, and there is no public endpoint to poll. The result lives in the run's thread. If the caller can use MCP and needs the response directly, use the agent's [MCP server](https://www.hyperagent.com/docs/concepts/agents/invocations/mcp-server) instead. Otherwise, have the webhook-triggered agent deliver the outcome to the calling system itself.

You can't. Hyperagent stores only its SHA-256 hash, so no screen can recover the original. Rotate the endpoint to a new secret and update the calling system.

Only for public integrations where you accept that anyone with the URL can trigger the agent and incur charges. These endpoints get an early rate limit and their runs don't auto-accept memories, but the URL is still the only access barrier. Keep the prompt narrow and add a secret as soon as the caller can send one.

The endpoint exceeded a rate limit, which defaults to 60 calls per minute. Use the response's `Retry-After` header to decide when to retry. Unauthenticated endpoints have a second, earlier limit in addition to the shared invocation limit.

No. Authentication uses the `X-Hyperagent-Webhook-Secret` header, not an HMAC signature of the body. There is no replay protection or IP allowlist. If you need signed payloads or network restrictions, enforce those before forwarding the request to Hyperagent.

The endpoint runs under its owner. If that owner leaves, the next call is skipped and the endpoint pauses itself. Another member can activate it to become the new owner.
