Proxy API

The Proxy API lets agents make authenticated HTTP requests to any connected service without ever seeing the raw credential. STACK injects the stored OAuth token or API key into the outbound request, acts as the intermediary, and returns the response. The agent never holds the credential in memory.

The Proxy API requires Studio or Enterprise tier. Free and Developer tier operators receive 403 Forbidden. Additionally, 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 field must be a full URL, not a relative path. STACK does not maintain a base URL registry per provider. The service field is used to look up the credential, not to determine the target URL.

Required Headers

  • Authorization: Bearer sk_live_... — operator or member API key
  • 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.

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

Rate Limiting

Proxy requests are rate-limited per operator on a monthly basis, tracked in Redis with automatic 35-day expiry.

  • Free — not available (403)
  • Developer — not available (403)
  • Studio — 10,000 proxy requests per month
  • Enterprise — unlimited

When the monthly limit is exceeded, requests return 429 Too Many Requests.

Check Proxy Usage

Check how many proxy requests you have used this month and your remaining allowance.

bash
curl https://api.getstack.run/v1/proxy/usage \
  -H "Authorization: Bearer sk_live_your_key"
json
{
  "tier": "studio",
  "limit": 10000,
  "used": 1523,
  "remaining": 8477,
  "period": "2026-04"
}

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

Security Model

  • Zero-knowledge access — the agent never sees the raw credential
  • Instant revocation — disconnect a service and the next proxy request fails immediately
  • 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" }

// Tier too low
{ "error": "FORBIDDEN", "message": "Credential proxy requires Studio or Enterprise tier (current: developer)" }

// 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" }

// Monthly limit exceeded
{ "error": "RATE_LIMIT_EXCEEDED", "message": "Monthly proxy limit exceeded (10000 calls/month for studio tier)" }

// 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 — Infrastructure for AI Agents