P
PUGUH

TypeScript SDK

The @arsaka/puguh-sdk NPM package provides a fully typed TypeScript client for integrating ARSAKA PUGUH infrastructure services into your frontend and Node.js applications. The SDK ships with 6 modules covering authentication, organizations, IAM, audit/control, applications, and webhooks.

Installation

bash
npm install @arsaka/puguh-sdk
# or
bun add @arsaka/puguh-sdk

Quick Start

typescript
import { AuthClient, OrganizationClient, IAMClient } from '@arsaka/puguh-sdk';

const auth = new AuthClient({ baseUrl: 'https://api-puguh.arsaka.io' });

// Login
const { access_token } = await auth.login({
  email: 'user@example.com',
  password: 'password',
});

// Use authenticated clients
const orgs = new OrganizationClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
});

const orgList = await orgs.list();
// orgList = { items: OrganizationWithMembership[], total: number }
console.log(orgList.items[0].organization.name);

Modules

The SDK is organized into 6 client modules, each mapping to a PUGUH core service. Import only the clients you need — every module is tree-shakeable.

1. AuthClient

Handles user authentication, token lifecycle, magic links, and multi-factor authentication.

typescript
import { AuthClient } from '@arsaka/puguh-sdk';

const auth = new AuthClient({ baseUrl: 'https://api-puguh.arsaka.io' });

// Register a new user
await auth.register({ email: 'new@example.com', password: 'securePass123' });

// Login and receive tokens
const { access_token, refresh_token } = await auth.login({
  email: 'new@example.com',
  password: 'securePass123',
});

// Refresh an expired access token
const { access_token: newToken } = await auth.refreshToken({ refresh_token });

// Magic link authentication
await auth.requestMagicLink({ email: 'user@example.com' });
const session = await auth.verifyMagicLink({ token: 'magic-link-token' });

// Multi-factor authentication
const { secret, qr_code_url } = await auth.setupMFA({ type: 'totp' });
await auth.confirmMFA({ code: '123456' });

Methods: login, register, refreshToken, requestMagicLink, verifyMagicLink, setupMFA, confirmMFA

2. OrganizationClient

Manage organizations, membership, and invitations. PUGUH uses a GitHub-style organization model where each user has a personal workspace plus team organizations.

typescript
import { OrganizationClient } from '@arsaka/puguh-sdk';

const orgs = new OrganizationClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
});

// List organizations the user belongs to
// Returns { items: OrganizationWithMembership[], total: number }
const orgList = await orgs.list();
console.log(orgList.items, orgList.total);

// Create a new team organization
const org = await orgs.create({ name: 'Acme Corp', slug: 'acme-corp' });

// Get organization details
const details = await orgs.get(org.organization.organizationId);

// Update organization settings
await orgs.update(org.organization.organizationId, { name: 'Acme Corporation' });

// Member management
const members = await orgs.listMembers(org.organization.organizationId);
await orgs.inviteMember(org.organization.organizationId, {
  email: 'colleague@example.com', role: 'member',
});
await orgs.removeMember(org.organization.organizationId, memberId);

// Delete organization
await orgs.delete(org.organization.organizationId);

Methods: list, get, create, update, delete, listMembers, inviteMember, removeMember

3. IAMClient

Identity and Access Management — manage users, roles, and role assignments across your organization.

typescript
import { IAMClient } from '@arsaka/puguh-sdk';

const iam = new IAMClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
  organizationId: 'org-uuid',
});

// List users in the organization
// Returns { items: IamUser[], total, page, pageSize, hasNext, hasPrev }
const users = await iam.listUsers();
console.log(users.items, users.total);

// Get a specific user
const user = await iam.getUser(userId);

// Get user stats
const stats = await iam.getUserStats();
console.log(stats.total, stats.active);

// Role management
const roles = await iam.listRoles();
const customRole = await iam.createRole({
  name: 'billing_admin',
  description: 'Can manage billing',
  permissions: ['billing.invoices.read', 'billing.invoices.create'],
});
await iam.assignRole(userId, customRole.roleId);

