Log in Create workspace
Skip to content

API reference

Gateway

Evaluate workspace policy, request operator approval, and execute permitted connector actions with audit traces.

Jump to 4 endpoints
On this page

Overview

The gateway sits between an agent and an external tool, such as an email service or an HTTP API. It decides whether the requested action is allowed, denied, or needs a human decision. Choose an authorization check to inspect policy without running the tool, a tool action to track approval, or gateway execution when you want the permitted action to run immediately.

Three routes, distinct effects
auth_checks returns allowed, denied, or requires approval as HTTP 200 and never executes. tool_actions writes audit/approval state but does not execute an already-allowed action. gateway/execute authorizes and immediately runs the connector only when policy says allowed.
Approval polling is observation
A tool action that needs approval returns 202, Location, Retry-After: 2, and a status URL. A permitted, signed-in operator's approval queues one execution attempt. The worker rechecks the connector, actor, freeze, and current authorization before running. Rejection or a failed recheck ends that attempt. Polling the status URL never starts execution or retries an interrupted attempt.
Policy fields have a shared envelope
Use the same connector, action, payload, amount, environment, risk_score, and contains_phi across check and execution. Authorization snapshots live in execution metadata and audit records, not as a top-level success field.

Before you begin

  • The command-line examples require curl and jq; any exception is identified in its introduction.
  • An active workspace connector created by an operator and a permission grant binding it to the calling service agent.
  • A credential with gateway:check for dry decisions or gateway:execute for tool actions, status reads, and execution.
  • An action supported by the connector executor; a policy-only connector can authorize yet still be non-executable.

How it works

  1. Preflight sensitive actions

    Run an auth check with real policy inputs. Branch on decision, not HTTP status, because denied and requires-approval decisions are valid 200 responses on this route.

  2. Choose immediate or approval-aware flow

    Use gateway execute for an action expected to be allowed now. Use tool actions when a requires-approval result should create an operator request and expose a pollable state machine.

  3. Retain execution evidence

    On direct execution, save the trace_id, connector, action, duration, events, and output. On approval flow, follow the returned status URL and stop polling according to state, next_action, and any retry-after value.

Examples

These examples are illustrative. Replace the sample values with your own. Keep credentials in environment variables or a secret manager, never in client-side code.

Create an approval-aware tool action

This records authorization state but does not itself call the connector.

bash
export AGENTOPS_BASE_URL="https://robotscenter.net"
export AGENT_TOKEN="machine_token_with_gateway_execute"

RESPONSE_HEADERS=$(mktemp)
RESPONSE_BODY=$(mktemp)
curl --silent --show-error \
  -D "$RESPONSE_HEADERS" -o "$RESPONSE_BODY" \
  -X POST -H 'content-type: application/json' \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  --data '{"connector_slug":"slack","action_name":"messages.post","payload":{"channel":"ops-alerts","text":"Investigate checkout alert"},"environment":"production","risk_score":0.4}' \
  "$AGENTOPS_BASE_URL/api/v1/tool_actions"

cat "$RESPONSE_HEADERS"
jq . "$RESPONSE_BODY"
rm -f "$RESPONSE_HEADERS" "$RESPONSE_BODY"
What to expect

Allowed policy returns HTTP 200 with status: ok but no connector execution. Approval-required policy returns HTTP 202 with approval_request_id, status_url, Location, Retry-After, and no-store headers.

Execute an allowed HTTP connector action

This route performs the external side effect synchronously after policy allows it. The internal-api connector must already have an operator-configured base URL and authentication plus a write grant for this agent; otherwise execution fails as not configured or unauthorized.

bash
export AGENTOPS_BASE_URL="https://robotscenter.net"
export AGENT_TOKEN="machine_token_with_gateway_execute"

curl --fail-with-body --silent --show-error \
  -X POST -H 'content-type: application/json' \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  --data '{"connector_slug":"internal-api","action":"write","payload":{"job":"inventory-sync","requested_by":"agent-operations"},"environment":"production"}' \
  "$AGENTOPS_BASE_URL/api/v1/gateway/execute" | jq .
What to expect

HTTP 200 returns status: success, trace and connector identities, duration, action, ordered execution events, and action-specific output.

Troubleshooting

