Relay documentation
Relay is OAuth and identity for AI agents. It does two jobs: it gives an agent a portable identity, and it lets your application accept agents through a consent flow.
The SDK has zero runtime dependencies and works in Node 18+, Deno, Bun, browsers and edge runtimes.
Install
The package is scoped. npm i empyre@relay-sdk fails with a 404 — that syntax asks npm for a package called empyre at a version tag.
npm i @empyre/relay-sdk
Two things Relay does
Relay Identity — mint an agent and get an Agent ID and Agent Secret. That pair is the agent's whole credential; it exchanges the pair for short-lived tokens at any application that accepts Relay.
Relay Platform — add Continue with Relay to your own application, so agents authenticate into it through a hosted consent flow using OAuth 2.1 with PKCE.
Give an agent an identity
Create an agent in the Relay identity dashboard, then authenticate it. Sign in by the target application's domain — Relay resolves it to that application's client id, so you never handle the opaque identifier.
import { RelayClient } from "@empyre/relay-sdk";
const relay = new RelayClient({ audience: "example.com" });
const result = await relay.authenticateAgent(
process.env.RELAY_AGENT_ID,
process.env.RELAY_AGENT_SECRET,
);
if ("status" in result && result.status === "pending_approval") {
// The owner receives a one-tap approval email. Retry after approval.
console.log(result.message);
} else {
console.log(result.access_token);
}
Permission modes
Chosen when the agent is created, and changeable afterwards.
ask_critical
The default. Normal actions run; critical ones email the owner for one-tap approval.
ask_everything
Every token request waits for the owner's approval.
full_access
The agent acts without asking. Everything is still recorded and still revocable.
Continue with Relay (authorisation code + PKCE)
The security contract requires a high-entropy state and an S256 PKCE challenge. The callback must consume the stored state exactly once and pass the matching verifier to exchangeCode.
import { RelayClient, createPkcePair, createOAuthState } from "@empyre/relay-sdk";
const relay = new RelayClient({
clientId: process.env.RELAY_CLIENT_ID,
clientSecret: process.env.RELAY_CLIENT_SECRET, // server-side only
});
// 1. Start the flow
const { verifier, challenge } = await createPkcePair();
const state = createOAuthState();
// store { verifier, state } in the session, then redirect the user to:
const url = relay.authorizeUrl({
redirectUri: "https://example.com/callback",
state,
codeChallenge: challenge,
});
// 2. In the callback, after checking state matches and consuming it once:
const tokens = await relay.exchangeCode(code, verifier, "https://example.com/callback");
// 3. Later
const refreshed = await relay.refresh(tokens.refresh_token);
await relay.revokeToken(tokens.access_token);
SDK surface
<code>authenticateAgent(agentId, agentSecret, scopes?)</code>
Exchange an agent credential pair for a short-lived access token, or receive a pending-approval response.
<code>authorizeUrl(params)</code>
Build the hosted consent URL. Requires high-entropy state and an S256 challenge.
<code>exchangeCode(code, codeVerifier, redirectUri)</code>
Redeem a one-time authorisation code for tokens.
<code>refresh(refreshToken)</code>
Rotate the refresh token. Each refresh invalidates the previous one.
<code>revokeToken(token)</code>
Revoke immediately. Revocation and introspection fail closed.
<code>resolveClient(domain)</code>
Look up an application's client id from its domain.
<code>createPkcePair()</code> / <code>createOAuthState()</code>
Generate an S256 verifier and challenge, and a high-entropy state value.
Security properties
Authorisation codes are one-time. Refresh tokens rotate, and replaying a spent refresh token revokes the entire token family — because a replay means the token leaked. Revoke and introspect fail closed, so an outage denies access rather than granting it.
The client secret is server-side only. An agent using Relay Identity does not need it; it uses its Agent ID and Agent Secret.
Relay's OAuth contract is frozen. Endpoint semantics and field names under the OAuth routes do not change, so an integration built against the published SDK keeps working.
Resolving a client id directly
If you would rather not use the SDK for this one call:
GET https://api.empyre.dev/relay/oauth/clients/resolve?domain=example.com
Frequently asked questions
What is the package name?
@empyre/relay-sdk. There is no package called @empyre/relay.
Do I need PKCE if I have a client secret?
Yes. Relay requires an S256 challenge regardless. Agents are frequently public clients, and requiring PKCE everywhere removes a whole class of interception attack rather than depending on deployment details.
What happens if a refresh token is used twice?
The entire token family is revoked. A second use means the token was captured, so the safe response is to end the session rather than to serve it.
Can I use Relay without Empyre?
Yes. Relay is a separate product with separate pricing. It shares the account system with Empyre, which is why one login works for both, but it has no dependency on you running a business on Empyre.
Relay — OAuth for AI agents
Hosted consent, S256 PKCE, scoped tokens, refresh rotation and fail-closed revocation.