// Permission matrix (roles x permissions)
const matrix = await iam.getPermissionMatrix();
console.log(matrix.roles, matrix.permissions); // permissions is string[]

Methods: listUsers, getUserStats, getUser, getUserRoles, listRoles, getRole, getRolePermissions, createRole, updateRole, deleteRole, assignRole, revokeRole, listServiceAccounts, listPermissions, getPermissionMatrix

4. ControlClient

Access the audit trail, event logs, dead-letter queue, and platform metrics. All paths are under /control.

typescript
import { ControlClient } from '@arsaka/puguh-sdk';

const control = new ControlClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
  organizationId: 'org-uuid',
});

// Audit logs — who did what, when
// Returns { items: AuditRecord[], total, page, limit, pages }
const audit = await control.listAudit({
  resourceType: 'user',
  limit: 50,
});

// Single audit record
const record = await control.getAudit(auditId);

// Event stream
// Returns { items: SystemEvent[], total, page, limit, pages }
const events = await control.listEvents({ limit: 50 });

// Dead-letter queue — failed event deliveries
// DLQ items have totalAttempts (not retryCount)
const dlq = await control.listDLQ();

// Metrics trends (7d, 30d, or 90d)
const trends = await control.getMetricsTrends('30d');

Methods: listAudit, getAudit, listEvents, getEvent, listDLQ, getDLQ, retryDLQ, getMetricsTrends

5. ApplicationClient

Manage applications within an organization. Each organization can contain multiple applications with their own member lists.

typescript
import { ApplicationClient } from '@arsaka/puguh-sdk';

const apps = new ApplicationClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
  organizationId: 'org-uuid',
});

// List all applications
// Returns { applications: Application[], total, limit, offset }
const appList = await apps.list();

// Create a new application
const app = await apps.create({ name: 'MANTRA Dashboard', slug: 'mantra' });

// Get application details
const appDetails = await apps.get(app.id);

// Update application
await apps.update(app.id, { name: 'MANTRA Production' });

// Manage application members
const appMembers = await apps.listMembers(app.id);

// Delete application
await apps.delete(app.id);

Methods: list, get, create, update, delete, listMembers

6. WebhookClient

Configure webhook endpoints and manage event deliveries. Use webhooks to receive real-time notifications when events occur in your organization.

typescript
import { WebhookClient } from '@arsaka/puguh-sdk';

const webhooks = new WebhookClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
  organizationId: 'org-uuid',
});

// List registered webhooks
// Returns { items: Webhook[], total, page, limit, pages }
const list = await webhooks.list();

// Create a new webhook (secret only returned on create)
const webhook = await webhooks.create({
  url: 'https://example.com/webhooks/puguh',
  eventTypes: ['user.registered', 'organization.created'],
  description: 'My webhook endpoint',
});
console.log(webhook.secret); // Save this — not returned again

// Get a webhook (no secret in response)
const wh = await webhooks.get(webhook.id);

// Update webhook
await webhooks.update(webhook.id, {
  description: 'Updated description',
  isActive: false,
});

// List recent deliveries for a webhook
const deliveries = await webhooks.listDeliveries(webhook.id);

// Test webhook (sends ping)
const testResult = await webhooks.test(webhook.id);
console.log(testResult.deliveryId, testResult.status);

// Delete webhook
await webhooks.delete(webhook.id);

Methods: create, list, get, update, delete, listDeliveries, test

TypeScript Types

The SDK exports full type definitions for all API entities. Use them to type your application code:

typescript
import type {
  OrganizationListResponse,
  OrganizationWithMembership,
  IamUser,
  AuditRecord,
  LoginResponse,
  PuguhError,
} from '@arsaka/puguh-sdk';

// Type your handlers
function handleOrg(item: OrganizationWithMembership): void {
  console.log(item.organization.name, item.role);
}

