Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/joseluis-dev/harness-ai/llms.txt

Use this file to discover all available pages before exploring further.

The runtime exposes an internal HTTP API reachable only on the private Docker network — never from the public internet. It is the sole writer of both the executions table and the audit_events table; no other service opens a direct Postgres connection. The BFF forwards all create and read requests here via fetch() over the Docker-internal hostname runtime-python:8000. Swagger UI and ReDoc are intentionally disabled on this service.

Pydantic Schemas

The runtime’s request and response shapes are defined as Pydantic v2 models with extra="forbid" — unknown fields are rejected at the boundary.
class ExecutionCreateRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    source_type: str = Field(min_length=1, max_length=128)
    intent: str = Field(min_length=1, max_length=128)
    payload: dict[str, Any] = Field(default_factory=dict)
    constraints: Optional[dict[str, Any]] = None


class ExecutionCreateResponse(BaseModel):
    id: uuid.UUID
    status: str


class ExecutionResponse(BaseModel):
    id: uuid.UUID
    status: str
    source_type: str
    intent: str
    payload: dict[str, Any]
    created_at: datetime
    updated_at: datetime
    completed_at: Optional[datetime] = None


class AuditEventResponse(BaseModel):
    id: uuid.UUID
    execution_id: Optional[uuid.UUID]
    event_type: str
    from_status: Optional[str]
    to_status: Optional[str]
    actor_type: str
    actor_id: Optional[str]
    payload: Optional[dict[str, Any]]
    created_at: datetime


class AuditIngestRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    events: list[dict[str, Any]] = Field(
        ...,
        min_length=1,
        max_length=1024,
    )


class AuditIngestResponse(BaseModel):
    accepted: int


class HealthzResponse(BaseModel):
    status: str = "ok"
    service: str = "runtime-python"
    version: str = "0.1.0"

GET /healthz

Liveness probe used by the Docker healthcheck directive and by the BFF’s aggregate health endpoint.

Response — 200 OK

{
  "status": "ok",
  "service": "runtime-python",
  "version": "0.1.0"
}
status
string
Always "ok" when the process is alive and accepting connections.
service
string
Always "runtime-python".
version
string
Service version string.

POST /internal/executions

Creates an execution row in Postgres and enqueues an RQ job for the worker. This is the sole writer of the executions table.

Request Body

source_type
string
required
The kind of caller driving the execution. 1–128 characters. Corresponds to source.type in the Execution Event Contract.
intent
string
required
Semantic description of the desired action. 1–128 characters.
payload
object
default:"{}"
Free-form JSONB body for the execution. Stored in the executions.payload column. Defaults to an empty object when omitted.
constraints
object
Optional constraints envelope. When provided, merged into payload under the "constraints" key before the row is committed.

Response — 201 Created

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "queued"
}
id
UUID
The new execution’s UUID. The BFF proxies this value to the caller verbatim.
status
string
Always "queued" immediately after creation.

Error Responses

// 400 — integrity violation or invalid payload
{
  "detail": "Invalid execution payload."
}

// 503 — RQ broker unavailable
{
  "detail": "Execution broker is unavailable; retry shortly."
}
The runtime commits the Postgres row first, then enqueues the RQ job. This ordering means the execution row always exists even during a broker outage. The worker is idempotent, so a duplicate enqueue after a retry is safe. A 503 from this endpoint means the row was written — the BFF should retry the enqueue rather than creating a duplicate row.

GET /internal/executions

Lists executions ordered by created_at descending. Supports pagination.

Query Parameters

limit
integer
default:"50"
Number of results to return. Minimum 1, maximum 200.
offset
integer
default:"0"
Number of results to skip. Minimum 0. Use with limit to paginate.

Response — 200 OK

Array of ExecutionResponse objects.
[
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "completed",
    "source_type": "astro",
    "intent": "query_sql_view",
    "payload": {},
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-15T10:30:05Z",
    "completed_at": "2024-01-15T10:30:05Z"
  }
]

