Files
foreman/docs/adr/0014-no-webhooks-on-sync-chat.md
steve daf07fd759 feat: add async /jobs surface, state webhooks, and artifact handling
Add the async job submission API, webhook state notifications, and
artifact serving endpoints on top of the Phase 3 queue infrastructure.

Key changes:
- POST /jobs: async job submission with 202 + job_id ULID; optional
  state_webhook_url for push notifications on state transitions
- GET /jobs/{id}: job status polling with result, error, and artifact
  metadata; artifacts <= 256KB inlined, larger ones by URL reference
- GET /jobs/{id}/artifacts/{name}: raw artifact data serving
- Webhook dispatcher: at-least-once delivery with exponential backoff
  (5 retries); optional HMAC-SHA256 signing (X-Foreman-Signature)
- ADR-0014: state_webhook_url only honored on POST /jobs, not sync
  /api/chat (caller already blocks for result)
- Comprehensive tests for /jobs lifecycle, webhook delivery, HMAC
  verification, artifact inline/URL threshold, and TTL pruning

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:30:18 -04:00

1.5 KiB

ADR-0014: No webhooks on synchronous /api/chat

Status: Accepted -- 2026-05-23

Context

The state_webhook_url field exists on the async POST /jobs surface to notify callers of state transitions. When Phase 3 promoted /api/chat to route through the internal job queue, the question arose: should webhook events also fire for synchronous chat requests?

Decision

state_webhook_url is only honored on POST /jobs. Synchronous /api/chat requests do not fire webhooks, even though they internally create job rows.

Rationale

  • The /api/chat caller holds an open HTTP connection and blocks until the response is ready. Webhooks would be redundant: the caller already gets the result directly.
  • Adding webhook delivery on the sync path would double the webhook volume with no consumer benefit.
  • The sync path is the go-llm target; webhook handling would add latency and complexity to the critical hot path.
  • Callers who want webhooks should use POST /jobs explicitly.

Consequences

  • POST /jobs is the only entry point that supports state_webhook_url.
  • /api/chat job rows are created without a webhook URL and produce no webhook traffic.
  • This keeps the webhook dispatcher's load proportional to async job volume only.

Alternatives considered

  • Fire webhooks on both paths. Adds webhook traffic for every go-llm request with no consumer; rejected.
  • Optional opt-in header on /api/chat. Over-engineered for a passthrough endpoint; rejected.