The official TypeScript SDK for the Cynco REST API.
Cynco is an AI-native business platform for accounting, invoicing, payments, and operations. This SDK provides full typed access to the Cynco API from any Node.js or TypeScript environment.
npm install @cynco/sdkpnpm add @cynco/sdkyarn add @cynco/sdkRequirements: Node.js 18 or later.
import Cynco from '@cynco/sdk';
const cynco = new Cynco('cak_your_api_key');
// List invoices
const page = await cynco.invoices.list({ status: 'paid', limit: 20 });
console.log(page.data); // Invoice[]
// Create a customer
const customer = await cynco.customers.create({
name: 'Acme Corp',
email: 'billing@acme.com',
type: 'company',
currency: 'MYR',
});
// Retrieve a single resource
const invoice = await cynco.invoices.retrieve('inv_abc123');const cynco = new Cynco('cak_your_api_key', {
baseUrl: 'https://app.cynco.io/api/v1', // Default
timeout: 30_000, // 30 seconds (default)
maxRetries: 3, // Automatic retries (default)
});| Option | Type | Default | Description |
|---|---|---|---|
baseUrl |
string |
https://app.cynco.io/api/v1 |
API base URL |
timeout |
number |
30000 |
Request timeout in milliseconds |
maxRetries |
number |
3 |
Max retries on transient failures |
fetch |
typeof fetch |
globalThis.fetch |
Custom fetch implementation |
All resources follow a consistent pattern:
cynco.{resource}.list(params?) // Paginated list (also async iterable)
cynco.{resource}.retrieve(id) // Get by ID
cynco.{resource}.create(data, opts) // Create (where applicable)
cynco.{resource}.update(id, data) // Update (where applicable)
cynco.{resource}.delete(id) // Delete (where applicable)// List with filters
const page = await cynco.invoices.list({
status: 'overdue',
customerId: 'cust_abc',
dateFrom: '2026-01-01',
limit: 50,
});
// Create
const invoice = await cynco.invoices.create({
customerId: 'cust_abc',
issueDate: '2026-03-22',
dueDate: '2026-04-22',
lineItems: [
{ description: 'Consulting', quantity: 10, unitPrice: 500 },
],
});
// Actions
await cynco.invoices.send('inv_123');
await cynco.invoices.markPaid('inv_123', { paidDate: '2026-03-25' });
await cynco.invoices.void('inv_123');
// PDF
const { url } = await cynco.invoices.getPdf('inv_123');const page = await cynco.customers.list({ search: 'acme', type: 'company' });
const customer = await cynco.customers.create({ name: 'Acme Corp' });
const updated = await cynco.customers.update('cust_123', { name: 'Acme Inc' });
await cynco.customers.delete('cust_123');const page = await cynco.vendors.list({ search: 'supplier' });
const vendor = await cynco.vendors.create({
name: 'Office Supplies Co',
email: 'accounts@supplier.com',
});const page = await cynco.bills.list({ status: 'received', vendorId: 'vnd_123' });
const bill = await cynco.bills.create({
vendorId: 'vnd_123',
issueDate: '2026-03-20',
dueDate: '2026-04-20',
lineItems: [{ description: 'Paper supplies', quantity: 100, unitPrice: 5 }],
});
await cynco.bills.markPaid('bill_123');
await cynco.bills.void('bill_123');const page = await cynco.items.list({ type: 'service' });
const item = await cynco.items.create({
name: 'Consulting Hour',
type: 'service',
unitPrice: 500,
});const page = await cynco.accounts.list({ type: 'revenue' });
const account = await cynco.accounts.create({
name: 'Sales Revenue',
code: '4000',
type: 'revenue',
});const page = await cynco.journalEntries.list({ status: 'posted' });
const entry = await cynco.journalEntries.create({
date: '2026-03-22',
description: 'Monthly depreciation',
lines: [
{ accountId: 'acc_dep_exp', debit: 1000, credit: 0 },
{ accountId: 'acc_accum_dep', debit: 0, credit: 1000 },
],
});
await cynco.journalEntries.post('je_123');
await cynco.journalEntries.void('je_123');const page = await cynco.bankAccounts.list();
const account = await cynco.bankAccounts.create({
name: 'Operating Account',
accountNumber: '1234567890',
bankName: 'Maybank',
currency: 'MYR',
});
// List transactions for a bank account
const txns = await cynco.bankAccounts.listTransactions('ba_123', {
status: 'cleared',
dateFrom: '2026-03-01',
});const bs = await cynco.reports.balanceSheet({
endDate: '2026-03-31',
currency: 'MYR',
});
console.log(bs.totalAssets, bs.totalLiabilities, bs.totalEquity);
const pnl = await cynco.reports.profitAndLoss({
startDate: '2026-01-01',
endDate: '2026-03-31',
});
console.log(pnl.netIncome);
const tb = await cynco.reports.trialBalance({ endDate: '2026-03-31' });
console.log(tb.totalDebits, tb.totalCredits);const page = await cynco.webhooks.list();
const webhook = await cynco.webhooks.create({
url: 'https://example.com/webhooks/cynco',
events: ['invoice.paid', 'customer.created'],
});
await cynco.webhooks.update('wh_123', { isActive: false });
const { secret } = await cynco.webhooks.rotateSecret('wh_123');const page = await cynco.invoices.list({ limit: 20, offset: 40 });
console.log(page.data); // Invoice[]
console.log(page.pagination.total); // Total count
console.log(page.pagination.hasMore); // More pages?
// Manual pagination
const nextPage = await page.nextPage(); // Page<Invoice> | nullIterate over all items across all pages automatically:
for await (const invoice of cynco.invoices.list({ limit: 50 })) {
console.log(invoice.id);
}const allCustomers: Customer[] = [];
for await (const customer of cynco.customers.list({ limit: 100 })) {
allCustomers.push(customer);
}All API errors are instances of CyncoError with specific subclasses:
import Cynco, {
CyncoError,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
} from '@cynco/sdk';
try {
await cynco.customers.create({ name: '' });
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.status); // 422
console.log(error.code); // "validation_error"
console.log(error.message); // "Validation failed"
console.log(error.details); // [{ field: "name", message: "is required" }]
console.log(error.requestId); // UUID for support
}
}| Error Class | Status | When |
|---|---|---|
AuthenticationError |
401 | Invalid or missing API key |
PermissionError |
403 | Insufficient permissions |
NotFoundError |
404 | Resource does not exist |
ConflictError |
409 | Conflicting request |
ValidationError |
422 | Invalid request body |
RateLimitError |
429 | Rate limit exceeded (after retries) |
InternalError |
5xx | Server error (after retries) |
TimeoutError |
- | Request timed out |
ConnectionError |
- | Network unreachable |
The SDK automatically retries on rate limits (429) and server errors (5xx) with exponential backoff and jitter.
- GET, HEAD, DELETE requests are always retried.
- POST, PATCH, PUT requests are only retried if an idempotency key is provided (except for 429, which is always safe to retry since the request was never processed).
- Retries respect the
Retry-Afterheader. - Maximum retry count is configurable via
maxRetries(default: 3).
For safe retries on mutations, pass an idempotency key:
const customer = await cynco.customers.create(
{ name: 'Acme Corp' },
{ idempotencyKey: 'create-acme-20260322' },
);If a request with the same idempotency key was already processed, the API returns the original response instead of creating a duplicate.
Verify incoming webhook signatures using the static Cynco.webhooks utility. No client instantiation needed.
import Cynco from '@cynco/sdk';
// In your webhook handler (e.g. Express)
app.post('/webhooks/cynco', (req, res) => {
const payload = req.body; // Raw string body
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const isValid = Cynco.webhooks.verify(
payload,
signature,
timestamp,
process.env.CYNCO_WEBHOOK_SECRET,
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
// Handle the event...
res.sendStatus(200);
});try {
Cynco.webhooks.verifyOrThrow(payload, signature, timestamp, secret);
} catch (error) {
// "Invalid webhook signature" or "Webhook timestamp is too old"
}By default, timestamps older than 5 minutes are rejected. You can customise this:
Cynco.webhooks.verify(payload, signature, timestamp, secret, {
tolerance: 600, // 10 minutes
});Generate test signatures for development:
const { signature, timestamp } = Cynco.webhooks.sign(
JSON.stringify({ event: 'invoice.paid', data: {} }),
'whsec_your_test_secret',
);Every resource, parameter, and response is fully typed. Import individual types as needed:
import type {
Invoice,
Customer,
InvoiceCreateInput,
CustomerListParams,
PaginatedResponse,
WebhookEvent,
} from '@cynco/sdk';The SDK ships as a dual ESM/CJS package:
const { default: Cynco } = require('cynco');
const cynco = new Cynco('cak_your_api_key');MIT - Cynco Sdn Bhd