GET /internal/executions/{execution_id}

Fetches a single execution row by its UUID.

Path Parameters

execution_id
UUID
required
The execution’s UUID.

Response — 200 OK

Full ExecutionResponse object.
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "running",
  "source_type": "n8n",
  "intent": "generate_report",
  "payload": { "report_id": "monthly-traffic-2024-01" },
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:02Z",
  "completed_at": null
}
id
UUID
UUID of the execution.
status
string
Current status: queued | running | completed | failed.
source_type
string
The originating source type.
intent
string
The semantic intent string.
payload
object
The stored JSONB payload including any merged constraints.
created_at
string (ISO 8601)
Row creation timestamp.
updated_at
string (ISO 8601)
Last modification timestamp.
completed_at
string (ISO 8601) | null
Completion timestamp, or null if not yet complete.

Error Response — 404 Not Found

{
  "detail": "Execution not found."
}

GET /internal/executions/{execution_id}/events

Fetches the audit_events rows for a single execution, ordered by created_at ascending (oldest first).

Path Parameters

execution_id
UUID
required
The execution’s UUID.

Response — 200 OK

Array of AuditEventResponse objects.
[
  {
    "id": "f1e2d3c4-b5a6-7890-fedc-ba9876543210",
    "execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "event_type": "execution.queued",
    "from_status": null,
    "to_status": "queued",
    "actor_type": "system",
    "actor_id": null,
    "payload": null,
    "created_at": "2024-01-15T10:30:00Z"
  }
]
id
UUID
Unique ID of the audit event row.
execution_id
UUID | null
The associated execution UUID. null for cross-service events forwarded via /internal/audit/ingest.
event_type
string
The audit event name, e.g. execution.queued, execution.completed, model.call.
from_status
string | null
The execution status before this transition. null for the first event or for non-lifecycle events.
to_status
string | null
The execution status after this transition.
actor_type
string
Type of principal that triggered the event: user | service | system.
actor_id
string | null
Identifier of the actor, if available.
payload
object | null
JSONB payload for the event — contents vary by event_type.
created_at
string (ISO 8601)
Timestamp of the audit event.
This endpoint returns 200 [] for unknown execution IDs — it does not return 404. This is intentional: the BFF can forward the response without branching on whether the execution exists yet, and the UI can render “no events yet” as a normal empty state.

POST /internal/audit/ingest

Accepts forwarded audit events from sidecars and other internal services (currently: model-gateway). Writes one audit_events row per event with execution_id=NULL.

Request Body

events
array
required
List of audit event objects to persist. Minimum 1 item, maximum 1024 items per request. Each element is a free-form object whose fields are stored verbatim in the JSONB payload column.
The model-gateway emitter always includes these additional fields in each event object, all of which are stored in the JSONB payload column:
FieldDescription
providerThe provider name that served the call.
modelThe model name used.
provider_kind"local" | "self" | "cloud" | "mock".
tokens_inInput token count.
tokens_outOutput token count.
latency_msWall-clock latency of the provider call.
correlation_idRequest-scoped correlation UUID.

Response — 200 OK

{
  "accepted": 3
}
accepted
integer
Number of events successfully written to audit_events.

Error Response — 422 Unprocessable Entity

Returned when the events array is empty (Pydantic v2 min_length=1 validation).
{
  "detail": [
    {
      "type": "too_short",
      "loc": ["body", "events"],
      "msg": "List should have at least 1 item after validation, not 0"
    }
  ]
}
The runtime is the sole writer of audit_events. The BFF must not open a direct Postgres connection to write audit rows — all audit writes flow through either the execution lifecycle (state transition writer) or this ingest endpoint. This invariant is enforced by the architecture and validated on every PR gate.
Cross-service events written via this endpoint carry execution_id=NULL. They are not lifecycle transitions (so from_status and to_status are also NULL), but they are fully queryable by event_type, actor_id, or the fields in the JSONB payload column.

Build docs developers (and LLMs) love