Go Proxy REST API Reference

Base URL: http://<proxy>:8080
Auth: X-Api-Key header (maps to organisation ID)


POST /mcp/tool_call

Intercept and execute a tool call through the UndoLog engine.

Request

Headers

Header Required Description
X-Api-Key Yes API key for tenant authentication. Maps to an organisation ID.
Content-Type Yes application/json

Body

{
  "session_id": "uuid",
  "tool_name": "str",
  "tool_version": "str",
  "step_index": 0,
  "args": {}
}
Field Type Required Description
session_id string Yes UUID identifying the session.
tool_name string Yes Logical name of the tool.
tool_version string No Semantic version of the tool implementation.
step_index uint32 No Monotonically increasing call order within the session.
args object Yes Tool arguments as a JSON object.

Responses

200 OK. Executed

{
  "status": "executed",
  "effect_id": "uuid",
  "result": {}
}
Field Type Description
status string "executed"
effect_id string Effect log entry identifier.
result object Upstream tool execution result.

A logical tool failure is returned as status: "executed" with result.success: false: when the upstream answers with a 4xx/5xx HTTP status but a structured ToolResult body (as the mock tool server does), the proxy forwards that result to the engine instead of treating it as a transport error. Transport failures (unreachable upstream, non-ToolResult error bodies, timeouts) still surface as 502 tool_error, and the effect is reported as failed through Fail.

Commit and Fail are retried with bounded backoff on transient engine failures (for example an unavailable engine), so a momentary engine outage does not turn into a spurious 502 commit_failed for an effect that the engine will record once the connection recovers. Intercept, Approve, and Reject are not retried. One residual window remains: if the engine applies a Commit or Fail but the response is lost in transit, a retried call hits the already-terminal state and the proxy reports the error even though the effect is recorded. Execution is still exactly-once there, because the SDK retry replays the cached result instead of re-running the tool.

200 OK. Replayed

{
  "status": "replayed",
  "effect_id": "uuid",
  "result": {}
}
Field Type Description
status string "replayed"
effect_id string Effect identifier of the original (cached) entry.
result object Cached tool result from the original execution.

202 Accepted. Pending Approval

{
  "status": "pending_approval",
  "approval_id": "uuid",
  "retry_after": 5
}
Field Type Description
status string "pending_approval"
approval_id string Approval request identifier.
retry_after int Suggested seconds before retrying the same call.

Error Responses

Status code Description
400 Bad Request invalid_request Invalid JSON body, missing session_id/tool_name, or uncanonicalizable args.
401 Unauthorized auth_failed Missing X-Api-Key header.
403 Forbidden auth_failed API key not recognised.
405 Method Not Allowed method_not_allowed HTTP method is not POST.
502 Bad Gateway intercept_failed Engine interception call failed.
502 Bad Gateway tool_error Upstream tool execution failed.
502 Bad Gateway commit_failed Engine commit call failed after successful execution.

Error body:

{
  "request_id": "uuid",
  "code": "error_code",
  "message": "Human-readable description",
  "timestamp": "2026-01-01T00:00:00Z"
}

Every response carries an X-Request-Id header (and the error body includes the same value as request_id). The proxy forwards this value as x-request-id gRPC metadata on every engine call, so engine and proxy logs for one tool call can be correlated across the two services.


GET /health

Liveness probe for the UndoLog proxy.

Request

No headers are required and the endpoint is unauthenticated.

Response

200 OK

{
  "status": "ok",
  "service": "undolog-proxy"
}
Field Type Description
status string "ok"
service string Service identifier.

The endpoint is a liveness probe only: it does not check the engine connection or upstream reachability, and it does not echo configuration values (such as the engine address or upstream URL), which are kept out of unauthenticated responses.


GET /metrics

Prometheus text exposition of proxy service metrics. No auth required.

Metric families

Metric Type Labels Description
undolog_proxy_http_requests_total counter route, status Completed HTTP requests by route and status code.
undolog_proxy_http_request_duration_seconds histogram route HTTP request duration per route. Note that /events reports the full stream lifetime.
undolog_proxy_engine_rpc_duration_seconds histogram method Engine gRPC call duration.
undolog_proxy_engine_rpc_errors_total counter method Engine calls that returned an error.
undolog_proxy_engine_rpc_retries_total counter method Engine calls retried after a transient failure.
undolog_proxy_executor_duration_seconds histogram result Upstream tool executor duration.
undolog_proxy_sse_subscribers gauge org Active SSE subscribers per organisation.
undolog_proxy_sse_events_dropped_total counter org SSE events dropped because a subscriber channel was full.
undolog_proxy_approval_decisions_total counter action, result Approval decisions by action and outcome.
undolog_proxy_approval_decision_duration_seconds histogram action Approval decision latency.

