The synchronous sibling of the webhook trigger, for callers whose protocol expects a meaningful response body rather than a bare acknowledgement — verification handshakes, challenge echoes, or providers that display the response to a user.
Authentication, forwarding, redaction, and eligibility are identical to the async endpoint: the platform does not authenticate the caller, the request reaches your agent verbatim, and your task must verify the caller itself. Read the webhook trigger first; this page covers only the differences.
Endpoint
POST /api/agents/{agentId}/runs/webhook-and-wait/{webhookToken}
Auth: none — the URL is the credential. Same token as the async endpoint.
Eligibility
Everything the async endpoint requires (managed_by = 'platform', status = 'active', allow_webhook_triggers = true), plus a runtime that supports synchronous triggering. n8n workflows qualify and must terminate in a "Respond to Webhook" node. Trigger.dev agents return 422.
Request
Identical to the async endpoint — any body up to 1 MiB, forwarded as-is. There are no input or timeoutMs fields: a webhook caller sends its own payload, and the wait window is resolved server-side from the agent's running_timeout_ms override and the platform ceiling.
Response — 200 OK
By default the response body is the agent's output, returned directly rather than wrapped in a platform run envelope — a third-party caller expects its own contract, not ours.
{
"processed": 12,
"reportUrl": "https://example.com/r/abc"
}
Controlling the response
When your caller needs a specific status code, header, or a non-JSON body, return a top-level webhookResponse object in the agent's output and the platform will use it to shape the HTTP response:
{
"webhookResponse": {
"status": 200,
"headers": { "x-provider-ack": "1" },
"body": "challenge-token-echoed-back"
}
}
| Field | Type | Description |
|---|---|---|
status | number | Clamped to 200–499. An agent cannot make the platform emit a 5xx (which would tell a retrying caller the platform failed) or a 3xx (which would make the endpoint an open redirector). |
headers | object | String values only. content-length, set-cookie, and hop-by-hop headers are dropped. |
body | any | A string is returned as text/plain, so challenge-echo flows that must reply with a bare token rather than a JSON string work. Anything else is returned as JSON. |
Other responses
| Status | Body | Meaning |
|---|---|---|
202 | { "runId": 1234, "status": "running", "timedOut": true } | The wait window elapsed. The run continues asynchronously — this is a 2xx on purpose, since a retrying caller would start a second run. |
502 | { "error": "…", "runId": 1234 } | The run failed or was cancelled. The platform worked; the agent did not. |
504 | { "error": "…", "runId": 1234 } | The runtime did not respond within the wait window. |
Errors
| Status | Reason |
|---|---|
401 | Unknown agent, or the token in the URL does not match. |
403 | allow_webhook_triggers is off for this agent. |
413 | Request body exceeds 1 MiB. |
422 | Agent is not platform-managed, is not active, or its runtime does not support synchronous triggering. |
429 | Rate limit exceeded — 10 requests/min per agent, stricter than the async endpoint because each call holds a connection open for the whole wait window. |
Choosing between the two
Use the async webhook trigger unless the caller genuinely needs the output in the response. Google Pub/Sub only needs a fast 2xx acknowledgement, so the async endpoint is the right choice there — and it does not tie up a connection or depend on the runtime finishing in time.