Auth check says allowed but execution fails
The check does not reserve policy state or test the external system. Recheck connector status, action support, current grant/policy state, outbound URL policy, and the executor events in the failed response.
A connector is 404
Machine lookup returns only active, non-archived connectors in the authenticated workspace. Missing and paused connectors are surfaced as not found on execution.
Polling does not start execution
That is intentional. Status GET is read-only, and automatic_retry is false. If state reports indeterminate execution or operator review, do not manufacture a retry that might duplicate the external side effect.
A lost POST response may hide a side effect
POST /tool_actions has no idempotency or deduplication: resending can create another approval request, and each duplicate can execute if approved. Reconcile the operator approval queue and audit trail with available request or business identifiers before resending. Non-SMTP /gateway/execute also ignores Idempotency-Key; HTTP executor retry: false prevents an internal transport retry but cannot prove a timed-out call did not reach the remote service. Only email-smtp send_email and send_bulk_email have the documented platform idempotency protection.

Endpoint reference

Ready to integrate? Open an endpoint for its required permissions, request fields, response format, and status codes. The examples above explain the workflow; these details define each individual request.

POST
/api/v1/auth_checks API key / access token

Check if an action is authorized

gateway:check

Description

Evaluates whether a service agent is authorized to perform a specific action on a connector. Prerequisite: A connector must exist in the workspace (create via operator dashboard).

Auth

API key / access token

Required Scopes

gateway:check
Request Body
Field Type Required Description
amount number No Numeric amount for approval-policy conditions (top-level)
payload object No Action input parameters used by approval-policy conditions
action_name string Yes Name of the action to check
environment string No Environment for approval-policy conditions (top-level)
connector_id string No Connector UUID
connector_slug string No Connector slug (alternative to connector_id)
contains_phi boolean No Healthcare policy input. Merged into payload.contains_phi for the same PDP envelope as execute and email.
risk_score number No Risk score for approval-policy conditions (top-level)
Responses
200

Authorization decision. denied and requires_approval are also 200 — this route never executes.

Response Body
Field Type Required Description
connector object Yes Resolved connector
connector.id string Yes Connector UUID
connector.slug string Yes Connector slug
decision string (allowed, denied, requires_approval) Yes PDP outcome
policy object No Matched approval policy, or null
policy.id string Yes Policy UUID
policy.name string Yes Policy name
Example
{
  "connector": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "slug": "email-smtp"
  },
  "decision": "allowed",
  "policy": null
}
401

Invalid or missing token

403

Missing required scope

404

Connector not found

422

Validation failed: missing connector_id/connector_slug or action_name, invalid connector UUID, or non-numeric amount/risk_score

POST
/api/v1/tool_actions API key / access token

Authorize a tool action

gateway:execute

Description

Authorizes a tool action, writes an audit row, and optionally creates an approval request. GatewayExecutor is not called. Allowed returns 200 {status:"ok", decision, connector_id}. requires_approval returns 202 with approval_request_id, status_url, Location and Retry-After: 2; poll status_url until execution completes. Indeterminate execution is never retried automatically and requires review. Prerequisite: a connector must exist in the workspace.

Auth

API key / access token

Required Scopes

gateway:execute
Request Body
Field Type Required Description
amount number No Numeric amount for approval-policy conditions (top-level)
payload object No Action input parameters
action_name string Yes Name of the action to authorize (this route does not execute it)
environment string No Environment for approval-policy conditions (top-level)
input_payload object No Alternative key for payload
connector_id string No Connector UUID
connector_slug string No Connector slug (alternative to connector_id)
contains_phi boolean No Healthcare policy input. Merged into payload.contains_phi for the same PDP envelope as execute and email.
risk_score number No Risk score for approval-policy conditions (top-level)
Responses
200

Authorized. No connector action ran. Body is {status:"ok", decision, connector_id}.

Example
{
  "status": "ok",
  "connector_id": "550e8400-e29b-41d4-a716-446655440000",
  "decision": "allowed"
}
202

Policy requires approval. Body includes status_url; poll it with the same gateway:execute token. Location points to the same URL, Retry-After is 2 seconds, and Cache-Control is no-store.

Example
{
  "status": "pending_approval",
  "connector_id": "550e8400-e29b-41d4-a716-446655440000",
  "decision": "requires_approval",
  "approval_request_id": "9c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "status_url": "/api/v1/tool_actions/9c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
}
401

Invalid or missing token

403

Denied by policy. Body is {error:"denied", decision, connector_id}, not problem JSON.

404

Connector not found

422

Validation failed: missing connector ref or action_name, invalid UUID, or non-numeric amount/risk_score

