Skip to content

DiegoMax/uisp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

16 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

UISP CRM API TypeScript Client

npm version GitHub TypeScript

A comprehensive TypeScript client library for the UISP (Ubiquiti ISP Platform) CRM API. This library provides type-safe access to all UISP CRM endpoints with full TypeScript support.

Version 2.x requires Node.js 22 or newer and uses the native Node fetch implementation.

What's New in 2.0.0

Version 2.0.0 is a breaking release focused on modernizing the transport layer, tightening the runtime contract, and cleaning up the public configuration surface.

  • Native Node fetch: the internal HTTP client now uses the built-in Node fetch API instead of axios.
  • Node.js 22+ only: the package now declares node >=22 and should be run on modern Node runtimes with native fetch support.
  • Axios removed: axios is no longer part of the library requirements.
  • Enhanced error handling: requests now use custom UISP error classes for authentication, permission, validation, network, rate-limit, and server failures.
  • Production-safe errors: stack traces are hidden outside development mode and error objects carry structured codes and status information.
  • Simpler config surface: the undocumented no-op retries option has been removed from UispCrmConfig and RequestConfig.
  • Timeout support preserved: request timeouts are still supported through the timeout option, now implemented with AbortController.
  • File downloads preserved: PDF and document download helpers still return ArrayBuffer.
  • Error handling preserved: common HTTP error mappings such as 401, 403, 404, and 422 still return the same library-level error messages.
  • Test coverage improved: fetch-mocked tests were added for the HTTP client and representative API request routing.
  • Dependency tree cleaned up: the lockfile was refreshed and vulnerable transitive dev dependencies were updated.

Upgrade Notes

  • Upgrade your runtime to Node.js 22 or newer before moving to 2.x.
  • Remove any expectation that axios is available or used internally by this package.
  • Remove retries from your client configuration if you were passing it.
  • No changes are required to the main client API shape for normal clients, services, invoices, and related endpoint usage.

Example migration:

const client = new UispCrmClient({
  baseUrl: process.env.UISP_BASE_URL!,
  appKey: process.env.UISP_APP_KEY!,
  timeout: 30000,
});

Features

  • πŸ”’ Type-safe - Full TypeScript support with comprehensive type definitions
  • πŸš€ Complete API coverage - All UISP CRM endpoints supported including Services API
  • πŸ›‘οΈ Enhanced Error handling - Production-safe error handling with custom error types and secure logging
  • πŸ“ Organized structure - Clean, modular API organized by functionality
  • πŸ”§ Easy configuration - Simple setup with sensible defaults
  • πŸ“– Well documented - Comprehensive documentation and examples
  • πŸ”„ Services Management - Full support for service lifecycle management, traffic shaping, and usage tracking
  • πŸ” Security-focused - Stack traces and sensitive information hidden in production
  • πŸ†” Error tracking - Unique error IDs for debugging and monitoring

Installation

npm install uisp-crm-api

Runtime requirement: Node.js 22 or newer.

Quick Start

import { UispCrmClient } from "uisp-crm-api";

// Initialize the client
const client = new UispCrmClient({
  baseUrl: "https://your-uisp-instance.com/crm/api/v1.0",
  appKey: "your-app-key-here",
});

// Test the connection
const isConnected = await client.testConnection();

// Get all clients
const clients = await client.clients.getClients();

// Create a new client
const newClient = await client.clients.createClient({
  firstName: "John",
  lastName: "Doe",
  email: "john.doe@example.com",
});

// Get all services
const services = await client.services.getServices();

// Create a service for the client
const newService = await client.services.createService(newClient.data.id, {
  name: "Internet Service",
  price: 29.99,
});

Configuration

Basic Configuration

const client = new UispCrmClient({
  baseUrl: "https://your-uisp-instance.com/crm/api/v1.0",
  appKey: "your-app-key-here",
});

Advanced Configuration

const client = new UispCrmClient({
  baseUrl: "https://your-uisp-instance.com/crm/api/v1.0",
  appKey: "your-app-key-here",
  timeout: 30000, // Request timeout in milliseconds (default: 30000)
});

Environment Variables

You can also use environment variables:

const client = new UispCrmClient({
  baseUrl: process.env.UISP_BASE_URL!,
  appKey: process.env.UISP_APP_KEY!,
});

Authentication

This library uses UISP CRM App Keys for authentication. To generate an app key:

  1. Go to your UISP CRM instance
  2. Navigate to Settings β†’ Security β†’ App keys
  3. Generate a new app key with appropriate permissions:
    • Read permissions for GET requests
    • Write permissions for POST, PUT, PATCH, DELETE requests

API Reference

The client is organized into logical groups of related functionality:

Clients

// Get all clients
const clients = await client.clients.getClients({
  limit: 10,
  offset: 0,
  query: "john",
  isArchived: 0,
});

// Get specific client
const client = await client.clients.getClient(123);

// Create new client
const newClient = await client.clients.createClient({
  firstName: "John",
  lastName: "Doe",
  email: "john@example.com",
  organizationId: 1,
});

