← Documentation

Integrations

api_call, connected credentials, placeholder substitution, and response redaction.

Integrations

Beyond the platform's own built-in tools, everything an agent does goes through api_call — an HTTP client scoped to this AIOS host. Every callable surface lives at /api/integrations/<app_slug>/<tool_slug> (plus any documented path parameters); discover what's available with search (source: "marketplace"), and read full per-app docs with api_call({ method: "GET", path: "/api/tools/<app_slug>" }).

Connected credentials: never ask a user to paste a secret into chat

Never ask a user to type or paste a password, API key, or token into the chat — chat messages persist, get logged, and can end up in memory or an export. When an agent needs to store a credential — for instance, right after it fills out a signup form itself — it saves it with credentials.store: a service slug, a login identifier, and the secret. The stored value never comes back in that call's own response (just confirmation it was stored, nothing else); reading it back later also needs approval, and the plaintext is handed to the model only transiently, for that one turn's use, never persisted into the visible transcript.

For a service the platform connects on the user's behalf, there's nothing to store at all: on auth_required, the chat UI renders a Connect button automatically — don't tell the user to go find a settings page; say what you were trying to do and retry after they've connected.

For apps with several connections, list their ids at GET /api/tools/<app_slug>?view=connections and pass connection_id as a top-level api_call parameter to choose one; without it, the default is used.

Placeholder substitution

Placeholder text is substituted into an api_call request immediately before it's sent, never before — most notably {{shareable_file_url:<artifact_id>:<ttl_minutes>}} (from library.get_shareable_file_url, see library), which becomes a real, time-boxed signed URL only at the moment of use.

Response redaction: secrets and signed URLs never appear in responses

Every api_call response passes through redaction before the model, the transcript, or any log sees it. A tool declared to return something sensitive gets that field replaced with a placeholder in the response the model receives; a signed URL substituted into a request is scrubbed back to its placeholder form in the response too — even if the destination's own error message tries to echo it back verbatim. This holds regardless of whether the secret shows up in a body, a header, or a rate-limit response. The practical rule: you will never see a real password, token, or signed URL in a tool result — only a redacted stand-in, or, for a value you genuinely need for your very next step, a transient value scoped to that one turn.

Errors

Every integration normalizes failures to one of: auth_required, auth_failed, permission_denied, rate_limited, not_found, invalid_input, upstream_error. rate_limited carries a retry-after hint when the destination provided one; invalid_input names the offending field.