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

MethodHTTPReturns
login(email, password, tenantId?)POST /auth/loginSdkResult<LoginResponse>
logout(token)POST /auth/logoutNoBodyResult
me(token)GET /auth/meSdkResult<MeResponse>
memberships(token)GET /auth/membershipsSdkResult<PaginatedResult<TenantMembership>>
exchange(token, tenantSlug)POST /auth/session/exchangeSdkResult<LoginResponse>
acceptInvite(inviteToken, password)POST /auth/invite/acceptSdkResult<LoginResponse>
requestPasswordReset(email, tenantId?)POST /auth/password/resetNoBodyResult
confirmPasswordReset(resetToken, password)POST /auth/password/reset/confirmNoBodyResult
// 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

MethodHTTPReturns
list(token, tenantId, opts?)GET /tenants/:id/usersSdkResult<PaginatedResult<User>>
get(token, tenantId, userId)GET /tenants/:id/users/:uidSdkResult<User>
invite(token, tenantId, email, roleId)POST /tenants/:id/users/inviteSdkResult<UserStub>
update(token, tenantId, userId, data)PATCH /tenants/:id/users/:uidSdkResult<User>
deactivate(token, tenantId, userId)DELETE /tenants/:id/users/:uidNoBodyResult
listRoles(token, tenantId, userId, opts?)GET /tenants/:id/users/:uid/rolesSdkResult<PaginatedResult<Role>>
assignRole(token, tenantId, userId, roleId)POST /tenants/:id/users/:uid/roles/:roleIdNoBodyResult
revokeRole(token, tenantId, userId, roleId)DELETE /tenants/:id/users/:uid/roles/:roleIdNoBodyResult
listPermissions(token, tenantId, userId, opts?)GET /tenants/:id/users/:uid/permissionsSdkResult<PaginatedResult<string>>
grantPermission(token, tenantId, userId, data)POST /tenants/:id/users/:uid/permissionsSdkResult<UserPermission>
updatePermission(token, tenantId, userId, permId, data)PATCH /tenants/:id/users/:uid/permissions/:permIdSdkResult<UserPermission>
revokePermission(token, tenantId, userId, permId)DELETE /tenants/:id/users/:uid/permissions/:permIdNoBodyResult

sdk.roles

MethodHTTPReturns
list(token, tenantId, opts?)GET /tenants/:id/rolesSdkResult<PaginatedResult<Role>>
get(token, tenantId, roleId)GET /tenants/:id/roles/:roleIdSdkResult<Role>
create(token, tenantId, data)POST /tenants/:id/rolesSdkResult<Role>
update(token, tenantId, roleId, data)PATCH /tenants/:id/roles/:roleIdSdkResult<Role>
delete(token, tenantId, roleId)DELETE /tenants/:id/roles/:roleIdNoBodyResult
listPermissions(token, tenantId, roleId, opts?)GET /tenants/:id/roles/:roleId/permissionsSdkResult<PaginatedResult<Permission>>
assignPermission(token, tenantId, roleId, permId)POST /tenants/:id/roles/:roleId/permissions/:permIdNoBodyResult
revokePermission(token, tenantId, roleId, permId)DELETE /tenants/:id/roles/:roleId/permissions/:permIdNoBodyResult

sdk.permissions

MethodHTTPReturns
list(token, opts?)GET /permissionsSdkResult<PaginatedResult<Permission>>
get(token, permissionId)GET /permissions/:idSdkResult<Permission>
create(token, data)POST /permissionsSdkResult<Permission>
listPolicies(token, permissionId, opts?)GET /permissions/:id/policiesSdkResult<PaginatedResult<Policy>>

sdk.policies

MethodHTTPReturns
list(token, opts?)GET /policiesSdkResult<PaginatedResult<Policy>>
get(token, policyId)GET /policies/:idSdkResult<Policy>
create(token, data)POST /policiesSdkResult<Policy>
update(token, policyId, data)PATCH /policies/:idSdkResult<Policy>
delete(token, policyId)DELETE /policies/:idNoBodyResult
attachToPermission(token, permissionId, policyId)POST /permissions/:permId/policies/:policyIdNoBodyResult
detachFromPermission(token, permissionId, policyId)DELETE /permissions/:permId/policies/:policyIdNoBodyResult