// Update client
const updatedClient = await client.clients.updateClient(123, {
  note: "Updated via API",
});

// Archive/restore client
await client.clients.archiveClient(123);
await client.clients.restoreClient(123);

// Manage client tags
await client.clients.addClientTag(123, 456);
await client.clients.removeClientTag(123, 456);

// Send invitation
await client.clients.sendInvitation(123);

// Geocode address
await client.clients.geocodeClient(123);

Services

// Get all services
const services = await client.services.getServices({
  clientId: 123,
  organizationId: 1,
  statuses: [1], // Active services only
  limit: 10,
});

// Get specific service
const service = await client.services.getService(456);

// Create new service for a client
const newService = await client.services.createService(123, {
  name: "Premium Internet Service",
  price: 49.99,
  servicePlanId: 1,
  activeFrom: "2024-01-01",
  invoicingPeriodType: 1, // MONTH
  invoicingPeriod: 1,
  street1: "123 Service Address",
  city: "Service City",
  zipCode: "12345",
});

// Update service
const updatedService = await client.services.updateService(456, {
  price: 59.99,
  note: "Price updated",
});

// Service management operations
await client.services.geocodeService(456); // Auto-geocode service address
await client.services.activateQuotedService(456, {
  activateDate: "2024-01-01",
  setupFeeInvoiceImmediately: true,
});
await client.services.suspendService(456); // Suspend service
await client.services.cancelSuspendService(456); // Resume service
await client.services.endService(456); // End service permanently

// Pause service for a period
await client.services.pauseService(456, {
  pauseFrom: "2024-06-01",
  pauseTo: "2024-06-30",
});

// Cancel deferred changes
await client.services.cancelDeferredChange(456);

// Traffic shaping controls
await client.services.enableTrafficShapingOverride(456, {
  downloadSpeedOverride: 100, // Mbps
  uploadSpeedOverride: 50, // Mbps
});
await client.services.disableTrafficShapingOverride(456);

// Get service usage data
const usageData = await client.services.getServiceDataUsage(
  456,
  "2024-01-01T00:00:00+0000",
);
console.log(
  `Download: ${usageData.data.download} ${usageData.data.downloadUnit}`,
);

// Service change requests
const changeRequests = await client.services.getServiceChangeRequests();

const newChangeRequest = await client.services.createServiceChangeRequest({
  serviceId: 456,
  servicePlanId: 2,
  note: "Customer requested upgrade",
});

await client.services.acceptServiceChangeRequest(newChangeRequest.data.id);

// Prepaid service periods (for prepaid services)
const periods = await client.services.getPrepaidServicePeriods({
  serviceId: 456,
  limit: 10,
});

const newPeriod = await client.services.createPrepaidServicePeriod({
  serviceId: 456,
  startDate: "2024-01-01",
  endDate: "2024-01-31",
  price: 49.99,
});

Client Bank Accounts

// Get client bank accounts
const bankAccounts = await client.clients.getClientBankAccounts(123);

// Create bank account
const newAccount = await client.clients.createClientBankAccount(123, {
  name: "Primary Account",
  field1: "Account Number",
  field2: "Routing Number",
});

// Update bank account
await client.clients.updateClientBankAccount(456, {
  name: "Updated Account Name",
});

// Delete bank account
await client.clients.deleteClientBankAccount(456);

Client Contacts

// Get client contacts
const contacts = await client.clients.getClientContacts(123);

// Create contact
const newContact = await client.clients.createClientContact(123, {
  name: "Jane Doe",
  email: "jane@example.com",
  phone: "+1234567890",
  isBilling: true,
});

// Update contact
await client.clients.updateClientContact(456, {
  name: "Updated Name",
});

// Delete contact
await client.clients.deleteClientContact(456);

Invoices

// Get invoices
const invoices = await client.invoices.getInvoices({
  clientId: 123,
  statuses: [1, 2], // Draft, Unpaid
  createdDateFrom: "2024-01-01",
  createdDateTo: "2024-12-31",
});

// Create invoice
const newInvoice = await client.invoices.createInvoiceForClient(123, {
  organizationId: 1,
  maturityDays: 30,
  notes: "Monthly service fee",
  invoiceItems: [
    {
      type: "service",
      label: "Internet Service",
      price: 50.0,
      quantity: 1,
      unit: "month",
      taxRate1: 10,
    },
  ],
});

// Invoice operations
await client.invoices.approveInvoice(456);
await client.invoices.sendInvoice(456);
await client.invoices.voidInvoice(456);

// Download PDF
const pdfBuffer = await client.invoices.getInvoicePdf(456);

Credit Notes

// Get credit notes
const creditNotes = await client.creditNotes.getCreditNotes({
  clientId: 123,
  createdDateFrom: "2024-01-01",
});

// Create credit note
const newCreditNote = await client.creditNotes.createCreditNoteForClient(123, {
  organizationId: 1,
  number: "CN-001",
  notes: "Service credit",
  creditNoteItems: [
    {
      type: "credit",
      label: "Service Credit",
      price: -25.0,
      quantity: 1,
    },
  ],
});

// Send credit note
await client.creditNotes.sendCreditNote(456);

