Rensei docs
Authentication

M2M OAuth

OAuth client_credentials token endpoint.

Machine-to-machine (M2M) authentication lets external services and CI/CD pipelines obtain short-lived access tokens without a user login. Rensei implements the standard OAuth 2.0 client_credentials grant.

When to use M2M vs API keys

ScenarioRecommended mechanism
CI/CD pipeline triggers, external service integrationsM2M client_credentials token
CLI operator, long-running daemon workerrsk_live_* API key
Single daemon worker registrationWorker registration key → runtime JWT

Use M2M tokens when you need org-scoped access from a service identity that is not a human user and where short token lifetimes (automatic expiry) are a compliance requirement.

Creating an M2M client

M2M clients are managed in the Rensei admin panel:

  1. Navigate to Admin > M2M Clients.
  2. Click Create Client.
  3. Note the client_id and client_secret - the secret is shown only once.

M2M clients are org-scoped. Each client is associated with a specific organization and inherits the org's policy boundaries.

Token endpoint

POST /api/oauth/token

Accepts application/x-www-form-urlencoded or application/json. Returns a standard OAuth 2.0 token response.

Request fields

FieldRequiredDescription
grant_typeYesMust be client_credentials
client_idYesYour M2M client ID
client_secretYesYour M2M client secret

Example - form-encoded

curl -X POST https://app.rensei.ai/api/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=m2m_abc&client_secret=secret_xyz"

Example - JSON body

curl -X POST https://app.rensei.ai/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "m2m_abc",
    "client_secret": "secret_xyz"
  }'

Success response

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

The response includes Cache-Control: no-store and Pragma: no-cache headers to prevent token caching by intermediaries.

Error responses

HTTP statusCondition
400 Bad Requestgrant_type is not client_credentials, or client_id/client_secret missing
401 UnauthorizedInvalid client_id or client_secret

Using the access token

Pass the token as a bearer token in the Authorization header:

curl https://app.rensei.ai/api/public/sessions \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token lifecycle

Request a token via POST /api/oauth/token with your client_id and client_secret.

Cache the token for its expires_in duration (typically 3600 seconds). The endpoint emits Cache-Control: no-store, so caching is the client's responsibility.

Refresh before expiry. Request a new token before the current one expires. The client_credentials grant always issues a fresh token - there is no refresh token in this flow.

TypeScript example

interface TokenResponse {
  access_token: string;
  token_type: string;
  expires_in: number;
}

async function getM2MToken(clientId: string, clientSecret: string): Promise<string> {
  const res = await fetch('https://app.rensei.ai/api/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type: 'client_credentials',
      client_id: clientId,
      client_secret: clientSecret,
    }),
  });

  if (!res.ok) {
    throw new Error(`Token request failed: ${res.status}`);
  }

  const data = (await res.json()) as TokenResponse;
  return data.access_token;
}

Security considerations

  • Store client_secret in a secrets manager - never in source code or environment files committed to version control.
  • Token lifetimes are short by design. Treat each token as ephemeral; your client must handle re-issuance.
  • M2M tokens carry org-scoped permissions. Audit client creation and deletion events in the Audit log.

On this page