Skip to content

Callback Contract

The Dispatcher POSTs a JSON payload to the Controller when a task reaches a terminal state.

Endpoint

POST /api/v1/tasks/<task-guid>/callback/

Authentication

Two layers, both checked before the payload is processed:

Bearer Token

Authorization: Bearer <token>

The token is generated at dispatch time and stored as a SHA-256 hash on the task record. The callback view hashes the incoming token and compares using constant-time comparison.

HMAC Signature (optional)

When CALLBACK_SIGNING_KEY is set, the Dispatcher must send:

X-Kohakku-Signature: sha256=<hex>

The signature is HMAC-SHA256(key, "<task-guid>:<raw-body>").

Timing attack prevention

Both checks use constant-time comparison to prevent timing attacks.

Payload Schema

Field Type Required Description
status string yes Terminal status: completed, failed, timed_out, or cancelled
exit_code integer or null no Process exit code. 0 = success. null if killed before exit
result_key string no Object storage key for the result artifact. Max 500 chars
result_metadata object no Arbitrary metadata (tokens used, duration, etc.)
error_message string no Human-readable error. Max 5000 chars
error string no Alias for error_message (backward compat). Max 5000 chars
completed_at string (ISO 8601) no When the task finished on the Dispatcher side
task_id string no Task UUID echoed back. Informational only
log_stream string no Log stream reference or URL. Max 1000 chars

Strict schema

Additional properties are not allowed. Unknown fields will be rejected.

Successful Completion

{
  "status": "completed",
  "exit_code": 0,
  "result_key": "results/550e8400-e29b-41d4-a716-446655440000/output.json",
  "result_metadata": {
    "tokens_used": 12450,
    "duration_seconds": 87
  }
}

Failure

{
  "status": "failed",
  "exit_code": 137,
  "error_message": "Container killed: OOM (memory limit 2Gi exceeded)"
}

Responses

200 OK

Callback accepted, task record updated.

{
  "status": "ok",
  "task_guid": "<uuid>"
}

400 Bad Request

Payload failed schema validation.

{
  "error": "Invalid callback payload.",
  "validation_errors": [
    "(root): 'status' is a required property"
  ]
}

403 Forbidden

Invalid token or HMAC signature.

404 Not Found

Task GUID does not exist.

429 Too Many Requests

Rate limit exceeded (100 requests/minute per IP).

Rate Limiting

Callbacks are rate-limited to 100 requests per minute per IP. The CallbackThrottle class in tasks/api.py enforces this.

Schema Definition

The canonical JSON Schema lives in controller/tasks/callback_schema.py as CALLBACK_SCHEMA. It is validated server-side using jsonschema before any task state is mutated.