End-user identity and credentials

When you build a multi-tenant product on Runtype, the calls to Runtype are made by your backend with your API key, but the person on the other end is one of your end users. End-user identity lets Runtype recognize that person so an agent can scope memory, records, and secrets to them, and act on their behalf.

This guide covers two related capabilities: proving who an end user is (Identity Exchange), and letting an end user connect their own third-party account so an agent can use it (credential registration).

End-user identity is for multi-tenant products where individual end users need their own scoped data or their own connected accounts. If every execution runs as your organization, you do not need any of this. Your API key already scopes everything to your account.

How it fits together

Three pieces work as a chain:

  1. Prove the end user. Your app sends Runtype a proof of who the end user is. Runtype verifies it and projects a durable end-user identity.
  2. Scope to the end user. That identity scopes the agent’s memory, records, and secrets, so one end user never sees another’s data.
  3. Connect their accounts. An end user attaches their own GitHub (and, over time, other) account. The token is stored against their identity and resolved for them at execution time.

You can adopt the pieces independently. Scoping works with a plain end-user id today; verified proofs and credential registration build on top.

Proving an end user’s identity

Runtype verifies end users two ways. Both produce the same durable end-user identity, so the rest of the platform does not care which you use.

PathUse it whenWhat you provide
Identity ExchangeYou already have an identity provider (Clerk, WorkOS, Auth0, Okta, or your own OIDC issuer)An integration that pins your issuer, plus a token per request
Runtype-hosted identityYou want Runtype to handle end-user sign-in for youNothing to run. Your end users sign in through a Runtype-hosted, branded page

Identity Exchange (bring your own IdP)

Identity Exchange is a token broker. You register your identity provider once as an integration, then pass a short-lived token from that provider on each request. Runtype verifies the token against your provider’s public keys and maps the verified subject to an end user.

Register an integration with your issuer’s details. Runtype mints the integration id, which is the isolation boundary between one provider and another.

cURL
$curl https://api.runtype.com/v1/identity-integrations \
> -H "Authorization: Bearer rt_live_abc123..." \
> -H "Content-Type: application/json" \
> -d '{
> "name": "Acme Clerk",
> "provider": "clerk",
> "descriptor": {
> "kind": "oidc-jwt",
> "issuer": "https://clerk.acme.com",
> "jwksUri": "https://clerk.acme.com/.well-known/jwks.json",
> "audience": "acme-production",
> "allowedAlgorithms": ["RS256"],
> "claimMap": {
> "subject": "sub",
> "email": "email"
> }
> }
> }'

The descriptor is validated when you create it:

FieldRequiredNotes
kindYesoidc-jwt for a verifiable JWT, session for an opaque session token, or custom
issuerYes for oidc-jwtMust be an https URL. Adapter selection keys off the token’s verified issuer, never a client-supplied hint
jwksUriYes for oidc-jwthttps endpoint serving your issuer’s public keys
audienceYes for oidc-jwtMust name a specific relying party. Wildcards are rejected
allowedAlgorithmsYes for oidc-jwtAsymmetric algorithms only (for example RS256, ES256). Symmetric algorithms are rejected
claimMapYesMaps identity fields to token claims using dot notation. subject is required; tenant, email, roles, and permissions are optional

Only one active integration may pin a given issuer. A second one is rejected so a token can never match two integrations ambiguously.

Runtype-hosted identity

If you do not run your own identity provider, Runtype can host end-user sign-in for you. Your end users sign in through a Runtype-hosted page branded to your product, and Runtype issues them a token you present the same way as any other proof. This path needs no integration setup on your side. Contact Runtype to enable it for your account.

Passing a proof on a request

Once an integration exists, attach the end user’s token to a dispatch or chat request as identityProof. provider is a hint that names the integration you expect; token is the proof itself.

