WorkOS Docs Homepage
Pipes
API referenceDashboardSign In
Getting StartedOverviewOverviewCustom providersCustom providersOrganization-scoped providersOrganization-scoped providersAPI key providersAPI key providersToken proxyToken proxyProvidersProviders
API Reference
API Reference
Events
Events
Integrations
Integrations
Migrate to WorkOS
Migrate to WorkOS
SDKs
SDKs

Token proxy

Call third-party APIs on behalf of your users without handling their access tokens.

On this page

  • Overview
  • How it works
  • Before you begin
  • Making requests
    • Request headers
    • URL routing
    • Path routing
  • Examples
    • Query Linear
    • Post a Slack message
  • Organization scoping
  • Handling authorization
  • Responses
  • Limits and behavior
  • Supported providers
  • Error reference

Overview

The token proxy calls third-party APIs on your users’ behalf without exposing their credentials to your application. Send the request to WorkOS, and the token proxy attaches the user’s credential server-side and streams the provider’s response back.

The token proxy is in early access. Contact support via email or Slack to request access for your environment.

The token proxy is a forward HTTP proxy built for AI agents and other untrusted runtimes. With access tokens, your infrastructure fetches a provider access token and calls the provider directly, so the token exists inside the calling environment, where it can be copied, logged, or leaked. With the token proxy, the token never leaves WorkOS: the agent authenticates with your WorkOS API key and names the user it acts for.

The token proxy removes provider tokens from the calling environment, not the WorkOS API key. That key authenticates every WorkOS API call for the environment, so treat it as a secret even in a sandbox: inject it at request time rather than baking it into agent-visible code or prompts, and rotate it from the API Keys section of the dashboard if a runtime is compromised.

How it works

Every proxied request follows the same lifecycle:

  1. Authenticate. Your WorkOS API key is verified and determines the environment. The key is stripped from the request and is never sent upstream.
  2. Resolve the user. The X-Token-Proxy-User header identifies the user the request acts on behalf of, scoped by X-Token-Proxy-Organization when the connection belongs to an organization.
  3. Look up the connected account. WorkOS finds the user’s Pipes connection for the provider you named. If the user hasn’t connected, or needs to re-authorize, the response is a 402 with an authorization_url to send them to.
  4. Fetch the credential. The user’s access token is retrieved from the Pipes credential store, and refreshed automatically if it has expired.
  5. Inject and forward. Proxy control headers, cookies, and forwarding metadata are stripped, the provider credential is injected into the Authorization header, and the request is forwarded to the provider over HTTPS.
  6. Stream the response. The provider’s status, headers, and body are streamed back verbatim, with an added X-Token-Proxy-Upstream-Status header.

Before you begin

The token proxy builds on your existing Pipes setup. You need:

  • A WorkOS API key for the environment, from the API Keys section of the WorkOS Dashboard.
  • The target provider enabled for your environment, configured in the Pipes section of the dashboard or through the data integration API.
  • A connected account for the user, created when the user authorizes the provider through the Pipes widget or an authorization URL from the get authorization URL endpoint.

Making requests

The base URL is https://api.workos.com/token-proxy, and any HTTP method is accepted. To convert a direct provider call into a proxied one:

  1. Keep the method, body, and content headers as they are.
  2. Send the request to the token proxy base URL, and move the original URL into the X-Token-Proxy-URL header.
  3. Replace the provider token with your WorkOS API key in Authorization.
  4. Name the provider in X-Token-Proxy-Provider and the user in X-Token-Proxy-User, adding X-Token-Proxy-Organization for organization-scoped connections.

Request headers

Header Required Description
Authorization Yes Bearer followed by your WorkOS API key. Determines the environment. Never forwarded to the provider.
X-Token-Proxy-User Yes The ID of the user the request acts on behalf of, for example user_01EHZ…. Must belong to the API key’s environment.
X-Token-Proxy-Organization Conditional The organization the connection was authorized under, for example org_01KV9…. Required when the connection is organization-scoped, and must be omitted when it isn’t.
X-Token-Proxy-Provider URL routing only The slug of the provider to call, for example github. Named explicitly rather than inferred, since one API host can back several providers.
X-Token-Proxy-URL URL routing only The full upstream URL, including path and query string. Must use HTTPS.