// Organization list is paginated
function showOrgs(response: OrganizationListResponse): void {
  console.log(response.items);       // OrganizationWithMembership[]
  console.log(response.total);       // number
}

Error Handling

All client methods throw PuguhError on non-2xx responses. The error includes the HTTP status code, a human-readable message, and optional details.

typescript
import { PuguhError } from '@arsaka/puguh-sdk';

try {
  const orgs = await client.list();
} catch (error) {
  if (error instanceof PuguhError) {
    console.error(error.statusCode);  // HTTP status code (e.g. 401, 403, 404)
    console.error(error.message);     // Human-readable error message
    console.error(error.errorCode);   // Error code string (e.g. 'UNAUTHORIZED')
    console.error(error.details);     // Optional additional error details
  }
}

Configuration Options

All client constructors accept the same configuration object:

OptionTypeRequiredDescription
baseUrl string Yes PUGUH API URL (e.g. https://api-puguh.arsaka.io)
accessToken string No* JWT access token from AuthClient.login(). Required for all clients except AuthClient.
apiKey string No API key for server-to-server authentication (alternative to accessToken)
organizationId string No Default organization UUID for scoped operations
applicationId string No Default application UUID for scoped operations
timeout number No Request timeout in milliseconds (default: 30000)

Note

AuthClient does not require accessToken since it is used to obtain tokens in the first place. All other clients require either accessToken or apiKey for authentication.

Environment Variables

A recommended .env setup for server-side usage:

bash
PUGUH_BASE_URL=https://api-puguh.arsaka.io
PUGUH_API_KEY=pk_live_your_api_key
PUGUH_ORGANIZATION_ID=your-org-uuid
PUGUH_APPLICATION_ID=your-app-uuid
typescript
import { OrganizationClient } from '@arsaka/puguh-sdk';

const client = new OrganizationClient({
  baseUrl: process.env.PUGUH_BASE_URL!,
  apiKey: process.env.PUGUH_API_KEY!,
  organizationId: process.env.PUGUH_ORGANIZATION_ID!,
});

Best Practices

1. Never Hardcode Credentials

typescript
// Bad
const auth = new AuthClient({ baseUrl: 'https://api-puguh.arsaka.io' });
const { access_token } = await auth.login({
  email: 'admin@arsaka.dev',
  password: 'hardcoded-password',  // Never do this
});

// Good — use environment variables
const { access_token } = await auth.login({
  email: process.env.PUGUH_EMAIL!,
  password: process.env.PUGUH_PASSWORD!,
});

2. Reuse Client Instances

Create client instances once and reuse them. Each instance maintains its configuration and authentication state.

typescript
// Create once at application startup
const orgs = new OrganizationClient({
  baseUrl: process.env.PUGUH_BASE_URL!,
  accessToken: access_token,
});

// Reuse across your application
export { orgs };

3. Handle Token Refresh

Access tokens expire. Use refreshToken to obtain a new access token without requiring the user to log in again.

typescript
import { AuthClient, PuguhError } from '@arsaka/puguh-sdk';

async function withTokenRefresh<T>(
  fn: () => Promise<T>,
  auth: AuthClient,
  refreshToken: string,
): Promise<T> {
  try {
    return await fn();
  } catch (error) {
    if (error instanceof PuguhError && error.statusCode === 401) {
      const { access_token } = await auth.refreshToken({
        refresh_token: refreshToken,
      });
      // Update client token and retry
      client.accessToken = access_token;
      return await fn();
    }
    throw error;
  }
}

4. Use Proper Error Handling

typescript
import { PuguhError } from '@arsaka/puguh-sdk';

try {
  const org = await orgs.get(orgId);
} catch (error) {
  if (error instanceof PuguhError) {
    switch (error.statusCode) {
      case 401:
        // Token expired — refresh and retry
        break;
      case 403:
        // Insufficient permissions
        break;
      case 404:
        // Organization not found
        break;
      case 429:
        // Rate limited — back off and retry
        break;
      default:
        // Unexpected error
        throw error;
    }
  }
}

Related