Proxy a DELETE request to an integration with server-side credential injection
High-value endpoint. Accepts an upstream HTTP request and forwards it to the upstream API after injecting the stored credentials server-side. Credentials never leave Appstrate.
Authentication: bearer only — either an API key with the credential-proxy:call scope (carried by a key created with scopes omitted or empty when its creator holds it) or an OIDC-issued JWT (device-flow access token for the interactive CLI, dashboard access token for second-party apps). Cookie sessions are rejected. Session binding pins the X-Session-Id to the first principal (API key or JWT user) that used it.
Optional Appstrate-User header scopes the call to an end-user's connection (API-key auth only).
URL and headers can contain {{credential_field}} placeholders substituted against the integration's credential schema. Set X-Substitute-Body: 1 to run the same substitution on the request body (verbs that carry one).
Boolean control headers (X-Substitute-Body, X-Stream-Request, X-Stream-Response) take 1 or 0; any other value is a 400.
Every response carries RFC 9209 Proxy-Status: appstrate; received-status=<n> on an upstream response relayed whatever its status (a relayed 401 carries no platform WWW-Authenticate challenge — it is the upstream refusing the connection's credential), appstrate; error=<type> on a response the proxy produced itself, whose problem code names the cause.
/api/credential-proxy/proxyAPI key authentication. Use Authorization: Bearer apst_... header. The org is resolved from the key — no X-Org-Id header needed. A key is apst_ + 30 base62 characters + a 6-character base62 CRC32 of those 30, so a malformed key is refused without a lookup.
In: header
Header Parameters
Space id (spc_…) the API key is scoped to.
Scoped integration package name (e.g. @afps/gmail).
Upstream endpoint: an absolute URL, or one whose {{credential_field}} placeholders (e.g. {{site_url}}/wp-json/…) the platform substitutes from the connection before any check; the substituted URL must be absolute. It must match the integration manifest auth's authorized_uris (rendered for the connection) unless allow_all_uris: true. allow_all_uris is ignored when a {{credential_field}} placeholder appears in this URL, a header, or a substituted body: the target and every redirect hop must then match authorized_uris, and the call is refused when that list is empty.
Caller-chosen session id; scopes the cookie jar. Fresh UUID per CLI invocation is typical.
When 1, the request body is decoded as UTF-8 and {{field}} placeholders are substituted. Ignored on verbs that do not carry a body (GET, DELETE).
"0" | "1"Impersonation header — scopes the call to this end-user's connection.
^eu_When 1, forward the request body as a stream instead of buffering. Required for uploads larger than the buffered body cap; the upstream content length is still validated against CREDENTIAL_PROXY_LIMITS.max_request_bytes. Ignored on verbs that do not carry a body (GET, DELETE).
"0" | "1"When 1, stream the upstream response body through the 100 MB streaming cap instead of buffering it. Skips the buffered max_response_bytes truncation, so X-Truncated is not emitted; an oversized stream is aborted rather than truncated.
"0" | "1"Optional cap (in bytes) on the buffered upstream response before truncation. Clamped to CREDENTIAL_PROXY_LIMITS.max_response_bytes. Ignored when X-Stream-Response: 1 is set.
Optional run id (run_…) of the run this call acts for — sent by a runner executing it (appstrate run --report). Must name an in-flight run of the calling actor in this space: an unknown id or one of another space is a 404, another actor's run a 403, a finished run a 400. It binds the call to the run's snapshot: the call reaches ONLY the connections the run's kickoff bound to the integration (every layer applied, agent-level ones included — admin pins, enforced defaults, launch overrides, member pins): one bound connection is used; several require X-Connection-Id naming one of them (409 must_choose_connection when absent, 400 connection_not_in_run when it names another); none is a 404, and so is a bound one no longer reachable (deleted or unshared); a bound one that needs reconnecting is a 409 needs_reconnection. Without it no agent is in play, so the admin and member pins (set per agent) cannot apply — only the space-level rules described under X-Connection-Id do.
Optional explicit connection UUID. With X-Run-Id, it must name a connection the run bound (see X-Run-Id). Without it, the space-level rules apply in this order: (1) an ENFORCED org default of the integration binds its set — a named id must be a member (400 connection_not_in_org_default otherwise); (2) the named connection, after validating it is one of the caller's own (user or end-user) or a connection another member shared in the request's space, of the requested integration; (3) a SOFT org default binds its set; (4) the caller's own connections: exactly one is used, none with some shared by other members is a 409 must_choose_connection (a shared connection is never used unless named or set as a default), none at all a 404, several a 409 must_choose_connection. A default set of one is used, several are a 409 must_choose_connection over the set, and a member the caller cannot reach is a 409 pinned_connection_unavailable, and a bound connection whose credentials need reconnecting (a default's member included) a 409 needs_reconnection. A non-uuid value is a 400; mismatched or unknown ids surface as 404 — no credentials.
uuidcurl -X DELETE "https://your-instance/api/credential-proxy/proxy" \ -H "X-Space-Id: string" \ -H "X-Integration-Id: string" \ -H "X-Target: string" \ -H "X-Session-Id: string"{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}{
"type": "https://docs.appstrate.dev/errors/rate-limited",
"title": "Rate Limited",
"status": 429,
"detail": "Too many requests. Please try again shortly.",
"code": "rate_limited",
"request_id": "req_abc123",
"retry_after": 30
}{
"type": "https://docs.appstrate.dev/errors/internal-error",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred. Please try again or contact support.",
"code": "internal_error",
"request_id": "req_abc123"
}{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}