# Thru — API access without provider credentials in the conversation Base URL: https://thru.snd.one OpenAPI: https://thru.snd.one/openapi.json Human guide: https://thru.snd.one/ Thru is a credential-holding HTTP proxy. You describe a trusted API and the credential fields it needs. The user reviews that destination and enters the credentials directly in Thru. Thru injects configured headers or AWS SigV4 signatures when you call the proxy. Provider credential values are never returned by the setup or status APIs. ## Agent workflow 1. Establish the user's intended API and task. Find the provider's official credential creation page and explain which scopes or permissions the task needs. Never ask the user to paste provider credentials into chat, a tool argument, or an agent-visible file. Prefer a narrowly scoped, short-lived key. 2. POST a JSON configuration to https://thru.snd.one/api/proxies. Creation needs no authentication. Specify a fixed public HTTPS target_url, descriptive metadata, credential field definitions, and header templates or AWS signing config. Request only the HTTP methods the task needs and a suitable lifetime. 3. Retain the returned access_token privately for proxy requests and status polling. Send the user the exact auth_url, including its # fragment. The fragment carries the setup capability; do not shorten or strip it. Setup links are one-use and expire after one hour. The user should verify the displayed destination, header schema, and permissions before entering credentials. 4. Wait for the user to say setup is complete, then GET the returned status_url with Authorization: Bearer . Proceed only when status is authenticated. Poll gently if needed; never repeatedly create proxies. 5. Make ordinary HTTP requests to proxy_url plus the API path, using Authorization: Bearer . Thru consumes this authorization header, injects the provider credentials, and forwards to the configured destination. Preserve the API's method, query, content type, and body. Do not place provider credentials in URLs or requests. Stop if the proxy is expired or revoked. The access_token is a Thru capability, distinct from the provider API key. Anyone holding it can use this proxy within its destination, methods, and lifetime. It is returned only when the proxy is created; keep it out of public messages and logs. The user receives a separate management link after setup and can revoke access from it. The agent cannot revoke using its access_token. ## Complete GitHub example Tell the user: "Create a GitHub personal access token with the permissions needed for this task. You will enter it on the Thru connection page, not in this chat." Create the proxy (the following contains definitions, never a real key): ```http POST https://thru.snd.one/api/proxies Content-Type: application/json { "name": "GitHub", "description": "Read your GitHub profile and repositories.", "target_url": "https://api.github.com", "credential_url": "https://github.com/settings/personal-access-tokens/new", "credentials": [ { "key": "api_key", "label": "GitHub personal access token", "description": "Use a token with only the repository permissions needed for this task.", "placeholder": "github_pat_…" } ], "headers": { "Authorization": "Bearer {{api_key}}", "Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" }, "allowed_methods": [ "GET", "HEAD" ], "expires_in": 86400 } ``` Creation responds with HTTP 201 and a Proxy object. For example: ```json { "id": "", "status": "unauthenticated", "name": "GitHub", "target_url": "https://api.github.com", "auth_url": "https://thru.snd.one/connect/#", "proxy_url": "https://thru.snd.one/p/", "status_url": "https://thru.snd.one/api/proxies/", "access_token": "", "created_at": "", "expires_at": "" } ``` The actual response also includes the public credential schema, configured headers, allowed methods, and other supplied metadata. Send auth_url unchanged to the user. Once connected: ```http GET https://thru.snd.one/api/proxies/ Authorization: Bearer ``` When status is authenticated, fetch the GitHub profile: ```http GET https://thru.snd.one/p//user Authorization: Bearer Accept: application/vnd.github+json ``` For an API configured at https://api.example.com/v1, a request to /p//widgets?limit=10 goes to https://api.example.com/v1/widgets?limit=10. A request to proxy_url itself goes to the configured base path. ## Credential and header schema credentials is an array of 1–8 fields with key and label, and optional description and placeholder. All declared fields are required and must be used by a header template or AWS signing. Use unique lowercase identifier keys such as api_key, username, password, or project_id. headers maps HTTP header names to literal strings with optional {{field_key}} interpolation. Templates support plain substitution only; there are no filters, expressions, base64 encoding, query injection, or body injection. A configured header replaces that same incoming header. Supply the complete secret value as a credential field if the API requires a pre-encoded value, such as HTTP Basic's base64 token. Credential values must contain 1–4096 printable ASCII characters with no line breaks. Cookie and transport or forwarding headers are reserved; authentication uses application headers or AWS signatures. An API that needs multiple user-supplied values can use: ```json { "name": "Acme API", "description": "Use the project API with a key and a separate project identifier.", "target_url": "https://api.example.com/v1", "credentials": [ { "key": "api_key", "label": "API key" }, { "key": "project_id", "label": "Project ID" } ], "headers": { "Authorization": "Bearer {{api_key}}", "X-Project-ID": "{{project_id}}" }, "allowed_methods": [ "GET", "HEAD" ] } ``` AWS APIs can require signing instead of a static header. Each AWS credential reference names a declared credential field; the actual values come from the user's form. Thru signs the complete request body with the configured service and region. This example uses temporary credentials: ```json { "name": "AWS Lambda", "description": "Invoke the Lambda functions needed for this task.", "target_url": "https://lambda.us-east-1.amazonaws.com/2015-03-31/functions", "credential_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html", "credentials": [ { "key": "access_key_id", "label": "AWS access key ID" }, { "key": "secret_access_key", "label": "AWS secret access key" }, { "key": "session_token", "label": "AWS session token", "description": "This example requires temporary credentials, including a session token." } ], "headers": {}, "auth": { "type": "aws-sigv4", "service": "lambda", "region": "us-east-1", "access_key_id": "access_key_id", "secret_access_key": "secret_access_key", "session_token": "session_token" }, "allowed_methods": [ "POST" ], "expires_in": 3600 } ``` To use access-key credentials without a session token, remove the session_token field from credentials and remove auth.session_token. Select the precise AWS service, regional endpoint, and least-privilege IAM permissions for the task. ## Browser-only setup and management GET /api/proxies/{id}/setup with X-Thru-Setup-Token retrieves public setup metadata. POST /api/proxies/{id}/credentials with that same header and JSON {"credentials":{"field_key":"user-entered value"},"allowed_methods":["GET"]} stores the user-entered credentials. The browser can narrow the configured HTTP methods; it cannot add methods. This endpoint returns status authenticated, expires_at, and a management_url ending in #manage=. The setup token is consumed. These calls are for the user's Thru form; agents must not collect provider credentials or perform credential submission on the user's behalf. GET /api/proxies/{id}/manage with Authorization: Bearer retrieves management metadata. DELETE /api/proxies/{id} with that owner token revokes the proxy and erases stored credentials. Save the management link privately; its owner token controls revocation and is never included in agent status responses. ## Limits, trust, and errors - Proxy lifetime defaults to 86,400 seconds (24 hours). expires_in must be an integer from 60 through 604,800 (7 days). The lifetime starts at creation. - Setup expires after one hour or at proxy expiry, whichever comes first. - Allowed methods default to GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS. The user can narrow these during setup. Prefer a read-only list for read-only work. - Targets must be public HTTPS hosts. The configured host and base path are fixed; redirects are not followed. Choose a trusted provider endpoint and verify it. - Request and response bodies are limited to 1 MiB. Responses must be supported UTF-8 text, JSON, or XML; opaque binary or compressed responses are rejected. JSON configuration and credential submissions are limited to 64 KiB and require Content-Type: application/json. Requests time out after 30 seconds. - Each proxy permits 60 requests per minute. On HTTP 429, wait before retrying. - Credentials are encrypted at rest. Thru blocks supported responses containing known direct and encoded credential echoes. This cannot guarantee that a malicious API will never transform or disclose a secret. Use only trusted targets and scoped credentials. An authenticated status means credentials were saved; it does not mean the provider has validated them. - 400: invalid configuration or request. 401: missing or incorrect capability. 403: forbidden cross-origin browser request. 404: unknown proxy or route. 405: a disallowed method. 409: not yet authenticated or setup already completed. 410: expired or revoked. 413: body too large. 415: JSON submission has the wrong Content-Type. 429: rate limited. 502: upstream failure, redirect, or an unsafe response. 503: Thru is not configured. 504: upstream timeout. Error JSON has the shape {"error":{"code":"machine_readable_code","message":"description"}}. Upstream API status codes are preserved when a response passes proxy checks. Never retry authorization failures with provider credentials pasted into chat. Explain the failure and, if necessary, create a new proxy with a fresh setup link.