{"_id":"@avantguardllc/node-halopsa","_rev":"2-1eeeb54a1bacfaadba69bd93b252c3d5","name":"@avantguardllc/node-halopsa","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.1":{"name":"@avantguardllc/node-halopsa","version":"1.0.1","keywords":["halopsa","halo","psa","msp","api","typescript","itsm","ticketing"],"author":{"name":"Wyre Technology"},"license":"Apache-2.0","_id":"@avantguardllc/node-halopsa@1.0.1","maintainers":[{"name":"avantguardllc","email":"appadmin@avantguard.it"}],"homepage":"https://github.com/wyre-technology/node-halopsa#readme","bugs":{"url":"https://github.com/wyre-technology/node-halopsa/issues"},"dist":{"shasum":"8afa01473bb530453fb00b7e03a290117997d258","tarball":"https://registry.npmjs.org/@avantguardllc/node-halopsa/-/node-halopsa-1.0.1.tgz","fileCount":180,"integrity":"sha512-o0WZ0spvK7ymOt6LPpHsmXzQX4aFQG5gIoWmsNiP3SoYvUf5Qi63v+2b9Uq+MK2o+7RlLO4CEMiL/Rhe+lqRKg==","signatures":[{"sig":"MEUCIC7fnmNVs1psqBD8frbR2/ycEb/xC3LPW2iRYtqor4XCAiEA/x792HJMDdStdKiwIe4jdIyC93qKGyXsTzvZNtSc6mg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":330736},"main":"./dist/index.cjs","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"3363227475b63f4662d1c9dbb3adb55cdbfdc2f3","scripts":{"dev":"tsup --watch","lint":"eslint src tests --ext .ts","test":"vitest run","build":"tsup","lint:fix":"eslint src tests --ext .ts --fix","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"avantguardllc","email":"appadmin@avantguard.it"},"repository":{"url":"git+https://github.com/wyre-technology/node-halopsa.git","type":"git"},"_npmVersion":"10.8.2","description":"Comprehensive, fully-typed Node.js/TypeScript library for the HaloPSA API","directories":{},"_nodeVersion":"20.20.2","dependencies":{"@rollup/rollup-linux-x64-gnu":"^4.60.3"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.1.0","tsup":"^8.0.1","eslint":"^8.56.0","vitest":"^1.3.0","typescript":"^5.3.3","@types/node":"^20.11.0","semantic-release":"^24.2.3","@vitest/coverage-v8":"^1.3.0","@semantic-release/git":"^10.0.1","@semantic-release/github":"^11.0.1","@typescript-eslint/parser":"^7.0.0","@semantic-release/changelog":"^6.0.3","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/node-halopsa_1.0.1_1777948657726_0.6800496175115107","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@avantguardllc/node-halopsa","version":"1.0.2","description":"Comprehensive, fully-typed Node.js/TypeScript library for the HaloPSA API","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"keywords":["halopsa","halo","psa","msp","api","typescript","itsm","ticketing"],"author":{"name":"AvantGuard LLC"},"license":"Apache-2.0","engines":{"node":">=22.0.0"},"devDependencies":{"@types/node":"^20.11.0","@typescript-eslint/eslint-plugin":"^7.0.0","@typescript-eslint/parser":"^7.0.0","@vitest/coverage-v8":"^1.3.0","eslint":"^8.56.0","msw":"^2.1.0","tsup":"^8.0.1","typescript":"^5.3.3","vitest":"^1.3.0"},"dependencies":{},"_id":"@avantguardllc/node-halopsa@1.0.2","gitHead":"3363227475b63f4662d1c9dbb3adb55cdbfdc2f3","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-sGjjAl/wdsEkGdr0cpERYgeaMJYwITb8faE241VVZVsDhj3OVJXkUvrVFU8tlwPBZzz+nrHv5snfK0hgLYAHrQ==","shasum":"cd317966ed97a9873a5d1f5ab9bed0d9e7303660","tarball":"https://registry.npmjs.org/@avantguardllc/node-halopsa/-/node-halopsa-1.0.2.tgz","fileCount":180,"unpackedSize":321540,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDadFGZGf+rwc/ePoMbIY2wJ1xHtVeMmVFbSKj4Mk306gIga2sbHs9BIeVSTs6LwMZ+CIvzUeCpjChfpL1XxQqjqrY="}]},"_npmUser":{"name":"avantguardllc","email":"appadmin@avantguard.it"},"directories":{},"maintainers":[{"name":"avantguardllc","email":"appadmin@avantguard.it"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/node-halopsa_1.0.2_1777981290603_0.6057519668774705"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-05T02:37:37.614Z","modified":"2026-05-05T11:41:30.866Z","1.0.1":"2026-05-05T02:37:37.944Z","1.0.2":"2026-05-05T11:41:30.750Z"},"author":{"name":"AvantGuard LLC"},"license":"Apache-2.0","keywords":["halopsa","halo","psa","msp","api","typescript","itsm","ticketing"],"description":"Comprehensive, fully-typed Node.js/TypeScript library for the HaloPSA API","maintainers":[{"name":"avantguardllc","email":"appadmin@avantguard.it"}],"readme":"# node-halopsa\n\nComprehensive, fully-typed Node.js/TypeScript library for the HaloPSA API.\n\n## Features\n\n- **Complete API coverage** - All documented HaloPSA endpoints are implemented\n- **Full TypeScript support** - Complete type definitions for all requests, responses, and entities\n- **OAuth 2.0 Client Credentials** - Automatic token acquisition, caching, and refresh\n- **Multi-tenant support** - Handle company-specific subdomains\n- **Automatic pagination** - Iterator patterns for seamless multi-page retrieval\n- **Rate limit handling** - Built-in request throttling (500 req/3min) with HTTP 429 backoff\n- **Zero live API testing** - Full test suite with mocked HTTP responses\n\n## Installation\n\n```bash\nnpm install node-halopsa\n```\n\n## Quick Start\n\n```typescript\nimport { HaloPsaClient } from 'node-halopsa';\n\nconst client = new HaloPsaClient({\n  clientId: process.env.HALOPSA_CLIENT_ID!,\n  clientSecret: process.env.HALOPSA_CLIENT_SECRET!,\n  tenant: 'yourcompany', // Results in https://yourcompany.halopsa.com\n});\n\n// List open tickets\nconst tickets = await client.tickets.list({ open_only: true });\n\n// Get a specific ticket\nconst ticket = await client.tickets.get(123);\n\n// Create a new ticket\nconst newTicket = await client.tickets.create({\n  summary: 'Network connectivity issue',\n  details: 'User cannot access shared drives',\n  client_id: 1,\n  tickettype_id: 1,\n});\n```\n\n## Configuration\n\n```typescript\nconst client = new HaloPsaClient({\n  // Required\n  clientId: 'your-client-id',\n  clientSecret: 'your-client-secret',\n\n  // Provide one of these:\n  tenant: 'yourcompany',                        // Uses https://yourcompany.halopsa.com\n  // OR\n  baseUrl: 'https://yourcompany.halopsa.com',  // Explicit URL\n\n  // Optional\n  tenantId: 'specific-tenant-id',              // For multi-tenant applications\n  scope: 'all',                                 // OAuth scope (default: 'all')\n\n  // Rate limiting (optional)\n  rateLimit: {\n    enabled: true,                              // default: true\n    maxRequests: 500,                           // default: 500\n    windowMs: 180000,                           // default: 180000 (3 minutes)\n    throttleThreshold: 0.8,                     // default: 0.8 (80%)\n    retryAfterMs: 5000,                         // default: 5000\n    maxRetries: 3,                              // default: 3\n  },\n});\n```\n\n## Resources\n\n### Tickets\n\n```typescript\n// List tickets with filtering\nconst tickets = await client.tickets.list({\n  client_id: 123,\n  status_id: 1,\n  open_only: true,\n  pageSize: 50,\n});\n\n// Auto-paginate all tickets\nfor await (const ticket of client.tickets.listAll()) {\n  console.log(ticket.summary);\n}\n\n// Collect all into array\nconst allTickets = await client.tickets.listAll().toArray();\n\n// Get, create, update, delete\nconst ticket = await client.tickets.get(1);\nconst newTicket = await client.tickets.create({ ... });\nawait client.tickets.update(1, { status_id: 2 });\nawait client.tickets.delete(1);\n\n// Ticket actions\nconst actions = await client.tickets.actions(1);\nawait client.tickets.addAction(1, { note: 'Working on it', timetaken: 15 });\n\n// Attachments\nconst attachments = await client.tickets.attachments(1);\nawait client.tickets.addAttachment(1, { filename: 'log.txt', data: '...' });\n```\n\n### Clients (Companies)\n\n```typescript\nconst clients = await client.clients.list();\nconst acme = await client.clients.get(1);\nconst newClient = await client.clients.create({ name: 'New Corp' });\nawait client.clients.update(1, { notes: 'Updated' });\nawait client.clients.delete(1);\n```\n\n### Sites\n\n```typescript\nconst sites = await client.sites.list({ client_id: 1 });\nconst site = await client.sites.get(1);\nconst newSite = await client.sites.create({\n  name: 'Headquarters',\n  client_id: 1,\n  addressline1: '123 Main St',\n});\n```\n\n### Assets (Configuration Items)\n\n```typescript\nconst assets = await client.assets.list({ client_id: 1 });\nconst asset = await client.assets.get(1);\nconst newAsset = await client.assets.create({\n  assettype_id: 1,\n  client_id: 1,\n  key_field: 'LAPTOP-001',\n});\n\n// Asset types\nconst types = await client.assetTypes.list();\nconst laptopType = await client.assetTypes.get(1);\n```\n\n### Contacts (Users)\n\n```typescript\nconst contacts = await client.contacts.list({ client_id: 1 });\nconst contact = await client.contacts.get(1);\nconst newContact = await client.contacts.create({\n  name: 'John Smith',\n  emailaddress: 'john@example.com',\n  client_id: 1,\n});\n```\n\n### Items (Products/Services)\n\n```typescript\nconst items = await client.items.list();\nconst item = await client.items.get(1);\nconst newItem = await client.items.create({\n  name: 'Hourly Support',\n  type: 'service',\n  unitprice: 150,\n});\n```\n\n### Contracts\n\n```typescript\nconst contracts = await client.contracts.list({ client_id: 1 });\nconst contract = await client.contracts.get(1);\n```\n\n### Invoices\n\n```typescript\nconst invoices = await client.invoices.list();\nconst invoice = await client.invoices.get(1);\nconst newInvoice = await client.invoices.create({\n  client_id: 1,\n  invoicedate: '2026-02-01',\n  lines: [{ item_id: 1, quantity: 2, unitprice: 100 }],\n});\nawait client.invoices.send(1);\n```\n\n### Quotes\n\n```typescript\nconst quotes = await client.quotes.list();\nconst quote = await client.quotes.get(1);\nawait client.quotes.send(1);\nconst invoiceResult = await client.quotes.convertToInvoice(1);\n```\n\n### Projects\n\n```typescript\nconst projects = await client.projects.list({ client_id: 1 });\nconst project = await client.projects.get(1);\nconst tasks = await client.projects.tasks(1);\n```\n\n### Agents (Technicians)\n\n```typescript\nconst agents = await client.agents.list();\nconst agent = await client.agents.get(1);\nconst me = await client.agents.me(); // Current authenticated agent\n```\n\n### Reference Data\n\n```typescript\n// Ticket types\nconst ticketTypes = await client.ticketTypes.list();\nconst incidentType = await client.ticketTypes.get(1);\n\n// Statuses\nconst statuses = await client.statuses.list();\nconst newStatus = await client.statuses.get(1);\n\n// Priorities\nconst priorities = await client.priorities.list();\n\n// Categories\nconst categories = await client.categories.list();\n\n// SLAs\nconst slas = await client.slas.list();\n\n// Custom fields\nconst customFields = await client.customFields.list();\n\n// User roles\nconst roles = await client.userRoles.list();\n```\n\n### Knowledge Base\n\n```typescript\nconst articles = await client.knowledgeBase.list();\nconst article = await client.knowledgeBase.get(1);\nawait client.knowledgeBase.create({\n  title: 'How to Reset Password',\n  content: 'Step by step guide...',\n});\n```\n\n### Reports\n\n```typescript\nconst reports = await client.reports.list();\nconst report = await client.reports.get(1);\nconst result = await client.reports.run(1, { startDate: '2026-01-01' });\n```\n\n## Error Handling\n\n```typescript\nimport {\n  HaloPsaError,\n  HaloPsaAuthenticationError,\n  HaloPsaForbiddenError,\n  HaloPsaNotFoundError,\n  HaloPsaValidationError,\n  HaloPsaRateLimitError,\n  HaloPsaServerError,\n} from 'node-halopsa';\n\ntry {\n  await client.tickets.get(999);\n} catch (error) {\n  if (error instanceof HaloPsaNotFoundError) {\n    console.log('Ticket not found');\n  } else if (error instanceof HaloPsaAuthenticationError) {\n    console.log('Authentication failed');\n  } else if (error instanceof HaloPsaForbiddenError) {\n    console.log('Access denied');\n  } else if (error instanceof HaloPsaValidationError) {\n    console.log('Validation errors:', error.errors);\n  } else if (error instanceof HaloPsaRateLimitError) {\n    console.log('Rate limited, retry after:', error.retryAfter);\n  } else if (error instanceof HaloPsaServerError) {\n    console.log('Server error:', error.statusCode);\n  }\n}\n```\n\n## Rate Limiting\n\nThe library automatically handles HaloPSA's rate limit (500 requests per 3-minute rolling window):\n\n- Proactive throttling starts at 80% (400 requests)\n- Automatic retry with exponential backoff on HTTP 429\n- Respects `Retry-After` header when present\n\nYou can monitor the current rate limit status:\n\n```typescript\nconst status = client.getRateLimitStatus();\nconsole.log(`Remaining: ${status.remaining}, Rate: ${status.rate}`);\n```\n\n## TypeScript\n\nAll types are fully exported:\n\n```typescript\nimport type {\n  Ticket,\n  TicketCreateData,\n  TicketListParams,\n  Client,\n  Site,\n  Asset,\n  Contact,\n  // ... and many more\n} from 'node-halopsa';\n```\n\n## Requirements\n\n- Node.js >= 18.0.0\n\n## License\n\nApache-2.0\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n","readmeFilename":"README.md"}