Build Nango Functions
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.
Implementation Scope
- Build or modify a Nango function implementation
- Build an action in Nango with
- Build a sync in Nango with
- Use the active workflow skill for compile, dryrun, test, and deploy mechanics
Sync Strategy Gate (required before writing code)
If the task is a sync, read
before writing code and state one of these paths first:
- Checkpoint plan:
- change source (, , changed-records endpoint, cursor, page token, offset/page, , or webhook)
- checkpoint schema
- how the checkpoint changes the provider request or resume state
- whether the request still walks the full dataset or returns changed rows only
- delete strategy
- Full refresh blocker:
- exact provider limitation from the docs or sample payloads
- why checkpoints cannot work here
Invalid sync implementations:
- full refresh because it is simpler
- without
- reading or saving a checkpoint without using it in request params or pagination state
- using or in a new sync
- a full refresh with no schema, or one that is never saved after each page — the run restarts from page 1 whenever it exceeds the execution window
- calling before , or without a preceding at all
- using / with a changed-only checkpoint (, , changed-records endpoint). Those requests omit unchanged rows, so will falsely delete them.
- using / in an incremental sync that already has explicit deleted-record events
Choose the Path
Action:
- One-time request, user-triggered, built with
- Read before writing code
Sync:
- Scheduled or webhook-driven cache updates built with
- Complete the Sync Strategy Gate first
- Read before writing code
Required Inputs (Ask User if Missing)
Always:
- Integration ID (provider name)
- Script/function name (kebab-case)
- API reference URL or sample response
- Connection ID if the active workflow will validate or dryrun the function
Action-specific:
- Use case summary
- Input parameters
- Output fields
- Metadata JSON if required
- Test input JSON if the active workflow will validate or dryrun the action (use for no-input actions)
Sync-specific:
- Model name (singular, PascalCase)
- Frequency (every hour, every 5 minutes, etc.)
- Checkpoint schema (timestamp, cursor, page token, offset/page, , or composite)
- How the checkpoint changes the provider request or resume state
- Delete strategy (deleted-record endpoint/webhook, or why full refresh is required)
- If proposing a full refresh, the exact provider limitation that blocks checkpoints from the docs/sample response
- Metadata JSON if required (team_id, workspace_id)
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.
Non-Negotiable Rules
Shared platform constraints
- Nango functions use / .
- You cannot add arbitrary packages. Use relative imports only when the chosen workflow supports them; built-ins include , /, and /.
- Use the Nango HTTP API for connection lookup, credentials, and proxy calls outside function code. Do not invent CLI token or connection commands.
- Add an API doc link comment above each provider call.
- Action outputs cannot exceed 2MB.
- File uploads and downloads cannot be implemented as actions (sandboxed runtime: no , no , 2 MB output limit). Use a proxy script in with instead — see .
- HTTP retries default to ; set deliberately. Treat as the normal maximum; for sync provider calls, values above are effectively forbidden unless docs prove they are safe and necessary. Avoid retries for non-idempotent writes unless the API supports idempotency.
- Do not set deprecated function definition routing fields: action and sync . Trigger actions by action name through the SDK/API, and consume sync records through the records API.
Sync rules
- Sync records need a stable string .
- New syncs should define a schema, call first, and after each page or batch.
- A checkpoint is valid only if it changes the request or resume state (, , , , , , , etc.). Saving one without using it is not incremental sync.
- New syncs must not use or .
- Default to + . Avoid manual loops when , , or pagination fits.
- Prefer when the provider returns deletions, tombstones, or delete webhooks.
- Use full refresh only if the provider cannot return changes, deletions, or resume state, or if the dataset is tiny.
- For full refresh, cite the exact provider limitation from docs or payloads. "It is easier" is not enough.
- Full refresh syncs still need a schema (page/cursor/offset) covering pagination progress, not just incremental syncs. Nango syncs run inside a time-limited execution window; a full refresh with no checkpoint restarts from page 1 on every run that exceeds the window, wasting compute re-fetching the same early pages and never reaching the rest.
deleteRecordsFromPreviousExecutions()
is deprecated. For full refresh, call on every execution (safe/idempotent — it will not overwrite the start of an already-open window), then after each page, after the last page, and only after that .
- Never combine / with changed-only checkpoints (, , changed-records endpoints, etc.). They omit unchanged rows, so would delete them.
- Checkpointed full refreshes are still full refreshes. Call only in the run that finishes and clears the checkpoint.
- If a sync requires metadata (e.g. , , ), set . The sync cannot run until the caller has set the metadata, so starting it automatically would fail.
Conventions
- Match field casing to the external API. Passthrough fields keep provider casing; non-passthrough fields should use the majority casing of that API.
- Prefer explicit field names.
- Add examples for IDs, timestamps, enums, and URLs.
- Avoid ; use inline mapping types.
- List actions should expose plus a next-cursor field in the majority casing of that API (, , etc.).
- Use only when you need custom validation or logging; otherwise rely on schemas plus the chosen validation workflow.
Schema Semantics
- Default non-required inputs to .
- Use only when has meaning, usually clear-on-update; add when callers may omit the field too.
- Raw provider schemas should match the provider: for omitted fields, for explicit , only when the provider truly does both.
- Final action outputs and normalized sync models should prefer and normalize upstream to omission unless matters.
- Default generated schemas to for non-required inputs and normalized outputs; widen only when the upstream contract justifies it.
- Prefer over or .
- Return only when the output schema allows it.
- strips unknown keys by default. For provider pass-through use
z.object({}).passthrough()
, , or with minimal refinements.
Field Naming and Casing Rules
- Use explicit suffixes in the API's majority casing: IDs (, ), names (, ), emails (, ), URLs (, ), and timestamps (, ).
Mapping example (API expects a different parameter name):
typescript
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
instead. The goal is API consistency.
References
- Action patterns, CRUD examples, metadata usage, and ActionError examples:
- Sync patterns, concrete checkpoint examples, delete strategies, and full refresh fallback:
Useful Nango docs (quick links)
When API Docs Do Not Render
If web fetching returns incomplete docs (JS-rendered):
- Ask the user for a sample response
- Use existing Nango actions or syncs in the workspace as a pattern when they exist
- Use the skill-specific validation or dryrun workflow until it passes