Relay-E
API Reference

Authentication

Per-tenant API keys via /v1/api-keys. DEV_API_KEY remains available as a fallback for local development.

All /v1/* endpoints require a Bearer token in the Authorization header:

curl http://localhost:3001/v1/skills \
  -H "Authorization: Bearer rle-<your-key>"

Missing or incorrect → 401 unauthorized.

Relay-E issues API keys per tenant. Each key is scoped to a single tenant — a key can only access that tenant's connectors, skills, sessions, and usage data.

Create a key

POST /v1/api-keys
Authorization: Bearer <existing-key-or-DEV_API_KEY>
Content-Type: application/json

{ "name": "production" }

Response (key shown once — store it securely):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "production",
  "prefix": "rle-a1b2c3",
  "raw": "rle-a1b2c3d4e5f6...",
  "createdAt": "2026-04-26T00:00:00.000Z"
}

The raw field is the key to use in Authorization: Bearer. It is never returned again after this response.

List keys

GET /v1/api-keys
Authorization: Bearer <key>

Returns active (non-revoked) keys for the calling tenant — no hashes, no raw values:

{
  "data": [
    {
      "id": "550e8400-...",
      "name": "production",
      "prefix": "rle-a1b2c3",
      "lastUsedAt": "2026-04-26T10:00:00.000Z",
      "createdAt": "2026-04-26T00:00:00.000Z"
    }
  ]
}

Revoke a key

DELETE /v1/api-keys/{id}
Authorization: Bearer <key>

Soft-deletes the key by setting revoked_at. Subsequent requests using the revoked key receive 401.

DEV_API_KEY (local development only)

For local development, set DEV_API_KEY in .env:

.env
DEV_API_KEY=sk_dev_local_change_me

When a request arrives with this key and no matching DB row is found, Relay-E falls back to a built-in dev-tenant. This avoids needing a database round-trip during initial setup.

Do not use DEV_API_KEY in production. It bypasses the per-tenant key lookup and always resolves to the same internal tenant. Treat it as a master key.

In production

For multi-tenant SaaS, each of your customers gets their own API key via POST /v1/api-keys. Your application:

  1. Creates a tenant (or derives one from your own user model).
  2. Calls POST /v1/api-keys on behalf of that tenant to issue a key.
  3. Stores the key securely in your own secrets manager or sends it to the customer.
  4. The customer uses the key directly in Authorization: Bearer.

For setups where end users should never see a Relay-E key, proxy through your own backend:

your-app/api/relay-e/[...path].ts
export async function POST(req: Request, { params }: { params: { path: string[] } }) {
  const user = await authenticateRequestSomehow(req);
  if (!user) return new Response("unauthorized", { status: 401 });

  const url = `${process.env.RELAY_E_URL}/v1/${params.path.join("/")}`;
  return fetch(url, {
    method: "POST",
    headers: {
      "authorization": `Bearer ${process.env.RELAY_E_KEY}`,
      "content-type": "application/json",
    },
    body: req.body,
  });
}

Your customers never see the Relay-E key — your backend holds it and proxies on their behalf.

Common errors

{
  "error": {
    "type": "authentication_error",
    "code": "unauthorized",
    "message": "Missing or invalid API key",
    "request_id": "req_..."
  }
}

The same response is returned whether the key is missing or wrong — don't leak whether a key exists. Use request_id for debugging.

Roadmap

ComingWhy
Scoped tokens (skills, sessions, expiry)Customer-issuable, narrowly-scoped
OAuth (Authorization Code + PKCE)Proper SaaS auth
mTLSHigh-compliance customers
JWT supportEasy integration with Clerk / WorkOS / Auth0 / Cognito

On this page