V2 Runtime API
The V2 Runtime API provides governed agent orchestration — task lifecycle, model routing, artifact validation, lineage tracking, and human-in-the-loop gates. All V2 data lives in the hd_runtime PostgreSQL schema.
V2 endpoints require HATIDATA_V2_RUNTIME_ENABLED=true. When disabled, all V2 endpoints return 404.
Base URL
https://api.hatidata.com/v2
All V2 endpoints are prefixed with /v2. Authentication is the same as V1 (JWT or API key).
Rate Limits
V2 endpoints share the same rate limits as V1:
| Scope | Default | Env Var |
|---|---|---|
| Per organization | 600/min | HATIDATA_RATE_LIMIT_ORG |
| Per API key | 200/min | HATIDATA_RATE_LIMIT_KEY |
Every response includes RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers.
Tasks
Tasks are the top-level unit of work. Each task has a lifecycle (pending → active → completed/failed/cancelled) and can have multiple attempts.
Create Task
POST /v2/runtime/tasks
{
"kind": "code_review",
"project_id": "proj-abc-123",
"work_item_key": "run-1:code_review:default:main",
"max_retries": 3,
"metadata": {
"phase": "build",
"agent_type": "code_reviewer"
}
}
Idempotent dispatch: If work_item_key is set, only one non-terminal task can exist per key per org. Creating a task with a key that already has a pending/active task returns the existing task instead of creating a duplicate. Enforced by a partial unique index in the database.
Response: 200 OK
{
"id": "70e8d8bb-b924-4be1-8015-cce149657d01",
"org_id": "604d4d37-025c-4426-83b7-454cf4d78dba",
"kind": "code_review",
"status": "pending",
"project_id": "proj-abc-123",
"work_item_key": "run-1:code_review:default:main",
"max_retries": 3,
"metadata": { "phase": "build", "agent_type": "code_reviewer" },
"latest_attempt": null,
"created_at": "2026-04-05T03:55:38.587119Z",
"updated_at": "2026-04-05T03:55:38.587119Z"
}
List Tasks
GET /v2/runtime/tasks
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status: pending, active, completed, failed, cancelled |
project_id | string | — | Filter by project ID (uses indexed column) |
limit | integer | 50 | Max results (capped at 200) |
offset | integer | 0 | Pagination offset |
curl "https://api.hatidata.com/v2/runtime/tasks?project_id=proj-abc&status=active&limit=20" \
-H "Authorization: ApiKey hd_live_..."
Always use project_id when polling for a specific project's tasks. Without it, the query scans all tasks for the org.
Get Task
GET /v2/runtime/tasks/:task_id
Returns the task with its latest attempt.
Get Task Detail (Aggregate)
GET /v2/runtime/tasks/:task_id/detail
Returns the full task with all attempts, decisions, invocations, artifacts, events, and reviews in a single response. Use this instead of making separate API calls per entity type.
{
"id": "70e8d8bb-...",
"kind": "code_review",
"status": "completed",
"attempts": [
{ "id": "...", "agent_id": "fleet-001", "status": "succeeded", ... }
],
"decisions": [
{ "id": "...", "model_id": "claude-sonnet-4-6", "confidence": 0.92, ... }
],
"invocations": [
{ "id": "...", "input_tokens": 2048, "output_tokens": 1024, "cost_usd": 0.012, "latency_ms": 450, ... }
],
"artifacts": [
{ "id": "...", "kind": "code", "confidence": 0.94, "validation_status": "pass", ... }
],
"events": [
{ "id": "...", "kind": "attempt_lifecycle", "sequence_num": 1, "payload": {"action": "started"}, ... }
],
"reviews": []
}
Cancel Task
POST /v2/runtime/tasks/:task_id/cancel
Cancels the task and all its active attempts. Used for L4 recovery escalation when retry budget is exhausted.
{
"reason": "Retry budget exhausted, no repair possible"
}
Returns 409 Conflict if the task is already terminal.
Attempts
Attempts represent individual execution runs of a task. Each attempt has a lease (heartbeat-based) and transitions through: pending → running → succeeded/failed/timed_out/cancelled.
Create Attempt
POST /v2/runtime/tasks/:task_id/attempts
{
"agent_id": "fleet-compliance-scanner-03"
}
A lease token is automatically issued. The agent_id triggers best-effort agent self-registration (trust_level: provisional).
Task status transition: When an attempt fails with retries remaining (retry_count < max_retries), the task transitions back to pending — making it available for the next agent to claim.
Claim Attempt
PUT /v2/runtime/attempts/:attempt_id/claim
{
"lease_token": "uuid-of-lease",
"agent_id": "fleet-compliance-scanner-03"
}
Heartbeat
PUT /v2/runtime/attempts/:attempt_id/heartbeat
Extends the lease. Call every 60–120 seconds to prevent lease expiry timeout.
{
"lease_token": "uuid-of-lease"
}
Complete Attempt
PUT /v2/runtime/attempts/:attempt_id/complete
{
"lease_token": "uuid-of-lease",
"outcome": "succeeded"
}
Or for failure:
{
"lease_token": "uuid-of-lease",
"outcome": "failed",
"failure_reason": "Budget exceeded: 16000 tokens > 8192 limit"
}
Decisions & Invocations
Record Decision
POST /v2/runtime/decisions
{
"attempt_id": "uuid",
"model_id": "claude-sonnet-4-6",
"routing_reason": "cost_optimized",
"confidence": 0.91,
"fallback_used": false,
"cost_estimate": 0.008
}
Record Invocation
POST /v2/runtime/invocations
{
"decision_id": "uuid",
"model_id": "claude-sonnet-4-6",
"input_tokens": 2048,
"output_tokens": 1024,
"cost_usd": 0.012,
"latency_ms": 450
}
Artifacts
Publish Artifact
POST /v2/runtime/artifacts
{
"attempt_id": "uuid",
"kind": "code",
"content_hash": "sha256:a8f2c901...",
"confidence": 0.94,
"artifact_key": "proj:abc:architecture"
}
List Artifacts
GET /v2/runtime/artifacts
| Parameter | Type | Description |
|---|---|---|
task_id | UUID | Filter by task (across all attempts) |
project_id | string | Filter by project (across all tasks) — uses indexed column |
kind | string | Filter by artifact kind |
limit | integer | Max results (default 100, max 1000) |
Either task_id or project_id is required.
Promote Artifact
POST /v2/runtime/artifacts/:artifact_id/promote
Promotes a branched artifact to mainline by setting branch_id = null. Used at phase boundaries to merge branched work so downstream phases can find artifacts without branch-awareness.
- In-place update: artifact ID does not change (preserves lineage)
- Idempotent: promoting an already-mainline artifact is a no-op
- Preserves: validation_status, confidence, content_hash, artifact_key
Record Validation
POST /v2/runtime/artifacts/:artifact_id/validations
Events
Append-only workflow events. Sequence numbers are assigned atomically per attempt.
Ingest Event
POST /v2/runtime/events
{
"attempt_id": "uuid",
"kind": "attempt_lifecycle",
"payload": { "action": "started", "agent": "fleet-001" }
}
List Events
GET /v2/runtime/events
| Parameter | Type | Description |
|---|---|---|
task_id | UUID | Required. Filter by task (across all attempts) |
kind | string | Filter by event kind |
limit | integer | Max results (default 100, max 1000) |
Reviews & Releases
Request Review
POST /v2/reviews
List Reviews
GET /v2/reviews
| Parameter | Type | Description |
|---|---|---|
task_id | UUID | Filter reviews for a specific task's artifacts |
status | string | Filter by status: pending, in_review, approved, rejected |
limit | integer | Max results (default 50, max 200) |
Resolve Review
PUT /v2/reviews/:review_id/resolve
Record Release
POST /v2/releases
Record Recovery
POST /v2/recovery
Routing Signals
GET /v2/routing/signals
Returns per-model routing performance over a configurable time window. Use this to optimize model selection at dispatch time.
| Parameter | Type | Default | Description |
|---|---|---|---|
window_days | integer | 7 | Lookback window (1–90 days) |
{
"window_days": 7,
"models": [
{
"model_id": "claude-sonnet-4-6",
"sample_size": 5231,
"success_rate": 0.94,
"avg_confidence": 0.91,
"avg_cost_usd": 0.008,
"avg_latency_ms": 340.0,
"total_cost_usd": 41.85,
"override_rate": 0.05,
"p50_latency_ms": 310.0,
"p99_latency_ms": 0.0
}
],
"freshness": {
"computed_at": "2026-04-05T04:50:56Z",
"staleness_ms": 15,
"within_slo": true
}
}
Operator Views
Real-time metrics from SQL-queryable hd_runtime views. Each response includes a freshness object indicating data age and SLO compliance.
Active Attempts
GET /v2/runtime/views/active-attempts
Returns running/pending/blocked attempt counts, oldest attempt age, and lease expiry warnings.
Invocation Costs
GET /v2/runtime/views/invocation-costs
Returns total cost, invocation count, token count, broken down by model.
Confidence Distribution
GET /v2/runtime/views/confidence-distribution
Returns artifact confidence buckets (low/medium/high) and percentiles (P50/P90/P99).
Recovery Actions
GET /v2/runtime/views/recovery-actions
Returns total recovery actions, broken down by kind, with recent action details.
Agent Self-Registration
When POST /v2/runtime/tasks/:task_id/attempts is called with an agent_id, the agent is automatically registered in the agent fleet with:
trust_level:provisional(never auto-upgraded from this path)registration_source:v2_create_attemptstatus:active
This makes V2-only agents visible in the Agent Fleet dashboard without requiring a wire protocol connection. The registration is best-effort — if it fails, the attempt still succeeds.
Database Configuration
The control plane's PostgreSQL connection pool is configurable to prevent resource exhaustion under burst load:
| Env Var | Default | Description |
|---|---|---|
DATABASE_MAX_CONNECTIONS | 20 | Maximum pool connections |
DATABASE_MIN_CONNECTIONS | 2 | Minimum idle connections |
DATABASE_ACQUIRE_TIMEOUT_SECS | 5 | Max wait for a connection (returns 503 if exceeded) |
DATABASE_IDLE_TIMEOUT_SECS | 300 | Close idle connections after this duration |
The DATABASE_ACQUIRE_TIMEOUT_SECS setting is critical for preventing OOM under burst load. Without it, requests queue indefinitely in memory when all connections are busy. The default 5-second timeout returns 503 Service Unavailable instead, bounding memory growth.