providers/claude/plugin/skills/apideck-node/SKILL.md
Apideck Unified API integration patterns for TypeScript and Node.js. Use when building integrations with accounting software (QuickBooks, Xero, NetSuite), CRMs (Salesforce, HubSpot, Pipedrive), HRIS platforms (Workday, BambooHR), file storage (Google Drive, Dropbox, Box), ATS systems (Greenhouse, Lever), e-commerce, or any of Apideck's 200+ connectors. Covers the @apideck/unify SDK, authentication, CRUD operations, pagination, filtering, webhooks, and Vault connection management.
npx skillsauth add apideck-libraries/api-skills apideck-nodeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The Apideck Unified API provides a single integration layer to connect with 200+ third-party services across accounting, CRM, HRIS, file storage, ATS, e-commerce, and more. The official TypeScript SDK (@apideck/unify) provides typed clients for all unified APIs.
Key capabilities:
npm add @apideck/unify
Requires Node.js 18+. The SDK is fully typed with TypeScript definitions.
@apideck/unify SDK. DO NOT make raw fetch/axios calls to the Apideck API.apiKey, appId, and consumerId when initializing the client. These are required for all API calls.APIDECK_API_KEY environment variable rather than hardcoding API keys.serviceId to specify which downstream connector to use (e.g., "salesforce", "quickbooks", "xero"). If a consumer has multiple connections for an API, serviceId is required.for await...of for iterating large result sets. DO NOT implement manual pagination.filter parameter to narrow results server-side. DO NOT fetch all records and filter client-side.fields parameter to request only the columns you need. This reduces response size and improves performance.import { Apideck } from "@apideck/unify";
const apideck = new Apideck({
apiKey: process.env["APIDECK_API_KEY"] ?? "",
appId: "your-app-id",
consumerId: "your-consumer-id",
});
// List CRM contacts
const { data } = await apideck.crm.contacts.list({
limit: 20,
filter: { email: "[email protected]" },
});
for (const contact of data) {
console.log(contact.name, contact.emails);
}
import { Apideck } from "@apideck/unify";
const apideck = new Apideck({
apiKey: process.env["APIDECK_API_KEY"] ?? "",
appId: "your-app-id",
consumerId: "your-consumer-id",
});
The consumerId identifies the end-user whose connections are being used. In multi-tenant apps, set this per-request or per-user session.
All resources follow the same pattern: apideck.{api}.{resource}.{operation}().
// LIST - retrieve multiple records
const { data } = await apideck.crm.contacts.list({
serviceId: "salesforce",
limit: 20,
filter: { email: "[email protected]" },
sort: { by: "updated_at", direction: "desc" },
fields: "id,name,email,phone_numbers",
});
// CREATE - create a new record
const { data: created } = await apideck.crm.contacts.create({
serviceId: "salesforce",
contact: {
first_name: "John",
last_name: "Doe",
emails: [{ email: "[email protected]", type: "primary" }],
phone_numbers: [{ number: "+1234567890", type: "mobile" }],
},
});
console.log(created.id); // "contact_abc123"
// GET - retrieve a single record
const { data: contact } = await apideck.crm.contacts.get({
id: "contact_abc123",
serviceId: "salesforce",
});
// UPDATE - modify an existing record
const { data: updated } = await apideck.crm.contacts.update({
id: "contact_abc123",
serviceId: "salesforce",
contact: { first_name: "Jane" },
});
// DELETE - remove a record
await apideck.crm.contacts.delete({
id: "contact_abc123",
serviceId: "salesforce",
});
Use async iteration to automatically handle cursor-based pagination:
const result = await apideck.accounting.invoices.list({
serviceId: "quickbooks",
limit: 50,
});
// Automatically fetches next pages
for await (const page of result) {
for (const invoice of page.data) {
console.log(invoice.number, invoice.total);
}
}
Or handle pagination manually:
let cursor: string | undefined;
do {
const { data, meta } = await apideck.accounting.invoices.list({
serviceId: "quickbooks",
limit: 50,
cursor,
});
for (const invoice of data) {
console.log(invoice.number);
}
cursor = meta?.cursors?.next ?? undefined;
} while (cursor);
import { Apideck } from "@apideck/unify";
import * as errors from "@apideck/unify/models/errors";
try {
const { data } = await apideck.crm.contacts.get({ id: "invalid" });
} catch (e) {
if (e instanceof errors.BadRequestResponse) {
console.error("Bad request:", e.message);
} else if (e instanceof errors.UnauthorizedResponse) {
console.error("Invalid API key or missing credentials");
} else if (e instanceof errors.NotFoundResponse) {
console.error("Record not found");
} else if (e instanceof errors.PaymentRequiredResponse) {
console.error("API limit reached or payment required");
} else if (e instanceof errors.UnprocessableResponse) {
console.error("Validation error:", e.message);
} else {
throw e;
}
}
Most list endpoints accept these parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| serviceId | string | Downstream connector ID (e.g., "quickbooks", "salesforce") |
| limit | number | Max results per page (1-200, default 20) |
| cursor | string | Pagination cursor from previous response |
| filter | object | Resource-specific filter criteria |
| sort | object | { by: string, direction: "asc" \| "desc" } |
| fields | string | Comma-separated field names to return |
| passThrough | object | Pass-through query parameters for the downstream API |
When the unified model doesn't cover a connector-specific field, use passThrough:
const { data } = await apideck.accounting.invoices.list({
serviceId: "quickbooks",
passThrough: {
search: "overdue",
},
});
For creating/updating, use pass_through in the request body to send connector-specific fields:
const { data } = await apideck.crm.contacts.create({
serviceId: "salesforce",
contact: {
first_name: "John",
last_name: "Doe",
pass_through: [
{
service_id: "salesforce",
operation_id: "contactsAdd",
extend_object: { custom_sf_field__c: "value" },
},
],
},
});
The SDK organizes APIs by namespace. See the reference files for detailed endpoints:
| Namespace | Reference | Resources |
|-----------|-----------|-----------|
| apideck.accounting.* | references/accounting-api.md | invoices, bills, payments, customers, suppliers, ledgerAccounts, journalEntries, taxRates, creditNotes, purchaseOrders, balanceSheet, profitAndLoss, and more |
| apideck.crm.* | references/crm-api.md | contacts, companies, leads, opportunities, activities, notes, pipelines, users |
| apideck.hris.* | references/hris-api.md | employees, companies, departments, payrolls, timeOffRequests |
| apideck.fileStorage.* | references/file-storage-api.md | files, folders, drives, driveGroups, sharedLinks, uploadSessions |
| apideck.ats.* | references/ats-api.md | applicants, applications, jobs |
| apideck.vault.* | references/vault-api.md | connections, connectionSettings, consumers, customMappings, logs, sessions |
| apideck.webhook.* | references/webhook-api.md | webhooks, eventLogs |
Use @apideck/vault-js to embed a pre-built modal that lets your users authorize connectors and manage integration settings. Session creation must happen server-side.
// 1. Server-side: create a session
const { data } = await apideck.vault.sessions.create({
session: {
consumer_metadata: { account_name: "Acme Corp", user_name: "John Doe", email: "[email protected]" },
redirect_uri: "https://myapp.com/integrations",
settings: { unified_apis: ["accounting", "crm"] },
theme: { vault_name: "My App", primary_color: "#4F46E5" },
},
});
// 2. Client-side: open the modal
import { ApideckVault } from "@apideck/vault-js";
ApideckVault.open({
token: sessionToken,
onConnectionChange: (connection) => console.log("Changed:", connection),
onClose: () => console.log("Closed"),
});
See references/vault-js.md for full configuration options, theming, React integration, and event callbacks.
development
Jira Teams via Apideck's Proxy API + managed Vault auth — Apideck handles auth and proxies HTTP calls to Jira Teams's native API. Use when the user wants to call Jira Teams (no unified API resource mapping). Routes through Apideck with serviceId "jira-teams".
development
Jira Service Desk via Apideck's Proxy API + managed Vault auth — Apideck handles auth and proxies HTTP calls to Jira Service Desk's native API. Use when the user wants to call Jira Service Desk (no unified API resource mapping). Routes through Apideck with serviceId "jira-service-desk".
development
Jira Data Center via Apideck's Proxy API + managed Vault auth — Apideck handles auth and proxies HTTP calls to Jira Data Center's native API. Use when the user wants to call Jira Data Center (no unified API resource mapping). Routes through Apideck with serviceId "jira-data-center".
development
JetBrains YouTrack via Apideck's Proxy API + managed Vault auth — Apideck handles auth and proxies HTTP calls to JetBrains YouTrack's native API. Use when the user wants to call JetBrains YouTrack (no unified API resource mapping). Routes through Apideck with serviceId "jetbrains-youtrack".