The metric endpoint is unauthenticated and intentionally carries no configuration or API-key material. If it must not be reachable by the public, scope it behind a reverse-proxy rule.

The route label carries the request path with the approval id collapsed to a fixed segment, so /approvals/{id}/approve and /approvals/{id}/reject produce one series per action instead of one per approval id. The /events duration histogram covers the full SSE stream lifetime. Approval latency only samples requests that reached the decision state machine. /health and /metrics are served outside the middleware chain, so they do not appear in the HTTP metrics and are not covered by API-key auth.


GET /approvals

List approval requests filtered by organisation and state.

Request

Headers

Header Required Description
X-Api-Key Yes API key for tenant authentication.
X-Org-Id Yes Organisation identifier (set by auth middleware).

Query Parameters

Parameter Type Required Default Description
state string No pending Filter by approval state. One of: pending, approved, rejected.
limit int No 100 Maximum number of records to return (1-500). Records are ordered newest first, with the ID as tiebreaker.

Response

200 OK

[
  {
    "id": "uuid",
    "org_id": "uuid",
    "session_id": "uuid",
    "effect_id": "uuid",
    "tool_name": "transfer_funds",
    "args": [123, 34, ...],
    "status": "pending"
  }
]

Each record contains:

Field Type Description
id string Approval request identifier.
org_id string Organisation identifier.
session_id string Session identifier.
effect_id string Effect identifier.
tool_name string Tool name.
args bytes Raw JSON tool arguments.
status string One of: pending, approved, rejected.

Error Responses

Status Description
400 Bad Request Invalid state parameter. An invalid or out-of-range limit falls back to the default (100).
401 Unauthorized Missing X-Org-Id header.

POST /approvals/{id}/approve

Approve a pending approval request and resume the suspended session.

Request

Headers

Header Required Description
X-Api-Key Yes API key for tenant authentication.
X-Org-Id Yes Organisation identifier (set by auth middleware).

Path Parameters

Parameter Type Description
id string Approval request identifier.

Response

200 OK

{
  "status": "approved",
  "approval_id": "uuid",
  "effect_id": "uuid",
  "execution": "committed",
  "result": {}
}
Field Type Description
status string "approved"
approval_id string Approval request identifier.
effect_id string Effect identifier.
execution string "committed" on success, "failed" if the tool execution failed after approval.
result object Upstream tool execution result (present when execution is "committed").
error string Execution error message (present when execution is "failed").

Error Responses

Status Description
400 Bad Request Missing approval ID in path or malformed decision body.
401 Unauthorized Missing X-Org-Id header.
404 Not Found Approval ID not found or does not belong to the organisation.
409 Conflict Approval already resolved.
502 Bad Gateway Engine rejected the approval decision.

POST /approvals/{id}/reject

Reject a pending approval request and halt the suspended session.

Request

Headers

Header Required Description
X-Api-Key Yes API key for tenant authentication.
X-Org-Id Yes Organisation identifier (set by auth middleware).

Path Parameters

Parameter Type Description
id string Approval request identifier.

Response

200 OK

{
  "status": "rejected",
  "approval_id": "uuid"
}
Field Type Description
status string "rejected"
approval_id string Approval request identifier.

Error Responses

Status Description
400 Bad Request Missing approval ID in path or malformed decision body.
401 Unauthorized Missing X-Org-Id header.
404 Not Found Approval ID not found or does not belong to the organisation.
409 Conflict Approval already resolved.
502 Bad Gateway Engine rejected the rejection decision.

GET /events

Server-Sent Events stream for real-time dashboard updates.

Request

Headers

Header Required Description
X-Api-Key Yes API key for tenant authentication.
X-Org-Id Yes Organisation identifier (set by auth middleware).

Response

text/event-stream with Cache-Control: no-cache.

Event types:

Event Description
effect_intercepted Tool call reached the engine for interception.
effect_committed Engine committed the effect successfully.
effect_replayed Tool call served from cached state.
effect_failed Failure at any interception stage.
approval_required Tool call waiting for human approval.
approval_approved Human approved an approval request.
approval_rejected Human rejected an approval request.

Event format:

event: effect_committed
id: 1704067200000000000
data: {"type":"effect_committed","timestamp":"2026-01-01T00:00:00Z","org_id":"...","session_id":"...","effect_id":"...","payload":{"stage":"committed"}}

Heartbeat (: ping) sent every 25 seconds to maintain connection.

Error Responses

Status Description
401 Unauthorized Missing org ID via header or query parameter.
500 Internal Server Error Streaming unsupported by proxy.