SDK Reference
The @wrift/orbit-sdk package provides a typed TypeScript client for orbit-service.
Install it, create a client instance, and call methods. All responses are discriminated unions — never throws for expected API errors.
Installation
bun add @wrift/orbit-sdk
Setup
import { OrbitAdmin } from '@wrift/orbit-sdk'
const sdk = new OrbitAdmin({
baseUrl: 'http://localhost:3001', // orbit-service
oauthBaseUrl: 'http://localhost:3003', // orbit-oauth2 (optional, for oauth2.keys)
timeout: 30000, // ms, default 30000
})
Result Types
Every method returns one of two discriminated union types:
// For endpoints that return a body
type SdkResult<T> =
| { status: 200 | 201; data: T }
| { status: 401 | 403 | 404 | 422 | 500 | 503; error: string; fields?: string[] }
// For endpoints that return 204 (no content)
type NoBodyResult =
| { status: 204 }
| { status: 401 | 403 | 404 | 422 | 500 | 503; error: string; fields?: string[] }
503 is synthesized by the SDK when the API is unreachable or the request times out.
Modules
sdk.auth
| Method | HTTP | Returns |
|---|---|---|
login(email, password, tenantId?) | POST /auth/login | SdkResult<LoginResponse> |
logout(token) | POST /auth/logout | NoBodyResult |
me(token) | GET /auth/me | SdkResult<MeResponse> |
memberships(token) | GET /auth/memberships | SdkResult<PaginatedResult<TenantMembership>> |
exchange(token, tenantSlug) | POST /auth/session/exchange | SdkResult<LoginResponse> |
acceptInvite(inviteToken, password) | POST /auth/invite/accept | SdkResult<LoginResponse> |
requestPasswordReset(email, tenantId?) | POST /auth/password/reset | NoBodyResult |
confirmPasswordReset(resetToken, password) | POST /auth/password/reset/confirm | NoBodyResult |
// Two-step login (orbit-dash pattern)
const login = await sdk.auth.login('admin@example.com', 'password')
if (login.status !== 200) throw new Error(login.error)
const memberships = await sdk.auth.memberships(login.data.token)
if (memberships.status !== 200) throw new Error(memberships.error)
const exchange = await sdk.auth.exchange(login.data.token, memberships.data.data[0].tenant_slug)
if (exchange.status !== 200) throw new Error(exchange.error)
const token = exchange.data.token // tenant-scoped session
sdk.users
| Method | HTTP | Returns |
|---|---|---|
list(token, tenantId, opts?) | GET /tenants/:id/users | SdkResult<PaginatedResult<User>> |
get(token, tenantId, userId) | GET /tenants/:id/users/:uid | SdkResult<User> |
invite(token, tenantId, email, roleId) | POST /tenants/:id/users/invite | SdkResult<UserStub> |
update(token, tenantId, userId, data) | PATCH /tenants/:id/users/:uid | SdkResult<User> |
deactivate(token, tenantId, userId) | DELETE /tenants/:id/users/:uid | NoBodyResult |
listRoles(token, tenantId, userId, opts?) | GET /tenants/:id/users/:uid/roles | SdkResult<PaginatedResult<Role>> |
assignRole(token, tenantId, userId, roleId) | POST /tenants/:id/users/:uid/roles/:roleId | NoBodyResult |
revokeRole(token, tenantId, userId, roleId) | DELETE /tenants/:id/users/:uid/roles/:roleId | NoBodyResult |
listPermissions(token, tenantId, userId, opts?) | GET /tenants/:id/users/:uid/permissions | SdkResult<PaginatedResult<string>> |
grantPermission(token, tenantId, userId, data) | POST /tenants/:id/users/:uid/permissions | SdkResult<UserPermission> |
updatePermission(token, tenantId, userId, permId, data) | PATCH /tenants/:id/users/:uid/permissions/:permId | SdkResult<UserPermission> |
revokePermission(token, tenantId, userId, permId) | DELETE /tenants/:id/users/:uid/permissions/:permId | NoBodyResult |
sdk.roles
| Method | HTTP | Returns |
|---|---|---|
list(token, tenantId, opts?) | GET /tenants/:id/roles | SdkResult<PaginatedResult<Role>> |
get(token, tenantId, roleId) | GET /tenants/:id/roles/:roleId | SdkResult<Role> |
create(token, tenantId, data) | POST /tenants/:id/roles | SdkResult<Role> |
update(token, tenantId, roleId, data) | PATCH /tenants/:id/roles/:roleId | SdkResult<Role> |
delete(token, tenantId, roleId) | DELETE /tenants/:id/roles/:roleId | NoBodyResult |
listPermissions(token, tenantId, roleId, opts?) | GET /tenants/:id/roles/:roleId/permissions | SdkResult<PaginatedResult<Permission>> |
assignPermission(token, tenantId, roleId, permId) | POST /tenants/:id/roles/:roleId/permissions/:permId | NoBodyResult |
revokePermission(token, tenantId, roleId, permId) | DELETE /tenants/:id/roles/:roleId/permissions/:permId | NoBodyResult |
sdk.permissions
| Method | HTTP | Returns |
|---|---|---|
list(token, opts?) | GET /permissions | SdkResult<PaginatedResult<Permission>> |
get(token, permissionId) | GET /permissions/:id | SdkResult<Permission> |
create(token, data) | POST /permissions | SdkResult<Permission> |
listPolicies(token, permissionId, opts?) | GET /permissions/:id/policies | SdkResult<PaginatedResult<Policy>> |
sdk.policies
| Method | HTTP | Returns |
|---|---|---|
list(token, opts?) | GET /policies | SdkResult<PaginatedResult<Policy>> |
get(token, policyId) | GET /policies/:id | SdkResult<Policy> |
create(token, data) | POST /policies | SdkResult<Policy> |
update(token, policyId, data) | PATCH /policies/:id | SdkResult<Policy> |
delete(token, policyId) | DELETE /policies/:id | NoBodyResult |
attachToPermission(token, permissionId, policyId) | POST /permissions/:permId/policies/:policyId | NoBodyResult |
detachFromPermission(token, permissionId, policyId) | DELETE /permissions/:permId/policies/:policyId | NoBodyResult |
sdk.apiKeys
| Method | HTTP | Returns |
|---|---|---|
list(token, tenantId, opts?) | GET /tenants/:id/api-keys | SdkResult<PaginatedResult<ApiKey>> |
create(token, tenantId, data) | POST /tenants/:id/api-keys | SdkResult<ApiKeyCreated> |
revoke(token, tenantId, keyId) | DELETE /tenants/:id/api-keys/:keyId | NoBodyResult |
sdk.audit
| Method | HTTP | Returns |
|---|---|---|
list(token, tenantId, opts?) | GET /tenants/:id/audit-logs | SdkResult<PaginatedResult<AuditLog>> |
Opts: { action?, userId?, from?, to?, modelType?, cursor?, limit? }
sdk.tenants
| Method | HTTP | Returns |
|---|---|---|
get(token, tenantId) | GET /tenants/:id | SdkResult<Tenant> |
update(token, tenantId, data) | PATCH /tenants/:id | SdkResult<Tenant> |
listChildren(token, tenantId, opts?) | GET /tenants/:id/children | SdkResult<PaginatedResult<Tenant>> |
create(token, data) | POST /tenants | SdkResult<Tenant> |
listAll(token, opts?) | GET /tenants | SdkResult<PaginatedResult<Tenant>> |
sdk.featureFlags
| Method | HTTP | Returns |
|---|---|---|
list(token, tenantId, opts?) | GET /tenants/:id/feature-flags | SdkResult<PaginatedResult<TenantFlagState>> |
get(token, tenantId, flagName) | GET /tenants/:id/feature-flags/:name | SdkResult<TenantFlagState> |
toggle(token, tenantId, flagName, data) | PUT /tenants/:id/feature-flags/:name | SdkResult<TenantFlagState> |
createGlobal(token, data) | POST /feature-flags | SdkResult<FeatureFlag> |
listGlobal(token, opts?) | GET /feature-flags | SdkResult<PaginatedResult<FeatureFlag>> |
getGlobal(token, flagName) | GET /feature-flags/:name | SdkResult<FeatureFlag> |
toggleGlobal(token, flagName, data) | PATCH /feature-flags/:name | SdkResult<FeatureFlag> |
sdk.modules
| Method | HTTP | Returns |
|---|---|---|
list(token, opts?) | GET /modules | SdkResult<PaginatedResult<Module>> |
get(token, moduleId) | GET /modules/:id | SdkResult<Module> |
create(token, data) | POST /modules | SdkResult<Module> |
update(token, moduleId, data) | PATCH /modules/:id | SdkResult<Module> |
sdk.oauth2
| Method | HTTP | Returns |
|---|---|---|
list(token, tenantId, opts?) | GET /tenants/:id/oauth2-clients | SdkResult<PaginatedResult<OAuth2Client>> |
create(token, tenantId, data) | POST /tenants/:id/oauth2-clients | SdkResult<OAuth2ClientCreated> |
get(token, tenantId, clientId) | GET /tenants/:id/oauth2-clients/:clientId | SdkResult<OAuth2Client> |
update(token, tenantId, clientId, data) | PATCH /tenants/:id/oauth2-clients/:clientId | SdkResult<OAuth2Client> |
delete(token, tenantId, clientId) | DELETE /tenants/:id/oauth2-clients/:clientId | NoBodyResult |
rotateSecret(token, tenantId, clientId) | POST .../rotate-secret | SdkResult<OAuth2ClientCreated> |
revokeAllTokens(token, tenantId, clientId) | POST .../revoke | NoBodyResult |
listGrants(token, tenantId, clientId, opts?) | GET .../grants | SdkResult<PaginatedResult<OAuth2Grant>> |
revokeGrant(token, tenantId, clientId, subject) | DELETE .../grants/:subject | NoBodyResult |
sdk.oauth2.keys (Signing Keys)
Targets oauthBaseUrl (orbit-oauth2 direct). System tenant only.
| Method | HTTP | Returns |
|---|---|---|
keys.list(token) | GET /oauth2/keys | SdkResult<PaginatedResult<SigningKey>> |
keys.rotate(token) | POST /oauth2/keys/rotate | SdkResult<SigningKey> |
keys.retire(token, kid) | POST /oauth2/keys/:kid/retire | NoBodyResult |
Key Types
interface LoginResponse {
token: string
expires_at: string
next: 'tenant_select' | '2fa' | 'complete'
}
interface MeResponse {
user: { id: string; email: string; name: string }
tenant_id: string | null
permissions: string[]
}
interface User {
id: string; tenant_id: string; email: string; name: string
deactivated_at: string | null; metadata: object
created_at: string; updated_at: string
}
interface Role {
id: string; tenant_id: string; name: string; description: string | null
created_at: string; updated_at: string
}
interface Permission {
id: string; module_id: string; name: string; description: string | null
created_at: string
}
interface PaginatedResult<T> {
data: T[]
next_cursor: string | null
}