Who owns what
Receipt Connect splits ownership deliberately between the integration provider and Receipt. The integration provider owns external authorization — OAuth, API keys and tokens — the provider credential itself, and token refresh. Receipt owns your user and workspace authentication, Receipt Connect token issuance, the mapping from a workspace connection name to a provider connection, credential materialization for real CLIs, and runtime policy, receipts and audit.Every
/connect/call action and every background run writes receipts. Calls made through the aggregate MCP bridge are not yet recorded — see the aggregate MCP server.What Receipt stores
For Receipt Connect integrations, the raw provider credential stays with the integration provider. Receipt stores only an encrypted reference:org_connection_secret table, encrypted with AES-256-GCM under a key from RECEIPT_CONNECTION_ENCRYPTION_KEY_B64. The browser never sees the encrypted payload: replication carries only the connection’s id, organization, workspace, provider, name, kind, status, expiry, last-validated time and timestamps, and only to a reader who is a member of that workspace — so the provider’s own integration and connection ids stay server-side. For the cipher and the full column allowlist, see the security model.
The two cases where Receipt does hold a credential
A connection has one of four kinds:
The last two are the only cases where Receipt holds a credential. Both are explicit, user-initiated imports, and both are encrypted in the same table as the references.
Every connection also carries a status of
valid, expired or invalid, plus an expiry and a last-validated time when the provider supplies them.
How a connection is addressed
A connection is addressed asprovider:name, and default is the implicit name. When a name is supplied, Receipt normalizes it: the value is trimmed, whitespace becomes -, and the result is lowercased. An empty name becomes default.
The gateway accepts either a connection id or a provider:name selector, and the organization and workspace predicates are part of the same query as the selector — so a valid id from another organization can never be used as a credential lookup for yours.
Capability keys follow one rule: the bare provider id for the default connection, and provider.name for a named one. One AWS connection plus a second one named prod therefore appear under two keys:
prod does not produce an AWS CLI profile called prod. For AWS, Receipt writes the profile receipt for the default connection and receipt-<name> for a named one — so prod becomes the profile receipt-prod. The default connection is additionally installed as the [default] profile, so a plain aws command inside a run still routes through Receipt Connect even when a tool subprocess drops the environment variables.
The Integrations pages never offer a connection-name field, so the connections you create there are all
default, and receipt connect <provider> does not send a name either. A connection gets a name only when the client that opens the connect session supplies one. One feature does: cloud accounts connected inside the Agent Registry use the reserved name agent-registry and are filtered out of the Integrations list, so a registry scan never reuses — and is never reused by — the AWS, Azure or Google Cloud connection you made for everything else.The capability manifest
GET /connect/capabilities returns the manifest of what the caller can currently reach. Each capability carries a command, provider, source, transport, status, connectionId, name, metadata and discoveredAt, plus an expiresAt when the connection has one.
- A capability’s
statusisvalid,expiredorinvalid. - The manifest’s own status is
onlinewhen any capability exists, andofflineotherwise.
invalid, expired, failed, failure, error, disconnected, revoked, unauthorized, unauthenticated, missing and blocked — so an expired connection is present in the manifest but is not usable, and a chat request that needs it does not start a background run.
What a connector publishes
Receipt classifies each catalog connector into exactly one execution surface, derived purely from the checked-in manifest and never from connection metadata. The surface decides what tools, if any, that connector publishes.
Across the 63 connectors that comes to 91 static Receipt tools, of which 3 are writes. Writes stay off until an owner or admin enables them for that connection; there is no per-action approval prompt in this release. See tools and permissions.
A
typed-proxy manifest is validated when the catalog loads, so the rules are hard: additionalProperties: false is enforced on every tool schema, GET endpoints must be classed read, path placeholders must match the declared path parameters in order and each must be a required string input, and any base-URL override must be an HTTPS origin with no path, query or fragment. At call time arguments are validated against that schema, path parameters are URL-encoded into the path, and remaining arguments become query parameters on a GET or a JSON body otherwise. Request bodies are capped at 1 MiB.
gcp sits in two places at once. Its manifest declares both a command-backed credential mode and proxy tools, and the classifier checks proxy tools first — so gcp reports as typed-proxy while still producing an isolated gcloud configuration in a worker.read-provider-resource
Every ordinary connector without a narrower manifest gets exactly one Receipt-owned compatibility tool, and so do the two Atlassian OAuth connectors. Its description, verbatim:
Read one provider-relative API resource through Receipt and Nango. This tool permits GET only; absolute URLs, request bodies, headers, and proxy routing overrides are rejected.Its input schema accepts only two things: a required
path that must begin with a single / and be at most 4096 characters, and an optional query object of scalars or arrays of scalars — at most 100 properties, string values at most 8192 characters, at most 100 array items. Nothing else is accepted.
The path parser rejects non-GET methods, paths that do not start with /, //-prefixed paths, backslashes, #, . and .. segments, %2f and %5c encodings (decoded repeatedly, up to ten times), /https:-style absolute-URL smuggling, and any request body. The tool takes no headers at all, and on the underlying call path a header is rejected rather than dropped — authorization, cookie, host, content-length, transfer-encoding, and anything beginning nango-, proxy- or x-forwarded- fail the request with header '<name>' is not allowed.
Responses are bounded at 5 MiB with a 30-second timeout, and only a safe allowlist of response headers is echoed back: content-language, content-type, etag, last-modified, link, retry-after, x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset.
Next step: decide which operations each connection allows.