Start a run for a platform-managed agent and block the HTTP response until the run reaches a terminal state — or until a bounded wait window elapses. Designed for agents whose value is the output payload itself: AI-rendered responses, structured reports, generated documents whose contents live inside a JSON payload. The agent output is returned in the same response, so the caller can hand it straight to the end user (download, render, forward).
Endpoint
POST /api/agents/{agentId}/runs/trigger-and-wait
Auth: Authorization: Bearer <ingest_api_key>
Eligibility
Same as /trigger:
managed_by = 'platform'status = 'active'allow_external_triggers = true
Runtime support
Both supported runtimes accept this endpoint, but they reach the result differently:
- n8n — the workflow must terminate in a Respond to Webhook node returning the agent output as JSON. The platform invokes the webhook synchronously and treats the response body as
output_data. - Trigger.dev — the platform triggers the task and subscribes to its real-time state via the Trigger.dev SDK. When the run reaches a terminal state the platform reads the task's return value as the agent output and surfaces it to the caller. Tasks invoked synchronously must
returntheir output — values reported only via the run callback won't appear in the synchronous response.
For both runtimes, the response shape and timeout semantics are identical.
Reporting the outputs metric
The response includes an outputs field (see the example below) — a non-negative integer count of agent-defined output units, used for billing and dashboards (see Run metrics if your SDK consumer is unfamiliar).
To populate it on the synchronous endpoint, include a top-level outputs: <integer> in your response (n8n) or return value (Trigger.dev), alongside the rest of the agent output:
// n8n "Respond to Webhook" body, or Trigger.dev task return value
{
"pdfUrl": "https://files.example.com/report.pdf",
"pages": 42,
"outputs": 1 // ← lifted into the dedicated metric column
}
The platform lifts the outputs value into the response's outputs field and into the runs.outputs column. The field is not stripped from output_data — anything reading it there continues to work. Anything that isn't a non-negative integer is silently dropped (same validation rule as the async callback flow).
Request
{
"input": { "topic": "Q3 financial summary" },
"timeoutMs": 120000
}
| Field | Type | Required | Description |
|---|---|---|---|
input | object | no | Merged on top of the agent's saved default input; caller's values win. |
userId | uuid | no | Attribution for runs.created_by. Unknown / non-member values are dropped silently. |
timeoutMs | number | no | Maximum time the server may wait, in milliseconds. Server further caps at the agent's running_timeout_ms and a platform hard ceiling. Defaults to 60 000 ms when omitted. |
Response — 200 OK (terminal)
The run reached a terminal state inside the wait window.
{
"runId": 1234,
"agentId": 42,
"status": "completed",
"output_data": { "pdfUrl": "https://files.example.com/report.pdf" },
"outputs": 1,
"error": null,
"duration_ms": 4231,
"started_at": "2026-05-26T12:00:00.123Z",
"completed_at": "2026-05-26T12:00:04.354Z",
"created_at": "2026-05-26T12:00:00.001Z"
}
status is one of "completed", "failed", or "cancelled". On "failed", output_data may still carry intermediate output the runtime wrote before failing; error carries the short failure reason.
Response — 202 Accepted (wait window elapsed)
The run did not reach a terminal state in time. It is still in flight — the existing async pipeline (callback + per-run poller) continues to drive it. The caller should fall back to polling.
{
"runId": 1234,
"agentId": 42,
"status": "running",
"timedOut": true
}
status is one of "queued", "running", or "waiting". Poll GET /api/agents/{agentId}/runs/{runId} (or, for zones, the equivalent zone endpoint) until terminal.
Response — 504 Gateway Timeout
The upstream runtime itself did not respond within the wait window (n8n only — Trigger.dev does not block on the trigger call). The run row is marked failed. The terminal-state guard on the callback endpoint prevents a late workflow completion from resurrecting it.
{
"error": "Runtime did not respond within the wait window",
"runId": 1234
}
Other errors
| Status | Reason |
|---|---|
401 | Missing or invalid bearer token. |
403 | Agent not eligible. |
422 | Agent is not platform-managed or not active. |
429 | Per-agent rate limit exceeded (stricter than /trigger because each call holds a connection). |
500 | Runtime invocation failed before the wait could begin. |
Choosing between /trigger and /trigger-and-wait
Use /trigger when | Use /trigger-and-wait when |
|---|---|
| The caller doesn't need the output in the response | The caller hands the output straight to the end user |
| The run may take longer than ~5 minutes | The run completes in seconds or low minutes |
| You'll poll, subscribe, or wait for a callback yourself | You want a single HTTP request that returns the result |
SDK
import { triggerRunAndWait } from '@cxpa/sdk/agent'
const result = await triggerRunAndWait({
baseUrl: process.env.CXPA_API_URL!,
ingestApiKey: process.env.CXPA_INGEST_KEY!,
agentId: 42,
input: { topic: 'Q3 financial summary' },
timeoutMs: 120_000,
})
if (result.timedOut) {
// Fall back to polling /api/agents/{agentId}/runs/{runId}
return { status: 202, runId: result.runId }
}
if (result.status === 'completed') {
// result.output_data has the agent payload
return { pdfUrl: result.output_data?.pdfUrl as string }
}
throw new Error(`Run ${result.status}: ${result.error}`)