POST
/api/v1/gateway/execute API key / access token

Execute connector action with full trace recording

gateway:execute

Description

Runs a connector action after PDP authorization, records a runtime trace, and returns the event timeline. SMTP (email-smtp) handles send_email and send_bulk_email; only those SMTP actions accept optional Idempotency-Key (maximum 200 visible ASCII bytes; malformed keys return 422) for safe replay; other connector actions do not use the key. SMTP accepts bounded inline attachments (filename, content_type, content_base64), while URL/path attachments are rejected. HTTPExecutor already runs slack, github, jira, linear, internal-api, stripe, salesforce, and twilio (events http_request/http_response; actions such as messages.post). Policy-only slugs have no executor and fail as EXECUTION_ERROR (:not_executable). AuthorizationSnapshot is stored only in execution metadata/audit — it is not a top-level 200 field.

Auth

API key / access token

Required Scopes

gateway:execute
Request Body
Field Type Required Description
metadata object No Optional metadata including trace_id, campaign_id, etc.
metadata.trace_id string No Custom trace ID: a non-blank string of at most 255 bytes with no null bytes, unique per workspace. Invalid values return 422; a reused value returns TRACE_ID_CONFLICT.
metadata.campaign_id string No Campaign identifier
action string No Action to execute (e.g. send_email, send_bulk_email, messages.post)
amount number No Fintech policy amount. Hoisted into the same PDP envelope as auth_checks/tool_actions.
payload object No Action-specific input parameters
action_name string No Alternative key for action (alias)
environment string No Environment label for approval-policy conditions (top-level)
service_agent_id string No Optional service agent ID for audit trail
input_payload object No Alternative key for payload
connector_id string No Connector UUID
connector_slug string No Connector slug (alternative to connector_id)
contains_phi boolean No Healthcare policy input. Cast is skipped; the controller hoists this into the PDP payload.contains_phi envelope.
risk_score number No Risk score for approval-policy conditions (top-level)
Parameters
Name In Type Required Description
Idempotency-Key header string No Optional SMTP-only key (maximum 200 visible ASCII bytes); malformed keys return 422.
Responses
200

Action executed successfully

Returns: GatewayExecuteResponse

Response Body
Field Type Required Description
output object No Action-specific output data
status string Yes Execution status
events array<object> Yes Array of execution events with timestamps
events[].timestamp datetime (ISO 8601) Yes ISO 8601 timestamp
events[].type string Yes Event type (e.g. smtp_connecting, email_sent, http_request, http_response)
events[].details object No Event-specific details
action string No Executed action name
message_id string No Message identifier (for email actions)
trace_id string Yes Trace ID for observability lookup
duration_ms integer Yes Total execution time in milliseconds
connector_id string Yes Connector UUID
connector_slug string Yes Connector slug
Example
{
  "output": {
    "delivered": true,
    "recipient": "recipient@example.com",
    "message_id": "<abc123@smtp.delivered>",
    "duration_ms": 245
  },
  "status": "success",
  "events": [
    {
      "timestamp": "2026-03-18T10:00:00Z",
      "type": "smtp_connecting",
      "details": {
        "port": 587,
        "host": "smtp.example.com"
      }
    },
    {
      "timestamp": "2026-03-18T10:00:00Z",
      "type": "smtp_connected",
      "details": {
        "port": 587,
        "host": "smtp.example.com"
      }
    },
    {
      "timestamp": "2026-03-18T10:00:01Z",
      "type": "smtp_authenticating",
      "details": {
        "user": "sender@example.com"
      }
    },
    {
      "timestamp": "2026-03-18T10:00:01Z",
      "type": "smtp_authenticated",
      "details": {
        "user": "sender@example.com"
      }
    },
    {
      "timestamp": "2026-03-18T10:00:02Z",
      "type": "email_sent",
      "details": {
        "from": "sender@example.com",
        "to": [
          "recipient@example.com"
        ],
        "subject": "Test Email",
        "message_id": "<abc123@smtp.delivered>"
      }
    },
    {
      "timestamp": "2026-03-18T10:00:02Z",
      "type": "smtp_disconnected",
      "details": {}
    }
  ],
  "action": "send_email",
  "message_id": "<abc123@smtp.delivered>",
  "trace_id": "a1b2c3d4e5f6",
  "duration_ms": 245,
  "connector_id": "550e8400-e29b-41d4-a716-446655440000",
  "connector_slug": "email-smtp"
}
400