All other request headers, including Content-Type, Accept, and provider-specific headers, are forwarded unchanged. See limits and behavior for the headers that are stripped.

URL routing

Put the complete upstream URL in X-Token-Proxy-URL and the provider’s slug in X-Token-Proxy-Provider. Your code keeps working with the provider’s real URLs, and naming the provider keeps the target unambiguous when several providers share an API host, as Gmail, Google Calendar, and generic Google access all do on www.googleapis.com.

curl --request GET https://api.workos.com/token-proxy \
--header "Authorization: Bearer sk_example_123456789" \
--header "X-Token-Proxy-Provider: github" \
--header "X-Token-Proxy-URL: https://api.github.com/user/repos?per_page=5" \
--header "X-Token-Proxy-User: user_01EHZNVPK3SFK441A1RGBFSHRT" \
--header "X-Token-Proxy-Organization: org_01EHZNVPK3SFK441A1RGBFSHRT"

Path routing

Alternatively, prefix the provider’s API path with the token proxy base URL and the provider’s slug. The slug in the path selects the provider, so no X-Token-Proxy-Provider header is needed. The path and query string are appended to the provider’s default API host, so /token-proxy/github/user proxies to https://api.github.com/user.

curl --request GET "https://api.workos.com/token-proxy/github/user/repos?per_page=5" \
--header "Authorization: Bearer sk_example_123456789" \
--header "X-Token-Proxy-User: user_01EHZNVPK3SFK441A1RGBFSHRT" \
--header "X-Token-Proxy-Organization: org_01EHZNVPK3SFK441A1RGBFSHRT"

URL routing is the better default: it can reach any of a provider’s allowed hosts rather than only the default one, and it keeps the provider’s own URLs intact in your code and logs.

Examples

Query Linear

curl --request POST https://api.workos.com/token-proxy \
--header "Authorization: Bearer sk_example_123456789" \
--header "X-Token-Proxy-Provider: linear" \
--header "X-Token-Proxy-URL: https://api.linear.app/graphql" \
--header "X-Token-Proxy-User: user_01EHZNVPK3SFK441A1RGBFSHRT" \
--header "X-Token-Proxy-Organization: org_01EHZNVPK3SFK441A1RGBFSHRT" \
--header "Content-Type: application/json" \
--data '{"query": "{ viewer { id name email } }"}'

Post a Slack message

curl --request POST https://api.workos.com/token-proxy \
--header "Authorization: Bearer sk_example_123456789" \
--header "X-Token-Proxy-Provider: slack" \
--header "X-Token-Proxy-URL: https://slack.com/api/chat.postMessage" \
--header "X-Token-Proxy-User: user_01EHZNVPK3SFK441A1RGBFSHRT" \
--header "X-Token-Proxy-Organization: org_01EHZNVPK3SFK441A1RGBFSHRT" \
--header "Content-Type: application/json" \
--data '{"channel": "C01XXXXXXXX", "text": "Hello from the Pipes token proxy"}'

Organization scoping

Pipes connections are either scoped to an organization or user-only, and the connection lookup requires an exact match on that scope:

  • If the user authorized the provider under an organization, requests must include X-Token-Proxy-Organization with that organization’s ID.
  • If the user authorized the provider without an organization, the header must be omitted.

A scope mismatch in either direction returns the same 402 token_proxy_authorization_required response as a user who never connected at all. If a connected user keeps getting a 402, check that the X-Token-Proxy-Organization header matches the organization on their connected account before asking them to re-authorize.

Handling authorization

When the user has no usable connection for the provider – they never connected, their grant was revoked, or the token can no longer be refreshed – the response is 402 Payment Required:

{
"code": "token_proxy_authorization_required",
"connection": "github",
"message": "User has not authorized provider \"github\"",
"authorization_url": "https://github.com/login/oauth/authorize?client_id=..."
}

Surface the authorization_url to the user, let them complete the provider’s OAuth flow, then retry the original request.

authorization_url is null when WorkOS can’t build an authorization URL for the provider, which usually means the provider’s Pipes configuration is incomplete. Fall back to your own connection flow, such as the Pipes widget, instead of assuming the field is present.

Authorization-required responses use 402, not 401 or 403, so they can’t be confused with a WorkOS authentication failure (401) or a provider-returned 401 or 403 passed through from upstream.

Responses

