Webhook Adapter
Overview
Mechanism: POST task payload to a tenant-configured URL. The external agent processes the task and POSTs the result back to callbackUrl (async phone-home pattern).
Why a webhook adapter:
- Enterprise tenants may have their own internal LLM inference infrastructure
- Enables integration with any agent runtime that can make HTTP calls — Python agents, Java services, GPU inference servers, or any future LLM
- Enables gradual migration: an enterprise can run their own agent while still being orchestrated by the control plane
This is the only async adapter — the control plane does not wait for the result inline; it receives it via callback when the external agent is done.
Configuration
interface WebhookAdapterConfig {
webhookUrl: string; // target endpoint for task delivery
authHeader: string; // e.g. 'Authorization'
authValue: string; // e.g. 'Bearer <secret>' — stored encrypted
timeoutMs: number; // how long to wait for the callback before timing out
}How Data Flows IN — POST to Webhook
POST https://agent-runner.internal.acmecorp.com/run
Authorization: Bearer <agent-runner-secret>
Content-Type: application/json
{
"taskId": "run_xyz789",
"tenantId": "ten_abc123",
"agentRole": "blog-writer",
"prompt": "<full assembled task prompt>",
"systemPrompt": "<system prompt with placeholders filled>",
"skillsContext": "<skills Markdown concatenated — no --add-dir available>",
"wakeReason": "new_task",
"sessionId": null,
"callbackUrl": "https://api.leadmetrics.io/api/agent-callback/run_xyz789",
"taskToken": "<JWT signed with run-scoped secret, expires in 1h>",
"timeoutMs": 180000
}How Data Flows OUT — The External Agent POSTs Back
POST https://api.leadmetrics.io/api/agent-callback/run_xyz789
Authorization: Bearer <taskToken>
Content-Type: application/json
{
"status": "completed",
"output": "# Blog Post\n\n...",
"sessionId": null,
"usage": { "inputTokens": 4200, "outputTokens": 1800 },
"error": null
}Callback Authentication — taskToken
- Short-lived JWT signed by the control plane at dispatch time
- Scoped to a single
runId— cannot be reused for other runs - Expires after the activity’s configured timeout
- The control plane validates the token before accepting the callback payload
Async Flow Diagram
Worker External Agent Control Plane API
│ │ │
│── POST /run ─────────────►│ │
│ (prompt + callbackUrl) │ │
│◄─ 202 Accepted ───────────│ │
│ │ (agent executes...) │
│ │── POST /agent-callback ──►│
│ │ (output + taskToken) │
│ │◄─ 200 OK ─────────────────│
│◄────────────────────────────────────── SSE event ─────│
│ (activity completed) │What Happens While Waiting
DispatchResult.callbackUrlis returned immediately (notextfield yet)- Activity status stays
in_progress - The callback handler at
/api/agent-callback/:runId:- Validates the JWT
- Stores the output in MongoDB
activity_streams - Updates the
activity_runsrecord - Broadcasts the completion event via SSE to any connected UI clients
Streaming
The webhook adapter does not support live text streaming. The entire output arrives in one payload at callback time. The UI shows a spinner until the callback arrives; then displays the full result at once.
Cost Source
Usage object from callback payload → MODEL_PRICING[model] × tokens if model pricing is known, else $0.00.