The official Node.js, Bun, and Deno library for the Abby API. Abby is an all-in-one business management platform for freelancers and micro-enterprises.
- Installation
- Requirements
- Usage
- Validation
- TypeScript Support
- Versioning
- Development
- Support
- License
Install the package using your preferred package manager:
npm install @abby-inc/node
# or
yarn add @abby-inc/node
# or
pnpm add @abby-inc/node
# or
bun add @abby-inc/nodeFor Deno, you can import directly from npm:
import Abby from 'npm:@abby-inc/node';- Node.js 22.13.0 or higher (CI: 22, 24, 26), Bun 1.0.0 or higher, or Deno 2.0.0 or higher
- An Abby account with an API key
Get your API key from the Abby settings.
import Abby from '@abby-inc/node';
const abby = new Abby('your_api_key');The SDK provides several services to interact with different parts of the Abby API:
abby.company: Current company info and preferences.abby.invoice: Create and manage invoices.abby.estimate: Create and manage estimates (quotes).abby.contact: Manage your contacts (customers).abby.organization: Manage organizations.abby.billing: Shared billing utilities (PDFs, emails).abby.opportunity: CRM and opportunities management.abby.asset: Manage business assets.abby.advance: Manage advance payments.abby.customerPortal: Customer portal payments and billing themes.
import Abby from '@abby-inc/node';
const abby = new Abby('your_api_key');
async function main() {
// Get current company information
const { data: me } = await abby.company.getMe();
console.log(`Welcome back, ${me.user.firstname}!`);
// List all contacts
const { data: contacts } = await abby.contact.retrieveContacts({
query: { limit: 10 },
});
console.log(`Found ${contacts.docs.length} contacts.`);
}
main().catch(console.error);You can pass optional configuration settings when initializing the SDK:
import Abby from '@abby-inc/node';
const abby = new Abby('your_api_key', {
baseUrl: 'https://api.app-abby.com',
timeout: 30000,
headers: {
'X-Custom-Header': 'value',
},
});| Option | Default | Description |
|---|---|---|
baseUrl |
'https://api.app-abby.com' |
Base URL for the Abby API. |
timeout |
30000 |
Request timeout in milliseconds. |
headers |
undefined |
Additional headers to include in every request. |
fetch |
globalThis.fetch |
Custom fetch implementation for proxies, logging, or testing. See Custom Fetch & Proxy Support. |
The SDK allows you to provide a custom fetch implementation for advanced use cases like proxies, custom logging, or testing.
With undici (recommended for Node.js):
import Abby from '@abby-inc/node';
import { fetch as undiciFetch, ProxyAgent } from 'undici';
const proxyAgent = new ProxyAgent('http://proxy.example.com:8080');
const abby = new Abby('your_api_key', {
fetch: (url, init) =>
undiciFetch(url, {
...init,
dispatcher: proxyAgent,
}),
});With node-fetch and https-proxy-agent:
import Abby from '@abby-inc/node';
import fetch from 'node-fetch';
import { HttpsProxyAgent } from 'https-proxy-agent';
const proxyAgent = new HttpsProxyAgent('http://proxy.example.com:8080');
const abby = new Abby('your_api_key', {
fetch: (url, init) =>
fetch(url, {
...init,
agent: proxyAgent,
}) as Promise<Response>,
});const abby = new Abby('your_api_key', {
fetch: async (url, init) => {
console.log(`[Abby SDK] ${init?.method ?? 'GET'} ${url}`);
const start = Date.now();
const response = await globalThis.fetch(url, init);
console.log(`[Abby SDK] ${response.status} in ${Date.now() - start}ms`);
return response;
},
});import { vi } from 'vitest';
const mockFetch = vi.fn(
async () =>
new Response(JSON.stringify({ success: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
);
const abby = new Abby('your_api_key', {
fetch: mockFetch,
});The SDK throws descriptive errors when a request fails. You can catch these errors to handle different failure scenarios:
try {
const { data } = await abby.invoice.getInvoice({
path: { invoiceId: 'inv_invalid' },
});
} catch (error) {
if (error.status === 404) {
console.error('Invoice not found');
} else if (error.status === 401) {
console.error('Invalid API key');
} else {
console.error('An unexpected error occurred:', error.message);
}
}The SDK provides an event emitter pattern for global error handling and logging. This is useful for integrating with error tracking services like Sentry or for centralized logging.
Subscribe to all API errors across your application:
import Abby from '@abby-inc/node';
const abby = new Abby('your_api_key');
// Global error handler - fires on any 4xx/5xx response
abby.on('error', (error) => {
console.error(`API Error: ${error.status} ${error.statusText}`);
console.error(`URL: ${error.method} ${error.url}`);
console.error(`Message: ${error.message}`);
console.error(`Duration: ${error.duration}ms`);
// Send to error tracking service
Sentry.captureException(new Error(error.message), {
extra: {
status: error.status,
url: error.url,
requestId: error.requestId,
body: error.body,
},
});
});Subscribe to all API responses (both successful and failed):
// Log all API calls for debugging/monitoring
abby.on('response', (response) => {
console.log(`${response.method} ${response.url} - ${response.status} (${response.duration}ms)`);
});Use off() to remove a listener:
const errorHandler = (error) => console.error(error);
// Add listener
abby.on('error', errorHandler);
// Remove listener later
abby.off('error', errorHandler);| Event | Description | Payload Type |
|---|---|---|
error |
Fires on 4xx/5xx responses | AbbyErrorEvent |
response |
Fires on all responses | AbbyResponseEvent |
AbbyErrorEvent properties:
| Property | Type | Description |
|---|---|---|
status |
number |
HTTP status code (4xx or 5xx) |
statusText |
string |
HTTP status text |
url |
string |
Request URL |
method |
string |
HTTP method (GET, POST, etc.) |
duration |
number |
Request duration in milliseconds |
message |
string? |
Error message from response body, if available |
body |
unknown? |
Response body, if available |
requestId |
string? |
X-Request-Id header, if available |
AbbyResponseEvent properties:
| Property | Type | Description |
|---|---|---|
status |
number |
HTTP status code |
url |
string |
Request URL |
method |
string |
HTTP method (GET, POST, etc.) |
duration |
number |
Request duration in milliseconds |
ok |
boolean |
Whether the response was successful (2xx) |
Interceptors allow you to hook into the request/response lifecycle:
const client = abby.getClient();
// Request Interceptor: Add a custom header to every request
client.interceptors.request.use((request) => {
request.headers.set('X-Request-ID', crypto.randomUUID());
return request;
});
// Response Interceptor: Log every response status
client.interceptors.response.use((response) => {
console.log(`API Response: ${response.status}`);
return response;
});If you need to call an endpoint that isn't covered by the SDK, or prefer to specify request details directly, you can use the underlying HTTP client:
import Abby from '@abby-inc/node';
const abby = new Abby('your_api_key');
const client = abby.getClient();
// GET request
const { data } = await client.get({
url: '/v2/some/endpoint',
});
// POST request with body
const { data: result } = await client.post({
url: '/v2/some/endpoint',
body: {
field: 'value',
},
});
// Other methods available: put, patch, delete, head, optionsThe client automatically includes your API key and SDK headers in all requests.
The SDK includes built-in Zod validation for API requests, catching malformed payloads before they leave your process.
All SDK methods automatically validate request data: parameters, query strings, and request bodies are checked before sending.
Responses are not validated. Response schemas are generated and exported (see below), but the SDK does not run them — a spec/API mismatch would otherwise turn a successful call into a thrown error. Types on responses remain compile-time only; parse with the exported z*Dto schemas yourself if you need a runtime guarantee.
If request validation fails, a ZodError is thrown with detailed information about what went wrong:
import Abby from '@abby-inc/node';
import { ZodError } from 'zod';
const abby = new Abby('your_api_key');
try {
// This will fail validation if the request body is invalid
await abby.contact.createContact({
body: {
// Missing required fields will trigger a ZodError
},
});
} catch (error) {
if (error instanceof ZodError) {
console.error('Validation failed:', error.errors);
}
}All Zod schemas are exported and can be used for your own validation needs:
import Abby, { zCreateContactDto, zReadContactDto } from '@abby-inc/node';
// Validate user input before sending to the API
const userInput = {
firstname: 'John',
lastname: 'Doe',
email: 'john@example.com',
};
// Parse and validate (throws ZodError if invalid)
const validatedContact = zCreateContactDto.parse(userInput);
// Or use safeParse for non-throwing validation
const result = zCreateContactDto.safeParse(userInput);
if (result.success) {
console.log('Valid:', result.data);
} else {
console.error('Invalid:', result.error.errors);
}
// Infer TypeScript types from schemas
import { z } from 'zod';
type CreateContactInput = z.infer<typeof zCreateContactDto>;Available schema patterns:
z{DtoName}- Schemas for DTOs (e.g.,zCreateContactDto,zReadInvoiceDto)z{ControllerMethod}Data- Request data schemas (e.g.,zContactControllerCreateContactData)
The SDK is written in TypeScript and provides complete type definitions for all API resources and responses.
import Abby, { ReadMeDto } from '@abby-inc/node';
const abby = new Abby('your_api_key');
async function getCompanyData() {
const { data }: { data: ReadMeDto } = await abby.company.getMe();
return data;
}You can also infer types directly from Zod schemas:
import { z } from 'zod';
import { zReadContactDto } from '@abby-inc/node';
// Infer the type from the Zod schema
type Contact = z.infer<typeof zReadContactDto>;This SDK uses independent versioning from the Abby API:
- SDK version (
versioninpackage.json): Follows semver for SDK changes (bug fixes, new features, breaking changes) - API version (
apiVersioninpackage.json): Tracks which Abby API version was used to generate the SDK
{
"version": "1.2.0", // SDK version
"apiVersion": "1.5.0" // API spec version used to generate
}This allows the SDK to receive bug fixes and improvements independently from API changes.
Contributions are welcome! Please see our Contributing Guidelines for more information.
If you encounter any issues or have questions, please check the following resources:
MIT License - see LICENSE for details.