Configuration
The SDK is configured through the ClientConfig interface passed to the ReasoningLayerClient constructor. Three fields are required: baseUrl, tenantId, and auth. Everything else has sensible defaults.
import { ReasoningLayerClient } from '@kortexya/reasoninglayer';
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: '550e8400-e29b-41d4-a716-446655440000', auth: { mode: 'bearer', token: process.env.RL_API_TOKEN! }, userId: '00000000-0000-0000-0000-000000000001', namespaceId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', timeoutMs: 15000, maxRetries: 2, retryOn503: true,});ClientConfig reference
| Option | Type | Default | Description |
|---|---|---|---|
baseUrl | string | required | Base URL of the Reasoning Layer API. Trailing slashes are stripped automatically. |
tenantId | string | required | Tenant UUID. Sent as X-Tenant-Id on every request. Not overridable per-call. |
auth | AuthConfig | required | Authentication mode. See Authentication below. |
userId | string | undefined | Default user UUID. Sent as X-User-Id. Overridable per-call via RequestOptions. |
namespaceId | string | undefined | Default namespace UUID. Sent as X-Namespace-Id. Overridable per-call via RequestOptions. |
authenticatedUser | string | undefined | Authenticated user identifier. Sent as X-Authenticated-User. |
timeoutMs | number | 30000 | Default request timeout in milliseconds. Overridable per-call. |
maxRetries | number | 3 | Maximum retry attempts for retryable errors (429, optionally 503). |
retryOn503 | boolean | false | Whether to retry on HTTP 503 (Service Unavailable). 429 is always retried. |
interceptors | Interceptor[] | [] | Request/response interceptors. See Interceptors below. |
fetch | typeof globalThis.fetch | globalThis.fetch | Custom fetch implementation for dependency injection or testing. |
Authentication
The auth field is required and explicit — the SDK has no implicit unauthenticated mode. Pick one of two modes depending on where the SDK is running.
Bearer token — server-side / programmatic usage
Send a long-lived API token (typically a service-account token issued by the auth gateway) on every request as Authorization: Bearer <token>.
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: '550e8400-e29b-41d4-a716-446655440000', auth: { mode: 'bearer', token: process.env.RL_API_TOKEN! },});Session cookie — in-browser SPA usage
Rely on the session cookie set by the auth gateway after the user has logged in interactively. The SDK issues HTTP requests with credentials: 'include' so the cookie is attached even on cross-origin requests; WebSocket connections rely on the browser attaching the cookie automatically (same-origin).
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: '550e8400-e29b-41d4-a716-446655440000', auth: { mode: 'cookie' },});The choice between modes belongs to the deployment context of the caller, not the SDK — pick bearer from servers and CI, cookie from browser code that has already gone through the platform’s login flow.
Authentication headers
The SDK derives every authentication-related HTTP header from ClientConfig. You should not set them yourself.
| Header | Source | Per-call override |
|---|---|---|
Authorization | ClientConfig.auth (bearer mode) | No |
X-Tenant-Id | ClientConfig.tenantId | No — requires a separate client instance |
X-User-Id | ClientConfig.userId | Yes, via RequestOptions.userId |
X-Namespace-Id | ClientConfig.namespaceId | Yes, via RequestOptions.namespaceId |
X-Authenticated-User | ClientConfig.authenticatedUser | No |
Retry behavior
The SDK automatically retries failed requests with exponential backoff and jitter.
Always retried:
- HTTP 429 (Too Many Requests) — respects the
Retry-Afterheader if present
Optionally retried (when retryOn503: true):
- HTTP 503 (Service Unavailable)
Never retried:
- HTTP 4xx errors (400, 404, 409, etc.)
- Timeout errors
- User-initiated abort
Backoff formula
delay = min(1000ms * 2^(attempt-1) + random(0..1000ms), 30000ms)For 429 responses with a Retry-After header, the SDK uses the server-specified delay instead.
Disabling retries
Set maxRetries: 0 globally:
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'my-tenant-uuid', auth: { mode: 'cookie' }, maxRetries: 0,});Timeouts
The default timeout is 30 seconds. When a request exceeds its timeout, the SDK throws a TimeoutError.
import { TimeoutError } from '@kortexya/reasoninglayer';
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'my-tenant-uuid', auth: { mode: 'cookie' }, timeoutMs: 10000, // 10 second default});
try { const result = await client.inference.backwardChain({ goal: psi('complex_goal', {}), maxDepth: 200, timeoutMs: 5000, // Server-side timeout: stop searching after 5 seconds });} catch (error) { if (error instanceof TimeoutError) { console.log(`Timed out after ${error.timeoutMs}ms`); }}Interceptors
Interceptors are middleware functions that wrap each HTTP request. They receive the Request object and a next function, and must return a Response. Interceptors are called in the order they are provided.
import type { Interceptor } from '@kortexya/reasoninglayer';
// Logging interceptorconst logger: Interceptor = async (request, next) => { const start = performance.now(); console.log(`--> ${request.method} ${request.url}`);
const response = await next(request);
const duration = Math.round(performance.now() - start); console.log(`<-- ${response.status} (${duration}ms)`);
return response;};
// Dynamic auth token interceptor (for token refresh scenarios)const dynamicAuth: Interceptor = async (request, next) => { const modified = new Request(request, { headers: new Headers(request.headers), }); modified.headers.set('Authorization', `Bearer ${await refreshToken()}`); return next(modified);};
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'my-tenant-uuid', auth: { mode: 'cookie' }, interceptors: [logger, dynamicAuth],});Interceptors execute in order: logger wraps authToken, which wraps the actual fetch call.
Custom fetch implementation
Inject a custom fetch for testing or specialized environments:
// Testing with a mock fetchconst client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'my-tenant-uuid', auth: { mode: 'cookie' }, fetch: myMockFetch,});This is useful for:
- Unit testing without network calls
- Custom HTTP agents or proxies
- Environments where
globalThis.fetchneeds augmentation
Multi-tenant usage
Since tenantId is not overridable per-call, create separate clients for each tenant:
const tenantA = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'tenant-a-uuid', auth: { mode: 'bearer', token: process.env.RL_TOKEN_A! },});
const tenantB = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'tenant-b-uuid', auth: { mode: 'bearer', token: process.env.RL_TOKEN_B! },});
// Each client operates in complete tenant isolationconst sortsA = await tenantA.sorts.listSorts();const sortsB = await tenantB.sorts.listSorts();Full configuration example
import { ReasoningLayerClient } from '@kortexya/reasoninglayer';import type { Interceptor } from '@kortexya/reasoninglayer';
const logger: Interceptor = async (request, next) => { console.log(`${request.method} ${request.url}`); return next(request);};
const client = new ReasoningLayerClient({ baseUrl: 'https://api.example.com', tenantId: '550e8400-e29b-41d4-a716-446655440000', auth: { mode: 'bearer', token: process.env.RL_API_TOKEN! }, userId: '00000000-0000-0000-0000-000000000001', namespaceId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', authenticatedUser: 'admin@example.com', timeoutMs: 15000, maxRetries: 2, retryOn503: true, interceptors: [logger],});