On success you receive the provider’s status code, headers, and body exactly as the provider sent them, streamed as they arrive. Provider errors are passed through untouched, so a GitHub 404 comes back as a 404 with GitHub’s own error body.

Every proxied response carries an X-Token-Proxy-Upstream-Status header with the provider’s status code. Its presence means the request reached the provider, so you can distinguish an upstream 404 from a token proxy 404 such as an unknown provider.

Limits and behavior

Behavior Detail
Request body Forwarded byte-for-byte, up to 5 MB. GET and HEAD requests send no body.
Upstream timeout 30 seconds. A provider that doesn’t respond in time yields 502 token_proxy_upstream_error.
HTTPS only X-Token-Proxy-URL must use HTTPS. Credentials are never injected into a plaintext request.
Host allowlist Requests can only target a supported provider’s allowed hosts. Anything else is rejected before the request is sent.
Redirects Not followed. Redirect responses are returned as-is.
Stripped request headers Hop-by-hop headers such as Connection and Transfer-Encoding, Cookie, forwarding metadata such as X-Forwarded-*, Via, and X-Real-IP, your Authorization header, and anything prefixed X-Token-Proxy- or X-WorkOS-.
Stripped response headers Hop-by-hop headers and Set-Cookie. Compressed bodies are decoded in transit, so Content-Encoding and Content-Length may be removed.

Supported providers

The token proxy supports the following providers. With path routing, the request goes to the provider’s base API host. With URL routing, any listed host is allowed. The provider must also be enabled for your environment.

Each provider’s API domains are part of its WorkOS configuration, so this list grows as providers are added without any change on your side.

Provider Slug Allowed hosts
Asana asana app.asana.com
Attio attio api.attio.com
Box box api.box.com
Calendly calendly api.calendly.com
Datadog datadog api.datadoghq.com
Dropbox dropbox api.dropboxapi.com
Front front api2.frontapp.com
GitHub github api.github.com, github.com
GitLab gitlab gitlab.com
Gong gong api.gong.io
Google google www.googleapis.com
Help Scout helpscout api.helpscout.net
HubSpot hubspot api.hubapi.com
Intercom intercom api.intercom.io
Jira jira api.atlassian.com
Linear linear api.linear.app
Microsoft microsoft graph.microsoft.com
Notion notion api.notion.com
npm npm registry.npmjs.org
QuickBooks quickbooks quickbooks.api.intuit.com
Salesforce salesforce login.salesforce.com
Sentry sentry sentry.io
Slack slack slack.com, api.slack.com
Stripe stripe api.stripe.com
Xero xero api.xero.com
Zendesk zendesk api.zendesk.com
Zoom zoom api.zoom.us

Error reference

Errors generated by the token proxy are JSON objects with code and message fields. Provider errors are passed through unchanged, so check for X-Token-Proxy-Upstream-Status to tell the two apart.

Status Code Meaning
401  –  Missing or invalid WorkOS API key.
400 token_proxy_missing_url URL routing was used but the X-Token-Proxy-URL header is absent.
400 token_proxy_provider_required URL routing was used but the X-Token-Proxy-Provider header is absent.
400 token_proxy_invalid_url X-Token-Proxy-URL isn’t a valid URL or doesn’t use HTTPS, or the resolved target host isn’t one of the provider’s allowed hosts.
400 token_proxy_user_required The X-Token-Proxy-User header is absent.
400 token_proxy_user_not_found No user with that ID exists in the API key’s environment.
402 token_proxy_authorization_required The user has no usable connection for this provider, or the organization scope doesn’t match. Includes an authorization_url.
404 token_proxy_provider_not_found The slug doesn’t match a supported provider, or the provider isn’t enabled in this environment.
404  –  A generic 404 with no code field means the token proxy isn’t enabled for this environment.
502 token_proxy_credential_error The user’s credential couldn’t be resolved, for a reason that re-authorization won’t fix.
502 token_proxy_upstream_error The provider was unreachable or timed out.
Providers Explore the third-party providers available for Pipes integrations
Up next
© WorkOS, Inc.
FeaturesAuthKitSingle Sign-OnDirectory SyncAdmin PortalFine-Grained Authorization
DevelopersDocumentationChangelogAPI Status
ResourcesBlogPodcastPricingSecuritySupport
CompanyAboutCustomersCareersLegalPrivacy
© WorkOS, Inc.