cURL
$curl https://api.runtype.com/v1/dispatch \
> -H "Authorization: Bearer rt_live_abc123..." \
> -H "Content-Type: application/json" \
> -d '{
> "agentId": "agt_...",
> "userMessage": "Summarize my open pull requests",
> "identityProof": {
> "provider": "clerk",
> "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6..."
> }
> }'

When the proof verifies, the verified end user replaces any tenant or end-user fields in the request body. Body-asserted identity is never trusted for browser-embedded callers, so the proof is the only thing that grants a verified end-user scope.

Treat the token like a secret. Send a freshly minted, short-lived token per request. Runtype never logs, stores, or echoes it. Do not put a long-lived token in client-side code.

Verified-proof upgrades are rolling out for multi-tenant deployments. If a request presents a proof before the upgrade is enabled for your account, the proof is ignored. A strategy-less resource keeps its existing scoping behavior. An end-user-isolated agent called from a browser or client-token surface instead fails closed with 403 because no verified end user reaches the tenancy gate; it never silently runs unscoped. A trusted backend using a management API key can continue to pass endUser directly. Contact Runtype before enabling the strategy for an embedded surface.

Scoping an agent to the verified end user

A verified proof establishes identity, but it does not change an agent’s data scope by itself. Set the saved agent’s Tenancy Strategy to end-user-isolated when you create or update it:

Agent config
1{
2 "config": {
3 "tenancyStrategy": {
4 "preset": "end-user-isolated"
5 }
6 }
7}

This preset requires a verified end user and fails closed when one is missing. For an admitted execution, Runtype applies the same end-user scope to memory, records, and secret resolution. Keep the strategy on the saved agent definition; do not accept it from an end-user-controlled dispatch body.

Connecting an end user’s third-party account

Credential registration lets an end user attach their own third-party account (GitHub first) so an agent can act as them, using their permissions. The connect step verifies the end user, then hands their browser to the provider’s consent screen; the callback stores the resulting token against that end user.

The flow has two legs.

Start the connection

Your backend calls the connect endpoint with the end user’s proof. It returns a URL to send the end user to.

cURL
$curl https://api.runtype.com/v1/end-user-integrations/github/connect \
> -H "Authorization: Bearer rt_live_abc123..." \
> -H "Content-Type: application/json" \
> -d '{
> "identityProof": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
> "redirectAfter": "https://app.acme.com/settings/connections"
> }'

The response includes an authorizeUrl. Redirect the end user’s browser there.

1{
2 "authorizeUrl": "https://github.com/login/oauth/authorize?client_id=...&state=...",
3 "state": "a1b2c3...",
4 "endUserId": "eu_..."
5}

Complete the connection

GitHub sends the end user back to the callback URL with an authorization code. Runtype exchanges it for a token, stores that token scoped to the end user, and redirects the browser to the redirectAfter URL you supplied at connect. You do not build the callback; it is a Runtype endpoint.

The end user now has a stored, per-user credential. Reconnecting later replaces the stored token in place.

Connecting a provider requires the provider’s OAuth app to be configured on your deployment. Until it is, the connect endpoint returns 501 Not Configured. Contact Runtype to provision a connector.

Using the stored credential

The token is stored under a stable key scoped to the end user. A GitHub token is stored as GITHUB_TOKEN. Reference it in a tool config the same way you reference any secret:

{{secret:GITHUB_TOKEN}}

When an agent with the end-user-isolated Tenancy Strategy runs for that end user (identified by a verified proof), {{secret:GITHUB_TOKEN}} resolves to their token. A different end user resolves to their own, and an end user who has not connected an account has no token to resolve. The key is constant across end users; the isolation comes from the end-user scope, not the name.

Memory-only scoping without a proof

Even before you wire up proofs, a trusted backend using a management API key can partition an agent’s memory per end user. Set the agent’s memory profileTemplate to {{_endUser.id}} and pass the end-user id at dispatch. This legacy setup scopes memory only. It does not scope records or secret resolution, so do not use it to isolate connected credentials. Use a verified proof with the end-user-isolated Tenancy Strategy for that. See Creating and configuring agents for the memory scoping options.