pbx0-sdk
v1.0.8
Published
PBX SDK for authentication and API integration
Maintainers
Readme
PBX0 SDK
A lightweight, modular TypeScript SDK for integrating with PBX APIs. Built with Axios, featuring automatic token management, request interceptors, and centralized error handling.
Table of Contents
- Installation
- Quick Start
- Architecture
- Authentication
- Module Reference
- Pagination
- Error Handling
- Token Management
- Usage Patterns
- Building & Development
Installation
npm install pbx0-sdkQuick Start
import { PBX0SDK } from 'pbx0-sdk';
const pbx = new PBX0SDK();
// Authenticate
const user = await pbx.auth.login({
email: '[email protected]',
password: 'your-password',
});
// Fetch users
const users = await pbx.user.getAll({ page: 1, limit: 10 });
// Fetch companies
const companies = await pbx.company.getAll({ page: 1, limit: 10 });Architecture
The SDK follows a modular architecture. Each business domain is a separate module, all sharing a single HTTP client with built-in authentication.
PBX0SDK (entry point)
├── auth → Authentication (login, token refresh)
├── user → Users & contacts
├── department → Departments
├── account → Accounts, clusters, sales agents
├── company → Companies
├── extension → Extensions
├── did → DIDs, port-ins, music-on-hold
├── ivr → IVR menus
├── queue → Call queues
├── netwatch → Network monitoring
├── schedule → Time-of-day routing
├── callGroup → Call/hunt groups
└── carrier → Carriers & carrier trunksCore Layer
| Component | Purpose |
|-----------|---------|
| PBXClient | Creates the Axios HTTP client with base URL |
| AuthManager | Singleton that stores access token & expiry |
| Request Interceptor | Injects Authorization: Bearer <token> on every request |
| Response Interceptor | Auto-refreshes token on 401, queues concurrent failures |
| Response Handler | Unwraps { status, data } envelope, throws PBXError on failure |
| PBXError | Custom error class with statusCode and errors fields |
Base URL Configuration
The SDK automatically resolves the API base URL from environment variables. You do not need to pass baseURL when constructing PBX0SDK:
| Environment Variable | Context | Priority |
|---------------------|---------|----------|
| API_URL | Server-side (Node.js) | Highest |
| NEXT_PUBLIC_API_URL | Client-side (Browser) | Fallback |
// No config needed — SDK reads API_URL or NEXT_PUBLIC_API_URL automatically
const pbx = new PBX0SDK();You may still explicitly pass baseURL to override the environment variables:
const pbx = new PBX0SDK({ baseURL: 'https://custom-api.example.com/api' });API Response Format
The SDK expects all API responses to follow this Laravel-style structure:
{
"status": true,
"message": "Success",
"data": { ... }
}On success, the SDK automatically unwraps and returns response.data.data. On failure, it throws a PBXError.
Authentication
Login
const user = await pbx.auth.login({
email: '[email protected]',
password: 'your-password',
});
// Returns user object
// Token is automatically stored in AuthManagerToken Management
// Manually set a token (e.g., from NextAuth session)
pbx.setToken('your-access-token', Date.now() + 3600 * 1000);
// Clear token (logout)
pbx.clearToken();Auto Token Refresh
When a 401 response is received, the SDK automatically:
- Calls
/auth/refreshto obtain a new token - Queues any concurrent requests that arrive during refresh
- Replays queued requests with the new token
- Falls back to clearing the token if refresh fails
No manual handling required.
Module Reference
1. AuthModule
Access: pbx.auth
| Method | Endpoint | Description |
|--------|----------|-------------|
| login(payload) | POST /auth/login | Authenticate with email & password |
| refreshToken() | POST /auth/refresh | Refresh the access token |
Login Example:
const user = await pbx.auth.login({
email: '[email protected]',
password: 'your-password',
});2. UserModule
Access: pbx.user
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /users | List users (paginated) |
| getById(id) | GET /users/:id | Get user by ID |
| create(payload) | POST /users | Create a new user |
| update(id, payload) | PUT /users/:id | Update a user |
| getProfile() | GET /user/profile | Get current user's profile |
| getContacts(options?) | GET /contacts | List contacts (paginated) |
| createContact(payload) | POST /contacts | Create a new contact |
Examples:
// Get paginated user list
const result = await pbx.user.getAll({ page: 1, limit: 25 });
console.log(result.data); // User[]
console.log(result.total); // Total count
console.log(result.current_page);
// Get user by ID
const user = await pbx.user.getById('123');
// Create user
const newUser = await pbx.user.create({
name: 'John Doe',
email: '[email protected]',
role: 'admin',
});
// Update user
await pbx.user.update('123', { name: 'Jane Doe' });
// Get my profile
const profile = await pbx.user.getProfile();
// Get contacts
const contacts = await pbx.user.getContacts({ page: 1, limit: 50 });
// Create contact
const contact = await pbx.user.createContact({
name: 'Jane Smith',
phone: '+1-555-0199',
email: '[email protected]',
});3. DepartmentModule
Access: pbx.department
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /departments | List all departments |
Example:
const departments = await pbx.department.getAll({ page: 1, limit: 20 });
// Returns Department[]4. AccountModule
Access: pbx.account
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /accounts | List accounts (paginated) |
| getById(id) | GET /accounts/:id | Get account by ID |
| create(payload) | POST /accounts | Create a new account |
| update(id, payload) | PUT /accounts/:id | Update an account |
| getClusters() | GET /clusters | Get all clusters |
| getSalesAgents() | GET /sales-agents | Get all sales agents |
Examples:
// List accounts
const accounts = await pbx.account.getAll({ page: 1, limit: 20 });
// Get account by ID
const account = await pbx.account.getById('456');
// Create account
const newAccount = await pbx.account.create({
name: 'Acme Corp',
cluster_id: 'cluster-1',
});
// Get clusters
const clusters = await pbx.account.getClusters();
// Get sales agents
const agents = await pbx.account.getSalesAgents();5. CompanyModule
Access: pbx.company
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /companies | List companies (paginated) |
| getById(id) | GET /companies/:id | Get company by ID |
| create(payload) | POST /companies | Create a new company |
| update(id, payload) | PUT /companies/:id | Update a company |
Examples:
// List companies
const result = await pbx.company.getAll({ page: 1, limit: 10 });
// Get company by ID
const company = await pbx.company.getById('comp-1');
// Create company
await pbx.company.create({
company_name: 'New Corp',
domain: 'newcorp.com',
email: '[email protected]',
});
// Update company
await pbx.company.update('comp-1', { company_name: 'Updated Corp' });6. ExtensionModule
Access: pbx.extension
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /extensions | List extensions (paginated) |
| getById(id) | GET /extensions/:id | Get extension by ID |
| create(payload) | POST /extensions | Create a new extension |
| update(id, payload) | PUT /extensions/:id | Update an extension |
| delete(id) | DELETE /extensions/:id | Delete an extension |
Examples:
// List extensions
const result = await pbx.extension.getAll({ page: 1, limit: 20 });
// Create extension
await pbx.extension.create({
account_id: 'acc-1',
extension_number: 101,
route_type: 'user',
route_id: 'user-123',
});
// Delete extension
await pbx.extension.delete('ext-1');7. DIDModule
Access: pbx.did
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /dids | List DIDs (paginated) |
| getById(id) | GET /dids/:id | Get DID by ID |
| create(payload) | POST /dids | Create a new DID |
| update(id, payload) | PUT /dids/:id | Update a DID |
| delete(id) | DELETE /dids/:id | Delete a DID |
| bulkUpdate(payload) | PATCH /dids/bulk-update | Bulk update DIDs |
| getMusicOnHold() | GET /music-on-hold | Get music-on-hold entries |
| getPortIns(options?) | GET /dids/port-in | List port-in requests |
| getPortInById(id) | GET /dids/port-in/:id | Get port-in by ID |
| createPortIn(payload) | POST /dids/port-in | Create port-in request |
| updatePortIn(id, payload) | PUT /dids/port-in/:id | Update port-in |
| deletePortIn(id) | DELETE /dids/port-in/:id | Delete port-in |
Examples:
// List DIDs
const result = await pbx.did.getAll({ page: 1, limit: 20 });
// Create DID
await pbx.did.create({
did: '+1-555-1234',
account_id: 'acc-1',
});
// Bulk update DIDs
const updated = await pbx.did.bulkUpdate({
ids: ['did-1', 'did-2'],
account_id: 'acc-2',
});
// Get music on hold list
const music = await pbx.did.getMusicOnHold();
// Port-in operations
const ports = await pbx.did.getPortIns({ page: 1, limit: 20 });
const port = await pbx.did.getPortInById('port-1');
await pbx.did.createPortIn({ number: '+1-555-5678' });
await pbx.did.deletePortIn('port-1');8. IVRsModule
Access: pbx.ivr
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /ivrs | List IVRs (paginated) |
| getById(id) | GET /ivrs/:id | Get IVR by ID |
| create(payload) | POST /ivrs | Create a new IVR |
| update(id, payload) | PUT /ivrs/:id | Update an IVR |
Examples:
const result = await pbx.ivr.getAll({ page: 1, limit: 10 });
const ivr = await pbx.ivr.getById('ivr-1');
await pbx.ivr.create({ name: 'Main Menu', account_id: 'acc-1' });
await pbx.ivr.update('ivr-1', { name: 'Updated Menu' });9. QueuesModule
Access: pbx.queue
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /queues | List queues (paginated) |
| getById(id) | GET /queues/:id | Get queue by ID |
| create(payload) | POST /queues | Create a new queue |
| update(id, payload) | PUT /queues/:id | Update a queue |
Examples:
const result = await pbx.queue.getAll({ page: 1, limit: 10 });
const queue = await pbx.queue.getById('queue-1');
await pbx.queue.create({ name: 'Support Queue', account_id: 'acc-1' });10. NetwatchModule
Access: pbx.netwatch
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /netwatches | List netwatch entries |
| getById(id) | GET /netwatch/:id | Get netwatch by ID |
| create(payload) | POST /netwatch | Create netwatch entry |
| update(id, payload) | PUT /netwatch/:id | Update netwatch |
| delete(id) | DELETE /netwatch/:id | Delete netwatch |
| checkStatus(id) | POST /api/netwatch/:id/check-status | Check netwatch status |
Examples:
const result = await pbx.netwatch.getAll({ page: 1, limit: 10 });
await pbx.netwatch.create({
description: 'Main Gateway',
ip_address: '10.0.0.1',
up_route_type: 'trunk',
up_route_id: 'trunk-1',
down_route_type: 'ivr',
down_route_id: 'ivr-1',
});
const status = await pbx.netwatch.checkStatus('net-1');
// { status: 'OK' | 'Failed', latency?: number }11. ScheduleModule
Access: pbx.schedule
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /time-of-day | List schedules (paginated) |
| getById(id) | GET /time-of-day/:id | Get schedule by ID |
| create(payload) | POST /time-of-day | Create a schedule |
| update(id, payload) | PUT /time-of-day/:id | Update a schedule |
| delete(id) | DELETE /time-of-day/:id | Delete a schedule |
Examples:
const result = await pbx.schedule.getAll({ page: 1, limit: 10 });
const schedule = await pbx.schedule.getById('sched-1');
await pbx.schedule.create({
account_id: 'acc-1',
description: 'Business Hours',
daymode: { active: true, route_id: 'ivr-1', route_type: 'ivr' },
nightmode: { active: false },
});
await pbx.schedule.delete('sched-1');12. CallGroupModule
Access: pbx.callGroup
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /api/callgroups | List call groups (paginated) |
| getById(id) | GET /api/callgroups/:id | Get call group by ID |
| create(payload) | POST /api/callgroups | Create a call group |
| update(id, payload) | PUT /api/callgroups/:id | Update a call group |
| delete(id) | DELETE /api/callgroups/:id | Delete a call group |
Examples:
const result = await pbx.callGroup.getAll({ page: 1, limit: 10 });
const group = await pbx.callGroup.getById('cg-1');
await pbx.callGroup.create({
account_id: 'acc-1',
description: 'Sales Team',
extension: '200',
strategy: 'ringall',
ringTime: 30,
members: [
{ id: 'user-1', name: 'Alice', isActive: true, isMonitoring: false },
],
});
await pbx.callGroup.delete('cg-1');13. CarrierModule
Access: pbx.carrier
| Method | Endpoint | Description |
|--------|----------|-------------|
| getAll(options?) | GET /carriers | List carriers (paginated) |
| getById(id) | GET /carriers/:id | Get carrier by ID |
| create(payload) | POST /carriers | Create a new carrier |
| update(id, payload) | PUT /carriers/:id | Update a carrier |
| getTrunks(options?) | GET /carrier-trunks | List carrier trunks (paginated) |
Examples:
// List carriers
const result = await pbx.carrier.getAll({ page: 1, limit: 10 });
// Create carrier
await pbx.carrier.create({
company_id: 'comp-1',
carrier_name: 'My Carrier',
address: '123 Main St',
city: 'New York',
state: 'NY',
zipcode: '10001',
billing: { email: '[email protected]', contact: 'John', phone: '', url: '' },
technical: { email: '', contact: '', phone: '', url: '' },
porting: { email: '', contact: '', phone: '', url: '' },
});
// Get carrier trunks
const trunks = await pbx.carrier.getTrunks({ page: 1, limit: 10 });Pagination
Modules that return lists follow a standard paginated response:
interface PaginatedResponse<T> {
data: T[]; // Array of items for current page
total: number; // Total items across all pages
current_page: number; // Current page number
per_page: number; // Items per page
last_page: number; // Total number of pages
}Modules using QueryOptions support:
interface QueryOptions {
isPaginated?: boolean; // Default: true
page?: number; // Default: 1
limit?: number; // Default: 10
search?: string; // Search term
sort?: string; // Sort field (prefix with "-" for descending, e.g. "-createdAt")
all?: boolean; // Fetch all records at once (skips pagination)
filters?: Record<string, any>; // Additional query filters
}Fetching all records:
const allAccounts = await pbx.account.getAll({ all: true });Fetching all records with sorting:
const allAccounts = await pbx.account.getAll({
all: true,
sort: "-createdAt",
});Paginated with sorting:
const result = await pbx.account.getAll({
page: 1,
limit: 25,
sort: "name",
});Example: Iterating all pages
async function getAllUsers(): Promise<User[]> {
let page = 1;
const all: User[] = [];
let total = 0;
do {
const result = await pbx.user.getAll({ page, limit: 100 });
all.push(...result.data);
total = result.total;
page++;
} while (all.length < total);
return all;
}Error Handling
All SDK methods throw PBXError on API failure:
import { PBX0SDK, PBXError } from 'pbx0-sdk';
try {
const user = await pbx.user.getById('invalid-id');
} catch (err) {
if (err instanceof PBXError) {
console.log(err.message); // Human-readable error message
console.log(err.statusCode); // HTTP status code (e.g., 404)
console.log(err.errors); // Field-level validation errors
}
}Error Properties
| Property | Type | Description |
|----------|------|-------------|
| message | string | Error message from the API |
| statusCode | number? | HTTP status code |
| errors | any | Field-level validation errors (typically Record<string, string[]>) |
Token Management
Setting a Token from External Auth
When using an external authentication system (e.g., NextAuth):
import { PBX0SDK } from 'pbx0-sdk';
const pbx = new PBX0SDK();
// After obtaining a token from your auth provider
pbx.setToken('jwt-access-token', expiresAtTimestamp);
// Later, to log out
pbx.clearToken();Token Persistence
The token is stored in memory only (via AuthManager singleton). For persistence across page reloads, store the token in localStorage or cookies and call setToken() on app initialization.
Usage Patterns
Client-Side (Browser)
import { PBX0SDK } from 'pbx0-sdk';
// Create a singleton instance
const pbx = new PBX0SDK();
// Set token from your auth system
pbx.setToken(accessToken, expiresAt);
// Use anywhere
async function loadUsers() {
const result = await pbx.user.getAll({ page: 1, limit: 25 });
return result.data;
}Server-Side (Next.js Server Actions)
import { getServerSDK } from '@/libs/server-sdk';
export async function getUsers() {
const pbx = await getServerSDK();
const result = await pbx.user.getAll({ page: 1, limit: 10 });
return { data: result.data, total: result.total };
}Server SDK factory implementation:
import { PBX0SDK } from 'pbx0-sdk';
let instance: PBX0SDK | null = null;
export async function getServerSDK(): Promise<PBX0SDK> {
const token = await getAuthTokenFromSession();
if (!instance) {
instance = new PBX0SDK();
}
if (token) {
instance.setToken(token, Infinity);
}
return instance;
}Complete Client-Side Integration Example (React)
import { PBX0SDK } from 'pbx0-sdk';
import { useEffect, useState } from 'react';
const pbx = new PBX0SDK();
function UsersPage() {
const [users, setUsers] = useState<any[]>([]);
useEffect(() => {
// Token is assumed to already be set via setToken()
pbx.user.getAll({ page: 1, limit: 10 })
.then((result) => setUsers(result.data))
.catch((err) => console.error('Failed:', err.message));
}, []);
return (
<ul>
{users.map((u) => <li key={u.id}>{u.name}</li>)}
</ul>
);
}Building & Development
# Build the SDK
npm run build
# Development - watch mode (auto-rebuild on changes)
npm run dev
# Run tests
npm test
# Run example
npm run startExports
import { PBX0SDK, PBXError } from 'pbx0-sdk';| Export | Description |
|--------|-------------|
| PBX0SDK | Main SDK class with all modules |
| PBXError | Custom error class for API errors |
License
MIT