sdk.apiKeys

MethodHTTPReturns
list(token, tenantId, opts?)GET /tenants/:id/api-keysSdkResult<PaginatedResult<ApiKey>>
create(token, tenantId, data)POST /tenants/:id/api-keysSdkResult<ApiKeyCreated>
revoke(token, tenantId, keyId)DELETE /tenants/:id/api-keys/:keyIdNoBodyResult

sdk.audit

MethodHTTPReturns
list(token, tenantId, opts?)GET /tenants/:id/audit-logsSdkResult<PaginatedResult<AuditLog>>

Opts: { action?, userId?, from?, to?, modelType?, cursor?, limit? }

sdk.tenants

MethodHTTPReturns
get(token, tenantId)GET /tenants/:idSdkResult<Tenant>
update(token, tenantId, data)PATCH /tenants/:idSdkResult<Tenant>
listChildren(token, tenantId, opts?)GET /tenants/:id/childrenSdkResult<PaginatedResult<Tenant>>
create(token, data)POST /tenantsSdkResult<Tenant>
listAll(token, opts?)GET /tenantsSdkResult<PaginatedResult<Tenant>>

sdk.featureFlags

MethodHTTPReturns
list(token, tenantId, opts?)GET /tenants/:id/feature-flagsSdkResult<PaginatedResult<TenantFlagState>>
get(token, tenantId, flagName)GET /tenants/:id/feature-flags/:nameSdkResult<TenantFlagState>
toggle(token, tenantId, flagName, data)PUT /tenants/:id/feature-flags/:nameSdkResult<TenantFlagState>
createGlobal(token, data)POST /feature-flagsSdkResult<FeatureFlag>
listGlobal(token, opts?)GET /feature-flagsSdkResult<PaginatedResult<FeatureFlag>>
getGlobal(token, flagName)GET /feature-flags/:nameSdkResult<FeatureFlag>
toggleGlobal(token, flagName, data)PATCH /feature-flags/:nameSdkResult<FeatureFlag>

sdk.modules

MethodHTTPReturns
list(token, opts?)GET /modulesSdkResult<PaginatedResult<Module>>
get(token, moduleId)GET /modules/:idSdkResult<Module>
create(token, data)POST /modulesSdkResult<Module>
update(token, moduleId, data)PATCH /modules/:idSdkResult<Module>

sdk.oauth2

MethodHTTPReturns
list(token, tenantId, opts?)GET /tenants/:id/oauth2-clientsSdkResult<PaginatedResult<OAuth2Client>>
create(token, tenantId, data)POST /tenants/:id/oauth2-clientsSdkResult<OAuth2ClientCreated>
get(token, tenantId, clientId)GET /tenants/:id/oauth2-clients/:clientIdSdkResult<OAuth2Client>
update(token, tenantId, clientId, data)PATCH /tenants/:id/oauth2-clients/:clientIdSdkResult<OAuth2Client>
delete(token, tenantId, clientId)DELETE /tenants/:id/oauth2-clients/:clientIdNoBodyResult
rotateSecret(token, tenantId, clientId)POST .../rotate-secretSdkResult<OAuth2ClientCreated>
revokeAllTokens(token, tenantId, clientId)POST .../revokeNoBodyResult
listGrants(token, tenantId, clientId, opts?)GET .../grantsSdkResult<PaginatedResult<OAuth2Grant>>
revokeGrant(token, tenantId, clientId, subject)DELETE .../grants/:subjectNoBodyResult

sdk.oauth2.keys (Signing Keys)

Targets oauthBaseUrl (orbit-oauth2 direct). System tenant only.

MethodHTTPReturns
keys.list(token)GET /oauth2/keysSdkResult<PaginatedResult<SigningKey>>
keys.rotate(token)POST /oauth2/keys/rotateSdkResult<SigningKey>
keys.retire(token, kid)POST /oauth2/keys/:kid/retireNoBodyResult

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
}