skills/building-nango-functions/SKILL.md
Builds Nango Function implementation patterns for createAction() and createSync() without choosing a local CLI or remote API workflow. Use only when the user asks to create or update a Nango action or sync and it is unclear whether the work should happen in a checked-out project via CLI or through Nango remote APIs. Do not load when building-nango-functions-locally or building-nango-functions-remotely applies; those skills overlap with this content and add workflow-specific validation and deploy details.
npx skillsauth add nangohq/skills building-nango-functionsInstall 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.
Build Nango action and sync implementations without choosing the execution workflow.
This skill covers the function design contract: schemas, provider calls, action outputs, sync models, checkpoints, deletion handling, metadata, retries, and runtime constraints. It intentionally does not cover local CLI validation/deploy or remote API compile/dryrun/deploy.
If the task becomes clearly local/CLI-based, use building-nango-functions-locally instead. If it becomes clearly remote/API-based, use building-nango-functions-remotely instead.
createAction()createSync()If the task is a sync, read references/syncs.md before writing code and state one of these paths first:
updated_at, modified_since, changed-records endpoint, cursor, page token, offset/page, since_id, or webhook)Invalid sync implementations:
saveCheckpoint() without getCheckpoint()syncType: 'incremental' or nango.lastSyncDate in a new synctrackDeletesStart() / trackDeletesEnd() with a changed-only checkpoint (modified_after, updated_after, changed-records endpoint). Those requests omit unchanged rows, so trackDeletesEnd() will falsely delete them.trackDeletesStart() / trackDeletesEnd() in an incremental sync that already has explicit deleted-record eventsAction:
createAction()references/actions.md before writing codeSync:
createSync()references/syncs.md before writing codeAlways:
Action-specific:
{} for no-input actions)Sync-specific:
since_id, or composite)If any required external values are missing, ask a targeted question after checking the repo and provider docs. For syncs, choose a checkpoint plus deletion strategy whenever the provider supports one. If you cannot find a viable checkpoint strategy, state exactly why before writing a full refresh.
createAction() / createSync().zod, crypto/node:crypto, and url/node:url.fs, no axios, 2 MB output limit). Use a proxy script in {integration}/proxy/ with @nangohq/node instead — see references/actions.md.0; set retries deliberately. Treat 3 as the normal maximum; for sync provider calls, values above 3 are effectively forbidden unless docs prove they are safe and necessary. Avoid retries for non-idempotent writes unless the API supports idempotency.id.checkpoint schema, call nango.getCheckpoint() first, and nango.saveCheckpoint() after each page or batch.since, updated_after, cursor, page_token, offset, page, since_id, etc.). Saving one without using it is not incremental sync.syncType: 'incremental' or nango.lastSyncDate.nango.paginate(...) + nango.batchSave(...). Avoid manual while (true) loops when cursor, link, or offset pagination fits.batchDelete() when the provider returns deletions, tombstones, or delete webhooks.deleteRecordsFromPreviousExecutions() is deprecated. For full refresh, call trackDeletesStart() before fetch/save and trackDeletesEnd() only after a successful full fetch/save.trackDeletesStart() / trackDeletesEnd() with changed-only checkpoints (modified_after, updated_after, changed-records endpoints, etc.). They omit unchanged rows, so trackDeletesEnd() would delete them.trackDeletesEnd() only in the run that finishes the full window.team_id, workspace_id, guild_id), set autoStart: false. The sync cannot run until the caller has set the metadata, so starting it automatically would fail..describe() examples for IDs, timestamps, enums, and URLs.any; use inline mapping types.:id / {id} in the exposed endpoint); pass IDs in input or params.cursor plus a next-cursor field in the majority casing of that API (next_cursor, nextCursor, etc.).nango.zodValidateInput() only when you need custom validation or logging; otherwise rely on schemas plus the chosen validation workflow..optional()..nullable() only when null has meaning, usually clear-on-update; add .optional() when callers may omit the field too..optional() for omitted fields, .nullable() for explicit null, .nullish() only when the provider truly does both..optional() and normalize upstream null to omission unless null matters..optional() for non-required inputs and normalized outputs; widen only when the upstream contract justifies it..nullable() over z.union([z.null(), T]) or z.union([T, z.null()]).null only when the output schema allows it.z.object() strips unknown keys by default. For provider pass-through use z.object({}).passthrough(), z.record(z.unknown()), or z.unknown() with minimal refinements.user_id, userId), names (channel_name, channelName), emails (user_email, userEmail), URLs (callback_url, callbackUrl), and timestamps (created_at, createdAt).Mapping example (API expects a different parameter name):
const InputSchema = z.object({
userId: z.string()
});
const config: ProxyConfiguration = {
endpoint: 'users.info',
params: {
user: input.userId
},
retries: 3
};
If the API is snake_case, use user_id instead. The goal is API consistency.
references/actions.mdreferences/syncs.md--save, Vitest): https://nango.dev/docs/implementation-guides/platform/functions/testingIf web fetching returns incomplete docs (JS-rendered):
development
Copy-pasteable Nango quickstart prompt for agents. Guides brand-new Nango customers from signup through API key setup, integration setup, connection authorization, and action/sync next steps. Use for first Nango integration walkthroughs.
development
Migrates existing Nango TypeScript createSync implementations from nango.lastSyncDate, legacy incremental syncType, and non-resumable full refreshes to checkpoint-based syncs. Use when updating customer Nango sync code to define checkpoint schemas, call getCheckpoint/saveCheckpoint/clearCheckpoint after every batchSave (including inside paginate loops), test dryruns with --checkpoint, and fix deletion handling for checkpointed incremental or full syncs.
tools
Universal gateway for any third-party API or SaaS (Google Calendar, Gmail, Slack, Notion, Linear, HubSpot, etc.). TRIGGER on any request to read or modify data in an external product, even when no matching MCP tool is loaded.
development
Manually migrates legacy `nango.yaml` Nango projects to Zero YAML TypeScript by generating `models.ts`, `index.ts`, package/tsconfig scaffolding, and wrapping legacy scripts in `createSync()`, `createAction()`, or `createOnEvent()`. Use when a Nango repo still has `nango.yaml` and needs manual Zero YAML migration.