Execution failed - includes events up to failure point

Returns: GatewayExecuteErrorResponse

Response Body
Field Type Required Description
message string Yes Human-readable error message
status string Yes Always 'error'
events array<object> No Partial events recorded before failure
details object No Error details
details.action string No Action name
details.provided_fields array<string> No List of provided field names
error_code string (UNSUPPORTED_ACTION, INVALID_EMAIL, SMTP_ERROR, ACCESS_DENIED, REQUIRES_APPROVAL, TRACE_ID_CONFLICT, INVALID_REQUEST, EXECUTION_ERROR) Yes Error code. Paused or missing connectors are 404 Problem JSON, not CONNECTOR_PAUSED/CONNECTOR_NOT_FOUND. :http_error, :not_executable, :http_not_configured, and outbound URL-policy atoms collapse to EXECUTION_ERROR. INVALID_REQUEST means a GET/HEAD query value or a {param} path value in the payload was not a string, number, boolean or null; nothing was sent upstream.
trace_id string No Trace ID for debugging
Example
{
  "message": "Failed to send email via SMTP",
  "status": "error",
  "events": [
    {
      "timestamp": "2026-03-18T10:00:00Z",
      "type": "smtp_connecting",
      "details": {
        "port": 587,
        "host": "smtp.example.com"
      }
    },
    {
      "timestamp": "2026-03-18T10:00:00Z",
      "type": "smtp_connected",
      "details": {
        "port": 587,
        "host": "smtp.example.com"
      }
    },
    {
      "timestamp": "2026-03-18T10:00:01Z",
      "type": "smtp_authentication_failed",
      "details": {
        "error": "Invalid credentials",
        "user": "sender@example.com"
      }
    },
    {
      "timestamp": "2026-03-18T10:00:01Z",
      "type": "smtp_disconnected",
      "details": {}
    }
  ],
  "details": {
    "action": "send_email",
    "provided_fields": [
      "action",
      "connector_id",
      "payload"
    ]
  },
  "error_code": "SMTP_ERROR",
  "trace_id": "a1b2c3d4e5f6"
}
401

Invalid or missing token

403

Missing gateway:execute scope

404

Connector not found. get_connector only returns active, non-archived connectors, so paused and missing connectors are this 404 Problem JSON — not a 400 CONNECTOR_PAUSED body.

409

Problem JSON for SMTP Idempotency-Key conflict, in-progress execution, or failed prior execution

422

Validation error (missing action or connector)

GET
/api/v1/tool_actions/:id API key / access token

Poll a tool approval and execution

gateway:execute

Description

Returns the requesting service agent's approval and execution state. This read never executes, retries, or enqueues the connector action. A pending request may expire without being mutated; an interrupted execution is indeterminate and requires operator review.

Auth

API key / access token

Required Scopes

gateway:execute
Parameters
Name In Type Required Description
id path string Yes Approval request UUID returned by POST /api/v1/tool_actions
Responses
200

Approval and execution state

Response Body
Field Type Required Description
id string Yes Approval request UUID
state string (pending_approval, queued, running, succeeded, failed, rejected, expired, indeterminate, not_requested) Yes Current combined state for a polling client
expires_at datetime (ISO 8601) Yes Approval expiry
retry_after_seconds integer (2, ) Yes Polling delay or null for a terminal/review state
execution_status string (not_requested, queued, running, succeeded, failed) Yes Durable execution state
approval_status string (pending, approved, rejected, expired) Yes Recorded approval state
automatic_retry boolean (false) Yes Always false
execution_completed_at datetime (ISO 8601) Yes Execution completion
execution_started_at datetime (ISO 8601) Yes Execution start
execution_trace_id string Yes Execution trace correlation only; confers no read access
next_action string (poll, continue, stop, review) Yes Continue the caller's workflow only after success; never resubmit this action
reviewed_at datetime (ISO 8601) Yes Operator decision time
Example
{
  "id": "9c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "state": "pending_approval",
  "expires_at": "2026-09-08T12:05:00Z",
  "retry_after_seconds": 2,
  "execution_status": "not_requested",
  "approval_status": "pending",
  "automatic_retry": false,
  "execution_completed_at": null,
  "execution_started_at": null,
  "execution_trace_id": null,
  "next_action": "poll",
  "reviewed_at": null
}
401

Invalid or missing token

403

Missing gateway:execute scope

404

Approval request not found for this service agent

Where to go next

see also