// Download PDF
const pdfBuffer = await client.creditNotes.getCreditNotePdf(456);

Organizations

// Get organizations
const organizations = await client.organizations.getOrganizations();

// Create organization
const newOrg = await client.organizations.createOrganization({
  name: "My ISP Company",
  email: "billing@myisp.com",
  street1: "123 Main St",
  city: "Anytown",
  zipCode: "12345",
});

// Get next invoice numbers
const nextInvoice = await client.organizations.getNextInvoiceNumber(1);
const nextProforma = await client.organizations.getNextProformaInvoiceNumber(1);
const nextQuote = await client.organizations.getNextQuoteNumber(1);

// Send email
await client.organizations.enqueueEmail(1, {
  subject: "Test Email",
  body: "Hello World",
  to: ["customer@example.com"],
});

Jobs (Scheduling)

// Get jobs
const jobs = await client.jobs.getJobs({
  clientId: 123,
  assignedUserId: 456,
  dateFrom: "2024-01-01",
  dateTo: "2024-12-31",
});

// Create job
const newJob = await client.jobs.createJob({
  title: "Installation Service",
  description: "Install new service",
  clientId: 123,
  assignedUserId: 456,
  date: "2024-12-01T10:00:00Z",
  duration: 120,
  status: 1,
});

// Add job comment
await client.jobComments.createJobComment({
  jobId: 789,
  message: "Job completed successfully",
});

// Add job task
await client.jobTasks.createJobTask({
  jobId: 789,
  label: "Verify equipment",
  closed: false,
});

Documents & Files

// Get documents
const documents = await client.documents.getDocuments({
  clientId: 123,
  types: ["document", "image"],
});

// Upload document
const newDoc = await client.documents.createDocument({
  clientId: 123,
  name: "contract.pdf",
  file: base64FileContent,
  mimeType: "application/pdf",
});

// Download file
const fileBuffer = await client.documents.getDocumentFile(456);

Custom Attributes

// Get custom attributes
const attributes = await client.customAttributes.getCustomAttributes({
  attributeType: "client",
});

// Create custom attribute
const newAttribute = await client.customAttributes.createCustomAttribute({
  name: "Customer Tier",
  attributeType: "client",
  key: "customer_tier",
});

Geocoding

// Geocode address
const location = await client.geocoding.geocode({
  address: "123 Main St, Anytown, USA",
});

// Get address suggestions
const suggestions = await client.geocoding.suggestAddresses({
  query: "123 Main",
  lat: "40.7128",
  lon: "-74.0060",
});

Error Handling

The library provides comprehensive, production-safe error handling with custom error types:

import {
  UispNetworkError,
  UispAuthenticationError,
  UispPermissionError,
  UispNotFoundError,
  UispValidationError,
} from "uisp-crm-api";

try {
  const client = await client.clients.getClient(123);
} catch (error) {
  if (error instanceof UispAuthenticationError) {
    console.error("Authentication failed - check your app key");
  } else if (error instanceof UispPermissionError) {
    console.error("Insufficient permissions for this operation");
  } else if (error instanceof UispNotFoundError) {
    console.error("Resource not found:", error.message);
  } else if (error instanceof UispValidationError) {
    console.error("Invalid input data:", error.message);
  } else if (error instanceof UispNetworkError) {
    console.error("Network connection failed");
  } else {
    console.error("Unexpected error:", error.message);
  }
}

Error Types

The library includes specific error types for different scenarios:

  • UispNetworkError - Connection issues, server unreachable
  • UispAuthenticationError - Invalid or missing app key (401)
  • UispPermissionError - Insufficient permissions (403)
  • UispNotFoundError - Resource not found (404)
  • UispValidationError - Request validation failures (422)
  • UispRateLimitError - Rate limiting exceeded (429)
  • UispServerError - Internal server errors (500)
  • UispServiceUnavailableError - Service temporarily unavailable (503)

Security Features

  • πŸ”’ Stack trace protection: Stack traces are hidden in production environments
  • 🎭 Sensitive data masking: API keys, file paths, and internal details are never exposed
  • πŸ†” Error tracking: Each error includes a unique tracking ID for debugging
  • πŸ“Š Structured logging: Clean, consistent error messages suitable for monitoring systems

TypeScript Support

This library is written in TypeScript and provides full type definitions:

import { ClientReadOnly, ClientWritable, InvoiceReadOnly } from "uisp-crm-api";

// All API responses are properly typed
const clients: ClientReadOnly[] = (await client.clients.getClients()).data;

// Input parameters are validated at compile time
const newClient: ClientWritable = {
  firstName: "John", // βœ… Valid
  lastName: "Doe", // βœ… Valid
  invalidField: "value", // ❌ TypeScript error
};

Examples

See the examples/ directory for more comprehensive examples:

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

Development

# Install dependencies
npm install

# Build the library
npm run build

# Run tests
npm test

# Lint code
npm run lint

# Watch mode for development
npm run build:watch

License

MIT License - see LICENSE file for details.

Support

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Native TypeScript library for the UISP CRM API

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors