STACK
MENU
DOCS / API REFERENCE / PROXY

Proxy API

The Proxy API sends authenticated HTTP requests to connected services. STACK injects the stored OAuth token or API key into the outbound request. A proxy-only agent does not receive the credential.

The Proxy API is available on every tier, including Free. A valid X-Passport-Token header is required on every proxy request.

Make a Proxy Request

Send an HTTP request through STACK to a connected service. STACK resolves the credential, injects authentication headers, forwards the request to the full target URL, and returns the upstream response.

bash
curl -X POST https://api.getstack.run/v1/proxy \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "X-Passport-Token: eyJhbGciOiJFZERTQSIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "service": "github",
    "method": "GET",
    "url": "https://api.github.com/user/repos?per_page=5"
  }'

ProxyRequestInput Fields

  • service (string, required) - provider slug (e.g. "github", "slack", "openai"). Maps to a connected service.
  • method (enum, required) - HTTP method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
  • url (string, required) - full target URL (e.g. "https://api.github.com/user/repos"), not a relative path
  • headers (Record<string, string>, optional) - extra headers to include. Auth headers injected by STACK will override these.
  • body (any, optional) - request body for POST, PUT, and PATCH requests
  • query (Record<string, string>, optional) - query parameters to append to the URL

The url must be a full URL on an allowed host for the selected service.

Required Headers

  • Authorization: Bearer $STACK_TOKEN - operator-scoped credential
  • X-Passport-Token - a valid passport JWT. Required for all proxy requests.

Response

The proxy returns the upstream response wrapped in a STACK envelope with status, headers, and body.

json
{
  "status": 200,
  "headers": {
    "content-type": "application/json; charset=utf-8",
    "x-ratelimit-remaining": "4999"
  },
  "body": [
    {
      "id": 123456,
      "name": "my-repo",
      "full_name": "user/my-repo",
      "private": false
    }
  ]
}

Proxy Enabled Flag

Each service connection has a proxy_enabled flag that must be set to true before the proxy can be used for that service. Toggle it using the service proxy-toggle endpoint:

bash
curl -X POST https://api.getstack.run/v1/services/conn_abc123/proxy-toggle \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "proxy_enabled": true }'

If proxy_enabled is false on the target service connection, proxy requests return 403 Forbidden.

Common Examples

GitHub: List repositories

bash
curl -X POST https://api.getstack.run/v1/proxy \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "X-Passport-Token: eyJ..." \
  -H "Content-Type: application/json" \
  -d '{
    "service": "github",
    "method": "GET",
    "url": "https://api.github.com/user/repos",
    "query": { "sort": "updated", "per_page": "10" }
  }'

Slack: Post a message

bash
curl -X POST https://api.getstack.run/v1/proxy \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "X-Passport-Token: eyJ..." \
  -H "Content-Type: application/json" \
  -d '{
    "service": "slack",
    "method": "POST",
    "url": "https://slack.com/api/chat.postMessage",
    "body": {
      "channel": "C0123456789",
      "text": "Deployed v2.1.0 to production"
    }
  }'

OpenAI: Chat completion

bash
curl -X POST https://api.getstack.run/v1/proxy \
  -H "Authorization: Bearer sk_live_your_key" \
  -H "X-Passport-Token: eyJ..." \
  -H "Content-Type: application/json" \
  -d '{
    "service": "openai",
    "method": "POST",
    "url": "https://api.openai.com/v1/chat/completions",
    "body": {
      "model": "gpt-4o",
      "messages": [{ "role": "user", "content": "Hello" }]
    }
  }'

Passport Scope Checking

STACK verifies that the requested service is within the passport's scope. The passport JWT contains intent_services and services arrays. The proxy checks that the service field matches one of these.

If the service is not in scope, the behavior depends on the passport's accountability mode:

  • enforced - request is blocked with 403 and a security event (credential_outside_scope) is recorded
  • logged - request proceeds but a warning-level security event is recorded

Additionally, proxy requests are blocked if the passport has already been checked out. This generates a credential_after_checkout security event.

Authority enforcement

When the Passport carries authority_refs, STACK resolves the exact active binding and enforces its pinned agent key, receiver, action, resource, Mission, validity, approval, and limits before forwarding the request. Missing, stale, revoked, or unavailable required state denies the action.

A proxy-mediated authority use proves STACK enforcement. It is not a native receiver proof. Native verification uses a signed receiver request and a signed-then-encrypted presentation delivered directly to the receiver. See Authority API.

How Credential Injection Works

STACK determines the correct authentication method based on the service provider and injects the appropriate headers into the outbound request. The agent's original request never contains the credential.

  • OAuth services - STACK adds an Authorization: Bearer <access_token> header
  • API key services - STACK injects the key into the provider-specific header
  • Custom services - STACK injects the stored credential according to template configuration
  • URL templating - URLs containing {{instance_url}} are resolved using the credential data

Action Allowance

Proxy requests use the shared monthly action allowance.

  • Free - 25,000 metered actions / month
  • Developer ($19/mo) - 250,000
  • Pro ($99/mo) - 1,500,000
  • Business ($349/mo) - 10,000,000
  • Enterprise - unlimited monthly allowance

Above the allowance, STACK debits the Wallet. A request returns 402 when the Wallet cannot cover the overage.

Check Proxy Usage

Check shared action use for the current billing period.

bash
curl https://api.getstack.run/v1/proxy/usage \
  -H "Authorization: Bearer sk_live_your_key"
json
{
  "tier": "pro",
  "limit": 1500000,
  "used": 205411,
  "remaining": 1294589,
  "period": "2026-04"
}

For Enterprise tier, limit and remaining are null (unlimited).

Security Model

  • Credential isolation - a proxy-only agent does not receive the raw credential
  • Revocation - disconnecting a service rejects the next proxy request
  • Passport-gated - every proxy request requires a valid passport JWT
  • Scope enforcement - only services listed in the passport scope are accessible
  • Post-checkout blocking - no proxy requests after passport checkout
  • Audit trail - every proxy request is logged as credential.proxy with agent ID and passport JTI
  • Credential access tracking - STACK tracks which services were accessed per passport for Signal 9 (credential_unreported)

Error Handling

json
// Missing X-Passport-Token
{ "error": "FORBIDDEN", "message": "X-Passport-Token header required for proxy requests" }

// Wallet cannot cover overage
{ "error": "INSUFFICIENT_BALANCE", "message": "Monthly action allowance exceeded on developer tier and wallet has insufficient balance for overage." }

// Service not in passport scope (enforced mode)
{ "error": "FORBIDDEN", "message": "Service notion not in passport scope" }

// Proxy disabled on connection
{ "error": "FORBIDDEN", "message": "Proxy access disabled for service notion" }

// Passport already checked out
{ "error": "FORBIDDEN", "message": "Passport already checked out" }

The proxy forwards the response body as-is from the upstream service. If the upstream response contains sensitive data (e.g., customer PII from a Stripe response), that data will be visible to the agent. Scope your API calls appropriately.

stack | Docs