@proteles/management
v0.1.1
Published
Typed client for the Proteles Management API (/api/v1/*): tenants, apps, API keys, connections, branding, and audit. Used by the dashboard and by CI.
Maintainers
Readme
@proteles/management
A typed client for the Proteles Management API (/api/v1/*) — the control
plane. Provision tenants, apps, and API keys; configure connections and
branding; read the audit trail. It's what the dashboard is built on, and it's
equally at home in CI.
Install
npm install @proteles/managementUsage
import { ManagementClient } from "@proteles/management";
// Platform token: full, cross-tenant access (provisioning).
const platform = new ManagementClient({
baseUrl: "https://api.proteles.com",
token: process.env.PROTELES_PLATFORM_TOKEN!,
});
const tenant = await platform.createTenant({ slug: "acme", displayName: "Acme Inc" });
const key = await platform.createApiKey(tenant.id, { name: "ci", scopes: ["audit:read"] }); // key.apiKey shown once
// Tenant key: scoped to its own tenant (day-to-day config).
const acme = new ManagementClient({ baseUrl: "https://api.proteles.com", token: key.apiKey });
const app = await acme.createApp({
clientName: "Acme Web",
tokenEndpointAuthMethod: "client_secret_basic",
grantTypes: ["authorization_code", "refresh_token"],
redirectUris: ["https://acme.example/api/auth/callback"],
scopes: ["openid", "profile", "email", "offline_access"],
});
// app.clientId / app.clientSecret (secret returned once)
await acme.upsertConnection({
provider: "google",
clientId: "…apps.googleusercontent.com",
clientSecret: "…",
redirectUri: "https://acme.example/api/auth/callback",
});
await acme.updateTenantBranding(tenant.id, { appName: "Acme", primaryColor: "#ff0066" });
const events = await acme.listAudit({ limit: 50 });Two credentials
- Platform bootstrap token — full, cross-tenant access. Use it to
createTenantandcreateApiKey; endpoints that operate on a specific tenant take atenantId. - Tenant API key (
mk_…) — scoped to its own tenant.tenantIdis derived from the key; passing a different tenant's id is rejected with aManagementError(status: 403).
Methods
| Method | Endpoint |
| --- | --- |
| createTenant(input) | POST /api/v1/tenants (platform) |
| getTenant(id) | GET /api/v1/tenants/{id} |
| updateTenantBranding(id, branding) | PATCH /api/v1/tenants/{id} |
| createApiKey(id, input) | POST /api/v1/tenants/{id}/keys (platform) |
| listApiKeys(id) | GET /api/v1/tenants/{id}/keys (platform) |
| revokeApiKey(id, keyId) | DELETE /api/v1/tenants/{id}/keys/{keyId} (platform) |
| listDomains(id) | GET /api/v1/tenants/{id}/domains (domains:write) |
| claimDomain(id, domain) | POST /api/v1/tenants/{id}/domains (domains:write) |
| verifyDomain(id, domain) | POST /api/v1/tenants/{id}/domains/{domain}/verify (domains:write) |
| releaseDomain(id, domain) | DELETE /api/v1/tenants/{id}/domains/{domain} (domains:write) |
| createApp(input) | POST /api/v1/apps |
| listApps(scope?) | GET /api/v1/apps |
| upsertConnection(input) | POST /api/v1/connections |
| listConnections(scope?) | GET /api/v1/connections |
| listAudit(options?) | GET /api/v1/audit |
Everything is camelCase; the client maps to/from the API's snake_case wire
format. Secrets (apiKey, clientSecret) are returned exactly once by the
server and never retrievable again.
Errors
Any non-2xx response throws a ManagementError carrying the API's code
(e.g. "conflict", "forbidden", "not_found") and HTTP status.
Develop
npm install # from the sdk/ workspace root
npm run build # tsc -> dist
npm test # tsx + node:test (wire mapping + error handling, mock fetch)The full control-plane lifecycle is also exercised against a live server by
sdk/scripts/verify-management.mjs — Test
15 of scripts/e2e-smoke-test.sh.
