Flow Unified API

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/sdk

2. Import & client setup

ESM · typescript
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)
});
CJS · javascript
// 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 | undefined

6. 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

ResourceWhat it covers
client.recordsRead and write unified records — the calls documented throughout these docs.
client.authConnect a merchant account to an integration (OAuth and direct-auth flows).
client.accountsManage connected accounts — the merchant links every record call runs through.
client.docsAggregated per-integration documentation: entities, filters, read/write support.
client.integrationsRaw catalogue of integrations, auth types, and their read/write operations.
client.entitiesUnified entity contracts — list, upsert, and AI contract generation.
client.templatesPre-built vertical templates (e.g. retail) that bootstrap a Unified API.
client.instancesYour tenant's Unified API instance — the entities and systems you've wired up.
client.requestsAudit trail of every Unified API request, filterable by account or outcome.
client.rateLimitsRate-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.

On this page