API Gateway Toolkit¶
The API gateway toolkit (kind: api) proxies arbitrary REST/HTTP APIs through the platform's auth, persona, and audit pipeline. It is the HTTP/JSON sibling of the MCP Gateway Toolkit, which proxies upstream MCP servers.
The toolkit exposes three MCP tools — api_invoke_endpoint, api_list_endpoints, api_get_endpoint_schema — that handle every operation on every configured API. Operators register the upstream as a connection of kind api; the model uses api_list_endpoints to discover what's available, api_get_endpoint_schema to learn the precise parameter shape of one operation, and api_invoke_endpoint to make the call. No tools are generated per endpoint, so adding ten APIs does not inflate the tool catalog by a thousand entries.
OpenAPI specs that describe each upstream are stored separately in API catalogs — versioned, globally-owned bundles that many connections can reference. See API Catalogs for the full surface.
When to use¶
Use the API gateway for upstreams that expose a REST API and authenticate with a bearer token, an API key, or OAuth 2.1. Common targets:
- Salesforce REST API
- Google APIs (Drive, Calendar, BigQuery REST surface)
- GitHub REST API
- Stripe API
- Internal HTTP services that should ride the platform's audit pipeline
For upstream MCP servers, use the MCP gateway (kind: mcp) instead.
Configuring a connection¶
API connections are stored in the database, not in platform.yaml. Enable the kind, then author connections through the admin portal or the admin REST API.
Minimal connection config (bearer auth):
curl -X PUT \
-H "X-API-Key: $ADMIN_KEY" -H "Content-Type: application/json" \
-d '{
"config": {
"base_url": "https://api.vendor.example.com",
"auth_mode": "bearer",
"credential": "your-vendor-token"
},
"description": "Vendor REST API"
}' \
https://platform.example.com/api/v1/admin/connection-instances/api/vendor
Auth modes¶
auth_mode |
What it sends |
|---|---|
none |
No outbound auth header |
bearer |
Authorization: Bearer <credential> |
api_key |
<api_key_header>: <credential> (header) or ?<api_key_param>=<credential> (query) |
oauth2_client_credentials |
Token fetched at oauth2_token_url, applied as Authorization: Bearer … |
oauth2_authorization_code |
Browser sign-in once; refresh token persisted (encrypted); access tokens refreshed silently |
The OAuth 2.1 authorization-code grant completes via the platform's shared /api/v1/admin/oauth/callback endpoint, the same path the MCP gateway uses. Register that exact callback URL with the upstream IdP.
Static headers¶
Some APIs require both an OAuth bearer and a separate header on every call. auth_mode is a single value, so the toolkit cannot satisfy that with auth_mode alone. static_headers is the second slot.
Headers listed under static_headers are attached to every outbound request, in addition to whatever auth_mode contributes. They are operator-supplied: the model cannot set, override, or read them, and validation refuses to load a connection whose static_headers would collide with the auth path.
Encryption at rest¶
Header values are encrypted with AES-256-GCM via the platform's FieldEncryptor (same mechanism that protects credential, client_secret, etc.). Set ENCRYPTION_KEY to enable; without it, values are stored in plaintext just like every other sensitive field. The admin API redacts header values to "[REDACTED]" so the portal can edit other fields without ever showing the secret.
Validation rules¶
- Header names must use only the RFC 7230 token character set (no spaces, no colons).
- Values cannot contain CR/LF/NUL (refused as a header-smuggling vector).
- Cannot set
Authorization(useauth_mode). - Cannot set the API-key header chosen by
auth_mode: api_key(already managed). - Cannot set hop-by-hop headers Go's net/http manages itself:
Host,Content-Length,Connection,Transfer-Encoding,Upgrade,Keep-Alive,Proxy-Authenticate,Proxy-Authorization,TE,Trailer.
The model is also blocked at request time from supplying a custom header whose name collides with any static_headers entry — the operator's header is authoritative.
Header precedence¶
For each outbound request, headers are layered in this order (later wins):
- Per-call headers from the tool input (
api_invoke_endpoint.headers). static_headers(operator-configured).auth_modecontribution (Authorization, API-key header, etc.).
Example: Google APIs¶
Google APIs that bill quota against a separate project use the x-goog-user-project header alongside the OAuth bearer.
"config": {
"base_url": "https://www.googleapis.com",
"auth_mode": "oauth2_authorization_code",
"oauth2_authorization_url": "https://accounts.google.com/o/oauth2/v2/auth",
"oauth2_token_url": "https://oauth2.googleapis.com/token",
"oauth2_client_id": "your-google-client-id",
"oauth2_client_secret": "your-google-client-secret",
"oauth2_scopes": ["https://www.googleapis.com/auth/drive.readonly"],
"static_headers": {
"x-goog-user-project": "your-quota-project-id"
}
}
Example: Salesforce REST¶
Salesforce's REST API typically does not need a second header, but the same shape works when a Salesforce instance fronts the API with an API gateway that adds a subscription header.
"config": {
"base_url": "https://your-instance.my.salesforce.com",
"auth_mode": "oauth2_authorization_code",
"oauth2_authorization_url": "https://login.salesforce.com/services/oauth2/authorize",
"oauth2_token_url": "https://login.salesforce.com/services/oauth2/token",
"oauth2_client_id": "your-connected-app-consumer-key",
"oauth2_client_secret": "your-connected-app-consumer-secret",
"oauth2_scopes": ["api", "refresh_token"]
}
Add refresh_token to oauth2_scopes so Salesforce issues a refresh token — without it, the platform cannot keep the connection alive across access-token expiry.
Admin portal¶
The admin portal's Connections page surfaces static_headers as a key/value editor under each kind: api connection. Existing values are masked (the portal never sees the cleartext secret after the first save); add or delete to change the set. Names remain visible so an operator can confirm which headers are configured without revealing the values.