Skip to main content
The gateway proxy is the primary entry point for agents. It authenticates the API key, classifies the action, evaluates it against your policies, and either forwards it to the tool API, blocks it, or defers it for approval. Base URL: https://<project-ref>.supabase.co/functions/v1/gateway-proxy Required scope: gateway

URL structure

POST /gateway-proxy/{tool}/{path...}
GET  /gateway-proxy/status/{correlationId}
SegmentValues
{tool}github, linear
{path}The tool API path, e.g. /repos/owner/repo/pulls

Request headers

HeaderRequiredDescription
AuthorizationYesBearer agk_...
X-Agent-IdRecommendedAgent identifier, recorded in audit logs and approval requests
Content-TypeYes (for writes)application/json

Forward a GitHub action

curl -X POST \
  "https://<project-ref>.supabase.co/functions/v1/gateway-proxy/github/repos/owner/repo/issues" \
  -H "Authorization: Bearer agk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Agent-Id: my-coding-agent" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Fix login bug",
    "body": "The logout button is broken on mobile.",
    "labels": ["bug"]
  }'

Success response (allow)

When the action is allowed, the response body is the tool API response directly. Sentrail adds three headers:
HTTP/1.1 201 Created
X-Sentrail-Decision: allow
X-Sentrail-AuditLogId: 7c9e6679-7425-40de-944b-e07fc1f90ae7
X-Sentrail-CorrelationId: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "id": 42,
  "title": "Fix login bug",
  "html_url": "https://github.com/owner/repo/issues/42",
  ...
}

Blocked response

// HTTP 403
{
  "ok": false,
  "decision": "block",
  "reason": "Policy 'Block all writes' matched",
  "auditLogId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "correlationId": "550e8400-e29b-41d4-a716-446655440000"
}

Approval required response

// HTTP 202
{
  "ok": true,
  "decision": "require_approval",
  "reason": "Policy 'Require approval for PRs' matched",
  "approvalRequestId": "550e8400-e29b-41d4-a716-446655440000",
  "auditLogId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "pollUrl": "/functions/v1/gateway-proxy/status/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Polling status

Poll for the decision using the correlation ID:
curl "https://<project-ref>.supabase.co/functions/v1/gateway-proxy/status/CORRELATION_ID" \
  -H "Authorization: Bearer agk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// Pending
{
  "ok": true,
  "status": "pending",
  "decision": "require_approval",
  "result": null,
  "killSwitch": {
    "global": false,
    "enabledAt": null,
    "enabledBy": null,
    "toolsPaused": []
  }
}

// After execution
{
  "ok": true,
  "status": "executed",
  "decision": "require_approval",
  "result": { "id": 15, "html_url": "https://github.com/owner/repo/pull/15" },
  "killSwitch": { "global": false, "toolsPaused": [] }
}

Status codes

CodeMeaning
200Action forwarded (body is tool API response)
202Action deferred for approval
400Bad request — unsupported tool, missing path, body too large
401Invalid or expired API key, or missing gateway scope
403Action blocked by policy
405Method not allowed
500Internal error — gateway proxy failure or deferred execution failure

Rate limiting

Sentrail enforces a limit of 100 requests per 60-second window per workspace. Exceeding the limit returns HTTP 429 with a Retry-After header indicating when the window resets:
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{
  "error": "rate_limit_exceeded",
  "message": "Workspace rate limit exceeded. Try again in 42 seconds.",
  "retry_after": 42
}
The limit is enforced by a distributed, database-backed counter shared across all gateway instances — instance recycling or horizontal scaling does not reset it.