TypeScript SDK
This guide shows how to install the TypeScript SDK (@enterprise-ai/sdk), connect it to the Control Plane of the
Navin Platform and call every resource group of API v1 with typed errors, retries, pagination and streaming.
Source: sdks/typescript/packages/sdk/src. Every class, method and type is listed in the
generated reference.
Version française : typescript.fr.md
Overview
EnterpriseAI is a promise based client over the standard fetch API. It exposes one sub-client per resource
group of API v1 in camelCase (client.agents, client.gateway, client.knowledgeBases, client.finops...). Each
method maps one documented route, sends the authentication and tenancy headers, retries the transient failures and
throws a typed EaiError subclass. The package also contains ManagerClient for the Navin Manager
(Navin Manager SDK), offline contract validation (Contracts v1) and agent helpers
(AgentRunner, ContextBudget). Realtime voice lives in the separate package @enterprise-ai/realtime
(Realtime guide).
Requirements and compatibility
| Item | Supported |
|---|---|
| Node.js | 18 or later ("engines": { "node": ">=18" }), which provides fetch, AbortController and crypto |
| Other runtimes | Any runtime with a standard fetch: browsers, Deno, Bun, edge workers; or pass your own fetch |
| Module formats | ESM (import) and CommonJS (require), with type declarations for both |
| TypeScript | Types are shipped in dist/index.d.ts; strict mode is supported |
| Dependencies | ajv and ajv-formats (offline contract validation) |
| Operating systems | Any OS supported by the runtime (Linux, macOS, Windows) |
| Control Plane API | v1 |
In a browser, authenticate with an OIDC token provider, never with an API key: anything shipped to a browser is readable by its user.
Installation
-
Download the packages from Harbor. The SDK packages are hosted in the
sdkproject of the Navinspire Harbor registry (artifactsdk/typescript, tag = SDK version). The Navinspire registry isregistry.navinspire.ai; log in with a robot account that can pull from thesdkproject (given by the platform team). If your organization mirrors thesdkproject in its own Harbor, use the address of that mirror instead.oras login registry.navinspire.aioras pull registry.navinspire.ai/sdk/typescript:1.0.2 -o sdk-typescriptExpected result: the
sdk-typescriptfolder holdsenterprise-ai-sdk-1.0.2.tgzandenterprise-ai-realtime-1.0.2.tgz. -
Add the package to your project.
npm install ./sdk-typescript/enterprise-ai-sdk-1.0.2.tgznpm install ./sdk-typescript/enterprise-ai-realtime-1.0.2.tgzThe second line is needed only for voice live sessions. Expected result:
package.jsonlists@enterprise-ai/sdkandnode -e "import('@enterprise-ai/sdk').then(m => console.log(typeof m.EnterpriseAI))"printsfunction.If it fails: check
node --version(18 or later) and the registry settings of npm (npm config get registry).
Offline install from the tarball
-
Copy the tarball pulled from Harbor to the target machine.
package.jsonthen holds afile:reference to it; keep the tarball in a folder of the project. The two dependencies of the SDK come from the npm registry configured on the target, or from their own tarballs.npm install ./enterprise-ai-sdk-1.0.2.tgz -
Or build from this repository.
cd sdks/typescript && npm ci && npm run build
Quickstart
This example lists the models the caller may use and sends one chat completion through the gateway, the only path to the models.
import { EnterpriseAI } from "@enterprise-ai/sdk";
const client = new EnterpriseAI({ baseUrl: "<control-plane-url>", apiKey: process.env.EAI_API_KEY, locale: "en" });
const models = await client.models.list({ limit: 20 });
for (const model of models.items) console.log(model.id);
const reply = await client.gateway.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: "Bonjour" }] });
console.log(reply.choices[0]?.message?.content);
What happens: the constructor checks the base URL (https required except on localhost), every call sends the key
as X-API-Key and a fresh W3C traceparent, and the completion is posted with an Idempotency-Key. Expected
result: the model ids, then the text of the answer. A refused call throws a typed error such as
PolicyDeniedError (Errors).
Authentication
The server always resolves the tenant from the credential. The SDK does not read environment variables: pass the
values explicitly, for example from process.env.
| Method | Option | Header sent |
|---|---|---|
Tenant API key eai_<prefix>_<secret> (servers only) | apiKey | X-API-Key |
| OIDC access token, fixed or refreshed by a provider called before each request | token: string | () => string | Promise<string> | Authorization: Bearer <token> |
Development mode (server started with EAI_AUTH_DEV=1) | tenant, devUser, devRoles | X-Dev-Tenant, X-Dev-User, X-Dev-Roles |
| Workload identity (SPIFFE, mTLS) | none: the mesh authenticates the workload | set by the mesh |
import { EnterpriseAI } from "@enterprise-ai/sdk";
const browserClient = new EnterpriseAI({ baseUrl: "<control-plane-url>", token: async () => "<token>", locale: "fr" });
const keys = await browserClient.apiKeys.list();
client.apiKeys.create(...) creates a key (the secret is shown once) and client.apiKeys.rotate(id) rotates it.
Client configuration
new EnterpriseAI(options: ClientOptions):
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
baseUrl | string | none | yes | Control Plane URL without /v1. https required except for localhost, 127.0.0.1 and ::1; embedded credentials are refused. |
apiKey | string | none | no | Tenant API key, sent as X-API-Key. |
token | string | (() => string | Promise<string>) | none | no | OIDC token or provider, sent as Authorization: Bearer. |
tenant | string | none | no | Development tenant slug, sent as X-Dev-Tenant. |
devUser | string | none | no | Development user, sent as X-Dev-User. |
devRoles | string[] | none | no | Development roles, sent as X-Dev-Roles (comma joined). |
project | string | none | no | Project slug, sent as X-Project. |
environment | "dev" | "test" | "staging" | "prod" | string | none | no | Environment, sent as X-Environment. |
locale | "en" | "fr" | "en" | no | Language of error messages, sent as Accept-Language. |
fetch | (input: string, init?: RequestInit) => Promise<Response> | global fetch | no | Custom fetch (proxy agent, tests, instrumentation). |
retries | number | 2 | no | Retries after the first attempt (Retries and timeouts). |
retryBaseDelayMs | number | 300 | no | Base of the exponential backoff, in milliseconds. |
retryMaxDelayMs | number | 20000 | no | Upper bound of one delay, and of an honored Retry-After. |
timeoutMs | number | 60000 | no | Timeout of one attempt in milliseconds; 0 disables it. Streams are not timed out. |
headers | Record<string, string> | {} | no | Extra headers sent with every request. |
sleep | (ms: number) => Promise<void> | setTimeout based | no | Sleep between retries, injectable in tests. |
Per call options (CallOptions, last argument of every method):
| Name | Type | Description |
|---|---|---|
idempotencyKey | string | Idempotency-Key of a POST; otherwise one is generated. |
traceparent | string | W3C trace context of the call; otherwise a random one is generated. |
headers | Record<string, string> | Extra headers for this call. |
signal | AbortSignal | Cancels the call; an aborted call is never retried. |
import { EnterpriseAI } from "@enterprise-ai/sdk";
const tuned = new EnterpriseAI({
baseUrl: "<control-plane-url>",
token: async () => "<token>",
project: "invoices",
environment: "prod",
retries: 4,
retryBaseDelayMs: 500,
retryMaxDelayMs: 10000,
timeoutMs: 30000,
headers: { "X-Client": "billing" },
});
const controller = new AbortController();
const agent = await tuned.agents.get("agt_ap", { signal: controller.signal, headers: { "X-Correlation-Id": "c-1" } });
JSON.stringify(client.http) and console.log(client.http) mask the API key and the token. Redirects are never
followed (redirect: "manual").
Requests and responses
Path parameters are positional, the body is one object typed after the v1 contracts, and the result is the decoded
JSON body typed by the SDK (Agent, ChatCompletion, Page<T>...). createWithMeta returns the governance
headers as well.
const { data: r, meta } = await client.gateway.chat.completions.createWithMeta({ model: "gpt-4o-mini", messages: [{ role: "user", content: "Bonjour" }], eai: { effort: "low", agent_id: "agt_ap" } });
console.log(meta.costMeasured, meta.decisionId);
for await (const chunk of client.gateway.chat.completions.stream({ model: "gpt-4o-mini", messages })) process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
const plan = await client.models.resolve({ model: "o3-mini", effort: "high" });
GatewayMeta holds decisionId (X-EAI-Decision-Id), costEstimated, costMeasured and requestId. The low
level client.http.raw(method, template, options) returns { data, status, headers, requestId } for any route.
Errors
Every error answer uses the envelope {"error": {"code", "message", "message_i18n": {"en", "fr"}, "details", "request_id"}}. The SDK throws the class of the code, else of the HTTP status. Every error has code, status,
message (in the client locale), messageI18n, details (secret looking keys masked), requestId and
retryAfterMs.
| Class | Codes | HTTP status | Retried by the SDK |
|---|---|---|---|
EaiError | any other code (missing_base_url, no_fetch, invalid_response, http_<status>...) | other | no |
AuthError | unauthorized, unauthenticated, forbidden, invalid_credentials | 401, 403 | no |
PolicyDeniedError (decisionId, reasons) | policy_denied | 403 | no |
QuotaExceededError | quota_exceeded, budget_exceeded | 429 | yes, as a 429, until the retries are spent |
EffortNotSupportedError | effort_not_supported | 422 | no |
ContextOverflowError | context_overflow | 422 | no |
ApprovalRequiredError (approvalId) | approval_required | 409 | no |
NotFoundError | not_found | 404 | no |
ValidationError | validation_error | 400, 422 | no |
ConflictError | conflict | 409 | no |
PayloadTooLargeError | payload_too_large | 413 | no |
RateLimitError | rate_limited | 429 | yes, then thrown |
ServerError | server_error and any 5xx | 500 to 599 | yes, then thrown |
NetworkError | network_error, timeout | none | yes, then thrown |
EgressDeniedError (reason) | egress_denied | 403 | no |
PromptInjectionBlockedError | prompt_injection_blocked | 403 | no |
AuthzUnconfirmedError | raised when an authorization answer has no explicit decision | none | no |
ContractValidationError | offline contract validation failed | none | no |
AgentLoopError | local agent loop stopped | none | no |
import { QuotaExceededError, EaiError } from "@enterprise-ai/sdk";
try { await client.gateway.chat.completions.create(req); }
catch (e) { if (e instanceof QuotaExceededError) console.log(e.code, e.messageI18n.fr, e.requestId); else throw e; }
for await (const run of client.runs.iterate({ agent_id: "agt_ap" })) console.log(run.run_id);
Retries and timeouts
An attempt that fails with HTTP 429, any HTTP 5xx, a network error or a timeout is retried up to retries times
(default 2), whatever the method: every POST carries an Idempotency-Key, so the server deduplicates the retry.
The delay is an exponential backoff with jitter (half of retryBaseDelayMs * 2^attempt plus a random half, at most
retryMaxDelayMs); a Retry-After header (seconds or HTTP date) replaces it, bounded by retryMaxDelayMs. The
other statuses are thrown at once. timeoutMs (default 60 s) bounds each attempt; an abort through signal is
thrown at once.
Pagination
List routes take limit and cursor and answer Page<T>: { items: T[], next_cursor: string | null }. A list
method returns one page; an iterate method (agents, runs, tenants, projects, members, models, approvals, audit)
returns an async generator over every item; paginate(fetchPage, params) does the same for any list method and
collect(iterable, max) gathers the items in an array.
import { collect, paginate } from "@enterprise-ai/sdk";
const first = await client.runs.list({ agent_id: "agt_ap", limit: 50 });
console.log(first.items.length, first.next_cursor);
const agents = await collect(client.agents.iterate({ limit: 100 }), 500);
const sessions = await collect(paginate((q) => client.sessions.list(q), { limit: 50 }));
paginate stops at the last page or when a cursor repeats.
Idempotency and long operations
Every POST sends an Idempotency-Key: the idempotencyKey you pass, else a generated UUID kept across the retries
of that call. The Control Plane replays the first answer for a key it already saw.
const run = await client.agents.run("agt_ap", { task: { input: { invoice_media_id: "med_01HZX3K9" } } }, { idempotencyKey: "inv-2291" });
const done = await client.runs.get(run.run_id);
console.log(await client.runs.steps(run.run_id));
A long creation answers with the Operation-Id and Operation-Location headers; client.operations.get(id) reads
the operation, its tracked resource and its live state. Runs (client.runs.get), sandbox executions
(client.sandbox.get) and multimodal jobs (client.multimodal.getJob) are read again until their state is final.
Async and concurrency
Every method returns a Promise; streams are async generators. One EnterpriseAI instance can serve many
concurrent calls (Promise.all): it keeps no per call state. Cancel a call with an AbortSignal.
const [me, models] = await Promise.all([client.me.get(), client.models.list()]);
Streaming and realtime
client.gateway.chat.completions.stream(body) returns an async generator of ChatCompletionChunk over
Server-Sent Events, ending at [DONE]. The lower level client.http.sse(...) and client.http.sseJSON(...) read
any SSE route. Voice live sessions use client.realtime.createSession and the @enterprise-ai/realtime package:
see the Realtime guide.
Logging and debugging
The SDK writes no log. To trace the calls, pass a fetch that logs the method, the URL, the status and the
X-Request-Id; never log the request headers, which hold the credentials.
import { EnterpriseAI } from "@enterprise-ai/sdk";
const logged = new EnterpriseAI({
baseUrl: "<control-plane-url>",
apiKey: process.env.EAI_API_KEY,
fetch: async (url, init) => {
const res = await fetch(url, init);
console.debug(init?.method ?? "GET", url, res.status, res.headers.get("x-request-id"));
return res;
},
});
console.log(JSON.stringify(logged.http.toJSON()));
Give the requestId of an error to the platform team: it is the key of the audit event and of the trace.
Testing
Inject a fake fetch: no server is needed and the real SDK code runs (headers, errors, pagination). Set
retries: 0 or a no-op sleep to keep the tests fast.
import { EnterpriseAI, NotFoundError } from "@enterprise-ai/sdk";
const fakeFetch = async (url: string): Promise<Response> =>
url.endsWith("/v1/agents/missing")
? new Response(JSON.stringify({ error: { code: "not_found", message: "Resource not found." } }), { status: 404 })
: new Response(JSON.stringify({ items: [{ id: "agt_1" }], next_cursor: null }), { status: 200 });
const testClient = new EnterpriseAI({ baseUrl: "<control-plane-url>", apiKey: "eai_test_key", fetch: fakeFetch, retries: 0 });
const listed = await testClient.agents.list();
try {
await testClient.agents.get("missing");
} catch (err) {
if (!(err instanceof NotFoundError)) throw err;
}
Versioning and compatibility
- The package follows semantic versioning:
1.xkeeps every exported name; a removal waits for2.0. - It targets API
v1and the contractsv1; unknown response fields are kept, so a newer Control Plane does not break an older SDK. - The tests
doc-coverage.test.tsandconformance.test.tscheck that every route of API v1 is covered by a method. - Pin the minor version in production (
"@enterprise-ai/sdk": "~1.0.2").
Resource guides
One subsection per resource group, with one example per common call. Every method and type is in the generated reference.
System, identity and tenancy
client.meta, client.me, client.catalog, client.operations, client.contracts, client.manifests,
client.tenants, client.projects, client.organizations, client.businessUnits, client.members,
client.apiKeys, client.serviceIdentities, client.authz, client.secrets.
await client.policies.evaluate({ action: "model.invoke", resource: { type: "model", id: "gpt-4o-mini" } });
await client.approvals.decide("apr_3", { decision: "approve", comment: "ok" });
await client.projects.create({ slug: "invoices", name: "Invoices" });
const res = await client.contracts.validate("usage-event", doc);
Licence, context, delegation and emergency access
client.license, client.context (execution contexts), client.delegations, client.mandates,
client.breakGlass.
const lic = await client.license.get(); // GET /v1/license
await client.license.activate({ token: signedLicense }); // POST /v1/license/activations
const me = await client.me.whoami(); // GET /v1/identity/whoami
const ctx = await client.context.issue({ run_id: "run_1", ttl_seconds: 300 }); // POST /v1/context/issue
await client.context.verify({ token: String(ctx.token) });
const jwks = await client.context.jwks(); // offline verification keys
const d = await client.delegations.create({ delegate: "agent:support", scopes: ["crm.read"], ttl_seconds: 3600 });
await client.delegations.restrict(String(d.id), { delegate: "agent:sub", scopes: ["crm.read"] });
await client.delegations.authorize({ action: "crm.read", resource: "crm:contact:42" });
const m = await client.mandates.create({ agent: "agt_nightly", purpose: "reporting", service_identity_id: "si_1" });
await client.mandates.decide(String(m.approval_id), { decision: "approve" }); // by another person
const grant = await client.breakGlass.request({ reason: "P1 outage", duration_seconds: 900, incident_ref: "INC-12" });
await client.breakGlass.revoke(String(grant.id), { reason: "resolved" });
Policies, approvals, risk and incidents
client.policies (evaluation, decisions, revisions, simulation, bundles, engines), client.approvals,
client.risk, client.incidents, client.governance, client.security.
await client.policies.revisions("pol_1");
await client.policies.diffRevisions("pol_1", { from_revision: 3, to_revision: 4 });
await client.policies.simulate({ candidate: draftPolicy, policy_id: "pol_1" });
await client.policies.createBundle({ revision: "2026-10-30.1" });
const archive = await client.policies.downloadBundle("current"); // ArrayBuffer (bundle.tar.gz)
const engines = await client.policies.engines(); // expected_revision, in_sync
Models, providers, routes and the gateway
client.providers, client.models, client.routes, client.litellm, client.adapters, client.gateway
(chat completions, embeddings, rerank, estimate), client.llm, client.llmManagement, client.isolation.
await client.models.createVersion("gpt-4o-mini", { version: "2026-10" });
await client.models.requestVersionApproval("gpt-4o-mini", "2026-10");
await client.models.startRollout("gpt-4o-mini", "2026-10", { percent: 10 });
await client.models.rollout("gpt-4o-mini");
await client.models.rollback("gpt-4o-mini");
await client.isolation.get({ project_id: "prj_1" }); // GET /v1/llm/isolation
Agents and runs
client.agents, client.runs, client.checkpoints, client.heartbeats, client.subagents, client.a2a,
client.sessions, client.schedules, client.triggers, client.events, client.sandbox.
import { signWebhook, tokenUnits } from "@enterprise-ai/sdk";
const headers = await signWebhook(secret, rawBody, { tenant: "acme", deliveryId: "evt-42" });
await client.webhooks.deliver("sap", { event: "invoice" }, { secret, tenant: "acme" });
const units = tokenUnits(1200, 300, { cached: 800, reasoning: 120 });
const hb = await client.heartbeats.send(runId, { progress: 0.5 });
if (hb.action === "stop") { /* checkpoint and exit */ }
A heartbeat answer with action: "stop" means: save a checkpoint and exit.
Tools, MCP, skills, plugins, connectors and channels
client.tools, client.mcp, client.mcpRuntime, client.skills, client.plugins, client.connectors,
client.credentials, client.webhooks, client.channels, client.navinLibrary, client.fileSecurity.
await client.tools.invoke("erp.lookup", { arguments: { invoice: "INV-2291" }, run_id: run.run_id });
await client.mcp.callTool("mcp_files", "read", { path: "/a.txt" });
await client.skills.verify("invoice-reader");
await client.channels.inbound("web", { user: "u1", text: "Bonjour" });
Data: RAG, memory, DLP and encryption
client.privacy, client.dlp, client.tokenization, client.encryption, client.knowledgeBases,
client.memory, client.retention, client.lineage, client.prompts.
const hits = await client.knowledgeBases.query("kb_1", { query: "délai de paiement", top_k: 5 });
await client.dlp.redact({ text: "IBAN FR76...", mode: "mask" });
const tok = await client.tokenization.tokenize({ values: ["FR7630006000011234567890189"] });
Multimodal
client.media (upload), client.multimodal (vision, images, video, speech, diarization, audio DLP, jobs),
client.realtime (sessions, consents, turns, transcripts).
const media = await client.media.upload({ file, operation: "vision_understanding" });
await client.multimodal.vision({ model: "vision-default", media_id: media.media_id, prompt: "Total ?" });
await client.multimodal.stt({ media_id: "med_AUD1", language: "fr" });
const job = await client.multimodal.generateVideo({ model: "video-gen-gpu", prompt: "Teaser", seconds: 8 });
await client.multimodal.getJob(job.job_id as string);
Quality, audit and observability
client.evaluations, client.redteam, client.audit, client.telemetry.
const gate = await client.evaluations.checkGate({ target: { type: "agent", ref: "agt_ap", version: "1.4.0" }, suite_ids: ["evs_12"] });
const v = await client.audit.verify();
const corr = await client.audit.correlations({ run_id: "run_42" });
const slo = await client.telemetry.slo({ window_minutes: 60 }); // budget_remaining and burn_rate per service
FinOps and quotas
client.finops and client.quotas. withReservation reserves, runs your function, then commits on success or
releases on error.
const est = await client.finops.estimate({ provider: "openai", usage_type: "llm", model_id: "gpt-4o-mini", units: { input_tokens: 12000, output_tokens: 2000 } });
await client.quotas.withReservation(
{ cost: "0.50", tokens: 4000, scope: { agent: "agt_ap" } },
async (r) => client.gateway.chat.completions.createWithMeta({ model: "gpt-4o-mini", messages }),
{ commitBody: (out) => ({ cost: out.meta.costMeasured, tokens: out.data.usage?.total_tokens }) },
);
const sum = await client.finops.summary({ group_by: "model" });
SOC and kill switches
client.soc: detections, containment orders, kill switches, receipts, recoveries (four eyes).
await client.soc.createDetection({ kind: "data_exfiltration", severity: "high", agent: "agt_1" });
const order = await client.soc.createOrder({ reaction: "kill_agent", reason: "exfiltration", target: { agent: "agt_1" } });
await client.soc.approveOrder(String(order.id));
await client.soc.verifyKillSwitch("ks_1"); // effective only once every receipt is verified
await client.soc.requestRecovery({ kill_switch_id: "ks_1", reason: "root cause fixed" });
await client.soc.approveRecovery("rec_1"); // four eyes: another person
await client.soc.webhook("falco", rawBody, { headers: { "X-EAI-Signature": signature } }); // raw bytes
Storage, marketplace and evidence
client.storageProfiles, client.marketplace, client.evidence, client.applications.
const sp = await client.storageProfiles.create({ name: "archive", provider: "s3_compatible", bucket: "eai-archive", credential_ref: "vault:kv/s3" });
await client.storageProfiles.verify(String(sp.id), { probe_write: true });
const { url } = await client.storageProfiles.presign(String(sp.id), { key: "exports/a.json", scope: "read" });
await client.marketplace.preflight({ package: "hr-onboarding", version: "1.2.0", environment: "staging" });
const inst = await client.marketplace.install({ package: "hr-onboarding", version: "1.2.0", environment: "staging" });
await client.marketplace.apply(String(inst.id));
await client.evidence.createSeal();
await client.evidence.verify();
await client.evidence.createHold({ case_id: "case_7", reason: "litigation" });
await client.evidence.runConfiguration("run_1");
Architecture interfaces
Named methods over documented routes. An OpenFGA or OPA outage propagates as a typed error (ServerError with code
authz_backend_error), and an answer without an explicit decision throws AuthzUnconfirmedError: the SDK never
reports an allow the engine did not confirm. A refusal propagates as its typed error (AuthError 403
relation_required, ValidationError 422, ConflictError 409 approval_closed or storage_object_protected).
import { AuthError, type AuditRecord } from "@enterprise-ai/sdk";
try {
const ctx = await client.serviceIdentities.assume("si-billing", { purpose: "nightly invoice sync", audience: ["erp"], ttl_seconds: 600 });
console.log(ctx.context_id, ctx.effective.scopes); // verified downstream with POST /v1/context/verify
} catch (err) {
if (err instanceof AuthError && err.code === "relation_required") { /* can_assume not granted */ }
else throw err;
}
const event: AuditRecord = { action: "app.invoice.validated", resource: { type: "invoice", id: "inv-42" }, outcome: "success" };
const receipt = await client.audit.record(event); // receipt.event_id, receipt.hash
await client.approvals.cancel("apr-1", { reason: "request withdrawn" });
const page = await client.storageProfiles.listObjects("sp-1", { prefix: "exports/", limit: 100 });
for (const o of page.items) await client.storageProfiles.deleteObject("sp-1", o.key, o.version_id ? { version_id: o.version_id } : {});
Platform Manager space (platform_admin)
client.platformManager reaches the Platform Manager space of the console: the Control Plane relays every call to
the Navin Manager on behalf of the caller, whose RBAC, approvals and stop rules decide. A refusal comes back with its
status and a platform_manager_<code> code.
const summary = await client.platformManager.summary(); // services total, healthy, warning, failed per cluster
const services = await client.platformManager.services("c1"); // five state fields and administrative state
const op = await client.platformManager.requestOperation("c1", "opa", "rolling_restart", { batch_size: 1 });
const restore = await client.platformManager.planRestore({ artifact_id: "b1", environment: "isolated" }); // two approvers other than the requester (ADR-0162)
const exercises = await client.platformManager.drExercises(); // observed RPO and RTO
const links = await client.platformManager.console(); // Manager console and operator consoles
Navin Manager client
ManagerClient of @enterprise-ai/sdk calls the Navin Manager API directly with a manager token
nmt_<12 hex>_<secret>; only GET, PUT and DELETE are retried, because the manager does not deduplicate POST calls.
The full guide is Navin Manager SDK.
import { ManagerClient } from "@enterprise-ai/sdk";
const nm = new ManagerClient({ baseUrl: "<manager-url>", token: async () => "<token>", onBehalfOf: "alice" });
const operation = await nm.services.action("c1", "kafka", { action: "restart" });
Reference
- Generated TypeScript reference: every class, method, parameter and type.
- API v1 of the Control Plane, Contracts v1, Realtime guide.
- Other SDKs.