Node.js SDK
@linktoany/sdk is the official TypeScript SDK — typed requests, automatic retries, and cursor pagination handled for you.
@linktoany/sdk is the official TypeScript SDK — it wraps every call in these docs with
typed requests and responses, retries 429/502/503/504 automatically with exponential
backoff honouring Retry-After, and walks cursor pagination for you. Zero runtime
dependencies, built on native fetch, Node.js 18+, ships CJS and ESM with full .d.ts
types.
1. Install
npm install @linktoany/sdk2. Import & client setup
import { LinkToAny } from '@linktoany/sdk';
const client = new LinkToAny({
apiKey: process.env.LINKTOANY_API_KEY, // your ak_… key
environment: 'prod', // 'prod' (default) | 'dev' (staging)
organisationId: process.env.LINKTOANY_ORG_ID, // optional tenant context
timeoutMs: 30_000, // per-request timeout (default 30s)
maxRetries: 2, // retries on 429/502/503/504 (default 2)
});// Works in CJS too:
const { LinkToAny } = require('@linktoany/sdk');
const client = new LinkToAny({ apiKey: process.env.LINKTOANY_API_KEY });environment: 'prod' (default) targets https://api.linktoany.com; 'dev' targets
https://api.staging.linktoany.com. Pass baseUrl to override either — handy for pointing
at http://localhost:3000 in development. Use an admin key for writes, a public key
for read-only access.
3. Discover integrations
// Everything known about one integration — entities,
// read/write capability, filters — pulled together in one call
const docs = await client.docs.describeIntegration(systemId);
console.log(docs.integration?.name, docs.integration?.supportedAuthTypes);
for (const entity of docs.entities) {
console.log(
entity.entityType, // 'order'
entity.readable, entity.writable, // capabilities
entity.filters.map(f => f.paramName), // usable in records.list(...).filters
);
}
// A failing source never fails the call — check docs.warnings
// Focused helpers
await client.docs.listEntities(systemId); // entity capabilities only
await client.docs.getEntityFilters(systemId, 'order'); // filters for one entity
await client.docs.describeAllIntegrations(); // whole catalogue
// Raw catalogue access
await client.integrations.list({ status: 'active' });
await client.integrations.listByAuthType('oauth2');client.docs aggregates everything the Unified API knows about an integration in one round
trip; client.integrations exposes the raw catalogue underneath it.
4. Add (connect) an account
// OAuth integrations return an authUrl to redirect the user to;
// direct-auth integrations (apikey / basic / bearer) connect immediately
const result = await client.auth.connectAccount(systemId, 'shopify', {
merchantId: 'merchant-123',
shop: 'my-store.myshopify.com',
returnUrl: 'https://app.example.com/integrations/done',
});
if (result.data.authType === 'oauth') {
redirect(result.data.authUrl); // Unified API handles the callback + token storage
} else {
console.log('Connected:', result.data.accountId);
}
await client.auth.getStatus(accountId); // confirm after the redirect completes
await client.auth.getTokenStatus(accountId); // token health
await client.auth.refreshToken(accountId); // force a refresh (normally automatic)// Account management
await client.accounts.list();
await client.accounts.get(accountId);
await client.accounts.create({ systemId, merchantId: 'merchant-123' });
await client.accounts.update(accountId, { merchantId: 'merchant-124' });
await client.accounts.delete(accountId);
await client.accounts.listBySystem(systemId);
await client.accounts.listByMerchant(merchantId);5. Fetch records
// One page of normalized records
const page = await client.records.list(accountId, 'order', {
pageSize: 50,
filters: { updatedAfter: '2026-01-01T00:00:00Z' },
});
console.log(page.data, page.pagination?.hasMore);Typed:
interface UnifiedOrder {
externalId: string;
orderNumber?: string;
state?: 'OPEN' | 'COMPLETED' | 'CANCELED' | 'PENDING';
total?: number;
}
const page = await client.records.list<UnifiedOrder>(accountId, 'order');
page.data[0].total; // number | undefined6. Pagination
// Page at a time — you hold the cursor
const first = await client.records.list(accountId, 'product', { pageSize: 100 });
const next = await client.records.list(accountId, 'product', {
pageSize: 100,
cursor: first.pagination?.cursor ?? undefined,
});
// Or let the SDK walk every page for you
for await (const product of client.records.iterate(accountId, 'product', { pageSize: 100 })) {
await save(product);
}records.iterate(...) walks every page without you managing the cursor; records.list(...)
hands the cursor back for you to hold yourself.
7. Create records
// Validated against the entity's unified schema,
// then transformed into the platform's native shape
const result = await client.records.create(accountId, 'product', {
externalId: 'prd_tee_black_m',
name: 'Organic Tee — Black / M',
sku: 'TEE-BLK-M',
price: 32.0,
});
console.log(result.success, result.data);8. Entity contracts & AI generation
// A contract = an entity's unified Zod schema + mappings onto
// each integration's read/write operations
const contracts = await client.entities.list({ onlyEnabled: true });
await client.entities.upsert('product', {
unifiedZodSchema: '…',
readMappings: [{ key: 'shopify', systemId, sourceEntityType: 'product' }],
});
// AI contract generation — group integration operations into unified
// entities automatically, then poll until the task completes
const { data: task } = await client.entities.generate({
systemIds: [shopifyId, squareId],
syncRequestConfigIds: [readOp1, readOp2],
});
const finished = await client.entities.waitForGeneration(task._id, {
intervalMs: 5_000,
timeoutMs: 600_000,
});9. Observability
// Audit trail of every Unified API request
const failed = await client.requests.list({ accountId, success: false, limit: 50 });
const detail = await client.requests.get(failed.data[0]._id);
// Rate-limit configuration for your organisation
await client.rateLimits.upsert({ organisationId, requestsPerMinute: 120 });10. Error handling
import {
LinkToAnyError,
AuthenticationError,
PermissionError,
NotFoundError,
RateLimitError,
ValidationError,
} from '@linktoany/sdk';
// Every error carries status, code, body, and requestId. Retryable
// failures (429, 5xx, network) retry automatically with exponential
// backoff honoring Retry-After before anything throws.
try {
await client.records.list(accountId, 'order');
} catch (err) {
if (err instanceof AuthenticationError) {
// 401 — missing, malformed, or revoked API key
} else if (err instanceof PermissionError) {
// 403 — key is valid but not scoped for this account or action
} else if (err instanceof NotFoundError) {
// 404 — unknown accountId, or entity this platform doesn't expose
} else if (err instanceof ValidationError) {
// 400 / 422 — payload failed the unified schema; details in err.body
} else if (err instanceof RateLimitError) {
await sleep((err.retryAfterSeconds ?? 1) * 1000);
} else if (err instanceof LinkToAnyError) {
log(err.status, err.message, err.requestId); // ServerError (5xx) and others
} else {
throw err; // TimeoutError / ConnectionError / not an API error — don't swallow it
}
}Each status and its error class is tabulated under Errors. Every error
carries status, code, body, and requestId — include requestId when escalating to
support.
11. Per-call options & cancellation
const client = new LinkToAny({
apiKey: process.env.LINKTOANY_API_KEY,
timeoutMs: 60_000, // default 30 000
maxRetries: 3, // default 2
});
// Per-call overrides, plus external cancellation
const controller = new AbortController();
const page = await client.records.list(accountId, 'order', {}, {
timeoutMs: 10_000,
maxRetries: 0,
signal: controller.signal,
});
// Elsewhere: controller.abort();Resource map
| Resource | What it covers |
|---|---|
client.records | Read and write unified records — the calls documented throughout these docs. |
client.auth | Connect a merchant account to an integration (OAuth and direct-auth flows). |
client.accounts | Manage connected accounts — the merchant links every record call runs through. |
client.docs | Aggregated per-integration documentation: entities, filters, read/write support. |
client.integrations | Raw catalogue of integrations, auth types, and their read/write operations. |
client.entities | Unified entity contracts — list, upsert, and AI contract generation. |
client.templates | Pre-built vertical templates (e.g. retail) that bootstrap a Unified API. |
client.instances | Your tenant's Unified API instance — the entities and systems you've wired up. |
client.requests | Audit trail of every Unified API request, filterable by account or outcome. |
client.rateLimits | Rate-limit configuration for your organisation. |
Runnable walkthrough
A complete, sectioned walkthrough of every workflow ships with the package — run npx tsx examples/usage-guide.ts in mock mode with zero credentials, or set LINKTOANY_API_KEY
for live mode. Every public method also carries TSDoc that your editor surfaces inline.