# Nango Docs > Curated full-text context for Nango core docs. Provider-specific API pages are excluded to keep this file focused; use the API catalog section at the end to discover supported provider slugs. ## Introduction Source: https://nango.dev/docs/getting-started/intro-to-nango.md Description: Build product integrations with AI, using code you control and infrastructure built for scale. ## What is Nango? Nango is the integration layer for AI-built, code-owned product integrations. Your users connect external APIs through Nango. You generate and customize TypeScript functions with your favorite coding agent. Your app, backend jobs, or agents then consume those functions through Nango's API, SDKs, or MCP server. Nango supports [800+ APIs](/integrations/overview) and handles auth, credentials, execution, retries, rate limits, observability, environments, and tenant isolation so you can ship integrations without rebuilding the same infrastructure for every provider. ## Why it matters Integration work used to mean choosing between slow custom builds and rigid pre-built connectors. With Nango, you can describe the integration you need, generate working code in seconds, extend coverage as customers ask for new APIs, and customize behavior per customer while keeping the implementation in code you can review, test, and version control. ## How it works Embed Nango Auth in your product so users can connect accounts from external APIs. Nango handles OAuth, API keys, token refresh, scopes, permissions, and credential storage. Build the integration logic as Nango Functions: TypeScript functions that call provider APIs, transform data, sync records, process webhooks, or perform actions. Generate them with AI, then edit them like normal application code. Call functions from your backend, trigger them on schedules or webhooks, or expose selected action functions as tools for AI agents through schemas and MCP. Nango's two core primitives are **Auth** for connecting user accounts and **Functions** for running integration logic on production infrastructure. ## What you can build Explore the main product integration use cases: Give agents scoped, observable access to external APIs through Nango action functions. Keep external API data fresh for your product, RAG pipeline, or change-detection workflow. Use Nango functions to read, write, and automate external APIs on demand. Receive provider webhooks through Nango, then forward or process them with functions. Normalize several external APIs behind your own stable product interface. Adapt integration behavior for each customer without forking code. ## What Nango handles OAuth flows, API keys, token refresh, scopes, provider quirks, and per-user access to external APIs. Actions, syncs, webhooks, schedules, retries, rate-limit backoff, checkpoints, and resumable execution. Tool schemas, MCP support, and scoped action functions your agents can call without handling provider credentials. Logs, metrics, alerts, isolated environments, CI/CD workflows, tenant isolation, open-source deployment, and compliance controls. ## Get started Connect your first API and make a request in minutes. ## Quickstart Source: https://nango.dev/docs/getting-started/quickstart.md Description: Authorize an API and run your first Nango function ## AI-assisted quickstart Copy this prompt into Claude, Cursor, or another coding agent to get guided through signup, API key setup, connection authorization, and your first function call.
For skills, the docs MCP server, and other agent setup options, see [Coding agent setup](/getting-started/coding-agent-setup). ## Manual quickstart The steps below show the flow end to end. This quickstart uses the GitHub API as an example, but you can choose any of the [800+ APIs](/integrations/overview). [Sign up](https://app.nango.dev/signup) for free. This quickstart uses GitHub (User OAuth), which is pre-created with integration ID `github-getting-started`. If you want to test another API, go to the Integrations tab > Set up new integration > pick an API. Go to the Connections tab > Add Test Connection > pick your integration, and complete the auth flow. For the GitHub example, pick `github-getting-started`. Copy the connection ID and your [Nango API key](/reference/backend/http-api/api-keys) from the Environment settings tab > API Keys. For the GitHub example, the integration ID is `github-getting-started`. You will use these values in the next steps. The fastest way to enable a function is to use a template. Go to the Integrations tab > open the `github-getting-started` integration > Functions sub-tab. In the functions table, turn on the toggle in the Enable column for `get-repository`. This is a Nango Function: it runs provider-specific code with the connected account's credentials, without exposing those credentials to your app or agent. You can also [build a custom function](/guides/functions/functions-guide#build-locally-with-the-cli) in your codebase, or use the [Functions API](/guides/functions/functions-guide#build-with-the-functions-api) to let an agent turn text-defined behavior into deployed function code. Trigger the function from your backend: Install the SDK with `npm i @nangohq/node`, then run: ```typescript import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env.NANGO_API_KEY! }); const result = await nango.triggerAction( 'github-getting-started', '', 'get-repository', { owner: 'NangoHQ', repo: 'nango' } ); console.log(result); ``` ```bash curl --request POST \ --url https://api.nango.dev/action/trigger \ --header 'Authorization: Bearer ' \ --header 'Connection-Id: ' \ --header 'Provider-Config-Key: github-getting-started' \ --header 'Content-Type: application/json' \ --data '{ "action_name": "get-repository", "input": { "owner": "NangoHQ", "repo": "nango" } }' ``` You should receive GitHub repository details such as the repository ID, full name, visibility, default branch, and owner. Open the Logs tab to inspect the function execution, provider request, response, and any errors. 🎉 You connected an API and ran your first integration function. Let users connect external APIs from your product. Generate, test, customize, and deploy integration code. Keep external API data fresh for your product or RAG pipeline. Use action functions through tool calling and MCP. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Coding agent setup Source: https://nango.dev/docs/getting-started/coding-agent-setup.md Description: Give coding agents the Nango docs, core skill, and MCP tools. Coding agents can speed up Nango integration work in two places: wiring Nango into your product and building [Nango Functions](/guides/functions/functions-guide). Getting started with Nango and want to use a coding agent? Check out the [AI-assisted quickstart](/getting-started/quickstart). This page is about generating integrations with AI. To expose Nango integrations to your product's agents, see [Tool calling & MCP](/guides/functions/tool-calling). ## Docs MCP server Add Nango's documentation MCP server to your coding agent to allow it to research docs & examples: ```text https://nango.dev/docs/mcp ``` It is public and does not require authentication. ## Skills Nango's public skills live in [NangoHQ/skills](https://github.com/NangoHQ/skills). Install the core skill to implement Nango integration functions: ```bash npx skills add NangoHQ/skills -s building-nango-functions ``` See the [functions guide](/guides/functions/functions-guide) for more details. ## Just-in-time integrations Nango integrations are defined as code. Usually, you or your coding agent write functions in a Git repo, review them like application code, and deploy them to Nango. Nango also exposes a Functions API for creating integrations just in time, without asking the user to interact with a codebase. A user can describe the integration behavior in text, and an agent can turn that into function code, compile it, dry run it against a connection, and deploy it directly to Nango. This is useful for faster onboarding, prototypes, and dynamic product experiences where integration behavior depends on what the user needs at that moment. It blurs the line between creating and consuming integrations: at consumption time, an agent can define the behavior, validate it, and make it available through Nango. See [build with the Functions API](/guides/functions/functions-guide#build-with-the-functions-api) for the workflow and API references. ## Other agent tools These resources help agents discover Nango docs, provider slugs, API shapes, and runtime tool-calling paths. | Tool | URL | Use it for | | --- | --- | --- | | Docs MCP server | `https://nango.dev/docs/mcp` | Live docs context inside coding agents. | | `llms.txt` | `https://nango.dev/docs/llms.txt` | Compact index of core Nango docs for LLM context windows. | | `llms-full.txt` | `https://nango.dev/docs/llms-full.txt` | Full generated docs text for deeper offline context. | | `api-catalog.txt` | `https://nango.dev/docs/api-catalog.txt` | Provider and integration slug discovery. | | OpenAPI spec | `https://nango.dev/docs/spec.yaml` | Exact HTTP API schemas for code generation or typed clients. | ## Tool calling for AI agents Source: https://nango.dev/docs/getting-started/use-cases/tool-calling.md Description: Give agents scoped, observable access to external APIs through Nango action functions. AI agents become useful when they can act in the systems your users already use: HubSpot, Gmail, Slack, Linear, Notion, Salesforce, and the rest. The hard part is not just calling an API. It is OAuth, token refresh, per-user permissions, rate limits, logging, and keeping credentials out of the model loop. Nango handles that plumbing. Users authorize integrations through Nango, you expose selected action functions as tools, and agents call those action functions through the Nango API or the built-in MCP server. ## When this fits Use Nango for AI tool calling when: - Your agent needs to fetch or change data in external APIs. - Tool calls must run with each user's own credentials. - You want logs, retries, rate-limit handling, and a deployable runtime. - You need template action functions now, with room for custom action functions later. - You want one MCP server URL for Nango-hosted tools. For RAG-style workflows where data should be replicated before the agent needs it, use [Sync external API data](/getting-started/use-cases/syncs). ## How Nango fits in 1. Your user authorizes an integration with Nango. 2. You enable template action functions or deploy custom action functions for allowed operations. 3. Your agent lists or receives those action functions as tools. 4. The agent asks Nango to execute an action function for the right connection. 5. Nango runs the external API call with the user's credentials and logs the execution. This keeps provider credentials in Nango and gives you a stable tool surface across frameworks. ## Technical setup Nango supports both direct action function execution and MCP-based tool discovery. Use the technical [Tool calling & MCP](/guides/functions/tool-calling) guide for headers, transports, tool discovery, action execution, and client setup. ## What to read next - [Tool calling & MCP](/guides/functions/tool-calling) - connect Nango action functions to agents, LLM SDKs, and MCP clients. - [Action functions](/guides/functions/action-functions) - build the action functions agents can call. - [Get integration functions config](/reference/backend/http-api/scripts/config) - list enabled action functions programmatically. - [Functions guide](/guides/functions/functions-guide) - create, test, deploy, and trigger functions. - [Auth guide](/guides/auth/auth-guide) - let users authorize external APIs. ## Sync external API data Source: https://nango.dev/docs/getting-started/use-cases/syncs.md Description: Keep external API data fresh for your product, RAG pipeline, or change-detection workflow. Many products need a local copy of customer data from CRMs, file stores, ticketing tools, billing systems, calendars, or communication apps. Doing this reliably means handling OAuth, polling, pagination, rate limits, retries, deletions, and missed webhooks for every provider. Nango gives you a function runtime for this pattern. A sync function fetches data from the external API, writes records into Nango's cache, and notifies your app when records change. Your app then reads the changed records by cursor. ## When this fits Use Nango to sync external API data when you need to: - Keep a local database or search index updated from customer tools. - Feed RAG pipelines with fresh external data. - Detect changes from APIs that do not offer reliable webhooks. - Combine polling with provider webhooks for near real-time updates. - Sync large datasets without restarting from scratch after failures. For one-off reads or writes, use [Run external API operations](/getting-started/use-cases/actions). ## How Nango fits in 1. Your user authorizes an integration with Nango. 2. You enable a template sync function or deploy your own. 3. Nango runs the sync function on its schedule, or when a provider webhook arrives. 4. The function saves records into Nango's records cache. 5. Nango sends your app a webhook when records change. 6. Your app reads changed records with a cursor and stores them in your system. The same foundation works for classic data sync, indexing, RAG ingestion, and trigger-style workflows. ## What to read next - [Sync functions](/guides/functions/syncs/sync-functions) - build, run, and consume sync functions end to end. - [Records cache](/guides/functions/syncs/records-cache) - understand records, change metadata, and cursors. - [Checkpoints](/guides/functions/syncs/checkpoints) - make long-running functions resumable and incremental. - [Deletion detection](/guides/functions/syncs/deletion-detection) - surface deletes from external APIs. - [Real-time syncs](/guides/functions/syncs/realtime-syncs) - combine provider webhooks with polling. - [Sync partitioning](/guides/functions/syncs/sync-partitioning) - fan out one connection into multiple sync variants. You can also pair sync functions with Action functions for two-way sync: sync reads from the external API, then call a function on demand to write changes back. ## Run external API operations Source: https://nango.dev/docs/getting-started/use-cases/actions.md Description: Use Nango functions to read, write, and automate external APIs on demand. Some integration work needs to happen immediately: create a CRM contact, send a Slack message, fetch the latest account settings, or run a multi-step workflow after a user clicks a button. Nango handles these on-demand operations with Action functions. You write the API logic once, Nango runs it with the right connection credentials, and your app calls the function from any backend or agent framework. ## When this fits Use Action functions when you need to: - Run reads or writes against an external API on demand. - Hide multi-step provider workflows behind one stable interface. - Normalize the same operation across several APIs. - Execute bulk or rate-limited work in the background. - Expose selected operations as tools for AI agents. For continuously replicated data, use [Sync external API data](/getting-started/use-cases/syncs) instead. ## How Nango fits in 1. Your user authorizes an integration with Nango. 2. You enable a template function or deploy your own. 3. Your app calls the function with an input payload. 4. Nango executes the provider API calls with the user's credentials. 5. Your app receives the result, or polls/listens for completion if the function runs asynchronously. These functions are the technical foundation behind on-demand API operations, unified APIs, and AI tool calls. ## What to read next - [Functions guide](/guides/functions/functions-guide) - create, test, deploy, and trigger functions end to end. - [Action functions](/guides/functions/action-functions) - synchronous calls, async execution, retries, and workflows. - [Tool calling for AI agents](/getting-started/use-cases/tool-calling) - use functions as AI tools. - [Build a unified API](/getting-started/use-cases/unified-apis) - use functions behind a normalized product API. - [Functions SDK reference](/reference/functions/functions-sdk) - runtime methods available inside function code. You can enable template functions from the [template catalog](https://www.nango.dev/templates) and skip writing code until you need custom behavior. ## Process external webhooks Source: https://nango.dev/docs/getting-started/use-cases/webhooks-from-external-apis.md Description: Receive provider webhooks through Nango, then forward or process them with functions. Many APIs can notify you when something changes, but each provider has its own webhook registration flow, payload shape, retries, and connection mapping. Nango gives external APIs one integration-specific webhook URL and attributes incoming events to the right connection. You can forward those webhooks to your app, process them in Nango functions, or combine them with sync functions for reliable near real-time data. ## When this fits Use Nango for external webhooks when: - A provider can notify you about changes faster than polling alone. - You need to map incoming provider webhooks to Nango connections. - Webhooks should trigger follow-up API requests with the user's credentials. - You want webhook events and related API calls visible in Nango logs. - You need real-time sync behavior without relying only on provider webhooks. For outgoing webhooks from Nango to your app, see [Receive webhooks from Nango](/guides/platform/webhooks-from-nango). ## How Nango fits in 1. You register the Nango webhook URL in the provider's developer portal. 2. The provider sends events to Nango. 3. Nango attributes each event to the right integration and connection when possible. 4. Nango forwards the event to your app or routes it into a function. 5. If the event updates synced data, the function writes records to Nango's cache. ## Common patterns - Forward the provider webhook payload to your app for custom handling. - Use a webhook function to fetch the changed object and update the records cache. - Keep a periodic sync function as reconciliation in case provider webhooks are missed. - Register per-connection provider webhooks in an event function after authorization. ## What to read next - [Webhook functions](/guides/functions/webhook-functions) - set up provider webhooks and process them in functions. - [Webhook forwarding](/guides/platform/webhook-forwarding) - forward external API webhooks through Nango to your app. - [Real-time syncs](/guides/functions/syncs/realtime-syncs) - combine provider webhooks with sync functions. - [Event functions](/guides/functions/event-functions) - register provider webhooks after connection creation. - [Receive webhooks from Nango](/guides/platform/webhooks-from-nango) - configure your app's webhook URL and verify signatures. - [Sync functions](/guides/functions/syncs/sync-functions) - write changed records into Nango's cache. ## Build a unified API Source: https://nango.dev/docs/getting-started/use-cases/unified-apis.md Description: Normalize several external APIs behind your own stable product interface. API unification means turning several provider-specific APIs into one interface your product can use consistently. For example, your app might expose one `create-contact` operation even though Salesforce, HubSpot, Attio, and Pipedrive each model contacts differently. Nango helps you build unified APIs with functions you control. You define the model that fits your product, then implement provider-specific functions that map to and from that model. ## When this fits Use Nango for API unification when: - Your customers use different tools for the same business object. - Your product wants one internal model for reads and writes. - You need provider-specific logic without leaking it into your app. - A pre-built unified API is too rigid for your schema, customers, or edge cases. Unification does not need to cover every provider feature. Often the useful path is a stable common model with explicit provider-specific extensions where needed. ## Where unification is hard Some APIs expose concepts that do not map neatly to other providers. Notion's block model, customizable ATS hiring stages, or provider-specific accounting workflows may need special handling. Design the unified model around your product's needs, not around an abstract lowest common denominator. Expect optional fields, explicit fallbacks, and customer-specific configuration for custom fields or statuses. ## How Nango fits in 1. You define the model your product wants to use. 2. Each provider gets functions that translate between the provider API and that model. 3. Sync functions read external data into your model. 4. Action functions write changes back through provider-specific logic. 5. Your app calls the stable Nango function interface instead of branching across providers. Use [data validation](/guides/functions/data-validation) to keep provider mappings honest and catch drift close to the external API. ## What to read next - [Unified APIs with functions](/guides/functions/unified-apis) - technical implementation guide for building unified models and functions. - [Action functions](/guides/functions/action-functions) - implement unified write operations and on-demand reads. - [Sync functions](/guides/functions/syncs/sync-functions) - read provider data into your unified model. - [Data validation](/guides/functions/data-validation) - validate models and generate schemas. - [Storage](/guides/functions/storage#customer-configuration-pattern) - handle custom fields and per-customer mapping. - [Functions SDK reference](/reference/functions/functions-sdk) - runtime methods available inside provider-specific functions. ## Customize per customer Source: https://nango.dev/docs/getting-started/use-cases/customer-configuration.md Description: Adapt integration behavior for each customer without forking code. Many integrations need customer-specific behavior: custom field mappings, record filters, account IDs, feature flags, or provider-specific options. Large customers often have custom schemas in CRMs, ERPs, ticketing tools, and productivity apps, so one fixed integration path is rarely enough. Nango stores this configuration as metadata on each connection. Your functions read the metadata at runtime and adjust API requests, validation, transformations, or sync function schedules for that customer. ## When this fits Use per-customer configuration when: - Customers need to map external custom fields to your product model. - Different customers should sync different objects, folders, projects, or account scopes. - A function needs provider-specific account context after authorization. - You want one integration implementation with customer-specific behavior. Avoid adding configuration before you need it. Start with a default path, then add typed metadata when repeated customer needs appear. ## How Nango fits in 1. Your app collects the customer's choices during onboarding or settings. 2. Your app stores those choices on the Nango connection as metadata. 3. Nango functions read metadata while running for that connection. 4. The same function code adapts requests and transformations for each customer. Metadata is available to Action functions, sync functions, and event functions. ## Common patterns - Fetch available custom fields with an action function, then store the customer's mapping. - Store provider account IDs or workspace IDs discovered after authorization. - Use metadata to filter which records a sync function should fetch. - Start a sync function schedule only after required customer configuration exists. ## What to read next - [Storage](/guides/functions/storage) - set, update, and read connection metadata from your app and functions. - [Sync functions](/guides/functions/syncs/sync-functions) - use metadata to filter synced data. - [Action functions](/guides/functions/action-functions) - fetch provider configuration options on demand. - [Event functions](/guides/functions/event-functions) - initialize metadata when a connection is created. - [Connection metadata API](/reference/backend/http-api/connections/set-metadata) - API reference. ## React to connection lifecycle events Source: https://nango.dev/docs/getting-started/use-cases/implement-event-handler.md Description: Run integration logic when a connection is validated, created, or deleted. Some integration work should happen around the connection lifecycle rather than from a user action or schedule. For example, you may need to verify credentials before accepting a connection, register provider webhooks after authorization, or clean up subscriptions before a customer disconnects. Nango supports this with event functions. They run automatically for a specific integration and connection event. ## When this fits Use event functions when you need to: - Validate credentials or provider permissions during connection creation. - Register external webhook subscriptions after a connection is created. - Fetch account context and store it as connection metadata. - Clean up provider resources before a connection is deleted. These functions are not called by your app directly. Nango runs them when the configured lifecycle event occurs. ## How Nango fits in 1. Your user creates or deletes a connection. 2. Nango detects the lifecycle event. 3. Nango runs the event function with that connection's credentials. 4. The function validates, initializes, registers, or cleans up provider-side resources. 5. The execution appears in Nango logs. ## What to read next - [Event functions](/guides/functions/event-functions) - supported events, function syntax, testing, and deployment. - [Webhook functions](/guides/functions/webhook-functions) - register and process external webhooks. - [Storage](/guides/functions/storage) - store account context on the connection. - [Functions SDK reference](/reference/functions/functions-sdk#createonevent) - `createOnEvent()` reference. ## Sample app Source: https://nango.dev/docs/getting-started/use-cases/sample-app.md Description: A practical demonstration of integrating Nango in your codebase. The sample app contains: - **A frontend**: lets users connect an integration using the Nango frontend SDK. - **A backend**: listens to Nango webhooks and consumes the Nango API to read & write data. The sample app uses Slack, Google Drive, OneDrive for Business, and OneDrive Personal as example integrations and is showcased in this [demo video](https://youtu.be/oTpWlmnv7dM). ### Access the repository
NangoHQ/sample-app
### Run the sample app - Go to [nango.dev](https://app.nango.dev?source=sample-app) and create an account (free). - Go to [Integrations](https://app.nango.dev/dev/integrations?source=sample-app) and create a Slack integration. - Set up a Slack app: 1. Go to [Slack Dev Center](https://api.slack.com/apps) and click **Create New App**. 2. Select **From scratch** for a simpler flow. 3. Enter your **App Name** and select a workspace to develop your app in, then click **Create App**. 4. In the **Basic Information** tab, copy your **Client ID** and **Client Secret**. 5. Go to **OAuth & Permissions** in the left sidebar. 6. Under **Redirect URLs**, add `https://api.nango.dev/oauth/callback` and click **Save URLs**. 7. Under **Scopes** > **Bot Token Scopes**, add: - `users:read` - `chat:write` - Go back to Nango. In the _Authorization_ tab, add: - `client_id`: from step 4 - `client_secret`: from step 4 - In the _Endpoints_ tab, activate `GET /users` and `POST /send-message`. - Go to [Integrations](https://app.nango.dev/dev/integrations?source=sample-app) and create a Google Drive integration. - Set up a Google Cloud project: 1. Go to the [Google Cloud Console](https://console.cloud.google.com/) and create a new project (or select an existing one). 2. Go to the [API Library](https://console.cloud.google.com/apis/library), search for **Google Drive API**, and click **Enable**. 3. Go to **APIs & Services** > **OAuth consent screen** and configure it: - Select **External** user type (or **Internal** for users within your Google Workspace organization). - Fill in the required app information and contact information. - Add the scope: `https://www.googleapis.com/auth/drive.readonly`. - Add test users if using External user type. 4. Go to **APIs & Services** > **Credentials** > **Create Credentials** > **OAuth client ID**. 5. Select **Web application**, enter a name, and add `https://api.nango.dev/oauth/callback` under **Authorized redirect URIs**. 6. Click **Create** and copy the **Client ID** and **Client Secret**. 7. Copy the **Project Number** from the Google Cloud console under **IAM & Admin** > **Settings** > **Project Number**. - Go back to the Google Drive integration in Nango. In the _Authorization_ tab, add: - `client_id`: from step 6 - `client_secret`: from step 6 - `scopes`: `https://www.googleapis.com/auth/drive.readonly` - In the _Endpoints_ tab, activate `GET /documents` endpoint. - Go to [Integrations](https://app.nango.dev/dev/integrations?source=sample-app) and create a OneDrive for Business integration. - Register an application in [Microsoft Entra admin center](https://entra.microsoft.com): 1. Sign in to the Microsoft Entra admin center and search for **App registrations** > **New registration**. 2. Enter a name (e.g., "Nango OneDrive Integration"). 3. Under **Supported account types**, select **Accounts in any organizational directory** for multitenant apps. 4. Click **Register** and note the **Application (client) ID** from the Overview page. 5. Go to **Authentication** > **Add a platform** > **Web** and add `https://api.nango.dev/oauth/callback` as the Redirect URI. 6. Go to **API permissions** > **Add a permission** > **Microsoft Graph** > **Delegated permissions** and add `Files.Read.All`. 7. Go to **Certificates & secrets** > **New client secret**, create a secret, and copy the value immediately (you won't see it again). - Go back to the OneDrive for Business integration in Nango. In the _Authorization_ tab, add: - `client_id`: Application (client) ID from step 4 - `client_secret`: Client secret value from step 7 - `scopes`: Add `offline_access` and `.default` - In the _Endpoints_ tab, activate `GET /user-files/selected` endpoint. - Go to [Integrations](https://app.nango.dev/dev/integrations?source=sample-app) and create a OneDrive Personal integration. - Register an application in [Microsoft Entra admin center](https://entra.microsoft.com): 1. Sign in to the Microsoft Entra admin center and search for **App registrations** > **New registration**. 2. Enter a name (e.g., "Nango OneDrive Personal Integration"). 3. Under **Supported account types**, select **Accounts in any organizational directory and personal Microsoft accounts**. 4. Click **Register** and note the **Application (client) ID** from the Overview page. 5. Go to **Authentication** > **Add a platform** > **Web** and add `https://api.nango.dev/oauth/callback` as the Redirect URI. 6. Go to **Certificates & secrets** > **New client secret**, create a secret, and copy the value immediately (you won't see it again). - Go back to the OneDrive Personal integration in Nango. In the _Authorization_ tab, add: - `client_id`: Application (client) ID from step 4 - `client_secret`: Client secret value from step 6 - `scopes`: Add `offline_access` and `onedrive.readonly` - In the _Endpoints_ tab, activate `GET /user-files/selected` endpoint. - Install: `NodeJS`, `Docker`. Then run: ```sh git clone https://github.com/NangoHQ/sample-app.git cd sample-app nvm use npm i ``` - Copy your Nango Secret Key, found in [Environment Settings > API Keys](https://app.nango.dev/dev/environment-settings#api-keys?source=sample-app). - Create a file to store environment variables and fill in the Nango Secret Key: ```sh cp .env.example .env ``` - Paste the **Project Number** you copied in step 7 of the Google Drive integration setup into the .env file as `GOOGLE_PROJECT_NUMBER=your_project_number_here`: ``` cd front-end cp .env.example .env ``` - This command should be running at all time: ```sh npm run webhooks-proxy ``` - Copy the URL the command gave you and go to [Environment Settings](https://app.nango.dev/dev/environment-settings?source=sample-app). Set Webhook URL to `${URL}/webhooks-from-nango`, e.g: `https://tame-socks-warn.loca.lt/webhooks-from-nango`. - Run: ```sh npm run start ``` - Go to: [http://localhost:3011](http://localhost:3011) The above sample app uses template functions, but you can also use custom ones. - Set up the Nango CLI: ```sh npm install -g nango cd nango-integrations/ nango init ``` - Add your Nango Secret Key in NANGO_SECRET_KEY_PROD in `./nango-integrations/.env`. - Optionally customize the functions using [this guide](/guides/functions/functions-guide). - Deploy the custom functions: ```sh nango deploy prod ``` ## File Integration Guide The sample app includes a complete implementation for integrating file storage providers (Google Drive, OneDrive) with your application. This guide walks you through using and understanding the file integration features. ### Overview Nango's template file functions allow you to: 1. **OAuth Flow**: Handle authentication using Nango's SDK 2. **File Picker**: Let users select specific files and folders to sync 3. **Document Sync**: Sync metadata for selected files and folders 4. **Document Fetching**: Download individual file contents as needed ### Requirements - Docker (desktop) installed & running - NodeJS & NPM installed - A Nango account (sign up at [nango.dev](https://nango.dev)) - Integration setup completed (see steps above) ### Using the File Integration After setting up and running the sample app (see steps above), follow these instructions to test the file integration: 1. **Access the sample app**: - Open [http://localhost:3011](http://localhost:3011/) - Click _Files_ to start the file integration Sample app home page with Files section highlighted 2. **Connect your account** (Google Drive, OneDrive, etc.): - Click the _Connect_ button on the provider's card - In the connection popup, click the _Connect_ button - Select your account and authorize the app - Click the _Finish_ button to close the modal Connect popup with file provider account options Successfully connected to file provider 3. **Select files and folders to sync**: - Click the _Select Files from [Provider]_ button - A file picker modal will appear - **note that each provider has its own native file picker**: - **Google Drive**: Uses Google's Drive Picker UI - **OneDrive for Business**: Uses Microsoft's file picker for organizational accounts - **OneDrive Personal**: Uses Microsoft's file picker for personal accounts - Select the files and folders you want to sync using the provider's file picker - Click the _Select_ button to confirm your selection Select files and folders from provider Select files and folders from provider 4. **View synced files**: - The files and folders will be synced to your Nango account and displayed in the UI Synced files and folders in the UI 5. **Download a file**: - Click the _Download_ button to download the file to your computer 6. **Disconnect your account**: - Click the _Disconnect_ button to disconnect your account and delete your connection from your Nango account Disconnect button ### What Happens Under the Hood The sample app uses Nango to: 1. **Authentication** - Authenticate users with file storage providers - Show the file picker - Handle OAuth flow automatically 2. **File Syncing** - Sync selected files & folders - Initial sync on selection - Periodic updates Nango dashboard showing sync status and file list 3. **File Download** - Download raw file content - Handle different file types - Use Nango's proxy for reliable downloads ### Key Components #### Frontend Implementation 1. **Authorization Flow** The authorization process is implemented in [`front-end/src/api.ts`](https://github.com/NangoHQ/sample-app/blob/main/front-end/src/api.ts): - `postConnectSession`: Initiates the OAuth connection - `getNangoCredentials`: Retrieves credentials after successful authorization The flow works as follows: 1. Frontend initiates connection via `postConnectSession` 2. Backend creates a Nango connection session 3. User is redirected to provider OAuth consent screen 4. After authorization, user is redirected back to the app 5. User is shown the provider-specific file picker (Google Drive Picker, OneDrive for Business picker, or OneDrive Personal picker) 6. Frontend can fetch credentials using `getNangoCredentials` #### Backend Implementation 1. **Webhook Handler & Data Sync** The webhook handling and data sync logic is implemented in [`back-end/src/routes/postWebhooks.ts`](https://github.com/NangoHQ/sample-app/blob/main/back-end/src/routes/postWebhooks.ts): Key components: - Main webhook handler that verifies signatures and routes requests - `handleNewConnectionWebhook`: Processes new file provider connections - `handleSyncWebhook`: Handles sync completion and data processing The sync process: 1. Nango syncs files from the provider 2. Webhook notifies backend of sync completion 3. Backend fetches records using `nango.listRecords` 4. File metadata is stored in the database 5. Deletions are tracked and handled appropriately 2. **File Download with Proxy** File download functionality is implemented in [`back-end/src/routes/downloadFile.ts`](https://github.com/NangoHQ/sample-app/blob/main/back-end/src/routes/downloadFile.ts): The download process: 1. Frontend requests file download with file ID and connection ID 2. Backend uses Nango proxy to fetch file from provider 3. File is streamed to client with proper headers 4. Error handling ensures graceful failure Key features: - Uses Nango proxy for secure file access - Proper content type and disposition headers - Streaming response for efficient file transfer - Comprehensive error handling ### Nango Dashboard Explore these sections in your Nango account: 1. **Integration Settings** - [View Integrations](https://app.nango.dev/dev/integrations) - Configure sync settings - View integration status 2. **Connections** - [View Connections](https://app.nango.dev/dev/connections) - Monitor sync status - Check connection health 3. **Logs** - [View Logs](https://app.nango.dev/dev/logs) - Track sync operations - Debug issues ### Common Issues and Solutions 1. **Connection Issues** - Check Docker is running - Verify webhook URL is correct - Ensure provider credentials are valid 2. **Sync Problems** - Check Nango logs - Verify file permissions - Monitor sync status ### File Type Support The sample app supports all file types through Nango's proxy: - **Regular files** (PDFs, images, documents, etc.) - **Google Workspace files** (Docs, Sheets, Slides) - **Microsoft Office files** (Word, Excel, PowerPoint) - **Other cloud storage file types** **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Next steps - [Auth guide](/guides/auth/auth-guide) - understand the authorization flow used by the sample app. - [Sync functions](/guides/functions/syncs/sync-functions) - customize the data sync logic. - [Webhooks from Nango](/guides/platform/webhooks-from-nango) - process sync and auth notifications. ## Auth guide Source: https://nango.dev/docs/guides/auth/auth-guide.md Description: Let your users connect external APIs to your agents & apps. ## Overview Nango Auth lets your users connect 800+ external APIs to your product. You embed a Nango-managed auth flow in your application, and Nango handles authorization, credential storage, refresh, and validation. A **Connection** is created after each successful authorization. It stores one user's credentials for one external API and keeps them valid over time. Credentials can be retrieved at scale or used directly by other Nango primitives — without ever passing through your codebase. ## How it works The end-to-end flow has four steps: 1. **Backend** — your server asks Nango for a short-lived **session token**. 2. **Frontend** — your app opens the Nango **Connect UI** with that token. 3. **User** — authorizes the API. Nango stores the credentials. 4. **Backend** — Nango sends an **auth webhook** with the **connection ID**. You persist it alongside your user/org/project. Once you have the connection ID, fetch credentials whenever you need them: ```ts await nango.getConnection(integrationId, connectionId); ``` ```bash curl "https://api.nango.dev/connections/{connectionId}?provider_config_key={integrationId}" \ -H "Authorization: Bearer {nangoSecretKey}" ``` ## Capabilities - **Auth schemes** — OAuth 2.0, OAuth 1.0a, API keys, basic auth, custom. 800+ APIs supported; new ones [added on demand](/integrations/contribute-or-request-api) within days, by request or self-contributed. - **Pre-built UI** — Embedded Connect UI with API-specific guidance, input validation, and your branding. Or [customize Connect UI or build a custom UI](/guides/auth/customize-connect-ui). - **Credential management** — Encrypted storage, automatic refresh, retrieval at scale. Combine with [Proxy](/guides/platform/proxy-requests) or [Functions](/guides/functions/functions-guide) to avoid handling credentials directly. - **Observability** — Failure detection, reconnect flow, and per-connection logs. ## Guide **Using a coding agent?** Set up the [docs MCP](/getting-started/coding-agent-setup#docs-mcp-server) and [skills](/getting-started/coding-agent-setup#skills), then copy this page as prompt. Sign up for free (no credit card): [![Try Nango Cloud](/images/nango-deploy-button.svg)](https://app.nango.dev/signup) Signup cannot be automated. Ask the user to sign up at [app.nango.dev/signup](https://app.nango.dev/signup) and provide their Nango API key from the Environment settings tab > API Keys. Use it as `Authorization: Bearer ` in API requests. The default API key created on signup has full access and is the simplest option. If the user creates a scoped API key instead, this guide needs `environment:integrations:create` (step 2) and `environment:connect_sessions:write` (steps 5 and 8). See [API keys](/reference/backend/http-api/api-keys). In the dashboard, open the [Integrations](https://app.nango.dev/dev/integrations) tab, click **Configure New Integration**, and pick the API. Each API has a dedicated [Nango documentation page](/integrations/overview) with setup notes and gotchas. OAuth APIs require an OAuth developer app registered with the provider. Register one on the provider's developer portal, use the `Callback URL` shown in your Nango integration settings, then paste the `Client ID` and `Client Secret` back into Nango. Register any required scopes too. If this integration will be used in production, complete the optional custom callback URL step below before real users connect. Retrofitting a callback URL later requires updating the provider app and can break new authorization or reconnect flows until the provider and Nango settings match. For OAuth 2.0 providers, Nango shows **suggested scopes** — auto-discovered and refreshed at least monthly. They're suggestions; you can use any scope the provider supports. For popular APIs, Nango ships a built-in shared developer app so you can test connections with zero setup. We strongly recommend registering your own OAuth app before going live — see [OAuth developer apps](#oauth-developer-apps) below. Create the integration with the API. If you need the provider slug first, list providers and use the matching provider's `name` field: ```bash curl --request GET \ --url https://api.nango.dev/providers \ --header 'Authorization: Bearer ' ``` For OAuth providers, walk the user through registering an OAuth developer app on the provider's portal: link them to the provider's OAuth-app creation page, tell them which `Callback URL` to paste (visible in the Nango integration settings after creation), and ask them to send back the `client_id` and `client_secret` so you can pass them in the request body. ```bash curl --request POST \ --url https://api.nango.dev/integrations \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "unique_key": "", "provider": "", "credentials": { "type": "OAUTH2", "client_id": "", "client_secret": "", "scopes": "," } }' ``` ```bash curl --request POST \ --url https://api.nango.dev/integrations \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "unique_key": "", "provider": "" }' ``` For all fields and response shapes, see the [List all providers API](/reference/backend/http-api/providers/list), [Create an integration API](/reference/backend/http-api/integration/create), and [Update an integration API](/reference/backend/http-api/integration/update). In the [Connections](https://app.nango.dev/dev/connections) tab, click **Add Test Connection**, pick your integration, and authorize using a test account. The new connection's credentials appear in its *Authorization* tab — securely stored and automatically refreshed. Skip the dashboard. Create a connect session and share the returned `connect_link` with the user — they authorize using their test account in the browser, no UI needed on your side: ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "tags": { "end_user_id": "" }, "allowed_integrations": [""] }' ``` The response contains a `connect_link` (valid 30 minutes). Send it to the user; once they complete authorization, confirm with `GET /connections`. See [Share a connect link](/guides/auth/share-connect-link). For production OAuth apps, a callback URL on your own domain is recommended. Some providers show the callback domain to users. To configure a custom callback URL: 1. Add an endpoint on your domain (e.g. `https://example.com/oauth-callback`) that **308-redirects** to `https://api.nango.dev/oauth/callback`, preserving all query parameters. 2. Update the registered callback URL with each API provider — otherwise they'll reject new authorization flows. 3. After confirming steps 1 and 2, update the callback URL in Nango's **Environment Settings**. Settings are per-environment, so repeat for every environment. Ask the user what production callback URL they want to use, then have them add a 308 redirect from that URL to `https://api.nango.dev/oauth/callback` before they register provider OAuth apps. The Nango environment setting is dashboard-only — ask the user to paste the final callback URL into **Environment Settings > Backend**. Set up a backend endpoint that your frontend will call before each authorization attempt to retrieve a session token from Nango ([API](/reference/backend/http-api/connect/sessions/create) / [Node SDK](/reference/backend/backend-sdk/node#create-a-connect-session)). You'll need an [API key](/reference/backend/http-api/api-keys) with the `environment:connect_sessions:write` scope (find it in **Environment Settings > API Keys**). ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env.NANGO_API_KEY! }); api.post('/sessionToken', async (req, res) => { const { data } = await nango.createConnectSession({ tags: { end_user_id: '', end_user_email: '', organization_id: '' }, allowed_integrations: [''] }); res.status(200).send({ sessionToken: data.token }); }); ``` ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" }, "allowed_integrations": [""] }' ``` Tags reconcile Nango's auth-success webhook with the right user, org, or entity in your system. Most apps start with: `end_user_id`, `end_user_email`, `organization_id`. Tags are also useful for filtering connections via the API and are displayed in the Nango UI. Set the reserved UI tag keys `end_user_display_name` and `end_user_email` to populate the connections list and connection detail page. See [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata) for more. `allowed_integrations` controls what the user sees: - **Multiple integrations** — Connect UI shows a picker. - **Single integration** — Connect UI sends the user straight to its auth flow. Load the Nango frontend SDK, fetch the session token from your backend, and open Connect UI: ![](/images/screenshots/connect-ui.gif) ```js import Nango from '@nangohq/frontend'; const nango = new Nango(); const connect = nango.openConnectUI({ onEvent: (event) => { if (event.type === 'close') { // Modal closed. } else if (event.type === 'connect') { // Auth flow successful. } }, }); const res = await fetch('/sessionToken', { method: 'POST' }); connect.setSessionToken(res.sessionToken); // A loading indicator shows until this is set. ``` See the [Frontend SDK reference](/reference/frontend/frontend-sdk#connect-using-nango-connect-ui) for more options. The pre-built Connect UI is recommended but optional — if you need full UX control, follow [Customize Connect UI](/guides/auth/customize-connect-ui). For email/cross-device flows, see [Share a connect link](/guides/auth/share-connect-link). When authorization succeeds, Nango generates a unique **connection ID**. You use this ID to manage the connection and retrieve its credentials. **You must store it on your side**, attached to the user/org/project that owns the connection — Nango doesn't model that ownership. Set up the webhook in **Environment Settings**: 1. Specify a **Webhook URL** in the Nango UI. 2. Enable **Send New Connection Creation Webhooks**. 3. Handle `POST` requests on that route in your backend. Webhook URL configuration is dashboard-only — there is no public API for environment settings. Ask the user to paste the webhook URL into **Environment Settings** and enable **Send New Connection Creation Webhooks**. Webhook payload: ```json { "type": "auth", "operation": "creation", "success": true, "connectionId": "", "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" } } ``` Persist `connectionId` against whichever entity owns the connection in your app. Credentials can become invalid when users revoke access, providers rotate refresh tokens, scopes change, or an app moves from testing to production. Re-authorization updates an existing connection's credentials while preserving its data and config. Deleting and re-creating the connection wipes that data — prefer re-authorization. Before showing integration settings, check the connection with `GET /connections/{connectionId}`: ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env.NANGO_API_KEY! }); try { await nango.getConnection('', ''); // Connection is valid. } catch (error) { if (error.response?.data?.error?.code === 'invalid_credentials') { // Connection is invalid — show a Reconnect button. displayReconnectUI(); } } ``` ```bash curl --request GET \ --url 'https://api.nango.dev/connections/?provider_config_key=' \ --header 'Authorization: Bearer ' ``` An `invalid_credentials` error means the connection failed credential refresh or validation — show an error state with a **Reconnect** button. To trigger re-authorization, use the same frontend flow as a new connection, but generate a reconnect session token on the backend. Pass the returned token to the Frontend SDK exactly like a normal session token. On success you'll receive an `auth` webhook with `operation = override`. ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env.NANGO_API_KEY! }); api.post('/sessionToken', async (req, res) => { const { data } = await nango.createReconnectSession({ connection_id: '', integration_id: '' }); res.status(200).send({ sessionToken: data.token }); }); ``` ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions/reconnect \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "connection_id": "", "integration_id": "" }' ``` Test the auth flow from your app and verify a connection appears in the [Connections](https://app.nango.dev/dev/connections) tab. Failed attempts are inspectable in the **Logs** tab. After the user runs the flow, confirm the connection was created with [`GET /connections`](/reference/backend/http-api/connections/list) — filter by the tags you set in step 5: ```bash curl --request GET \ --url 'https://api.nango.dev/connections?tags[end_user_id]=' \ --header 'Authorization: Bearer ' ``` Auth logs are dashboard-only — if the call returns no connection, ask the user to check the **Logs** tab for the failure reason. 🎉 **You are connected!** Next: - View connections and credentials in the **Connections** tab. - Retrieve credentials via the [API](/reference/backend/http-api/connections/get) or [Node SDK](/reference/backend/backend-sdk/node#get-a-connection-with-credentials). - Build with [sync functions](/guides/functions/syncs/sync-functions), [Action functions](/guides/functions/action-functions), [webhook functions](/guides/functions/webhook-functions), or any other Nango function type. **Questions, problems, feedback?** Reach out in the [Slack community](https://nango.dev/slack). ## OAuth developer apps Nango ships built-in shared developer apps for some OAuth 2.0 APIs so you can test connections with zero setup. They work in production too, but we strongly recommend registering your own before going live. Most OAuth APIs have a step-by-step setup guide in [APIs & Integrations](/integrations/overview) — for example, [Gmail](/api-integrations/google-mail/how-to-register-your-own-gmail-api-oauth-app). Reasons to register your own: - **Whitelabel auth** — with Nango's app, users authorize Nango instead of your product. - **Scopes** — Nango developer apps have fixed scopes. Your own app supports any scope the provider exposes. - **Callback URL** — Nango developer apps use Nango's callback. Your own app supports a [custom callback URL](#custom-oauth-callback-url-optional) on your domain. - **Marketplace listings** — provider marketplaces require your own developer app. - **Advanced API features** — some provider features are only available to first-party apps. - **Revocation risk** — most providers don't allow shared credentials. Nango developer apps may be revoked by the provider at any time. - **Portability** — only your own developer app lets you export access and refresh tokens and move off Nango without users re-authorizing. The same tradeoffs apply to shared OAuth apps provided by other auth platforms. ## Related guides - [Share a connect link](/guides/auth/share-connect-link) - send authorization links by email, support tools, or cross-device flows. - [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata) - store the right connection-level data in the right field. - [Customize Connect UI](/guides/auth/customize-connect-ui) - customize branding or build your own authorization UI. - [Token refreshing and validity](/guides/auth/token-refreshing) - handle revoked or expired credentials. ## Share a connect link Source: https://nango.dev/docs/guides/auth/share-connect-link.md Description: Generate a short-lived connect link and share it with your end user. Use a connect link when you want to trigger authorization without opening Connect UI immediately in your app (for example: email-based onboarding, support-assisted setup, or cross-device flows). ## Option 1: Generate a link from your backend Create a connect session and return the `connect_link` to your frontend or internal tools: ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env[''] }); api.post('/connectLink', async (req, res) => { const { data } = await nango.createConnectSession({ tags: { end_user_id: '', end_user_email: '', organization_id: '' }, allowed_integrations: [''] }); res.status(200).send({ connectLink: data.connect_link, expiresAt: data.expires_at }); }); ``` ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" }, "allowed_integrations": [ "" ] }' ``` The returned `connect_link` expires after 30 minutes. ## Option 2: Generate a link from the dashboard In the Nango dashboard, open the Connections page and use the **Share connect link** button. This generates a link that you can share directly with the end user. ## Notes - For the API details, see [Create a connect session](/reference/backend/http-api/connect/sessions/create). ## Related guides - [Auth guide](/guides/auth/auth-guide) - embed the standard Connect UI flow. - [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata) - reconcile completed links with users and orgs. - [Create a connect session API](/reference/backend/http-api/connect/sessions/create) - API details for generated links. ## Connection tags, configuration, and metadata Source: https://nango.dev/docs/guides/auth/connection-tags-configuration-metadata.md Description: Use tags, connection configuration, and metadata for the right connection-level data. Pre-requisite: complete the [Set up an integration](/guides/auth/auth-guide#guide) section of the Auth guide. A Nango connection can store several kinds of information besides credentials. Use the right field depending on what the data does. | Field | Use it for | Example | | --- | --- | --- | | `tags` | Attribution, filtering, routing, and webhook reconciliation. | `end_user_id`, `organization_id`, `workspace_id` | | `connection_config` | Provider-specific information required for the connection to be valid or usable. | `subdomain`, `tenantId`, `region`, API base URL | | `metadata` | Your own application or function configuration stored on the connection. | field mappings, selected folder IDs, feature flags | ## Connection tags Tags are small key/value strings you attach to a Connect session. Nango copies them to the resulting connection and includes them in auth webhooks. Their primary purpose is **attribution**. Nango uses random UUIDs as connection IDs, so tags are the bridge between a Nango connection and the user, organization, workspace, or other entity in your system. Most apps start with these tags: - `end_user_id` - `end_user_email` - `organization_id` Then add whatever additional routing identifiers you need, such as `workspace_id`, `project_id`, or `plan`. To make connections easier to identify in the Nango dashboard, set `end_user_display_name` and `end_user_email`. Nango uses these reserved UI tag keys to populate the connections list and connection detail page. Tags are designed for identifiers and routing metadata. Avoid putting secrets or large/free-form payloads in tags. ### Add tags when creating a Connect session When requesting a new Connect session token from Nango, pass a `tags` object: ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env[''] }); api.post('/sessionToken', async (req, res) => { const { data } = await nango.createConnectSession({ tags: { end_user_id: '', end_user_email: '', organization_id: '' }, allowed_integrations: [''] }); res.status(200).send({ sessionToken: data.token }); }); ``` ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" }, "allowed_integrations": [ "" ] }' ``` ### Read tags from the auth webhook These tags appear in successful auth webhooks sent by Nango: ```json { "type": "auth", "operation": "creation", "success": true, "connectionId": "", "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" } } ``` Use these values to reconcile the generated `connectionId` with the user, organization, or workspace that initiated the flow in your application. ### Read and filter connections by tags Nango stores tags on the connection itself. ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: '' }); const connection = await nango.getConnection('', ''); console.log(connection.tags); ``` ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: '' }); const { connections } = await nango.listConnections({ tags: { end_user_id: '', organization_id: '' }, limit: 100 }); console.log(connections); ``` ```bash curl --request GET \ --url 'https://api.nango.dev/connections?tags[end_user_id]=&tags[organization_id]=' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' ``` Filtering uses an AND match: a connection must contain all provided tag keys and values. ### Update tags on an existing connection If you need to update tags after a connection is created, patch the connection: ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: '' }); await nango.patchConnection( { connectionId: '', provider_config_key: '' }, { tags: { end_user_id: '', end_user_email: '', organization_id: '', environment: '' } } ); ``` ```bash curl --request PATCH \ --url 'https://api.nango.dev/connections/?provider_config_key=' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "", "environment": "" } }' ``` Updating tags replaces the whole tag object. If you want to add or remove a single tag, fetch the current connection first, merge locally, then patch. ### Reserved UI tag keys Two tag keys are reserved for improving the Nango dashboard UI: - `end_user_display_name` - shown instead of the connection ID in the connections list, making it easier to identify who a connection belongs to. - `end_user_email` - shown alongside `end_user_display_name` in the connections list. The domain from the email address is also used to display the end user's company logo on the connection detail page. ![](/images/screenshots/connection-tags-1.png) ![](/images/screenshots/connection-tags-2.png) ### Tag rules - `end_user_email` must be a valid email address, for example `user@example.com`. - Tag keys are lowercased, so `End_User_Email` and `end_user_email` are treated as the same key. - Keys are case-insensitive, and duplicate keys after normalization are rejected. - Keys must start with a letter and contain only alphanumerics, underscores, hyphens, periods, or slashes. - Keys can be up to 64 characters long. - A connection can have up to 10 tag keys. - Values must be non-empty strings up to 255 characters long. ## Connection configuration Connection configuration is provider-specific information stored on a connection because the connection needs it to work. It can be required to: - build an OAuth authorization URL, such as a Zendesk subdomain - choose the correct API base URL, such as a region or account-specific host - call API endpoints later, such as a tenant ID, account ID, portal ID, or workspace ID - store values Nango discovers during authorization, such as an API instance URL or provider account ID Use connection configuration when the value is part of the connection's validity. If the value is missing, the connection may fail to authorize, fail credential checks, or be unusable for API requests. When you use the pre-built [Connect UI](/guides/auth/auth-guide), Nango generates the required forms for the integration and stores the submitted values as connection configuration. When you use [headless auth with a custom UI](/guides/auth/customize-connect-ui#build-your-own-auth-ui), you must collect required connection configuration in your own UI and pass it to `nango.auth()`: ```js nango.auth('zendesk', { params: { subdomain: '' } }); ``` You can also set defaults when creating a Connect session: ```ts const { data } = await nango.createConnectSession({ allowed_integrations: [''], integrations_config_defaults: { '': { connection_config: { '': '' } } } }); ``` ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "allowed_integrations": [""], "integrations_config_defaults": { "": { "connection_config": { "": "" } } } }' ``` Read connection configuration from the connection: ```ts const connection = await nango.getConnection('', ''); console.log(connection.connection_config); ``` For the REST API response shape, see [Get a connection](/reference/backend/http-api/connections/get). For the Node SDK method, see [Get a connection with credentials](/reference/backend/backend-sdk/node#get-a-connection-with-credentials). ## Connection metadata Metadata is arbitrary JSON storage on the connection. Use it for information your app or Nango functions need, but that is not a condition for the connection itself to exist. Good metadata examples: - field mappings chosen by a customer - folder, project, board, or object IDs selected during onboarding - filters and feature flags used by sync functions - tenant-specific identifiers discovered by your setup flow - provider webhook subscription IDs used by webhook functions Do not use metadata for credentials or values required to make the provider connection valid. Use connection configuration for that. Do not use metadata for synced datasets or large lists of records; use the [records cache](/guides/functions/syncs/records-cache) for sync output. Set metadata after a connection is created: ```ts await nango.setMetadata( '', '', { syncArchived: false, fieldMapping: { companyName: 'Account_Name__c' } } ); ``` ```bash curl --request POST \ --url https://api.nango.dev/connections/metadata \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "connection_id": "", "provider_config_key": "", "metadata": { "syncArchived": false, "fieldMapping": { "companyName": "Account_Name__c" } } }' ``` Functions can read metadata for the connection they run for: ```ts const metadata = await nango.getMetadata(); ``` For deeper function patterns, see [Storage](/guides/functions/storage). **Related guides** - [Auth guide](/guides/auth/auth-guide) - create connections with Connect UI. - [Customize Connect UI](/guides/auth/customize-connect-ui) - customize branding or pass connection configuration without Connect UI. - [Share a connect link](/guides/auth/share-connect-link) - include tags in link-based flows. - [Storage](/guides/functions/storage) - use metadata inside functions. - [List connections API](/reference/backend/http-api/connections/list) - filter connections by tags. ## Customize Connect UI Source: https://nango.dev/docs/guides/auth/customize-connect-ui.md Description: Guide to branding Nango Connect UI, overriding setup links, or building a headless auth flow with your own UI. Pre-requisites: - complete the [Set up an integration](/guides/auth/auth-guide#guide) section of the Auth guide - generate a [Connect session token](/guides/auth/auth-guide#guide) Nango's pre-built [Connect UI](/guides/auth/auth-guide) is the recommended way to let users authorize integrations. It generates the right form for each API, validates required inputs, tests credentials when supported, and shows users where to find API keys or other required values. You can customize that pre-built UI with your own branding. If you need full control over every screen, you can also skip Connect UI and use Nango in a **headless auth** mode. ## Customize the pre-built Connect UI You can customize the Connect UI theme from the Nango dashboard: 1. Open **Environment Settings**. 2. Go to the **Connect UI** tab. 3. Choose the theme: light, dark, or system. 4. Set the primary color for the light theme. 5. Set the primary color for the dark theme. 6. Optionally remove the **Secured by Nango** mention. These settings apply to the pre-built Connect UI that you open with [`nango.openConnectUI()`](/reference/frontend/frontend-sdk#connect-using-nango-connect-ui). Connect UI branding customization is available on the [Growth plan](https://www.nango.dev/pricing). ## Override documentation links By default, the Connect UI displays info icons that link to the Nango documentation. If you are embedding Connect UI in a customer-facing application and want these links to point to your own documentation instead, override them per integration using the `overrides` parameter when [creating a Connect session](/reference/backend/http-api/connect/sessions/create). This feature is available on the [Growth plan](https://www.nango.dev/pricing). See the full [SDK reference](/reference/backend/backend-sdk/node#create-a-connect-session) for all available parameters. ```js const { data } = await nango.createConnectSession({ // Recommended: copied onto the connection and included in auth webhooks. tags: { end_user_id: '', end_user_email: '', organization_id: '' }, allowed_integrations: ['', ''], integrations_config_defaults: { '': { connection_config: { '': '' } } }, overrides: { '': { docs_connect: 'https://your-docs.com/how-to-connect' } } }); ``` See the full [API reference](/reference/backend/http-api/connect/sessions/create) for all available parameters. ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "tags": { "end_user_id": "", "end_user_email": "" }, "overrides": { "": { "docs_connect": "https://your-docs.com/how-to-connect" } } }' ``` Replace `` with the unique name of the integration, for example `jira` or `slack`, and set `docs_connect` to the URL you want the info icons to link to. ## Build your own auth UI Headless auth means you build the user-facing setup UI yourself and use the frontend SDK to pass the required values to Nango. Headless auth still uses Nango for the backend parts of auth: Nango stores credentials, refreshes OAuth tokens, manages connection records, sends auth webhooks, and lets you call APIs through the proxy or from functions. What changes is the setup experience your user sees before the connection is created. ## What changes with headless auth For OAuth APIs, calling `nango.auth()` opens the external API's login popup directly. Your user skips the Nango pre-authorization screen. This is useful when you want a shorter flow, but it also means you must collect any provider-specific inputs that the Nango Connect UI would normally ask for first. For API key, Basic Auth, and other non-OAuth flows, there is no external login popup. You must build the form that collects the user's credentials, validate field formats in your own UI, and pass the credentials to `nango.auth()`. Nango can still reject invalid credentials when the integration supports credential checks, but your UI owns the input fields, error messages, and setup guidance. Some APIs also require **connection configuration** in addition to credentials. For example, an API might need a subdomain, domain, tenant ID, region, or account-specific base URL to build the authorization URL or make future API requests. Connect UI collects these fields automatically. With headless auth, your UI must collect them and pass them programmatically. See [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata) for the difference between connection configuration, metadata, and tags. ## Use the frontend SDK for headless auth In your frontend, initiate Nango ([reference](/reference/frontend/frontend-sdk#instantiate-the-frontend-sdk)): ```ts import Nango from '@nangohq/frontend'; const nango = new Nango({ connectSessionToken: '' }); ``` Then create the connection with `nango.auth()` ([reference](/reference/frontend/frontend-sdk#connect-using-the-headless-client)): For OAuth, `nango.auth()` opens the external API's login popup directly. ```js nango .auth('') .then((result) => { // Show success UI. }) .catch((error) => { // Show failure UI. }); ``` For API key auth, collect the API key in your own UI, then pass it to Nango. ```js nango .auth('', { credentials: { apiKey: '' } }) .then((result) => { // Show success UI. }) .catch((error) => { // Show failure UI. }); ``` For Basic Auth, collect the username and password in your own UI, then pass them to Nango. ```js nango .auth('', { credentials: { username: '', password: '' } }) .then((result) => { // Show success UI. }) .catch((error) => { // Show failure UI. }); ``` ## Pass connection configuration Some APIs require connection-specific configuration to authorize or make API requests later. Zendesk is a common example: the OAuth authorization URL includes the user's Zendesk subdomain: `https://.zendesk.com/oauth/authorizations/new` With Connect UI, Nango asks for this value in an automatically generated form. With headless auth, collect it in your own UI and pass it as `params`: ```js nango.auth('zendesk', { params: { subdomain: '' } }); ``` You can also set default connection configuration when you create the Connect session. This is useful when your backend already knows a required value: ```js const { data } = await nango.createConnectSession({ allowed_integrations: [''], integrations_config_defaults: { '': { connection_config: { '': '' } } } }); ``` ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "allowed_integrations": [""], "integrations_config_defaults": { "": { "connection_config": { "": "" } } } }' ``` In some cases, you might want to override scopes at the connection level. Pass the scopes to `nango.auth()`: ```js nango.auth('', { params: { oauth_scopes_override: 'custom-connection-scope' } }); ``` Nango stores this connection configuration on the connection. You can retrieve it with the SDK ([reference](/reference/backend/backend-sdk/node#get-a-connection-with-credentials)) or API ([reference](/reference/backend/http-api/connections/get)), or see it in connection details in the Nango UI. ## Next Once authorization succeeds, [_save the connection ID_](/guides/auth/auth-guide#guide). **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Related guides - [Auth guide](/guides/auth/auth-guide) - compare the managed Connect UI flow. - [Frontend SDK reference](/reference/frontend/frontend-sdk#connect-using-the-headless-client) - headless authorization methods and parameters. - [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata) - understand connection-level storage. ## Token refreshing & validity Source: https://nango.dev/docs/guides/auth/token-refreshing.md Description: How Nango handles OAuth token refresh and what to do when tokens are revoked. ## Automatic token refresh Nango automatically refreshes OAuth access tokens before they expire. You don't need to implement any refresh logic in your application. To prevent token revocation due to inactivity (some APIs revoke unused refresh tokens), Nango refreshes each access token at least once every 24 hours. ## Handling refresh failures Token refresh can fail for various reasons: - The user revoked access in the external application - The external API revoked the refresh token - The external API experienced an outage When a refresh fails, Nango can [notify your app via webhook](/guides/platform/webhooks-from-nango) so you can prompt the user to reconnect. ## Best practices Revoked access tokens and refresh failures happen to all integrations. To handle them gracefully: 1. **Monitor for failures**: Set up [webhooks from Nango](/guides/platform/webhooks-from-nango) to receive notifications when token refresh fails 2. **Handle revoked tokens**: Follow our [guide on handling revoked access tokens](/guides/platform/common-issues#token-refresh-error-from-the-external-api) 3. **Implement re-authorization**: Make sure you have the [re-authorization flow](/guides/auth/auth-guide#re-authorize-an-existing-connection) in place so users can easily reconnect broken connections ## Related guides - [Auth guide](/guides/auth/auth-guide#re-authorize-an-existing-connection) - implement reconnect sessions for invalid credentials. - [Webhooks from Nango](/guides/platform/webhooks-from-nango) - receive refresh failure notifications. - [Common issues](/guides/platform/common-issues#token-refresh-error-from-the-external-api) - troubleshoot revoked tokens. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Functions guide Source: https://nango.dev/docs/guides/functions/functions-guide.md Description: Understand Nango Functions and choose the right way to start using them. ## Overview **Functions** are Nango's runtime for custom integration logic. When you build integrations directly, every provider forces you to solve the same hard problems: OAuth credentials, token refresh, per-customer permissions, retries, rate limits, data freshness, observability, and safe execution. Nango handles that infrastructure, and functions let you write the provider-specific logic that is unique to your product. You write functions in TypeScript and deploy them to Nango. Your app can call them from any language, framework, backend job, or agent workflow. Each execution runs for a specific integration and connection, so the function has scoped access to the right customer's credentials without exposing those credentials to your app or agent. ## How functions fit 1. Your user authorizes an external API through [Nango Auth](/guides/auth/auth-guide). 2. Your app stores the resulting `connectionId`. 3. You enable or deploy functions for the integration. 4. Nango runs the function on demand, on a schedule, from an external webhook, or after a connection lifecycle event. 5. Your app receives the function result, sync records, or webhooks from Nango. Functions are the right layer when you need custom API logic, data syncing, webhook processing, AI tools, or per-customer integration behavior. ## Capabilities - **Scoped external API access** — run API calls with the credentials and scopes of one connection. - **Any app stack** — trigger functions through HTTP APIs or SDKs from any backend language. - **Multiple trigger types** — call functions on demand, run them on a schedule, route external webhooks into them, or react to connection lifecycle events. - **Agent-ready tools** — expose selected functions to agents through the API or Nango's MCP server while keeping provider credentials out of the model loop. - **Built-in integration infrastructure** — retries, rate-limit handling, logs, OpenTelemetry export, records storage, checkpoints, and per-connection metadata. - **Flexible development workflows** — enable a template, build headlessly through the Functions API, or keep function code in a local CLI project. ## Guide ### Ways to enable functions There are three ways to enable or develop Nango functions. Start with the path that matches how much control and dynamism you need. | Path | Best for | How it works | | --- | --- | --- | | [Option 1: Use function templates](#use-function-templates) | Getting started quickly when Nango already has the provider operation in the catalog. | Enable a pre-built function on an integration, then call it from your app, backend job, or agent workflow. | | [Option 2: Build locally with the CLI](#build-locally-with-the-cli) | Serious production use cases where you want reviewable diffs, tests, CI/CD, and source control. | Write TypeScript functions in your repo, dry run them against real connections, and deploy with the Nango CLI. | | [Option 3: Build with the Functions API](#build-with-the-functions-api) | Prototypes, provider operations not covered by templates, and just-in-time integrations where an agent creates behavior dynamically. | Send generated TypeScript source to Nango, compile it, dry run it, and deploy it without a local functions project. | ### Option 1: Use function templates Function templates are pre-built Nango functions for common provider operations. Use them when the catalog already covers what you need and you want the shortest path to a working integration. You can find templates in the [template catalog](https://www.nango.dev/templates), in the integration page of the Nango dashboard, and in the [Nango integration templates repository](https://github.com/NangoHQ/integration-templates). When a template matches your use case, enable it on the integration and call it from your app. If it is close but not exact, [pull it into a local functions project and customize it](#extend-a-template). #### Extend a template Use the CLI to pull a template's code into your local functions project from the public catalog: ```bash nango pull github list-repos --catalog ``` `nango pull` writes a single function and its dependencies. After pulling, review the source and schemas, import the function from `index.ts` if needed, adjust inputs, outputs, record models, metadata, or provider mapping logic, then run `nango dryrun` and deploy with `nango deploy`. Pull template code when you need custom fields, a smaller output shape, customer-specific filtering, different retry behavior, or a provider workflow that is close to a template function but not identical. ### Option 2: Build locally with the CLI Local development is the default path for production integrations. Use it when your engineering team wants function code in source control, reviewable diffs, local tests, CI/CD, and a stable project layout.
Install the Nango CLI globally: ```bash npm install -g nango ``` Initialize your integrations folder at the root of your repo and commit it to source control: ```bash nango init nango-integrations ``` This creates a `nango-integrations/` folder with example configuration. The `.nango` subdirectory must be committed because the CLI uses it to track deployed state. Add your [API keys](/reference/backend/http-api/api-keys) to a `.env` file inside `nango-integrations/`. The CLI currently reads these API keys from `NANGO_SECRET_KEY_` variables: ```bash NANGO_SECRET_KEY_PROD='' NANGO_SECRET_KEY_DEV='' ``` Get your [API keys](/reference/backend/http-api/api-keys) from **Environment Settings > API Keys**. Despite the variable name, each value stores a [Nango API key](/reference/backend/http-api/api-keys). The CLI picks up the matching key for whichever environment you deploy to. `nango init` creates `nango-integrations/.gitignore` with `.env` ignored. Keep the `.env` file untracked so [Nango API keys](/reference/backend/http-api/api-keys) are not committed. Keys are matched by environment name: `NANGO_SECRET_KEY_`. If you rename an environment in the UI, update the variable name in `.env` to match. For example, an environment named `staging` needs: ```bash NANGO_SECRET_KEY_STAGING='' ``` Use a shared environment (usually `dev`) for day-to-day collaboration. Create personal test connections with [per-connection webhook URL overrides](/guides/platform/webhooks-from-nango#override-webhook-urls-per-connection) pointing at your local webhook endpoint, and deploy functions granularly (`nango deploy --sync` / `--action` / `--integration`) so teammates don't overwrite each other. See [Engineering collaboration](/guides/platform/environments#engineering-collaboration). Use CI to do full deploys across stable environments (like `staging` or `prod`). See the [CI/CD guide](/guides/functions/ci-cd). Run `nango init nango-integrations` to scaffold the folder. Ask the user for their [Nango API key](/reference/backend/http-api/api-keys) from **Environment Settings > API Keys** and write it to the matching `NANGO_SECRET_KEY_` variable in `nango-integrations/.env`. Confirm that `nango-integrations/.gitignore` ignores `.env`. For additional context while building, add Nango's documentation MCP server (`https://nango.dev/docs/mcp`) and install the local builder skill: ```bash npx skills add NangoHQ/skills -s building-nango-functions-locally ``` A Nango integrations folder is a small TypeScript project. The root contains shared project files, and each integration gets its own folder named after the integration ID. Inside each integration folder, keep function files grouped by type. Sync functions live in `syncs/`, action functions in `actions/`, and event functions in `on-events/`. This keeps the trigger model visible from the file path and matches how the CLI discovers function code. The root `index.ts` is the deployment entry point. It should import every function file you want Nango to compile and deploy, for example `import './github/syncs/github-issues';`. Files that are not imported from `index.ts` are not part of the deployed function set. The `.nango/` folder stores CLI-managed project state, including deployment tracking. Commit it so teammates and CI deploy from the same baseline. ``` nango-integrations/ ├── .nango/ # auto-managed state, commit it ├── .env # API keys per environment ├── .gitignore # keeps .env and dist untracked ├── index.ts # imports every function file ├── package.json └── / # one folder per integration, e.g. github ├── syncs/ │ └── github-issues.ts ├── actions/ │ └── github-create-issue.ts └── on-events/ └── post-connection-creation.ts ``` Keep dev mode running while you write code: ```bash nango dev ``` `nango dev` continuously type-checks and compiles your function files, surfacing errors immediately. Choose the right function type for your integration use case: | If you need to... | Use | Common trigger | Guide | | --- | --- | --- | --- | | Keep external API data fresh, replicate records, or reconcile changes | Sync function | Schedule, manual sync trigger, or external webhook reconciliation | [Sync functions](/guides/functions/syncs/sync-functions) | | Run an operation when your app, backend job, or agent asks for it | Action function | HTTP API, SDK, async action queue, or MCP/tool call | [Action functions](/guides/functions/action-functions) | | Process an external provider webhook inside Nango | Webhook function | External API webhook | [Webhook functions](/guides/functions/webhook-functions) | | React to Nango connection lifecycle events | Event function | Connection creation, validation, or deletion | [Event functions](/guides/functions/event-functions) | For shared primitives, use [Storage](/guides/functions/storage) for connection metadata and [Data validation](/guides/functions/data-validation) for function input, output, and provider response validation. Dry run a function against a real connection: ```bash nango dryrun '' -e dev ``` If the function reads connection metadata, pass test metadata: ```bash nango dryrun '' -e dev --metadata '{"accountRegion":"eu"}' ``` Deploy the function you changed: ```bash nango deploy --sync dev # or nango deploy --action dev # or nango deploy --integration dev ``` A bare `nango deploy` reconciles every function in the environment and can overwrite teammates' work on a shared collaborative environment. See [Engineering collaboration](/guides/platform/environments#engineering-collaboration). For function-type-specific commands, see the [Sync function guide](/guides/functions/syncs/sync-functions#test-and-deploy), [Action function guide](/guides/functions/action-functions#test-and-deploy), and [Testing](/guides/functions/testing). ### Option 3: Build with the Functions API Use the Functions API when an agent should create or change an integration without the user working in a codebase. The agent might be Codex, Claude Code, Cursor, or a product agent running inside your app or sandbox. This is useful for prototypes, onboarding flows, provider operations not covered by templates, and just-in-time integrations where users describe behavior in text and an agent publishes the matching function to Nango.
At a high level, the flow is: 1. Give the agent the integration ID, a connection ID for validation, the intended behavior, input and output shape, and any provider API context. 2. The agent generates a self-contained TypeScript function using the right function type. 3. Compile the source with `POST /functions/compile`. Send the TypeScript source as `code` and use compiler errors to improve it. 4. Start a dry run with `POST /functions/dryruns`. Send the integration ID, function type, source `code`, connection ID, and any action input, sync metadata, or checkpoint payload needed for the run. 5. Poll `GET /functions/dryruns/{id}` until the dry run reaches `success` or `failed`, then inspect the result, logs, records, or error. 6. Deploy it with `POST /functions/deployments`. Send `type: "function"`, the integration ID, function name, function type, and final source `code`. After deployment, your app can call or trigger the function like any other Nango function. Resolve these endpoints against `NANGO_SERVER_URL`, or `https://api.nango.dev` by default. Send `Authorization: Bearer ` and `Content-Type: application/json`. Scoped API keys need `environment:functions:compile` for compile, `environment:functions:dryrun` for dry run creation and polling, and `environment:deploy` for deployment. The Functions API is powerful, but it intentionally skips a local Git workflow. For production integrations that need code review, tests, ownership, and CI/CD, prefer [local development with the CLI](#build-locally-with-the-cli). ### Take ownership of a deployed function Pull the source of a function that is already deployed to an environment so you can manage it in your own repository: ```bash nango pull github list-repos --env dev ``` This works for any deployed function — a template you enabled from the dashboard, or code deployed through [`POST /functions/deployments`](/reference/backend/http-api/functions/deployments). The CLI writes the function's TypeScript source locally; from there, `nango deploy` redeploys it as a repo-managed function, so it lives in your Git workflow with code review, tests, and CI/CD. ## Related guides - [Function tool calling](/guides/functions/tool-calling) — expose action functions to agents, LLM SDKs, and MCP clients. - [Unified APIs](/guides/functions/unified-apis) — build provider-specific functions behind one stable model. - [Logging](/guides/functions/logging) — control custom function logs locally and in cloud environments. - [Storage](/guides/functions/storage) — store per-connection metadata and read it inside functions. - [Data validation](/guides/functions/data-validation) — validate function input, output, and provider API responses. - [Testing](/guides/functions/testing) — dry runs, mocks, and snapshot tests. - [Rate limits](/guides/functions/rate-limits) — handle provider `429` responses and long-running retries. - [CI/CD](/guides/functions/ci-cd) — deploy functions with your application pipeline. ## Sync function guide Source: https://nango.dev/docs/guides/functions/syncs/sync-functions.md Description: Build sync functions that keep external API data fresh and consumable by your app. Sync functions keep external API data fresh in your app. They run on a recurring cadence, write records to Nango's records cache, and let your app consume changes reliably. Use sync functions when your app needs to replicate a dataset from an external system for sync, indexing, RAG, reporting, or change-detection workflows. ## Create the function Add a file under the integration's `syncs/` folder: ```typescript crm/syncs/contacts.ts import { createSync } from 'nango'; import * as z from 'zod'; const Contact = z.object({ id: z.string(), email: z.string().email().optional(), updatedAt: z.string() }); const Metadata = z.object({ accountRegion: z.string().optional() }); const Checkpoint = z.object({ lastUpdatedAt: z.string() }); export default createSync({ description: 'Syncs contacts', version: '1.0.0', frequency: 'every hour', autoStart: true, models: { Contact }, metadata: Metadata, checkpoint: Checkpoint, exec: async (nango) => { const metadata = await nango.getMetadata(); const checkpoint = await nango.getCheckpoint(); const since = checkpoint?.lastUpdatedAt; for await (const page of nango.paginate({ endpoint: '/contacts', params: { ...(since ? { updated_after: since } : {}), ...(metadata?.accountRegion ? { region: metadata.accountRegion } : {}) }, paginate: { type: 'cursor', cursor_path_in_response: 'pagination.next_cursor', cursor_name_in_request: 'cursor', response_path: 'contacts', limit_name_in_request: 'limit', limit: 100 } })) { const contacts = page.map((contact) => ({ id: contact.id, email: contact.email, updatedAt: contact.updated_at })); await nango.batchSave(contacts, 'Contact'); const lastContact = contacts[contacts.length - 1]; if (lastContact) { await nango.saveCheckpoint({ lastUpdatedAt: lastContact.updatedAt }); } } } }); ``` Import the function from `index.ts`: ```typescript index.ts import './crm/syncs/contacts'; ``` Sync implementation quality matters. Suboptimal syncs can consume far more API calls, compute, memory, and storage than needed; they can also become expensive, crash, behave unreliably, or hit provider rate limits. Before deploying a sync to production, review the [Sync efficiency guide](/guides/functions/syncs/sync-efficiency). Before generating a sync, collect the integration ID, connection ID for testing, target records, provider pagination style, incremental filter, deletion strategy, required metadata, and expected sync frequency. Prefer incremental syncs when the provider supports them. Save records page by page, checkpoint after successful writes, and keep the record model limited to fields the app actually needs. ## Pass parameters with metadata Use [connection metadata](/guides/functions/storage#connection-metadata) for any value the sync needs on scheduled runs: - Customer configuration, such as selected folders, projects, accounts, regions, or custom field mappings. - Provider context discovered after authorization. - Feature flags or filters that change what the sync fetches. If the sync should not run before configuration exists, set `autoStart: false`, save the required metadata from your app, then start the schedule after configuration is complete. From your app, call the Node SDK [`nango.startSync()` method](/reference/backend/backend-sdk/node#start-schedule-for-syncs): ```ts await nango.startSync('crm', ['contacts'], ''); ``` Or call the [Start sync API](/reference/backend/http-api/sync/start) from any backend. Starting a sync schedule also triggers an immediate first run. ## Test and deploy Dry run the function against a real connection: ```bash nango dryrun contacts '' -e dev ``` If the sync reads connection metadata, pass test metadata to the dry run: ```bash nango dryrun contacts '' -e dev --metadata '{"accountRegion":"eu"}' ``` Or load metadata from a file: ```bash nango dryrun contacts '' -e dev --metadata @fixtures/metadata.json ``` Simulate resuming from a checkpoint: ```bash nango dryrun contacts '' -e dev --checkpoint '{"lastUpdatedAt":"2026-01-01T00:00:00.000Z"}' ``` Print local diagnostics for memory and CPU behavior: ```bash nango dryrun contacts '' -e dev --diagnostics ``` Deploy your functions: ```bash nango deploy ``` To deploy only this sync function: ```bash nango deploy --sync contacts dev ``` ## Start or trigger runs If `autoStart: true`, Nango starts the schedule for new connections automatically. Otherwise, start it after the connection is ready and required metadata is saved: ```typescript await nango.startSync('crm', ['contacts'], ''); ``` `nango.startSync()` is a [Node SDK method](/reference/backend/backend-sdk/node#start-schedule-for-syncs). Use the [Start sync API](/reference/backend/http-api/sync/start) if you are starting schedules from another backend language. Trigger a run immediately: ```typescript await nango.triggerSync('crm', ['contacts'], ''); ``` `nango.triggerSync()` is a [Node SDK method](/reference/backend/backend-sdk/node#trigger-syncs). Use the [Trigger sync API](/reference/backend/http-api/sync/trigger) for one-off sync runs from another backend language. ## Consume records Configure your app's Nango webhook URL in **Environment Settings > Webhook URLs**. After a run, Nango sends a sync webhook with the connection, integration, sync name, model, and change counts. Your app should then read records by cursor: ```typescript import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env.NANGO_SECRET_KEY! }); const result = await nango.listRecords({ providerConfigKey: 'crm', connectionId: '', model: 'Contact', cursor: '' }); ``` ```bash curl -G https://api.nango.dev/records \ --header 'Authorization: Bearer ' \ --header 'Provider-Config-Key: crm' \ --header 'Connection-Id: ' \ --data-urlencode 'model=Contact' \ --data-urlencode 'cursor=' ``` The consumer loop is: receive a sync webhook from Nango, look up the last cursor processed for that connection and model, fetch records with that cursor, process them, then store the cursor from the last record fetched. ## Two-way syncs You can implement two-way syncs by combining sync functions with [action functions](/guides/functions/action-functions): - Use a sync function to replicate external data into your app and keep your local state fresh. - Use action functions for writes back to the external API, such as creating, updating, or deleting records. - After a write action succeeds, update your local state optimistically or trigger the relevant sync so the records cache catches up with the external source of truth. This keeps the read path and write path explicit. The sync owns ongoing data freshness, checkpoints, record storage, and change delivery. Actions own user-initiated writes and provider-specific validation or side effects. For unified models, use the same schema for synced records and write action input where possible. This keeps your app from branching on each provider while still letting each provider implementation handle API-specific fields and edge cases. ## Deep dives - [Sync efficiency](/guides/functions/syncs/sync-efficiency) - [Checkpoints](/guides/functions/syncs/checkpoints) - [Records cache](/guides/functions/syncs/records-cache) - [Deletion detection](/guides/functions/syncs/deletion-detection) - [Real-time syncs](/guides/functions/syncs/realtime-syncs) - [Sync partitioning](/guides/functions/syncs/sync-partitioning) ## Sync efficiency Source: https://nango.dev/docs/guides/functions/syncs/sync-efficiency.md Description: Build sync functions that replicate external datasets without wasting API calls, memory, or compute. Sync functions are often used to replicate external datasets into your app or to notify your app when external data changes. Efficient syncs fetch only what changed, save records in batches, and let your app consume changes promptly. ## Prefer incremental syncs An **incremental sync** fetches only records that changed since the previous run. The function stores progress in a [checkpoint](/guides/functions/syncs/checkpoints), then passes that progress back to the external API on the next run. A **full sync** fetches the whole dataset every run, then saves or compares the complete result. Incremental syncs are usually an order of magnitude more efficient than full syncs. They reduce provider API calls, function runtime, memory usage, Nango compute, records churn, and downstream writes in your app. Full syncs can be acceptable for small datasets or low-frequency jobs. For large datasets, they can become plainly unrealistic: expensive to run, slow to finish, more likely to crash, more likely to hit provider rate limits, and less reliable when a run needs to process thousands or millions of records every time. | Approach | Best for | Tradeoff | | --- | --- | --- | | Incremental sync | Large datasets, frequent polling, APIs with `updated_after`, cursor, or sequence filters | Requires checkpoint logic and provider support | | Full refresh | Small datasets or APIs without incremental filters | Simpler, but uses more API calls, memory, and compute as data grows | This choice is constrained by the external API shape. To benefit from incremental syncing, the provider usually needs a parameter such as `updated_at`, `updated_since`, `modified_after`, a cursor, a sequence ID, or an event stream that lets you ask for only changed records. The hardest case is a large dataset where the API does not support incremental filters. Often, that happens because the provider expects you to use webhooks instead. In Nango, use [webhook functions](/guides/functions/webhook-functions), [webhook forwarding](/guides/platform/webhook-forwarding), or [real-time syncs](/guides/functions/syncs/realtime-syncs) to process provider events and avoid polling the entire dataset. The risky combination is: **large dataset + no incremental API + high freshness requirement**. If you need to run a full sync frequently to keep data fresh, the design is usually unrealistic in terms of cost, runtime, reliability, and rate limits. Look for a webhook strategy, reduce freshness requirements, partition the dataset, or narrow the scope of synced data. ## Save page by page Do not accumulate the full dataset in memory before calling `batchSave()`. Save each page as soon as it is mapped, then checkpoint after the save succeeds. ```typescript const checkpoint = await nango.getCheckpoint(); for await (const page of nango.paginate({ endpoint: '/contacts', params: checkpoint ? { updated_after: checkpoint.lastModifiedISO } : {}, paginate: { type: 'cursor', cursor_path_in_response: '', cursor_name_in_request: 'cursor', response_path: '', limit_name_in_request: 'limit', limit: 100 } })) { const contacts = page.map(mapContact); await nango.batchSave(contacts, 'Contact'); const lastContact = contacts[contacts.length - 1]; if (lastContact) { await nango.saveCheckpoint({ lastModifiedISO: lastContact.updatedAt }); } } ``` This pattern keeps memory bounded and lets the next run resume from the last saved page if the function stops midway. ## Keep records small Only fetch and save fields your app needs. Smaller records reduce external API usage, Nango compute, cache size, downstream processing, and cost. Keep records steady too. During full syncs, Nango can process unchanged records faster when the saved record payload stays identical between runs. Avoid adding fields that change on every fetch even when the underlying external record did not change, such as `lastFetchedAt`, request timestamps, random IDs, or transient API metadata. Volatile fields make the record look changed every run, which forces extra processing and downstream updates. ## Filter unnecessary data early Filter data as early as possible, either by using filters provided by the external API or by discarding records before calling `batchSave()`. This keeps the external system as the source of truth while reducing provider API usage, Nango compute, cache size, and downstream processing. Prefer provider-side filters such as selected folders, projects, accounts, regions, statuses, or `updated_after` parameters. If the provider cannot filter server-side, map and filter each page before saving so Nango and your app only process records that matter. Use [connection metadata](/guides/functions/storage#connection-metadata) for per-customer filters. For example, store selected project IDs, folder IDs, regions, pipelines, or custom field mappings on the connection, then read that metadata inside the sync and pass it to the provider API. ```typescript const metadata = await nango.getMetadata(); const checkpoint = await nango.getCheckpoint(); for await (const page of nango.paginate({ endpoint: '/tasks', params: { project_id: metadata?.projectId, updated_after: checkpoint?.lastModifiedISO }, paginate: { type: 'cursor', cursor_path_in_response: 'next_cursor', cursor_name_in_request: 'cursor', response_path: 'tasks' } })) { const tasks = page.filter((task) => !metadata?.status || task.status === metadata.status).map(mapTask); await nango.batchSave(tasks, 'Task'); } ``` If you need additional data later, update the sync function and trigger a run with `reset: true` to backfill historical data with the new shape or broader filter. ## Consume changes promptly Nango's records cache is a delivery mechanism, not your app's long-term data store. Configure [webhooks from Nango](/guides/platform/webhooks-from-nango), fetch changed records from the [records API](/reference/backend/http-api/sync/records-list), store them in your system, and persist the last processed cursor. When reading records from Nango, keep page sizes modest. A page size around `100` records is a good default: it keeps payloads quick to transfer, reduces memory spikes in your worker, and makes retries less expensive when a downstream write fails. Read [Records cache](/guides/functions/syncs/records-cache) for cursor behavior, payload shape, and retention details. ## Related guides - [Functions guide](/guides/functions/syncs/sync-functions) - [Checkpoints](/guides/functions/syncs/checkpoints) - [Records cache](/guides/functions/syncs/records-cache) - [Real-time syncs](/guides/functions/syncs/realtime-syncs) - [Sync partitioning](/guides/functions/syncs/sync-partitioning) ## Checkpoints Source: https://nango.dev/docs/guides/functions/syncs/checkpoints.md Description: Save progress mid-run, resume after failures, and turn full syncs into incremental ones. Checkpoints let sync functions save their progress and resume from where they left off. This enables a function to fetch only new or changed data instead of re-fetching everything on each run. Checkpoints are what make sync functions **resilient to failures** and enable **incremental syncing**, the recommended approach for any non-trivial dataset. Define a `checkpoint` schema in your sync function to enable checkpoint support. If you are migrating from `nango.lastSyncDate`, see the [migration guide](/guides/platform/migrations/migrate-to-checkpoints). ## Why checkpointing matters Without checkpoints, every sync run fetches the entire dataset from the external API. This works for small datasets, but quickly becomes unsustainable as data grows. Consider a Salesforce account with 500,000 contacts syncing every 15 minutes: without checkpoints, each run re-fetches all 500,000 records even if only a handful changed — consuming orders of magnitude more API calls, compute time, and memory than necessary. Checkpoints solve this by tracking how far your sync has progressed. The benefits compound: - **Performance** — Only fetch what changed. A 15-minute incremental sync processes minutes of changes, not years of history. - **Resilience** — If a sync fails mid-execution, the next run resumes from the last checkpoint instead of restarting from scratch. - **Lower costs** — Less compute time on Nango, fewer API calls against the external API's rate limits, and lower bandwidth consumption. - **Higher freshness** — Faster syncs mean you can run them more frequently, keeping your data closer to real-time. ## Function interruption and resumption Nango reserves the right to cap the duration of a function execution. When an execution reaches the limit, Nango interrupts it gracefully and schedules the next execution to start immediately. Checkpoints are what make this seamless: the next execution should call `getCheckpoint()` and resumes from where the previous one stopped. **Without checkpoints, each execution starts from scratch**, meaning any function that takes longer than the execution cap will loop indefinitely without ever completing. If your sync processes a large dataset, you must call `saveCheckpoint()` after every `batchSave`. A sync without checkpoints that exceeds the execution duration will restart from the beginning on every run and never finish. The interrupt-and-resume flow: 1. Sync runs, processes records, saves a checkpoint after each page. 2. Nango interrupts the sync gracefully when the execution cap is reached (ie: after the current `saveCheckpoint()` call completes). 3. The next execution starts immediately, calls `getCheckpoint()`, and continues from that point. 4. This repeats until the sync finishes naturally. ## How checkpoints work A checkpoint is a small payload, defined by a schema you control, that your sync saves after processing each batch of data. On the next execution, the sync reads the checkpoint to know where it left off. The typical flow is: 1. Read the current checkpoint with `nango.getCheckpoint()` (returns `null` on first run). 2. Fetch a page of data from the external API, starting from the checkpoint. 3. Save the records to Nango's cache with `nango.batchSave()`. 4. Save a new checkpoint with `nango.saveCheckpoint()`. 5. Repeat until there is no more data. ## Defining a checkpoint schema You define your checkpoint schema in the `createSync()` declaration using [Zod](https://zod.dev), just like your data models. The schema describes the shape of the progress marker your sync will save. Most commonly, the checkpoint is a timestamp indicating how far you've synced: ```ts import { createSync } from 'nango'; import * as z from 'zod'; export default createSync({ description: 'Sync contacts from Salesforce', frequency: 'every hour', checkpoint: z.object({ lastModifiedISO: z.string(), }), models: { Contact: ContactSchema }, exec: async (nango) => { // Implementation below }, }); ``` The checkpoint payload can be anything the external API gives you to track progress: a timestamp, a cursor, a page token, or any combination. You control the schema. ```ts // Timestamp-based checkpoint checkpoint: z.object({ lastModifiedISO: z.string() }) // Cursor-based checkpoint checkpoint: z.object({ nextCursor: z.string() }) // Composite checkpoint checkpoint: z.object({ lastModifiedISO: z.string(), pageToken: z.string().optional() }) ``` ## Implementing an incremental sync with checkpoints Here is a complete example syncing Salesforce contacts incrementally: ```ts import { createSync } from 'nango'; import * as z from 'zod'; const ContactSchema = z.object({ id: z.string(), first_name: z.string(), last_name: z.string(), email: z.string(), last_modified_date: z.string(), }); export default createSync({ description: 'Sync contacts from Salesforce', frequency: 'every hour', checkpoint: z.object({ lastModifiedISO: z.string(), }), models: { Contact: ContactSchema }, exec: async (nango) => { // 1. Read the checkpoint (null on first run) const checkpoint = await nango.getCheckpoint(); let query = 'SELECT Id, FirstName, LastName, Email, LastModifiedDate FROM Contact'; if (checkpoint) { // Only fetch records modified since the last checkpoint query += ` WHERE LastModifiedDate > ${checkpoint.lastModifiedISO}`; } query += ' ORDER BY LastModifiedDate ASC'; for await (const records of nango.paginate({ endpoint: '/services/data/v53.0/query', params: { q: query }, paginate: { type: 'link', response_path: 'records', link_path_in_response_body: 'nextRecordsUrl', }, })) { const contacts = mapContacts(records); // 2. Save records to the cache await nango.batchSave(contacts, 'Contact'); // 3. Save checkpoint after each page const lastContact = contacts[contacts.length - 1]; await nango.saveCheckpoint({ lastModifiedISO: lastContact.last_modified_date, }); } }, }); ``` The key pattern: **save records and checkpoint after every page**. This ensures that if the sync fails on page 50 of 100, the next run resumes from page 50 — not page 1. ## The first sync run Even with checkpoints, the very first execution has no previous progress to resume from — `getCheckpoint()` returns `null`. This initial run fetches the entire historical dataset and is inherently more resource-intensive than subsequent runs. One strategy to manage this is to limit the backfill window. For example, if you are syncing a Notion workspace, you might only fetch pages modified in the last three months, assuming older pages are less relevant: ```ts const checkpoint = await nango.getCheckpoint(); const since = checkpoint?.lastModifiedISO ?? threeMonthsAgo(); ``` After the initial run completes, subsequent runs are fast and incremental — only processing changes since the last checkpoint. ## Implementing a full sync with checkpoints Not every sync can be incremental. Some APIs don't support filtering by modification date, so you must fetch all records on every run. Such syncs can still benefit from checkpoints for resilience: if a run is interrupted mid-dataset, the next run resumes from the last page instead of starting over. Call `clearCheckpoint()` at the end of a successful run so the next scheduled run starts from the first page. ```ts export default createSync({ description: 'Sync all contacts', frequency: 'every hour', checkpoint: z.object({ nextCursor: z.string(), }), models: { Contact: ContactSchema }, exec: async (nango) => { // Resume from the last page if this run was interrupted const checkpoint = await nango.getCheckpoint(); let nextCursor: string | undefined = checkpoint?.nextCursor; do { const res = await nango.get({ endpoint: '/contacts', params: { cursor: nextCursor }, }); const contacts = mapContacts(res.data.records); await nango.batchSave(contacts, 'Contact'); nextCursor = res.data.nextCursor; if (nextCursor) { await nango.saveCheckpoint({ nextCursor }); } } while (nextCursor); // All pages fetched, clear checkpoint so the next run starts from the first page await nango.clearCheckpoint(); }, }); ``` ## When to sync without checkpoints For small datasets (e.g., a list of Slack users for an organization with fewer than 100 employees), a sync without checkpoint can be perfectly fine. As datasets grow, full syncs become unscalable — taking longer to run, triggering rate limits, and consuming more compute and memory. If your dataset has more than a few thousand records and the API supports pagination and/or filtering by date or cursor, use checkpoints. `deleteRecordsFromPreviousExecutions()` is incompatible with checkpoints because it requires comparing the full dataset between consecutive runs. Use `trackDeletesStart`/`trackDeletesEnd` instead — see [Deletion detection](/guides/functions/syncs/deletion-detection). ## Checkpoints vs. cursors Both checkpoints and [cursors](/guides/functions/syncs/records-cache#cursors-and-sync-progress) are markers of sync progress, but they track different relationships: | | Checkpoint | Cursor | | --- | --- | --- | | **Direction** | Nango → external API | Your app → Nango | | **What it tracks** | How far Nango has synced from the external API | How far your app has fetched from Nango's records cache | | **Who manages it** | Your sync function, via `saveCheckpoint()` | Your application, via the `cursor` parameter on [GET /records](/reference/backend/http-api/sync/records-list) | | **Typical payload** | A timestamp or API cursor from the external system | An opaque string returned by Nango | Both are important: checkpoints keep your sync efficient, and cursors keep your application's data consumption efficient. ## Re-syncing: resetting checkpoints and the cache When you [trigger a sync](/reference/backend/http-api/sync/trigger) manually (from the UI or API), you can control whether to preserve or reset progress: | Option | Default | What it does | | --- | --- | --- | | `reset` | `false` | When `true`, clears the checkpoint and re-fetches the full dataset from the external API. The cache is preserved, so Nango can still distinguish new records from updated ones. | | `emptyCache` | `false` | When `true`, deletes all cached records before the sync runs. Must be used with `reset: true`. The sync starts completely fresh. | **Leave both off** for a standard incremental sync. **Use `reset: true`** when you want to re-fetch everything from the external API but preserve the cache for accurate change detection. This is useful when you suspect data may have been missed. **Use `reset: true` with `emptyCache: true`** when you need to start from scratch — for example, after a breaking change to your data model. Resetting the checkpoint triggers a full re-sync from the external API, which takes longer and may incur higher compute costs. Clearing the cache additionally means every record will be reported as `ADDED`, your previously persisted [cursors](/guides/functions/syncs/records-cache#cursors-and-sync-progress) become invalid, and you will need to reprocess the entire dataset on your side. ```ts // Preserve checkpoint (standard incremental sync) await nango.triggerSync('', ['contacts'], ''); // Reset checkpoint, re-fetch everything, preserve cache for change detection await nango.triggerSync('', ['contacts'], '', { reset: true }); // Full reset — re-fetch everything and clear the cache await nango.triggerSync('', ['contacts'], '', { reset: true, emptyCache: true }); ``` ## Observability Checkpoints are exposed in several places to help you monitor sync progress: - **Sync webhooks** — The [sync completion webhook](/guides/platform/webhooks-from-nango) includes checkpoint information, so your application knows how far the sync has progressed. - **Sync status API** — The [GET /sync/status](/reference/backend/http-api/sync/status) endpoint returns the current checkpoint for each sync, letting you check progress programmatically. - **Logs** — Checkpoint-related details are visible in the Nango dashboard logs. *(We are actively improving checkpoint visibility in the dashboard.)* ## Testing checkpoints locally When developing locally with the Nango CLI, you can pass a checkpoint value to `dryrun` to simulate resuming from a previous run: ```bash nango dryrun salesforce-contacts '' --checkpoint '{"lastModifiedISO": "2024-01-15T00:00:00Z"}' ``` This lets you test that your sync correctly resumes from a checkpoint without needing to run a full initial sync first. See the [testing guide](/guides/functions/testing) for more details, including how to write unit tests for checkpoint logic. **Migrating from `nango.lastSyncDate`?** Checkpoints replace it with a schema you control, mid-run granularity, and failure-resumption. Follow the [migration guide](/guides/platform/migrations/migrate-to-checkpoints) for step-by-step instructions. ## Reference | Function | Description | | --- | --- | | [`nango.getCheckpoint()`](/reference/functions/functions-sdk#getcheckpoint) | Retrieves the current checkpoint. Returns `null` on first run or after a reset. | | [`nango.saveCheckpoint()`](/reference/functions/functions-sdk#savecheckpoint) | Saves progress. Call after each batch of data. | | [`nango.clearCheckpoint()`](/reference/functions/functions-sdk#clearcheckpoint) | Clears the checkpoint. Use at the end of a full sync so the next run starts from the first page. Checkpoints are also automatically cleared when triggering a sync with `reset: true`. | ## Records cache Source: https://nango.dev/docs/guides/functions/syncs/records-cache.md Description: How Nango stores synced records, detects changes, and lets your app fetch deltas via cursors. Sync functions replicate records from an external API to your system continuously. To do this reliably, Nango uses a **records cache** — an intermediate store that sits between the external API and your application. Every time you call [`nango.batchSave()`](/reference/functions/functions-sdk#save-records) or [`nango.batchDelete()`](/reference/functions/functions-sdk#delete-records) inside a sync function, you are writing to this cache. Your application then reads from the cache using the [GET /records](/reference/backend/http-api/sync/records-list) endpoint or the [Node SDK](/reference/backend/backend-sdk/node#get-records). ## What the cache does The records cache fulfils three roles: **Change detection** — By tracking every record that has been synced, Nango can tell you which records are new, which have been updated, and which have been deleted. Your application only needs to fetch the delta, reducing bandwidth and processing on your side. **Reliable data availability** — Fetching from external APIs is inherently unreliable: rate limits, timeouts, and transient errors are common. The cache decouples this unreliable step from your application. Once data lands in the cache, you can fetch it quickly and reliably. **Observability** — The cache gives you visibility into the state of synced data directly from the Nango dashboard and API, including record counts, last sync times, and change history. ## How records are identified and compared Each record in the cache is uniquely identified by two things: - **Record ID** — The `id` field you set on each record in your sync function. This should match the unique identifier of the record in the external system (e.g. the external API's primary key). - **Payload hash** — Nango computes a hash of the full record payload. When a record with the same ID is saved again, Nango compares hashes to determine whether the record has actually changed. If the ID already exists and the hash is identical, the record is considered unchanged and no update is emitted. If the hash differs, Nango marks it as updated. Be careful with fields that change on every fetch but don't represent a meaningful change to the record, such as a `fetched_at` timestamp. Including such fields in the payload will cause Nango to report spurious updates on every sync run. If possible, exclude or normalize these fields before calling `batchSave()`. ## How records are stored The cache only keeps the **latest version** of each record — there is no versioning or history of payloads. When you call `batchSave()` with an existing ID, the previous payload is overwritten. When you call `batchDelete()`, you only need to pass the record's `id`. This does **not** remove the record from the cache. Instead, it marks the record as deleted (a soft delete), so your application can react to the deletion event. The last-known payload is preserved. ```ts // Saving records to the cache await nango.batchSave(contacts, 'Contact'); // Marking records as deleted (soft delete) const toDelete = [{ id: 'record-123' }, { id: 'record-456' }]; await nango.batchDelete(toDelete, 'Contact'); ``` ## Fetching records: the change stream The [GET /records](/reference/backend/http-api/sync/records-list) endpoint returns a **chronologically ordered stream of record changes**. Each entry in the stream includes: - The full record payload - Metadata indicating whether the record was `ADDED`, `UPDATED`, or `DELETED` - A cursor for tracking your sync progress ```json { "records": [ { "id": "contact-1", "name": "Alice", "_nango_metadata": { "first_seen_at": "2024-01-15T10:00:00.000Z", "last_modified_at": "2024-01-15T10:00:00.000Z", "last_action": "ADDED", "deleted_at": null, "cursor": "MjAyNC0wMS0xNVQxMDowMDowMC..." } }, { "id": "contact-2", "name": "Bob (updated)", "_nango_metadata": { "first_seen_at": "2024-01-10T08:00:00.000Z", "last_modified_at": "2024-01-15T10:05:00.000Z", "last_action": "UPDATED", "deleted_at": null, "cursor": "MjAyNC0wMS0xNVQxMDowNTowMC..." } } ], "next_cursor": "MjAyNC0wMS0xNVQxMDowNTowMC..." } ``` You can filter the stream to only return records with a specific `last_action` (`added`, `updated`, or `deleted`) using the `filter` query parameter. ## Cursors and sync progress Every record change in the cache has a **cursor** attached to it. Cursors are opaque, ordered strings that let you: 1. **Track how far you've synced** — After fetching records, persist the cursor of the last record you processed. On the next fetch, pass it back to only receive changes that happened after that point. 2. **Paginate through large result sets** — The same cursor is used for pagination when there are more records than the page `limit`. You must persist the cursor on your side for each combination of **connection** and **sync function**. This is how you keep track of your sync progress and avoid reprocessing records. ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: '' }); // Fetch only records that changed since your last cursor const result = await nango.listRecords({ providerConfigKey: '', connectionId: '', model: 'Contact', cursor: '' }); // Process records... // Persist the cursor of the last record for next time if (result.records.length > 0) { const lastCursor = result.records[result.records.length - 1]._nango_metadata.cursor; await saveToDatabase(connectionId, syncName, lastCursor); } ``` ```bash curl -G https://api.nango.dev/records \ --header 'Authorization: Bearer ' \ --header 'Provider-Config-Key: ' \ --header 'Connection-Id: ' \ --data-urlencode 'model=Contact' \ --data-urlencode 'cursor=' ``` ## Re-syncing: preserve vs. clear the cache See [Re-syncing: resetting checkpoints and the cache](/guides/functions/syncs/checkpoints#re-syncing-resetting-checkpoints-and-the-cache) in the Checkpoints guide for the `reset` / `emptyCache` options and their implications. ## Cache summary | Concept | Details | | --- | --- | | **Writing to the cache** | `nango.batchSave()` and `nango.batchDelete()` in sync functions | | **Reading from the cache** | `GET /records` endpoint or `nango.listRecords()` SDK method | | **Record identity** | Determined by the `id` field you set on each record | | **Change detection** | Based on comparing payload hashes for the same `id` | | **Versioning** | None — only the latest payload is kept | | **Deletions** | Soft delete — record is marked as deleted, payload is preserved | | **Cursor** | Opaque ordered string for tracking sync progress and pagination | | **Payload TTL** | 30 days without update → payload pruned | | **Full record TTL** | 60 days without sync execution → all records hard-deleted | ## Related guides - [Sync functions](/guides/functions/syncs/sync-functions) - write records into the cache. - [Sync efficiency](/guides/functions/syncs/sync-efficiency) - keep records small and consume changes promptly. - [Deletion detection](/guides/functions/syncs/deletion-detection) - mark records deleted in the cache. - [Get records API](/reference/backend/http-api/sync/records-list) - fetch changed records from your app. ## Deletion detection Source: https://nango.dev/docs/guides/functions/syncs/deletion-detection.md Description: Surface deletes from the external API — even when the API itself doesn't expose them cleanly. Sometimes you need to know when a record you are syncing has been deleted in the external system. Deletion detection works differently depending on whether your sync function is incremental or full — pick the strategy that matches your sync approach. For incremental syncs, you must actively tell Nango which IDs have been removed. For full syncs (which fetch all records on every run), Nango can compute deletes for you automatically. ## Detecting deletes in incremental syncs When your sync only fetches changed data since the previous run, Nango has no built-in way to know which records disappeared on the provider side. You must actively tell Nango which IDs have been removed by calling `nango.batchDelete()` ([full reference](/reference/functions/functions-sdk#delete-records)) inside the sync function. ### When can you use this? You can use `nango.batchDelete()` if the external API supports one of the following: - A dedicated "recently deleted" endpoint (e.g. `GET /entities/deleted?since=...`) - The ability to filter or sort by a deletion timestamp - The ability to filter or sort by last-modified timestamp and records include a flag like `is_deleted`, `archived`, etc. If none of these are available, you cannot detect deletes in an incremental sync. You'll either need to switch to a full sync or skip deletion detection. ### Example ```ts import { createSync } from 'nango'; import * as z from 'zod'; const AccountSchema = z.object({ id: z.string(), name: z.string() }); export default createSync({ description: 'Sync Accounts incrementally and handle deletions', frequency: 'every 2 hours', endpoints: [{ method: 'GET', path: '/accounts', group: 'Accounts' }], models: { Account: AccountSchema }, checkpoint: z.object({ lastSyncedISO: z.string(), }), exec: async (nango) => { const checkpoint = await nango.getCheckpoint(); const now = new Date().toISOString(); // (1) Fetch newly created / updated accounts const res = await nango.get({ endpoint: '/accounts', params: { ...(checkpoint && { since: checkpoint.lastSyncedISO }) } }); await nango.batchSave(res.data, 'Account'); // (2) Fetch deletions since the last run (if this is not the first run) if (checkpoint) { const deletedRes = await nango.get({ endpoint: '/accounts/deleted', params: { since: checkpoint.lastSyncedISO } }); // (3) Tell Nango which IDs have been deleted in the external system const toDelete = deletedRes.data.map((row: any) => ({ id: row.id })); if (toDelete.length) { await nango.batchDelete(toDelete, 'Account'); } } // (4) Save checkpoint for next run await nango.saveCheckpoint({ lastSyncedISO: now }); } }); ``` ## Detecting deletes in full syncs Sync functions that fetch all records on every run can automatically detect deletions. Nango detects removals by computing the diff between what existed before `trackDeletesStart` and what was saved between `trackDeletesStart` and `trackDeletesEnd` ([full reference](/reference/functions/functions-sdk#detect-deletions-automatically)). This works whether your full sync uses checkpoints for resilience or not. If it does, `trackDeletesStart` should be called before fetching any data and `trackDeletesEnd` after all records are saved and the checkpoint is cleared. ### Example ```ts import { createSync } from 'nango'; import * as z from 'zod'; const TicketSchema = z.object({ id: z.string(), subject: z.string(), status: z.string() }); export default createSync({ description: 'Fetch all help-desk tickets', frequency: 'every day', endpoints: [{ method: 'GET', path: '/tickets', group: 'Tickets' }], models: { Ticket: TicketSchema }, exec: async (nango) => { // Mark the start of deletion tracking await nango.trackDeletesStart('Ticket'); const tickets = nango.paginate<{ id: string; subject: string; status: string }>({ endpoint: '/tickets', paginate: { type: 'cursor', cursor_path_in_response: 'next', cursor_name_in_request: 'cursor', response_path: 'tickets' } }); for await (const page of tickets) { await nango.batchSave(page, 'Ticket'); } // Detect and mark deleted records await nango.trackDeletesEnd('Ticket'); } }); ``` ### How the algorithm works 1. When `trackDeletesStart` is called, Nango marks the beginning of the deletion tracking window for the model. 2. Records saved with `batchSave` between `trackDeletesStart` and `trackDeletesEnd` are tracked. 3. When `trackDeletesEnd` is called, Nango compares what existed before `trackDeletesStart` with what was saved in the window. 4. Any records missing from the new dataset are marked as deleted (soft delete). They remain accessible from the Nango cache, but with `record._metadata.deleted === true`. **Be careful with exception handling when using** `trackDeletesStart`/`trackDeletesEnd` Nango only performs deletion detection (the "diff") if a sync run completes successfully without any uncaught exceptions. Exception handling is critical: - If your sync doesn't fetch the full dataset between the two calls (e.g. you catch and swallow an exception), Nango will attempt the diff on an incomplete dataset. - This leads to false positives, where valid records are mistakenly considered deleted. **What You Should Do** If a failure prevents full data retrieval, make sure the sync run fails and `trackDeletesEnd` is not being called: - Let exceptions bubble up and interrupt the run. - If you're using `try/catch`, re-throw exceptions that indicate incomplete data. **How to use** `trackDeletesStart`/`trackDeletesEnd` **safely** If some records are incorrectly marked as deleted, you can trigger a full resync (via the UI or API) to restore the correct data state. We strongly recommend not performing irreversible destructive actions (like hard-deleting records in your system) based solely on deletions reported by Nango. A full resync should always be able to recover from issues. ## Troubleshooting deletion detection issues | Symptom | Likely cause | | --- | --- | | Records that still exist in the source API are shown as `deleted` in Nango | Sync didn't save all records (silent failures) between `trackDeletesStart` and `trackDeletesEnd` | | You never see deleted records | Check if deletion detection is implemented for the sync. | ## Related guides - [Sync functions](/guides/functions/syncs/sync-functions) - implement full and incremental syncs. - [Records cache](/guides/functions/syncs/records-cache) - understand how deleted records are exposed to your app. - [Sync efficiency](/guides/functions/syncs/sync-efficiency) - reduce API work during delete detection. - [Functions SDK reference](/reference/functions/functions-sdk#delete-records) - deletion helper methods. ## Real-time syncs with webhooks Source: https://nango.dev/docs/guides/functions/syncs/realtime-syncs.md Description: Combine webhooks from external APIs with periodic polling for a reliable, real-time stream of changes. Nango supports real-time syncs using [webhook functions](/guides/functions/webhook-functions). You can rely entirely on webhooks or combine them with polling to ensure you never miss data. ## What is a real-time sync Real-time sync involves receiving webhooks from external APIs and processing them inside a sync function. By default, sync functions poll data at a set frequency, but they can also be configured to handle webhooks for real-time updates. When processed in a sync: - The webhook triggers the execution of a Nango sync script - The script extracts data from the webhook and optionally fetches additional data from the external API - The modified data is stored in the Nango cache Whenever the cache is updated, Nango sends a [sync completion webhook](/guides/platform/webhooks-from-nango#sync-webhooks) to notify your application of new data. This entire process happens in real-time. ### Webhooks & periodic polling Webhooks can fail due to external API outages or unordered events. Periodic polling ensures data consistency by reconciling any missed updates. If you use webhooks for real-time syncs, we recommend always combining them with a polling sync function. ### Near real-time syncing As an alternative to real-time syncing, consider **near**-real-time syncing by polling at a higher frequency, which is simpler. Sync functions can run as frequently as every 30 seconds. This is more resource intensive, but can be a good compromise if you are only syncing data for a handful of [Connections](/guides/auth/auth-guide#overview). ## Implement a real-time sync ### Step 1 — Setup prerequisites To create a real-time sync, you need: - Webhooks from the external API enabled. Follow [Webhook functions](/guides/functions/webhook-functions). - A sync function for the object you want to sync. See [Sync functions](/guides/functions/syncs/sync-functions). ### Step 2 — Enable webhooks processing To enable the real-time sync, define `webhookSubscriptions` and `onWebhook` in your sync function: ```typescript export default createSync({ exec: async (nango) => { // Use for periodic polling. }, webhookSubscriptions: ['contact.propertyChange'], // Webhook event type to listen to // Webhook handler onWebhook: async (nango, payload) => { if (payload.subscriptionType === 'contact.propertyChange') { const updatedObject = { id: payload.objectId, [payload.propertyName]: payload.propertyValue }; // Use nango.batchSave() or nango.batchUpdate() to save/update records. } } }); ``` To check if a sync supports webhooks, navigate to the _Integrations_ tab > select an integration > _Endpoints_ sub-tab > check the function settings for webhook subscriptions. ## Sync concurrency considerations When a polling run and a webhook race to update the same record, the later write wins by default — which can clobber a fresher webhook update with stale polled data. To prevent that, set a merging strategy at the start of your function: ```typescript export default createSync({ exec: async (nango) => { // Don't overwrite records that were modified after this batch was fetched await nango.setMergingStrategy({ strategy: 'ignore_if_modified_after' }, 'Contact'); const contacts: Contact[] = []; await nango.batchSave(contacts, 'Contact'); } }); ``` Two strategies are available: - **`override`** (default) — applies updates regardless of last modification time. May overwrite real-time webhook updates with older polled data. - **`ignore_if_modified_after`** — preserves records that were modified after the current batch was fetched. **Recommended** when you combine polling with webhooks. Run `batchSave` / `batchUpdate` / `batchDelete` calls sequentially (not in parallel), and persist data promptly after fetching — concurrent batch operations can race against each other. ## Related guides - [Webhook functions](/guides/functions/webhook-functions) - process external API webhooks in Nango. - [Webhook forwarding](/guides/platform/webhook-forwarding) - forward provider webhooks to your app. - [Sync functions](/guides/functions/syncs/sync-functions) - keep a polling sync as reconciliation. - [Webhooks from Nango](/guides/platform/webhooks-from-nango) - consume sync completion notifications. ## Sync partitioning Source: https://nango.dev/docs/guides/functions/syncs/sync-partitioning.md Description: Spawn multiple independent sync processes inside a single connection — one per mailbox, drive folder, Slack channel, repo, or any other partition. Sync variants let you spawn multiple independent sync processes **inside a single connection** — one per mailbox, file folder, Slack channel, repo, or any other sub-resource the end-user has access to. Each variant runs on its own schedule, with its own checkpoint and records cache, so a slow partition doesn't block a fast one. Nango is already multi-tenant by default — every sync function executes in the context of an end-user [Connection](/guides/auth/auth-guide#overview), and runs independently for each connection. Variants are for the next level down: fan-out *within* one connection. ## Overview Traditionally, a sync function is defined once and executed once per connection. With variants, you can create multiple instances of the same function that share the base configuration but run independently. Each variant has its own execution schedule, checkpoint, and data storage. Key benefits: - **Parallel execution** — variants run independently, so a slow mailbox or folder doesn't block the others. - **Custom filtering** — each variant can target a different sub-resource (e.g., a specific Gmail label, a specific Drive folder). - **Per-partition state** — each variant has its own checkpoint and cache, so failures and re-syncs are isolated. - **Dynamic scale** — create or delete variants programmatically as the end-user adds/removes sub-resources. ## How sync variants work - By default, a sync is associated with a base variant. - You can create additional sync variants programmatically using the API or SDK. - Each variant is treated as a separate sync in the Nango UI. - Sync webhooks include a variant field to identify which variant was executed. - External webhooks (e.g., from Airtable) are always processed by the base variant. - If a sync variant needs additional context (e.g., filtering parameters), store/retrieve it using the connection metadata, namespaced with the variant ID. ## Creating a sync variant Each sync can have a maximum of **100 variants** per connection. For heavier workloads, reach out to increase this limit. To create a sync variant, use the Nango API or SDK. The variant must have a unique name and cannot be "base" (reserved). ```ts // Create a sync variant await nango.createSyncVariant({ provider_config_key: 'my-integration', connection_id: 'customer-123', name: 'sync-orders', variant: 'high-value-orders' }); ``` ```bash curl --request POST \ --url https://api.nango.dev/sync/sync-orders/variant/high-value-orders \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "provider_config_key": "my-integration", "connection_id": "customer-123" }' ``` This creates a new variant named `high-value-orders` for the `sync-orders` sync. It will run separately from the base variant. ## Running & managing sync variants Once created, a variant can be managed just like a regular sync. All sync operations (triggering, pausing, resuming, etc.) support the variant parameter. Check out the [HTTP API reference](/reference/backend/http-api/sync/trigger) or [Node SDK reference](/reference/backend/backend-sdk/node#trigger-syncs) for details. ## Accessing the variant in a sync script When a sync script runs, the variant name is available via `nango.variant`. You can use this to customize the behavior of your sync. ```ts example-partitioned-sync-script.ts export default createSync({ exec: async (nango) => { await nango.log(`Running sync with variant: ${nango.variant}`); // Use the variant name to namespace the metadata and/or customize API calls. const res = await nango.get({ endpoint: `/orders?filter=${nango.variant}` }); // Records are saved in the context of this specific variant. await nango.batchSave(res.data.orders, 'Order'); }, }); ``` ## Fetching data for a variant Use the `variant` query parameter when retrieving records from the Nango API. ```ts const records = await nango.listRecords({ providerConfigKey: 'my-integration', connectionId: 'customer-123', model: 'Order', variant: 'high-value-orders' }); ``` ```bash curl --request GET \ --url "https://api.nango.dev/records?model=Order&variant=high-value-orders" \ --header 'Authorization: Bearer ' ``` This fetches only the records for the `high-value-orders` variant. ## Storing variant-specific context If a sync variant requires custom parameters (e.g., a filtering threshold), store them in the connection metadata, namespaced by the variant ID. Storing metadata: ```ts await nango.setMetadata('my-integration', 'customer-123', { 'high-value-orders.threshold': 500 }); ``` ```bash curl --request POST \ --url https://api.nango.dev/metadata \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "connection_id": "customer-123", "metadata": { "high-value-orders.threshold": 500 } }' ``` Retrieving metadata in a sync script: ```ts example-partitioned-sync-script.ts const metadata = await nango.getMetadata(); const threshold = metadata[`${nango.variant}.threshold`] || 100; await nango.log(`Filtering orders above ${threshold}`); ``` Keep variants non-overlapping in data scope (unless intentional), store variant-specific settings in connection metadata rather than hardcoding them, and avoid creating one variant per row of data — variants are best for partitioning, not row-level fan-out. ## Related guides - [Sync functions](/guides/functions/syncs/sync-functions) - build the base sync before adding variants. - [Checkpoints](/guides/functions/syncs/checkpoints) - track progress per variant. - [Records cache](/guides/functions/syncs/records-cache) - fetch data for each variant. - [Create sync variant API](/reference/backend/http-api/sync/create-variant) - manage variants programmatically. ## Action functions Source: https://nango.dev/docs/guides/functions/action-functions.md Description: Build Action functions that your app, backend jobs, or agents can call on demand. Action functions run when your app, backend job, or agent explicitly calls them. In SDK and API names, this function type is often called an **action** because it receives an input and returns an output. Use action functions for reads, writes, multi-step provider workflows, unified API operations, and AI tools. ## Create the function Add a file under the integration's `actions/` folder: ```typescript github/actions/create-issue.ts import { createAction } from 'nango'; import * as z from 'zod'; export default createAction({ description: 'Creates a GitHub issue', version: '1.0.0', input: z.object({ owner: z.string(), repo: z.string(), title: z.string(), body: z.string().optional() }), output: z.object({ id: z.number(), url: z.string().url() }), exec: async (nango, input) => { const response = await nango.post({ endpoint: `/repos/${input.owner}/${input.repo}/issues`, data: { title: input.title, body: input.body } }); return { id: response.data.id, url: response.data.html_url }; } }); ``` Import it from `index.ts`: ```typescript index.ts import './github/actions/create-issue'; ``` The output of an action function cannot exceed 2 MB. Return only the fields your app needs. For larger payloads, use the [requests proxy](/guides/platform/proxy-requests) to fetch data directly from your app. Before generating an action function, collect the integration ID, connection ID for testing, provider endpoint, required input fields, expected output shape, and whether the operation is safe to retry. Prefer explicit Zod schemas and short provider response mapping. If the action performs writes, make the logic idempotent before using async execution or retries. ## Test and deploy Dry run the function with realistic input: ```bash nango dryrun create-issue '' -e dev --input '{"owner":"NangoHQ","repo":"interactive-demo","title":"Bug report","body":"Created from a Nango action"}' ``` Deploy your functions: ```bash nango deploy ``` To deploy only this action function: ```bash nango deploy --action create-issue dev ``` For broader test coverage, see the [testing guide](/guides/functions/testing). To deploy from CI, see [CI/CD](/guides/functions/ci-cd). ## Trigger synchronously Synchronous execution is the default. The API or SDK call returns the function output. ```typescript import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env.NANGO_SECRET_KEY! }); const result = await nango.triggerAction( 'github', '', 'create-issue', { owner: 'NangoHQ', repo: 'interactive-demo', title: 'Bug report', body: 'Created from a Nango action' } ); ``` ```bash curl --request POST \ --url https://api.nango.dev/action/trigger \ --header 'Authorization: Bearer ' \ --header 'Connection-Id: ' \ --header 'Provider-Config-Key: github' \ --header 'Content-Type: application/json' \ --data '{ "action_name": "create-issue", "input": { "owner": "NangoHQ", "repo": "interactive-demo", "title": "Bug report", "body": "Created from a Nango action" } }' ``` ## Trigger asynchronously Use async execution for bulk writes, bursty work, or provider endpoints with tight rate limits. Nango queues the execution, applies retries, and lets you poll or receive a webhook when it completes. Execution timing for asynchronous actions is not guaranteed. Async actions are currently processed sequentially per environment, so completion time depends on how many actions are queued and how long each one runs. Design callers to handle delays by polling the result endpoint or listening for the completion webhook. ```typescript const { id, statusUrl } = await nango.triggerActionAsync( 'github', '', 'create-issue', { owner: 'NangoHQ', repo: 'interactive-demo', title: 'Bug report', body: 'Created from an async Nango action' } ); ``` ```bash curl --request POST \ --url https://api.nango.dev/action/trigger \ --header 'Authorization: Bearer ' \ --header 'Connection-Id: ' \ --header 'Provider-Config-Key: github' \ --header 'X-Async: true' \ --header 'X-Max-Retries: 3' \ --header 'Content-Type: application/json' \ --data '{ "action_name": "create-issue", "input": { "owner": "NangoHQ", "repo": "interactive-demo", "title": "Bug report", "body": "Created from an async Nango action" } }' ``` Poll the result: ```typescript const result = await nango.getAsyncActionResult({ id }); ``` The async trigger response includes a `statusUrl` and `id`. Poll `GET /action/` for the result: `404` means the execution is still running, `200` returns the function output, and `500` returns the execution error. To receive completion events instead of polling, configure [webhooks from Nango to your app](/guides/platform/webhooks-from-nango) and enable the `Async action completed` webhook. Use retries only for idempotent function logic. Retried writes can otherwise create duplicate side effects in the external API. ## Related guides - [Function tool calling](/guides/functions/tool-calling) - [Data validation](/guides/functions/data-validation) - [Rate limits](/guides/functions/rate-limits) - [Trigger action API](/reference/backend/http-api/action/trigger) - [Functions SDK reference](/reference/functions/functions-sdk) ## Webhook functions Source: https://nango.dev/docs/guides/functions/webhook-functions.md Description: Process external API webhooks inside Nango functions. Webhook functions run when an external API sends a webhook to Nango. Use them when the webhook should execute Nango-hosted logic, update records, normalize events, or mark data for later reconciliation. If your app should handle the webhook directly, use [webhook forwarding](/guides/platform/webhook-forwarding) instead. ## Processing or forwarding Webhook functions and webhook forwarding both start with the same external event: a provider sends a webhook to Nango. Use a webhook function when: - Nango should run function code as soon as the provider webhook arrives. - The webhook should update the records cache. - The webhook payload needs normalization before your app sees it. - The event should trigger reconciliation logic inside a sync function. Use [webhook forwarding](/guides/platform/webhook-forwarding) when: - Your app already has webhook handling logic. - You want Nango to attribute the event to a connection and forward it. - The event should not run Nango function code. ## How routing works External APIs send webhooks to the integration's Nango webhook URL. Find it in **Integrations > [Integration] > Webhooks**. When the provider has a webhook routing script, Nango reads the incoming payload, headers, or allowed query parameters and tries to map the event to one or more Nango connections. Provider-specific guides explain the exact routing fields when extra setup is required. If Nango can map the webhook to a connection, the webhook function runs in that connection context. If Nango cannot map the event, the event cannot safely run connection-scoped function logic; use provider-specific setup or webhook forwarding for that case. Some providers require one global webhook registration. Others require one webhook subscription per connected account, which you can automate with an [event function](/guides/functions/event-functions). Before implementing a webhook function, inspect the provider-specific Nango docs for webhook routing requirements. Some providers need the Nango connection ID embedded in the provider webhook payload, while others route automatically from account, team, tenant, or installation identifiers. Do not assume an external webhook payload is already trusted or complete. Keep `onWebhook` short, idempotent, and resilient to duplicates or out-of-order delivery. ## Create the function Webhook function logic usually lives on a sync function with `webhookSubscriptions` and `onWebhook`: ```typescript crm/syncs/contacts.ts import { createSync } from 'nango'; import * as z from 'zod'; const Contact = z.object({ id: z.string(), email: z.string().email().optional(), updatedAt: z.string().optional() }); export default createSync({ description: 'Sync contacts and process contact webhooks', version: '1.0.0', frequency: 'every hour', webhookSubscriptions: ['contact.updated', 'contact.deleted'], models: { Contact }, exec: async (nango) => { // Periodic reconciliation logic. Keep this as the source of truth // when provider webhooks can be missed, delayed, or partial. }, onWebhook: async (nango, payload) => { if (payload.body.event === 'contact.deleted') { await nango.batchDelete([{ id: payload.body.contactId }], 'Contact'); return; } if (payload.body.event === 'contact.updated' && payload.body.contact) { await nango.batchSave( [ { id: payload.body.contact.id, email: payload.body.contact.email, updatedAt: payload.body.contact.updated_at } ], 'Contact' ); } } }); ``` Import it from `index.ts`: ```typescript index.ts import './crm/syncs/contacts'; ``` ## Register the provider webhook URL Register the integration's Nango webhook URL in the external API's developer portal. If the provider requires per-connection registration, use an event function after connection creation. For example, an event function can call the provider's webhook subscription API with the connection's credentials and store the provider webhook subscription ID in connection metadata for cleanup later. ## Test and deploy Dry run the sync function's `exec` logic: ```bash nango dryrun contacts '' -e dev ``` Deploy your functions: ```bash nango deploy ``` To deploy only this webhook-backed sync function: ```bash nango deploy --sync contacts dev ``` Then send a test webhook from the provider dashboard, CLI, or webhook testing tool to the Nango webhook URL. Check Nango logs for the incoming webhook, routing result, and function execution. ## Keep webhook processing lightweight Avoid making slow provider API calls directly from `onWebhook` unless the provider's webhook is explicitly designed for that flow. Webhooks can arrive in bursts, be retried, or arrive out of order. Prefer lightweight, idempotent handling in `onWebhook`, and let the sync function's `exec` reconcile full state. ## Related guides - [Webhook forwarding](/guides/platform/webhook-forwarding) - [Process external webhooks](/getting-started/use-cases/webhooks-from-external-apis) - [Real-time syncs](/guides/functions/syncs/realtime-syncs) - [Event functions](/guides/functions/event-functions) - [Records cache](/guides/functions/syncs/records-cache) ## Event functions Source: https://nango.dev/docs/guides/functions/event-functions.md Description: Run function logic automatically when Nango connection lifecycle events happen. Event functions run automatically when Nango reaches a connection lifecycle event. They are defined with `createOnEvent()`. Use them to validate credentials, register provider webhooks, seed connection-specific metadata, or clean up external resources before a connection is deleted. ## Choose the lifecycle event Supported events: | Event | When it runs | Common use | | --- | --- | --- | | `post-connection-creation` | Immediately after a connection is created | Register provider webhooks, seed metadata, run setup checks | | `validate-connection` | During connection creation and reconnect | Reject invalid credentials, missing provider permissions, or policy violations (e.g. same-account enforcement) | | `pre-connection-deletion` | Before a connection is deleted | Delete provider webhook subscriptions or external resources | Before generating an event function, identify the lifecycle event, provider endpoint, required metadata, and desired failure behavior. For `validate-connection`, throwing rejects the auth attempt. On first connect, the connection is deleted. On reconnect, the connection is marked as an auth error (`refresh_exhausted`) and the user must reconnect explicitly. For cleanup functions, make the provider call idempotent because the external resource may already be gone. For same-account enforcement on reconnect, store the provider account identifier in **connection metadata** during `post-connection-creation`, then compare it to a live identity API call in `validate-connection`. Do not compare `connection_config` fields: reconnect upserts new credentials before validation runs, so config values already reflect the newly authenticated account. ## Create a setup function Add a file under the integration's `on-events/` folder: ```typescript salesforce/on-events/post-connection-setup.ts import { createOnEvent } from 'nango'; import * as z from 'zod'; const Metadata = z.object({ organization_id: z.string().optional(), webhookId: z.string().optional() }); export default createOnEvent({ description: 'Register a Salesforce webhook after a successful connection', event: 'post-connection-creation', metadata: Metadata, exec: async (nango) => { const response = await nango.post({ endpoint: '/webhooks', data: { url: 'https://api.myapp.com/webhooks/salesforce', events: ['contact.updated'] } }); await nango.setMetadata({ webhookId: response.data.id }); } }); ``` Import it from `index.ts`: ```typescript index.ts import './salesforce/on-events/post-connection-setup'; ``` ## Create a connection validation function Add a file under the integration's `on-events/` folder: ```typescript salesforce/on-events/validate-connection.ts import { createOnEvent } from 'nango'; import * as z from 'zod'; const Metadata = z.object({ organizationId: z.string().optional(), webhookId: z.string().optional() }); export default createOnEvent({ description: 'Validate connection against saved organization id to prevent changing account on reconnect', event: 'validate-connection', metadata: Metadata, exec: async (nango) => { const metadata = await nango.getMetadata(); const lockedOrganizationId = metadata?.organizationId; const response = await nango.get<{organization_id: string}>({ endpoint: '/services/oauth2/userinfo' }); const organizationId = response.data?.organization_id; if (!organizationId) { throw new nango.ActionError('Salesforce connection missing organization_id from /services/oauth2/userinfo'); } // On initial connection, save organization id to connection metadata if (!lockedOrganizationId) { await nango.setMetadata({organizationId}); return; } // On subsequent connections, check if new organization id matches stored value if (lockedOrganizationId !== organizationId) { throw new nango.ActionError( `Salesforce org mismatch: expected ${lockedOrganizationId}, got ${organizationId}` ); } } }); ``` Import it from `index.ts`: ```typescript index.ts import './salesforce/on-events/validate-connection'; ``` On first connect, validation passes when metadata has no locked org yet; the setup function records `organization_id` after auth succeeds. On reconnect, validation compares the new credentials against the saved org ID and rejects the attempt if they differ. Compare the locked value in **metadata** to a live value from the provider API. Do not compare `connection_config` fields: on reconnect, Nango upserts the new credentials and overwrites `connection_config` **before** `validate-connection` runs, so config already reflects the account the user just authenticated with. ## Create a cleanup function If the provider webhook is registered per connection, clean it up before the Nango connection is deleted: ```typescript salesforce/on-events/pre-connection-cleanup.ts import { createOnEvent } from 'nango'; import * as z from 'zod'; const Metadata = z.object({ organization_id: z.string().optional(), webhookId: z.string().optional() }); export default createOnEvent({ description: 'Delete the Salesforce webhook before connection deletion', event: 'pre-connection-deletion', metadata: Metadata, exec: async (nango) => { const metadata = await nango.getMetadata(); if (!metadata?.webhookId) { return; } await nango.delete({ endpoint: `/webhooks/${metadata.webhookId}` }); } }); ``` Import it from `index.ts`: ```typescript index.ts import './salesforce/on-events/pre-connection-cleanup'; ``` ## Test and deploy Dry run an event function against an existing connection: ```bash nango dryrun post-connection-setup '' -e dev ``` Deploy your functions: ```bash nango deploy ``` Event functions run automatically when their configured event occurs. Your app does not trigger them directly. ## Related guides - [Webhook functions](/guides/functions/webhook-functions) - [Webhook forwarding](/guides/platform/webhook-forwarding) - [Storage](/guides/functions/storage) - [Functions SDK reference](/reference/functions/functions-sdk) ## Tool calling & MCP Source: https://nango.dev/docs/guides/functions/tool-calling.md Description: Expose Nango action functions to agents, LLM SDKs, and MCP clients. Tool calling in Nango happens through [action functions](/guides/functions/action-functions). Each tool is an action function that an agent, LLM SDK, or MCP client can call on demand for a specific connection. The agent chooses a tool, your application asks Nango to execute the action function for that connection, and Nango handles the external API credentials, retries, rate limits, and logs. ## Prerequisites - A Nango integration configured for the external API. - A connection for the user whose credentials should be used. - One or more enabled action functions for the operations the agent may call. Build custom tools with [action functions](/guides/functions/action-functions), or enable template action functions from the [template catalog](/guides/functions/functions-guide#template-catalog). ## Common auth and execution Keep provider credentials out of the agent runtime. Your app should resolve the Nango connection for the current user, then pass only the connection ID and selected tool to Nango. ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env.NANGO_SECRET_KEY! }); const integrationId = 'hubspot'; async function getConnectionIdForUser(userId: string) { const connections = await nango.listConnections({ integrationId, tags: { end_user_id: userId } }); let connectionId = connections.connections[0]?.connection_id; if (!connectionId) { const session = await nango.createConnectSession({ allowed_integrations: [integrationId], tags: { end_user_id: userId } }); // Send this URL to the user in your app, then wait for authorization. console.log(session.data.connect_link); const connection = await nango.waitForConnection(integrationId, userId); connectionId = connection!.connection_id; } return connectionId; } async function executeNangoTool(actionName: string, input?: unknown) { const connectionId = await getConnectionIdForUser('user_123'); return await nango.triggerAction(integrationId, connectionId, actionName, input); } ``` The examples below focus on framework-specific tool syntax and call the shared `executeNangoTool()` helper. ## Framework examples These examples expose a HubSpot `whoami` action function as a `who_am_i` tool. ```ts import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! }); const tools = [ { type: 'function' as const, name: 'who_am_i', description: 'Get the current HubSpot user information.', strict: true, parameters: { type: 'object', properties: {}, required: [], additionalProperties: false } } ]; const input: any[] = [ { role: 'user', content: 'Use who_am_i and summarize the current HubSpot user.' } ]; const response = await client.responses.create({ model: 'gpt-5', input, tools }); for (const item of response.output) { if (item.type !== 'function_call' || item.name !== 'who_am_i') { continue; } const result = await executeNangoTool('whoami', JSON.parse(item.arguments || '{}')); input.push(item); input.push({ type: 'function_call_output', call_id: item.call_id, output: JSON.stringify(result) }); } const finalResponse = await client.responses.create({ model: 'gpt-5', input, tools }); console.log(finalResponse.output_text); ``` ```ts import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }); const tools = [ { name: 'who_am_i', description: 'Get the current HubSpot user information.', input_schema: { type: 'object', properties: {}, required: [] } } ]; const messages: Anthropic.MessageParam[] = [ { role: 'user', content: 'Use who_am_i and summarize the current HubSpot user.' } ]; const message = await client.messages.create({ model: 'claude-sonnet-4-5', max_tokens: 1024, messages, tools }); const toolUse = message.content.find((block) => block.type === 'tool_use'); if (toolUse?.type === 'tool_use' && toolUse.name === 'who_am_i') { const result = await executeNangoTool('whoami', toolUse.input); messages.push({ role: 'assistant', content: message.content }); messages.push({ role: 'user', content: [ { type: 'tool_result', tool_use_id: toolUse.id, content: JSON.stringify(result) } ] }); const finalMessage = await client.messages.create({ model: 'claude-sonnet-4-5', max_tokens: 1024, messages, tools }); console.log(finalMessage.content); } ``` ```ts import { generateText, stepCountIs, tool } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; const { text, toolResults } = await generateText({ model: openai('gpt-4o'), prompt: 'Use who_am_i and summarize the current HubSpot user.', tools: { who_am_i: tool({ description: 'Get the current HubSpot user information.', inputSchema: z.object({}), execute: async () => { return await executeNangoTool('whoami'); } }) }, stopWhen: stepCountIs(5) }); console.log(text); console.log(toolResults); ``` ```ts import { createAgent, tool } from 'langchain'; import { z } from 'zod'; const whoAmI = tool( async () => { const result = await executeNangoTool('whoami'); return JSON.stringify(result); }, { name: 'who_am_i', description: 'Get the current HubSpot user information.', schema: z.object({}) } ); const agent = createAgent({ model: 'openai:gpt-5.4', tools: [whoAmI] }); const result = await agent.invoke({ messages: [ { role: 'user', content: 'Use who_am_i and summarize the current HubSpot user.' } ] }); console.log(result.messages.at(-1)?.content); ``` ```ts import { openai } from '@ai-sdk/openai'; import { Agent } from '@mastra/core/agent'; import { createTool } from '@mastra/core/tools'; import { z } from 'zod'; const whoAmI = createTool({ id: 'who_am_i', description: 'Get the current HubSpot user information.', inputSchema: z.object({}), execute: async () => { return await executeNangoTool('whoami'); } }); const agent = new Agent({ name: 'HubSpot Agent', instructions: 'Use available tools to answer HubSpot account questions.', model: openai('gpt-4o'), tools: { who_am_i: whoAmI } }); const response = await agent.generate('Use who_am_i and summarize the current HubSpot user.'); console.log(response.text); ``` ## Programmatic tool discovery The [Get integration functions config API](/reference/backend/http-api/scripts/config) returns enabled action function definitions. Use it when an agent or orchestration layer should discover available tools at runtime. ```bash curl --request GET \ --url 'https://api.nango.dev/scripts/config?provider_config_key=&format=openai' \ --header 'Authorization: Bearer ' ``` Use `format=nango` for Nango's native configuration shape, or `format=openai` when you want OpenAI-compatible function definitions. ## Direct tool execution For SDKs that let you provide your own tool executor, define the tool schema in your app and call Nango from the executor. ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env.NANGO_SECRET_KEY! }); async function executeTool(connectionId: string, input: { owner: string; repo: string; title: string }) { return await nango.triggerAction('github', connectionId, 'create-issue', input); } ``` Keep these values explicit: - `providerConfigKey` / integration ID - which integration owns the action function. - `connectionId` - which user's credentials the call uses. - Action function name - the enabled action function to execute. - Input payload - validated by the action function schema when configured. ## MCP server Nango exposes enabled action functions through a hosted MCP server: ```text https://api.nango.dev/mcp ``` The server supports Streamable HTTP transport. Requests must include: | Header | Value | | --- | --- | | `Authorization` | `Bearer ` | | `connection-id` | The connection whose credentials should be used | | `provider-config-key` | The integration ID | Example client setup with the MCP TypeScript SDK: ```ts import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; const transport = new StreamableHTTPClientTransport(new URL('https://api.nango.dev/mcp'), { requestInit: { headers: { Authorization: `Bearer ${process.env.NANGO_SECRET_KEY}`, 'connection-id': '', 'provider-config-key': '' } } }); ``` For desktop clients that only support stdio, use a bridge such as `mcp-remote` and pass the same headers. Keep the header spacing exactly as required by the bridge you choose. ## Auth flow for agents Agents should not receive provider credentials. Your app should: 1. Check whether the user already has a Nango connection. 2. Create a Connect session if they need to authorize. 3. Store the resulting `connectionId` in your app. 4. Use that `connectionId` for future tool calls. See the [Auth guide](/guides/auth/auth-guide) and [Create Connect Session API](/reference/backend/http-api/connect/sessions/create) for the end-to-end auth flow. If you are implementing this flow programmatically, first list or create the user's connection, then call `GET /scripts/config` to discover enabled action functions, and finally execute tools with `POST /action/trigger` or the MCP server. Required values for every execution are the Nango secret key, integration ID, connection ID, action function name, and input payload. ## Observability and safety Every action function execution appears in Nango logs. Use logs to debug failed tool calls, inspect provider requests, and review agent behavior. Keep tool names and descriptions specific. Expose only action functions the agent is allowed to call for the current user and workflow. For write operations, make action function logic idempotent before using async execution or retries. ## Related references - [Action functions](/guides/functions/action-functions) - [Template catalog](/guides/functions/functions-guide#template-catalog) - [Get integration functions config API](/reference/backend/http-api/scripts/config) - [Trigger action API](/reference/backend/http-api/action/trigger) - [Functions SDK reference](/reference/functions/functions-sdk) - [Observability](/guides/platform/observability) ## Storage Source: https://nango.dev/docs/guides/functions/storage.md Description: Use connection metadata as the default storage for function parameters and per-customer configuration. Functions have access to storage that is scoped to the connection they run for. Use it to keep integration behavior tied to the customer, account, workspace, or tenant that authorized the connection. For the product-level workflow, see [Customize per customer](/getting-started/use-cases/customer-configuration). ## Connection metadata Connection metadata is the default blob storage for Nango Functions. It is a JSON object stored on a Nango connection and available to action functions, sync functions, webhook functions, and event functions. Use metadata for: - Customer-specific settings, such as field mappings, filters, folders, projects, account IDs, or feature flags. - Function configuration values that should persist across executions. - Values discovered during setup, such as provider webhook subscription IDs or tenant-specific identifiers. - Configuration shared across multiple functions for the same connection. Do not use metadata for synced datasets or large lists of records. For data replicated by sync functions, use the [records cache](/guides/functions/syncs/records-cache). ## Set and update metadata from your app Set metadata after the customer chooses integration settings during onboarding or in your app's settings UI. ```ts await nango.setMetadata( '', '', { accountRegion: 'eu', syncArchived: false, fieldMapping: { companyName: 'Account_Name__c' } } ); ``` ```bash curl --request POST \ --url https://api.nango.dev/connection//metadata \ --header 'Authorization: Bearer ' \ --header 'Provider-Config-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "accountRegion": "eu", "syncArchived": false, "fieldMapping": { "companyName": "Account_Name__c" } }' ``` ```ts await nango.updateMetadata( '', '', { syncArchived: true } ); ``` ```bash curl --request PATCH \ --url https://api.nango.dev/connection//metadata \ --header 'Authorization: Bearer ' \ --header 'Provider-Config-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "syncArchived": true }' ``` `setMetadata` replaces the metadata object. `updateMetadata` merges the provided fields into the existing object. For exact parameters and response shapes, see the [set metadata API](/reference/backend/http-api/connections/set-metadata), [update metadata API](/reference/backend/http-api/connections/update-metadata), [Node SDK set metadata method](/reference/backend/backend-sdk/node#set-connection-metadata), and [Node SDK edit metadata method](/reference/backend/backend-sdk/node#edit-connection-metadata). ## Read metadata from your app Read metadata from your app when you need to render saved settings, pre-fill a configuration UI, or decide whether a connection is ready for syncs: ```ts const metadata = await nango.getMetadata('', ''); ``` You can also fetch the connection with the REST API: ```bash curl --request GET \ --url 'https://api.nango.dev/connection/?provider_config_key=' \ --header 'Authorization: Bearer ' ``` ## Read metadata inside a function Define a metadata schema on the function, then call `nango.getMetadata()`: ```ts crm/syncs/contacts.ts import { createSync } from 'nango'; import * as z from 'zod'; const Metadata = z.object({ accountRegion: z.string().optional(), syncArchived: z.boolean().default(false) }); export default createSync({ description: 'Sync contacts', frequency: 'every hour', metadata: Metadata, exec: async (nango) => { const metadata = await nango.getMetadata(); await nango.get({ endpoint: '/contacts', params: { region: metadata.accountRegion, archived: metadata.syncArchived } }); } }); ``` Inside a function, `nango.getMetadata()` automatically reads metadata for the connection being executed. When a sync function reads metadata with `nango.getMetadata()`, metadata can be cached for up to 60 seconds during that execution. The next execution always reads the latest metadata. ## Customer configuration pattern A common customer configuration pattern is to ask the customer how their external custom fields map to your product model, then store that mapping as connection metadata. ```json { "fieldMapping": { "internal_customer_id": "External_Customer_ID__c", "plan": "Plan__c" } } ``` Use an action function to fetch available custom fields from the provider, save the customer's choices as metadata, then read the mapping inside the sync function that fetches records. If a sync function should start only after the required configuration exists, start it after saving metadata: ```ts await nango.startSync('', ['contacts'], ''); ``` ## Records cache The records cache is the storage layer for sync output. Sync functions write records with `nango.batchSave()`, `nango.batchUpdate()`, and `nango.batchDelete()`. Your app reads changed records through the records API or SDK. Use the records cache for synced external data, not for function configuration. See [Records cache](/guides/functions/syncs/records-cache). ## Related guides - [Customize per customer](/getting-started/use-cases/customer-configuration) - design the product workflow for customer-specific integration settings. - [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata) - distinguish tags, connection configuration, and runtime metadata. - [Set connection metadata API](/reference/backend/http-api/connections/set-metadata) - replace metadata from your app. - [Update connection metadata API](/reference/backend/http-api/connections/update-metadata) - patch metadata from your app. ## Function logging Source: https://nango.dev/docs/guides/functions/logging.md Description: Use function logs for local debugging, cloud observability, and production troubleshooting. Nango records function executions in the Logs tab. You can also add custom logs from action, sync, webhook, and event functions with `nango.log()`. Use custom logs for information that helps you understand function behavior: selected filters, page counts, checkpoint values, provider identifiers, branch decisions, and recoverable errors. ## Add custom logs ```ts await nango.log('Starting contacts sync'); await nango.log('Applying region filter', { level: 'debug' }); await nango.log('Provider returned a partial response', { level: 'warn' }); await nango.log('Failed to map contact', { level: 'error' }); ``` If you do not pass a level, `nango.log()` uses `info`. ## Log levels Nango supports these levels, from most to least verbose: | Level | Use for | | --- | --- | | `debug` | Local diagnostics, pagination details, temporary investigation logs | | `info` | Normal execution milestones you may want during development | | `warn` | Unexpected but recoverable behavior | | `error` | Failed operations, invalid provider responses, or errors you catch and continue from | | `off` | Disable custom logs | Only logs at or above the configured logger level are ingested into the Nango UI Logs tab. For example, when the logger level is `warn`, only `warn` and `error` custom logs are visible in the cloud logs. ## Cloud vs local behavior | Runtime | Default custom log level | Where custom logs appear | | --- | --- | --- | | Cloud environments | `warn` | Nango UI Logs tab for logs at `warn` or `error` | | `nango dryrun` | `debug` | Local console for all custom log levels | This means `await nango.log('message')` is visible during local dry runs, because local dry runs default to `debug`. The same call defaults to `info`, so it is not visible in cloud logs unless you lower the configured logger level to `info` or `debug`. Cloud custom logs can affect log volume and billing. Prefer `debug` and `info` for local investigation, and use `warn` or `error` for production signals you want retained by default. ## Configure the logger Set the default logger level for an environment with `NANGO_LOGGER_LEVEL` in **Environment Settings > Environment Variables**: ```bash NANGO_LOGGER_LEVEL=info ``` Valid values are `debug`, `info`, `warn`, `error`, and `off`. You can also change the logger level inside a function. This applies to `nango.log()` calls after the setting is changed: ```ts nango.setLogger({ level: 'debug' }); await nango.log('Detailed investigation log'); nango.setLogger({ level: 'warn' }); ``` Use function-level configuration when one run or one branch needs additional visibility. Use the environment variable when all functions in that environment should share the same threshold. ## What to log Good function logs are specific and bounded: - Log the connection-specific configuration that changes behavior, such as selected folder IDs, regions, or pipeline IDs. - Log provider pagination progress with counts, cursors, or checkpoints, but avoid logging every record. - Log warnings when the provider omits optional data or returns a shape you can recover from. - Log errors when you catch an exception and continue, so the run is still inspectable. Avoid logging secrets, access tokens, refresh tokens, full request headers, or raw payloads that may contain sensitive customer data. ## Related guides - [Observability](/guides/platform/observability) - [Functions SDK reference](/reference/functions/functions-sdk#logging) - [Testing](/guides/functions/testing) - [Sync efficiency](/guides/functions/syncs/sync-efficiency) ## Rate limits Source: https://nango.dev/docs/guides/functions/rate-limits.md Description: Guide on how to handle API rate limits from external APIs. External APIs often enforce rate limits to prevent excessive use. When these limits are reached, you'll start receiving HTTP `429 Too Many Requests` errors, blocking further requests until the limit resets. To maintain data freshness and avoid disruptions in your integration functions, it's crucial to manage your API call volume and handle rate limits effectively. Nango simplifies this process significantly. ## Strategy 1: Retry with exponential backoff The simplest strategy involves retrying failed requests after waiting: When you configure HTTP requests using Nango's helper ([reference](/reference/functions/functions-sdk#http-requests)), you can specify the number of retries: ```ts await nango.get({ endpoint: '/some-endpoint', retries: 10 }); ``` This configuration enables automatic retries on receiving a `429` status code, with the retries spaced out using exponential backoff. This method is efficient because: - It uses exponential backoff to wait out the rate-limit period - Nango [sync functions](/guides/functions/syncs/sync-functions) can run for up to 24 hours, allowing retries to occur within this window (note that action functions and webhook functions have shorter lifespans; see [Resource limits](/guides/platform/limits)) - It works for APIs without requiring any API-specific configurations Find details about HTTP request retries in the [reference](/reference/functions/functions-sdk#http-request-retries). ## Strategy 2: Leverage rate-limit headers Most of the time, the first strategy is sufficient to handle rate limits. For APIs with stringent limits, Nango provides a more refined and customized approach by automatically parsing API-specific rate-limit headers from responses and scheduling retries only after the rate limit period has reset: This is set up in the same way as the first strategy: ```ts await nango.get({ endpoint: '/some-endpoint', retryHeader: { at: "x-ratelimit-reset" } }); ``` If rate-limit headers are not already configured for an API you are using, you can request their inclusion by reaching out to the Nango team via the [community](https://nango.dev/slack)—typically within 24 hours. ## Related guides - [Sync efficiency](/guides/functions/syncs/sync-efficiency) - avoid unnecessary provider requests. - [Action functions](/guides/functions/action-functions) - design retries for idempotent actions. - [Resource limits](/guides/platform/limits) - understand runtime constraints. - [Functions SDK reference](/reference/functions/functions-sdk#http-request-retries) - configure request retries. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Data validation Source: https://nango.dev/docs/guides/functions/data-validation.md Description: How to validate data in your functions and in your app Nango offers data validation at two levels: inside your [Nango functions](/guides/functions/functions-guide) (when calling external APIs) and in your application code (when consuming function responses). ## Validate API responses in Nango functions Use Zod to validate data you receive from and send to external APIs. The CLI will also surface *type* errors during dry runs when using the `--validation` option. ```typescript import * as z from 'zod'; const dataFromAPI = z.object({ ticketId: z.string(), }); export default createSync({ exec: async (nango) => { const response = await nango.get({ endpoint: '/tickets' }); const isValid = dataFromAPI.parse(response.json); if (isValid) { [...] } }, }); ``` ## Validate Nango function responses in your app ### TypeScript types (compile-time) Use `z.infer` to derive TypeScript types from your Zod schemas and export them from your sync/action files: ```ts // In: github/syncs/fetchIssues.ts (Nango function) export type GithubIssue = z.infer; ``` Then import the types in your application code: ```ts import type { GithubIssue } from './nango-integrations/github/syncs/fetchIssues'; const issues = await nango.listRecords({ ... }); ``` ### JSON Schema (runtime, language-agnostic) When you run `nango compile` or `nango deploy`, Nango generates a `nango.json` file in the `.nango` folder. Each sync/action entry includes a `json_schema` property with the JSON Schema for its models: ```json // .nango/nango.json (simplified) [ { "providerConfigKey": "github", "syncs": [ { "name": "fetchIssues", "output": ["GithubIssue"], "json_schema": { "definitions": { "GithubIssue": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" }, "state": { "type": "string" } }, "required": ["id", "title", "state"], "additionalProperties": false } } } } ] } ] ``` You can extract the `json_schema` object for a given sync or action and use it with any JSON Schema validator in your language of choice (e.g., `ajv` in TypeScript, `jsonschema` in Python, `jsonschema` in Rust, `santhosh-tekuri/jsonschema` in Go). Alternatively, Zod v4 supports JSON Schema conversion natively with [`z.toJSONSchema()`](https://zod.dev/json-schema?id=ztojsonschema): ```ts import { z } from 'zod'; import { issueSchema } from './nango-integrations/github/syncs/fetchIssues'; const jsonSchema = z.toJSONSchema(issueSchema); ``` ## Related guides - [Action functions](/guides/functions/action-functions) - validate action inputs and outputs. - [Sync functions](/guides/functions/syncs/sync-functions) - validate synced record models. - [Unified APIs](/guides/functions/unified-apis) - normalize provider data behind stable schemas. - [Testing](/guides/functions/testing) - cover validation behavior in tests. ## Testing integrations Source: https://nango.dev/docs/guides/functions/testing.md Description: How to test your Nango integrations using dry runs, mocks, and snapshots Nango provides a comprehensive testing framework that combines dry runs with mock-based snapshot testing. This guide will walk you through testing your integrations effectively. ## Testing approach Nango's testing framework is built on three core concepts: 1. **Dry runs**: Test your integrations against live API connections without affecting production data 2. **Mocks**: Save API responses during dry runs to create reproducible test fixtures 3. **Snapshot testing**: Automatically compare integration outputs against saved snapshots using Vitest This approach ensures your integrations work correctly while providing fast, reliable tests that don't depend on external APIs. ## Dry run testing Dry runs allow you to execute your sync functions and action functions against real API connections without saving data to your database. This is essential for: - Testing integration logic before deployment - Debugging issues with live data - Generating test fixtures (mocks) - Validating data transformations ### Basic dry run Execute a sync or action against an existing connection: ```bash nango dryrun ``` **Example:** ```bash nango dryrun fetch-tickets abc-123-connection ``` ### Dry run options Common options for testing: ```bash # Specify environment (dev or prod) nango dryrun fetch-tickets abc-123 -e prod # For action functions: pass input data nango dryrun create-ticket abc-123 --input '{"title": "Test ticket"}' # For sync functions: specify an initial checkpoint nango dryrun fetch-tickets abc-123 --checkpoint '{ "lastModifiedAt": "2026-02-01T01:00:00.000Z" }' # Use a specific integration when sync names overlap nango dryrun fetch-tickets abc-123 --integration-id github # Execute a specific variant nango dryrun fetch-tickets abc-123 --variant premium ``` ## Data validation Validation ensures your integration inputs and outputs match your defined schemas. This catches data transformation errors early. ### Enable validation during dry runs Use the `--validate` flag to enforce validation: ```bash nango dryrun fetch-tickets abc-123 --validate ``` When validation is enabled: - **Action inputs** are validated before execution - **Action outputs** are validated after execution - **Sync records** are validated before they would be saved - **Validation failures** halt execution and display detailed error messages ### Validation with Zod You can also validate data directly in your integration code using Zod: ```typescript import { z } from 'zod'; const TicketSchema = z.object({ id: z.string(), title: z.string(), status: z.enum(['open', 'closed']), createdAt: z.string().datetime(), }); export default createSync({ exec: async (nango) => { const response = await nango.get({ endpoint: '/tickets' }); // Validate the API response const tickets = response.data.map(ticket => { return TicketSchema.parse(ticket); // Throws if validation fails }); await nango.batchSave(tickets, 'Ticket'); }, }); ``` ### Validation error output When validation fails, you'll see detailed error information: ``` Invalid sync record. Use `--validate` option to see the details (invalid_sync_record) { "validation": [ { "path": "createdAt", "message": "Invalid datetime string! Must be UTC.", "code": "invalid_string" } ], "model": "Ticket" } ``` ## Saving mocks for tests Mocks are saved API responses that allow you to run tests without hitting external APIs. This makes tests faster and more reliable. ### Generate mocks with dry run Use the `--save` flag to save all API responses: ```bash nango dryrun fetch-tickets abc-123 --save ``` **Important**: When using `--save`, validation is automatically enabled. Mocks are only saved if validation passes, ensuring your test fixtures contain valid data. ### Mock file structure Mocks are saved in a single `.test.json` file that can be used alongside your test file: ``` github/ ├── tests/ │ ├── fetch-tickets.test.ts │ └── fetch-tickets.test.json ``` The `test.json` file contains all the necessary mock data for a given test: ```json { "input": { "title": "Test ticket" }, "output": { "id": "TKT-123", "status": "created" }, "nango": { "getConnection": { "connectionId": "abc-123", "provider": "github" }, "getMetadata": { "accountId": "test-123" }, "batchSave": { "Ticket": [{ "id": "TKT-123", "title": "Test ticket" }] }, "batchDelete": { "Ticket": [{ "_nango_id": "del-456" }] } }, "api": { "GET": { "/tickets": { "response": [{ "id": "TKT-123", "title": "Test ticket" }] } }, "POST": { "/issues": { "response": { "id": "ISS-789" } } } } } ``` ### Using stubbed metadata For sync functions that rely on connection metadata, you can provide test metadata: ```bash nango dryrun fetch-tickets abc-123 --save --metadata '{"accountId": "test-123"}' ``` Or load from a file: ```bash nango dryrun fetch-tickets abc-123 --save --metadata @fixtures/metadata.json ``` ## Testing with Vitest Nango uses [Vitest](https://vitest.dev/) as its testing framework. Vitest is fast, has a great developer experience, and provides snapshot testing out of the box. ### Setup Install Vitest as a dev dependency: ```bash npm install -D vitest ``` Generate tests for your integrations: ```bash nango generate:tests ``` You can also generate tests for specific integrations, sync functions, or action functions: ```bash # Generate tests for a specific integration nango generate:tests -i github # Generate tests for a specific sync function nango generate:tests -s fetch-tickets # Generate tests for a specific action function nango generate:tests -a create-ticket # Combine flags for more specific targeting nango generate:tests -i github -s fetch-tickets ``` Run your tests: ```bash npm test ``` ### Auto-generated tests When you run `nango generate:tests`, Nango creates test files for all your integrations: **Sync test example:** ```typescript import { vi, expect, it, describe, beforeAll } from 'vitest'; import createSync from '../syncs/fetch-tickets.js'; describe('github fetch-tickets tests', () => { let nangoMock; beforeAll(async () => { nangoMock = new global.vitest.NangoSyncMock({ dirname: __dirname, name: "fetch-tickets", Model: "Ticket" }); }); const models = 'Ticket'.split(','); const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave'); it('should get, map correctly the data and batchSave the result', async () => { await createSync.exec(nangoMock); for (const model of models) { const expectedBatchSaveData = await nangoMock.getBatchSaveData(model); const spiedData = batchSaveSpy.mock.calls.flatMap(call => { if (call[1] === model) { return call[0]; } return []; }); const spied = JSON.parse(JSON.stringify(spiedData)); expect(spied).toStrictEqual(expectedBatchSaveData); } }); it('should get, map correctly the data and batchDelete the result', async () => { await createSync.exec(nangoMock); for (const model of models) { const batchDeleteData = await nangoMock.getBatchDeleteData(model); if (batchDeleteData && batchDeleteData.length > 0) { expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model); } } }); }); ``` **Action test example:** ```typescript import { vi, expect, it, describe, beforeAll } from 'vitest'; import createAction from '../actions/create-ticket.js'; describe('github create-ticket tests', () => { let nangoMock; beforeAll(async () => { nangoMock = new global.vitest.NangoActionMock({ dirname: __dirname, name: "create-ticket", Model: "Ticket" }); }); it('should output the action output that is expected', async () => { const input = await nangoMock.getInput(); const response = await createAction.exec(nangoMock, input); const output = await nangoMock.getOutput(); expect(response).toEqual(output); }); }); ``` ### How mocks work in tests The `NangoSyncMock` and `NangoActionMock` classes automatically load your saved mocks from the `.test.json` file: 1. **API requests** are intercepted and return saved mock responses from the `api` section. 2. **Input data** is loaded from the `input` property for action functions. 3. **Expected outputs** are loaded from the `output` property. 4. **Tests compare** actual outputs against expected outputs. This means: - Tests run **instantly** (no API calls) - Tests are **deterministic** (same input = same output) - Tests work **offline** ### Migrating from the old format If you have tests using the old multi-file mock format, you can automatically migrate them to the new unified format. Set the `MIGRATE_MOCKS` environment variable to `2026-01` and run your tests: ```bash MIGRATE_MOCKS=2026-01 npm test ``` This will: 1. Run your tests using the old mock files. 2. Intercept all mock data accessed during the test run. 3. Save the data into a new `.test.json` file. 4. The old mock directory (`mocks` and `fixtures`) can then be safely deleted once a clean green test run is achieved. > **Note:** This migration tool works by intercepting the mock data loaded by your existing tests. It requires that your tests are using the standard Nango mock utilities (`NangoSyncMock` or `NangoActionMock`) imported from `nango/test`. **Pagination Bug in Legacy Test Utilities** The legacy test utilities had a bug where pagination would sometimes stop after the first page. This means: 1. **Tests appeared to pass** but were only testing the first page of results 2. **Mock files may exist** for subsequent pages, but might be incorrect (hashes or params were never checked) 3. **Some mock files may be missing entirely** if they were never recorded **How the migration handles this:** The migration tool performs a **best-effort recovery**: - It first tries to match mock files by their exact request hash - If that fails, it scans all available mock files and matches by comparing the actual request parameters (endpoint, query params, headers) - This handles cases where the hash in the filename is wrong but the request data inside the file is correct **What to do if tests fail after migration:** If your tests fail after running the migration, it means one of: - The mock file for a paginated request doesn't exist at all (it was never recorded due to the bug) - The request parameters stored in the mock file don't match what Nango's pagination now sends **To fix failing tests, re-record your mocks:** ```bash nango dryrun --save ``` This will generate a complete `.test.json` file with all paginated responses using Nango's actual pagination implementation. ### Running tests ```bash # Run all tests npm test # Run tests in watch mode npm test -- --watch # Run tests for a specific integration npm test github # Run a specific test file npm test github-fetch-tickets.test.ts # Run with coverage npm test -- --coverage ``` ### Test configuration Vitest is configured via `vite.config.ts` in your project root: ```typescript import { defineConfig } from 'vite'; export default defineConfig({ test: { globals: true, environment: 'node', setupFiles: ['./vitest.setup.ts'], }, }); ``` The `vitest.setup.ts` file makes Nango mocks available globally: ```typescript import { NangoActionMock, NangoSyncMock } from "nango/test"; globalThis.vitest = { NangoActionMock, NangoSyncMock, }; ``` ## Customizing tests with business logic While auto-generated tests validate basic data flow, you often need custom tests for business logic. ### Adding custom assertions Extend the generated tests with additional assertions: ```typescript import { vi, expect, it, describe, beforeAll } from 'vitest'; import createSync from '../syncs/fetch-tickets.js'; describe('github fetch-tickets tests', () => { let nangoMock; beforeAll(async () => { nangoMock = new global.vitest.NangoSyncMock({ dirname: __dirname, name: "fetch-tickets", Model: "Ticket" }); }); it('should correctly transform ticket priorities', async () => { await createSync.exec(nangoMock); const savedTickets = await nangoMock.getBatchSaveData('Ticket'); // Custom business logic validation savedTickets.forEach(ticket => { // Ensure priority is normalized expect(['low', 'medium', 'high', 'critical']).toContain(ticket.priority); // Ensure dates are ISO 8601 expect(ticket.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/); // Ensure ticket numbers are prefixed correctly if (ticket.source === 'github') { expect(ticket.id).toMatch(/^GH-\d+$/); } }); }); it('should filter out spam tickets', async () => { await createSync.exec(nangoMock); const savedTickets = await nangoMock.getBatchSaveData('Ticket'); // Verify spam filtering logic const spamTickets = savedTickets.filter(t => t.title.toLowerCase().includes('spam') ); expect(spamTickets).toHaveLength(0); }); }); ``` ### Testing error handling Test how your integration handles errors: ```typescript it('should handle API errors gracefully', async () => { // Create a mock that will throw an error const errorMock = new global.vitest.NangoSyncMock({ dirname: __dirname, name: "fetch-tickets-error", Model: "Ticket" }); // Mock the get method to throw vi.spyOn(errorMock, 'get').mockRejectedValue( new Error('API rate limit exceeded') ); // Verify error is logged const logSpy = vi.spyOn(errorMock, 'log'); await expect(async () => { await createSync.exec(errorMock); }).rejects.toThrow('API rate limit exceeded'); expect(logSpy).toHaveBeenCalledWith( expect.stringContaining('rate limit'), { level: 'error' } ); }); ``` ### Testing pagination Verify pagination logic works correctly: ```typescript it('should fetch all pages of results', async () => { await createSync.exec(nangoMock); const savedTickets = await nangoMock.getBatchSaveData('Ticket'); // Verify we got results from multiple pages // (based on your pagination implementation) expect(savedTickets.length).toBeGreaterThan(100); // Assuming page size is 100 }); ``` ### Testing sync functions with checkpoints Test that checkpoint logic works: ```typescript it('should only fetch tickets after checkpoint date', async () => { const getSpy = vi.spyOn(nangoMock, 'get'); const saveCheckpointSpy = vi.spyOn(nangoMock, 'saveCheckpoint'); // Mock getCheckpoint to return a previous checkpoint vi.spyOn(nangoMock, 'getCheckpoint').mockResolvedValue({ lastModifiedISO: '2024-01-01T00:00:00Z' }); await createSync.exec(nangoMock); // Verify the API request used the checkpoint value expect(getSpy).toHaveBeenCalledWith( expect.objectContaining({ params: expect.objectContaining({ since: '2024-01-01T00:00:00Z' }) }) ); // Verify checkpoint was saved expect(saveCheckpointSpy).toHaveBeenCalled(); }); ``` ### Parameterized tests Test multiple scenarios with different inputs: ```typescript import { it, describe, expect, beforeAll } from 'vitest'; describe.each([ { priority: 'P0', expected: 'critical' }, { priority: 'P1', expected: 'high' }, { priority: 'P2', expected: 'medium' }, { priority: 'P3', expected: 'low' }, ])('priority mapping for $priority', ({ priority, expected }) => { let nangoMock; beforeAll(async () => { nangoMock = new global.vitest.NangoSyncMock({ dirname: __dirname, name: `fetch-tickets-${priority}`, Model: "Ticket" }); }); it(`should map ${priority} to ${expected}`, async () => { await createSync.exec(nangoMock); const savedTickets = await nangoMock.getBatchSaveData('Ticket'); const ticket = savedTickets.find(t => t.rawPriority === priority); expect(ticket?.priority).toBe(expected); }); }); ``` ## Related resources - [Data Validation](/guides/functions/data-validation) - Learn more about schema validation - [Vitest Documentation](https://vitest.dev/) - Official Vitest docs - [Action functions](/guides/functions/action-functions) - Building and testing on-demand functions - [Sync functions](/guides/functions/syncs/sync-functions) - Building and testing sync functions ## CI/CD for Nango Functions Source: https://nango.dev/docs/guides/functions/ci-cd.md Description: How to integrate Nango Function deployments into your CI/CD pipeline. CI/CD for Nango is about keeping your [Functions](/guides/functions/functions-guide) — sync functions, action functions, webhook functions, and event functions — in sync with your application across environments. The core principle: **deploy to Nango as part of the same pipeline step as your application**. Deploying them separately risks your app and your Functions going out of sync, which can cause subtle bugs that are hard to trace. ## Authentication To deploy from CI, you need a scoped API key for each Nango environment. 1. **Create API keys**: In the Nango UI, go to **Environment Settings > API Keys** and create a key for each environment (e.g., `dev` and `prod`). For CI/CD pipelines, use a key scoped to `environment:deploy` only — see the [CI/CD Deploy profile](/reference/backend/http-api/api-keys#cicd-deploy). 2. **Store the keys**: Add them as secrets in your CI/CD provider. The Nango CLI reads them from these environment variables: - `NANGO_SECRET_KEY_DEV` - `NANGO_SECRET_KEY_PROD` ## Recommended pipeline Treat Nango Functions like application code: validate on every PR, deploy to each environment in the same step as your application. | Pipeline stage | What to do | |---|---| | **Pull request** | Run `nango compile` and `npm test` as required checks. Block merge on failure. | | **Merge to main** | Deploy to your dev/staging Nango environment alongside your app. | | **Release to production** | Deploy to your prod Nango environment in the same step as your app release. | The Nango CLI `deploy` command targets a specific environment: ```bash nango deploy ``` If you don't specify an environment, it defaults to `dev`. **If you don't have a staging environment**, wire the prod Nango deploy directly to your production release step — use a `workflow_dispatch` trigger or equivalent manual approval. This keeps production deploys intentional, and ensures they always happen through CI using the prod API key rather than from a local machine. Add the `dist/` folder inside your `nango-integrations` directory to `.gitignore`. The CLI writes compiled JS artifacts there, and committing them creates noisy diffs and risks deploying stale bundles. ## Example: GitHub Actions Two workflow files implement the pipeline above. ```yaml name: Validate Nango Functions on: pull_request: branches: - main jobs: validate: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Install dependencies run: npm ci - name: Compile run: npx nango@latest compile --no-dependency-update - name: Test run: npm test ``` ```yaml name: Deploy Nango Functions on: # Manual trigger to deploy to a specific environment workflow_dispatch: inputs: stage: type: choice description: 'Environment to deploy to' required: true default: 'dev' options: - dev - prod allowDestructive: type: boolean description: 'Allow destructive changes' required: true default: false # Automatic deploy to dev on merge to main push: branches: - main jobs: deploy: runs-on: ubuntu-latest env: NANGO_SECRET_KEY_DEV: ${{ secrets.NANGO_SECRET_KEY_DEV }} NANGO_SECRET_KEY_PROD: ${{ secrets.NANGO_SECRET_KEY_PROD }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Install dependencies run: npm ci - name: Deploy to Nango run: | if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then stage="${{ inputs.stage }}" else stage="dev" fi secret_key_var="NANGO_SECRET_KEY_$(echo "$stage" | tr '[:lower:]' '[:upper:]')" secret_key="${!secret_key_var}" if [ -z "$secret_key" ]; then echo "Error: ${secret_key_var} is not set" exit 1 fi destructive_flag="" if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.allowDestructive }}" == "true" ]; then destructive_flag="--allow-destructive" fi npx nango@latest deploy "$stage" --secret-key "$secret_key" --no-dependency-update $destructive_flag ``` **Destructive Changes** A destructive change removes an integration, sync function, or action function. To prevent accidental deletions, `nango deploy` will prompt for confirmation when it detects one. In CI, use `--auto-confirm` or `--allow-destructive` to bypass the prompt. We recommend only doing this on a manual trigger, as shown above. ## Testing in CI Run your test suite in CI before every deployment. Nango's testing framework uses dry runs and snapshot testing to validate your Functions without affecting live data: ```bash npm test ``` See the [Testing integrations guide](/guides/functions/testing) for details. ## Ephemeral preview environments (e.g. Vercel) If you use ephemeral preview environments — such as Vercel preview deployments — as your staging layer, you'll run into a challenge: each preview has a unique URL, but Nango webhook URLs must be registered in advance and point to a stable destination. Nango doesn't currently support creating environments programmatically. This is on the roadmap and will make ephemeral environment setups much cleaner when available. For now, the simplest approach is to designate a fixed, long-lived Nango environment (e.g., `dev`) specifically for Nango-related changes, and use that consistently across preview deployments rather than trying to create a new Nango environment per preview. If you need previews to receive live webhook events, a proxy server is the current workaround: register a stable URL in Nango, and have the proxy forward incoming webhooks to the correct preview URL. ## Dependency-safe CI and monorepos When running Nango in CI or monorepos, you often want to avoid modifying `package.json` or running extra installs from within Nango commands. Use `--no-dependency-update` on CLI commands, or set `NANGO_CLI_DEPENDENCY_UPDATE=false` as an environment variable: ```bash npx nango@latest deploy dev --no-dependency-update ``` With `--no-dependency-update`, Nango will not install dependencies. Make sure your pipeline does so first (e.g. `npm ci`, `pnpm install --frozen-lockfile`, `yarn install --immutable`, or `bun install --frozen-lockfile`). In CI environments, Nango automatically disables dependency updates to prevent modifying `package.json`. Passing `--no-dependency-update` explicitly silences the related warning. ## Related resources - [Testing integrations](/guides/functions/testing) - A comprehensive guide to testing your Nango Functions. - [API Keys reference](/reference/backend/http-api/api-keys) - Available scopes and advised profiles for CI/CD and other use cases. ## Unified APIs with functions Source: https://nango.dev/docs/guides/functions/unified-apis.md Description: Build provider-specific functions behind one stable model and operation surface. API unification is the process of standardizing multiple provider APIs behind one interface that your product can use consistently. In Nango, unification is optional and code-first. You define the models and operations that fit your product, then implement provider-specific functions that translate to and from each external API. ## When to unify Unification is useful when your customers use different systems for the same business object: - CRMs with contacts, companies, deals, and custom fields. - ATS platforms with candidates, jobs, stages, and applications. - Accounting systems with invoices, customers, transactions, and accounts. - Support tools with tickets, comments, users, and organizations. It is less useful when the provider exposes concepts that are deeply unique. For example, Notion's block model can be partially normalized for simple document export, but advanced block behavior usually needs provider-specific logic. Unification does not need to be perfect to be valuable. A stable common model with explicit provider extensions is often better than trying to force every API into a lowest-common-denominator shape. ## Design principles Design the unified model around your product, not around the external APIs. If your app already has a `Contact`, `Invoice`, or `Candidate` model, use that as the starting point. Keep the fields your workflows actually need, and resist carrying every provider field into the common model. Expect optional fields. Providers rarely expose the same data with the same semantics, and some customers may rely on custom fields or custom statuses. Model those gaps intentionally with nullable fields, fallbacks, metadata, or provider-specific extensions. Use the same model for reads and writes when possible. If a sync writes `UnifiedContact` records into your app, the corresponding create or update action should accept the same shape unless the write operation truly needs a different contract. Validate close to the external API. Use [data validation](/guides/functions/data-validation) in your functions so mapping drift, malformed provider responses, and invalid write inputs fail at the integration boundary. ## Define the model Put shared schemas in a common file and import them from each provider implementation: ```ts models.ts import * as z from 'zod'; export const UnifiedUser = z.object({ id: z.string().optional(), name: z.string().optional(), email: z.string().email().optional(), externalUrl: z.string().url().optional(), raw: z.unknown().optional() }); export const CreateUnifiedUser = z.object({ name: z.string(), email: z.string().email() }); export type UnifiedUser = z.infer; ``` Keep provider-specific fields explicit: ```ts models.ts export const JiraUser = UnifiedUser.extend({ accountType: z.string().optional() }); ``` ## Implement unified actions Each provider gets an action with the same input and output contract: ```ts jira/actions/create-user.ts import { createAction } from 'nango'; import { CreateUnifiedUser, UnifiedUser } from '../../models'; export default createAction({ description: 'Creates a user in Jira', input: CreateUnifiedUser, output: UnifiedUser, exec: async (nango, input) => { const response = await nango.post({ endpoint: '/rest/api/3/user', data: { emailAddress: input.email, displayName: input.name } }); return { id: response.data.accountId, name: response.data.displayName ?? input.name, email: input.email, raw: response.data }; } }); ``` ```ts zendesk/actions/create-user.ts import { createAction } from 'nango'; import { CreateUnifiedUser, UnifiedUser } from '../../models'; export default createAction({ description: 'Creates a user in Zendesk', input: CreateUnifiedUser, output: UnifiedUser, exec: async (nango, input) => { const response = await nango.post({ endpoint: '/api/v2/users.json', data: { user: { name: input.name, email: input.email } } }); return { id: String(response.data.user.id), name: response.data.user.name, email: response.data.user.email, externalUrl: response.data.user.url, raw: response.data.user }; } }); ``` Your app can choose the integration ID at runtime and call the same action name for each provider: ```ts await nango.triggerAction(integrationId, connectionId, 'create-user', { name: 'Ada Lovelace', email: 'ada@example.com' }); ``` ## Implement unified syncs Sync functions are where read-side unification usually lives. Fetch provider records, map each page into your unified model, then save those records to Nango's records cache: ```ts hubspot/syncs/users.ts import { createSync } from 'nango'; import { UnifiedUser } from '../../models'; export default createSync({ description: 'Syncs users into the unified user model', frequency: 'every hour', models: { UnifiedUser }, exec: async (nango) => { for await (const page of nango.paginate({ endpoint: '/crm/v3/objects/contacts', params: { properties: 'email,firstname,lastname' }, paginate: { type: 'cursor', cursor_path_in_response: 'paging.next.after', cursor_name_in_request: 'after', response_path: 'results', limit_name_in_request: 'limit', limit: 100 } })) { const users = page.map((contact) => ({ id: contact.id, name: [contact.properties.firstname, contact.properties.lastname].filter(Boolean).join(' '), email: contact.properties.email, raw: contact })); await nango.batchSave(users, 'UnifiedUser'); } } }); ``` ## Handle provider-specific behavior Use one of these patterns when the common model is not enough: - Extend the common model with provider-specific fields for integrations that need them. - Store raw provider data under a `raw` field for debugging or advanced workflows. - Use connection metadata for customer-specific mappings, such as custom fields, custom statuses, or selected pipelines. - Create provider-specific actions for operations that do not map cleanly to the unified interface. ## Related guides - [Action functions](/guides/functions/action-functions) - [Sync functions](/guides/functions/syncs/sync-functions) - [Data validation](/guides/functions/data-validation) - [Storage](/guides/functions/storage) ## Observability Source: https://nango.dev/docs/guides/platform/observability.md Description: How Nango helps you with observability for your integrations Nango's logs are comprehensive and give you a detailed picture of what happens in your integrations. ## Our approach Since integrations rely on third-party APIs, production issues are bound to happen. When you encounter a problem, great observability is the difference between a week-long needle-in-a-haystack hunt and a timely resolution. We built Nango's logs based on three principles: - **Comprehensive**: Everything that happens in Nango for your integrations produces an "operation" in the logs - **Detailed**: Every operation and every log message contains detailed information about the integration, [connection](/guides/auth/auth-guide#overview), and [function](/guides/functions/functions-guide). Error messages are as detailed as the external APIs allow. - **Customizable**: You can easily log your own messages from functions you write ([learn more](/reference/functions/functions-sdk#logging)). To consume logs, you can either use our built-in interface or export them with our [OpenTelemetry exporter](#opentelemetry-export) ## Overview of Nango logs ![](/images/screenshots/nango-logs-overview.png) Logs are structured into: - Operations: High-level actions happening in your Nango account (e.g., sync executed, connection created, webhook processed) - Log messages: Nested in operations, these provide details on what happened (e.g., HTTP request executed, data validation warning, custom log message) Nango provides detailed filtering and searching of log messages by integration, connection, operation type, status, etc. We recommend exploring the logs in your own Nango account under **Logs**. ## OpenTelemetry export Nango supports exporting OpenTelemetry traces so you can monitor and analyze integration activity in your own observability stack. Use this for: - Advanced custom metrics - Advanced alerting and escalation paths (e.g., segment sync errors by customer account for customized escalation paths) ### Supported operations The following operations are exported as traces: - Sync executions - Action executions - Third-party API webhook executions - Proxied requests If you need support for other operations please reach out. We can enable support for them. ### Configuration Log into the Nango dashboard and navigate to the Environment settings page. In the Environment Settings, provide the following information: - OpenTelemetry endpoint: The endpoint URL of your OpenTelemetry collector. Ex: https://my.otlp.collector:4318 - Headers (Optional): Any authorization headers or additional headers required to access your collector. ## Related guides - [Function logging](/guides/functions/logging) - add custom logs from function code. - [Webhooks from Nango](/guides/platform/webhooks-from-nango#webhook-retries-debugging) - debug webhook delivery. - [Common issues](/guides/platform/common-issues) - troubleshoot failed auth, sync, and proxy operations. ## Environments Source: https://nango.dev/docs/guides/platform/environments.md Description: Organize your integrations & data with environments Environments help you segment your integration configuration and customer data across different stages of your development lifecycle. ## Overview Each environment in your Nango account is completely isolated with its own: - Integration configurations - [Functions](/guides/functions/functions-guide) - [Connections](/guides/auth/auth-guide) - Environment-specific settings Each Nango account comes with **dev** and **prod** environments by default. Additional environments are available depending on your [pricing plan](https://www.nango.dev/pricing). ## Production environments Any environment can be designated as a production environment. This affects how team members interact with it based on their [role](/guides/platform/security#team-and-roles). **To mark an environment as production:** 1. Open the environment settings (gear icon in the top navigation) 2. Toggle the **Production environment** switch Only **Full Access** team members can toggle the production flag. Once an environment is marked as production, **Support** members get read-only access to it, and **Contributor** members lose access entirely. By default, the `prod` environment created with your account is already marked as production. ## Engineering collaboration Use a **shared non-production environment** (usually `dev`) for day-to-day engineering work. The team shares integrations and the baseline function set. For local webhook testing, each engineer creates their own connections with a webhook URL override (see below). ### Local webhooks on a shared environment Keep the environment webhook URL pointed at your shared app (or staging backend). To receive webhooks from Nango locally, create connections with a [per-connection `webhook_url` override](/guides/platform/webhooks-from-nango#override-webhook-urls-per-connection) pointing at your local webhook endpoint (for example via [ngrok](https://ngrok.com/)). One convenient pattern is to read the override from an optional env var, so the same connect-session code works locally and when the var is unset. For example, set `NANGO_CONNECTION_WEBHOOK_URL` in your local `.env` to that URL and leave it unset elsewhere: ```ts const webhookUrl = process.env.NANGO_CONNECTION_WEBHOOK_URL; // e.g. https://.ngrok.app/webhooks-from-nango const session = await nango.createConnectSession({ tags: { end_user_id: '' }, allowed_integrations: [''], ...(webhookUrl && { integrations_config_defaults: { '': { connection_config: { webhook_url: webhookUrl } } } }) }); ``` When the var is set, new connections from that session route webhooks to your machine without changing the environment webhook URLs. ### Local function deploys on a shared environment A full `nango deploy` reconciles every function in the environment. On a shared collaborative environment, that can overwrite teammates' deploys. Deploy only what you changed: ```bash nango deploy --sync nango deploy --action nango deploy --integration # all functions for one integration, including event functions ``` Reserve full deploys for environments where the repo is the sole source of truth (for example staging or prod). See the [CI/CD guide](/guides/functions/ci-cd). ### One environment per engineer You can give each engineer their own environment for full isolation. You then need to recreate integrations, connections, and settings in each environment yourself; that also means more API keys to manage and config that tends to drift over time. A shared `dev` environment avoids that overhead for most teams. ## Best practices **Mirror your application environments** We recommend creating Nango environments that match your application's deployment stages. For example: - Development / local → dev environment - Staging → staging environment - Production → prod environment - Demo → demo environment **Deploy to specific environments** When you deploy [Functions](/guides/functions/functions-guide), you always target a specific environment. This ensures changes are tested before reaching production. ## Related guides - [API keys](/reference/backend/http-api/api-keys) - scope keys per environment. - [CI/CD](/guides/functions/ci-cd) - deploy functions safely across environments. - [Webhooks from Nango](/guides/platform/webhooks-from-nango) - environment and per-connection webhook URLs. - [Self-host Nango](/guides/platform/self-hosting) - plan environments for self-hosted deployments. ## Limits Source: https://nango.dev/docs/guides/platform/limits.md Description: Overview of resource limits on Nango Cloud. Nango Cloud has several resource limits to ensure platform stability and fair usage across all customers. For plan-specific limits, refer to the [pricing page](https://www.nango.dev/pricing) and the Usage & Billing page in your account. ## API rate limits Rate limits apply to API requests from your application to Nango: | Plan | Requests per Minute | | - | - | | Free tier | 200 | | Starter | 1,000 | | Growth | 2,000 | | Enterprise | Custom | If you exceed the rate limit, use the standard rate-limit headers to determine when to resume requests. ## Function execution limits Different function types have varying execution and enqueuing time limits: | Function Type | Max Execution Time | | - | - | | [Sync functions](/guides/functions/syncs/sync-functions) | 24 hours | | [Action functions](/guides/functions/action-functions) | 15 minutes | | [Webhook functions](/guides/functions/webhook-functions) | 1 hour | ### Minimum sync frequency For polling syncs, the minimum frequency is **30 seconds**, available on all plans. You can set the frequency in the sync definition or override it per connection using the [SDK](/reference/backend/backend-sdk/node#override-sync-connection-frequency) or [API](/reference/backend/http-api/sync/update-connection-frequency). For real-time updates, use [real-time syncs](/guides/functions/syncs/realtime-syncs) which trigger from external API webhooks instead of polling. ### Sync variant limits Each sync can have a maximum of **100 variants** per connection. For heavier workloads, reach out to increase this limit. See the [sync partitioning guide](/guides/functions/syncs/sync-partitioning) for more details. ### Action function output limits Action functions have a maximum output payload size of **2MB**. For large payloads, use the [Proxy](/guides/platform/proxy-requests) instead. ## Data retention policies Nango automatically manages data retention for records in the sync cache: ### Payload pruning (30 days) If a record hasn't been updated for **30 days**, Nango automatically prunes its payload while preserving: - Record ID - Sync state - Payload hash (for change detection) The record still exists and Nango can detect future changes, but the data fields are no longer retrievable from the cache. ### Hard deletion (60 days) If a sync hasn't executed for **60 days**, all associated records (payload + metadata) are permanently deleted. These policies ensure sensitive data isn't stored indefinitely. If you need to access record payloads long after updates (30+ days), please reach out in the [Slack community](https://nango.dev/slack). ## Webhook limits Webhooks sent from Nango to your application have the following limits: - **Timeout**: 20 seconds - **Retries**: 2 attempts These limits apply to all webhook types: auth, sync function completion, async action function completion, and webhook forwards. ## Related guides - [Rate limits](/guides/functions/rate-limits) - handle provider throttling in functions. - [Sync efficiency](/guides/functions/syncs/sync-efficiency) - stay within payload and runtime limits. - [Security](/guides/platform/security) - review retention and access constraints. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Security Source: https://nango.dev/docs/guides/platform/security.md Description: How Nango secures your API credentials and customer data. Nango is designed with security as a core principle. This page provides technical details about how Nango stores, encrypts, and manages API credentials. ## Compliance Nango is **SOC 2 Type II** certified, **GDPR** compliant, and **HIPAA** compliant. | Certification | Details | | - | - | | SOC 2 Type II | Annual audit covering security, availability, and confidentiality. [View report →](https://trust.nango.dev) | | GDPR | Data Processing Agreement (DPA) automatically applies to all cloud accounts. [View DPA →](https://nango.dev/terms#dpa) | | HIPAA | Business Associate Agreement (BAA) available on request. [Contact us →](https://nango.dev/demo) | Our security practices include: - Regular security assessments and penetration testing - Secure development lifecycle - Incident response procedures - Continuous monitoring and alerting Visit our [Trust Center](https://trust.nango.dev) for the latest compliance documentation, security policies, and audit reports. ## Credential storage ### What credentials are stored Nango stores the following credential types depending on your API integrations: - **OAuth tokens**: Access tokens, refresh tokens, and associated metadata - **API keys**: For APIs using key-based authentication - **Client credentials**: OAuth client IDs and secrets for your integrations - **Connection configuration**: OAuth scopes, authorization parameters, and provider-specific settings ### Storage infrastructure Nango Cloud stores all data in **AWS Aurora PostgreSQL** databases hosted in AWS. The database infrastructure includes: - Encryption at rest using AWS-managed keys - Automated backups with point-in-time recovery - Multi-AZ deployment for high availability - Network isolation within a private VPC For self-hosted deployments, you control the database infrastructure and can apply your own security policies. ## Encryption ### Encryption at rest All sensitive credentials are encrypted before being stored in the database using **AES-256-GCM** (Advanced Encryption Standard with Galois/Counter Mode). | Property | Value | | - | - | | Algorithm | AES-256-GCM | | Key size | 256 bits (32 bytes) | | IV size | 12 bytes (generated per encryption) | | Auth tag size | 16 bytes | ### What is encrypted The following data types are encrypted at rest: - Connection credentials (OAuth tokens, API keys, etc.) - OAuth client secrets (your integration credentials) - Environment secret keys - Environment variables ### Key management - **Nango Cloud**: Encryption keys are securely managed by Nango. All credentials are encrypted at rest. - **Self-hosted**: You must provide your own encryption key to enable encryption at rest. ### Encryption in transit All data transmitted to and from Nango is encrypted using TLS 1.2+. This includes: - API requests between your application and Nango - Requests from Nango to external APIs (using each provider's TLS configuration) - Dashboard access ## Data retention ### Active connections Credentials are stored for as long as the connection exists. You have full control over connection lifecycle: - Create connections when users authorize integrations - Delete connections at any time via the API or dashboard - Connections can be programmatically managed through your application #### Retention after deletion When a connection is deleted: 1. **Immediate soft delete**: Connection is marked as deleted and becomes inaccessible 2. **Associated data cleanup**: Sync functions are stopped, and scheduled tasks are cancelled 3. **Hard delete after retention period**: Credentials and associated data are permanently removed from the database The default retention period is **31 days**. For self-hosted deployments, this is configurable. This retention period allows for: - Recovery from accidental deletions - Compliance with audit requirements - Graceful handling of in-flight operations #### What is deleted When a connection is permanently deleted, the following data is removed: - All stored credentials (tokens, keys, secrets) - Connection metadata and configuration - Sync records associated with the connection - Related job history and logs ### Synced records retention This section only applies if you are using Functions of type Sync, e.g. for replicating data or receiving trigger notifications from an external API. Nango automatically manages data retention for records stored in the Nango records cache to ensure that sensitive user data is not stored indefinitely when it's no longer needed. #### Automated payload pruning (30 days) If a record has not been updated for **30 days**, Nango will automatically empty its payload. **Impact:** After pruning, the record's data fields are no longer retrievable from the Nango cache via the API or SDK. However, Nango retains the metadata needed for change detection (record ID and payload hash). Your sync functions will continue to work normally. This pruning does not impact sync execution or delta-detection capabilities. #### Automated hard deletion (60 days) If a sync has not executed for **60 days**, all records belonging to that sync will be permanently deleted. **Impact:** Complete removal of all records from the inactive sync, including metadata and payload hashes used for delta-detection. The sync itself remains configured but starts fresh on the next execution. Nango will no longer be able to detect changes against previously synced records. #### When do retention policies apply? - **30-day pruning countdown**: Starts from the last time a specific record was updated. - **60-day deletion countdown**: Starts from the last time a sync successfully executed. #### Best practices To work optimally with Nango's retention policies: **1. Fetch records promptly** Set up [webhooks from Nango](/guides/platform/webhooks-from-nango) to be notified when new data is available. Fetch the updated records immediately after receiving a sync webhook. **2. Use cursor-based synchronization** Track your sync progress using cursors to ensure you never miss records, even if webhooks are occasionally missed. Store the cursor of the last-fetched record and use it on subsequent fetches. **3. Store data in your own system** Don't use Nango's cache as your primary data store. Instead: - Store synced records in your own database - Use Nango as a synchronization pipeline - Keep your own copy of all critical data #### Longer retention requirements If your use case requires accessing record payloads from Nango's cache beyond the 30-day window, please reach out to the Nango team. We can work with you to ensure you don't experience breaking changes. ## Deletion procedures ### User-initiated deletion You can delete connections through: - **Dashboard**: Navigate to the connection and click "Delete" - **API**: Call the [delete connection endpoint](/reference/backend/http-api/connections/delete) - **SDK**: Use the `deleteConnection` method in any of our backend SDKs ### Automatic cleanup Nango automatically cleans up: - Expired OAuth sessions - Expired connect session tokens - Orphaned data from deleted integrations ### Data purge requests For compliance requirements (e.g., GDPR), contact Nango support to request immediate data purges. We can expedite the deletion process when required by regulation. ## Access controls ### API authentication Nango supports multiple authentication methods: | Method | Use case | Token lifetime | | - | - | - | | Secret key | Server-to-server API access | Long-lived (until rotated) | | Connect session token | Frontend auth flows | 30 minutes | | Session cookie | Dashboard access | Session-based | ### Secret key security - Secret keys are environment-specific (dev, prod, etc.) - Keys can be rotated via the dashboard - All API requests require a valid secret key in the `Authorization` header ### Environment isolation Each [environment](/guides/platform/environments) in your Nango account is completely isolated: - Separate credentials and connections - Separate integration configurations - Separate secret keys This ensures your production data is never accessible from development environments. ### Audit logging All credential access and modifications are logged: - Connection creation and deletion - Credential refresh operations - API requests using credentials Logs are available in the Nango dashboard and can be exported via [OpenTelemetry](/guides/platform/observability#opentelemetry-export). ## Team & roles Nango supports multiple team members per account with role-based access control (RBAC). Roles determine what each member can view and modify, particularly in production environments. ### Role overview There are three roles: | | **Full Access** | **Support** | **Contributor** | | - | - | - | - | | **Summary** | Unrestricted access across all environments. The only role that can manage account settings, billing, and the team. | Designed for monitoring production without being able to change it. Full access in non-production environments. | Designed for developers who work exclusively in non-production environments. No production access at all. | **Full Access is the default role** for just-in-time (JIT) provisioning when using SSO. When inviting members manually, you choose their role during the invitation flow. At least one Full Access member is required per account. ### Permissions | | **Full Access** | **Support** | **Contributor** | | - | - | - | - | | Non-production: read (integrations, connections, syncs, logs) | ✓ | ✓ | ✓ | | Non-production: write / delete | ✓ | ✓ | ✓ | | Production: read (integrations, connections, syncs, logs) | ✓ | ✓ | — | | Production: trigger syncs | ✓ | ✓ | — | | Production: write / delete | ✓ | — | — | | Production: view secret key | ✓ | — | — | | Production: view integration credentials | ✓ | — | — | | Production: view connection credentials | ✓ | — | — | | Invite / update / remove team members | ✓ | — | — | | Manage billing & plan | ✓ | — | — | | Configure Connect UI | ✓ | — | — | | Create environments / toggle production flag | ✓ | — | — | All environments appear in the dashboard switcher regardless of role, but access is enforced server-side — users are blocked from entering environments their role does not permit. Permissions cannot be bypassed by manipulating URLs or API parameters. ### Production environment flag Whether an environment is considered production is configured in **Environment Settings → General**. The production status of the current environment is also visible in the environment dropdown at the top left of the dashboard. The flag can be set on any environment regardless of its name — not just `prod`. This lets you protect environments like `prod-eu`, `production`, or `live` the same way. Only **Full Access** members can toggle this flag. Once set, Support members get read-only access (without sensitive credentials) and Contributor members lose access entirely. The production flag currently governs role-based access. In the future it may also control additional environment traits to differentiate production-grade environments from development or staging ones. These roles apply to dashboard access only. They do not affect API authentication — any caller with a valid secret key retains full API access regardless of their dashboard role. ## Single sign-on (SSO) Nango Cloud uses [WorkOS](https://workos.com) to power SSO. ### Availability | Plan | SSO support | | - | - | | Free / Starter | Google SSO only | | Growth | SSO with 20+ identity providers (add-on) | | Enterprise (Cloud) | SSO with 20+ identity providers (included) | | Enterprise (Self-hosted) | Not yet available (planned) | ### Supported providers Through WorkOS, Nango supports the main SSO providers, including **Okta**, **Entra ID**, **OneLogin** and 20+ other identity providers. ### How to enable SSO 1. To enable SSO for your account, contact the Nango team (on our shared Slack channel or the [community](https://nango.dev/slack)). 2. If you are on the Growth plan, we will subscribe your workspace to the SSO add-on. 3. We will invite an admin from your company to complete the SSO setup flow. ### Setup flow During setup, your company admin can configure the **domains allowed to sign in**. User accounts are created automatically through just-in-time (JIT) provisioning. ### How users log in Once SSO is configured, users sign in through your company's identity provider (IdP). Nango currently does not provide a dedicated SSO button on the login page — login must be initiated from the IdP side. ## IP Allowlist Some integrations require network-level access controls that only allow inbound traffic from known IP addresses. This page lists the **public IP addresses used by Nango services** when making outbound requests. ### When is IP allowlisting required? IP allowlisting is only required if **your customers' systems** restrict inbound network traffic. Common examples include: - Private or self-hosted APIs behind a firewall - Enterprise systems that only accept traffic from approved IPs - Third-party services with IP-based access controls If a system is publicly accessible without IP restrictions, **no allowlist configuration is needed**. ### Nango public IP addresses Allowlist the following IP addresses to permit inbound traffic from Nango: - `52.34.139.153` - `54.69.127.183` - `44.247.133.183` - `52.26.211.56` ### IP address changes While we aim to keep IP addresses stable, they **may change over time** as we scale or improve reliability. To minimize disruption: - Ensure all listed IPs are allowlisted - Periodically review this page for updates We will make reasonable efforts to communicate breaking changes in advance. ## API Key Scoping Nango supports scoped API keys, allowing you to follow the principle of least privilege when granting programmatic access to your environments. Each API key can be restricted to specific operations — for example, a CI/CD pipeline key that can only deploy, or a backend service key that can read connections but not manage integrations. For details on available scopes, advised profiles, and key management, see the [API Keys reference](/reference/backend/http-api/api-keys). **Questions about security?** Please reach out in the [Slack community](https://nango.dev/slack) or contact security@nango.dev. ## Related guides - [API keys](/reference/backend/http-api/api-keys) - create scoped keys for production access. - [Auth guide](/guides/auth/auth-guide) - keep provider credentials out of your app. - [Token refreshing and validity](/guides/auth/token-refreshing) - handle revoked OAuth credentials. - [Self-host Nango](/guides/platform/self-hosting) - review deployment and data ownership options. ## Self-host Nango Source: https://nango.dev/docs/guides/platform/self-hosting.md Description: Overview of self-hosted deployments. Nango offers a self-hosted deployment option for Enterprise customers. This guide covers deployment, scaling, updates, and other key operational details. ## Architecture overview ## Architecture components Nango consists of several core services, each handling specific responsibilities: - **Server (Node service)**: Powers the dashboard, API, proxy requests, and incoming/outgoing webhooks. - **Orchestrator (Node service)**: Manages task scheduling and state tracking. - **Jobs (Node service)**: Processes tasks and dispatches them to the Runner. - **Runner (Node service)**: Executes integration code and interacts with external APIs. - **Persist (Node service)**: Stores synced records and logs. - **Postgres**: Stores data for the control plane, API credentials, scheduled tasks, and synced records. - **Object Storage (e.g. S3)**: Stores compiled integration code for execution by the Runner. - **ElasticSearch**: Stores execution data. - **Redis**: Caches system data, including socket information, token refresh locks, and rate limits. ## Cloud vs. self-hosted architecture The Nango architecture is largely the same for both Cloud and Enterprise self-hosting. This ensures self-hosted instances benefit from continuous dogfooding and load testing. The primary differences are: - In the Cloud version, the Runner service runs as isolated instances per customer. - The Postgres database is segmented by use case (control plane, task scheduling, synced records). ## Features All paid features available on Nango Cloud are also included in the Enterprise self-hosted edition. ## Plan requirement An [Enterprise plan](https://www.nango.dev/pricing) subscription is required. Enterprise Self-Hosted pricing contain a fixed annual license and maintenance fee, plus a fraction of the cloud usage-based fees since infrastructure is on the customer side. ## Intended users Inteded for large and/or regulated enterprises. ## Deployment By default, Nango is deployed using [Helm charts](https://github.com/NangoHQ/nango-helm-charts). Custom deployments (e.g., ECS) are possible with our guidance. ## Updates Managed image updates are published on a **two-month cadence**, with occasional **hotfixes** as needed. Notifications about new releases will be posted to your dedicated Nango Slack channel. You can also subscribe to release notifications on the [`NangoHQ/managed-image-releases`](https://github.com/NangoHQ/managed-image-releases) repository: - **GitHub:** watch the repository, select **Custom**, and enable **Releases**. - **RSS/Atom:** subscribe to `https://github.com/NangoHQ/managed-image-releases/releases.atom`, which can be wired into Slack, Microsoft Teams, RSS readers, or internal automation. Each release includes the managed image tag, application version, source commit hash, release date, Docker pull reference, a GitHub compare link to the previous managed release, and a generated changelog of changes since the previous managed release. ### Image tags Managed image tags follow this format: ``` nangohq/nango:managed-{managed-release-version}-{application-version}-{commit-sha} ``` * `managed-release-version`: Semantic version for the managed image lifecycle (major = breaking, minor/patch = features/fixes) * `application-version`: Semantic version of the Nango application baked into the image * `commit-sha`: Full Git commit hash of the released source For example, pull a release from Docker Hub with: ```bash docker pull nangohq/nango:managed-1.5.4-0.70.4-0fe07a6d83aea0becc4cea382c5cead2f555718d ``` Customers should **pin their CLI version to the `application-version`** specified in the tag for compatibility. ### Versions and policy You can find the latest version in [`managed-manifest.json`](https://github.com/NangoHQ/managed-image-releases/blob/main/managed-manifest.json) on `managed-image-releases` (mirrored from [`NangoHQ/nango`](https://github.com/NangoHQ/nango/blob/master/managed-manifest.json) on each release). The in-repo [`CHANGELOG.md`](https://github.com/NangoHQ/managed-image-releases/blob/main/CHANGELOG.md) tracks release history. Each managed release maps to a specific source commit, and published image tags are never changed after release. Customers are encouraged to stay reasonably current with managed image releases. See the full [changelog](/updates/changelog) for details on each release. ## Cloud provider support Supports all major cloud providers (AWS, GCP, Azure). ## Recommended configuration - **5 Node services** (Server, Persist, Runner, Jobs, Orchestrator): 1 CPU, 2GB RAM per service - **Postgres database**: 2 CPU, 8GB RAM, 128GB storage - **Redis data store**: 128MB - **ElasticSearch data store**: 2 vCPU, 1GB RAM, 30GB storage - **Object storage (e.g. S3)**: less than 500MB of storage ## Scaling The default configuration supports 1M+ sync/action executions per day (assuming ~2s execution time per action/sync). Auto-scaling is not provided out-of-the-box yet, but the default configuration scales far. We can guide you on configuring auto-scaling when needed. Bottlenecks mostly depend on: - **Action/sync execution time**: solved by scaling the Runner service vertically, then horizontally. - **Cached records & size (for sync functions only)**: solved by scaling Postgres vertically. ## Data storage - **Postgres**: Stores data for the control plane, API credentials, scheduled tasks, and synced records. - **Object Storage (e.g. S3)**: Stores compiled integration code for execution by the Runner. - **ElasticSearch**: Stores execution data. - **Redis**: Caches system data, including socket information, token refresh locks, and rate limits. ## Using existing data stores Yes, Nango is flexible with data store setups. However, we recommend a separate instance for independent scaling. ## Internet access requirements - **Server:** Required for proxy requests, credential management, and incoming/outgoing webhooks (inbound & outbound traffic). - **Runner:** Required for reading/writing data from external APIs during sync and action executions (outbound traffic only). ## Exporting metrics & logs Yes, metrics and logs can be exported to any monitoring tool using our OpenTelemetry Export add-on. Additional metrics and logs can be added upon request. ## Email service Nango uses emails for account verification, password reset, and sending invitations, etc... Any SMTP server can be configured to be used by Nango for these email communications. ## Encryption key You must provide your own encryption key via the `NANGO_ENCRYPTION_KEY` environment variable to enable encryption at rest. It encrypts credentials in the control-plane database as well as data in the records cache. Without this key, credentials are stored unencrypted. The records cache always requires this key. If you run sync functions that persist records, the persist and records services fail to store or retrieve records when `NANGO_ENCRYPTION_KEY` is not set—they do not fall back to plaintext. The encryption key must be a base64-encoded 256-bit (32-byte) key. Key rotation is not supported yet—changing the key after initial setup will cause decryption failures. Plan your key management accordingly. ## Data retention configuration The default retention period for deleted connections is 31 days. You can configure this via the `CRON_DELETE_OLD_CONNECTIONS_MAX_DAYS` environment variable. ## Free self-hosting A limited free self-hosting option is available for hobby projects. It is intended for lightweight deployments that need Auth and Proxy, without the managed features and support included with Enterprise self-hosting or Nango Cloud. For more details, see the [pricing page](https://nango.dev/pricing) or [schedule a call](https://nango.dev/demo) to discuss the Enterprise self-hosted version. ### Feature availability | Feature | Free self-hosted | Enterprise self-hosted / Nango Cloud | | --- | --- | --- | | Auth | Yes | Yes | | Proxy | Yes | Yes | | Observability | Auth + proxy only | Full | | OpenTelemetry export | No | Yes | | Functions | No | Yes | | Webhooks | No | Yes | | MCP server | No | Yes | | Customize auth branding | No | Yes | | Role-based permissions | No | Yes | | SAML SSO | No | On the roadmap | | Support SLA | No | Yes | ### Server URL, callback URL, and custom domains Add server environment variables for the instance URL and port in the `.env` file or directly in your hosting provider: ```sh NANGO_SERVER_URL= SERVER_PORT= ``` The resulting OAuth callback URL is `/oauth/callback`. You can customize the callback URL by updating the "Callback URL" field in the "Environment Settings" tab in the Nango admin. If you are using a custom domain, update `NANGO_SERVER_URL` to match it. ### Connect UI Nango Connect is available for self-hosted deployments in the main Docker image. ```sh FLAG_SERVE_CONNECT_UI=true NANGO_CONNECT_UI_PORT=3009 NANGO_PUBLIC_CONNECT_URL= ``` Nango Connect is available by default at `http://localhost:3009`. If you are using a custom domain, update `NANGO_PUBLIC_CONNECT_URL` to match it. See [Auth](/guides/auth/auth-guide) for the end-user connection flow. ### Persistent storage If you deploy with Docker Compose, the bundled database uses local container storage. This is not appropriate for production. Connect Nango to an external Postgres database by setting the database environment variables: ```sh NANGO_DB_USER= NANGO_DB_PASSWORD= NANGO_DB_HOST= NANGO_DB_PORT= NANGO_DB_NAME= NANGO_DB_SSL=true ``` You can also use a database URL: ```sh NANGO_DATABASE_URL=postgresql://user:password@host:port/dbname ``` Special characters in `NANGO_DATABASE_URL` must be URL encoded. Nango is incompatible with connection poolers using `pool_mode=transaction`. Use a direct database connection or configure the pooler to use a different mode. Records saved by sync functions can be persisted in a dedicated database. To use a dedicated records database, set `RECORDS_DATABASE_URL`: ```sh RECORDS_DATABASE_URL=postgresql://user:password@host:port/dbname ``` Special characters in `RECORDS_DATABASE_URL` must be URL encoded. If it is not specified, records are stored in the main database. Deploying with Render or Heroku automatically generates a persistent database connected to your Nango instance. ### External Redis The bundled Redis is fine for local use but not for production. Connect Nango to an external Redis (or Valkey) with either a full URL or discrete variables: ```sh NANGO_REDIS_URL=rediss://:@: # or NANGO_REDIS_HOST= NANGO_REDIS_PORT= NANGO_REDIS_AUTH= ``` Use the `rediss://` scheme (or discrete variables, which default to TLS) to enable in-transit encryption. #### IAM / short-lived token authentication Managed Redis with IAM authentication (for example GCP Memorystore for Valkey) uses a short-lived token as the password and requires the token to be refreshed before it expires. Instead of a static `NANGO_REDIS_AUTH`, point Nango at a file that an external process (such as a sidecar) keeps up to date: ```sh NANGO_REDIS_HOST= NANGO_REDIS_PORT= NANGO_REDIS_AUTH_TOKEN_FILE=/path/to/token # re-read on every (re)connect NANGO_REDIS_USERNAME= # optional; defaults to "default" ``` Nango reads the token file on every connection and reconnection, so a rotated token is always picked up without a restart. This is cloud-agnostic: anything that writes the current token to the file works. The writer must update the file atomically — write a temporary file and `rename()` it into place — so a reconnect never reads a half-written token and fails authentication. When `NANGO_REDIS_AUTH_TOKEN_FILE` is set, do not embed credentials in `NANGO_REDIS_URL`. The runner boundary has the same set of variables prefixed with `NANGO_CUSTOMER_REDIS_` (for example `NANGO_CUSTOMER_REDIS_AUTH_TOKEN_FILE`); it falls back to the system Redis when unset. ### Securing your instance #### Proxy base URL override hardening The [proxy](/guides/platform/proxy-requests) can send authenticated requests to external APIs. Some proxy calls accept a **base URL override** (HTTP header `Base-Url-Override`, SDK `baseUrlOverride`, or an integration `custom.baseUrl`) to target a host that differs from the provider's default API base URL. Because the proxy makes outbound HTTP requests from your Nango server (and from the runner for sync and action scripts), a caller with permission to use the proxy could use an override to reach hosts that were not meant to be exposed—such as cloud metadata services or `localhost` on the host making the request. This is a classic **SSRF** risk. By default, Nango keeps base URL override **enabled** and blocks override targets and redirect hops whose hostnames match a built-in denylist (cloud metadata and loopback addresses). Configure these environment variables and restart the **server** and **runners** after changes: ```sh NANGO_PROXY_BASE_URL_OVERRIDE_ENABLED=true NANGO_PROXY_BASE_URL_OVERRIDE_DENYLIST='["169.254.169.254","metadata.google.internal","localhost","127.0.0.1","[::1]"]' ``` | Variable | Default | Purpose | | - | - | - | | `NANGO_PROXY_BASE_URL_OVERRIDE_ENABLED` | `true` | Set to `false` to reject all base URL overrides | | `NANGO_PROXY_BASE_URL_OVERRIDE_DENYLIST` | Secure defaults when unset | JSON array of hostnames or URLs to block; custom entries are merged with defaults | | `NANGO_OUTBOUND_URL_POLICY` | Secure defaults when unset | JSON object controlling SSRF protection for proxy, customer webhooks, and `uncontrolledFetch` | **Operator guidance:** - **Production:** keep the default denylist. Add environment-specific hosts if your deployment exposes additional internal endpoints. - **Legitimate localhost overrides (dev only):** set `NANGO_PROXY_BASE_URL_OVERRIDE_DENYLIST='[]'` to restore fail-open behavior. - **No overrides at all:** set `NANGO_PROXY_BASE_URL_OVERRIDE_ENABLED=false`. Denylist matching is hostname-based and applies to redirect hops as well as the initial override target. #### Outbound URL policy (DNS rebinding, private IPs, redirects) Beyond the hostname denylist, Nango routes every outbound connection on the **proxy**, **customer webhook delivery**, and **`uncontrolledFetch`** paths through DNS-pinning agents that re-validate the *resolved* IP address — closing DNS-rebinding and redirect-to-internal-address SSRF holes. The behavior is controlled by `NANGO_OUTBOUND_URL_POLICY`, a JSON object (applied on top of the denylist). It propagates automatically to runners. The `mode` field selects how hostnames are filtered: - **`denylist`** (default): every destination is reachable except hostnames on the denylist (plus the IP-based protections below). This is the out-of-the-box mode, since the denylist always carries the secure defaults. - **`allowlist`**: only hostnames in `allowlist` are reachable (a leading `.` matches subdomains); everything else is rejected. The denylist and the IP-based protections still apply on top, so allowlist is strictly *more* restrictive — a listed hostname that resolves to a blocked IP is still rejected. - **`permissive`**: no hostname-based filtering (the IP-based protections below still apply). You only reach this mode deliberately — either by emptying the server denylist with no explicit mode (see below), or by setting `mode: "permissive"`. The IP-based protections apply in **every** mode, including `permissive`. Loopback (`127.0.0.0/8`, `::1`) and unspecified addresses are **always** blocked; private/RFC1918/CGNAT addresses (`blockPrivateIps`) and link-local addresses — including the cloud-metadata IP `169.254.169.254` (`blockLinkLocal`) — are blocked by default. The denylist is **never empty by default** — it is always seeded with the secure defaults (`localhost`, `metadata.google.internal`, `169.254.169.254`, â€Ļ). It only becomes empty if you explicitly opt out: either `NANGO_PROXY_BASE_URL_OVERRIDE_DENYLIST='[]'` (server only, which then selects `permissive`) or `mode: "permissive"`. Even then, only hostname string matches such as `localhost` are dropped — literal or resolved internal IPs stay blocked unless you also set `blockPrivateIps`/`blockLinkLocal` to `false`. Runners re-apply the secure denylist defaults even when the server has emptied its denylist; note an explicit `mode: "permissive"` empties the denylist everywhere, runners included. ```sh # Block private/link-local IPs (default) and cap redirects at 3 NANGO_OUTBOUND_URL_POLICY='{"blockPrivateIps":true,"blockLinkLocal":true,"maxRedirects":3}' # Allowlist mode: only the listed hostnames (and their subdomains) are reachable NANGO_OUTBOUND_URL_POLICY='{"mode":"allowlist","allowlist":[".hubspot.com","api.github.com"]}' ``` | Field | Default | Purpose | | - | - | - | | `mode` | `denylist` (auto-`permissive` only if you empty the denylist) | `denylist`, `allowlist`, or `permissive` (see above) | | `allowlist` | `[]` | In `allowlist` mode, hostnames (a leading `.` matches subdomains) that may be reached | | `blockPrivateIps` | `true` | Block RFC1918 / CGNAT addresses (all modes) | | `blockLinkLocal` | `true` | Block link-local addresses incl. `169.254.169.254` (all modes) | | `maxRedirects` | `5` | Maximum redirect hops followed | Each resolved address (including on every redirect hop) is validated; in `allowlist` mode, only listed hostnames are reachable regardless of IP. #### Securing the dashboard By default, the dashboard of your Nango instance is open to anyone who can access your instance URL. You can secure it with Basic Auth by setting the following environment variables and restarting the server: ```bash FLAG_AUTH_ENABLED=false NANGO_DASHBOARD_USERNAME= NANGO_DASHBOARD_PASSWORD= ``` #### Encrypting sensitive data You can enforce encryption of sensitive data, including tokens, secret keys, and app secrets, by setting a 256-bit base64-encoded key: ```sh openssl rand -base64 32 ``` Set the generated value as `NANGO_ENCRYPTION_KEY`: ```sh NANGO_ENCRYPTION_KEY= ``` After you restart the Nango server, database encryption happens automatically. The encryption key cannot currently be modified after it is set. `NANGO_ENCRYPTION_KEY` is also required for Connect UI. It is used to generate sessions for [Connect UI](/reference/frontend/frontend-sdk#connect-using-nango-connect-ui). Without this key, Connect UI will not work. #### Custom websockets path The Nango server serves websockets from `/` by default for use by `@nangohq/frontend` during the login flow. To isolate websockets from the dashboard, set `NANGO_SERVER_WEBSOCKETS_PATH`: ```sh NANGO_SERVER_WEBSOCKETS_PATH= ``` If you set a custom path, configure `websocketsPath` when initializing the `Nango` object in the `@nangohq/frontend` SDK: ```js import Nango from '@nangohq/frontend'; let nango = new Nango({ host: 'https://', websocketsPath: '' }); ``` ### Telemetry Self-hosted instances do not automatically send telemetry back to Nango. Operational metrics and logs stay within your own infrastructure and are only exported if you configure the OpenTelemetry Export add-on described above. ### Logs Nango stores execution logs and powers the logs UI with either Elasticsearch or OpenSearch. To keep free self-hosted deployments lighter, this stack is optional. To enable logs: - Host an Elasticsearch or OpenSearch cluster. - Set `NANGO_LOGS_ENABLED=true`. - Configure the relevant `NANGO_LOGS_ES_*` environment variables (these apply to both providers). Nango uses Elasticsearch by default. To use OpenSearch instead, set: ```sh NANGO_LOGS_PROVIDER=opensearch ``` Elasticsearch hosting options include: - Local: uncomment the service in [`docker-compose.yaml`](https://github.com/NangoHQ/nango/blob/master/docker-compose.yaml#L63) and run `docker-compose up`. - Elastic Cloud: use [elastic.co](https://www.elastic.co/). - Render: deploy an Elasticsearch instance with [Render](https://docs.render.com/deploy-elasticsearch). If `NANGO_LOGS_ENABLED` is `false`, logs are sent to stdout and can be viewed in your host logs. ### Run and update Nango To install Nango on a VM: ```bash mkdir nango && cd nango wget https://raw.githubusercontent.com/NangoHQ/nango/master/docker-compose.yaml docker-compose up -d ``` To update Nango: ```bash docker-compose stop docker-compose rm -f docker-compose pull docker-compose up -d ``` If you are interested in the Enterprise self-hosting version, please get in touch with us in the [community](https://nango.dev/slack) or [book a demo](https://nango.dev/demo). ## Related guides - [Security](/guides/platform/security) - review data, encryption, and access controls. - [Environments](/guides/platform/environments) - organize dev, staging, and production setups. - [Changelog](/updates/changelog) - track changes that affect self-hosted upgrades. ## Proxy requests Source: https://nango.dev/docs/guides/platform/proxy-requests.md Description: How to use the requests proxy from Nango Use the requests proxy to make authenticated API requests to the external API. ## How to use the proxy Make sure you have an [integration configured](/guides/auth/auth-guide#guide) in your environment, and at least one [connection](/guides/auth/auth-guide#overview) available for it. You can use the proxy with the [Node SDK](/reference/backend/backend-sdk/node#proxy) or [REST API](/reference/backend/http-api/proxy/get): ```typescript try { const res = await nango.proxy({ method: 'POST', baseUrlOverride: 'https://api.example.com', endpoint: '/external-endpoint', providerConfigKey: '', connectionId: '', retries: 5, // Retries with exponential backoff (optional, default 0) data: { id: 1, colorId: 'blue', selected: true } }); // Response was 200! // See https://axios-http.com/docs/res_schema console.log(res.data); } catch (error) { // Status of response != 200 // See https://axios-http.com/docs/handling_errors console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } ``` ```bash curl -X POST -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -H 'Provider-Config-Key: ' \ -H 'Connection-Id: ' \ -d '{"colorId: "blue"}' \ 'https://api.nango.dev/proxy/' ``` ## Related guides - [Auth guide](/guides/auth/auth-guide) - create the connection used by proxy requests. - [Node SDK reference](/reference/backend/backend-sdk/node#proxy) - proxy helper methods. - [Proxy API reference](/reference/backend/http-api/proxy/get) - REST endpoints for proxied requests. - [Rate limits](/guides/functions/rate-limits) - handle provider throttling. ## Receive webhooks from Nango Source: https://nango.dev/docs/guides/platform/webhooks-from-nango.md Description: Reference for the webhooks Nango sends to your app — setup, types, payloads, signature verification, and retries. Nango POSTs webhooks to your app to notify it of important events: a new connection was created, a sync run finished, an async action completed, or an external webhook was forwarded. This page covers how to receive and verify webhooks from Nango, plus the payloads for auth, sync, and async action webhooks. To process webhooks coming **from external APIs** (not from Nango), see [Process external webhooks](/getting-started/use-cases/webhooks-from-external-apis). To forward external API webhooks through Nango to your app, see [Webhook forwarding](/guides/platform/webhook-forwarding). ## Set up webhooks from Nango Webhook settings in Nango are specific to each [environment](/guides/platform/environments). To subscribe to Nango webhooks: 1. Set up a `POST` endpoint in your app to receive the Nango webhooks 2. Input the endpoint's URL in your _Environment Settings_, under _Webhook URLs_ ([direct link for your dev environment](https://app.nango.dev/dev/environment-settings#notification)) 3. Implement [verify incoming webhooks](#verifying-webhooks-from-nango) to ensure sure only authentic Nango webhooks are processed 4. Implement processing logic for each [webhook type](#types-of-nango-webhooks) from Nango you want to handle - Make sure your processing logic handles the webhooks you have enabled in the environment settings You can configure up to two webhook URLs per environment. Nango webhooks will be sent to both. A single connection can [override these with its own URL](#override-webhook-urls-per-connection). To test webhooks locally, use a webhook forwarding service like [ngrok](https://dev.to/mmascioni/testing-and-debugging-webhooks-with-ngrok-4alp), or [webhook.site](https://webhook.site/). ## Override webhook URLs per connection A single connection can override the environment's webhook URLs with its own `webhook_url`. The connection still receives all the same [webhook types](#types-of-nango-webhooks), with the same signing, retries, and logs. When a connection sets `webhook_url`, its webhooks are sent **only** to that URL — the environment's primary and secondary webhook URLs are both bypassed for that connection. It does not add a third destination. Set `webhook_url` when creating a [connect session](/reference/backend/http-api/connect/sessions/create), under `integrations_config_defaults..connection_config`: ```ts const session = await nango.createConnectSession({ allowed_integrations: [''], integrations_config_defaults: { '': { connection_config: { webhook_url: 'https://.ngrok.app/webhooks-from-nango' } } } }); ``` ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "allowed_integrations": [""], "integrations_config_defaults": { "": { "connection_config": { "webhook_url": "https://.ngrok.app/webhooks-from-nango" } } } }' ``` You can also set `webhook_url` from the **Create connection** flow in the Nango dashboard, under the connection's advanced configuration. `webhook_url` can only be set at connect-session creation time — the trusted actor that creates the session. The same key passed to `nango.auth()` `params` from the client is ignored. **Local development:** create a connection with `webhook_url` pointing at your local webhook endpoint (for example via [ngrok](https://ngrok.com/) or [webhook.site](https://webhook.site/)) to receive that connection's webhooks on your machine — without changing the shared environment webhook URLs. See [Engineering collaboration](/guides/platform/environments#engineering-collaboration) for the shared-environment workflow. ## Verifying webhooks from Nango Validate webhooks from Nango by looking at the `X-Nango-Hmac-Sha256` header. It's an HMAC-SHA256 hash of the webhook payload, using the webhook signing key found in **Environment Settings > Webhooks > Signing key** in the Nango UI. Nango webhook requests include an `X-Nango-Hmac-Sha256` header for secure verification. A legacy `X-Nango-Signature` header (using plain SHA-256) is also sent for backwards compatibility but should not be used. If you're currently using `X-Nango-Signature`, migrate to `X-Nango-Hmac-Sha256` for improved security. The webhook signature can be verified with the following code: Verify the signature with the **webhook signing key** from **Environment Settings > Webhooks > Signing key** — not your API secret key. These are two distinct keys. With the Node SDK, pass it as `webhookSigningKey`; for raw HMAC verification, use it as the HMAC secret. Using the API secret key will cause verification to fail on any environment where the keys differ. Pass the webhook signing key as `webhookSigningKey` so a single client can make API calls with the API key and verify webhooks with the signing key. `webhookSigningKey` falls back to `secretKey` when omitted. ```typescript const nango = new Nango({ apiKey: process.env.NANGO_API_KEY, // used for API calls webhookSigningKey: process.env.NANGO_WEBHOOK_SIGNING_KEY // used to verify webhooks }); async (req, res) => { const isValid = nango.verifyIncomingWebhookRequest(req.body, req.headers); } ``` ```typescript import crypto from 'crypto'; // From Environment Settings > Webhooks > Signing key const webhookSigningKey = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; const body = // raw request body as string const hash = crypto.createHmac('sha256', webhookSigningKey).update(body).digest('hex'); ``` ```python import hmac import hashlib # From Environment Settings > Webhooks > Signing key webhook_signing_key = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' body = # raw request body as bytes hash = hmac.new(webhook_signing_key.encode('utf-8'), body, hashlib.sha256).hexdigest() ``` ```java import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; public class Main { public static void main(String[] args) throws Exception { // From Environment Settings > Webhooks > Signing key String webhookSigningKey = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; String body = // raw request body as string Mac mac = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKey = new SecretKeySpec(webhookSigningKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); mac.init(secretKey); byte[] hashBytes = mac.doFinal(body.getBytes(StandardCharsets.UTF_8)); StringBuilder hexString = new StringBuilder(); for (byte b : hashBytes) { hexString.append(String.format("%02x", b)); } String hash = hexString.toString(); } } ``` ```ruby require 'openssl' # From Environment Settings > Webhooks > Signing key webhook_signing_key = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' body = # raw request body hash = OpenSSL::HMAC.hexdigest('SHA256', webhook_signing_key, body) ``` ```go package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "io" "net/http" ) func main() { // From Environment Settings > Webhooks > Signing key webhookSigningKey := "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" var body []byte // raw request body h := hmac.New(sha256.New, []byte(webhookSigningKey)) h.Write(body) hash := hex.EncodeToString(h.Sum(nil)) } ``` ```rust use hmac::{Hmac, Mac}; use sha2::Sha256; type HmacSha256 = Hmac; // From Environment Settings > Webhooks > Signing key let webhook_signing_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; let body = // raw request body as string or bytes let mut mac = HmacSha256::new_from_slice(webhook_signing_key.as_bytes()).unwrap(); mac.update(body.as_bytes()); let hash = format!("{:x}", mac.finalize().into_bytes()); ``` ```php Webhooks > Signing key $webhookSigningKey = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; $body = // raw request body as string $hash = hash_hmac('sha256', $body, $webhookSigningKey); ?> ``` Only accept a webhook if the `X-Nango-Hmac-Sha256` header value matches the webhook signature. ## Types of Nango webhooks New Nango webhook types are added regularly, without considering this a breaking change. Your webhook handling logic should gracefully support receiving new types of webhooks by simply ignoring them. All webhooks from Nango are POST requests. The exact webhook type definitions can be found [here](https://github.com/NangoHQ/nango/blob/master/packages/types/lib/webhooks/api.ts). ### Auth webhooks New connection webhooks have `"type": "auth"` and `"operation": "creation"`. They are sent after a connection has been successfully created. Payload received following a connection creation: ```json { "type": "auth", "operation": "creation", "connectionId": "", "authMode": "OAUTH2 | BASIC | API_KEY | ...", "providerConfigKey": "", "provider": "", "environment": "DEV | PROD | ...", "success": true, "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" } } ``` Processing webhooks with `"type": "auth"` and `"operation": "creation"` is **necessary**. After a connection is created, these webhooks give you the generated connection ID which lets you access the connection later on. Use `tags` to reconcile and save the connection ID with the user/org/workspace that initiated the connection. All `authMode` values can be found [here](https://github.com/NangoHQ/nango/blob/master/packages/types/lib/auth/api.ts). The `authMode` value depends on the `provider` value. All `operation` values are: - `creation`: a new connection has been created - `override`: a connection has been re-authorized - `refresh`: an OAuth connection's access token has failed to refresh Payload received following a refresh token error: ```json { "type": "auth", "operation": "refresh", "connectionId": "", "authMode": "OAUTH2 | BASIC | API_KEY | ...", "providerConfigKey": "", "provider": "", "environment": "DEV | PROD | ...", "success": false, "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" }, "error": { "type": "", "description": "" } } ``` Webhooks are only sent for certain connection creation errors. For example, during the OAuth flow, some errors are reported locally in the OAuth modal by the external API. Since Nango does not receive these errors, it cannot trigger a webhook for them. ### Sync webhooks Sync webhooks are sent when a [sync function](/guides/functions/syncs/sync-functions) execution finishes, whether successful or not. Payload received following a successful sync execution: ```json { "type": "sync", "connectionId": "", "providerConfigKey": "", "syncName": "", "model": "", "syncType": "INCREMENTAL | INITIAL | WEBHOOK", // DEPRECATED — use checkpoints instead "success": true, "modifiedAfter": "", "responseResults": { "added": number, "updated": number, "deleted": number }, // checkpoints itself may be missing or null if the sync doesn't use checkpoints — // always guard against that before reading `from`/`to`. "checkpoints": { "from": { "": "" }, // or null if no prior checkpoint "to": { "": "" } // or null if the sync didn't save one } } ``` `modifiedAfter` is an ISO-8601 timestamp marking the start of the last run. To read the changed records, use cursor-based fetching as described in [Records cache → Cursors and sync progress](/guides/functions/syncs/records-cache#cursors-and-sync-progress). **Distinguishing full vs incremental syncs:** use `checkpoints` instead of the deprecated `syncType` field. `checkpoints.from`/`checkpoints.to` are checkpoint objects (`Record`) as saved by the sync function via `saveCheckpoint()` — not strings — and either can be `null`. The `checkpoints` field itself can also be missing or `null`, so guard against that before reading nested fields: ```javascript const { checkpoints } = payload; const isFullSync = !checkpoints || checkpoints.from === null; ``` - `checkpoints` is missing/`null`, or `checkpoints.from === null` — the run started with no prior state. This happens on the first run, after a manual reset, or when the sync explicitly cleared its checkpoint. Treat this as a full sync and overwrite your data. - `checkpoints.from` is a non-null checkpoint object — the run continued from a previously saved checkpoint. This is an incremental sync; merge the new records into your existing data. - **Webhook-triggered runs** (deprecated `syncType: "WEBHOOK"`): always treat these as incremental merges, regardless of `checkpoints.from`. A webhook-triggered run processes a single incoming external event on top of already-synced data — it never represents a full resync. **Fetch records promptly**: Due to Nango's [data retention policies](/guides/platform/security#synced-records-retention), you should fetch and store synced records in your own system promptly after receiving webhook notifications. Records not updated for 30 days will have their payload pruned, and sync functions not executed for 60 days will have all records deleted. By default, Nango sends a webhook even if no modified data was detected in the last sync execution (referred as an "empty" sync), but this is configurable in your _Environment Settings_. In case of an empty sync, the `responseResults` would be: ```json { "added": 0, "updated": 0, "deleted": 0 } ``` Payload received following a failed sync execution: ```json { "type": "sync", "connectionId": "", "providerConfigKey": "", "syncName": "", "model": "", "syncType": "INCREMENTAL | INITIAL | WEBHOOK", // DEPRECATED — use checkpoints instead "success": false, "error": { "type": "", "description": "" }, "startedAt": "", "failedAt": "", // checkpoints itself may be missing or null — see the note above. "checkpoints": { "from": { "": "" }, // or null "to": { "": "" } // or null } } ``` ### External webhook forwarding Forwarded external webhooks use the same webhook URLs, signing, retries, and logs as other Nango webhooks. See [Webhook forwarding](/guides/platform/webhook-forwarding) for setup, routing behavior, and payload shapes. ## Webhook retries & debugging Nango retries each webhook with non-2xx responses 2 times with exponential backoff (starting delay: 100ms, time multiple: 2, view details in the [code](https://github.com/NangoHQ/nango/blob/master/packages/webhooks/lib/utils.ts)). Webhooks time out after 20 seconds. Each webhook attempt is logged in Nango's [logs](/guides/platform/observability). You can also use the [OpenTelemetry exporter](/guides/platform/observability#opentelemetry-export) to monitor Nango webhooks in your own observability stack. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Related guides - [Webhook forwarding](/guides/platform/webhook-forwarding) - forward external provider webhooks through Nango. - [Webhook functions](/guides/functions/webhook-functions) - process provider webhooks inside Nango. - [Event functions](/guides/functions/event-functions) - react to connection lifecycle events. - [Observability](/guides/platform/observability) - monitor webhook operations and retries. ## External webhook forwarding Source: https://nango.dev/docs/guides/platform/webhook-forwarding.md Description: Forward external API webhooks through Nango to your app, with connection attribution. Webhook forwarding lets Nango receive webhooks from external APIs and forward them to your app's webhook URL. Use it when your app should own the webhook handling logic, but you still want Nango to provide the provider-specific webhook URL, routing, signing, retries, and logs. For processing the webhook inside Nango function code, use [webhook functions](/guides/functions/webhook-functions). ## How forwarding works 1. You configure your app's webhook URL in **Environment Settings > Webhook URLs**. 2. You register the integration's Nango webhook URL in the provider's developer portal. Find it in **Integrations > [Integration] > Webhooks**. 3. The provider sends a webhook to Nango. 4. Nango runs the provider-specific routing logic and tries to map the event to one or more Nango connections. 5. Nango forwards the payload to your app's webhook URL and signs the request like other Nango webhooks. Nango forwards webhooks asynchronously after accepting the provider webhook, so slow app endpoints do not block the provider response. ## Forwarded payloads When Nango can attribute the webhook to a connection, your app receives a Nango wrapper: ```json { "from": "hubspot", "providerConfigKey": "", "type": "forward", "connectionId": "", "payload": { "...": "raw provider payload" } } ``` If a provider webhook maps to multiple connections, Nango sends one forwarded webhook per connection. If Nango cannot map the webhook to a connection, Nango forwards the raw provider payload without the wrapper. Your consumer should handle both shapes when the provider can emit unattributed events. ## Headers and signatures Nango forwards safe provider headers when possible, excluding headers that should not be replayed such as `authorization`, `cookie`, `host`, `content-length`, `content-type`, `user-agent`, and similar transport-sensitive headers. Every forwarded webhook is signed with the same headers used by other Nango webhooks: - `X-Nango-Hmac-Sha256` - `X-Nango-Signature` for backwards compatibility Verify forwarded webhooks the same way you verify other webhooks from Nango. See [Verifying webhooks from Nango](/guides/platform/webhooks-from-nango#verifying-webhooks-from-nango). ## When to use forwarding Use forwarding when: - Your app already has webhook handling logic. - You want app code to decide what to do with the provider event. - You need Nango to attribute the event to a connection when possible. - You want webhook delivery logs and retries without running Nango function code. Use [webhook functions](/guides/functions/webhook-functions) when Nango should update records, normalize the payload, or run connection-scoped integration logic immediately. ## Configure forwarding In Nango, open **Environment Settings > Webhook URLs** and add your app's endpoint. Nango sends forwarded webhooks to the same URLs used for auth, sync, and async action webhooks. In the integration page, copy the Nango webhook URL from **Integrations > [Integration] > Webhooks** and register it in the provider's developer portal. Some providers use one global webhook registration. Others require one webhook registration per connected account. Provider-specific docs explain the required setup. If the payload has `"type": "forward"`, read `connectionId`, `providerConfigKey`, and `payload`. If the payload does not have `"type": "forward"`, handle it as the raw provider payload. This can happen when the provider event cannot be attributed to a Nango connection. When implementing webhook forwarding, first find the provider-specific Nango webhook guide. Confirm whether the provider routes automatically or requires embedding the Nango connection ID, tenant ID, team ID, installation ID, or another identifier in the provider webhook subscription. Implement signature verification with `X-Nango-Hmac-Sha256`, then branch on whether the incoming body has `type: "forward"`. Store and use `connectionId` only when it is present. ## Retries and debugging Nango retries forwarded webhooks when your app returns a non-2xx response or has a network failure. Forwarded attempts appear in Nango logs as webhook forwarding operations. For retry behavior, timeout limits, and local testing tips, see [Webhook retries & debugging](/guides/platform/webhooks-from-nango#webhook-retries-debugging) and [Webhook limits](/guides/platform/limits#webhook-limits). ## Related guides - [Webhook functions](/guides/functions/webhook-functions) - [Process external webhooks](/getting-started/use-cases/webhooks-from-external-apis) - [Receive webhooks from Nango](/guides/platform/webhooks-from-nango) - [Observability](/guides/platform/observability) ## Common issues Source: https://nango.dev/docs/guides/platform/common-issues.md Description: Troubleshooting guide for frequent errors and how to fix them. If you encounter an issue not documented here, reach out on the [Slack community](https://nango.dev/slack). ## I just run `nango migrate-to-zero-yaml`, can I change the folder structure? Yes, you can change the folder structure. You can move files, zod definitions, types, rename files, folders, etc. The results of the migration is just an example of how you can structure your project. ## I'm getting `ZodXX is missing the following properties` errors If you are getting Zod errors, it's most likely because you are using a different version of Zod than the one we use in our CLI. We currently require zod `4.0.5`. If you have an overrides in your package.json or something that is changing the version of Zod, you will need to remove that. NB: `nango compile` will automatically update your dependencies to the version we use in our CLI. ## Token refresh error from the external API Indications of the error: - Nango logs may show: ```json { "error": { "message": "An error occurred during an HTTP call", "payload": { "error": { "code": "invalid_credentials", "payload": { [...] }, "message": "The refresh limit has been reached for this connection." } } } } ``` - Or in `Token refresh` log operations: ```json { "payload": { "dataMessage": { "error_description": "Token has been expired or revoked.", "error": "invalid_grant" } }, "type": "refresh_token_external_error", "status": 400 } ``` The `error` and `error_description` fields may differ by API, but typically include `invalid_grant` and mention the token being expired or revoked. ### How to debug - This error means the external API (e.g., Google, Salesforce) rejected the refresh token as expired or revoked. Nango retries several times, but if the error persists, the credentials are considered permanently broken. - The only solution is to ask the user to re-authenticate. See: [How to trigger a re-authorization flow in Nango](/guides/auth/auth-guide#re-authorize-an-existing-connection) - We recommend enabling [Nango's revoked token webhooks](/guides/platform/webhooks-from-nango#auth-webhooks) for real-time notifications. Revoked refresh tokens are a normal part of integrations. Ensure your app provides a good re-authorization experience for users. APIs may revoke tokens for unpredictable reasons. Only investigate if you observe an unusual increase of connection revocations for an API. Achieving a 0% revocation rate is not realistic. Known API-specific behaviors: - Google: All tokens are revoked after 7 days if your OAuth app is in test mode ([detailed guide](https://www.nango.dev/blog/google-oauth-invalid-grant-token-has-been-expired-or-revoked)). To go to production, read our [guide for Google's OAuth app review](/api-integrations/google-shared/google-security-review). - Salesforce: See [Salesforce refresh token issues](https://nango.dev/blog/salesforce-oauth-refresh-token-invalid-grant/). ## Connection not found (unknown_connection / 404) Indications of the error: - Request failed with status code `404` - code `unknown_connection` - error message: "No connection matching the provided params of 'connection_id' and 'provider_config_key'" ```json { "error": { "message": "Request failed with status code 404", "data": { "error": { "message": "No connection matching the provided params of 'connection_id' and 'provider_config_key'.Please make sure these values exist in the Nango dashboard [...]", "code": "unknown_connection" } } } } ``` ### How to debug 1. Confirm the environment and secret key - The `secretKey` in the API or SDK is specific to the environment. Make sure you pass the key that corresponds to the environment you are using (e.g., dev vs prod). - Open [Environment Settings > API Keys](https://app.nango.dev/dev/environment-settings#api-keys) and verify the key matches. 2. Verify the Integration ID (aka `providerConfigKey`) - Go to [Integrations](https://app.nango.dev/dev/integrations), open the integration, and copy the `Integration ID` exactly. - Ensure the value you send as `providerConfigKey` matches this ID. 3. Verify the Connection ID - Go to [Connections](https://app.nango.dev/dev/connections), find the connection, and copy its `Connection ID`. - Ensure the value you send as `connectionId` (header `Connection-Id` when using HTTP, or `connectionId` in the SDK) matches exactly. ## Related guides - [Observability](/guides/platform/observability) - inspect logs and exported traces. - [Auth guide](/guides/auth/auth-guide#re-authorize-an-existing-connection) - repair broken connections with re-authorization. - [Token refreshing and validity](/guides/auth/token-refreshing) - understand refresh failures. - [Webhooks from Nango](/guides/platform/webhooks-from-nango) - receive failure notifications. ## Migrate to checkpoints Source: https://nango.dev/docs/guides/platform/migrations/migrate-to-checkpoints.md Description: Guide to migrate your sync functions from nango.lastSyncDate to the new checkpointing system.
# Who is impacted If any of your sync functions read `nango.lastSyncDate` to determine where to resume syncing, you are impacted by this migration. To check, search your sync files for `lastSyncDate`. If you find references like `nango.lastSyncDate` or destructured equivalents, those syncs should be migrated to use checkpoints. If you want your syncs to be resilient to failures, ensuring that a failed run resumes from where it left off instead of starting over, you should also migrate any sync that paginates through large datasets, even if it doesn't currently use `lastSyncDate`. # What is changing `nango.lastSyncDate` is deprecated and replaced by checkpoints — a more flexible and resilient way to track sync progress. With `lastSyncDate`, Nango automatically tracked the timestamp of the last completed sync run. Your function could read it but not set it, and it was always a timestamp. With checkpoints, you define the schema, you control when it's saved, and you can store any type of progress marker — not just a date. | | `lastSyncDate` (deprecated) | Checkpoints | | --- | --- | --- | | **Type** | Always a `Date` | Any schema you define with Zod | | **Control** | Managed by Nango, read-only | You set it explicitly with `saveCheckpoint()` | | **Granularity** | Updated once per completed run | Updated mid-execution, after each batch | | **Resilience** | If a run fails, no progress is saved | Progress is saved incrementally — the next run resumes from the last checkpoint | Checkpoints also replace the `syncType: 'incremental'` field in your sync declaration. Whether the dataset is fetched from scratch or incrementally depends on how you use the checkpoint in your function code, not just whether you declared a checkpoint schema. # Why this deprecation `lastSyncDate` had two significant limitations: 1. **No mid-run resilience.** If a sync fetched 90% of its data and then failed, all progress was lost. The next run started over from the previous `lastSyncDate`, re-fetching everything. 2. **Rigid type.** The value was always a timestamp managed by Nango. Some APIs use cursors, page tokens, or composite markers to track progress — `lastSyncDate` couldn't accommodate these. Checkpoints solve both problems. For a full overview, see the [checkpoints guide](/guides/functions/syncs/checkpoints). # Migration steps ## Migrate with the migrating-to-checkpoints skill You can migrate syncs to checkpoints with the **`migrating-to-checkpoints`** skill from [NangoHQ/skills](https://github.com/NangoHQ/skills). The skill guides a coding agent through converting `createSync()` implementations from `nango.lastSyncDate`, legacy `syncType: 'incremental'`, and non-resumable full refreshes to checkpoint-based syncs. Install it in your functions repo: ```bash npx skills add NangoHQ/skills -s migrating-to-checkpoints ``` Then ask your agent to migrate a specific sync using the `migrating-to-checkpoints` skill. The skill covers checkpoint schema selection, replacing `lastSyncDate` with `getCheckpoint()` and `saveCheckpoint()`, full-refresh recovery with `clearCheckpoint()`, and validating with `nango dryrun --checkpoint`. The manual steps below walk through the same migration if you prefer to do it by hand or want to review what the skill changes. ## Step 1: Add a checkpoint schema to your sync declaration Add a `checkpoint` field to your `createSync()` call. In most cases, this is a single string field holding a timestamp: **Before:** ```typescript export default createSync({ description: 'Sync contacts from Salesforce', frequency: 'every hour', models: { Contact: ContactSchema }, exec: async (nango) => { // ... }, }); ``` **After:** ```typescript export default createSync({ description: 'Sync contacts from Salesforce', frequency: 'every hour', checkpoint: z.object({ lastModifiedISO: z.string(), }), models: { Contact: ContactSchema }, exec: async (nango) => { // ... }, }); ``` If your sync previously declared `syncType: 'incremental'`, remove it — the presence of the `checkpoint` field replaces it. ## Step 2: Replace `lastSyncDate` reads with `getCheckpoint()` Replace every reference to `nango.lastSyncDate` with a call to `nango.getCheckpoint()`. **Before:** ```typescript exec: async (nango) => { const lastSyncDate = nango.lastSyncDate; let query = 'SELECT Id, FirstName, LastName FROM Contact'; if (lastSyncDate) { query += ` WHERE LastModifiedDate > ${lastSyncDate.toISOString()}`; } query += ' ORDER BY LastModifiedDate ASC'; // ... fetch and save records } ``` **After:** ```typescript exec: async (nango) => { const checkpoint = await nango.getCheckpoint(); let query = 'SELECT Id, FirstName, LastName FROM Contact'; if (checkpoint) { query += ` WHERE LastModifiedDate > ${checkpoint.lastModifiedISO}`; } query += ' ORDER BY LastModifiedDate ASC'; // ... fetch and save records } ``` Note that `getCheckpoint()` is async and returns `null` on first run or after a reset, just like `lastSyncDate` was `null` on the first execution. ## Step 3: Add `saveCheckpoint()` calls after each batch This is the most important change. With `lastSyncDate`, progress was saved automatically at the end of a run. With checkpoints, you save progress explicitly — and you should do it after every batch of records. **Before:** ```typescript exec: async (nango) => { const lastSyncDate = nango.lastSyncDate; while (nextPage) { const res = await nango.get({ endpoint: '/contacts', params: { since: lastSyncDate?.toISOString(), cursor: nextPage }, }); await nango.batchSave(mapContacts(res.data.records), 'Contact'); nextPage = res.data.nextCursor; } // Progress saved automatically by Nango at run completion } ``` **After:** ```typescript exec: async (nango) => { const checkpoint = await nango.getCheckpoint(); while (nextPage) { const res = await nango.get({ endpoint: '/contacts', params: { since: checkpoint?.lastModifiedISO, cursor: nextPage }, }); const contacts = mapContacts(res.data.records); await nango.batchSave(contacts, 'Contact'); // Save progress after each page const lastRecord = contacts[contacts.length - 1]; await nango.saveCheckpoint({ lastModifiedISO: lastRecord.lastModified }); nextPage = res.data.nextCursor; } } ``` The pattern is: fetch a page, save records, save checkpoint. If the sync fails mid-way, the next run picks up from the last checkpoint instead of starting over. ## Step 4: Test locally Use the CLI `dryrun` command with the `--checkpoint` flag to simulate resuming from a previous run: ```bash nango dryrun my-sync '' --checkpoint '{"lastModifiedISO": "2024-06-01T00:00:00Z"}' ``` Verify that your sync correctly filters data based on the checkpoint value. See the [testing guide](/guides/functions/testing) for how to write unit tests for checkpoint logic. ## Step 5: Deploy Deploy as usual. On the first run after deployment, `getCheckpoint()` will return `null` (since no checkpoint has been saved yet), so the sync will perform a full initial fetch — just like it did on its first run with `lastSyncDate`. Subsequent runs will be incremental from the checkpoint. ```bash nango deploy ``` Because the first post-migration run starts fresh, it may take longer than usual. This is expected and only happens once. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Related guides - [Checkpoints](/guides/functions/syncs/checkpoints) - understand the new checkpoint model. - [Sync functions](/guides/functions/syncs/sync-functions) - update sync implementations. - [Testing](/guides/functions/testing) - test checkpoint filtering. ## Migrate from end user Source: https://nango.dev/docs/guides/platform/migrations/migrate-from-end-user.md Description: Guide to migrate away from using end users in connections and connect sessions # Who is impacted You are impacted if you use any of the following to identify who a connection belongs to: - `end_user` / `organization` in `POST /connect/sessions` - `end_user` / `organization` in auth webhooks (`endUser` payload) - `end_user` fields in the Connections list or connection details for reconciliation # What is changing Connections now have a first-class `tags` object. Connect sessions accept a top-level `tags` object as well. The legacy `end_user` and `organization` fields are deprecated. Tags exist because the end-user fields were not flexible enough for many real-world attribution and routing use cases. When a connection is created via a Connect session: - the Connect session `tags` are copied onto the newly created connection - the auth webhook includes those tags as top-level `tags` Use `tags` as your source of truth for identifying who a connection belongs to. The legacy `end_user` and `organization` fields are deprecated and will be removed in a future version. # Migration steps ## Step 1: Start sending tags for new connect sessions Add a `tags` object when creating a Connect session token in your backend: ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env[''] }); const { data } = await nango.createConnectSession({ // Recommended tags: { end_user_id: '', end_user_email: '', organization_id: '' }, allowed_integrations: [''] }); ``` ```bash curl --request POST \ --url https://api.nango.dev/connect/sessions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" }, "allowed_integrations": [""] }' ``` For more details (including limitations and filtering), see [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata). ## Step 2: Update webhook reconciliation to use `tags` Auth webhooks include connection tags as top-level `tags`. If you previously used `endUser.endUserId`, switch to using `tags` as your primary source of truth. For compatibility during rollout, handle both: ```ts function resolveOwnerFromWebhook(payload: any) { // Preferred if (payload.tags?.end_user_id) { return { endUserId: payload.tags.end_user_id, endUserEmail: payload.tags.end_user_email, organizationId: payload.tags.organization_id }; } // Legacy fallback if (payload.endUser?.endUserId) { return { endUserId: payload.endUser.endUserId, endUserEmail: payload.endUser.endUserEmail, organizationId: payload.endUser.organizationId }; } return null; } ``` ## Step 3: Existing connections are already backfilled You do not need to patch anything. Nango has already backfilled standard tags (like `end_user_id`, `end_user_email`, `organization_id`) onto existing connections based on your legacy end-user fields. During the deprecation period, if you still create new connections using the deprecated `end_user` / `organization` fields, Nango will also populate connection tags automatically using the same translation logic: - `end_user.id` -> `tags.end_user_id` - `end_user.email` (if present) -> `tags.end_user_email` - `end_user.display_name` (if present) -> `tags.end_user_display_name` - `organization.id` -> `tags.organization_id` - `organization.display_name` (if present) -> `tags.organization_display_name` - `end_user.tags` (if present) -> merged into `tags` (subject to the same validation and normalization rules) If you provide both deprecated end-user fields and a top-level `tags` object, the top-level `tags` values win. ## Step 4: Stop using `end_user` Once your code relies on `tags`, stop sending or reading the deprecated `end_user` and `organization` fields. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Related guides - [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata) - use tags for connection attribution. - [Auth guide](/guides/auth/auth-guide) - create connect sessions with tags. - [Webhooks from Nango](/guides/platform/webhooks-from-nango#auth-webhooks) - reconcile auth webhooks with tags. ## Migrate from public key Source: https://nango.dev/docs/guides/platform/migrations/migrate-from-public-key.md Description: Guide to migrate from using the public key (and HMAC) to a Connect session. # Who is impacted If you use the Nango public key in your frontend, you are impacted by this migration. To check if you are using the public key, inspect your frontend code where the `Nango` object is instantiated. If the `config` parameter includes `publicKey`, you are affected. If it uses `connectSessionToken`, you are already up-to-date. # Changes summary 1. The Nango public key is replaced with a short-lived Connect session token, fetched from your backend and passed to your frontend 2. The connection ID is now randomly generated by Nango instead of being specified manually 3. The connection ID is returned via backend webhooks instead of the frontend SDK # Migration timeline Public keys and HMAC signatures will be supported until **March 31, 2025** (announced on **January 31, 2025**). # Why this deprecation? For security reasons. If you were using the HMAC signature (strongly recommended in the docs), your authorization flow was already secure. However, not all users implemented HMAC, and we want to enforce best-in-class security practices by default. For those not using HMAC, the risk of attack was minimal but theoretically possible. We have no reports of such attacks, but feel free to reach out for more details. # Improvements with the Connect session 1. The new Connect session flow is **secure by default** and replaces the HMAC signature. Randomly generated connection IDs further enhance security 2. Connection IDs are now **unique across integrations**, which will allow us to simplify API endpoints by transitioning from composite keys (`connection ID` + `integration ID`) to a single **unique connection ID** 3. The Connect session will let us provide additional API capabilities that can be called directly and securely from the frontend 4. The Connect session unlocks our [pre-built authorization UI](/guides/auth/auth-guide#guide), which simplifies collecting API keys, basic credentials, and OAuth flows with custom parameters. This UI also validates end-user inputs and provides guidance on required credentials The Connect session works with both the pre-built authorization UI and custom authorization UIs. Custom authorization UIs are not being deprecated; they remain a critical feature and fully supported. The deprecated public key, however, was only compatible with custom authorization UIs and did not support the pre-built authorization UI. # Migration steps This migration is split into 3 steps. You can migrate at your own pace, but we recommend doing it in this order. During the migration, both connect session and public key authentication will work so you don't have to do all 3 steps at once. Existing connections will keep the custom id you have used until now but new connections will have a random id. Your code will need to support both. ## Step 1: Attach tags to all existing connections Attach tags to all existing connections. This step will allow you to use connect sessions with existing connections. This will also be used to attribute webhooks to the correct end user. ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: process.env[''] }); await nango.patchConnection({ connectionId: '', provider_config_key: '', }, { tags: { end_user_id: '', end_user_email: '', organization_id: '' } }); ``` ```bash curl -X PATCH https://api.nango.dev/v1/connections/ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "tags": { "end_user_id": "", "end_user_email": "", "organization_id": "" } }' ``` Check endpoint documentation [here](/reference/backend/http-api/connections/patch). ## Step 2: Implement the Connect and reconnect flows Follow the steps in this [guide](/guides/auth/auth-guide#guide), focusing on these key changes: Connect session token is now required to be passed to the frontend SDK. This improves security and allows you to pass additional information to the connection. ### Step 3: Clean up Finally, remove all usage of the **public key and HMAC** from your frontend. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Related guides - [Auth guide](/guides/auth/auth-guide) - implement Connect sessions. - [Customize Connect UI](/guides/auth/customize-connect-ui) - migrate custom authorization UIs or customize the pre-built UI. - [Connection tags, configuration, and metadata](/guides/auth/connection-tags-configuration-metadata) - add ownership attribution during migration. ## Migrating to Zero YAML Source: https://nango.dev/docs/guides/platform/migrations/migrate-to-zero-yaml.md Description: Guide on how to migrate to Zero YAML in Nango.
The `nango.yaml` configuration file is deprecated and should not be used for new integrations. If you are still using it, this guide will help you migrate your existing Nango integrations from the YAML-based approach to our new Zero YAML system, which uses pure TypeScript with modern tooling. ## What is Zero YAML? Zero YAML is Nango's new approach to building integrations that eliminates the need for separate `nango.yaml` configuration files. Instead, everything is defined in TypeScript using a configuration-as-code approach with full type safety. Key benefits: - **Type Safety**: Full TypeScript support with Zod schema validation - **Better Developer Experience**: Modern tooling, enhanced CLI output, and better error messages - **Portability**: Self-contained files that can be easily shared and version controlled - **No Custom Syntax**: Pure TypeScript—no need to learn YAML-specific conventions and our previous custom model syntax Here's a quick example of the new syntax: ```typescript import { createSync } from 'nango'; import * as z from 'zod'; const issueSchema = z.object({ id: z.string(), title: z.string(), state: z.string() }); const sync = createSync({ description: 'Fetches GitHub issues', frequency: 'every hour', models: { GithubIssue: issueSchema }, exec: async (nango) => { await nango.batchSave([{ id: 'foobar' }], 'GithubIssue'); } }); ``` ## How Zero YAML Works The new system is built around simplification ### Simplified file Structure Like a regular TypeScript codebase ```sh ├── index.ts # Declare what's deployed ├── package.json # Dependency management and types ├── github/ # An integration ├── syncs/ │ └── fetchIssues.ts # A sync └── actions/ └── createIssue.ts # An action ``` ### Simplified configuration Three core functions that replace YAML configuration. You'll benefit from IntelliSense and type safety, directly in your IDE. No need to jump between files to find the right configuration. - **`createSync()`**: Defines data synchronization jobs - **`createAction()`**: Defines one-off jobs - **`createOnEvent()`**: Defines event-based jobs ---- ## Quick Migration Guide ### Automatic Migration The easiest way to migrate is using our automated migration command: ```bash nango migrate-to-zero-yaml ``` This **operation is destructive** and will overwrite your existing files. Make sure to back up your existing files before running the command. This command will: 1. Analyze your existing `nango.yaml` and TypeScript files 2. Generate new self-contained TypeScript files 3. Create the required `package.json` if it doesn't exist 4. Set up the `index.ts` entry point 5. Preserve your existing logic and configuration ### Post-Migration Steps The migration command should automatically transform all your files. However, you should review the changes and make sure everything is correct. Because the new format is stricter, some type issues can arise after the migration. ### Migration Plan We recommend: 1. **Back up** your current nango integrations folder 2. Use your **dev env** to migrate to test the new format 3. Deploy to **prod env** after you have assessed that everything works properly ## What Has Changed? ### Configuration: YAML → TypeScript Objects **Before** (nango.yaml + separate .ts file): ```yaml # nango.yaml integrations: github: issues: runs: every 1h sync_type: full endpoint: GET /issues description: Fetches GitHub issues models: GithubIssue: id: string title: string ``` **After** (single self-contained file): ```typescript const sync = createSync({ description: 'Fetches GitHub issues', frequency: 'every hour', models: { GithubIssue: issueSchema } }); ``` ### Models: YAML Definitions → Zod Schemas **Before**: ```yaml models: GithubIssue: id: string title: string state: string ``` **After**: ```typescript const issueSchema = z.object({ id: z.string(), title: z.string(), state: z.string() }); ``` ## Before and After Examples ### Sync Example **Before** (issues.ts + nango.yaml excerpt): ```typescript // issues.ts import type { NangoSync, GithubIssue } from '../../models'; export default async function fetchData(nango: NangoSync) { // Sync logic here } ``` ```yaml # nango.yaml excerpt integrations: github: issues: runs: every 1h sync_type: full endpoint: GET /issues ``` **After** (self-contained fetchIssues.ts): ```typescript import { createSync } from 'nango'; import * as z from 'zod'; const issueSchema = z.object({ id: z.string(), title: z.string(), state: z.string() }); const sync = createSync({ description: 'Fetches GitHub issues', frequency: 'every hour', models: { GithubIssue: issueSchema }, exec: async (nango) => { // Your existing sync logic here } }); export default sync; ``` ### Action Example **Before** (separate files): ```typescript // create-issue.ts export default async function runAction(nango: NangoAction, input: CreateIssueInput) { // Action logic } ``` **After** (self-contained): ```typescript import { createAction } from 'nango'; import * as z from 'zod'; const inputSchema = z.object({ title: z.string(), body: z.string() }); const action = createAction({ description: 'Create a GitHub issue', input: inputSchema, output: z.void(), exec: async (nango, input) => { // Your action logic here } }); export default action; ``` ### Index File Structure The new `index.ts` file imports all your integrations: ```typescript // index.ts import './github/syncs/fetchIssues.js'; import './github/actions/createIssue.js'; // Add more imports as needed ``` ## Migration Gotchas & FAQ ### Gotchas ```typescript err - github/syncs/issues.ts:57:43 Argument of type '{ id: number; owner: string; repo: string; issue_number: number; title: string; author: string; author_id: string; state: string; date_created: Date; date_last_modified: Date; body: string; }[]' is not assignable to parameter of type 'RawModel[]'. Type '{ id: number; owner: string; repo: string; issue_number: number; title: string; author: string; author_id: string; state: string; date_created: Date; date_last_modified: Date; body: string; }' is not assignable to type 'RawModel'. Types of property 'id' are incompatible. Type 'number' is not assignable to type 'string'. ``` Previously you were setting the ID property of a sync to a number when it should always be a string. ------ ```typescript err - 1password-scim/actions/create-user.ts:26:45 Property 'errors' does not exist on type 'ZodError<{ firstName: string; lastName: string; email: string; active?: boolean | undefined; externalId?: string | undefined; phoneNumbers?: { type: "work" | "mobile" | "other"; value: string; }[] | undefined; photos?: { ...; }[] | undefined; addresses?: { ...; }[] | undefined; title?: string | undefined; }>'. ``` Previously you were validating an input using Zod in this way ```typescript const parsedInput = scimCreateUserSchema.safeParse(input); if (!parsedInput.success) { for (const error of parsedInput.error.errors) { await nango.log( `Invalid input provided to create a user: ${error.message} at path ${error.path.join(".")}`, { level: "error" }, ); } throw new nango.ActionError({ message: "Invalid input provided to create a user", }); } ``` Instead of this, you can use a Nango helper: ```typescript const parsedInput = await nango.zodValidateInput({ zodSchema: scimCreateUserSchema, input }); ``` ### Nango.yaml EOL **Q: When will nango.yaml stop being supported?** **A:** nango.yaml integrations will be supported until the end of 2025. Zero YAML will be the only supported way to build integrations after that. An official roadmap will be published soon. ### Package.json Requirement **Q: Why is package.json now mandatory?** **A:** The new system uses modern JavaScript tooling that requires proper dependency management. The `package.json` ensures: - Consistent dependency versions across environments - Proper module resolution - Better integration with modern development tools If you don't have one, the migration command will create it automatically. Note: Bundling custom dependencies is not yet possible but is now on our roadmap thanks to this update. ### Backward Compatibility **Q: What still works the same?** **A:** Your core integration logic remains unchanged. No new features or breaking changes. All CLI commands are still available. ### Testing Migrated Integrations **Q: How do I test after migration?** **A:** Use the same testing commands: ```bash # Test a specific sync nango dryrun ``` ### Rollback Process **Q: Can I rollback if something goes wrong?** **A:** Yes, Nango's platform remains compatible with nango.yaml at all times: 1. Go to your backup folder 2. `nango deploy dev` Keep your original files until you've fully validated the migration. --- The Zero YAML approach represents a significant step forward in Nango's developer experience. By eliminating custom syntax and embracing pure TypeScript, we've made integrations more maintainable, shareable, and enjoyable to work with. Many more features will be possible thanks to this new syntax. If you encounter any issues during migration, our support team is ready to help. Happy integrating! 🚀 **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Related guides - [Functions guide](/guides/functions/functions-guide) - understand the current TypeScript-first project layout. - [Functions CLI](/reference/functions/functions-cli) - deploy and dry-run migrated functions. - [Testing](/guides/functions/testing) - verify migrated integrations before rollout. ## Frontend SDK Source: https://nango.dev/docs/reference/frontend/frontend-sdk.md Trigger authorization flows in your frontend with this SDK. It is available on [NPM](https://www.npmjs.com/package/@nangohq/frontend) as `@nangohq/frontend`. # Instantiate the frontend SDK ```ts import Nango from '@nangohq/frontend'; const nango = new Nango({ connectSessionToken: '' }); ``` **Parameters** Get your public key in the environment settings of the Nango UI. This is key is not sensitive. Omitting the host points to Nango Cloud. For local development, use `http://localhost:3003`. Use your instance URL if self-hosting. For self-hosted instances only to specify a customs path for the WebSocket connection. Specify a specific width for the OAuth authorization modal. Specify a specific height for the OAuth authorization modal. Print additional console logs to debug authorization issues. # Connect using Nango Connect UI Nango provides a UI component that guides your app's users through automatically and securely setting up an integration. This UI is hosted on Nango's servers and requires minimal setup on your end to get started quickly. This is the recommended way to use Nango in your frontend. ```js const connectUI = nango.openConnectUI({ sessionToken: 'SESSION_TOKEN' }); ``` **Parameters** The unique token to identify your user. It is required but can be set asynchronously. The base URL to load the UI The base URL to reach Nango API A callback to listen to events sent by Nango Connect Control OAuth popup close detection. If set to true, a closed popup will be detected as a failed authorization. The language to use for the UI. Defaults to browser language or english if not supported. The theme (light, dark, system) to use for the UI. Overrides your global setting in the dashboard. **Response** The class to manipulate Nango Connect # Connect using the headless client You store end-user credentials with the `nango.auth` method. It creates a connection in Nango. For OAuth, this will open a modal to let the user log in to their external account. ```js const result = await nango.auth('').catch((error) => { ... }); ``` For API key authorization, pass the end-user's previously-collected API key directly in the parameters. ```js const result = await nango.auth('', { credentials: { apiKey: '' } }).catch((error) => { ... }); ``` For Basic Auth, pass the end-user's previously-collected username & password in the parameters. ```js const result = nango.auth('', { credentials: { username: '', password: '' } }).catch((error) => { ... }); ``` **Parameters** The integration ID that you can find in the integration settings on the Nango UI. The connection ID that you can find in the _Connections_ tab on the Nango UI. Specify additional connection configuration necessary to perform the authorization request. HMAC key to secure the authorization flow If `true`, `nango.auth()` would fail if the login window is closed before the authorization flow is completed. For OAuth, specify the query parameters of the authorization URL. For Slack OAuth, specify user-specific scopes. For API key authorization, pass in the user's API key. For Basic authorization, pass in the user's username. For Basic authorization, pass in the user's password. For OAuth 2, override the integration's client ID with a connection-level client ID. This is useful when your users bring their own OAuth 2 app (e.g. Netsuite). For OAuth 2, override the integration's client secret with a connection-level client secret. This is useful when your users bring their own OAuth 2 app (e.g. Netsuite). **Success response** The integration ID that you can find in the integration settings on the Nango UI. The connection ID that you can find in the _Connections_ tab on the Nango UI. **Error response** The type of error (e.g. 'authorization_cancelled'). The detailed error message (e.g. 'Authorization fail: The user has closed the authorization modal before the process was complete.'). # Error Handling The Nango Frontend SDK may throw different types of errors during authentication and connection setup. Below is a list of all possible error types along with their meanings: 1. `missing_auth_token` - Occurs when neither a public key nor a connect session token is provided. - Message: "You must specify a public key OR a connect session token (cf. documentation)." 2. `blocked_by_browser` - Occurs when the browser blocks the popup window for authentication. - Message: "Modal blocked by browser" 3. `invalid_host_url` - Occurs when the provided host URL is invalid. - Message: "Invalid URL provided for the Nango host." 4. `missing_credentials` - Occurs when required credentials are not provided. - Message: "You must specify credentials." 5. `window_closed` - Occurs when the authentication window is closed before completing the flow. - Message: "The authorization window was closed before the authorization flow was completed" 6. `connection_test_failed` - Occurs when credential verification fails for certain integrations before the connection is established. - Message: "The given credentials were found to be invalid. Please check the credentials and try again." 7. `missing_connect_session_token` - Occurs when attempting to reconnect without a session token. - Message: "Reconnect requires a session token" 8. `resource_capped` - Occurs when the resource usage limit has been reached. - Message: "Reached maximum number of allowed connections for your plan" or "Reached maximum number of connections with functions enabled" 9. `unknown_error` - Occurs when an unexpected error happens that doesn't fall into other error categories. - Message: Varies depending on the underlying error (e.g., network failures, unexpected exceptions) ## Error Response Structure All errors from the Nango Frontend SDK follow this structure: ```json { "error": { "code": "", "message": "" } } ``` ## Handling Errors You can handle these errors in your code using the `AuthError` class. ```ts import { AuthError } from '@nangohq/frontend'; try { const result = await nango.auth('', { credentials: { apiKey: '' } }); } catch (error) { if (error instanceof AuthError && error.type === '') { // Handle specific error console.error('Your error message'); } } ``` **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## API keys Source: https://nango.dev/docs/reference/backend/http-api/api-keys.md Description: Manage API keys with scoped permissions for your Nango environments. # API keys API keys allow programmatic access to your Nango environment. Each environment can have multiple API keys with different permissions, enabling you to follow the principle of least privilege. ## Managing API Keys API keys are managed in the Nango UI under **Environment Settings > API Keys**. Each environment comes with a **Default - Full access** key that grants access to all API endpoints. You can create additional keys with restricted scopes for specific use cases. ### Creating a Key 1. Go to **Environment Settings > API Keys** 2. Click **Create API Key** 3. Enter a display name (e.g., "CI Deploy Key", "Backend service") 4. Choose **Full access** or **Custom** permissions — custom lets you pick individual scopes 5. The key is created immediately and can be revealed and copied from the key list ### Rotating a Key To rotate a key: 1. Create a new key with the same scopes 2. Update your application to use the new key 3. Monitor the **Last used** column on the old key to confirm it's no longer in use 4. Delete the old key ### Using a Key Pass the API key as a Bearer token in the `Authorization` header: ```ts import { Nango } from '@nangohq/node'; const nango = new Nango({ secretKey: '' }); ``` ```bash curl --request GET \ --url https://api.nango.dev/connections \ --header 'Authorization: Bearer ' ``` ## Scopes Scopes control what an API key can access. When creating a key with **Custom** permissions, you select which scopes to grant. A key without a specific scope will receive a `403 Forbidden` response when trying to access a protected endpoint. ### Credential Scopes Some resources (Integrations and Connections) have sensitive credential data. Access to this data is controlled by dedicated `_credentials` scopes: - `list` / `read` — returns the resource **without** sensitive credentials - `list_credentials` / `read_credentials` — returns the resource **with** credentials (access tokens, client secrets, etc.) The `_credentials` scopes are supersets — selecting `read_credentials` automatically includes `read` access. You don't need to select both. | Resource | Without credentials | With credentials | |----------|-------------------|-----------------| | **Connections** | Connection metadata, tags, status | + access/refresh tokens | | **Integrations** | Provider, display name, config | + client ID and client secret | ## Advised Profiles Common scope combinations for typical use cases: ### Auth (Connect UI) For backends that create connect sessions for the auth flow: | Scope | |-------| | `environment:connect_sessions:write` | ### CI/CD Deploy For CI/CD pipelines deploying syncs and actions to production: | Scope | |-------| | `environment:deploy` | ### Backend Service For backend services that consume data, trigger actions, and proxy requests: | Scope | |-------| | `environment:connections:read` | | `environment:records:read` | | `environment:actions:execute` | | `environment:syncs:execute` | | `environment:proxy` | Add `environment:connections:read_credentials` if the service needs access to connection tokens. For extra security, avoid when possible granting `environment:connections:list` to backend services. Without it, connection IDs act as connection-specific secrets — a leaked API key alone won't let an attacker enumerate and access customer data. ### Local Development For local development, use a **Full access** key. This is the default key created for each environment. ## CLI The Nango CLI uses the `NANGO_SECRET_KEY_` environment variable for authentication. Set it to an API key with the required scopes: | CLI Command | Required Scope | |-------------|---------------| | `nango deploy` | `environment:deploy` | | `nango dryrun` | `environment:connections:read` + `environment:integrations:read` + `environment:proxy` | For most functions, `environment:connections:read` is sufficient for `nango dryrun` — Nango injects credentials into the proxy automatically, so the function does not need them in its response. Upgrade to `environment:connections:read_credentials` when the function reads credential data from the connection, including: - accessing `connection.credentials` directly (e.g. `nango.getConnection().credentials`) - calling `nango.getToken()` — returns the access token from `connection.credentials` - calling `nango.getRawTokenResponse()` — returns `connection.credentials.raw` Without `read_credentials`, these helpers return empty values because the API strips the `credentials` field from the response. The **Default - Full access** key that comes with each environment already has all required scopes for deploying and dry-running. For production CI/CD pipelines, consider creating a dedicated key with only the `environment:deploy` scope to follow the principle of least privilege. ## All Available Scopes ### Integrations | Scope | Description | |-------|-------------| | `environment:integrations:list` | List integrations (no credentials) | | `environment:integrations:list_credentials` | List integrations with client credentials | | `environment:integrations:list_functions` | List deployed functions (syncs and actions) | | `environment:integrations:read` | Read a single integration (no credentials) | | `environment:integrations:read_credentials` | Read a single integration with client credentials | | `environment:integrations:create` | Create integrations | | `environment:integrations:update` | Update integrations | | `environment:integrations:delete` | Delete integrations | ### Connections | Scope | Description | |-------|-------------| | `environment:connections:list` | List connections (no credentials) | | `environment:connections:list_credentials` | List connections with access/refresh tokens | | `environment:connections:read` | Read a single connection (no credentials) | | `environment:connections:read_credentials` | Read a single connection with access/refresh tokens | | `environment:connections:create` | Create connections | | `environment:connections:update` | Update connections and metadata | | `environment:connections:delete` | Delete connections | ### Connect Sessions | Scope | Description | |-------|-------------| | `environment:connect_sessions:write` | Create and reconnect sessions for the Connect UI auth flow | ### Syncs | Scope | Description | |-------|-------------| | `environment:syncs:read` | Read sync status | | `environment:syncs:execute` | Trigger, pause, start syncs | | `environment:syncs:update` | Update sync connection frequency | | `environment:syncs:variant:create` | Create sync variants | | `environment:syncs:variant:delete` | Delete sync variants | ### Deploy | Scope | Description | |-------|-------------| | `environment:deploy` | Deploy syncs and actions via CLI or API | ### Functions | Scope | Description | |-------|-------------| | `environment:functions:list` | List deployed functions | | `environment:functions:read` | Read a single deployed function | | `environment:functions:delete` | Delete a deployed function | | `environment:functions:compile` | Compile function source code | | `environment:functions:dryrun` | Dry run function source code | ### Records | Scope | Description | |-------|-------------| | `environment:records:read` | Read synced records | | `environment:records:write` | Prune records | ### Actions | Scope | Description | |-------|-------------| | `environment:actions:execute` | Trigger actions and read action results | ### Proxy | Scope | Description | |-------|-------------| | `environment:proxy` | Send proxy requests to external APIs through Nango | ### Variables | Scope | Description | |-------|-------------| | `environment:variables:read` | Read environment variables | ### MCP | Scope | Description | |-------|-------------| | `environment:mcp` | Access the MCP endpoint | ## Authentication Source: https://nango.dev/docs/reference/backend/http-api/authentication.md Use an [API key](/reference/backend/http-api/api-keys) as a Bearer token in the `Authorization` header. API keys are managed in **Environment Settings > API Keys** in the Nango UI. ``` Authorization: Bearer ``` Each API key has [scopes](/reference/backend/http-api/api-keys#scopes) that control which endpoints it can access. A key without the required scope will receive a `403 Forbidden` response. ## Rate limits Source: https://nango.dev/docs/reference/backend/http-api/rate-limits.md The Nango API is rate-limited to prevent abuse and ensure fair usage across all clients. The rate limit is enforced on a per-account basis, with a fixed window of time and a maximum number of requests allowed within that window. Those headers are sent back with every API response: - `X-RateLimit-Limit` indicates the maximum number of requests a client can make within the rate limit window. - `X-RateLimit-Remaining` shows how many requests are remaining before the limit is reached. - `X-RateLimit-Reset` provides a Unix timestamp representing the time at which the current rate limit window resets, and the remaining request count is replenished. If a client exceeds the rate limit, the API will respond with a 429 `Too Many Requests` status code. In this case, the `Retry-After` header is included, indicating the number of seconds the client should wait before making another request to avoid being rate-limited. ## List all integrations Source: https://nango.dev/docs/reference/backend/http-api/integration/list.md Requires an API key with one of: `environment:integrations:list` or `environment:integrations:list_credentials`. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ```ts Node Client const nango = new Nango({ secretKey }); const response = await nango.listIntegrations(); ``` ```json Example Response { "data": [ { "unique_key": "slack-nango-community", "display_name": "Slack", "provider": "slack", "logo": "http://localhost:3003/images/template-logos/slack.svg", "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", }, { "unique_key": "github-prod", "display_name": "GitHub", "provider": "github", "logo": "http://localhost:3003/images/template-logos/github.svg", "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", }, ] } ``` ## Get an integration Source: https://nango.dev/docs/reference/backend/http-api/integration/get.md Requires an API key with one of: `environment:integrations:read` or `environment:integrations:read_credentials`. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ```json Basic response { "data": { "unique_key": "slack-nango-community", "display_name": "Slack", "provider": "slack", "logo": "http://localhost:3003/images/template-logos/github.svg", "forward_webhooks": true, "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z" } } ``` ```json With credentials (OAuth2/OAuth1/TBA) { "data": { "unique_key": "github-nango", "display_name": "GitHub", "provider": "github", "logo": "http://localhost:3003/images/template-logos/github.svg", "forward_webhooks": false, "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", "credentials": { "type": "OAUTH2", "client_id": "abc123", "client_secret": "secret456", "scopes": "repo,user", "webhook_secret": null } } } ``` ```json With credentials (GitHub App / CUSTOM) { "data": { "unique_key": "github-app-nango", "display_name": "GitHub App", "provider": "github-app", "logo": "http://localhost:3003/images/template-logos/github.svg", "forward_webhooks": false, "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", "credentials": { "type": "CUSTOM", "client_id": "abc123", "client_secret": "secret456", "app_id": "123456", "app_link": "https://github.com/apps/my-app", "private_key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" } } } ``` ## Create an integration Source: https://nango.dev/docs/reference/backend/http-api/integration/create.md Requires an API key with the `environment:integrations:create` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ```json Example Response { "data": { "unique_key": "slack-nango-community", "display_name": "Slack", "provider": "slack", "logo": "http://localhost:3003/images/template-logos/github.svg", "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", } } ``` ## Update an integration Source: https://nango.dev/docs/reference/backend/http-api/integration/update.md Requires an API key with the `environment:integrations:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Delete an integration Source: https://nango.dev/docs/reference/backend/http-api/integration/delete.md Requires an API key with the `environment:integrations:delete` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Create a connect session Source: https://nango.dev/docs/reference/backend/http-api/connect/sessions/create.md Requires an API key with the `environment:connect_sessions:write` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Creates a short-lived connect session (30m). The returned token can be used for instance to create connections through Connect UI. You can attach connection-level tags to the session via the `tags` field. These tags are copied onto the created connection and included in auth webhooks. Most apps start with these tags: `end_user_id`, `end_user_email`, `organization_id`. ## Create a reconnect session Source: https://nango.dev/docs/reference/backend/http-api/connect/sessions/reconnect.md Requires an API key with the `environment:connect_sessions:write` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Get a connect session Source: https://nango.dev/docs/reference/backend/http-api/connect/session/get.md Retrieves a connect session information (allowed integrations, default connection configurations). ## Delete a connect session Source: https://nango.dev/docs/reference/backend/http-api/connect/session/delete.md Deletes a connect session ## Import a connection Source: https://nango.dev/docs/reference/backend/http-api/connections/post.md Requires an API key with the `environment:connections:create` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## When to use Use this API endpoint to import existing access tokens into Nango. It is mostly meant for one-off bulk imports when onboarding Nango, or for migrating existing connections when a provider introduced changes to its auth flow. If a connection with the same `connection_id` and `provider_config_key` already exists, the endpoint updates it with the provided credentials instead of creating a new one. To trigger OAuth flows with Nango, use the [Nango frontend SDK](/reference/frontend/frontend-sdk). ## Request body You can use this endpoint to import OAuth 2, OAuth 1, API Keys, and Basic auth credentials. The required fields depend on the type of authentication of the _connections_ you are trying to import. ## List connections Source: https://nango.dev/docs/reference/backend/http-api/connections/list.md Requires an API key with one of: `environment:connections:list` or `environment:connections:list_credentials`. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ```json Example Response { "connections": [ { "id": 1, "connection_id": "test-1", "provider": "slack", "provider_config_key": "slack-nango-community", "created": "2023-06-03T14:53:22.051Z", "metadata": null, "tags": { "end_user_id": "your-internal-id", "end_user_email": "user@example.com", "organization_id": "user-organization-id" }, "errors": [{ "type": "auth", "log_id": "VrnbtykXJFckCm3HP93t"}] }, { "id": 2, "connection_id": "test-2", "provider": "slack", "provider_config_key": "slack-nango-community", "created": "2023-06-03T15:00:14.945Z", "metadata": { "bot_id": "some-uuid" }, "tags": { "end_user_id": "your-internal-id", "end_user_email": "user@example.com", "organization_id": "user-organization-id" }, "errors": [] } ] } ``` ## Get connection & credentials Source: https://nango.dev/docs/reference/backend/http-api/connections/get.md Requires an API key with one of: `environment:connections:read` or `environment:connections:read_credentials`. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). The response content depends on the API authentication type (e.g: OAuth 2, OAuth 1, API key, etc.). If you do not want to deal with collecting & injecting credentials in requests for multiple authentication types, use the [Proxy](/guides/platform/proxy-requests). Every time you fetch the connection with this API endpoint, Nango will check if the access token has expired. If it has, it will refresh it. **We recommend you always fetch the token just before you use it to make sure it is fresh!** ## Patch a connection Source: https://nango.dev/docs/reference/backend/http-api/connections/patch.md Requires an API key with the `environment:connections:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Set connection metadata Source: https://nango.dev/docs/reference/backend/http-api/connections/set-metadata.md Requires an API key with the `environment:connections:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Set _connection metadata_ Nango uses the request body as the new metadata (it must be a JSON object). Note that this overrides any existing metadata. The connection_id must be passed in the body as well. Note that the connection_id can be a single string or an array of strings. ## Fetching _connection metadata_ To read the existing metadata of a _connection_, simply fetch the connection. Your custom metadata is included as part of the returned connection object. ## Edit connection metadata Source: https://nango.dev/docs/reference/backend/http-api/connections/update-metadata.md Requires an API key with the `environment:connections:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Update _connection metadata_ Nango uses the request body as the new metadata (it must be a JSON object). Note that this overrides specified properties, not the entire metadata. The connection_id must be passed in the body as well. Note that the connection_id can be a single string or an array of strings. ## Fetching _connection metadata_ To read the existing metadata of a _connection_, simply fetch the connection. Your custom metadata is included as part of the returned connection object. ## Delete a connection Source: https://nango.dev/docs/reference/backend/http-api/connections/delete.md Requires an API key with the `environment:connections:delete` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## List functions Source: https://nango.dev/docs/reference/backend/http-api/functions/list.md Requires an API key with the `environment:functions:list` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Returns the functions deployed to an integration. ## Get a function Source: https://nango.dev/docs/reference/backend/http-api/functions/get.md Requires an API key with the `environment:functions:read` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Returns a single deployed function. ## Get a function's source code Source: https://nango.dev/docs/reference/backend/http-api/functions/code.md Requires an API key with the `environment:functions:read` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Returns the TypeScript source code of a function for a given integration. ## Delete a function Source: https://nango.dev/docs/reference/backend/http-api/functions/delete.md Requires an API key with the `environment:functions:delete` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Deletes a deployed function and enqueues its async teardown. Functions managed by `nango deploy` (repo source) cannot be deleted through this endpoint. ## Compile a function Source: https://nango.dev/docs/reference/backend/http-api/functions/compile.md Requires an API key with the `environment:functions:compile` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Compiles TypeScript function source code and returns the bundled JavaScript. ## Dry run a function Source: https://nango.dev/docs/reference/backend/http-api/functions/dryruns-create.md Requires an API key with the `environment:functions:dryrun` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Starts an asynchronous dry run for TypeScript function source code against a connection without deploying it. ## Get dry run results Source: https://nango.dev/docs/reference/backend/http-api/functions/dryruns-get.md Requires an API key with the `environment:functions:dryrun` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Returns the status and result of an asynchronous function dry run. ## Create a function deployment Source: https://nango.dev/docs/reference/backend/http-api/functions/deployments.md Requires an API key with the `environment:deploy` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Deploys a function to an existing integration: either submitted TypeScript source code (`type: function`) or a template from the provider catalog (`type: template`). ## Get integration functions config Source: https://nango.dev/docs/reference/backend/http-api/scripts/config.md Requires an API key with the `environment:integrations:list_functions` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). The `/scripts/config` endpoint returns the configuration for all integration functions. There are two variants of this endpoint: 1. `/scripts/config?format=nango` - Returns the standard configuration format 2. `/scripts/config?format=openai` - Returns the configuration in OpenAI's function calling format ## OpenAI Function Format The `/scripts/config?format=openai` endpoint transforms the script configurations into OpenAI's function calling format. This format is particularly useful when working with OpenAI's API to enable function calling capabilities. ### Parameter Descriptions The endpoint automatically parses parameter descriptions from the script's description field. If a script's description contains a markdown list of parameters, these descriptions will be used for the corresponding parameters in the OpenAI function format. For example, in your script file, you can define a script like this: ```typescript google-calendar/actions/move-event.ts export default createAction({ description: `Move an event to a different time or calendar with the following parameters: - eventId: The ID of the event to move - start: New start time in ISO format (e.g., "2024-03-28T14:00:00") - end: New end time in ISO format (e.g., "2024-03-28T15:00:00") - calendar: Optional new calendar ID to move the event to`, version: '1.0.0', endpoints: [{ method: 'GET', path: '/example/github/issues', group: 'Issues' }], frequency: 'every hour', autoStart: false, scopes: ['https://www.googleapis.com/auth/calendar'], input: z.object({ eventId: z.string(), start: z.string(), end: z.string(), calendar: z.string().optional() }) }); ``` The endpoint will generate a function definition like this: ```json { "data": [ { "name": "move-event", "description": "Move an event to a different time or calendar with the following parameters:\n- eventId: The ID of the event to move\n- start: New start time in ISO format (e.g., \"2024-03-28T14:00:00\")\n- end: New end time in ISO format (e.g., \"2024-03-28T15:00:00\")\n- calendar: Optional new calendar ID to move the event to", "parameters": { "type": "object", "properties": { "eventId": { "type": "string", "description": "The ID of the event to move" }, "start": { "type": "string", "description": "New start time in ISO format (e.g., \"2024-03-28T14:00:00\")" }, "end": { "type": "string", "description": "New end time in ISO format (e.g., \"2024-03-28T15:00:00\")" }, "calendar": { "type": "string", "description": "Optional new calendar ID to move the event to" } }, "required": ["eventId", "start", "end"] } } ] } ``` ### Array Fields The endpoint properly handles array fields in the configuration. For example, if a field is defined as: ```typescript google-calendar/actions/create-event.ts export default createAction({ description: `Create a new calendar event with the following parameters: - summary: The title of the event - attendees: List of email addresses of event attendees`, version: '1.0.0', endpoints: [{ method: 'POST', path: '/example/google-calendar/events', group: 'Events' }], input: z.object({ summary: z.string(), attendees: z.array(z.string()).optional() }) }); ``` It will be transformed into: ```json { "data": [ { "name": "create-event", "description": "Create a new calendar event with the following parameters:\n- summary: The title of the event\n- attendees: List of email addresses of event attendees", "parameters": { "type": "object", "properties": { "summary": { "type": "string", "description": "The title of the event" }, "attendees": { "type": "array", "description": "List of email addresses of event attendees", "items": { "type": "string" } } }, "required": ["summary"] } } ] } ``` ## Get records Source: https://nango.dev/docs/reference/backend/http-api/sync/records-list.md Requires an API key with the `environment:records:read` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Receive webhooks on data updates Receive webhooks from Nango when new records are available. Follow the [sync functions section](/guides/functions/syncs/sync-functions). **Data availability**: Records are subject to Nango's [data retention policies](/guides/platform/security#synced-records-retention). Records not updated for 30 days will have their payload pruned (only metadata remains). Records from syncs not executed for 60 days are permanently deleted. Fetch and store records promptly in your own system. ## Response This endpoint returns a list of records, ordered by modification date ascending. If some records are updated while you paginate through this endpoint, you might see these records multiple times. A response may contain fewer records than `limit` when records have a large size. Always check `next_cursor` and continue paginating until it is `null`. ### Default behavior By default this returns an array of objects in the data model that you queried with some metadata about each record. ```json { records: [ { id: 123, ..., // Fields as specified in the model you queried _nango_metadata: { deleted_at: null, last_action: 'ADDED', first_seen_at: '2023-09-18T15:20:35.941305+00:00', last_modified_at: '2023-09-18T15:20:35.941305+00:00', cursor: 'MjAyNC0wMy0wNFQwNjo1OTo1MS40NzE0NDEtMDU6MDB8fDE1Y2NjODA1LTY0ZDUtNDk0MC1hN2UwLTQ1ZmM3MDQ5OTdhMQ==' } }, ... ], next_cursor: "MjAyMy0xMS0xN1QxMTo0NzoxNC40NDcrMDI6MDB8fDAzZTA1NzIxLWNiZmQtNGYxNS1iYTNhLWFlNjM2Y2MwNmEw==" } ``` ## Prune records Source: https://nango.dev/docs/reference/backend/http-api/sync/prune-records.md Requires an API key with the `environment:records:write` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). Prunes synced records for a given connection and model using cursor-based pagination. ## What is pruning? Pruning empties the record payload while preserving the record metadata. This is useful for compliance requirements, ensuring Nango doesn't hold onto your records data while maintaining the ability to track record state. The payload can be restored by re-syncing the data. Pruning is not the same as marking a record as deleted from the external API. This endpoint prunes data from Nango’s cache only. It does not delete anything on the external API, and it is not the same as [detecting a deletion from the source](/guides/functions/syncs/deletion-detection). If you need to tell your customers that a record was deleted on the external API while keeping its last-known payload in cache, use `batchDelete` or `trackDeletesStart`/`trackDeletesEnd` in your sync functions instead. ## Cursor behavior Records are pruned up to and including the specified `until_cursor` value. Use the `_nango_metadata.cursor` value from the record you want to prune up to. ## Response The `has_more` field indicates whether there are potentially more records to prune. When `true`, make another request with the same `until_cursor` to continue pruning. When `false`, all records up to the cursor have been pruned. ```json { "count": 150, "has_more": true } ``` ## Trigger sync(s) Source: https://nango.dev/docs/reference/backend/http-api/sync/trigger.md Requires an API key with the `environment:syncs:execute` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Triggering one-off syncs This is especially useful if you e.g. changed the metadata for the connection and now want to re-import data. Use `reset` to clear the checkpoint and re-fetch all data, or `reset` with `emptyCache` to start completely fresh. See the [checkpoints guide](/guides/functions/syncs/checkpoints#re-syncing-resetting-checkpoints-and-the-cache) for details. ## Start sync(s) Source: https://nango.dev/docs/reference/backend/http-api/sync/start.md Requires an API key with the `environment:syncs:execute` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Pause sync(s) Source: https://nango.dev/docs/reference/backend/http-api/sync/pause.md Requires an API key with the `environment:syncs:execute` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Sync status Source: https://nango.dev/docs/reference/backend/http-api/sync/status.md Requires an API key with the `environment:syncs:read` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). The response includes the current `checkpoint` for each sync, which tracks how far the sync has progressed. Learn more about [checkpoints](/guides/functions/syncs/checkpoints). ## Override sync connection frequency Source: https://nango.dev/docs/reference/backend/http-api/sync/update-connection-frequency.md Requires an API key with the `environment:syncs:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Get environment variables Source: https://nango.dev/docs/reference/backend/http-api/sync/environment-variables.md Requires an API key with the `environment:variables:read` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Create sync variant Source: https://nango.dev/docs/reference/backend/http-api/sync/create-variant.md Requires an API key with the `environment:syncs:variant:create` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Creating a sync variant Sync variants allow you to create different configurations of the same sync for a specific connection. This is useful when you need to sync the same data with different settings or filters. ### Key features - Each variant operates independently with its own sync schedule - Variants can be queried separately through the records endpoint using the `variant` query parameter - Cannot use "base" as a variant name (protected) After creating a variant, you can use the `/records` endpoint to access its data by specifying the variant query parameter: ``` GET /records?model=MyModel&variant=MyVariant ``` ## Delete sync variant Source: https://nango.dev/docs/reference/backend/http-api/sync/delete-variant.md Requires an API key with the `environment:syncs:variant:delete` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Trigger an action Source: https://nango.dev/docs/reference/backend/http-api/action/trigger.md Requires an API key with the `environment:actions:execute` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Proxy - GET requests Source: https://nango.dev/docs/reference/backend/http-api/proxy/get.md Requires an API key with the `environment:proxy` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ### API request The path, query params, and headers you send to the Proxy are all passed on to the external API, except for the configuration headers mentioned here: * Authorization: See [Nango API Authentication](/reference/backend/http-api/authentication). Nango will add a new Authorization header for the external API as needed. * Provider-Config-Key * Connection-Id * Retries * Base-Url-Override: provide an API base URL when the base API is not listed in the [providers.yaml](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml) or it needs to be overridden ### API response The response from the external API is passed back to you exactly as Nango gets it: * Response code * Response headers * Response body ## Proxy - POST requests Source: https://nango.dev/docs/reference/backend/http-api/proxy/post.md Requires an API key with the `environment:proxy` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ### API request The path, query params, and headers you send to the Proxy are all passed on to the external API, except for the configuration headers mentioned here: * Authorization: See [Nango API Authentication](/reference/backend/http-api/authentication). Nango will add a new Authorization header for the external API as needed. * Provider-Config-Key * Connection-Id * Retries ### API response The response from the external API is passed back to you exactly as Nango gets it: * Response code * Response headers * Response body ## Proxy - PUT requests Source: https://nango.dev/docs/reference/backend/http-api/proxy/put.md Requires an API key with the `environment:proxy` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ### API request The path, query params, and headers you send to the Proxy are all passed on to the external API, except for the configuration headers mentioned here: * Authorization: See [Nango API Authentication](/reference/backend/http-api/authentication). Nango will add a new Authorization header for the external API as needed. * Provider-Config-Key * Connection-Id * Retries * Base-Url-Override: provide an API base URL when the base API is not listed in the [providers.yaml](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml) or it needs to be overridden ### API response The response from the external API is passed back to you exactly as Nango gets it: * Response code * Response headers * Response body ## Proxy - PATCH requests Source: https://nango.dev/docs/reference/backend/http-api/proxy/patch.md Requires an API key with the `environment:proxy` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ### API request The path, query params, and headers you send to the Proxy are all passed on to the external API, except for the configuration headers mentioned here: * Authorization: See [Nango API Authentication](/reference/backend/http-api/authentication). Nango will add a new Authorization header for the external API as needed. * Provider-Config-Key * Connection-Id * Retries ### API response The response from the external API is passed back to you exactly as Nango gets it: * Response code * Response headers * Response body ## Proxy - DELETE requests Source: https://nango.dev/docs/reference/backend/http-api/proxy/delete.md Requires an API key with the `environment:proxy` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ### API request The path, query params, and headers you send to the Proxy are all passed on to the external API, except for the configuration headers mentioned here: * Authorization: See [Nango API Authentication](/reference/backend/http-api/authentication). Nango will add a new Authorization header for the external API as needed. * Provider-Config-Key * Connection-Id * Retries * Base-Url-Override: provide an API base URL when the base API is not listed in the [providers.yaml](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml) or it needs to be overridden ### API response The response from the external API is passed back to you exactly as Nango gets it: * Response code * Response headers * Response body ## List all providers Source: https://nango.dev/docs/reference/backend/http-api/providers/list.md ```json Example Response { "data": [ { "name": "hubspot", "logo_url": "https://app.nango.dev/images/template-logos/hubspot.svg", "display_name": "HubSpot", "categories": ["marketing","support","crm"], "auth_mode": "OAUTH2", "authorization_url": "https://app.hubspot.com/oauth/authorize", "token_url": "https://api.hubapi.com/oauth/v1/token", "connection_configuration": ["portalId"], "post_connection_script": "hubspotPostConnection", "webhook_routing_script": "hubspotWebhookRouting", "proxy": { "base_url": "https://api.hubapi.com", "decompress": true, "paginate": { "type": "cursor", "cursor_path_in_response": "paging.next.after", "limit_name_in_request": "limit", "cursor_name_in_request": "after", "response_path": "results" } }, "docs": "https://nango.dev/docs/api-integrations/hubspot" }, { "name": "posthog", "logo_url": "https://app.nango.dev/images/template-logos/posthog.svg", "display_name": "PostHog", "categories": ["dev-tools"], "auth_mode": "API_KEY", "proxy": { "base_url": "https://api.posthog.com", }, "docs": "https://nango.dev/docs/api-integrations/posthog" } ] } ``` ## Get a provider Source: https://nango.dev/docs/reference/backend/http-api/providers/get.md ```json Example Response { "data": { "name": "hubspot", "logo_url": "https://app.nango.dev/images/template-logos/hubspot.svg", "display_name": "HubSpot", "categories": ["marketing","support","crm"], "auth_mode": "OAUTH2", "authorization_url": "https://app.hubspot.com/oauth/authorize", "token_url": "https://api.hubapi.com/oauth/v1/token", "connection_configuration": ["portalId"], "post_connection_script": "hubspotPostConnection", "webhook_routing_script": "hubspotWebhookRouting", "proxy": { "base_url": "https://api.hubapi.com", "decompress": true, "paginate": { "type": "cursor", "cursor_path_in_response": "paging.next.after", "limit_name_in_request": "limit", "cursor_name_in_request": "after", "response_path": "results" } }, "docs": "https://nango.dev/docs/api-integrations/hubspot" } } ``` ## List provider templates Source: https://nango.dev/docs/reference/backend/http-api/providers/templates.md Returns the template functions available in the catalog for a provider. ## List connections (deprecated) Source: https://nango.dev/docs/reference/backend/http-api/connection/list.md Requires an API key with one of: `environment:connections:list` or `environment:connections:list_credentials`. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ```json Example Response { "connections": [ { "id": 1, "connection_id": "test-1", "provider": "slack", "provider_config_key": "slack-nango-community", "created": "2023-06-03T14:53:22.051Z", "metadata": null, "tags": { "end_user_id": "your-internal-id", "end_user_email": "user@example.com", "organization_id": "user-organization-id" }, "errors": [{ "type": "auth", "log_id": "VrnbtykXJFckCm3HP93t"}] }, { "id": 2, "connection_id": "test-2", "provider": "slack", "provider_config_key": "slack-nango-community", "created": "2023-06-03T15:00:14.945Z", "metadata": { "bot_id": "some-uuid" }, "tags": { "end_user_id": "your-internal-id", "end_user_email": "user@example.com", "organization_id": "user-organization-id" }, "errors": [] } ] } ``` ## Get connection & credentials (deprecated) Source: https://nango.dev/docs/reference/backend/http-api/connection/get.md Requires an API key with one of: `environment:connections:read` or `environment:connections:read_credentials`. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). The response content depends on the API authentication type (e.g: OAuth 2, OAuth 1, API key, etc.). If you do not want to deal with collecting & injecting credentials in requests for multiple authentication types, use the [Proxy](/guides/platform/proxy-requests). Every time you fetch the connection with this API endpoint, Nango will check if the access token has expired. If it has, it will refresh it. **We recommend you always fetch the token just before you use it to make sure it is fresh!** ## Import a connection (deprecated) Source: https://nango.dev/docs/reference/backend/http-api/connection/post.md Requires an API key with the `environment:connections:create` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## When to use Use this API endpoint to import existing access tokens into Nango. It is mostly meant for one-off bulk imports when onboarding Nango, or for migrating existing connections when a provider introduced changes to its auth flow. If a connection with the same `connection_id` and `provider_config_key` already exists, the endpoint updates it with the provided credentials instead of creating a new one. To trigger OAuth flows with Nango, use the [Nango frontend SDK](/reference/frontend/frontend-sdk). ## Request body You can use this endpoint to import OAuth 2, OAuth 1, API Keys, and Basic auth credentials. The required fields depend on the type of authentication of the _connections_ you are trying to import. ## Set connection metadata (deprecated) Source: https://nango.dev/docs/reference/backend/http-api/connection/set-metadata.md Requires an API key with the `environment:connections:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Set _connection metadata_ Nango uses the request body as the new metadata (it must be a JSON object). Note that this overrides any existing metadata. The connection_id must be passed in the body as well. Note that the connection_id can be a single string or an array of strings. ## Fetching _connection metadata_ To read the existing metadata of a _connection_, simply fetch the connection. Your custom metadata is included as part of the returned connection object. ## Set connection metadata (deprecated) Source: https://nango.dev/docs/reference/backend/http-api/connection/set-metadata-legacy.md Requires an API key with the `environment:connections:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Set _connection metadata_ Nango uses the request body as the new metadata (it must be a JSON object). Note that this overrides any existing metadata. ## Fetching _connection metadata_ To read the existing metadata of a _connection_, simply fetch the connection. Your custom metadata is included as part of the returned connection object. ## Edit connection metadata (deprecated) Source: https://nango.dev/docs/reference/backend/http-api/connection/update-metadata.md Requires an API key with the `environment:connections:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Update _connection metadata_ Nango uses the request body as the new metadata (it must be a JSON object). Note that this overrides specified properties, not the entire metadata. The connection_id must be passed in the body as well. Note that the connection_id can be a single string or an array of strings. ## Fetching _connection metadata_ To read the existing metadata of a _connection_, simply fetch the connection. Your custom metadata is included as part of the returned connection object. ## Edit connection metadata (deprecated) Source: https://nango.dev/docs/reference/backend/http-api/connection/update-metadata-legacy.md Requires an API key with the `environment:connections:update` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Update _connection metadata_ Nango uses the request body as the new metadata (it must be a JSON object). ## Fetching _connection metadata_ To read the existing metadata of a _connection_, simply fetch the connection. Your custom metadata is included as part of the returned connection object. ## Delete a connection (deprecated) Source: https://nango.dev/docs/reference/backend/http-api/connection/delete.md Requires an API key with the `environment:connections:delete` scope. [Learn more about API key scopes](/reference/backend/http-api/api-keys#scopes). ## Node Source: https://nango.dev/docs/reference/backend/backend-sdk/node.md The backend SDK lets you interact with the Nango API. It is available on [NPM](https://www.npmjs.com/package/@nangohq/node) as `@nangohq/node`. ## Instantiate the backend SDK Install it with your favorite package manager, e.g.: ```bash npm i -S @nangohq/node ``` Instantiate the `Nango` class: ```js import { Nango } from '@nangohq/node'; const nango = new Nango({ apiKey: '' }); ``` **Parameters** Your environment API key, found in **Environment Settings > API Keys** in the Nango UI. Sent as the bearer token for API calls. This key should never be shared. Deprecated alias for `apiKey`, kept for backward compatibility. Provide either `apiKey` or `secretKey`. The environment's webhook signing key, found in **Environment Settings > Webhooks > Signing key**. Used by `verifyIncomingWebhookRequest` to validate incoming webhook signatures. Falls back to `secretKey` when omitted. On environments created after 2026-04-20 (or any environment that later rotated its API key), the signing key differs from the API key, so set this explicitly to verify webhooks. Omitting the host points to Nango Cloud. For local development, use `http://localhost:3003`. Use your instance URL if self-hosting. ## Rate limits The Nango SDK is rate-limited to prevent abuse and ensure fair usage across all clients. The rate limit is enforced on a per-account basis, with a fixed window of time and a maximum number of requests allowed within that window. If a client exceeds the rate limit, the API will respond with a 429 `Too Many Requests` status code. In this case, the `Retry-After` header is included, indicating the number of seconds the client should wait before making another request to avoid being rate-limited. To handle rate limiting gracefully, clients should monitor for the 429 status code and honor the `Retry-After` header value provided in the response. ```js // Example: try { const res = await nango.listIntegrations(); ... } catch(err) { if (err.response.status === 429) { const retryAfter = err.response.headers['retry-after']; // wait and retry ... } ... } ``` ## Integrations ### List all integrations Returns a list of integrations. ```js await nango.listIntegrations() ``` **Example Response** ```json { "configs": [ { "unique_key": "slack-nango-community", "provider": "slack", "logo": "http://localhost:3003/images/template-logos/slack.svg", "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", }, { "unique_key": "github-prod", "provider": "github", "logo": "http://localhost:3003/images/template-logos/github.svg", "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", }, ] } ``` ### Get an integration Returns a specific integration. ```js await nango.getIntegration({ uniqueKey: }); // Deprecated await nango.getIntegration(); ``` **Parameters** The integration ID Include sensitive data. Allowed values: `webhook`, `credentials` **Example Response** ```json { "data": { "unique_key": "slack-nango-community", "display_name": "Slack", "provider": "slack", "logo": "http://localhost:3003/images/template-logos/slack.svg", "forward_webhooks": true, "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z" } } ``` ### Create an integration Create a new integration. ```js await nango.createIntegration({ provider: '', unique_key: '' }); ``` **Parameters** The ID of the API provider in Nango (cf. [providers.yaml](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml) for a list of API provider IDs.) The integration ID. The display name of this integration The credentials to include depend on the specific integration that you want to create. Provider-specific configuration for providers with an `integration_config` schema (e.g. `private-api-generic`, `aws-sigv4`). See the provider's integration page for fields. Whether to forward webhooks received for this integration. Defaults to `true`. **Example Response** ```json { "data": { "unique_key": "slack-nango-community", "provider": "slack", "logo": "http://localhost:3003/images/template-logos/slack.svg", "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", } } ``` ### Update an integration Patch an integration. The first argument identifies the integration; all body fields are optional and only the fields you pass are updated. ```js await nango.updateIntegration({ uniqueKey: '' }, { display_name: 'New name' }); ``` **Parameters** The integration ID to update. Passed in the first argument. A new integration ID, to rename the integration. Not allowed when the integration has active connections. The display name of this integration Whether to forward webhooks received for this integration. The credentials to include depend on the specific integration that you want to update. Provider-specific configuration for providers with an `integration_config` schema. Only the fields you pass are updated. Free-form custom values for providers without an `integration_config` schema. **Example Response** ```json { "data": { "unique_key": "slack-nango-community", "provider": "slack", "logo": "http://localhost:3003/images/template-logos/slack.svg", "created_at": "2023-10-16T08:45:26.241Z", "updated_at": "2023-10-16T08:45:26.241Z", } } ``` ### Delete an integration Deletes a specific integration. ```js await nango.deleteIntegration(); ``` **Parameters** The integration ID. **Example Response** ```json { "success": true } ``` ## Connect ### Create a connect session Create a connect session. The token is short-lived and lasts for 30 minutes. ```js const { data } = await nango.createConnectSession({ // Recommended: copied onto the connection and included in auth webhooks. tags: { end_user_id: '', end_user_email: '', organization_id: '' }, allowed_integrations: ['', ''], integrations_config_defaults: { : { connection_config: { : '' } } } }); ``` **Parameters** Deprecated. Use `tags` instead. Deprecated. Use `tags` instead. The unique identifier for the organization. The display name of the organization. An array of integration IDs that are allowed for this session. Optional. Tags that will be copied onto the created connection and included in auth webhooks. Default configuration for specific integrations. For OAuth2 integrations, you can allow end-users to provide their own OAuth client credentials by setting empty `oauth_client_id_override` and `oauth_client_secret_override` values: ```js integrations_config_defaults: { google: { connection_config: { oauth_client_id_override: '', oauth_client_secret_override: '' } } } ``` When set to empty strings, Connect UI will display optional input fields for users to enter their own OAuth app credentials. `connection_config` also accepts `webhook_url` to override the environment's webhook URLs for the connection created with this session. When set, that connection's webhooks are sent only to this URL. See [Override webhook URLs per connection](/guides/platform/webhooks-from-nango#override-webhook-urls-per-connection). ```js integrations_config_defaults: { '': { connection_config: { webhook_url: 'https://.ngrok.app/webhooks-from-nango' } } } ``` Override Connect UI settings per integration. The key is your integration's unique ID (e.g. `jira`, `slack`, `github`) and the value is an object with the settings to override. Currently supports overriding the documentation URL shown in info icons during the connect flow: ```js overrides: { '': { docs_connect: 'https://your-docs.com/how-to-connect' } } ``` Available on the [Growth plan](https://www.nango.dev/pricing). **Returns** ```json { "data": { "token": "nango_connect_session_4603dbca8a588315ba69b5bfddde52e72d312dc2d2870bd5e45da6357333601c", "connect_link": "https://connect.nango.dev/?session_token=nango_connect_session_4603dbca8a588315ba69b5bfddde52e72d312dc2d2870bd5e45da6357333601c", "expires_at": "2024-09-27T19:49:51.449Z" } } ``` ### Create a reconnect session Create a reconnect session for a given `connection_id`. You can pass `tags` to update connection attribution during reconnection. Use this method when a user needs to input new credentials or to manually refresh token. This method is only compatible with `connection_id` created with a session token. ```js const { data } = await nango.createReconnectSession({ // Required connection_id: '', integration_id: '', // Optional tags: { end_user_id: '', end_user_email: '', organization_id: '' }, integrations_config_defaults: { : { connection_config: { : '' } } } }); ``` **Parameters** The unique identifier for the connection. The unique identifier for the integration. Optional. Tags to apply to the connection during reconnection. Deprecated. Use `tags` instead. Deprecated. Use `tags` instead. The unique identifier for the organization. The display name of the organization. Default configuration for specific integrations. **Returns** ```json { "data": { "token": "nango_connect_session_4603dbca8a588315ba69b5bfddde52e72d312dc2d2870bd5e45da6357333601c", "connect_link": "https://connect.nango.dev/?session_token=nango_connect_session_4603dbca8a588315ba69b5bfddde52e72d312dc2d2870bd5e45da6357333601c", "expires_at": "2024-09-27T19:49:51.449Z" } } ``` ## Connections ### List connections Returns a list of connections without credentials. ```js await nango.listConnections({ connectionId: 'connection-id', integrationId: 'integration-456', limit: 100, tags: { "end_user_id": 'user-123' } }); ``` **Parameters** Optional. Will exactly match a given connectionId. Can return multiple connections with the same ID across integrations. Deprecated. Prefer filtering connections using `tags` instead (e.g. `end_user_id`). Optional. Filter by integration ID. Optional. Filter by tags associated with connections. **Example Response** ```json { "connections": [ { "id": 1, "connection_id": "test-1", "provider": "slack", "provider_config_key": "slack-nango-community", "created": "2023-06-03T14:53:22.051Z", "metadata": null, "tags": { "end_user_id": "your-internal-id", "end_user_email": "user@example.com", "organization_id": "user-organization-id" }, "errors": [] }, { "id": 2, "connection_id": "test-2", "provider": "slack", "provider_config_key": "slack-nango-community", "created": "2023-06-03T15:00:14.945Z", "metadata": { "bot_id": "some-uuid" }, "tags": { "end_user_id": "your-internal-id", "end_user_email": "user@example.com", "organization_id": "user-organization-id" }, "errors": [{ "type": "auth", "log_id": "VrnbtykXJFckCm3HP93t"}] } ] } ``` ### Get a connection (with credentials) Returns a specific connection with credentials. ```js await nango.getConnection(, ); ``` The response content depends on the API authentication type (OAuth 2, OAuth 1, API key, Basic auth, etc.). If you do not want to deal with collecting & injecting credentials in requests for multiple authentication types, use the Proxy ([step-by-step guide](/guides/platform/proxy-requests)). When you fetch the connection with this API endpoint, Nango will check if the access token has expired. If it has, it will refresh it. We recommend not caching tokens for longer than 5 minutes to ensure they are fresh. **Parameters** The integration ID. The connection ID. Defaults to `false`. If `false`, the token will only be refreshed if it expires within 15 minutes. If `true`, a token refresh attempt will happen on each request. This is only useful for testing and should not be done at high traffic. Defaults to `false`. If `false`, the refresh token is not included in the response, otherwise it is. In production, it is not advised to return the refresh token, for security reasons, since only the access token is needed to sign requests. Defaults to `false`. If true, this will refresh the JWT token for GitHub App / Github App OAuth connections **Example Response** ```json { "id": 18393, "created_at": "2023-03-08T09:43:03.725Z", "updated_at": "2023-03-08T09:43:03.725Z", "provider_config_key": "github", "connection_id": "1", "credentials": { "type": "OAUTH2", "access_token": "gho_tsXLG73f....", "refresh_token": "gho_fjofu84u9....", "expires_at": "2024-03-08T09:43:03.725Z", "raw": { // Raw token response from the OAuth provider: Contents vary! "access_token": "gho_tsXLG73f....", "refresh_token": "gho_fjofu84u9....", "token_type": "bearer", "scope": "public_repo,user" } }, "connection_config": { "subdomain": "myshop", "realmId": "XXXXX", "instance_id": "YYYYYYY" }, "metadata": { "myProperty": "yes", "filter": "closed=true" } } ``` ### Patch a connection Patch a connection. ```js await nango.patchConnection({ connectionId: '', provider_config_key: '' }, { ...body }); ``` **Parameters** The body of the connection (see [API Reference](/reference/backend/http-api/connections/patch)). Connection tags (key/value strings). Keys are normalized to lowercase. Maximum string length: 255 Deprecated. Use tags instead Deprecated. Uniquely identifies the end user. Deprecated. The email address of the end user. Deprecated. The display name of the end user. Deprecated. Tags associated with the end user. Only accepts string values, up to 64 keys. **Example Response** ```json { "success": true } ``` ### Get connection metadata Returns a connection's metadata. ```js await nango.getMetadata('', 'CONNECTION-ID'); ``` If you know the structure of the metadata, you can specify a type; ```ts interface CustomMetadata { anyKey: Record; } const myTypedMetadata = await nango.getMetadata('', ''); ``` **Parameters** The integration ID of the connection. The connection ID. **Example Response** ```json { "custom_key1": "custom_value1" } ``` ### Set connection metadata Set custom metadata for the connection or connections (overrides existing metadata). ```js await nango.setMetadata('', 'CONNECTION-ID', { 'CUSTOM_KEY1': 'CUSTOM_VALUE1' }); # set an array of connection ids await nango.setMetadata('', ['CONNECTION-ID', 'CONNECTION-ID-TWO'], { 'CUSTOM_KEY1': 'CUSTOM_VALUE1' }); ``` **Parameters** The integration ID of the connection. The connection ID or connection IDs. The custom metadata to store in the connection. **Response** ```json { "connection_id": "", "provider_config_key": "", "metadata": { "CUSTOM_KEY1": "CUSTOM_VALUE1" } } ``` ### Edit connection metadata Edit custom metadata for the connection or connections. Only overrides specified properties, not the entire metadata. ```js await nango.updateMetadata('', 'CONNECTION-ID', { 'CUSTOM_KEY1': 'CUSTOM_VALUE1' }); # update an array of connection ids await nango.updateMetadata('', ['CONNECTION-ID', 'CONNECTION-ID-TWO'], { 'CUSTOM_KEY1': 'CUSTOM_VALUE1' }); ``` **Parameters** The integration ID of the connection. The connection ID or connection IDs. The custom metadata to store in the connection. **Response** ```json { "connection_id": "", "provider_config_key": "", "metadata": { "CUSTOM_KEY1": "CUSTOM_VALUE1" } } ``` ### Delete a connection Deletes a specific connection. ```js await nango.deleteConnection('', 'CONNECTION-ID'); ``` **Parameters** The integration ID of the connection. The connection ID. **Response** Empty response. ### Wait for connection Waits for a connection to be created for a given end user and integration. This is useful in agentic flows where you need to wait for a user to complete the OAuth flow before proceeding. ```js const connection = await nango.waitForConnection('', ''); ``` This method polls for a connection every 2 seconds for up to 60 seconds (30 attempts). If no connection is found within this time, it will throw a timeout error. **Parameters** The integration ID (provider config key). The end user ID to wait for a connection for. **Example Response** ```json { "id": 1, "connection_id": "", "provider": "slack", "provider_config_key": "slack-integration", "created": "2023-06-03T14:53:22.051Z", "metadata": null, "errors": [], "tags": { "end_user_id": "user-123", "end_user_email": "user@example.com", "organization_id": "org-456" } } ``` ## Integration functions ### List functions Returns the functions deployed to an integration, with pagination. ```ts await nango.listFunctions({ uniqueKey: 'github' }); ``` Filter by function type, name, or page: ```ts await nango.listFunctions({ uniqueKey: 'github' }, { type: 'action', search: 'issue', page: 0, limit: 20 }); ``` **Parameters** The integration ID. Filter by function type: `sync`, `action`, or `on-event`. Case-insensitive filter on the function name. Page to return (starts at `0`, default `0`). Results per page (1-100, default `20`). **Example Response** ```json { "data": [ { "type": "action", "name": "create-issue", "description": "Create a GitHub issue", "returns": ["GithubIssue"], "json_schema": null, "id": 42, "enabled": true, "last_deployed": "2024-03-28T14:00:00.000Z", "source": "repo" }, { "type": "sync", "name": "issues", "returns": ["GithubIssue"], "json_schema": null, "runs": "every hour", "auto_start": true, "track_deletes": false, "id": 43, "enabled": true, "last_deployed": "2024-03-28T14:00:00.000Z", "source": "repo" } ], "pagination": { "total": 2, "page": 0, "limit": 20 } } ``` ### Get a function Retrieves a deployed function of an integration. ```ts await nango.getFunction({ uniqueKey: 'github', name: 'create-issue' }); ``` Pass `type` to disambiguate when a sync and an action share the same name: ```ts await nango.getFunction({ uniqueKey: 'github', name: 'issues' }, { type: 'sync' }); ``` **Parameters** The integration ID. The function name. Disambiguates when functions share a name: `sync`, `action`, or `on-event`. **Example Response** ```json { "data": { "type": "action", "name": "create-issue", "description": "Create a GitHub issue", "returns": ["GithubIssue"], "json_schema": null, "id": 42, "enabled": true, "last_deployed": "2024-03-28T14:00:00.000Z", "source": "repo" } } ``` ### Get function code Retrieves the source code of a deployed function. ```ts await nango.getFunctionCode({ uniqueKey: 'github', name: 'create-issue' }, { type: 'action' }); ``` **Parameters** The integration ID. The function name. Disambiguates when functions share a name: `sync`, `action`, or `on-event`. **Example Response** ```json { "type": "action", "code": "export default createAction({ /* ... */ });" } ``` ### Delete a function Deletes a deployed function of an integration. The `type` is required. ```ts await nango.deleteFunction({ uniqueKey: 'github', name: 'create-issue' }, { type: 'action' }); ``` **Parameters** The integration ID. The function name. The function type: `sync` or `action`. **Example Response** ```json { "data": { "success": true } } ``` ### Get integration functions config Return the configuration for all integration functions ```ts const scriptsConfig = await nango.getScriptsConfig(); ``` **Example Response** ```json [ { "providerConfigKey": "demo-github-integration", "syncs": [ { "name": "github-issue-example", "type": "sync", "models": [ { "name": "GithubIssue", "fields": [ { "name": "id", "type": "integer" }, { "name": "owner", "type": "string" }, { "name": "repo", "type": "string" }, { "name": "issue_number", "type": "number" }, { "name": "title", "type": "string" }, { "name": "author", "type": "string" }, { "name": "author_id", "type": "string" }, { "name": "state", "type": "string" }, { "name": "date_created", "type": "date" }, { "name": "date_last_modified", "type": "date" }, { "name": "body", "type": "string" } ] } ], "sync_type": "FULL", // DEPRECATED "runs": "every half hour", "track_deletes": false, "auto_start": false, "last_deployed": "2024-02-28T20:16:38.052Z", "is_public": false, "pre_built": false, "version": "4", "attributes": {}, "input": {}, "returns": [ "GithubIssue" ], "description": "Fetches the Github issues from all a user's repositories.\nDetails: doesn't track deletes, metadata is not required.\n", "scopes": [ "public_repo" ], "endpoints": [ { "GET": "/github/issue-example" } ], "nango_yaml_version": "v2", "webhookSubscriptions": [] } ], "actions": [ { "name": "fetch-issues", "type": "action", "models": [ { "name": "GithubIssue", "fields": [ { "name": "id", "type": "integer" }, { "name": "owner", "type": "string" }, { "name": "repo", "type": "string" }, { "name": "issue_number", "type": "number" }, { "name": "title", "type": "string" }, { "name": "author", "type": "string" }, { "name": "author_id", "type": "string" }, { "name": "state", "type": "string" }, { "name": "date_created", "type": "date" }, { "name": "date_last_modified", "type": "date" }, { "name": "body", "type": "string" } ] } ], "runs": "", "is_public": false, "pre_built": false, "version": "4", "last_deployed": "2024-02-28T20:16:38.052Z", "attributes": {}, "returns": [ "GithubIssue" ], "description": "", "scopes": [], "input": {}, "endpoints": [ { "GET": "/github/issues" } ], "nango_yaml_version": "v2" } ], "postConnectionScripts": [], "provider": "github" } ] ``` You can also pass in an optional argument with the value of `nango` or `openai`. The default is `nango` ```ts const scriptsConfig = await nango.getScriptsConfig('openai'); ``` ```json { "data": [ { "name": "calendars", "description": "Sync the user's calendar list.\nIncludes all calendars the user has access to.", "parameters": { "type": "object", "properties": {}, "required": [] } }, { "name": "events", "description": "Sync calendar events from the primary calendar.\nIncludes events from the past month.", "parameters": { "type": "object", "properties": {}, "required": [] } }, { "name": "cancel-event", "description": "Cancel/delete an event by searching for it.\n\nInput parameters:\n- title: The title of the event to cancel\n- date: The date of the event in ISO format\n- time: The time of the event in 24-hour format (HH:mm)\n- calendar: Calendar ID where to search for the event\n- id: Calendar identifier\n- summary: Calendar name/title\n- description: Calendar description\n- location: Calendar location\n- timeZone: Time zone of the calendar\n- backgroundColor: Calendar color in UI\n- foregroundColor: Text color in UI\n- selected: Whether the calendar is selected in UI\n- accessRole: User's access role for the calendar\n- primary: Whether this is the primary calendar", "parameters": { "type": "object", "properties": { "id": { "type": "string", "description": "Calendar identifier" }, "summary": { "type": "string", "description": "Calendar name/title" }, "description": { "type": "string", "description": "Calendar description" }, "location": { "type": "string", "description": "Calendar location" }, "timeZone": { "type": "string", "description": "Time zone of the calendar" }, "backgroundColor": { "type": "string", "description": "Calendar color in UI" }, "foregroundColor": { "type": "string", "description": "Text color in UI" }, "selected": { "type": "boolean", "description": "Whether the calendar is selected in UI" }, "accessRole": { "type": "string", "description": "User's access role for the calendar" }, "primary": { "type": "boolean", "description": "Whether this is the primary calendar" }, "title": { "type": "string", "description": "The title of the event to cancel" }, "date": { "type": "string", "description": "The date of the event in ISO format" }, "time": { "type": "string", "description": "The time of the event in 24-hour format (HH:mm)" }, "calendar": { "type": "string", "description": "Calendar ID where to search for the event" } }, "required": [ "id", "summary" ] } }, { "name": "create-event", "description": "Create a new calendar event.\n\nInput parameters:\n- summary: The title/summary of the event\n- description: A detailed description of the event\n- location: The location of the event\n- start: The start time of the event in ISO format\n- end: The end time of the event in ISO format\n- attendees: List of email addresses to invite\n- recurrence: List of recurrence rules in RRULE format\n- calendar: Calendar ID to create the event in\n- id: Calendar identifier\n- timeZone: Time zone of the calendar\n- backgroundColor: Calendar color in UI\n- foregroundColor: Text color in UI\n- selected: Whether the calendar is selected in UI\n- accessRole: User's access role for the calendar\n- primary: Whether this is the primary calendar", "parameters": { "type": "object", "properties": { "id": { "type": "string", "description": "Calendar identifier" }, "summary": { "type": "string", "description": "The title/summary of the event" }, "description": { "type": "string", "description": "A detailed description of the event" }, "location": { "type": "string", "description": "The location of the event" }, "timeZone": { "type": "string", "description": "Time zone of the calendar" }, "backgroundColor": { "type": "string", "description": "Calendar color in UI" }, "foregroundColor": { "type": "string", "description": "Text color in UI" }, "selected": { "type": "boolean", "description": "Whether the calendar is selected in UI" }, "accessRole": { "type": "string", "description": "User's access role for the calendar" }, "primary": { "type": "boolean", "description": "Whether this is the primary calendar" }, "start": { "type": "string", "description": "The start time of the event in ISO format" }, "end": { "type": "string", "description": "The end time of the event in ISO format" }, "attendees": { "type": "array", "items": { "type": "string" }, "description": "List of email addresses to invite" }, "recurrence": { "type": "array", "items": { "type": "string" }, "description": "List of recurrence rules in RRULE format" }, "calendar": { "type": "string", "description": "Calendar ID to create the event in" } }, "required": [ "id", "summary", "start", "end" ] } }, { "name": "move-event", "description": "Move an event to a different time or calendar.\n\nInput parameters:\n- eventId: The ID of the event to move\n- title: The title of the event to move\n- sourceStart: The current start time of the event\n- start: The new start time for the event in ISO format\n- end: The new end time for the event in ISO format\n- calendar: Calendar ID to move the event to\n- id: Calendar identifier\n- summary: Calendar name/title\n- description: Calendar description\n- location: Calendar location\n- timeZone: Time zone of the calendar\n- backgroundColor: Calendar color in UI\n- foregroundColor: Text color in UI\n- selected: Whether the calendar is selected in UI\n- accessRole: User's access role for the calendar\n- primary: Whether this is the primary calendar", "parameters": { "type": "object", "properties": { "id": { "type": "string", "description": "Calendar identifier" }, "summary": { "type": "string", "description": "Calendar name/title" }, "description": { "type": "string", "description": "Calendar description" }, "location": { "type": "string", "description": "Calendar location" }, "timeZone": { "type": "string", "description": "Time zone of the calendar" }, "backgroundColor": { "type": "string", "description": "Calendar color in UI" }, "foregroundColor": { "type": "string", "description": "Text color in UI" }, "selected": { "type": "boolean", "description": "Whether the calendar is selected in UI" }, "accessRole": { "type": "string", "description": "User's access role for the calendar" }, "primary": { "type": "boolean", "description": "Whether this is the primary calendar" }, "eventId": { "type": "string", "description": "The ID of the event to move" }, "title": { "type": "string", "description": "The title of the event to move" }, "sourceStart": { "type": "string", "description": "The current start time of the event" }, "start": { "type": "string", "description": "The new start time for the event in ISO format" }, "end": { "type": "string", "description": "The new end time for the event in ISO format" }, "calendar": { "type": "string", "description": "Calendar ID to move the event to" } }, "required": [ "id", "summary", "start", "end" ] } }, { "name": "whoami", "description": "Get information about the authenticated user.\n\nNo input parameters.", "parameters": { "type": "object", "properties": {}, "required": [] } }, { "name": "invite-user-to-repository", "description": "Invite a user to a GitHub repository.\n\nInput parameters:\n- owner: The owner (user or organization) of the repository\n- repo: The name of the repository to invite the user to\n- username: The GitHub username of the user to invite\n- permission: The permission level to grant (\"pull\", \"push\", \"admin\", \"maintain\", \"triage\")", "parameters": { "type": "object", "properties": { "owner": { "type": "string", "description": "The owner (user or organization) of the repository" }, "repo": { "type": "string", "description": "The name of the repository to invite the user to" }, "username": { "type": "string", "description": "The GitHub username of the user to invite" }, "permission": { "type": "string", "description": "The permission level to grant (\"pull\", \"push\", \"admin\", \"maintain\", \"triage\")" } }, "required": [ "owner", "repo", "username" ] } } ] } ``` ## Syncs ### Get records Returns the synced data. ```ts import type { ModelName } from '/models' const records = await nango.listRecords({ providerConfigKey: '', connectionId: '', model: '' }); ``` **Parameters** The integration ID. The connection ID. The name of the model of the data you want to retrieve. The variant of the model to fetch. When omitted, the default base variant is used. Each record from this endpoint comes with a synchronization cursor in `_nango_metadata.cursor`. Save the last fetched record's cursor to track how far you've synced. By providing the cursor to this method, you'll continue syncing from where you left off, receiving only post-cursor changes. This same cursor is used to paginate through the results of this endpoint. The maximum number of records to return. Defaults to 100. Filter to only show results that have been added or updated or deleted. Available options: added, updated, deleted Timestamp, e.g. 2023-05-31T11:46:13.390Z. If passed, only records modified after this timestamp are returned, otherwise all records are returned. An array of string containing a list of your records IDs. The list will be filtered to include only the records with a matching ID. Deprecated. (use modifiedAfter) Timestamp, e.g. 2023-05-31T11:46:13.390Z. If passed, only records modified after this timestamp are returned, otherwise all records are returned. **Example Response** This endpoint returns a list of records, ordered by modification date ascending. If some records are updated while you paginate through this endpoint, you might see these records multiple times. ```json { records: [ { id: 123, ..., // Fields as specified in the model you queried _nango_metadata: { deleted_at: null, last_action: 'ADDED', first_seen_at: '2023-09-18T15:20:35.941305+00:00', last_modified_at: '2023-09-18T15:20:35.941305+00:00', cursor: 'MjAyNC0wMi0yNlQwMzowMDozOS42MjMzODgtMDU6MDB8fGVlMDYwM2E1LTEwNDktNDA4Zi05YTEwLTJjNzVmNDkwODNjYQ==' } }, ... ], next_cursor: "Y3JlYXRlZF9hdF4yMDIzLTExLTE3VDExOjQ3OjE0LjQ0NyswMjowMHxpZF4xYTE2MTYwMS0yMzk5LTQ4MzYtYWFiMi1mNjk1ZWI2YTZhYzI" } ``` ### Prune records Prunes record payloads from Nango's cache for a given model and connection up to a specified cursor. The payload is emptied but record metadata is retained. Payload can be restored by re-syncing the data. ```ts const result = await nango.pruneRecords({ providerConfigKey: '', connectionId: '', model: '', untilCursor: '' }); console.log(`Pruned ${result.count} records. More records available: ${result.has_more}`); ``` **Parameters** The integration ID. The connection ID. The name of the model from which to prune records. The variant of the model. When omitted, the default base variant is used. The cursor up to which records should be pruned. Only records with a cursor less than or equal to this value will be pruned. The maximum number of records to prune in this operation. **Response** ```json { "count": 150, "has_more": true } ``` The number of records that were pruned in this operation. Indicates whether there are more records available for pruning that match the criteria. If `true`, you must call the method again to continue pruning records. ### Trigger sync(s) Triggers an additional, one-off execution of specified sync(s) for a given connection or all applicable connections if no connection is specified. ```ts // Simple usage - trigger sync preserving checkpoint await nango.triggerSync('', ['SYNC_NAME1', 'SYNC_NAME2'], ''); // Reset checkpoint and start fresh await nango.triggerSync('', ['SYNC_NAME1'], '', { reset: true }); // Reset checkpoint and clear all cached records await nango.triggerSync('', ['SYNC_NAME1'], '', { reset: true, emptyCache: true }); // Using variants await nango.triggerSync('', [ { name: 'SYNC_NAME1', variant: 'VARIANT_A' }, 'SYNC_NAME2' // Uses default base variant ], '', { reset: true }); ``` **Parameters** The integration ID. The name of the syncs to trigger. If the array is empty, all syncs are triggered. Each sync can be specified as either: - A string: the sync name (uses the default base variant) - An object: with name and variant properties to target a specific sync variant The connection ID. If omitted, the sync will trigger for all relevant connections. Options for triggering the sync. See the [checkpoints guide](/guides/functions/syncs/checkpoints#re-syncing-resetting-checkpoints-and-the-cache) for details on `reset` and `emptyCache`. If `true`, clears the checkpoint before triggering the sync, causing it to start fresh. If `true`, deletes all cached records associated with the sync before triggering. Must be used with `reset: true`. **Response** Empty response. ### Start schedule for sync(s) Starts the schedule of specified sync(s) for a given connection or all applicable connections if no connection is specified. Upon starting the schedule, the sync will execute immediately and then continue to run at the specified frequency. If the schedule was already started, this will have no effect. ```ts // Simple usage with sync names await nango.startSync('', ['SYNC_NAME1', 'SYNC_NAME2'], ''); // Using variants await nango.startSync('', [ { name: 'SYNC_NAME1', variant: 'VARIANT_A' }, 'SYNC_NAME2' // Uses default base variant ], ''); ``` **Parameters** The integration ID. The name of the syncs that should be triggered. Each sync can be specified as either: - A string: the sync name (uses the default base variant) - An object: with name and variant properties to target a specific sync variant The connection ID. If omitted, the sync will trigger for all relevant connections. **Response** Empty response. ### Pause schedule for sync(s) Pauses the schedule of specified sync(s) for a given connection or all applicable connections if no connection is specified. ```ts // Simple usage with sync names await nango.pauseSync('', ['SYNC_NAME1', 'SYNC_NAME2'], ''); // Using variants await nango.pauseSync('', [ { name: 'SYNC_NAME1', variant: 'VARIANT_A' }, 'SYNC_NAME2' // Uses default base variant ], ''); ``` **Parameters** The integration ID. The name of the syncs that should be paused. Each sync can be specified as either: - A string: the sync name (uses the default base variant) - An object: with name and variant properties to target a specific sync variant The connection ID. If omitted, the sync will pause for all relevant connections. **Response** Empty response. ### Sync status Get the status of specified sync(s) for a given connection or all applicable connections if no connection is specified. ```ts // Simple usage with sync names await nango.syncStatus('', ['SYNC_NAME1', 'SYNC_NAME2'], ''); // Using variants await nango.syncStatus('', [ { name: 'SYNC_NAME1', variant: 'VARIANT_A' }, 'SYNC_NAME2' // Uses default base variant ], ''); // Get all syncs await nango.syncStatus('', '*', ''); ``` **Parameters** The integration ID. Either "*" to return all syncs, or an array of syncs to fetch status for. When using an array, each sync can be specified as either: - A string: the sync name (uses the default base variant) - An object: with name and variant properties to target a specific sync variant The connection ID. If omitted, all connections will be surfaced. **Response** ```json { "syncs": [ { "id": "", "connection_id": "", "name": "", "variant": "", "status": "RUNNING", "type": "INCREMENTAL", // DEPRECATED "finishedAt": "", "nextScheduledSyncAt": "", "frequency": "", "latestResult": { "": { "added": , "updated": , "deleted": , } }, "recordCount": { "": ... }, "checkpoint": { "": "" ... } } ] } ``` The `checkpoint` field contains the last saved checkpoint for the sync. Learn more about [checkpoints](/guides/functions/syncs/checkpoints). ### Override sync connection frequency Override a sync's default frequency for a specific connection, or revert to the default frequency. ```ts // For base variant await nango.updateSyncConnectionFrequency('', 'SYNC_NAME', '', ''); // For a specific variant await nango.updateSyncConnectionFrequency('', { name: 'SYNC_NAME', variant: 'VARIANT_NAME' }, '', ''); ``` **Parameters** The integration ID. The sync to update. Can be specified as either: - A string: the sync name (uses the default base variant) - An object: with name and variant properties to target a specific sync variant The connection ID. The frequency you want to set (ex: 'every hour'). Set to `null` to revert to the default frequency. Uses the https://github.com/vercel/ms notations. See [minimum sync frequency](/guides/platform/limits#minimum-sync-frequency) for details. **Response** ```json { "frequency": "" } ``` ### Create sync variant Creates a new sync variant for a specific connection. Sync variants allow you to run multiple instances of the same sync with different configurations. ```ts await nango.createSyncVariant({ provider_config_key: '', connection_id: '', name: 'SYNC_NAME', variant: 'VARIANT_NAME' }); ``` **Parameters** The integration ID. The connection ID. The name of the sync. The name of the variant to create. Cannot be "base" (protected name). **Example Response** ```json { "id": "12345", "name": "SYNC_NAME", "variant": "VARIANT_NAME" } ``` ### Delete sync variant Deletes a sync variant for a specific connection. ```ts await nango.deleteSyncVariant({ provider_config_key: '', connection_id: '', name: 'SYNC_NAME', variant: 'VARIANT_NAME' }); ``` **Parameters** The integration ID. The connection ID. The name of the sync. The name of the variant to delete. Cannot be "base" (protected name). **Example Response** ```json { "success": true } ``` ### Get environment variables Retrieve the environment variables as added in the Nango dashboard. ```js await nango.getEnvironmentVariables(); ``` **Parameters** No parameters. **Response** ```json [ { "name": "MY_SECRET_KEY", "value": "SK_373892NSHFNCOWFO..." } ] ``` ## Actions ### Trigger an action Triggers an action for a connection. ```js await nango.triggerAction('', '', '', { 'custom_key1': 'custom_value1' }); ``` **Parameters** The integration ID. The connection ID. The name of the action to trigger. The necessary input for your action's `exec` function. **Response** ```json { "your-properties": "The data returned by the action" } ``` The output of an action cannot exceed 10MB. ### Trigger an action asynchronously Triggers an action asynchronously for a connection. This allows you to start an action and retrieve the results later. ```js const { id, statusUrl } = await nango.triggerActionAsync('', '', '', { 'custom_key1': 'custom_value1' }); ``` **Parameters** The integration ID. The connection ID. The name of the action to trigger. The necessary input for your action's `exec` function. **Response** ```json { "id": "action_123456", "statusUrl": "/action/action_123456" } ``` ### Get async action result Retrieves the result of an asynchronous action. ```js // Using the id const result = await nango.getAsyncActionResult({ id: 'action_123456' }); // OR using the statusUrl const result = await nango.getAsyncActionResult({ statusUrl: '/action/action_123456' }); ``` **Parameters** The ID of the action to retrieve the result for. The URL where the result can be retrieved. **Response** ```json { "your-properties": "The data returned by the action" } ``` ## Proxy Makes an HTTP request using the [proxy](/guides/platform/proxy-requests): ```js const config = { endpoint: '/some-endpoint', providerConfigKey: '', connectionId: '' }; await nango.get(config); // GET request await nango.post(config); // POST request await nango.put(config); // PUT request await nango.patch(config); // PATCH request await nango.delete(config); // DELETE request ``` **Parameters** The endpoint of the request. The integration ID (for credential injection). The connection ID (for credential injection). The headers of the request. The query parameters of the request. The body of the request. The number of retries in case of failure (with exponential back-off). Optional, default 0. Array of additional status codes to retry a request in addition to the 5xx, 429, ECONNRESET, ETIMEDOUT, and ECONNABORTED The API base URL. Can be omitted if the base URL is configured for this API in the [providers.yaml](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml). Override the decompress option when making requests. Optional, defaults to false The type of the response. **Response** The response from the external API is passed back to you exactly as Nango gets it: - response code - response headers - response body ## Providers ### List all providers Returns a list of providers. ```js await nango.listProviders() ``` **Example Response** ```json { "data": [ { "name": "posthog", "categories": ["dev-tools"], "auth_mode": "API_KEY", "proxy": { "base_url": "https://api.posthog.com", }, "docs": "https://nango.dev/docs/api-integrations/posthog" } ] } ``` ### Get a provider Returns a specific provider. ```js await nango.getProvider({ provider: }) ``` **Example Response** ```json { "data": { "name": "posthog", "categories": ["dev-tools"], "auth_mode": "API_KEY", "proxy": { "base_url": "https://api.posthog.com", }, "docs": "https://nango.dev/docs/api-integrations/posthog" } } ``` ### Get provider templates Returns the function templates available for a provider. ```js await nango.getProviderTemplates({ provider: }) ``` **Parameters** The provider name. **Example Response** ```json { "data": [ { "type": "action", "name": "create-issue", "description": "Create a GitHub issue", "returns": ["GithubIssue"], "json_schema": null } ] } ``` ## Webhooks from Nango ### Verify Webhook Signature Asserts that a Webhook is coming from Nango's backend. Verification uses `webhookSigningKey` when the client is constructed with one, otherwise it falls back to `secretKey`. On environments created after 2026-04-20 (or any environment that later rotated its API key), the signing key differs from the API key, so construct the client with `webhookSigningKey` (from **Environment Settings > Webhooks > Signing key**). ```js const nango = new Nango({ apiKey: '', webhookSigningKey: '' }); async (req, res) => { const isValid = nango.verifyIncomingWebhookRequest(req.body, req.headers); } ``` **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Python Source: https://nango.dev/docs/reference/backend/backend-sdk/python.md Coming soon. Use the [REST API](/reference/backend/http-api/authentication) in the meantime. ## Java Source: https://nango.dev/docs/reference/backend/backend-sdk/java.md Coming soon. Use the [REST API](/reference/backend/http-api/authentication) in the meantime. ## Ruby Source: https://nango.dev/docs/reference/backend/backend-sdk/ruby.md Coming soon. Use the [REST API](/reference/backend/http-api/authentication) in the meantime. ## Go Source: https://nango.dev/docs/reference/backend/backend-sdk/go.md Coming soon. Use the [REST API](/reference/backend/http-api/authentication) in the meantime. ## Rust Source: https://nango.dev/docs/reference/backend/backend-sdk/rust.md Coming soon. Use the [REST API](/reference/backend/http-api/authentication) in the meantime. ## PHP Source: https://nango.dev/docs/reference/backend/backend-sdk/php.md Coming soon. Use the [REST API](/reference/backend/http-api/authentication) in the meantime. ## Functions CLI Source: https://nango.dev/docs/reference/functions/functions-cli.md Description: Full reference of the CLI available to implement, test & deploy Nango Functions. ### Install the Nango CLI Install the Nango CLI globally: ```bash npm install nango -g ``` In the folder where you want your integration folder (e.g. root of your project), run: ```bash nango init nango-integrations ``` This creates the `./nango-integrations` folder with some initial configuration and an example sync script. The `nango-integrations` directory looks like this: ``` nango-integrations/ ├── .env ├── index.ts └── demo-github-integration # this is the integration unique ID and must match an integration ID in the UI └── syncs/ └── github-issue-example.ts ``` ### CLI Authentication Add the following env vars. We recommend that you have a `.env` file in `./nango-integrations`: ```bash NANGO_SECRET_KEY_PROD='' NANGO_SECRET_KEY_DEV='' ``` Get your `prod` and `dev` secret keys from [Environment Settings > API Keys](https://app.nango.dev/environment-settings#api-keys) (toggle between the `prod` and `dev` environment in the left nav bar). For self-hosting, set the `NANGO_HOSTPORT` env variable to `http://localhost:3003` (for local development) or your instance's URL. ## All CLI commands & command details Check out all CLI commands by running: ```bash nango ``` Get details about a specific command by running: ```bash nango [command] --help ``` ## Interactive Mode The Nango CLI includes an interactive mode that prompts you for missing arguments. For example, if you run `nango create` without specifying the function type, integration, or name, the CLI will prompt you for them. This mode is enabled by default when you're in an interactive terminal session. ### Usage Examples **Interactive Usage:** If you run a command without all the required arguments, the CLI will prompt you for them. ```bash # Running "nango create" without arguments $ nango create ? What type of function do you want to create? ❯ sync action on-event ``` **Non-Interactive (Explicit) Usage:** You can provide all arguments upfront to bypass the interactive prompts. This is ideal for scripting. ```bash nango create --sync --integration my-api --name get-contacts ``` ### Disabling Interactive Mode You can disable interactive mode in two ways: 1. **Using a flag:** Pass the `--no-interactive` flag to any command. ```bash nango create --no-interactive ``` 2. **In a CI environment:** Interactive mode is automatically disabled when the `CI` environment variable is set. This is the standard way to detect CI/CD environments. ### Backwards Compatibility Interactive mode is fully backward compatible. If you provide all the required arguments for a command, the CLI will not prompt you for anything and will behave exactly as it did before. ## Flags & environment variables Global command flags: ```bash # Command flag to auto-confirm all prompts (useful for CI). # Note: Destructive changes (like removing a sync or renaming a model) requires confirmation, even when --auto-confirm is set. To bypass this restriction, the --allow-destructive flag can be passed to nango deploy. --auto-confirm # Command flag to disable interactive mode. --no-interactive # Command flag to skip automatic package.json updates and package installs. # Recommended in CI and monorepos where dependency updates are managed elsewhere. --no-dependency-update ``` Environment variables: ```bash # Recommendation: in a ".env" file in ./nango-integrations. # Authenticates the CLI (get the keys in the dashboard's Environment Settings > API Keys). NANGO_SECRET_KEY_DEV=xxxx-xxx-xxxx NANGO_SECRET_KEY_PROD=xxxx-xxx-xxxx # Nango's instance URL (OSS: change to http://localhost:3003 or your instance URL). NANGO_HOSTPORT=https://api.nango.dev # Default value # How to handle CLI upgrades ("prompt", "auto" or "ignore"). NANGO_CLI_UPGRADE_MODE=prompt # Default value # Whether to prompt before deployments. # Note: Destructive changes (like removing a sync or renaming a model) requires confirmation, even when NANGO_DEPLOY_AUTO_CONFIRM is set to true. To bypass this restriction, the --allow-destructive flag can be passed to nango deploy. NANGO_DEPLOY_AUTO_CONFIRM=false # Default value # Control automatic dependency updates. Set to "false" to skip installs (equivalent to --no-dependency-update). NANGO_CLI_DEPENDENCY_UPDATE=true # Default value ``` ## Dependency management For Zero YAML projects, the CLI can keep required dev dependencies (for example `nango` and related tooling) in sync and run package installation when needed. ### `--no-dependency-update` Use `--no-dependency-update` to disable automatic `package.json` updates and dependency installs: ```bash nango deploy dev --no-dependency-update ``` This is especially useful when: - your CI pipeline should not modify files - your monorepo manages dependencies at the workspace root - you want full control over when `install` runs In CI, dependency updates are automatically disabled to avoid hanging. Passing `--no-dependency-update` explicitly is still recommended to make intent clear and silence the warning. When dependency updates are disabled, Nango will not install dependencies for you. Ensure dependencies are already installed before running commands. ## Package manager support Nango supports all major JavaScript package managers. The CLI automatically detects and uses your package manager for installs. Detection works from the current directory upward (monorepo-aware), in this order: 1. `package.json` `packageManager` field (Corepack standard) 2. lock files (`pnpm-lock.yaml`, `yarn.lock`, `bun.lockb` / `bun.lock`) 3. fallback to `npm` Supported managers are `npm`, `pnpm`, `yarn`, and `bun`. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Functions SDK Source: https://nango.dev/docs/reference/functions/functions-sdk.md Description: Full reference of the SDK available in Nango Functions. ## Examples ```ts import { createSync } from 'nango'; const githubIssueDemoSchema = z.object({ ... }); type GithubIssueDemo = z.infer; export default createSync({ description: `Fetches the Github issues from all a user's repositories.`, version: '1.0.0', frequency: 'every hour', autoStart: true, metadata: z.void(), models: { GithubIssueDemo: githubIssueDemoSchema }, exec: async (nango) => { // Mark the start of deletion tracking await nango.trackDeletesStart('GithubIssueDemo'); // Fetch issues from GitHub. const res = await nango.get({ endpoint: '/repos/NangoHQ/interactive-demo/issues?labels=demo&sort=created&direction=asc' }); // Map issues to your preferred schema. const issues: GithubIssueDemo[] = res.data.map(({ id, title, html_url }: any) => { return { id, title, url: html_url }; }); // Persist issues to the Nango cache. await nango.batchSave(issues, 'GithubIssueDemo'); // Detect and mark deleted records await nango.trackDeletesEnd('GithubIssueDemo'); }, }); ``` ```ts import { createAction } from 'nango'; import { GithubCreateIssueInput, GithubCreateIssueResult } from '../../models.js'; export default createAction({ description: `Create an issue in GitHub`, version: '1.0.0', input: GithubCreateIssueInput, output: GithubCreateIssueResult, exec: async (nango, input) => { // Create a GitHub issue. const res = await nango.post({ endpoint: '/repos/NangoHQ/interactive-demo/issues', data: { title: `[demo] ${input.title}`, body: `The body of the issue.`, labels: ['automatic'] } }); // Send response. return { url: res.data.html_url, status: res.status }; }, }); ``` Read more about [integration functions](/guides/functions/functions-guide) to understand what role they play in Nango. ## Configuration ### `createSync` The description of the sync. The function that will be called when the sync is triggered. The frequency of the sync. ```ts frequency: 'every 1 minute', frequency: 'every hour', frequency: 'every 2 days' frequency: 'every 3 week' ``` The models that will be synced by this function. You need one endpoint per model. ```ts models: { GithubIssue: z.object({ id: z.string(), }), }, ``` Optional schema defining the shape of checkpoint data for this sync. Use checkpoints to save progress and avoid re-fetching all data on every run. See the [checkpoints guide](/guides/functions/syncs/checkpoints) for details. ```ts checkpoint: z.object({ cursor: z.string(), }), ``` If `true`, automatically runs the sync when a new connection is created. Otherwise, it needs to be triggered via the API or Nango UI. The connection's metadata of the action. ```ts metadata: z.object({ userId: z.string(), }); ``` The function that will be called when a webhook is received. The integration's scopes required by the action. This field is for documentation purposes only and currently not enforced by Nango. ```ts scopes: ['read:user', 'write:user'], ``` [DEPRECATED] Instead use `await nango.trackDeletesStart('modelName')` and `await nango.trackDeletesEnd('modelName')` inside the sync `exec` function. When `trackDeletes` is set to `true`, Nango automatically detects deleted records **during full syncs only** and marks them as deleted in each record’s metadata (soft delete). These records remain stored in the cache. When set to `false`, Nango does not mark missing records as deleted, even if they weren’t returned in the latest full sync—they simply remain in the cache unchanged. Defaults to `false`. The version of the sync. Use it to track changes to the sync inside Nango's UI. The webhook subscriptions of the sync. Specify the types of webhooks the method `onWebhook` will handle. If a webhook type is not on the list, it will not be handled. ```ts webhookSubscriptions: ['*'], ``` [DEPRECATED] Use the [`GET /records`](/reference/backend/http-api/sync/records-list) API endpoint to fetch records. ### `createAction` The description of the sync. The function that will be called when the action is triggered. The input required by the action when triggering it. ```ts input: z.object({ title: z.string(), }); ``` The output of the action. ```ts output: z.object({ issueId: z.string(), }); ``` The version of the sync. Use it to track changes to the sync inside Nango's UI. The connection's metadata of the action. ```ts metadata: z.object({ userId: z.string(), }); ``` The integration's scopes required by the action. This field is for documentation purposes only and currently not enforced by Nango. ```ts scopes: ['read:user', 'write:user'], ``` [DEPRECATED] Use the [`POST /action/trigger`](/reference/backend/http-api/action/trigger) API endpoint to trigger actions. ### `createOnEvent` The description of the sync. The event that will trigger this function. The function that will be called when the action is triggered. The version of the onEvent function. Use it to track changes to the onEvent function inside Nango's UI. The connection's metadata of the function. ```ts metadata: z.object({ userId: z.string(), }); ``` ## HTTP requests Makes an HTTP request inside an integration function: ```js import type { ProxyConfiguration } from 'nango'; const config: ProxyConfiguration = { endpoint: '/some-endpoint' }; await nango.get(config); // GET request await nango.post(config); // POST request await nango.put(config); // PUT request await nango.patch(config); // PATCH request await nango.delete(config); // DELETE request ``` Note that all HTTP requests benefit from automatic credential injection. Because functions are executed in the context of a specific integration & connection, Nango can automatically retrieve & refresh the relevant API credentials. **Parameters** The endpoint of the request. The headers of the request. The query parameters of the request. The body of the request. The number of retries in case of failure (with exponential back-off). Optional, default 0. Array of HTTP status codes to retry, in addition to those specified [below](#when-a-failed-request-is-retried). The API base URL. Can be omitted if the base URL is configured for this API in the [providers.yaml](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml). Override the decompress option when making requests. Optional, defaults to false The type of the response. **Response** ```json { data: {}, // the response provided by the server status: 200, // the HTTP status code headers: {}, // the HTTP headers config: {}, // the config provided for the request request: {} // the request that generated this response } ``` The response object is an [Axios response](https://axios-http.com/docs/res_schema). ## HTTP request retries Configure behavior with two optional fields on your [HTTP request](#http-requests) config: [`retries`](#param-retries) and [`retryOn`](#param-retry-on). ### When a failed request is retried Nango retries only when the failure looks **transient**. Exact rules depend on the integration: | Field | Condition for Retry | | --- | --- | | **Network Error** | If code is one of:
`ECONNRESET`, `ETIMEDOUT`, `ECONNABORTED`, `ECONNREFUSED`, `EHOSTUNREACH`, `EAI_AGAIN` | | **Transient HTTP Error** | If provider specific `proxy.retry.error_code` is configured in [providers.yaml](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml), only matching statuses are retried.

Otherwise, by default, status codes `401`, `429`, `5xx` are retried.

Additional status specified in [`retryOn`](#param-retry-on) are always retried in addition to the above statuses. | If nothing in the table applies, the error is **not** retried. Status `401` is retryable so Nango can recover after refreshing the connection with potentially updated credentials. If a `401` happens and the connection credentials **do not change** on the next fetch, Nango treats that as a definitive authentication failure and stops performing additional retries ### Backoff Between retry attempts Nango waits with **exponential backoff**: first wait **3000ms**, then each wait doubles, capped at **10 minutes**. ## Logging You can collect logs in integration functions. This is particularly useful when: - developing, to debug your integration functions - in production, to collect information about integration function executions & understand issues Collect logs in integration functions as follows: ```ts await nango.log("This is a log."); // default log level is 'info' await nango.log("This is an info log", { level: 'info' }); await nango.log("This is a warning", { level: 'warn' }); await nango.log("This is an error", { level: 'error' }); ``` ### Log Levels Nango supports the following log levels (from most to least verbose): `debug`, `info`, `warn`, `error`, `off`. Only logs with a level greater than or equal to the configured logger level will surfaced in the Nango UI Logs tab. For example, if the logger level is set to `warn`, only `warn` and `error` logs will be shown. **Default log levels:** - **All cloud environments**: `warn` (only warnings and errors are logged by default) - **CLI dry-run**: `debug` (all logs are shown in the console) ### Configuring Log Level You can override the default log level in two ways: 1. **Environment variable**: Set `NANGO_LOGGER_LEVEL` in your environment settings (values: `debug`, `info`, `warn`, `error`, `off`). It will apply to all your functions running in this environment and can be overridden in functions by using `nango.setLogger(...)` 2. **In your function code**: ```ts nango.setLogger({ level: 'debug' }); // All logs will show in your cloud environments. It will apply to all `nango.log()` following this statement. nango.setLogger({ level: 'off' }); // No logs will show ``` Logs can be viewed & searched in the Nango UI. We plan to make them exportable in the future as well. You can monitor your usage of custom logs in the [billing tab](https://app.nango.dev/prod/team/billing) of your Nango dashboard. ## Environment variables Integration functions sometimes need to access sensitive variables that should not be revealed directly in the code. For this, you can define environment variables in the Nango UI, in the _Environment Settings_ tab. Then you can retrieve these environment variables from integration functions with: ```js await nango.getEnvironmentVariables(); ``` **Parameters** No parameters. **Response** ```json [ { "name": "MY_SECRET_KEY", "value": "SK_373892NSHFNCOWFO..." } ] ``` ## Trigger action You can call action functions from other integration functions with: ```js await nango.triggerAction('', { 'custom_key1': 'custom_value1' }); ``` **Parameters** The name of the action to trigger. The necessary input for your action's `exec` function. **Response** ```json { "your-properties": "The data returned by the action" } ``` ## Paginate through API responses Nango provides a helper to automatically paginate endpoint responses. Similar to [HTTP requests](/reference/functions/functions-sdk#http-requests), the `nango.paginate()` method takes in a `ProxyConfiguration` parameter. Use the `paginate` field to of the `ProxyConfiguration` to specify how the endpoint's pagination work. Here's an example for a Jira endpoint: ```ts const config: ProxyConfiguration = { // https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-projects/#api-rest-api-3-project-search-get endpoint: `/ex/jira/${cloud.cloudId}/rest/api/3/project/search`, params: { properties: properties }, paginate: { type: 'offset', offset_name_in_request: 'startAt', response_path: 'values', limit_name_in_request: 'maxResults', limit: 50, on_page: async ({ nextPageParam, response }) => { await nango.log(`Next offset value = ${nextPageParam}`); await nango.log(`Fetched ${response.data.total} records`); }, }, headers: { 'X-Atlassian-Token': 'no-check' }, retries: 10 }; for await (const projects of nango.paginate(config)) { const projectsToSave = toProjects(projects, cloud.baseUrl); await nango.batchSave(projectsToSave, 'Project'); } ``` As shown in the example above, use a `for` loop to iterate through the paginated results. Nango has pre-configured the pagination settings for some popular APIs, so you don't have to specify them in functions. You can view the pre-configured pagination settings for all APIs in the [providers.yaml](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml) file. Please note that some APIs have diverging pagination strategies per endpoint, so you might still need to override pre-configured pagination settings at times. The pagination helper supports 3 types of pagination: `cursor`, `link` or `offset` with the following settings: _For all pagination types._ The name of the parameter containing the number of items per page, in the request. Inserted in the query parameters for `GET`/`DELETE`, in the body for `POST`/`PUT`/`PATCH`. _For all pagination types._ The maximum number of items per page. If omitted, no limit will be sent to the external endpoint, relying on the endpoint's default limit. _For all pagination types._ The path of the field containing the results, in the response. If omitted or empty string, it defaults to the root. Use `.` for nested fields, e.g. `"results.contacts"`. _For all pagination types._ The pagination strategy. _For cursor pagination only (required)._ The path of the field containing the cursor for the next page, in the response. Use `.` for nested fields, e.g. `"pagination.cursor"`. _For cursor pagination only (required)._ The name of the parameter containing the cursor for the next page, in the request. Inserted in the query parameters for `GET`/`DELETE`, in the body for `POST`/`PUT`/`PATCH`. _`For link pagination (required unless link_path_in_response_body is specified).`_ The header containing the link to the next page, in the response. _`For link pagination (required unless link_rel_in_response_header is specified).`_ The path of the field containing the link to the next page, in the response. Use `.` for nested fields, e.g. `"pagination.link"`. _For offset pagination only (required)._ The name of the parameter containing the offset for the next page, in the request. Inserted in the query parameters for `GET`/`DELETE`, in the body for `POST`/`PUT`/`PATCH`. _For offset pagination only (optional)._ The initial offset. Defaults to 0, but some APIs start at 1. _For offset pagination only (optional)._ The offset calculation method. `by-response-size` (default) means the offset is incremented by the number of results. `per-page` means the offset is incremented by one for each page. _For all pagination types (optional)._ A callback function that is called after each page is fetched. Useful for logging or tracking pagination progress. The callback receives the next page parameter and the full Axios response object, which includes the response data, status, headers, and request configuration. You can find details on the pagination [types](https://github.com/NangoHQ/nango/blob/master/packages/runner-sdk/models.d.ts) and [logic](https://github.com/NangoHQ/nango/blob/master/packages/runner-sdk/lib/paginate.service.ts) in the code. ## Get Integration Returns the current integration information ```js await nango.getIntegration(); ``` With credentials ```js await nango.getIntegration({ include: ['credentials'] }); ``` **Parameters** See `GET /integrations/{uniqueKey}` query parameters: [documentation](/reference/backend/http-api/integration/get) **Response** See `GET /integrations/{uniqueKey}` response: [documentation](/reference/backend/http-api/integration/get) ## Manage connection metadata ### Get connection metadata Returns the connection's metadata. ```js await nango.getMetadata(); ``` Better, you can specify the type of the metadata; ```ts interface CustomMetadata { anyKey: Record; } const myTypedMetadata = await nango.getMetadata(); ``` **Parameters** No parameters. **Example Response** ```json { "custom_key1": "custom_value1" } ``` ### Set connection metadata Set custom metadata for the connection (overrides existing metadata). ```js await nango.setMetadata({ 'CUSTOM_KEY1': 'CUSTOM_VALUE1' }); ``` **Parameters** The custom metadata to store in the connection. **Response** Empty response. ### Edit connection metadata Edit custom metadata for the connection. Only overrides & adds specified properties, not the entire metadata. ```js await nango.updateMetadata({ 'CUSTOM_KEY1': 'CUSTOM_VALUE1' }); ``` **Parameters** The custom metadata to store in the connection. **Response** Empty response. ## Get the connection credentials Returns a specific connection with credentials. ```js await nango.getConnection(); ``` The response content depends on the API authentication type (OAuth 2, OAuth 1, API key, Basic auth, etc.). When you fetch the connection with this API endpoint, Nango will check if the access token has expired. If it has, it will refresh it. We recommend not caching tokens for longer than 5 minutes to ensure they are fresh. **Parameters** An optional integration ID to override the one from the function context. An optional connection ID to override the one from the function context. Defaults to `false`. If `false`, the token will only be refreshed if it expires within 15 minutes. If `true`, a token refresh attempt will happen on each request. This is only useful for testing and should not be done at high traffic. Defaults to `false`. If `false`, the refresh token is not included in the response, otherwise it is. In production, it is not advised to return the refresh token, for security reasons, since only the access token is needed to sign requests. Defaults to `false`. If true, this will refresh the JWT token for GitHub App / Github App OAuth connections **Example Response** ```json { "id": 18393, "created_at": "2023-03-08T09:43:03.725Z", "updated_at": "2023-03-08T09:43:03.725Z", "provider_config_key": "github", "connection_id": "1", "credentials": { "type": "OAUTH2", "access_token": "gho_tsXLG73f....", "refresh_token": "gho_fjofu84u9....", "expires_at": "2024-03-08T09:43:03.725Z", "raw": { // Raw token response from the OAuth provider: Contents vary! "access_token": "gho_tsXLG73f....", "refresh_token": "gho_fjofu84u9....", "token_type": "bearer", "scope": "public_repo,user" } }, "connection_config": { "subdomain": "myshop", "realmId": "XXXXX", "instance_id": "YYYYYYY" }, "account_id": 0, "metadata": { "myProperty": "yes", "filter": "closed=true" } } ``` ## Sync-specific helper methods Sync functions persist data updates to the Nango cache, which your app later fetches. See the [sync functions section](/guides/functions/syncs/sync-functions). ### Checkpoints Checkpoints allow syncs to save their progress and resume from where they left off. This is useful for: - Resuming after failures without re-fetching all data - Tracking pagination state across sync runs - Storing custom cursor/offset values To use checkpoints, define a `checkpoint` schema in your sync configuration (see [Configuration](#createsync)). For a complete guide on implementing incremental syncs with checkpoints, see the [checkpoints guide](/guides/functions/syncs/checkpoints). If you are migrating from `nango.lastSyncDate`, see the [migration guide](/guides/platform/migrations/migrate-to-checkpoints). #### getCheckpoint() Retrieves the current checkpoint. Returns `null` on first run or after a reset. ```ts const checkpoint = await nango.getCheckpoint(); if (checkpoint) { // Resume from checkpoint startCursor = checkpoint.cursor; } ``` **Response** Returns the checkpoint object matching your schema, or `null` if no checkpoint exists. #### saveCheckpoint() Saves the current progress. Call this after processing each batch of data to enable resumption if the sync fails. ```ts await nango.saveCheckpoint({ cursor: nextCursor }); ``` **Parameters** The checkpoint data matching your defined schema. #### clearCheckpoint() Clears the checkpoint. Use at the end of a full sync so the next run starts from the first page. Checkpoints are also automatically cleared when triggering a sync with `reset: true`. ```ts await nango.clearCheckpoint(); ``` ### Save records Upserts records to the Nango cache (i.e. create new records, update existing ones). Each record needs to contain a unique `id` field used to dedupe records. ```js const githubIssues: GitHubIssue[] = ...; // Fetch issues from GitHub API. await nango.batchSave(githubIssues, 'GitHubIssue'); ``` **Parameters** The list of records to persist. The model type of the records to persist. ### Delete records Marks records as deleted in the Nango cache. Deleted records are still returned when you fetch them, but they are marked as deleted in the record's metadata (i.e. soft delete). This does not remove cached payloads. `nango.batchDelete()` is used to mark records as deleted in Nango because they were deleted on the external API. Nango may still keep the last-known payload so your customer can react to the deletion event. If you want to permanently remove data from Nango storage for cost or compliance reasons, use [record pruning instead](/reference/backend/http-api/sync/prune-records). To implement deletion detection in your syncs, [follow this guide](/guides/functions/syncs/deletion-detection). The only field that needs to be present in each record when calling `batchDelete` is the unique `id`; the other fields are ignored. ```js const githubIssuesToDelete: { id: string }[] = ...; // Fetch issues to delete from GitHub API. await nango.batchDelete(githubIssuesToDelete, 'GitHubIssue'); ``` **Parameters** The list of records to delete. The model type of the records to delete. ### Detect deletions automatically Automatically detects and marks records as deleted by comparing what existed before `trackDeletesStart` with what was saved between `trackDeletesStart` and `trackDeletesEnd`. This does not remove cached payloads. `nango.trackDeletesStart()`/`nango.trackDeletesEnd()` are used to mark records as deleted in Nango because they were not returned by the external API. Nango may still keep the last-known payload so your customer can react to the deletion event. If you want to permanently remove data from Nango storage for cost or compliance reasons, use [record pruning instead](/reference/backend/http-api/sync/prune-records). ```js await nango.trackDeletesStart('ModelName'); // ... fetch and save all records ... await nango.trackDeletesEnd('ModelName'); ``` Call `trackDeletesStart` at the beginning of your sync execution, before fetching any data. Call `trackDeletesEnd` after all records have been saved with `batchSave`. Nango will compare the records that existed before `trackDeletesStart` with those saved in the window and mark any missing records as deleted. **Parameters** (both functions) The model type to detect deletions for. **Important considerations:** - Only use within syncs that fetch the complete dataset between `trackDeletesStart` and `trackDeletesEnd` - If your sync fails or doesn't fetch the complete dataset, avoid calling `trackDeletesEnd` as it may cause false deletions - Records are soft deleted (marked with `_metadata.deleted = true`) and remain in the cache `deleteRecordsFromPreviousExecutions` is deprecated. Use `trackDeletesStart`/`trackDeletesEnd` instead. For more details on deletion detection strategies, see the [detecting deletes guide](/guides/functions/syncs/deletion-detection). ### Update records Updates records in the Nango cache by merging the given data into the existing record. The `id` field is required in each record and used to determine what existing record to merge into. `batchUpdate` is primarily useful in webhook sync functions, where you receive partial updates from a webhook and want to merge them into the existing records. The merge algorithm used is a deep merge. Nested objects are merged recursively, while arrays always use the new value for the array. Any fields not present in the update record are left unchanged. ```ts // Create partial GitHub Issue update records with only id and state. const githubIssues: Pick[] = ...; await nango.batchUpdate(githubIssues, 'GitHubIssue'); ``` **`Take special care when using batchUpdate with records containing arrays.`** The merge algorithm does not attempt to merge arrays, but rather always uses the value of the new array. ```ts // given a an existing record: // { id: '1', tags: [{id: 12, name: 'Dev'}, {id: 13, name: "QA"}] } const updates: Pick[] = [ { id: '1', tags: [{id: 14, name: 'UX'}] } ]; // after the update, the record will be: // { id: '1', tags: [{id: 14, name: "UX"}] } await nango.batchUpdate(updates, 'Issue'); ``` **Parameters** The list of partial records to persist. The model type of the records to persist. ### Get records Fetches records from the Nango cache by ID. Returns a Map where the keys are the requested IDs, and the values are the corresponding records. Any records that are not found will simply be absent from the map. Example usage: ```ts const records = await nango.getRecordsById(['1', '2', '3'], 'Issue'); if (records.has('1')) { const record = records.get('1'); await nango.log(record.title); } else { await nango.log('Record with id 1 not found.'); } ``` Fetching records by ID is useful when you need to update specific records with a more granular approach than [`nango.batchUpdate()`](/reference/functions/functions-sdk#update-records), which performs a deep merge. Note that `nango.batchUpdate()` is more performant than using `nango.getRecordsById()`, followed by `nango.batchSave()`. A common use case is when handling external webhooks, where only a partial update of a record is received from an API. ### Variant If you are using sync variants, you can access the current variant name via the `nango.variant` property. ```ts export default createSync({ exec: async (nango) => { await nango.log(`Running sync with variant: ${nango.variant}`); // Customize sync behavior based on variant const res = await nango.get({ endpoint: `/spreadsheet/${nango.variant}` }); // Rest of sync implementation... }, }); ``` ## Action-specific helper methods ### `ActionError` You can use `ActionError` in an action function to return a descriptive error to your app when needed: ```ts export default createAction({ exec: async (nango) => { // Something went wrong... throw new ActionError({ any_key: 'any_value' }); }, }); ``` In this case, the response to the trigger action call will be: ```json { "error_type": "action_script_failure", "payload": { "any_key": "any_value" } } ``` ## Relative imports in functions You can import relative files into your functions to allow for code abstraction and to maintain DRY (Don't Repeat Yourself) principles. This means you can reuse code across different functions by importing it. The imported file must live in the `nango-integrations` directory and can be imported in the following way: ```ts import { issueMapper } from '../mappers/issue-mappper'; export default createSync({ exec: async (nango) => { // Fetch issues from GitHub. const res = await nango.get({ endpoint: '/repos/NangoHQ/interactive-demo/issues?labels=demo&sort=created&direction=asc' }); // Persist issues to the Nango cache. await nango.batchSave(issueMapper(res.data), 'GithubIssueDemo'); }, }); ``` Note that you cannot import third-party modules at this time. Additionally, if there is a compilation error in an imported file, the entry point file will also fail to compile. ## Pre-included Dependencies Some libraries are pre-included for usage in functions: - [zod](https://github.com/colinhacks/zod) - [crypto / node:crypto](https://nodejs.org/api/crypto.html#crypto) - [url / node:url](https://nodejs.org/api/url.html#url) Please reach out in the [community](https://nango.dev/slack) if you would like to request additional ones. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Explore APIs & integrations Source: https://nango.dev/docs/integrations/overview.md Nango is the leading provider of **API access for AI agents & apps**. It is designed for developers building integrations for their users, with full flexibility. 800+ APIs & thousands of integrations. We deliver them in <48h. It's fast & easy. ## API configuration (`providers.yaml`) Source: https://nango.dev/docs/integrations/api-configuration.md API configurations are listed in the `providers.yaml` file, located in the [Nango GitHub repository](https://github.com/NangoHQ/nango/blob/master/packages/providers/providers.yaml). # Examples ```yaml hubspot: display_name: HubSpot categories: - marketing - support - crm auth_mode: OAUTH2 authorization_url: https://app.hubspot.com/oauth/authorize token_url: https://api.hubapi.com/oauth/v1/token connection_configuration: - portalId post_connection_script: hubspotPostConnection webhook_routing_script: hubspotWebhookRouting proxy: base_url: https://api.hubapi.com decompress: true paginate: type: cursor cursor_path_in_response: paging.next.after limit_name_in_request: limit cursor_name_in_request: after response_path: results docs: https://nango.dev/docs/api-integrations/hubspot ``` ```yaml salesforce: display_name: Salesforce categories: - crm auth_mode: OAUTH2 authorization_url: https://login.salesforce.com/services/oauth2/authorize token_url: https://login.salesforce.com/services/oauth2/token authorization_params: prompt: consent default_scopes: - offline_access token_response_metadata: - instance_url proxy: base_url: ${connectionConfig.instance_url} webhook_routing_script: salesforceWebhookRouting post_connection_script: salesforcePostConnection docs: https://nango.dev/docs/api-integrations/salesforce connection_config: instance_url: type: string title: Instance URL description: The instance URL of your Salesforce account format: uri pattern: '^https?://.*$' ``` ```yaml google: display_name: Google auth_mode: OAUTH2 authorization_url: https://accounts.google.com/o/oauth2/v2/auth token_url: https://oauth2.googleapis.com/token authorization_params: response_type: code access_type: offline prompt: consent proxy: base_url: https://www.googleapis.com paginate: type: cursor cursor_path_in_response: nextPageToken limit_name_in_request: maxSize cursor_name_in_request: pageToken response_path: items docs: https://nango.dev/docs/api-integrations/google ``` ```yaml notion: display_name: Notion categories: - knowledge-base - productivity auth_mode: OAUTH2 authorization_url: https://api.notion.com/v1/oauth/authorize token_url: https://api.notion.com/v1/oauth/token authorization_params: response_type: code owner: user authorization_method: header body_format: json proxy: retry: after: 'Retry-After' base_url: https://api.notion.com headers: 'Notion-Version': '2022-06-28' paginate: type: cursor cursor_path_in_response: next_cursor cursor_name_in_request: start_cursor limit_name_in_request: page_size response_path: results docs: https://nango.dev/docs/api-integrations/notion ``` ```yaml linear: display_name: Linear categories: - productivity - ticketing auth_mode: OAUTH2 authorization_url: https://linear.app/oauth/authorize token_url: https://api.linear.app/oauth/token scope_separator: ',' authorization_params: prompt: consent proxy: base_url: https://api.linear.app disable_pkce: true webhook_routing_script: linearWebhookRouting post_connection_script: linearPostConnection webhook_user_defined_secret: true docs: https://nango.dev/docs/api-integrations/linear ``` ```yaml slack: display_name: Slack categories: - productivity auth_mode: OAUTH2 authorization_url: https://slack.com/oauth/v2/authorize token_url: https://slack.com/api/oauth.v2.access token_response_metadata: - incoming_webhook.url - incoming_webhook.channel - incoming_webhook.channel_id - bot_user_id - team.id proxy: base_url: https://slack.com/api paginate: type: cursor cursor_path_in_response: response_metadata.next_cursor cursor_name_in_request: cursor limit_name_in_request: limit webhook_routing_script: slackWebhookRouting docs: https://nango.dev/docs/api-integrations/slack ``` # All configuration fields Looking for the JSON Schema, find it in our [GitHub](https://github.com/NangoHQ/nango/blob/master/scripts/validation/providers/schema.json) Allows to extend the configuration of another API. The display name of the provider, used in the UI. The authentication mode. Must be one of: "API_KEY", "APP", "APP_STORE", "BASIC", "NONE", "OAUTH1", "OAUTH2", "OAUTH2_CC", "CUSTOM", "TBA", "JWT", "BILL", "TWO_STEP", "SIGNATURE". Authentication configuration. The response type for authentication. The authorization method. Must be `header` Query parameters of the authorization request. Possible value: "offline" Possible values: "auto", "force" Possible value: "permanent" Possible value: "never" Whether to force verification A unique string to be included in the request Possible value: "consent" The mode of the response The type of the response The URL to get the OAuth 2 credentials from the external API. Should the `authorization_url` be encoded or not An object containing key-value pairs for replacements in the authorization URL. The format of the request body. e.g: `json` An array of strings representing the categories of the API. An array of strings representing the `connectionConfig` that are automatically populated by post-connection functions. Whether to decode the URL or not. The minimum list of scopes that are necessary to connect to the API. Disables the [PKCE](https://oauth.net/2/pkce/) extension to the Authorization Code flow. The URL to the developer API documentation. The URL to the end user documentation (e.g: how to connect). The name of the script to run after a connection is established. Proxy configuration for the API. The base URL for the API. Whether to decompress the response. Headers to be sent with each request. Pagination configuration. The name of the cursor parameter in the request. The path to the cursor value in the response. The name of the limit parameter in the request. The rel attribute of the link header for pagination. The path to the link in the response body. The name of the offset parameter in the request. The method to calculate the offset in the request. Must be one of: "per-page" or "by-response-size". Optional parameter that defaults to "by-response-size". The starting value for the offset. The path to the paginated data in the response. The type of pagination. Must be one of: "link", "cursor", "offset". Query parameters to be sent with each request. Retry configuration. The name of the rate-limit header, e.g. `after: 'X-Rate-Limit-Reset'` The name of the rate-limit header, e.g. `at: 'x-ratelimit-reset'` Configure a HTTP call to verify if credentials are valid, only used for API Key and Basic auth. The HTTP method for verification. Must be either "GET" or "POST". The endpoint for verification. Override the base URL for verification. Headers for the verification request. The metadata to capture from the callback request. The URL used for refreshing the access token. Defaults to the `token_url` Parameters used for refreshing the access token. The grant type for refreshing the token. Must be "refresh_token". An object containing request parameters to pass to an authorization request. The URL for making API an authorization request. The separator used between scopes in the authorization request. Defaults to space, but some APIs sur `,` or `+`. The signature method used for OAuth 1.0. Must be either "HMAC-SHA1" or "PLAINTEXT". A buffer time (in seconds) before the token expiration to trigger a refresh. Parameters used for obtaining the access token. The grant type for obtaining the token. Must be either "authorization_code" or "client_credentials". Additional request information for obtaining the token. The authentication method used for token requests. Must be either "basic" or "custom". The unit of time for token expiration. Must be "milliseconds". An array of strings representing metadata from the token response that will be saved in `connection.metadata`. The URL to get the OAuth 2 credentials from the external API. Should the `token_url` be encoded or not Wether the webhook secret is set by the user. Specifies a script to handle external [webhooks](/getting-started/use-cases/webhooks-from-external-apis). Specifies a fragment to be appended to the authorization URL, typically used to modify the redirection flow or pass additional information in the URL before the query parameters. The list of properties available in `${connectionConfig}` and their validation. The type of this value. Must be "string". The title of this value. The description of this value. An example of valid value to display in a UI. A regex pattern for the credential. The special format of this value (e.g: `hostname`, `uri`) The order in which this field should be displayed in a UI The documentation section relative to `docs_connect` The list of credentials and their validation, only filed if necessary (e.g: API_KEY, BASIC) The type of the credential. Must be "string". The title of the credential. The description of the credential. An example of the credential. A pattern for the credential. The format of the credential. The documentation section for the credential. **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## Contribute or request an API Source: https://nango.dev/docs/integrations/contribute-or-request-api.md Description: Add support for a new API to Nango ## Option 1: Request a new API (most popular) The easiest way to add a new API is to request it from the Nango team. If you have a shared Slack channel with Nango, send your request there. Otherwise, post in the `#request-a-new-api` channel of the [community](https://nango.dev/slack). We'll deliver them according to the SLAs on our [pricing page](https://www.nango.dev/pricing). ## Option 2: Contribute a new API yourself To contribute a new API, follow the steps below and check out past [PRs](https://github.com/NangoHQ/nango/pulls?q=is%3Apr+is%3Aclosed) starting with `feat(integrations)` for examples. ### Pre-requisites - You have access to a test account for the API - The API is publicly accessible - The API is of type HTTP or SOAP Nango supports all authorization types (OAuth, API key, basic), including custom ones. ### Add an API configuration Fork the [repo](https://github.com/NangoHQ/nango) and edit the API configurations file ([providers.yaml](https://nango.dev/providers-yaml)). See the [API configuration reference](/integrations/api-configuration) for the available fields. You can test the configuration of your new provider with this command: ```sh npx tsx scripts/validation/providers/validate.ts ``` ### Test the API To test your new provider, go to the `nango` repo root and run: ```bash docker compose up ``` You can modify the ports in the `docker-compose.yaml` file if there are conflicts with other local services on your host machine. When you are ready to test your API: **Create an integration** Open the [local Nango UI](http://localhost:3003) in your browser and add a new integration with your freshly added API. **Create a connection** Create a new connection for this API in the Nango UI, completing the authorization flow. **Verify the connection** If all goes well, you should see your new connection in the _Connections_ tab. Check the connection details and make sure that the credentials are valid. ### Document the API Add a `.mdx` file (e.g. `github.mdx`) for your API to the `docs/integrations/all` folder. Check out [other examples](/integrations/overview) to fill out the content of the documentation page. Reference the page in the `docs/docs.json` file in the `800+ APIs & Integrations` group in alphabetical order. ### Submit a pull request Verify your contribution against [examples of past contributions](https://github.com/NangoHQ/nango/pulls?q=is%3Apr+is%3Amerged+label%3Aapi+). Submit a pull request with the new provider to the Nango repo. Please thoroughly test the integration\! Thanks a lot for your contribution\!\! â¤ī¸ **Questions, problems, feedback?** Please reach out in the [Slack community](https://nango.dev/slack). ## API and Integration Catalog Use these slugs to construct provider-specific docs URLs only when needed. | Slug | Name | Auth mode | Docs | Connect | Setup | Categories | | - | - | - | - | - | - | - | | `1password-events` | 1Password (Events API) | API_KEY | [docs](https://nango.dev/docs/api-integrations/1password-events.md) | [connect](https://nango.dev/docs/api-integrations/1password-events/connect.md) | | iam | | `1password-scim` | 1Password (SCIM) | API_KEY | [docs](https://nango.dev/docs/integrations/all/1password-scim.md) | [connect](https://nango.dev/docs/integrations/all/1password-scim/connect.md) | | iam | | `1password-users` | 1Password (Users API) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/1password-users.md) | [connect](https://nango.dev/docs/api-integrations/1password-users/connect.md) | | iam | | `3cx` | 3CX | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/3cx.md) | [connect](https://nango.dev/docs/api-integrations/3cx/connect.md) | | communication | | `8x8` | 8x8 | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/8x8.md) | [connect](https://nango.dev/docs/api-integrations/8x8/connect.md) | | communication | | `a-leads` | A-Leads | API_KEY | [docs](https://nango.dev/docs/api-integrations/a-leads.md) | [connect](https://nango.dev/docs/api-integrations/a-leads/connect.md) | | marketing | | `absorb-lms` | Absorb LMS | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/absorb-lms.md) | [connect](https://nango.dev/docs/api-integrations/absorb-lms/connect.md) | [setup](https://nango.dev/docs/api-integrations/absorb-lms/how-to-register-your-own-absorb-lms-api-oauth-app.md) | other | | `accelo` | Accelo | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/accelo.md) | | [setup](https://nango.dev/docs/api-integrations/accelo/how-to-register-your-own-accelo-api-oauth-app.md) | invoicing, ticketing | | `active-campaign` | ActiveCampaign | API_KEY | [docs](https://nango.dev/docs/integrations/all/active-campaign.md) | [connect](https://nango.dev/docs/integrations/all/active-campaign/connect.md) | | marketing, communication | | `acuity-scheduling` | Acuity Scheduling | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/acuity-scheduling.md) | | | productivity | | `acumatica` | Acumatica | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/acumatica.md) | [connect](https://nango.dev/docs/api-integrations/acumatica/connect.md) | [setup](https://nango.dev/docs/api-integrations/acumatica/connect.md) | erp, accounting | | `addepar` | Addepar (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/addepar.md) | [connect](https://nango.dev/docs/integrations/all/addepar/connect.md) | | analytics | | `addepar-basic` | Addepar (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/addepar-basic.md) | [connect](https://nango.dev/docs/integrations/all/addepar-basic/connect.md) | | analytics | | `adobe` | Adobe | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/adobe.md) | | | design | | `adobe-commerce` | Adobe Commerce | API_KEY | [docs](https://nango.dev/docs/api-integrations/adobe-commerce.md) | [connect](https://nango.dev/docs/api-integrations/adobe-commerce/connect.md) | | e-commerce | | `adobe-umapi` | UMAPI (Adobe User Management API) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/adobe-umapi.md) | [connect](https://nango.dev/docs/integrations/all/adobe-umapi/connect.md) | | other | | `adobe-workfront` | Adobe Workfront | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/adobe-workfront.md) | [connect](https://nango.dev/docs/integrations/all/adobe-workfront/connect.md) | | productivity | | `adp` | ADP | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/adp.md) | [connect](https://nango.dev/docs/integrations/all/adp/connect.md) | | hr | | `adp-lyric` | ADP Lyric | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/adp-lyric.md) | [connect](https://nango.dev/docs/integrations/all/adp-lyric/connect.md) | | hr | | `adp-run` | RUN Powered by ADP | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/adp-run.md) | [connect](https://nango.dev/docs/integrations/all/adp-run/connect.md) | | hr | | `adp-workforce-now` | ADP Workforce Now | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/adp-workforce-now.md) | [connect](https://nango.dev/docs/integrations/all/adp-workforce-now/connect.md) | | hr | | `adp-workforce-now-next-gen` | ADP Workforce Now Next Generation | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/adp-workforce-now-next-gen.md) | [connect](https://nango.dev/docs/integrations/all/adp-workforce-now-next-gen/connect.md) | | hr | | `adyen` | Adyen | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/adyen.md) | | | payment | | `adyntel` | Adyntel | API_KEY | [docs](https://nango.dev/docs/api-integrations/adyntel.md) | [connect](https://nango.dev/docs/api-integrations/adyntel/connect.md) | | marketing | | `affinity` | Affinity (v1) | BASIC | [docs](https://nango.dev/docs/integrations/all/affinity.md) | [connect](https://nango.dev/docs/integrations/all/affinity/connect.md) | | crm | | `affinity-v2` | Affinity (v2) | API_KEY | [docs](https://nango.dev/docs/integrations/all/affinity-v2.md) | [connect](https://nango.dev/docs/integrations/all/affinity-v2/connect.md) | | crm | | `agiloft` | Agiloft | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/agiloft.md) | [connect](https://nango.dev/docs/api-integrations/agiloft/connect.md) | [setup](https://nango.dev/docs/api-integrations/agiloft/how-to-register-your-own-agiloft-api-oauth-app.md) | productivity | | `agiloft-cc` | Agiloft (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/agiloft-cc.md) | [connect](https://nango.dev/docs/api-integrations/agiloft-cc/connect.md) | | productivity | | `ahrefs` | Ahrefs | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/ahrefs.md) | | [setup](https://nango.dev/docs/api-integrations/ahrefs/how-to-register-your-own-ahrefs-api-oauth-app.md) | marketing, analytics | | `aimfox` | Aimfox | API_KEY | [docs](https://nango.dev/docs/integrations/all/aimfox.md) | [connect](https://nango.dev/docs/integrations/all/aimfox/connect.md) | | surveys | | `aimfox-oauth` | Aimfox (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/aimfox-oauth.md) | | | surveys | | `aircall` | Aircall (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/aircall.md) | | | support | | `aircall-basic` | Aircall (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/aircall-basic.md) | [connect](https://nango.dev/docs/integrations/all/aircall-basic/connect.md) | | support | | `airtable` | Airtable | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/airtable.md) | | [setup](https://nango.dev/docs/api-integrations/airtable/how-to-register-your-own-airtable-api-oauth-app.md) | popular, productivity | | `airtable-pat` | Airtable (Personal Access Token) | API_KEY | [docs](https://nango.dev/docs/integrations/all/airtable-pat.md) | [connect](https://nango.dev/docs/integrations/all/airtable-pat/connect.md) | | productivity | | `algolia` | Algolia | API_KEY | [docs](https://nango.dev/docs/integrations/all/algolia.md) | [connect](https://nango.dev/docs/integrations/all/algolia/connect.md) | | search | | `altrata` | Altrata | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/altrata.md) | [connect](https://nango.dev/docs/api-integrations/altrata/connect.md) | | analytics | | `amazon` | Amazon | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/amazon.md) | | [setup](https://nango.dev/docs/api-integrations/amazon/how-to-register-your-own-amazon-api-oauth-app.md) | dev-tools, e-commerce | | `amazon-selling-partner` | Amazon Selling Partner | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/amazon-selling-partner.md) | [connect](https://nango.dev/docs/api-integrations/amazon-selling-partner/connect.md) | [setup](https://nango.dev/docs/api-integrations/amazon-selling-partner/how-to-register-your-own-amazon-selling-partner-api-oauth-app.md) | dev-tools, e-commerce | | `amazon-selling-partner-beta` | Amazon Selling Partner (Beta) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/amazon-selling-partner-beta.md) | [connect](https://nango.dev/docs/api-integrations/amazon-selling-partner-beta/connect.md) | [setup](https://nango.dev/docs/api-integrations/amazon-selling-partner-beta/how-to-register-your-own-amazon-selling-partner-beta-api-oauth-app.md) | dev-tools, e-commerce | | `amplitude` | Amplitude (Event Streaming API) | BASIC | [docs](https://nango.dev/docs/api-integrations/amplitude.md) | [connect](https://nango.dev/docs/api-integrations/amplitude/connect.md) | | analytics | | `amplitude-mcp` | Amplitude (MCP US) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/amplitude-mcp.md) | | | analytics, mcp | | `amplitude-mcp-eu` | Amplitude (MCP EU) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/amplitude-mcp-eu.md) | | | analytics, mcp | | `anrok` | Anrok | API_KEY | [docs](https://nango.dev/docs/integrations/all/anrok.md) | [connect](https://nango.dev/docs/integrations/all/anrok/connect.md) | | legal | | `anthropic` | Anthropic | API_KEY | [docs](https://nango.dev/docs/integrations/all/anthropic.md) | | | productivity, dev-tools, popular | | `anthropic-admin` | Anthropic Administrator | API_KEY | [docs](https://nango.dev/docs/integrations/all/anthropic-admin.md) | | | productivity, dev-tools | | `anvil` | Anvil | BASIC | [docs](https://nango.dev/docs/api-integrations/anvil.md) | [connect](https://nango.dev/docs/api-integrations/anvil/connect.md) | | legal, productivity | | `apaleo` | Apaleo | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/apaleo.md) | | | erp | | `apify` | Apify | API_KEY | [docs](https://nango.dev/docs/integrations/all/apify.md) | [connect](https://nango.dev/docs/integrations/all/apify/connect.md) | | dev-tools, analytics, productivity | | `apollo` | Apollo (API Key) | API_KEY | [docs](https://nango.dev/docs/api-integrations/apollo.md) | [connect](https://nango.dev/docs/api-integrations/apollo/connect.md) | | marketing | | `apollo-oauth` | Apollo (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/apollo-oauth.md) | | | marketing | | `apple-app-store` | Apple App Store | APP_STORE | [docs](https://nango.dev/docs/integrations/all/apple-app-store.md) | | | e-commerce | | `apple-business-manager` | Apple Business Manager | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/apple-business-manager.md) | [connect](https://nango.dev/docs/api-integrations/apple-business-manager/connect.md) | | productivity | | `appstle-subscriptions` | Appstle Subscriptions | API_KEY | [docs](https://nango.dev/docs/integrations/all/appstle-subscriptions.md) | [connect](https://nango.dev/docs/integrations/all/appstle-subscriptions/connect.md) | | productivity | | `asana` | Asana | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/asana.md) | | [setup](https://nango.dev/docs/api-integrations/asana/how-to-register-your-own-asana-api-oauth-app.md) | productivity, ticketing | | `asana-mcp` | Asana (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/asana-mcp.md) | | [setup](https://nango.dev/docs/api-integrations/asana-mcp/how-to-register-your-own-asana-mcp-oauth-app.md) | productivity, ticketing, mcp | | `asana-scim` | Asana (SCIM API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/asana-scim.md) | [connect](https://nango.dev/docs/integrations/all/asana-scim/connect.md) | | productivity, ticketing | | `ashby` | Ashby | BASIC | [docs](https://nango.dev/docs/integrations/all/ashby.md) | [connect](https://nango.dev/docs/integrations/all/ashby/connect.md) | | ats, popular | | `aspire` | Aspire | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/aspire.md) | [connect](https://nango.dev/docs/api-integrations/aspire/connect.md) | | erp | | `atlas-so` | Atlas.so | API_KEY | [docs](https://nango.dev/docs/integrations/all/atlas-so.md) | | | support | | `atlassian` | Atlassian | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/atlassian.md) | | | dev-tools | | `atlassian-admin` | Atlassian Cloud Admin | API_KEY | [docs](https://nango.dev/docs/integrations/all/atlassian-admin.md) | [connect](https://nango.dev/docs/integrations/all/atlassian-admin/connect.md) | | dev-tools | | `atlassian-government-cloud` | Atlassian Government Cloud | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/atlassian-government-cloud.md) | [connect](https://nango.dev/docs/integrations/all/atlassian-government-cloud/connect.md) | | productivity, ticketing | | `atlassian-service-account-api-token` | Atlassian Service Account (API Token) | BASIC | [docs](https://nango.dev/docs/api-integrations/atlassian-service-account-api-token.md) | [connect](https://nango.dev/docs/api-integrations/atlassian-service-account-api-token/connect.md) | | dev-tools | | `atlassian-service-account-oauth2` | Atlassian Service Account (OAuth 2.0) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/atlassian-service-account-oauth2.md) | [connect](https://nango.dev/docs/api-integrations/atlassian-service-account-oauth2/connect.md) | | dev-tools | | `attention` | Attention | API_KEY | [docs](https://nango.dev/docs/api-integrations/attention.md) | [connect](https://nango.dev/docs/api-integrations/attention/connect.md) | | productivity | | `attio` | Attio | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/attio.md) | | [setup](https://nango.dev/docs/api-integrations/attio/how-to-register-your-own-attio-api-oauth-app.md) | crm, popular | | `attio-mcp` | Attio (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/attio-mcp.md) | | | crm, mcp | | `auth0` | Auth0 | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/auth0.md) | | | iam | | `auth0-cc` | Auth0 (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/auth0-cc.md) | [connect](https://nango.dev/docs/integrations/all/auth0-cc/connect.md) | | iam | | `autodesk` | Autodesk | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/autodesk.md) | | | design | | `autotask` | AutoTask | API_KEY | [docs](https://nango.dev/docs/integrations/all/autotask.md) | [connect](https://nango.dev/docs/integrations/all/autotask/connect.md) | | support, ticketing | | `auvik` | Auvik | BASIC | [docs](https://nango.dev/docs/integrations/all/auvik.md) | [connect](https://nango.dev/docs/integrations/all/auvik/connect.md) | | support | | `availity` | Availity | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/availity.md) | [connect](https://nango.dev/docs/api-integrations/availity/connect.md) | | other | | `avalara` | Avalara | BASIC | [docs](https://nango.dev/docs/integrations/all/avalara.md) | [connect](https://nango.dev/docs/integrations/all/avalara/connect.md) | | legal | | `avalara-sandbox` | Avalara (Sandbox) | BASIC | [docs](https://nango.dev/docs/integrations/all/avalara-sandbox.md) | [connect](https://nango.dev/docs/integrations/all/avalara-sandbox/connect.md) | | legal | | `avanan` | Avanan | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/avanan.md) | [connect](https://nango.dev/docs/api-integrations/avanan/connect.md) | | other | | `avoma` | Avoma | API_KEY | [docs](https://nango.dev/docs/integrations/all/avoma.md) | [connect](https://nango.dev/docs/integrations/all/avoma/connect.md) | | productivity | | `aws` | AWS | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/aws.md) | | | dev-tools, e-commerce | | `aws-iam` | AWS IAM | BASIC | [docs](https://nango.dev/docs/api-integrations/aws-iam.md) | [connect](https://nango.dev/docs/api-integrations/aws-iam/connect.md) | | dev-tools, iam | | `aws-inspector2` | AWS Inspector2 | BASIC | [docs](https://nango.dev/docs/api-integrations/aws-inspector2.md) | [connect](https://nango.dev/docs/api-integrations/aws-inspector2/connect.md) | | dev-tools | | `aws-multi-service` | AWS Multi-Service | BASIC | [docs](https://nango.dev/docs/api-integrations/aws-multi-service.md) | [connect](https://nango.dev/docs/api-integrations/aws-multi-service/connect.md) | | dev-tools | | `aws-scim` | AWS (SCIM) | API_KEY | [docs](https://nango.dev/docs/integrations/all/aws-scim.md) | [connect](https://nango.dev/docs/integrations/all/aws-scim/connect.md) | | dev-tools, iam | | `aws-sigv4` | AWS SigV4 Proxy | AWS_SIGV4 | [docs](https://nango.dev/docs/integrations/all/aws-sigv4.md) | [connect](https://nango.dev/docs/integrations/all/aws-sigv4/connect.md) | | storage | | `axiom` | Axiom | API_KEY | [docs](https://nango.dev/docs/api-integrations/axiom.md) | [connect](https://nango.dev/docs/api-integrations/axiom/connect.md) | | dev-tools, analytics | | `azure-blob-storage` | Azure Blob Storage | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/azure-blob-storage.md) | [connect](https://nango.dev/docs/integrations/all/azure-blob-storage/connect.md) | [setup](https://nango.dev/docs/api-integrations/microsoft/how-to-register-your-own-microsoft-api-oauth-app.md) | storage | | `azure-devops` | Azure DevOps | BASIC | [docs](https://nango.dev/docs/integrations/all/azure-devops.md) | [connect](https://nango.dev/docs/integrations/all/azure-devops/connect.md) | | dev-tools | | `bamboohr` | BambooHR (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/bamboohr.md) | [connect](https://nango.dev/docs/api-integrations/bamboohr/connect.md) | [setup](https://nango.dev/docs/api-integrations/bamboohr/how-to-register-your-own-bamboohr-oauth-app.md) | hr | | `bamboohr-basic` | BambooHR (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/bamboohr-basic.md) | [connect](https://nango.dev/docs/integrations/all/bamboohr-basic/connect.md) | | hr | | `basecamp` | Basecamp | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/basecamp.md) | [connect](https://nango.dev/docs/integrations/all/basecamp/connect.md) | | productivity | | `battlenet` | Battle.net | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/battlenet.md) | | | gaming | | `beehiiv` | Beehiiv | API_KEY | [docs](https://nango.dev/docs/integrations/all/beehiiv.md) | | | communication, marketing | | `bettercontact` | BetterContact | API_KEY | [docs](https://nango.dev/docs/api-integrations/bettercontact.md) | [connect](https://nango.dev/docs/api-integrations/bettercontact/connect.md) | | crm | | `bigchange` | BigChange | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/bigchange.md) | [connect](https://nango.dev/docs/api-integrations/bigchange/connect.md) | | productivity | | `bigcommerce` | BigCommerce | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/bigcommerce.md) | | | e-commerce | | `bill` | Bill (Connect API) | BILL | [docs](https://nango.dev/docs/integrations/all/bill.md) | | | payment | | `bill-sandbox` | Bill (Connect API Sandbox) | BILL | [docs](https://nango.dev/docs/integrations/all/bill-sandbox.md) | [connect](https://nango.dev/docs/integrations/all/bill-sandbox/connect.md) | | payment | | `bing-webmasters` | Bing Webmasters | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/bing-webmasters.md) | | [setup](https://nango.dev/docs/api-integrations/bing-webmasters/how-to-register-your-own-bing-webmasters-api-oauth-app.md) | dev-tools | | `bird` | Bird | API_KEY | [docs](https://nango.dev/docs/api-integrations/bird.md) | [connect](https://nango.dev/docs/api-integrations/bird/connect.md) | | communication | | `bitbucket` | Bitbucket | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/bitbucket.md) | | | dev-tools | | `bitdefender` | Bitdefender | BASIC | [docs](https://nango.dev/docs/integrations/all/bitdefender.md) | [connect](https://nango.dev/docs/integrations/all/bitdefender/connect.md) | | other | | `bitly` | Bitly | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/bitly.md) | | | marketing, social | | `blackbaud` | Blackbaud | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/blackbaud.md) | | | crm | | `blackbaud-basic` | Blackbaud (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/blackbaud-basic.md) | [connect](https://nango.dev/docs/integrations/all/blackbaud-basic/connect.md) | | crm | | `blandai` | BlandAI | API_KEY | [docs](https://nango.dev/docs/integrations/all/blandai.md) | | | support | | `bliro` | Bliro | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/bliro.md) | [connect](https://nango.dev/docs/api-integrations/bliro/connect.md) | | communication | | `boldsign` | BoldSign | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/boldsign.md) | | | legal | | `booking-com` | Booking.com | BASIC | [docs](https://nango.dev/docs/integrations/all/booking-com.md) | | | e-commerce | | `boondmanager` | BoondManager | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/boondmanager.md) | | [setup](https://nango.dev/docs/api-integrations/boondmanager/how-to-register-your-own-boondmanager-api-oauth-app.md) | erp | | `box` | Box | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/box.md) | | [setup](https://nango.dev/docs/api-integrations/box/how-to-register-your-own-box-api-oauth-app.md) | knowledge-base, storage | | `braintree` | Braintree | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/braintree.md) | | | payment | | `braintree-sandbox` | Braintree (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/braintree-sandbox.md) | | | payment | | `braze` | Braze | API_KEY | [docs](https://nango.dev/docs/integrations/all/braze.md) | [connect](https://nango.dev/docs/integrations/all/braze/connect.md) | | communication | | `breezy-hr` | Breezy HR | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/breezy-hr.md) | [connect](https://nango.dev/docs/integrations/all/breezy-hr/connect.md) | | hr, ats | | `brevo-api-key` | Brevo | API_KEY | [docs](https://nango.dev/docs/integrations/all/brevo-api-key.md) | | | marketing | | `brex` | Brex (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/brex.md) | | [setup](https://nango.dev/docs/api-integrations/brex/how-to-register-your-own-brex-oauth-app.md) | banking | | `brex-api-key` | Brex (API Key) | API_KEY | [docs](https://nango.dev/docs/api-integrations/brex-api-key.md) | [connect](https://nango.dev/docs/api-integrations/brex-api-key/connect.md) | | banking | | `brex-staging` | Brex (Staging OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/brex-staging.md) | | [setup](https://nango.dev/docs/api-integrations/brex-staging/how-to-register-your-own-brex-staging-oauth-app.md) | banking | | `brightcrowd` | BrightCrowd | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/brightcrowd.md) | [connect](https://nango.dev/docs/integrations/all/brightcrowd/connect.md) | | social | | `buffer` | Buffer | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/buffer.md) | | [setup](https://nango.dev/docs/api-integrations/buffer/how-to-register-your-own-buffer-oauth-app.md) | marketing, social | | `builder-io-private` | Builder.io (Private) | API_KEY | [docs](https://nango.dev/docs/api-integrations/builder-io-private.md) | [connect](https://nango.dev/docs/api-integrations/builder-io-private/connect.md) | | dev-tools, design, cms | | `builder-io-public` | Builder.io (Public) | API_KEY | [docs](https://nango.dev/docs/api-integrations/builder-io-public.md) | [connect](https://nango.dev/docs/api-integrations/builder-io-public/connect.md) | | dev-tools, design, cms | | `buildium` | Buildium | API_KEY | [docs](https://nango.dev/docs/integrations/all/buildium.md) | [connect](https://nango.dev/docs/integrations/all/buildium/connect.md) | | accounting, crm, payment | | `builtwith` | BuiltWith | API_KEY | [docs](https://nango.dev/docs/integrations/all/builtwith.md) | [connect](https://nango.dev/docs/integrations/all/builtwith/connect.md) | | dev-tools, analytics, crm, marketing, e-commerce | | `bullhorn` | Bullhorn | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/bullhorn.md) | [connect](https://nango.dev/docs/integrations/all/bullhorn/connect.md) | | hr, ats | | `cal-com-oauth` | Cal.com (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/cal-com-oauth.md) | | [setup](https://nango.dev/docs/api-integrations/cal-com-oauth/how-to-register-your-own-cal-com-oauth-app.md) | productivity | | `cal-com-v1` | Cal.com (v1) | API_KEY | [docs](https://nango.dev/docs/integrations/all/cal-com-v1.md) | [connect](https://nango.dev/docs/integrations/all/cal-com-v1/connect.md) | | productivity | | `cal-com-v2` | Cal.com (v2) | API_KEY | [docs](https://nango.dev/docs/api-integrations/cal-com-v2.md) | [connect](https://nango.dev/docs/api-integrations/cal-com-v2/connect.md) | | productivity | | `calendly` | Calendly | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/calendly.md) | | [setup](https://nango.dev/docs/api-integrations/calendly/how-to-register-your-own-calendly-api-oauth-app.md) | productivity | | `callrail` | Callrail | API_KEY | [docs](https://nango.dev/docs/integrations/all/callrail.md) | [connect](https://nango.dev/docs/integrations/all/callrail/connect.md) | | marketing | | `candis` | Candis | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/candis.md) | | [setup](https://nango.dev/docs/api-integrations/candis/how-to-register-your-own-candis-oauth-app.md) | accounting | | `canny` | Canny | API_KEY | [docs](https://nango.dev/docs/integrations/all/canny.md) | [connect](https://nango.dev/docs/integrations/all/canny/connect.md) | | support | | `canva` | Canva | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/canva.md) | | | design | | `canva-mcp` | Canva (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/canva-mcp.md) | | | design, mcp | | `canva-scim` | Canva (SCIM API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/canva-scim.md) | [connect](https://nango.dev/docs/integrations/all/canva-scim/connect.md) | | design, dev-tools | | `canvas-lms` | Canvas LMS | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/canvas-lms.md) | [connect](https://nango.dev/docs/api-integrations/canvas-lms/connect.md) | [setup](https://nango.dev/docs/api-integrations/canvas-lms/how-to-register-your-own-canvas-lms-oauth-app.md) | productivity | | `certn` | Certn | API_KEY | [docs](https://nango.dev/docs/integrations/all/certn.md) | [connect](https://nango.dev/docs/integrations/all/certn/connect.md) | | legal | | `certn-partner` | Certn Partner | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/certn-partner.md) | | | legal | | `chargebee` | Chargebee | BASIC | [docs](https://nango.dev/docs/integrations/all/chargebee.md) | [connect](https://nango.dev/docs/integrations/all/chargebee/connect.md) | | payment | | `chatarmin` | Chatarmin | API_KEY | [docs](https://nango.dev/docs/api-integrations/chatarmin.md) | [connect](https://nango.dev/docs/api-integrations/chatarmin/connect.md) | | communication | | `chattermill` | Chattermill | API_KEY | [docs](https://nango.dev/docs/integrations/all/chattermill.md) | | | support, analytics | | `checkhq` | Check | API_KEY | [docs](https://nango.dev/docs/integrations/all/checkhq.md) | [connect](https://nango.dev/docs/integrations/all/checkhq/connect.md) | | accounting | | `checkout-com` | Checkout.com | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/checkout-com.md) | [connect](https://nango.dev/docs/integrations/all/checkout-com/connect.md) | | payment | | `checkout-com-sandbox` | Checkout.com (Sandbox) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/checkout-com-sandbox.md) | [connect](https://nango.dev/docs/integrations/all/checkout-com-sandbox/connect.md) | | payment | | `checkr-partner` | Checkr Partner | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/checkr-partner.md) | | | legal | | `checkr-partner-staging` | Checkr Partner (Staging) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/checkr-partner-staging.md) | | | legal | | `chorus` | Chorus | API_KEY | [docs](https://nango.dev/docs/integrations/all/chorus.md) | [connect](https://nango.dev/docs/integrations/all/chorus/connect.md) | | analytics | | `cin7-core` | Cin7 Core | API_KEY | [docs](https://nango.dev/docs/api-integrations/cin7-core.md) | [connect](https://nango.dev/docs/api-integrations/cin7-core/connect.md) | | e-commerce | | `circle-so` | Circle.so | API_KEY | [docs](https://nango.dev/docs/integrations/all/circle-so.md) | | | communication | | `circleback-mcp` | Circleback (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/circleback-mcp.md) | | | productivity, mcp | | `cisco-duo-admin` | Cisco Duo Admin API | BASIC | [docs](https://nango.dev/docs/api-integrations/cisco-duo-admin.md) | [connect](https://nango.dev/docs/api-integrations/cisco-duo-admin/connect.md) | | iam | | `clari-copilot` | Clari Copilot | API_KEY | [docs](https://nango.dev/docs/integrations/all/clari-copilot.md) | | | marketing | | `clay` | Clay | API_KEY | [docs](https://nango.dev/docs/api-integrations/clay.md) | [connect](https://nango.dev/docs/api-integrations/clay/connect.md) | | crm, marketing | | `clerk` | Clerk | API_KEY | [docs](https://nango.dev/docs/integrations/all/clerk.md) | [connect](https://nango.dev/docs/integrations/all/clerk/connect.md) | | dev-tools, iam | | `cleverreach` | CleverReach | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/cleverreach.md) | | [setup](https://nango.dev/docs/api-integrations/cleverreach/how-to-register-your-own-cleverreach-api-oauth-app.md) | marketing | | `clickhouse` | ClickHouse | BASIC | [docs](https://nango.dev/docs/api-integrations/clickhouse.md) | [connect](https://nango.dev/docs/api-integrations/clickhouse/connect.md) | | dev-tools | | `clicksend` | ClickSend | BASIC | [docs](https://nango.dev/docs/integrations/all/clicksend.md) | [connect](https://nango.dev/docs/integrations/all/clicksend/connect.md) | | communication | | `clickup` | ClickUp | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/clickup.md) | | [setup](https://nango.dev/docs/api-integrations/clickup/how-to-register-your-own-clickup-api-oauth-app.md) | productivity, ticketing | | `clio` | Clio | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/clio.md) | [connect](https://nango.dev/docs/api-integrations/clio/connect.md) | [setup](https://nango.dev/docs/api-integrations/clio/how-to-register-your-own-clio-api-oauth-app.md) | legal | | `close` | Close | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/close.md) | | | crm | | `cloudbeds` | Cloudbeds | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/cloudbeds.md) | | [setup](https://nango.dev/docs/api-integrations/cloudbeds/how-to-register-your-own-cloudbeds-oauth-app.md) | other | | `cloudentity` | Cloudentity | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/cloudentity.md) | [connect](https://nango.dev/docs/integrations/all/cloudentity/connect.md) | | iam | | `cloudflare` | Cloudflare | API_KEY | [docs](https://nango.dev/docs/api-integrations/cloudflare.md) | [connect](https://nango.dev/docs/api-integrations/cloudflare/connect.md) | | dev-tools | | `cloudflare-mcp` | Cloudflare (MCP) | MCP_OAUTH2_GENERIC | [docs](https://nango.dev/docs/api-integrations/cloudflare-mcp.md) | [connect](https://nango.dev/docs/api-integrations/cloudflare-mcp/connect.md) | | dev-tools, mcp | | `cloudtalk` | CloudTalk | BASIC | [docs](https://nango.dev/docs/api-integrations/cloudtalk.md) | [connect](https://nango.dev/docs/api-integrations/cloudtalk/connect.md) | | communication | | `clover` | Clover | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/clover.md) | [connect](https://nango.dev/docs/api-integrations/clover/connect.md) | [setup](https://nango.dev/docs/api-integrations/clover/how-to-register-your-own-clover-api-oauth-app.md) | e-commerce | | `coda` | Coda | API_KEY | [docs](https://nango.dev/docs/integrations/all/coda.md) | [connect](https://nango.dev/docs/integrations/all/coda/connect.md) | | knowledge-base, productivity | | `codeclimate` | Code Climate | API_KEY | [docs](https://nango.dev/docs/integrations/all/codeclimate.md) | [connect](https://nango.dev/docs/integrations/all/codeclimate/connect.md) | | dev-tools, productivity | | `codegen` | Codegen | API_KEY | [docs](https://nango.dev/docs/integrations/all/codegen.md) | [connect](https://nango.dev/docs/integrations/all/codegen/connect.md) | | dev-tools | | `commercetools` | Commercetools | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/commercetools.md) | [connect](https://nango.dev/docs/integrations/all/commercetools/connect.md) | | e-commerce | | `companycam` | CompanyCam | API_KEY | [docs](https://nango.dev/docs/integrations/all/companycam.md) | | | productivity | | `conductorone` | ConductorOne | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/conductorone.md) | [connect](https://nango.dev/docs/api-integrations/conductorone/connect.md) | | productivity | | `confluence` | Confluence | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/confluence.md) | | [setup](https://nango.dev/docs/api-integrations/confluence/how-to-register-your-own-confluence-api-oauth-app.md) | knowledge-base, popular | | `confluence-basic` | Confluence (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/confluence-basic.md) | [connect](https://nango.dev/docs/integrations/all/confluence-basic/connect.md) | | knowledge-base | | `confluence-data-center` | Confluence Data Center | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/confluence-data-center.md) | [connect](https://nango.dev/docs/integrations/all/confluence-data-center/connect.md) | | productivity, ticketing | | `connectwise-psa` | ConnectWise PSA | BASIC | [docs](https://nango.dev/docs/api-integrations/connectwise-psa.md) | [connect](https://nango.dev/docs/api-integrations/connectwise-psa/connect.md) | | support, ticketing | | `connectwise-psa-staging` | ConnectWise PSA (Staging) | BASIC | [docs](https://nango.dev/docs/integrations/all/connectwise-psa-staging.md) | | | support, ticketing | | `connectwise-rmm` | ConnectWise RMM | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/connectwise-rmm.md) | [connect](https://nango.dev/docs/api-integrations/connectwise-rmm/connect.md) | | support | | `constant-contact` | Constant Contact | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/constant-contact.md) | | [setup](https://nango.dev/docs/api-integrations/constant-contact/how-to-register-your-own-constant-contact-oauth-app.md) | marketing | | `conta-azul` | Conta Azul | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/conta-azul.md) | | [setup](https://nango.dev/docs/api-integrations/conta-azul/how-to-register-your-own-conta-azul-api-oauth-app.md) | accounting, erp | | `contactout` | ContactOut | API_KEY | [docs](https://nango.dev/docs/integrations/all/contactout.md) | [connect](https://nango.dev/docs/integrations/all/contactout/connect.md) | | crm | | `contentful` | Contentful | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/contentful.md) | | | dev-tools, design, cms | | `contentstack` | Contentstack | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/contentstack.md) | | | cms | | `copper` | Copper (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/copper.md) | | | crm | | `copper-api-key` | Copper (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/copper-api-key.md) | [connect](https://nango.dev/docs/integrations/all/copper-api-key/connect.md) | | crm | | `coros` | Coros | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/coros.md) | | | sports | | `coros-sandbox` | Coros (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/coros-sandbox.md) | | | sports | | `coupa-compass` | Coupa Compass | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/coupa-compass.md) | | | payment, invoicing | | `crisp` | Crisp | BASIC | [docs](https://nango.dev/docs/integrations/all/crisp.md) | [connect](https://nango.dev/docs/integrations/all/crisp/connect.md) | | communication, support | | `crisp-plugin-install` | Crisp (Plugin Install) | INSTALL_PLUGIN | [docs](https://nango.dev/docs/api-integrations/crisp-plugin-install.md) | | [setup](https://nango.dev/docs/api-integrations/crisp-plugin-install/how-to-register-your-own-crisp-plugin.md) | communication, support | | `crowdstrike` | CrowdStrike | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/crowdstrike.md) | [connect](https://nango.dev/docs/integrations/all/crowdstrike/connect.md) | | dev-tools | | `crunchbase` | Crunchbase | API_KEY | [docs](https://nango.dev/docs/api-integrations/crunchbase.md) | [connect](https://nango.dev/docs/api-integrations/crunchbase/connect.md) | | crm | | `cursor` | Cursor | API_KEY | [docs](https://nango.dev/docs/integrations/all/cursor.md) | [connect](https://nango.dev/docs/integrations/all/cursor/connect.md) | | dev-tools | | `cursor-admin` | Cursor Admin | BASIC | [docs](https://nango.dev/docs/integrations/all/cursor-admin.md) | [connect](https://nango.dev/docs/integrations/all/cursor-admin/connect.md) | | dev-tools | | `customer-io` | Customer.io | BASIC | [docs](https://nango.dev/docs/api-integrations/customer-io.md) | [connect](https://nango.dev/docs/api-integrations/customer-io/connect.md) | | marketing, analytics | | `cyberimpact` | Cyberimpact | API_KEY | [docs](https://nango.dev/docs/integrations/all/cyberimpact.md) | [connect](https://nango.dev/docs/integrations/all/cyberimpact/connect.md) | | marketing | | `databricks-account` | Databricks (Account Level) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/databricks-account.md) | | | analytics | | `databricks-workspace` | Databricks (Workspace Level) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/databricks-workspace.md) | | | analytics | | `datacandy` | DataCandy | BASIC | [docs](https://nango.dev/docs/integrations/all/datacandy.md) | | | payment | | `datadog` | Datadog | API_KEY | [docs](https://nango.dev/docs/api-integrations/datadog.md) | [connect](https://nango.dev/docs/api-integrations/datadog/connect.md) | | analytics, dev-tools | | `datev` | Datev | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/datev.md) | | | legal | | `datto-rmm` | Datto RMM | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/datto-rmm.md) | [connect](https://nango.dev/docs/integrations/all/datto-rmm/connect.md) | | support | | `datto-rmm-password-grant` | Datto RMM (Password Grant) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/datto-rmm-password-grant.md) | [connect](https://nango.dev/docs/integrations/all/datto-rmm-password-grant/connect.md) | | support | | `dayforce` | Dayforce | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/dayforce.md) | [connect](https://nango.dev/docs/api-integrations/dayforce/connect.md) | | hr | | `deel` | Deel | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/deel.md) | | | hr | | `deel-sandbox` | Deel (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/deel-sandbox.md) | | | hr | | `demodesk` | DemoDesk | API_KEY | [docs](https://nango.dev/docs/api-integrations/demodesk.md) | [connect](https://nango.dev/docs/api-integrations/demodesk/connect.md) | | productivity | | `devin` | Devin | API_KEY | [docs](https://nango.dev/docs/integrations/all/devin.md) | [connect](https://nango.dev/docs/integrations/all/devin/connect.md) | | dev-tools | | `dialpad` | Dialpad | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/dialpad.md) | | | communication | | `dialpad-sandbox` | Dialpad (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/dialpad-sandbox.md) | | | communication | | `dialpad-wfm` | Dialpad WFM | API_KEY | [docs](https://nango.dev/docs/api-integrations/dialpad-wfm.md) | [connect](https://nango.dev/docs/api-integrations/dialpad-wfm/connect.md) | | hr | | `diffbot` | Diffbot | API_KEY | [docs](https://nango.dev/docs/api-integrations/diffbot.md) | [connect](https://nango.dev/docs/api-integrations/diffbot/connect.md) | | analytics | | `digitalocean` | DigitalOcean | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/digitalocean.md) | | | dev-tools | | `digits` | Digits | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/digits.md) | | [setup](https://nango.dev/docs/api-integrations/digits/how-to-register-your-own-digits-api-oauth-app.md) | accounting | | `digits-mcp` | Digits (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/digits-mcp.md) | | | accounting, mcp | | `discolike` | DiscoLike | API_KEY | [docs](https://nango.dev/docs/api-integrations/discolike.md) | [connect](https://nango.dev/docs/api-integrations/discolike/connect.md) | | marketing | | `discord` | Discord | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/discord.md) | | [setup](https://nango.dev/docs/api-integrations/discord/how-to-register-your-own-discord-api-oauth-app.md) | gaming, social | | `discourse` | Discourse | API_KEY | [docs](https://nango.dev/docs/integrations/all/discourse.md) | | | communication | | `dixa` | Dixa | API_KEY | [docs](https://nango.dev/docs/integrations/all/dixa.md) | | | support | | `document360` | Document360 | API_KEY | [docs](https://nango.dev/docs/integrations/all/document360.md) | | | knowledge-base | | `docusign` | DocuSign | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/docusign.md) | | | legal | | `docusign-sandbox` | DocuSign (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/docusign-sandbox.md) | | | legal | | `docuware` | DocuWare | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/docuware.md) | [connect](https://nango.dev/docs/integrations/all/docuware/connect.md) | | productivity | | `domo` | Domo | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/domo.md) | [connect](https://nango.dev/docs/api-integrations/domo/connect.md) | | productivity, other | | `drata` | Drata | API_KEY | [docs](https://nango.dev/docs/api-integrations/drata.md) | [connect](https://nango.dev/docs/api-integrations/drata/connect.md) | | dev-tools | | `drchrono` | DrChrono | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/drchrono.md) | | | other | | `dropbox` | Dropbox | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/dropbox.md) | | [setup](https://nango.dev/docs/api-integrations/dropbox/how-to-register-your-own-dropbox-api-oauth-app.md) | knowledge-base, storage | | `dropbox-sign` | Dropbox Sign | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/dropbox-sign.md) | | | legal | | `drupal` | Drupal | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/drupal.md) | [connect](https://nango.dev/docs/integrations/all/drupal/connect.md) | | dev-tools, design, cms | | `dualentry-mcp` | Dualentry (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/dualentry-mcp.md) | | | mcp | | `dynatrace` | Dynatrace | API_KEY | [docs](https://nango.dev/docs/api-integrations/dynatrace.md) | [connect](https://nango.dev/docs/api-integrations/dynatrace/connect.md) | | analytics, dev-tools | | `dynatrace-oauth` | Dynatrace (OAuth) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/dynatrace-oauth.md) | [connect](https://nango.dev/docs/api-integrations/dynatrace-oauth/connect.md) | | analytics, dev-tools | | `e-conomic` | e-conomic | BASIC | [docs](https://nango.dev/docs/integrations/all/e-conomic.md) | | | accounting | | `easypost` | EasyPost | BASIC | [docs](https://nango.dev/docs/api-integrations/easypost.md) | [connect](https://nango.dev/docs/api-integrations/easypost/connect.md) | | e-commerce | | `ebay` | eBay | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ebay.md) | | | e-commerce | | `ebay-sandbox` | eBay (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ebay-sandbox.md) | | | e-commerce | | `ecu360` | ECU360 (Test) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/ecu360.md) | [connect](https://nango.dev/docs/api-integrations/ecu360/connect.md) | | other | | `ecu360-production` | ECU360 (Production) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/ecu360-production.md) | [connect](https://nango.dev/docs/api-integrations/ecu360-production/connect.md) | | other | | `egnyte` | Egnyte | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/egnyte.md) | | | storage | | `elevenlabs` | Eleven Labs | API_KEY | [docs](https://nango.dev/docs/integrations/all/elevenlabs.md) | | | dev-tools | | `elevio` | Elevio | API_KEY | [docs](https://nango.dev/docs/integrations/all/elevio.md) | | | knowledge-base, support | | `emarsys` | Emarsys Core API (WSSE) | SIGNATURE | [docs](https://nango.dev/docs/integrations/all/emarsys.md) | | | marketing | | `emarsys-oauth` | Emarsys (OAuth) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/emarsys-oauth.md) | [connect](https://nango.dev/docs/integrations/all/emarsys-oauth/connect.md) | | marketing | | `employment-hero` | Employment Hero | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/employment-hero.md) | | | hr | | `entrata` | Entrata | BASIC | [docs](https://nango.dev/docs/integrations/all/entrata.md) | | | other | | `envoy` | Envoy | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/envoy.md) | | | productivity | | `epc-gov-uk` | Energy Performance Certificates (Gov.UK) | API_KEY | [docs](https://nango.dev/docs/api-integrations/epc-gov-uk.md) | [connect](https://nango.dev/docs/api-integrations/epc-gov-uk/connect.md) | | other | | `epic-games` | Epic Games | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/epic-games.md) | | | gaming | | `etsy` | Etsy | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/etsy.md) | | [setup](https://nango.dev/docs/api-integrations/etsy/how-to-register-your-own-etsy-api-oauth-app.md) | e-commerce | | `evaluagent` | EvaluAgent | BASIC | [docs](https://nango.dev/docs/integrations/all/evaluagent.md) | | | support | | `eventbrite` | Eventbrite | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/eventbrite.md) | | | marketing | | `everflow` | Everflow | API_KEY | [docs](https://nango.dev/docs/api-integrations/everflow.md) | [connect](https://nango.dev/docs/api-integrations/everflow/connect.md) | | marketing, analytics | | `exa` | Exa | API_KEY | [docs](https://nango.dev/docs/integrations/all/exa.md) | | | analytics | | `exact-online` | Exact Online | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/exact-online.md) | | | accounting, erp | | `exist` | Exist | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/exist.md) | | | other | | `expensify` | Expensify | BASIC | [docs](https://nango.dev/docs/integrations/all/expensify.md) | [connect](https://nango.dev/docs/integrations/all/expensify/connect.md) | | productivity | | `facebook` | Facebook | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/facebook.md) | | [setup](https://nango.dev/docs/api-integrations/facebook/how-to-register-your-own-facebook-api-oauth-app.md) | marketing, social | | `factorial` | Factorial | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/factorial.md) | | | hr | | `fairing` | Fairing | API_KEY | [docs](https://nango.dev/docs/integrations/all/fairing.md) | [connect](https://nango.dev/docs/integrations/all/fairing/connect.md) | | marketing, analytics | | `falai` | fal.ai | API_KEY | [docs](https://nango.dev/docs/integrations/all/falai.md) | | | productivity, dev-tools | | `fanvue` | Fanvue | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/fanvue.md) | | [setup](https://nango.dev/docs/api-integrations/fanvue/how-to-register-your-own-fanvue-oauth-app.md) | other | | `fathom` | Fathom | API_KEY | [docs](https://nango.dev/docs/integrations/all/fathom.md) | [connect](https://nango.dev/docs/integrations/all/fathom/connect.md) | | communication, video | | `fathom-oauth` | Fathom (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/fathom-oauth.md) | | [setup](https://nango.dev/docs/api-integrations/fathom-oauth/how-to-register-your-own-fathom-oauth-app.md) | communication, video | | `fellow` | Fellow | API_KEY | [docs](https://nango.dev/docs/api-integrations/fellow.md) | [connect](https://nango.dev/docs/api-integrations/fellow/connect.md) | | productivity | | `fern` | Fern (Public API) | API_KEY | [docs](https://nango.dev/docs/api-integrations/fern.md) | [connect](https://nango.dev/docs/api-integrations/fern/connect.md) | | dev-tools | | `fiber-ai` | Fiber AI | API_KEY | [docs](https://nango.dev/docs/api-integrations/fiber-ai.md) | [connect](https://nango.dev/docs/api-integrations/fiber-ai/connect.md) | | analytics | | `figjam` | FigJam | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/figjam.md) | | | design, productivity | | `figma` | Figma | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/figma.md) | | | design, productivity | | `figma-scim` | Figma (SCIM) | API_KEY | [docs](https://nango.dev/docs/integrations/all/figma-scim.md) | [connect](https://nango.dev/docs/integrations/all/figma-scim/connect.md) | | design, productivity | | `fillout` | Fillout (Oauth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/fillout.md) | | | surveys | | `fillout-api-key` | Fillout (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/fillout-api-key.md) | [connect](https://nango.dev/docs/integrations/all/fillout-api-key/connect.md) | | surveys | | `findymail` | FindyMail | API_KEY | [docs](https://nango.dev/docs/integrations/all/findymail.md) | [connect](https://nango.dev/docs/integrations/all/findymail/connect.md) | | marketing, crm | | `firefish` | Firefish | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/firefish.md) | | | crm | | `fireflies` | Fireflies | API_KEY | [docs](https://nango.dev/docs/integrations/all/fireflies.md) | | | analytics, communication, productivity | | `firstbase` | Firstbase | API_KEY | [docs](https://nango.dev/docs/api-integrations/firstbase.md) | [connect](https://nango.dev/docs/api-integrations/firstbase/connect.md) | | hr | | `fiserv` | Fiserv (OAuth) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/fiserv.md) | | | banking, payment | | `fiserv-api-key` | Fiserv (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/fiserv-api-key.md) | | | banking, payment | | `fitbit` | Fitbit | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/fitbit.md) | | | sports | | `float` | Float | API_KEY | [docs](https://nango.dev/docs/integrations/all/float.md) | [connect](https://nango.dev/docs/integrations/all/float/connect.md) | | productivity | | `folk` | Folk | API_KEY | [docs](https://nango.dev/docs/api-integrations/folk.md) | [connect](https://nango.dev/docs/api-integrations/folk/connect.md) | | crm | | `followupboss` | Follow Up Boss | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/followupboss.md) | | [setup](https://nango.dev/docs/api-integrations/followupboss/how-to-register-your-own-followupboss-oauth-app.md) | crm | | `fortnox` | Fortnox | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/fortnox.md) | | | accounting, invoicing | | `freeagent` | FreeAgent | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/freeagent.md) | | [setup](https://nango.dev/docs/api-integrations/freeagent/how-to-register-your-own-freeagent-oauth-app.md) | accounting, invoicing | | `freeagent-sandbox` | FreeAgent (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/freeagent-sandbox.md) | | [setup](https://nango.dev/docs/api-integrations/freeagent-sandbox/how-to-register-your-own-freeagent-oauth-app.md) | accounting, invoicing | | `freepik` | Freepik | API_KEY | [docs](https://nango.dev/docs/api-integrations/freepik.md) | [connect](https://nango.dev/docs/api-integrations/freepik/connect.md) | | other | | `freshbooks` | FreshBooks | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/freshbooks.md) | | | accounting | | `freshdesk` | FreshDesk | BASIC | [docs](https://nango.dev/docs/api-integrations/freshdesk.md) | [connect](https://nango.dev/docs/api-integrations/freshdesk/connect.md) | | support | | `freshsales` | Freshsales | API_KEY | [docs](https://nango.dev/docs/api-integrations/freshsales.md) | [connect](https://nango.dev/docs/api-integrations/freshsales/connect.md) | | crm | | `freshservice` | Freshservice | BASIC | [docs](https://nango.dev/docs/integrations/all/freshservice.md) | [connect](https://nango.dev/docs/integrations/all/freshservice/connect.md) | | support | | `freshteam` | Freshteam | API_KEY | [docs](https://nango.dev/docs/integrations/all/freshteam.md) | [connect](https://nango.dev/docs/integrations/all/freshteam/connect.md) | | hr, ats | | `freshworks` | Freshworks | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/freshworks.md) | [connect](https://nango.dev/docs/api-integrations/freshworks/connect.md) | [setup](https://nango.dev/docs/api-integrations/freshworks/how-to-register-your-own-freshworks-api-oauth-app.md) | crm, support | | `front` | Front | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/front.md) | | | support, ticketing | | `front-api-key` | Front (API Key) | API_KEY | [docs](https://nango.dev/docs/api-integrations/front-api-key.md) | [connect](https://nango.dev/docs/api-integrations/front-api-key/connect.md) | | support, ticketing | | `fullenrich` | FullEnrich | API_KEY | [docs](https://nango.dev/docs/api-integrations/fullenrich.md) | [connect](https://nango.dev/docs/api-integrations/fullenrich/connect.md) | | crm | | `gainsight-cc` | Gainsight CC | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/gainsight-cc.md) | [connect](https://nango.dev/docs/integrations/all/gainsight-cc/connect.md) | | support, crm | | `gamma` | Gamma | API_KEY | [docs](https://nango.dev/docs/api-integrations/gamma.md) | [connect](https://nango.dev/docs/api-integrations/gamma/connect.md) | | productivity | | `garmin` | Garmin | OAUTH1 | [docs](https://nango.dev/docs/integrations/all/garmin.md) | | | sports | | `gebruder-weiss` | GebrÃŧder Weiss | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/gebruder-weiss.md) | | | erp | | `gem` | Gem | API_KEY | [docs](https://nango.dev/docs/integrations/all/gem.md) | [connect](https://nango.dev/docs/integrations/all/gem/connect.md) | | ats | | `gerrit` | Gerrit | BASIC | [docs](https://nango.dev/docs/integrations/all/gerrit.md) | [connect](https://nango.dev/docs/integrations/all/gerrit/connect.md) | | dev-tools | | `getty-images` | Getty Images | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/getty-images.md) | | [setup](https://nango.dev/docs/api-integrations/getty-images/how-to-register-your-own-getty-images-oauth-app.md) | design | | `ghost-admin` | Ghost (Admin API) | JWT | [docs](https://nango.dev/docs/integrations/all/ghost-admin.md) | | | dev-tools, design, cms | | `ghost-content` | Ghost (Content API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/ghost-content.md) | | | dev-tools, design, cms | | `github` | GitHub (User OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/github.md) | | [setup](https://nango.dev/docs/api-integrations/github/how-to-register-your-own-github-api-oauth-app.md) | dev-tools, support, ticketing | | `github-app` | GitHub (App) | APP | [docs](https://nango.dev/docs/api-integrations/github-app.md) | | [setup](https://nango.dev/docs/api-integrations/github-app/how-to-register-your-own-github-app-api-app.md) | dev-tools, popular, ticketing | | `github-app-oauth` | GitHub (App OAuth) | CUSTOM | [docs](https://nango.dev/docs/api-integrations/github-app-oauth.md) | | [setup](https://nango.dev/docs/api-integrations/github-app-oauth/how-to-register-your-own-github-app-api-oauth-app.md) | dev-tools, ticketing | | `github-pat` | Github (Personal Access Token) | API_KEY | [docs](https://nango.dev/docs/integrations/all/github-pat.md) | [connect](https://nango.dev/docs/integrations/all/github-pat/connect.md) | | dev-tools, ticketing | | `gitlab` | GitLab | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/gitlab.md) | | | dev-tools, ticketing | | `gitlab-pat` | GitLab (Personal Access Token) | API_KEY | [docs](https://nango.dev/docs/integrations/all/gitlab-pat.md) | [connect](https://nango.dev/docs/integrations/all/gitlab-pat/connect.md) | | dev-tools, ticketing | | `glyphic` | Glyphic | API_KEY | [docs](https://nango.dev/docs/api-integrations/glyphic.md) | [connect](https://nango.dev/docs/api-integrations/glyphic/connect.md) | | communication | | `gong` | Gong (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/gong.md) | [connect](https://nango.dev/docs/integrations/all/gong/connect.md) | | communication, marketing, productivity, video | | `gong-oauth` | Gong (Oauth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/gong-oauth.md) | | | communication, marketing, productivity, video, popular | | `google` | Google | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google.md) | | [setup](https://nango.dev/docs/api-integrations/google/how-to-register-your-own-google-api-oauth-app.md) | communication, dev-tools, productivity, social | | `google-ads` | Google Ads | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-ads.md) | | [setup](https://nango.dev/docs/api-integrations/google-ads/how-to-register-your-own-google-ads-api-oauth-app.md) | marketing | | `google-analytics` | Google Analytics | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-analytics.md) | | [setup](https://nango.dev/docs/api-integrations/google-analytics/how-to-register-your-own-google-analytics-api-oauth-app.md) | analytics | | `google-bigquery` | Google BigQuery | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-bigquery.md) | | [setup](https://nango.dev/docs/api-integrations/google-bigquery/how-to-register-your-own-google-bigquery-api-oauth-app.md) | productivity | | `google-calendar` | Google Calendar | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-calendar.md) | | [setup](https://nango.dev/docs/api-integrations/google-calendar/how-to-register-your-own-google-calendar-api-oauth-app.md) | popular, productivity | | `google-chat` | Google Chat | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-chat.md) | | [setup](https://nango.dev/docs/api-integrations/google-chat/how-to-register-your-own-google-chat-api-oauth-app.md) | productivity | | `google-cloud-storage` | Google Cloud Storage | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-cloud-storage.md) | | [setup](https://nango.dev/docs/api-integrations/google-cloud-storage/how-to-register-your-own-google-cloud-storage-api-oauth-app.md) | storage | | `google-contacts` | Google Contacts | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-contacts.md) | | [setup](https://nango.dev/docs/api-integrations/google-contacts/how-to-register-your-own-google-contacts-api-oauth-app.md) | productivity | | `google-docs` | Google Docs | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-docs.md) | | [setup](https://nango.dev/docs/api-integrations/google-docs/how-to-register-your-own-google-docs-api-oauth-app.md) | productivity | | `google-drive` | Google Drive | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-drive.md) | | [setup](https://nango.dev/docs/api-integrations/google-drive/how-to-register-your-own-google-drive-api-oauth-app.md) | knowledge-base, popular, storage | | `google-forms` | Google Forms | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-forms.md) | | [setup](https://nango.dev/docs/api-integrations/google-forms/how-to-register-your-own-google-forms-api-oauth-app.md) | productivity | | `google-gemini` | Google Gemini | API_KEY | [docs](https://nango.dev/docs/api-integrations/google-gemini.md) | [connect](https://nango.dev/docs/api-integrations/google-gemini/connect.md) | | productivity, dev-tools | | `google-health` | Google Health | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-health.md) | | [setup](https://nango.dev/docs/api-integrations/google-health/how-to-register-your-own-google-health-api-oauth-app.md) | sports | | `google-mail` | Gmail | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-mail.md) | | [setup](https://nango.dev/docs/api-integrations/google-mail/how-to-register-your-own-gmail-api-oauth-app.md) | popular, productivity | | `google-maps` | Google Maps | API_KEY | [docs](https://nango.dev/docs/api-integrations/google-maps.md) | [connect](https://nango.dev/docs/api-integrations/google-maps/connect.md) | | productivity | | `google-meet` | Google Meet | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-meet.md) | | [setup](https://nango.dev/docs/api-integrations/google-meet/how-to-register-your-own-google-meet-api-oauth-app.md) | communication, productivity | | `google-play` | Google Play | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/google-play.md) | | | dev-tools | | `google-search-console` | Google Search Console | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-search-console.md) | | [setup](https://nango.dev/docs/api-integrations/google-search-console/how-to-register-your-own-google-search-console-oauth-app.md) | productivity | | `google-service-account` | Google Service Account | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/google-service-account.md) | [connect](https://nango.dev/docs/integrations/all/google-service-account/connect.md) | | dev-tools | | `google-sheet` | Google Sheet | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-sheet.md) | | [setup](https://nango.dev/docs/api-integrations/google-sheet/how-to-register-your-own-google-sheet-api-oauth-app.md) | productivity | | `google-slides` | Google Slides | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-slides.md) | | [setup](https://nango.dev/docs/api-integrations/google-slides/how-to-register-your-own-google-slides-api-oauth-app.md) | productivity | | `google-tasks` | Google Tasks | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-tasks.md) | | [setup](https://nango.dev/docs/api-integrations/google-tasks/how-to-register-your-own-google-tasks-api-oauth-app.md) | productivity | | `google-workspace-admin` | Google Workspace Admin | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/google-workspace-admin.md) | | [setup](https://nango.dev/docs/api-integrations/google-workspace-admin/how-to-register-your-own-google-workspace-admin-api-oauth-app.md) | productivity | | `gorgias` | Gorgias | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/gorgias.md) | | | e-commerce | | `gorgias-basic` | Gorgias (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/gorgias-basic.md) | [connect](https://nango.dev/docs/integrations/all/gorgias-basic/connect.md) | | e-commerce | | `grafana` | Grafana | API_KEY | [docs](https://nango.dev/docs/integrations/all/grafana.md) | [connect](https://nango.dev/docs/integrations/all/grafana/connect.md) | | dev-tools | | `grain` | Grain (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/grain.md) | | | video, communication, productivity | | `grain-api-key` | Grain (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/grain-api-key.md) | | | video, communication, productivity | | `grammarly` | Grammarly | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/grammarly.md) | | | productivity | | `grammarly-scim` | Grammarly SCIM | API_KEY | [docs](https://nango.dev/docs/integrations/all/grammarly-scim.md) | [connect](https://nango.dev/docs/integrations/all/grammarly-scim/connect.md) | | productivity | | `granola` | Granola | API_KEY | [docs](https://nango.dev/docs/api-integrations/granola.md) | [connect](https://nango.dev/docs/api-integrations/granola/connect.md) | | productivity, knowledge-base | | `granola-mcp` | Granola (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/granola-mcp.md) | | | productivity, knowledge-base, mcp | | `greenhouse` | Greenhouse (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/greenhouse.md) | [connect](https://nango.dev/docs/integrations/all/greenhouse/connect.md) | | ats | | `greenhouse-assessment` | Greenhouse (Assessment API) | BASIC | [docs](https://nango.dev/docs/integrations/all/greenhouse-assessment.md) | [connect](https://nango.dev/docs/integrations/all/greenhouse-assessment/connect.md) | | ats | | `greenhouse-basic` | Greenhouse (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/greenhouse-basic.md) | [connect](https://nango.dev/docs/integrations/all/greenhouse-basic/connect.md) | | ats | | `greenhouse-harvest` | Greenhouse (Harvest API) | BASIC | [docs](https://nango.dev/docs/integrations/all/greenhouse-harvest.md) | [connect](https://nango.dev/docs/integrations/all/greenhouse-harvest/connect.md) | | ats | | `greenhouse-harvest-oauth2-cc` | Greenhouse Harvest (Client Credentials V3) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/greenhouse-harvest-oauth2-cc.md) | [connect](https://nango.dev/docs/api-integrations/greenhouse-harvest-oauth2-cc/connect.md) | | ats | | `greenhouse-harvest-partner` | Greenhouse Harvest Partner (V3) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/greenhouse-harvest-partner.md) | | [setup](https://nango.dev/docs/api-integrations/greenhouse-harvest-partner/how-to-register-your-own-greenhouse-harvest-partner-oauth-app.md) | ats | | `greenhouse-ingestion` | Greenhouse (Ingestion API) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/greenhouse-ingestion.md) | | | ats | | `greenhouse-job-board` | Greenhouse (Job Board API) | BASIC | [docs](https://nango.dev/docs/integrations/all/greenhouse-job-board.md) | [connect](https://nango.dev/docs/integrations/all/greenhouse-job-board/connect.md) | | ats | | `greenhouse-onboarding` | Greenhouse (Onboarding API) | BASIC | [docs](https://nango.dev/docs/integrations/all/greenhouse-onboarding.md) | [connect](https://nango.dev/docs/integrations/all/greenhouse-onboarding/connect.md) | | ats | | `grist` | Grist | API_KEY | [docs](https://nango.dev/docs/api-integrations/grist.md) | [connect](https://nango.dev/docs/api-integrations/grist/connect.md) | | productivity | | `gumroad` | Gumroad | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/gumroad.md) | | | design, e-commerce, payment | | `guru` | Guru | BASIC | [docs](https://nango.dev/docs/integrations/all/guru.md) | [connect](https://nango.dev/docs/integrations/all/guru/connect.md) | | knowledge-base | | `guru-scim` | Guru (SCIM) | BASIC | [docs](https://nango.dev/docs/integrations/all/guru-scim.md) | [connect](https://nango.dev/docs/integrations/all/guru-scim/connect.md) | | knowledge-base | | `gusto` | Gusto | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/gusto.md) | | | hr | | `gusto-demo` | Gusto (Demo) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/gusto-demo.md) | | | hr | | `hackerrank-work` | HackerRank Work | BASIC | [docs](https://nango.dev/docs/integrations/all/hackerrank-work.md) | [connect](https://nango.dev/docs/integrations/all/hackerrank-work/connect.md) | | ats | | `halo-psa` | Halo PSA | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/halo-psa.md) | [connect](https://nango.dev/docs/api-integrations/halo-psa/connect.md) | | support, ticketing | | `harvest` | Harvest | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/harvest.md) | | | productivity | | `health-gorilla` | Health Gorilla | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/health-gorilla.md) | | | other | | `heap` | Heap | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/heap.md) | [connect](https://nango.dev/docs/integrations/all/heap/connect.md) | | other | | `helpscout-docs` | Help Scout Docs | BASIC | [docs](https://nango.dev/docs/integrations/all/helpscout-docs.md) | [connect](https://nango.dev/docs/integrations/all/helpscout-docs/connect.md) | | knowledge-base, support | | `helpscout-mailbox` | Help Scout Mailbox | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/helpscout-mailbox.md) | | | support | | `hex` | Hex | API_KEY | [docs](https://nango.dev/docs/api-integrations/hex.md) | [connect](https://nango.dev/docs/api-integrations/hex/connect.md) | | analytics | | `heygen` | HeyGen | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/heygen.md) | | [setup](https://nango.dev/docs/api-integrations/heygen/how-to-register-your-own-heygen-oauth-app.md) | video | | `heymarket` | Heymarket | JWT | [docs](https://nango.dev/docs/api-integrations/heymarket.md) | [connect](https://nango.dev/docs/api-integrations/heymarket/connect.md) | | communication, marketing | | `heyreach` | HeyReach | API_KEY | [docs](https://nango.dev/docs/integrations/all/heyreach.md) | [connect](https://nango.dev/docs/integrations/all/heyreach/connect.md) | | social, marketing | | `hibob-service-user` | Hibob Service User | BASIC | [docs](https://nango.dev/docs/integrations/all/hibob-service-user.md) | [connect](https://nango.dev/docs/integrations/all/hibob-service-user/connect.md) | | hr, popular | | `highlevel` | HighLevel | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/highlevel.md) | | [setup](https://nango.dev/docs/api-integrations/highlevel/how-to-register-your-own-highlevel-api-oauth-app.md) | marketing | | `highlevel-white-label` | HighLevel (LeadConnector) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/highlevel-white-label.md) | | [setup](https://nango.dev/docs/api-integrations/highlevel-white-label/how-to-register-your-own-highlevel-white-label-api-oauth-app.md) | crm | | `holded` | Holded | API_KEY | [docs](https://nango.dev/docs/integrations/all/holded.md) | | | accounting, crm, invoicing | | `hover` | Hover | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/hover.md) | | | productivity | | `hubspot` | HubSpot | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/hubspot.md) | | [setup](https://nango.dev/docs/api-integrations/hubspot/how-to-register-your-own-hubspot-api-oauth-app.md) | marketing, support, crm, popular | | `hubspot-mcp` | HubSpot (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/hubspot-mcp.md) | | [setup](https://nango.dev/docs/api-integrations/hubspot-mcp/how-to-register-your-own-hubspot-mcp-api-oauth-app.md) | marketing, support, crm, mcp | | `huntress` | Huntress | BASIC | [docs](https://nango.dev/docs/api-integrations/huntress.md) | [connect](https://nango.dev/docs/api-integrations/huntress/connect.md) | | other | | `icypeas` | Icypeas | API_KEY | [docs](https://nango.dev/docs/integrations/all/icypeas.md) | [connect](https://nango.dev/docs/integrations/all/icypeas/connect.md) | | marketing, crm | | `immybot` | ImmyBot | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/immybot.md) | [connect](https://nango.dev/docs/api-integrations/immybot/connect.md) | | dev-tools, support | | `incident-io` | Incident.io | API_KEY | [docs](https://nango.dev/docs/integrations/all/incident-io.md) | [connect](https://nango.dev/docs/integrations/all/incident-io/connect.md) | | ticketing | | `insightly` | Insightly | BASIC | [docs](https://nango.dev/docs/integrations/all/insightly.md) | | | crm | | `instagram` | Instagram | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/instagram.md) | | [setup](https://nango.dev/docs/api-integrations/instagram/how-to-register-your-own-instagram-api-oauth-app.md) | marketing, social | | `instantly` | Instantly | API_KEY | [docs](https://nango.dev/docs/integrations/all/instantly.md) | [connect](https://nango.dev/docs/integrations/all/instantly/connect.md) | | marketing, communication | | `intercom` | Intercom | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/intercom.md) | [connect](https://nango.dev/docs/api-integrations/intercom/connect.md) | [setup](https://nango.dev/docs/api-integrations/intercom/how-to-register-your-own-intercom-api-oauth-app.md) | marketing, popular, support, surveys, ticketing | | `intuit` | Intuit | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/intuit.md) | | | accounting | | `ironclad` | Ironclad | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ironclad.md) | [connect](https://nango.dev/docs/integrations/all/ironclad/connect.md) | | legal | | `ironclad-cc` | Ironclad (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/ironclad-cc.md) | [connect](https://nango.dev/docs/api-integrations/ironclad-cc/connect.md) | | legal | | `itglue` | IT Glue | API_KEY | [docs](https://nango.dev/docs/integrations/all/itglue.md) | [connect](https://nango.dev/docs/integrations/all/itglue/connect.md) | | productivity, other | | `jamf` | Jamf Pro (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/jamf.md) | | | other | | `jamf-basic` | Jamf Pro (Basic Auth) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/jamf-basic.md) | | | other | | `jamie` | Jamie | API_KEY | [docs](https://nango.dev/docs/api-integrations/jamie.md) | [connect](https://nango.dev/docs/api-integrations/jamie/connect.md) | | productivity | | `jazzhr` | JazzHR | API_KEY | [docs](https://nango.dev/docs/integrations/all/jazzhr.md) | [connect](https://nango.dev/docs/integrations/all/jazzhr/connect.md) | | hr, ats | | `jiminny` | Jiminny | API_KEY | [docs](https://nango.dev/docs/api-integrations/jiminny.md) | [connect](https://nango.dev/docs/api-integrations/jiminny/connect.md) | | productivity, communication | | `jira` | Jira (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/jira.md) | | [setup](https://nango.dev/docs/api-integrations/jira/how-to-register-your-own-jira-api-oauth-app.md) | popular, productivity, ticketing | | `jira-basic` | Jira (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/jira-basic.md) | [connect](https://nango.dev/docs/integrations/all/jira-basic/connect.md) | | productivity, ticketing | | `jira-data-center` | Jira Data Center | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/jira-data-center.md) | | | productivity, ticketing | | `jira-data-center-api-key` | Jira Data Center (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/jira-data-center-api-key.md) | [connect](https://nango.dev/docs/integrations/all/jira-data-center-api-key/connect.md) | | productivity, ticketing | | `jira-data-center-basic` | Jira Data Center (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/jira-data-center-basic.md) | [connect](https://nango.dev/docs/integrations/all/jira-data-center-basic/connect.md) | | productivity, ticketing | | `jobadder` | Jobadder | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/jobadder.md) | | | hr, ats | | `jobber` | Jobber | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/jobber.md) | | | crm, invoicing | | `jobdiva` | JobDiva | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/jobdiva.md) | [connect](https://nango.dev/docs/integrations/all/jobdiva/connect.md) | | hr, ats | | `jobvite` | Jobvite | API_KEY | [docs](https://nango.dev/docs/integrations/all/jobvite.md) | [connect](https://nango.dev/docs/integrations/all/jobvite/connect.md) | | ats | | `jotform` | Jotform | API_KEY | [docs](https://nango.dev/docs/integrations/all/jotform.md) | | | surveys | | `jumpcloud` | JumpCloud | API_KEY | [docs](https://nango.dev/docs/integrations/all/jumpcloud.md) | [connect](https://nango.dev/docs/integrations/all/jumpcloud/connect.md) | | iam | | `juniper-mist` | Juniper Mist | API_KEY | [docs](https://nango.dev/docs/api-integrations/juniper-mist.md) | [connect](https://nango.dev/docs/api-integrations/juniper-mist/connect.md) | | dev-tools | | `justworks` | Justworks | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/justworks.md) | | [setup](https://nango.dev/docs/api-integrations/justworks/how-to-register-your-own-justworks-oauth-app.md) | hr | | `kandji` | Kandji | API_KEY | [docs](https://nango.dev/docs/integrations/all/kandji.md) | [connect](https://nango.dev/docs/integrations/all/kandji/connect.md) | | support, productivity | | `keap` | Keap | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/keap.md) | | | marketing | | `keeper-scim` | Keeper (SCIM) | API_KEY | [docs](https://nango.dev/docs/integrations/all/keeper-scim.md) | [connect](https://nango.dev/docs/integrations/all/keeper-scim/connect.md) | | productivity | | `kintone` | Kintone | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/kintone.md) | [connect](https://nango.dev/docs/integrations/all/kintone/connect.md) | | productivity | | `kintone-user-api` | Kintone User API | API_KEY | [docs](https://nango.dev/docs/integrations/all/kintone-user-api.md) | [connect](https://nango.dev/docs/integrations/all/kintone-user-api/connect.md) | | productivity | | `klaviyo` | Klaviyo (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/klaviyo.md) | [connect](https://nango.dev/docs/integrations/all/klaviyo/connect.md) | | marketing | | `klaviyo-oauth` | Klaviyo (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/klaviyo-oauth.md) | | | marketing | | `klicktipp` | KlickTipp | API_KEY | [docs](https://nango.dev/docs/api-integrations/klicktipp.md) | [connect](https://nango.dev/docs/api-integrations/klicktipp/connect.md) | | marketing | | `klipfolio` | Klipfolio | API_KEY | [docs](https://nango.dev/docs/integrations/all/klipfolio.md) | | | productivity, dev-tools | | `kno-commerce` | Kno Commerce | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/kno-commerce.md) | [connect](https://nango.dev/docs/api-integrations/kno-commerce/connect.md) | | e-commerce | | `knowbe4` | KnowBe4 | API_KEY | [docs](https://nango.dev/docs/api-integrations/knowbe4.md) | [connect](https://nango.dev/docs/api-integrations/knowbe4/connect.md) | | support | | `konnektive` | Konnektive | BASIC | [docs](https://nango.dev/docs/api-integrations/konnektive.md) | [connect](https://nango.dev/docs/api-integrations/konnektive/connect.md) | | crm, e-commerce | | `kustomer` | Kustomer | API_KEY | [docs](https://nango.dev/docs/integrations/all/kustomer.md) | | | crm | | `lagrowthmachine` | La Growth Machine | API_KEY | [docs](https://nango.dev/docs/integrations/all/lagrowthmachine.md) | [connect](https://nango.dev/docs/integrations/all/lagrowthmachine/connect.md) | | marketing | | `lastpass` | LastPass | BASIC | [docs](https://nango.dev/docs/integrations/all/lastpass.md) | [connect](https://nango.dev/docs/integrations/all/lastpass/connect.md) | | productivity | | `lattice` | Lattice | API_KEY | [docs](https://nango.dev/docs/integrations/all/lattice.md) | [connect](https://nango.dev/docs/integrations/all/lattice/connect.md) | | hr | | `leadfeeder` | Leadfeeder | API_KEY | [docs](https://nango.dev/docs/api-integrations/leadfeeder.md) | [connect](https://nango.dev/docs/api-integrations/leadfeeder/connect.md) | | marketing, crm | | `leadmagic` | LeadMagic | API_KEY | [docs](https://nango.dev/docs/integrations/all/leadmagic.md) | [connect](https://nango.dev/docs/integrations/all/leadmagic/connect.md) | | marketing, crm | | `lemlist` | lemlist | BASIC | [docs](https://nango.dev/docs/integrations/all/lemlist.md) | [connect](https://nango.dev/docs/integrations/all/lemlist/connect.md) | | marketing, communication | | `lessonly` | Lessonly | BASIC | [docs](https://nango.dev/docs/integrations/all/lessonly.md) | | | productivity | | `lever` | Lever (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/lever.md) | | | ats | | `lever-basic` | Lever (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/lever-basic.md) | [connect](https://nango.dev/docs/integrations/all/lever-basic/connect.md) | | ats | | `lever-basic-sandbox` | Lever (Basic Auth Sandbox)) | BASIC | [docs](https://nango.dev/docs/integrations/all/lever-basic-sandbox.md) | | | ats | | `lever-sandbox` | Lever (OAuth Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/lever-sandbox.md) | | | ats | | `lightfield` | Lightfield | API_KEY | [docs](https://nango.dev/docs/api-integrations/lightfield.md) | [connect](https://nango.dev/docs/api-integrations/lightfield/connect.md) | | crm | | `lightspeed-retail` | Lightspeed Retail (X-Series) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/lightspeed-retail.md) | | [setup](https://nango.dev/docs/api-integrations/lightspeed-retail/how-to-register-your-own-lightspeed-retail-api-oauth-app.md) | e-commerce | | `linear` | Linear | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/linear.md) | | [setup](https://nango.dev/docs/api-integrations/linear/how-to-register-your-own-linear-api-oauth-app.md) | popular, productivity, ticketing | | `linear-mcp` | Linear (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/linear-mcp.md) | | | popular, productivity, ticketing, mcp | | `linkedin` | LinkedIn | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/linkedin.md) | | [setup](https://nango.dev/docs/api-integrations/linkedin/how-to-register-your-own-linkedin-api-oauth-app.md) | ats, social | | `linkhut` | LinkHut | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/linkhut.md) | | | social | | `listmonk` | Listmonk | BASIC | [docs](https://nango.dev/docs/integrations/all/listmonk.md) | | | marketing | | `listrak` | Listrak | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/listrak.md) | [connect](https://nango.dev/docs/api-integrations/listrak/connect.md) | | marketing | | `lob` | Lob | BASIC | [docs](https://nango.dev/docs/api-integrations/lob.md) | [connect](https://nango.dev/docs/api-integrations/lob/connect.md) | | marketing | | `lokalise` | Lokalise | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/lokalise.md) | | [setup](https://nango.dev/docs/api-integrations/lokalise/how-to-register-your-own-lokalise-oauth-app.md) | productivity | | `looker` | Looker | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/looker.md) | [connect](https://nango.dev/docs/api-integrations/looker/connect.md) | | analytics | | `looker-oauth` | Looker (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/looker-oauth.md) | [connect](https://nango.dev/docs/api-integrations/looker-oauth/connect.md) | [setup](https://nango.dev/docs/api-integrations/looker-oauth/how-to-register-your-own-looker-oauth-app.md) | analytics | | `loom-scim` | Loom (SCIM) | API_KEY | [docs](https://nango.dev/docs/integrations/all/loom-scim.md) | [connect](https://nango.dev/docs/integrations/all/loom-scim/connect.md) | | other | | `loop-returns` | Loop Returns | API_KEY | [docs](https://nango.dev/docs/integrations/all/loop-returns.md) | [connect](https://nango.dev/docs/integrations/all/loop-returns/connect.md) | | e-commerce | | `loops-so` | Loops.so | API_KEY | [docs](https://nango.dev/docs/integrations/all/loops-so.md) | | | marketing, communication | | `lucid-scim` | Lucid (SCIM) | API_KEY | [docs](https://nango.dev/docs/integrations/all/lucid-scim.md) | [connect](https://nango.dev/docs/integrations/all/lucid-scim/connect.md) | | productivity | | `luma` | Luma | API_KEY | [docs](https://nango.dev/docs/integrations/all/luma.md) | | | productivity, ticketing | | `lumos` | Lumos | API_KEY | [docs](https://nango.dev/docs/api-integrations/lumos.md) | [connect](https://nango.dev/docs/api-integrations/lumos/connect.md) | | productivity | | `mailchimp` | Mailchimp | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/mailchimp.md) | | | marketing, surveys | | `mailgun` | Mailgun | BASIC | [docs](https://nango.dev/docs/integrations/all/mailgun.md) | [connect](https://nango.dev/docs/integrations/all/mailgun/connect.md) | | marketing | | `mailjet` | Mailjet | BASIC | [docs](https://nango.dev/docs/api-integrations/mailjet.md) | [connect](https://nango.dev/docs/api-integrations/mailjet/connect.md) | | marketing | | `make` | Make | API_KEY | [docs](https://nango.dev/docs/integrations/all/make.md) | [connect](https://nango.dev/docs/integrations/all/make/connect.md) | | productivity | | `malwarebytes` | Malwarebytes | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/malwarebytes.md) | [connect](https://nango.dev/docs/integrations/all/malwarebytes/connect.md) | | other | | `manatal` | Manatal | API_KEY | [docs](https://nango.dev/docs/integrations/all/manatal.md) | [connect](https://nango.dev/docs/integrations/all/manatal/connect.md) | | crm, hr, ats | | `marketo` | Marketo | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/marketo.md) | [connect](https://nango.dev/docs/integrations/all/marketo/connect.md) | | marketing | | `mattermost` | Mattermost | API_KEY | [docs](https://nango.dev/docs/api-integrations/mattermost.md) | [connect](https://nango.dev/docs/api-integrations/mattermost/connect.md) | | communication | | `maximizer` | Maximizer (Cloud) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/maximizer.md) | [connect](https://nango.dev/docs/api-integrations/maximizer/connect.md) | [setup](https://nango.dev/docs/api-integrations/maximizer/how-to-register-your-own-maximizer-oauth-app.md) | crm | | `maximizer-on-premise` | Maximizer (On-Premise) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/maximizer-on-premise.md) | [connect](https://nango.dev/docs/api-integrations/maximizer-on-premise/connect.md) | [setup](https://nango.dev/docs/api-integrations/maximizer-on-premise/how-to-register-your-own-maximizer-on-premise-oauth-app.md) | crm | | `maxio` | Maxio | BASIC | [docs](https://nango.dev/docs/api-integrations/maxio.md) | [connect](https://nango.dev/docs/api-integrations/maxio/connect.md) | | payment | | `mcp-generic` | MCP Server OAuth2 (Generic) | MCP_OAUTH2_GENERIC | [docs](https://nango.dev/docs/integrations/all/mcp-generic.md) | | | mcp | | `medallia` | Medallia | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/medallia.md) | [connect](https://nango.dev/docs/integrations/all/medallia/connect.md) | | crm, support, surveys | | `mercury` | Mercury | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/mercury.md) | [connect](https://nango.dev/docs/api-integrations/mercury/connect.md) | [setup](https://nango.dev/docs/api-integrations/mercury/how-to-register-your-own-mercury-oauth-app.md) | accounting | | `meta-marketing-api` | Meta Marketing API | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/meta-marketing-api.md) | | [setup](https://nango.dev/docs/api-integrations/meta-marketing-api/how-to-register-your-own-meta-marketing-api-oauth-app.md) | marketing | | `metabase` | Metabase | API_KEY | [docs](https://nango.dev/docs/api-integrations/metabase.md) | [connect](https://nango.dev/docs/api-integrations/metabase/connect.md) | | analytics | | `microsoft` | Microsoft | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/microsoft.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft/how-to-register-your-own-microsoft-api-oauth-app.md) | communication, dev-tools, productivity | | `microsoft-admin` | Microsoft (Admin) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/microsoft-admin.md) | [connect](https://nango.dev/docs/integrations/all/microsoft-admin/connect.md) | | communication, dev-tools, productivity, iam | | `microsoft-ads` | Microsoft Ads | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/microsoft-ads.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft/how-to-register-your-own-microsoft-api-oauth-app.md) | marketing | | `microsoft-business-central` | Microsoft Business Central | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/microsoft-business-central.md) | [connect](https://nango.dev/docs/integrations/all/microsoft-business-central/connect.md) | | erp | | `microsoft-entra-id` | Microsoft Entra ID | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/microsoft-entra-id.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft/how-to-register-your-own-microsoft-api-oauth-app.md) | iam | | `microsoft-excel` | Microsoft Excel | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/microsoft-excel.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft/how-to-register-your-own-microsoft-api-oauth-app.md) | productivity, analytics | | `microsoft-intune` | Microsoft Intune | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/microsoft-intune.md) | [connect](https://nango.dev/docs/api-integrations/microsoft-intune/connect.md) | | dev-tools | | `microsoft-oauth2-cc` | Microsoft (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/microsoft-oauth2-cc.md) | [connect](https://nango.dev/docs/api-integrations/microsoft-oauth2-cc/connect.md) | | communication, dev-tools, productivity | | `microsoft-oauth2-cc-cert` | Microsoft (Client Credentials - Certificate) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/microsoft-oauth2-cc-cert.md) | [connect](https://nango.dev/docs/api-integrations/microsoft-oauth2-cc-cert/connect.md) | | communication, dev-tools, productivity | | `microsoft-people` | Microsoft People | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/microsoft-people.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft-people/how-to-register-your-own-microsoft-people-api-oauth-app.md) | productivity | | `microsoft-planner` | Microsoft Planner | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/microsoft-planner.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft-planner/how-to-register-your-own-microsoft-planner-api-oauth-app.md) | productivity, analytics | | `microsoft-power-bi` | Microsoft Power BI | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/microsoft-power-bi.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft/how-to-register-your-own-microsoft-api-oauth-app.md) | productivity | | `microsoft-powerpoint` | Microsoft PowerPoint | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/microsoft-powerpoint.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft-powerpoint/how-to-register-your-own-microsoft-powerpoint-api-oauth-app.md) | productivity | | `microsoft-teams` | Microsoft Teams | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/microsoft-teams.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft-teams/how-to-register-your-own-microsoft-teams-api-oauth-app.md) | productivity, video, popular | | `microsoft-teams-bot` | Microsoft Teams Bot | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/microsoft-teams-bot.md) | [connect](https://nango.dev/docs/api-integrations/microsoft-teams-bot/connect.md) | [setup](https://nango.dev/docs/api-integrations/microsoft-teams-bot/how-to-register-your-own-microsoft-teams-bot-oauth-app.md) | communication | | `microsoft-tenant-specific` | Microsoft (Tenant) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/microsoft-tenant-specific.md) | | | erp | | `microsoft-word` | Microsoft Word | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/microsoft-word.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft-word/how-to-register-your-own-microsoft-word-api-oauth-app.md) | productivity | | `mimecast` | Mimecast | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/mimecast.md) | [connect](https://nango.dev/docs/integrations/all/mimecast/connect.md) | | communication | | `mindbody` | Mindbody | API_KEY | [docs](https://nango.dev/docs/integrations/all/mindbody.md) | [connect](https://nango.dev/docs/integrations/all/mindbody/connect.md) | | productivity | | `minimax` | MiniMax | API_KEY | [docs](https://nango.dev/docs/integrations/all/minimax.md) | [connect](https://nango.dev/docs/integrations/all/minimax/connect.md) | | productivity, dev-tools | | `mip-cloud` | MIP Cloud | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/mip-cloud.md) | [connect](https://nango.dev/docs/integrations/all/mip-cloud/connect.md) | | accounting | | `mip-on-premise` | MIP On Premise | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/mip-on-premise.md) | [connect](https://nango.dev/docs/integrations/all/mip-on-premise/connect.md) | | accounting | | `miro` | Miro | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/miro.md) | | | design, productivity | | `miro-scim` | Miro (SCIM API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/miro-scim.md) | [connect](https://nango.dev/docs/integrations/all/miro-scim/connect.md) | | design, productivity | | `missive` | Missive | API_KEY | [docs](https://nango.dev/docs/integrations/all/missive.md) | | | productivity | | `mixpanel` | Mixpanel | BASIC | [docs](https://nango.dev/docs/integrations/all/mixpanel.md) | [connect](https://nango.dev/docs/integrations/all/mixpanel/connect.md) | | analytics | | `modjo-ai` | Modjo AI | API_KEY | [docs](https://nango.dev/docs/api-integrations/modjo-ai.md) | [connect](https://nango.dev/docs/api-integrations/modjo-ai/connect.md) | | crm | | `modmed` | ModMed | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/modmed.md) | [connect](https://nango.dev/docs/api-integrations/modmed/connect.md) | | other | | `mollie` | Mollie | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/mollie.md) | | [setup](https://nango.dev/docs/api-integrations/mollie/how-to-register-your-own-mollie-oauth-app.md) | payment | | `momentum-io` | Momentum.io | API_KEY | [docs](https://nango.dev/docs/integrations/all/momentum-io.md) | [connect](https://nango.dev/docs/integrations/all/momentum-io/connect.md) | | productivity | | `monday` | Monday | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/monday.md) | | [setup](https://nango.dev/docs/api-integrations/monday/how-to-register-your-own-monday-oauth-app.md) | productivity, ticketing | | `mural` | Mural | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/mural.md) | | | design | | `nable-ncentral` | N-able N-central | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/nable-ncentral.md) | [connect](https://nango.dev/docs/api-integrations/nable-ncentral/connect.md) | | support | | `namely` | Namely | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/namely.md) | [connect](https://nango.dev/docs/integrations/all/namely/connect.md) | | hr | | `namely-pat` | Namely (PAT) | API_KEY | [docs](https://nango.dev/docs/integrations/all/namely-pat.md) | [connect](https://nango.dev/docs/integrations/all/namely-pat/connect.md) | | hr | | `nationbuilder` | NationBuilder | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/nationbuilder.md) | | | crm | | `nerdio` | Nerdio | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/nerdio.md) | [connect](https://nango.dev/docs/api-integrations/nerdio/connect.md) | | other | | `netsuite` | NetSuite (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/netsuite.md) | | | accounting, erp | | `netsuite-client-credentials` | NetSuite (Client Credentials) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/netsuite-client-credentials.md) | [connect](https://nango.dev/docs/api-integrations/netsuite-client-credentials/connect.md) | | accounting, erp | | `netsuite-tba` | NetSuite (TBA) | TBA | [docs](https://nango.dev/docs/api-integrations/netsuite-tba.md) | | [setup](https://nango.dev/docs/api-integrations/netsuite-tba/how-to-set-up-netsuite-tba-with-nango.md) | accounting, erp, popular | | `next-cloud-ocs` | Next Cloud OCS | BASIC | [docs](https://nango.dev/docs/integrations/all/next-cloud-ocs.md) | [connect](https://nango.dev/docs/integrations/all/next-cloud-ocs/connect.md) | | storage, productivity | | `nexthink` | Nexthink | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/nexthink.md) | [connect](https://nango.dev/docs/api-integrations/nexthink/connect.md) | | analytics | | `ninjaone-rmm` | NinjaOne RMM | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/ninjaone-rmm.md) | [connect](https://nango.dev/docs/integrations/all/ninjaone-rmm/connect.md) | | support | | `ninjaone-rmm-oauth2` | NinjaOne RMM (OAuth2) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/ninjaone-rmm-oauth2.md) | | [setup](https://nango.dev/docs/api-integrations/ninjaone-rmm-oauth2/how-to-register-your-own-ninjaone-rmm-api-oauth-app.md) | support | | `ninjaone-saas-backup` | NinjaOne SaaS Backup | API_KEY | [docs](https://nango.dev/docs/api-integrations/ninjaone-saas-backup.md) | [connect](https://nango.dev/docs/api-integrations/ninjaone-saas-backup/connect.md) | | storage, productivity | | `nocrm` | nocrm.io | API_KEY | [docs](https://nango.dev/docs/api-integrations/nocrm.md) | [connect](https://nango.dev/docs/api-integrations/nocrm/connect.md) | | crm, productivity | | `northbeam` | Northbeam | API_KEY | [docs](https://nango.dev/docs/api-integrations/northbeam.md) | [connect](https://nango.dev/docs/api-integrations/northbeam/connect.md) | | analytics, marketing | | `notion` | Notion | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/notion.md) | | [setup](https://nango.dev/docs/api-integrations/notion/how-to-register-your-own-notion-api-oauth-app.md) | knowledge-base, popular, productivity | | `notion-mcp` | Notion (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/notion-mcp.md) | | | knowledge-base, productivity, mcp | | `notion-scim` | Notion (SCIM API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/notion-scim.md) | [connect](https://nango.dev/docs/integrations/all/notion-scim/connect.md) | | knowledge-base, productivity | | `nyne-ai` | Nyne AI | API_KEY | [docs](https://nango.dev/docs/api-integrations/nyne-ai.md) | [connect](https://nango.dev/docs/api-integrations/nyne-ai/connect.md) | | analytics | | `ocean-io` | Ocean.io | API_KEY | [docs](https://nango.dev/docs/api-integrations/ocean-io.md) | [connect](https://nango.dev/docs/api-integrations/ocean-io/connect.md) | | crm | | `odoo` | Odoo (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/odoo.md) | | | erp | | `odoo-cc` | Odoo (Client Credentials) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/odoo-cc.md) | [connect](https://nango.dev/docs/integrations/all/odoo-cc/connect.md) | | erp | | `okta` | Okta | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/okta.md) | | | dev-tools, iam | | `okta-cc` | Okta (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/okta-cc.md) | [connect](https://nango.dev/docs/api-integrations/okta-cc/connect.md) | | dev-tools, iam | | `okta-preview` | Okta (Preview) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/okta-preview.md) | | | dev-tools, iam | | `one-drive` | OneDrive for Business | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/one-drive.md) | | [setup](https://nango.dev/docs/api-integrations/one-drive/how-to-register-your-own-onedrive-for-business-api-oauth-app.md) | knowledge-base, storage | | `one-drive-personal` | OneDrive Personal | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/one-drive-personal.md) | | | knowledge-base, storage | | `one-note` | One Note | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/one-note.md) | | [setup](https://nango.dev/docs/api-integrations/microsoft/how-to-register-your-own-microsoft-api-oauth-app.md) | productivity | | `onelogin` | OneLogin | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/onelogin.md) | [connect](https://nango.dev/docs/integrations/all/onelogin/connect.md) | | dev-tools, iam | | `onlogist` | Onlogist | API_KEY | [docs](https://nango.dev/docs/api-integrations/onlogist.md) | [connect](https://nango.dev/docs/api-integrations/onlogist/connect.md) | | other | | `oomnitza` | Oomnitza | API_KEY | [docs](https://nango.dev/docs/api-integrations/oomnitza.md) | [connect](https://nango.dev/docs/api-integrations/oomnitza/connect.md) | | productivity | | `open-hands` | Open Hands | API_KEY | [docs](https://nango.dev/docs/integrations/all/open-hands.md) | [connect](https://nango.dev/docs/integrations/all/open-hands/connect.md) | | dev-tools | | `openai` | OpenAI | API_KEY | [docs](https://nango.dev/docs/integrations/all/openai.md) | [connect](https://nango.dev/docs/integrations/all/openai/connect.md) | | productivity, dev-tools, popular | | `openai-admin` | OpenAI (Admin) | API_KEY | [docs](https://nango.dev/docs/integrations/all/openai-admin.md) | [connect](https://nango.dev/docs/integrations/all/openai-admin/connect.md) | | productivity, dev-tools | | `oracle-cloud-identity` | Oracle Cloud Identity | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/oracle-cloud-identity.md) | [connect](https://nango.dev/docs/api-integrations/oracle-cloud-identity/connect.md) | | dev-tools, iam | | `oracle-hcm` | Oracle Fusion Cloud (HCM) | BASIC | [docs](https://nango.dev/docs/integrations/all/oracle-hcm.md) | [connect](https://nango.dev/docs/integrations/all/oracle-hcm/connect.md) | | hr | | `orange-logic` | Orange Logic | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/orange-logic.md) | [connect](https://nango.dev/docs/api-integrations/orange-logic/connect.md) | | storage | | `ordinal` | Ordinal | API_KEY | [docs](https://nango.dev/docs/api-integrations/ordinal.md) | [connect](https://nango.dev/docs/api-integrations/ordinal/connect.md) | | productivity | | `ory` | Ory | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/ory.md) | | | iam | | `osu` | Osu | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/osu.md) | | | gaming | | `oura` | Oura | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/oura.md) | | | sports | | `outlook` | Outlook | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/outlook.md) | | [setup](https://nango.dev/docs/api-integrations/outlook/how-to-register-your-own-outlook-api-oauth-app.md) | communication, popular | | `outreach` | Outreach | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/outreach.md) | | | marketing | | `paddle` | Paddle | API_KEY | [docs](https://nango.dev/docs/api-integrations/paddle.md) | [connect](https://nango.dev/docs/api-integrations/paddle/connect.md) | [setup](https://nango.dev/docs/api-integrations/paddle/connect.md) | payment | | `pagerduty` | PagerDuty | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/pagerduty.md) | | | dev-tools | | `paligo` | Paligo | BASIC | [docs](https://nango.dev/docs/api-integrations/paligo.md) | [connect](https://nango.dev/docs/api-integrations/paligo/connect.md) | | cms | | `pandadoc` | Pandadoc | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/pandadoc.md) | | | legal | | `pandadoc-api-key` | Pandadoc (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/pandadoc-api-key.md) | [connect](https://nango.dev/docs/integrations/all/pandadoc-api-key/connect.md) | | legal | | `passportal` | Passportal | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/passportal.md) | [connect](https://nango.dev/docs/integrations/all/passportal/connect.md) | | support | | `pax8` | Pax8 | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/pax8.md) | [connect](https://nango.dev/docs/integrations/all/pax8/connect.md) | | other | | `paychex` | Paychex | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/paychex.md) | [connect](https://nango.dev/docs/integrations/all/paychex/connect.md) | | hr | | `paycom` | Paycom | BASIC | [docs](https://nango.dev/docs/integrations/all/paycom.md) | [connect](https://nango.dev/docs/integrations/all/paycom/connect.md) | | hr | | `paycor` | Paycor | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/paycor.md) | [connect](https://nango.dev/docs/integrations/all/paycor/connect.md) | | hr | | `paycor-sandbox` | Paycor (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/paycor-sandbox.md) | [connect](https://nango.dev/docs/integrations/all/paycor-sandbox/connect.md) | | hr | | `payfit` | Payfit | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/payfit.md) | | | hr | | `paylocity` | Paylocity (Weblink) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/paylocity.md) | [connect](https://nango.dev/docs/api-integrations/paylocity/connect.md) | | hr | | `paylocity-nextgen` | Paylocity (NextGen) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/paylocity-nextgen.md) | [connect](https://nango.dev/docs/api-integrations/paylocity-nextgen/connect.md) | | hr | | `paypal` | Paypal | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/paypal.md) | | | payment | | `paypal-sandbox` | Paypal (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/paypal-sandbox.md) | | | payment | | `pendo` | Pendo | API_KEY | [docs](https://nango.dev/docs/integrations/all/pendo.md) | [connect](https://nango.dev/docs/integrations/all/pendo/connect.md) | | analytics | | `pendo-oauth` | Pendo (OAuth) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/pendo-oauth.md) | [connect](https://nango.dev/docs/api-integrations/pendo-oauth/connect.md) | | analytics | | `pennylane` | Pennylane | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/pennylane.md) | | | accounting, banking, invoicing, payment | | `pennylane-company-api` | Pennylane (Company API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/pennylane-company-api.md) | [connect](https://nango.dev/docs/integrations/all/pennylane-company-api/connect.md) | | accounting, banking, invoicing, payment | | `peopledatalabs` | People Data Labs | API_KEY | [docs](https://nango.dev/docs/integrations/all/peopledatalabs.md) | | | analytics | | `perdoo` | Perdoo | API_KEY | [docs](https://nango.dev/docs/api-integrations/perdoo.md) | [connect](https://nango.dev/docs/api-integrations/perdoo/connect.md) | | productivity, analytics | | `perimeter81` | Perimeter81 | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/perimeter81.md) | [connect](https://nango.dev/docs/integrations/all/perimeter81/connect.md) | | productivity | | `perk` | Perk | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/perk.md) | | [setup](https://nango.dev/docs/api-integrations/perk/how-to-register-your-own-perk-api-oauth-app.md) | productivity | | `perplexity` | Perplexity | API_KEY | [docs](https://nango.dev/docs/integrations/all/perplexity.md) | | | productivity, dev-tools | | `personio` | Personio (v1) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/personio.md) | [connect](https://nango.dev/docs/integrations/all/personio/connect.md) | | hr | | `personio-recruiting` | Personio Recruiting | API_KEY | [docs](https://nango.dev/docs/integrations/all/personio-recruiting.md) | | | hr, ats | | `personio-v2` | Personio (v2) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/personio-v2.md) | | | hr | | `pingboard` | Pingboard | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/pingboard.md) | | | productivity | | `pingone` | PingOne | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/pingone.md) | [connect](https://nango.dev/docs/integrations/all/pingone/connect.md) | | dev-tools, iam | | `pingone-cc` | PingOne (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/pingone-cc.md) | [connect](https://nango.dev/docs/integrations/all/pingone-cc/connect.md) | | dev-tools, iam | | `pinterest` | Pinterest | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/pinterest.md) | | | design, marketing, social, video | | `pipedream` | Pipedream (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/pipedream.md) | [connect](https://nango.dev/docs/integrations/all/pipedream/connect.md) | | dev-tools, productivity | | `pipedream-oauth2-cc` | Pipedream (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/pipedream-oauth2-cc.md) | [connect](https://nango.dev/docs/integrations/all/pipedream-oauth2-cc/connect.md) | | dev-tools, productivity | | `pipedrive` | Pipedrive | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/pipedrive.md) | | [setup](https://nango.dev/docs/api-integrations/pipedrive/how-to-register-your-own-pipedrive-oauth-app.md) | crm | | `pivotaltracker` | Pivotal Tracker | API_KEY | [docs](https://nango.dev/docs/integrations/all/pivotaltracker.md) | | | productivity | | `plain` | Plain | API_KEY | [docs](https://nango.dev/docs/integrations/all/plain.md) | [connect](https://nango.dev/docs/integrations/all/plain/connect.md) | | support | | `planning-center-online` | Planning Center Online (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/planning-center-online.md) | | [setup](https://nango.dev/docs/api-integrations/planning-center-online/how-to-register-your-own-planning-center-oauth-app.md) | crm, productivity | | `planning-center-online-pat` | Planning Center Online (Personal Access Token) | BASIC | [docs](https://nango.dev/docs/api-integrations/planning-center-online-pat.md) | [connect](https://nango.dev/docs/api-integrations/planning-center-online-pat/connect.md) | | crm, productivity | | `pleo` | Pleo | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/pleo.md) | [connect](https://nango.dev/docs/api-integrations/pleo/connect.md) | [setup](https://nango.dev/docs/api-integrations/pleo/how-to-register-your-own-pleo-api-oauth-app.md) | payment, invoicing | | `podium` | Podium | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/podium.md) | | | communication, marketing | | `podscribe` | Podscribe | API_KEY | [docs](https://nango.dev/docs/api-integrations/podscribe.md) | [connect](https://nango.dev/docs/api-integrations/podscribe/connect.md) | | marketing, analytics | | `posthog` | PostHog | API_KEY | [docs](https://nango.dev/docs/api-integrations/posthog.md) | [connect](https://nango.dev/docs/api-integrations/posthog/connect.md) | | dev-tools | | `posthog-oauth` | PostHog (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/posthog-oauth.md) | [connect](https://nango.dev/docs/api-integrations/posthog-oauth/connect.md) | [setup](https://nango.dev/docs/api-integrations/posthog-oauth/how-to-register-your-own-posthog-oauth-api-oauth-app.md) | dev-tools | | `practicefusion` | PracticeFusion | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/practicefusion.md) | [connect](https://nango.dev/docs/api-integrations/practicefusion/connect.md) | [setup](https://nango.dev/docs/api-integrations/practicefusion/how-to-register-your-own-practicefusion-api-oauth-app.md) | other | | `precisefp` | PreciseFP | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/precisefp.md) | | | crm, productivity | | `printful` | Printful | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/printful.md) | | [setup](https://nango.dev/docs/api-integrations/printful/how-to-register-your-own-printful-oauth-app.md) | e-commerce | | `private-api-basic` | Private API (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/private-api-basic.md) | [connect](https://nango.dev/docs/integrations/all/private-api-basic/connect.md) | | other | | `private-api-bearer` | Private API (Bearer Auth) | API_KEY | [docs](https://nango.dev/docs/integrations/all/private-api-bearer.md) | [connect](https://nango.dev/docs/integrations/all/private-api-bearer/connect.md) | | other | | `private-api-generic` | Private API (Generic) | API_KEY | [docs](https://nango.dev/docs/integrations/all/private-api-generic.md) | [connect](https://nango.dev/docs/integrations/all/private-api-generic/connect.md) | | other | | `prive` | Prive | API_KEY | [docs](https://nango.dev/docs/integrations/all/prive.md) | [connect](https://nango.dev/docs/integrations/all/prive/connect.md) | | e-commerce | | `procore` | Procore | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/procore.md) | [connect](https://nango.dev/docs/api-integrations/procore/connect.md) | [setup](https://nango.dev/docs/api-integrations/procore/how-to-register-your-own-procore-oauth-app.md) | erp | | `productboard` | Productboard | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/productboard.md) | | | productivity | | `prospeo` | Prospeo | API_KEY | [docs](https://nango.dev/docs/api-integrations/prospeo.md) | [connect](https://nango.dev/docs/api-integrations/prospeo/connect.md) | | crm, marketing | | `provenexpert` | ProvenExpert | BASIC | [docs](https://nango.dev/docs/api-integrations/provenexpert.md) | [connect](https://nango.dev/docs/api-integrations/provenexpert/connect.md) | | marketing | | `prtg-classic` | PRTG Classic | API_KEY | [docs](https://nango.dev/docs/api-integrations/prtg-classic.md) | [connect](https://nango.dev/docs/api-integrations/prtg-classic/connect.md) | | support | | `pushpay-chms-v1` | Pushpay ChMS (v1) | BASIC | [docs](https://nango.dev/docs/api-integrations/pushpay-chms-v1.md) | [connect](https://nango.dev/docs/api-integrations/pushpay-chms-v1/connect.md) | | crm, productivity | | `pverify` | pVerify | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/pverify.md) | [connect](https://nango.dev/docs/api-integrations/pverify/connect.md) | | other | | `pylon` | Pylon | API_KEY | [docs](https://nango.dev/docs/api-integrations/pylon.md) | [connect](https://nango.dev/docs/api-integrations/pylon/connect.md) | | productivity | | `qualia` | Qualia | BASIC | [docs](https://nango.dev/docs/api-integrations/qualia.md) | [connect](https://nango.dev/docs/api-integrations/qualia/connect.md) | | legal | | `qualtrics` | Qualtrics | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/qualtrics.md) | | | surveys | | `quentn` | Quentn | API_KEY | [docs](https://nango.dev/docs/api-integrations/quentn.md) | [connect](https://nango.dev/docs/api-integrations/quentn/connect.md) | | marketing | | `quickbase` | Quickbase | API_KEY | [docs](https://nango.dev/docs/integrations/all/quickbase.md) | [connect](https://nango.dev/docs/integrations/all/quickbase/connect.md) | | productivity | | `quickbooks` | Quickbooks | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/quickbooks.md) | [connect](https://nango.dev/docs/api-integrations/quickbooks/connect.md) | [setup](https://nango.dev/docs/api-integrations/quickbooks/how-to-register-your-own-quickbooks-api-oauth-app.md) | accounting, popular | | `quickbooks-sandbox` | Quickbooks (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/quickbooks-sandbox.md) | [connect](https://nango.dev/docs/api-integrations/quickbooks-sandbox/connect.md) | [setup](https://nango.dev/docs/api-integrations/quickbooks/how-to-register-your-own-quickbooks-api-oauth-app.md) | accounting | | `quipteams` | Quipteams | API_KEY | [docs](https://nango.dev/docs/api-integrations/quipteams.md) | [connect](https://nango.dev/docs/api-integrations/quipteams/connect.md) | | other | | `ragieai` | Ragie AI | API_KEY | [docs](https://nango.dev/docs/integrations/all/ragieai.md) | | | dev-tools | | `raindrop-mcp` | Raindrop (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/raindrop-mcp.md) | | | dev-tools, mcp | | `ramp` | Ramp | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ramp.md) | | | banking | | `ramp-sandbox` | Ramp (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ramp-sandbox.md) | | | banking | | `rapidapi` | RapidAPI | API_KEY | [docs](https://nango.dev/docs/integrations/all/rapidapi.md) | | | dev-tools | | `razorpay` | Razorpay | BASIC | [docs](https://nango.dev/docs/integrations/all/razorpay.md) | [connect](https://nango.dev/docs/integrations/all/razorpay/connect.md) | | payment | | `read-ai` | Read.ai | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/read-ai.md) | | [setup](https://nango.dev/docs/api-integrations/read-ai/how-to-register-your-own-read-ai-oauth-app.md) | productivity, video | | `readwise` | Readwise | API_KEY | [docs](https://nango.dev/docs/integrations/all/readwise.md) | [connect](https://nango.dev/docs/integrations/all/readwise/connect.md) | | productivity | | `readwise-reader` | Readwise Reader | API_KEY | [docs](https://nango.dev/docs/integrations/all/readwise-reader.md) | [connect](https://nango.dev/docs/integrations/all/readwise-reader/connect.md) | | productivity | | `reapit` | Reapit Connect | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/reapit.md) | | [setup](https://nango.dev/docs/api-integrations/reapit/how-to-register-your-own-reapit-oauth-app.md) | crm | | `recall-ai` | Recall.ai | API_KEY | [docs](https://nango.dev/docs/api-integrations/recall-ai.md) | [connect](https://nango.dev/docs/api-integrations/recall-ai/connect.md) | | dev-tools, productivity | | `recharge` | Recharge | API_KEY | [docs](https://nango.dev/docs/integrations/all/recharge.md) | [connect](https://nango.dev/docs/integrations/all/recharge/connect.md) | | e-commerce | | `recruitcrm` | Recruitcrm | API_KEY | [docs](https://nango.dev/docs/integrations/all/recruitcrm.md) | [connect](https://nango.dev/docs/integrations/all/recruitcrm/connect.md) | | hr, ats | | `recruitee` | Recruitee | API_KEY | [docs](https://nango.dev/docs/integrations/all/recruitee.md) | [connect](https://nango.dev/docs/integrations/all/recruitee/connect.md) | | ats | | `recruiterflow` | Recruiterflow | API_KEY | [docs](https://nango.dev/docs/integrations/all/recruiterflow.md) | [connect](https://nango.dev/docs/integrations/all/recruiterflow/connect.md) | | hr, ats | | `reddit` | Reddit | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/reddit.md) | | | social | | `redtail-crm-sandbox` | Redtail CRM (Sandbox) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/redtail-crm-sandbox.md) | [connect](https://nango.dev/docs/integrations/all/redtail-crm-sandbox/connect.md) | | crm | | `refiner` | Refiner | API_KEY | [docs](https://nango.dev/docs/integrations/all/refiner.md) | | | surveys | | `render-mcp` | Render (MCP) | API_KEY | [docs](https://nango.dev/docs/api-integrations/render-mcp.md) | [connect](https://nango.dev/docs/api-integrations/render-mcp/connect.md) | | dev-tools, mcp | | `replicate` | Replicate | API_KEY | [docs](https://nango.dev/docs/integrations/all/replicate.md) | | | dev-tools | | `reply-io` | Reply.io | API_KEY | [docs](https://nango.dev/docs/api-integrations/reply-io.md) | [connect](https://nango.dev/docs/api-integrations/reply-io/connect.md) | | marketing, communication | | `researchdesk` | ResearchDesk | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/researchdesk.md) | [connect](https://nango.dev/docs/api-integrations/researchdesk/connect.md) | | other | | `resend` | Resend | API_KEY | [docs](https://nango.dev/docs/api-integrations/resend.md) | [connect](https://nango.dev/docs/api-integrations/resend/connect.md) | | communication | | `retell-ai` | Retell AI | API_KEY | [docs](https://nango.dev/docs/integrations/all/retell-ai.md) | [connect](https://nango.dev/docs/integrations/all/retell-ai/connect.md) | | dev-tools, productivity | | `revivn` | Revivn | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/revivn.md) | [connect](https://nango.dev/docs/api-integrations/revivn/connect.md) | | other | | `ring-central` | RingCentral | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ring-central.md) | | | support | | `ring-central-sandbox` | RingCentral (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ring-central-sandbox.md) | | | communication | | `ringover` | Ringover | API_KEY | [docs](https://nango.dev/docs/api-integrations/ringover.md) | [connect](https://nango.dev/docs/api-integrations/ringover/connect.md) | | communication | | `rippling` | Rippling | API_KEY | [docs](https://nango.dev/docs/integrations/all/rippling.md) | [connect](https://nango.dev/docs/integrations/all/rippling/connect.md) | | hr | | `rippling-shop-app` | Rippling Shop App | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/rippling-shop-app.md) | | | hr | | `roam-scim` | Roam (SCIM API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/roam-scim.md) | [connect](https://nango.dev/docs/integrations/all/roam-scim/connect.md) | | productivity | | `robinhood-mcp` | Robinhood (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/robinhood-mcp.md) | | | payment, mcp | | `rock-gym-pro` | Rock Gym Pro | BASIC | [docs](https://nango.dev/docs/integrations/all/rock-gym-pro.md) | | | crm | | `rocketlane` | Rocketlane | API_KEY | [docs](https://nango.dev/docs/integrations/all/rocketlane.md) | [connect](https://nango.dev/docs/integrations/all/rocketlane/connect.md) | | productivity | | `rocketreach` | RocketReach | API_KEY | [docs](https://nango.dev/docs/api-integrations/rocketreach.md) | [connect](https://nango.dev/docs/api-integrations/rocketreach/connect.md) | | crm | | `roller` | Roller | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/roller.md) | | | ticketing | | `rootly` | Rootly | API_KEY | [docs](https://nango.dev/docs/api-integrations/rootly.md) | [connect](https://nango.dev/docs/api-integrations/rootly/connect.md) | | dev-tools | | `rydoo` | Rydoo | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/rydoo.md) | [connect](https://nango.dev/docs/api-integrations/rydoo/connect.md) | | accounting | | `sage` | Sage | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/sage.md) | | | accounting, erp | | `sage-200` | Sage 200 | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/sage-200.md) | | [setup](https://nango.dev/docs/api-integrations/sage-200/how-to-register-your-own-sage-200-oauth-app.md) | accounting, erp | | `sage-hr` | Sage HR | API_KEY | [docs](https://nango.dev/docs/integrations/all/sage-hr.md) | [connect](https://nango.dev/docs/integrations/all/sage-hr/connect.md) | | hr | | `sage-intacct` | Sage Intacct | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/sage-intacct.md) | [connect](https://nango.dev/docs/integrations/all/sage-intacct/connect.md) | | accounting, erp | | `sage-intacct-cc` | Sage Intacct (Client Credentials) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/sage-intacct-cc.md) | [connect](https://nango.dev/docs/api-integrations/sage-intacct-cc/connect.md) | [setup](https://nango.dev/docs/api-integrations/sage-intacct-cc/connect.md) | accounting, erp | | `sage-intacct-oauth` | Sage Intacct (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/sage-intacct-oauth.md) | | | accounting, erp, popular | | `sage-people` | Sage People | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/sage-people.md) | [connect](https://nango.dev/docs/integrations/all/sage-people/connect.md) | | hr | | `salesforce` | Salesforce | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/salesforce.md) | [connect](https://nango.dev/docs/api-integrations/salesforce/connect.md) | [setup](https://nango.dev/docs/api-integrations/salesforce/salesforce-api-oauth-app-setup.md) | crm, popular | | `salesforce-cc` | Salesforce (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/salesforce-cc.md) | [connect](https://nango.dev/docs/api-integrations/salesforce-cc/connect.md) | | crm | | `salesforce-cdp` | Salesforce (Data Cloud) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/salesforce-cdp.md) | [connect](https://nango.dev/docs/integrations/all/salesforce-cdp/connect.md) | | storage | | `salesforce-experience-cloud` | Salesforce Experience Cloud | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/salesforce-experience-cloud.md) | | | crm | | `salesforce-jwt` | Salesforce (JWT) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/salesforce-jwt.md) | [connect](https://nango.dev/docs/api-integrations/salesforce-jwt/connect.md) | | crm | | `salesforce-sandbox` | Salesforce (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/salesforce-sandbox.md) | | [setup](https://nango.dev/docs/api-integrations/salesforce-sandbox/how-to-register-your-own-salesforce-sandbox-api-oauth-app.md) | crm | | `salesloft` | Salesloft | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/salesloft.md) | | | marketing | | `salesmsg` | Salesmsg (PAT) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/salesmsg.md) | [connect](https://nango.dev/docs/api-integrations/salesmsg/connect.md) | | communication, marketing | | `salesmsg-oauth2` | Salesmsg (OAuth2) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/salesmsg-oauth2.md) | | [setup](https://nango.dev/docs/api-integrations/salesmsg-oauth2/how-to-register-your-own-salesmsg-oauth-app.md) | communication, marketing | | `sap-ariba` | SAP Ariba | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/sap-ariba.md) | [connect](https://nango.dev/docs/api-integrations/sap-ariba/connect.md) | | erp | | `sap-business-one` | SAP Business One | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/sap-business-one.md) | [connect](https://nango.dev/docs/integrations/all/sap-business-one/connect.md) | | erp | | `sap-concur` | SAP Concur | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/sap-concur.md) | [connect](https://nango.dev/docs/integrations/all/sap-concur/connect.md) | | erp | | `sap-concur-password` | SAP Concur (Password Grant) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/sap-concur-password.md) | [connect](https://nango.dev/docs/api-integrations/sap-concur-password/connect.md) | | erp | | `sap-fieldglass` | SAP Fieldglass | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/sap-fieldglass.md) | [connect](https://nango.dev/docs/integrations/all/sap-fieldglass/connect.md) | | erp | | `sap-odata-basic` | SAP S/4HANA Cloud (Basic Auth) | BASIC | [docs](https://nango.dev/docs/api-integrations/sap-odata-basic.md) | [connect](https://nango.dev/docs/api-integrations/sap-odata-basic/connect.md) | | erp | | `sap-odata-oauth2-cc` | SAP S/4HANA Cloud (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/sap-odata-oauth2-cc.md) | [connect](https://nango.dev/docs/api-integrations/sap-odata-oauth2-cc/connect.md) | | erp, popular | | `sap-success-factors` | SAP SuccessFactors | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/sap-success-factors.md) | [connect](https://nango.dev/docs/api-integrations/sap-success-factors/connect.md) | | hr | | `schwab` | Schwab | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/schwab.md) | | [setup](https://nango.dev/docs/api-integrations/schwab/how-to-register-your-own-schwab-oauth-app.md) | banking, accounting | | `scrapedo` | Scrape.do | API_KEY | [docs](https://nango.dev/docs/integrations/all/scrapedo.md) | [connect](https://nango.dev/docs/integrations/all/scrapedo/connect.md) | | other | | `sedna` | Sedna (OAuth) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/sedna.md) | | | communication | | `sedna-basic` | Sedna (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/sedna-basic.md) | | | communication | | `segment` | Segment | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/segment.md) | | | analytics, marketing | | `sellercloud` | Sellercloud | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/sellercloud.md) | [connect](https://nango.dev/docs/api-integrations/sellercloud/connect.md) | | e-commerce | | `sellsy` | Sellsy | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/sellsy.md) | | | invoicing, accounting, crm | | `sellsy-oauth2-cc` | Sellsy (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/sellsy-oauth2-cc.md) | [connect](https://nango.dev/docs/integrations/all/sellsy-oauth2-cc/connect.md) | | invoicing, accounting, crm | | `semrush` | Semrush (v3) | API_KEY | [docs](https://nango.dev/docs/integrations/all/semrush.md) | [connect](https://nango.dev/docs/integrations/all/semrush/connect.md) | | marketing, analytics | | `sendgrid` | SendGrid | API_KEY | [docs](https://nango.dev/docs/integrations/all/sendgrid.md) | [connect](https://nango.dev/docs/integrations/all/sendgrid/connect.md) | | marketing | | `sentinelone` | SentinelOne | API_KEY | [docs](https://nango.dev/docs/api-integrations/sentinelone.md) | [connect](https://nango.dev/docs/api-integrations/sentinelone/connect.md) | | dev-tools | | `sentry` | Sentry | API_KEY | [docs](https://nango.dev/docs/integrations/all/sentry.md) | [connect](https://nango.dev/docs/integrations/all/sentry/connect.md) | | dev-tools, analytics | | `sentry-oauth` | Sentry (Public Integrations) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/sentry-oauth.md) | [connect](https://nango.dev/docs/integrations/all/sentry-oauth/connect.md) | | dev-tools, analytics | | `servicem8` | ServiceM8 | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/servicem8.md) | | | productivity | | `servicenow` | ServiceNow | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/servicenow.md) | | | productivity, popular | | `servicenow-oauth2-cc` | ServiceNow (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/servicenow-oauth2-cc.md) | [connect](https://nango.dev/docs/api-integrations/servicenow-oauth2-cc/connect.md) | | productivity, ticketing | | `setmore` | Setmore | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/setmore.md) | [connect](https://nango.dev/docs/integrations/all/setmore/connect.md) | | productivity | | `sharepoint-online` | SharePoint Online (v2) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/sharepoint-online.md) | | [setup](https://nango.dev/docs/api-integrations/sharepoint-online/how-to-register-your-own-sharepoint-online-api-oauth-app.md) | storage, communication, popular | | `sharepoint-online-oauth2-cc` | SharePoint Online (Client Credentials V2) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/sharepoint-online-oauth2-cc.md) | [connect](https://nango.dev/docs/integrations/all/sharepoint-online-oauth2-cc/connect.md) | | storage, communication | | `sharepoint-online-v1` | SharePoint Online (v1) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/sharepoint-online-v1.md) | [connect](https://nango.dev/docs/integrations/all/sharepoint-online-v1/connect.md) | | storage, communication | | `shipbob-pat` | ShipBob (Personal Access Token) | API_KEY | [docs](https://nango.dev/docs/api-integrations/shipbob-pat.md) | [connect](https://nango.dev/docs/api-integrations/shipbob-pat/connect.md) | | e-commerce | | `shippo` | Shippo | API_KEY | [docs](https://nango.dev/docs/api-integrations/shippo.md) | [connect](https://nango.dev/docs/api-integrations/shippo/connect.md) | | e-commerce | | `shipstation` | Shipstation (v1) | BASIC | [docs](https://nango.dev/docs/integrations/all/shipstation.md) | [connect](https://nango.dev/docs/integrations/all/shipstation/connect.md) | | e-commerce | | `shipstation-v2` | Shipstation (v2) | API_KEY | [docs](https://nango.dev/docs/integrations/all/shipstation-v2.md) | [connect](https://nango.dev/docs/integrations/all/shipstation-v2/connect.md) | | e-commerce | | `shopify` | Shopify (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/shopify.md) | [connect](https://nango.dev/docs/api-integrations/shopify/connect.md) | [setup](https://nango.dev/docs/api-integrations/shopify/how-to-register-your-own-shopify-api-oauth-app.md) | e-commerce, popular | | `shopify-api-key` | Shopify (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/shopify-api-key.md) | [connect](https://nango.dev/docs/integrations/all/shopify-api-key/connect.md) | | e-commerce | | `shopify-cc` | Shopify (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/shopify-cc.md) | [connect](https://nango.dev/docs/api-integrations/shopify-cc/connect.md) | | e-commerce | | `shopify-partner` | Shopify Partner | API_KEY | [docs](https://nango.dev/docs/integrations/all/shopify-partner.md) | [connect](https://nango.dev/docs/integrations/all/shopify-partner/connect.md) | | e-commerce | | `shopify-scim` | Shopify (SCIM API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/shopify-scim.md) | [connect](https://nango.dev/docs/integrations/all/shopify-scim/connect.md) | | e-commerce | | `shopvox` | ShopVox | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/shopvox.md) | [connect](https://nango.dev/docs/api-integrations/shopvox/connect.md) | | other | | `shopware` | Shopware (Admin API) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/shopware.md) | [connect](https://nango.dev/docs/api-integrations/shopware/connect.md) | | e-commerce | | `shopworks` | ShopWorks | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/shopworks.md) | [connect](https://nango.dev/docs/api-integrations/shopworks/connect.md) | | other | | `shortcut` | Shortcut | API_KEY | [docs](https://nango.dev/docs/integrations/all/shortcut.md) | | | dev-tools, productivity | | `signnow` | SignNow | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/signnow.md) | | | legal | | `signnow-sandbox` | SignNow (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/signnow-sandbox.md) | | | legal | | `simpro` | Simpro | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/simpro.md) | [connect](https://nango.dev/docs/api-integrations/simpro/connect.md) | [setup](https://nango.dev/docs/api-integrations/simpro/how-to-register-your-own-simpro-api-oauth-app.md) | productivity, invoicing | | `skio` | Skio | API_KEY | [docs](https://nango.dev/docs/integrations/all/skio.md) | [connect](https://nango.dev/docs/integrations/all/skio/connect.md) | | e-commerce | | `slab` | Slab | API_KEY | [docs](https://nango.dev/docs/api-integrations/slab.md) | [connect](https://nango.dev/docs/api-integrations/slab/connect.md) | | productivity, knowledge-base | | `slack` | Slack | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/slack.md) | | [setup](https://nango.dev/docs/api-integrations/slack/how-to-register-your-own-slack-api-oauth-app.md) | popular, productivity | | `slack-mcp` | Slack (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/slack-mcp.md) | | [setup](https://nango.dev/docs/api-integrations/slack-mcp/how-to-register-your-own-slack-mcp-oauth-app.md) | popular, productivity, communication, mcp | | `smartlead-ai` | Smartlead.ai | API_KEY | [docs](https://nango.dev/docs/integrations/all/smartlead-ai.md) | [connect](https://nango.dev/docs/integrations/all/smartlead-ai/connect.md) | | communication, marketing | | `smartrecruiters-api-key` | Smartrecruiters | API_KEY | [docs](https://nango.dev/docs/integrations/all/smartrecruiters-api-key.md) | | | ats | | `smartsheet` | Smartsheet | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/smartsheet.md) | [connect](https://nango.dev/docs/api-integrations/smartsheet/connect.md) | [setup](https://nango.dev/docs/api-integrations/smartsheet/how-to-register-your-own-smartsheet-api-oauth-app.md) | productivity | | `smugmug` | Smugmug | OAUTH1 | [docs](https://nango.dev/docs/integrations/all/smugmug.md) | | | storage | | `snapchat` | Snapchat (Ads API) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/snapchat.md) | | | video, social | | `snipe-it` | Snipe-IT | API_KEY | [docs](https://nango.dev/docs/integrations/all/snipe-it.md) | [connect](https://nango.dev/docs/integrations/all/snipe-it/connect.md) | | productivity, other | | `snowflake` | Snowflake | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/snowflake.md) | [connect](https://nango.dev/docs/api-integrations/snowflake/connect.md) | [setup](https://nango.dev/docs/api-integrations/snowflake/how-to-register-your-own-snowflake-api-oauth-app.md) | dev-tools | | `snowflake-jwt` | Snowflake (JWT) | JWT | [docs](https://nango.dev/docs/integrations/all/snowflake-jwt.md) | [connect](https://nango.dev/docs/integrations/all/snowflake-jwt/connect.md) | | dev-tools | | `sophos-central` | Sophos Central | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/sophos-central.md) | [connect](https://nango.dev/docs/api-integrations/sophos-central/connect.md) | | dev-tools | | `splitwise` | Splitwise | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/splitwise.md) | | | payment, social | | `spotify` | Spotify (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/spotify.md) | | | other | | `spotify-oauth2-cc` | Spotify (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/spotify-oauth2-cc.md) | | | other | | `squarespace` | Squarespace | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/squarespace.md) | | | dev-tools, design | | `squareup` | Squareup | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/squareup.md) | | [setup](https://nango.dev/docs/api-integrations/squareup/how-to-register-your-own-squareup-oauth-app.md) | payment | | `squareup-sandbox` | Squareup (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/squareup-sandbox.md) | | [setup](https://nango.dev/docs/api-integrations/squareup-sandbox/how-to-register-your-own-squareup-sandbox-oauth-app.md) | payment | | `stackexchange` | Stack Exchange | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/stackexchange.md) | | | knowledge-base, support | | `statamic` | Statamic | API_KEY | [docs](https://nango.dev/docs/api-integrations/statamic.md) | [connect](https://nango.dev/docs/api-integrations/statamic/connect.md) | | cms | | `statista` | Statista | API_KEY | [docs](https://nango.dev/docs/integrations/all/statista.md) | [connect](https://nango.dev/docs/integrations/all/statista/connect.md) | | analytics | | `stay-ai` | Stay AI | API_KEY | [docs](https://nango.dev/docs/api-integrations/stay-ai.md) | [connect](https://nango.dev/docs/api-integrations/stay-ai/connect.md) | | e-commerce | | `stitch-mcp` | Stitch (MCP) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/stitch-mcp.md) | | [setup](https://nango.dev/docs/api-integrations/stitch-mcp/how-to-register-your-own-stitch-mcp-api-oauth-app.md) | design, mcp | | `store-census` | StoreCensus | API_KEY | [docs](https://nango.dev/docs/api-integrations/store-census.md) | [connect](https://nango.dev/docs/api-integrations/store-census/connect.md) | | e-commerce, crm | | `store-leads` | Store Leads | API_KEY | [docs](https://nango.dev/docs/api-integrations/store-leads.md) | [connect](https://nango.dev/docs/api-integrations/store-leads/connect.md) | | e-commerce | | `strava` | Strava (Mobile) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/strava.md) | | | social, sports | | `strava-web` | Strava (Web) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/strava-web.md) | | | social, sports | | `streak` | Streak | BASIC | [docs](https://nango.dev/docs/api-integrations/streak.md) | [connect](https://nango.dev/docs/api-integrations/streak/connect.md) | | crm | | `stripe` | Stripe Connect | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/stripe.md) | | [setup](https://nango.dev/docs/api-integrations/stripe/how-to-register-your-own-stripe-api-oauth-app.md) | payment | | `stripe-api-key` | Stripe (API Key) | BASIC | [docs](https://nango.dev/docs/api-integrations/stripe-api-key.md) | [connect](https://nango.dev/docs/api-integrations/stripe-api-key/connect.md) | | payment | | `stripe-app` | Stripe App | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/stripe-app.md) | | | payment, popular | | `stripe-app-sandbox` | Stripe App (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/stripe-app-sandbox.md) | [connect](https://nango.dev/docs/integrations/all/stripe-app-sandbox/connect.md) | | payment | | `stripe-express` | Stripe Express | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/stripe-express.md) | | | payment | | `supabase` | Supabase | API_KEY | [docs](https://nango.dev/docs/api-integrations/supabase.md) | [connect](https://nango.dev/docs/api-integrations/supabase/connect.md) | | dev-tools, storage | | `supabase-mcp` | Supabase (MCP) | API_KEY | [docs](https://nango.dev/docs/api-integrations/supabase-mcp.md) | [connect](https://nango.dev/docs/api-integrations/supabase-mcp/connect.md) | | dev-tools, mcp | | `supabase-mcp-oauth` | Supabase (MCP OAuth) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/supabase-mcp-oauth.md) | [connect](https://nango.dev/docs/api-integrations/supabase-mcp-oauth/connect.md) | | dev-tools, mcp | | `superhuman-mcp` | Superhuman (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/superhuman-mcp.md) | | | communication, productivity, mcp | | `surecontact` | SureContact | API_KEY | [docs](https://nango.dev/docs/api-integrations/surecontact.md) | [connect](https://nango.dev/docs/api-integrations/surecontact/connect.md) | | crm | | `survey-monkey` | SurveyMonkey | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/survey-monkey.md) | | | surveys | | `swoogo` | Swoogo | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/swoogo.md) | [connect](https://nango.dev/docs/api-integrations/swoogo/connect.md) | | marketing | | `tableau` | Tableau (PAT) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/tableau.md) | [connect](https://nango.dev/docs/integrations/all/tableau/connect.md) | | analytics | | `tailscale` | Tailscale (OAuth) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/tailscale.md) | [connect](https://nango.dev/docs/integrations/all/tailscale/connect.md) | | dev-tools, productivity | | `tailscale-api-key` | Tailscale (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/tailscale-api-key.md) | [connect](https://nango.dev/docs/integrations/all/tailscale-api-key/connect.md) | | dev-tools, productivity | | `talentlms` | TalentLMS | API_KEY | [docs](https://nango.dev/docs/api-integrations/talentlms.md) | [connect](https://nango.dev/docs/api-integrations/talentlms/connect.md) | | other | | `tally` | Tally | API_KEY | [docs](https://nango.dev/docs/api-integrations/tally.md) | [connect](https://nango.dev/docs/api-integrations/tally/connect.md) | | productivity | | `tanium` | Tanium | API_KEY | [docs](https://nango.dev/docs/api-integrations/tanium.md) | [connect](https://nango.dev/docs/api-integrations/tanium/connect.md) | | dev-tools | | `tapclicks` | TapClicks | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/tapclicks.md) | [connect](https://nango.dev/docs/integrations/all/tapclicks/connect.md) | | marketing, analytics | | `teamleader` | Teamleader Focus | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/teamleader.md) | | | invoicing, crm | | `teamtailor` | Teamtailor | API_KEY | [docs](https://nango.dev/docs/integrations/all/teamtailor.md) | | | ats | | `teamwork` | Teamwork | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/teamwork.md) | | | productivity, ticketing | | `telegram` | Telegram | API_KEY | [docs](https://nango.dev/docs/api-integrations/telegram.md) | [connect](https://nango.dev/docs/api-integrations/telegram/connect.md) | | communication | | `tempo` | Tempo | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/tempo.md) | [connect](https://nango.dev/docs/api-integrations/tempo/connect.md) | [setup](https://nango.dev/docs/api-integrations/tempo/how-to-register-your-own-tempo-api-oauth-app.md) | productivity | | `terraform` | Terraform | API_KEY | [docs](https://nango.dev/docs/integrations/all/terraform.md) | [connect](https://nango.dev/docs/integrations/all/terraform/connect.md) | | dev-tools | | `the-swarm` | The Swarm | API_KEY | [docs](https://nango.dev/docs/api-integrations/the-swarm.md) | [connect](https://nango.dev/docs/api-integrations/the-swarm/connect.md) | | crm | | `theirstack` | TheirStack | API_KEY | [docs](https://nango.dev/docs/api-integrations/theirstack.md) | [connect](https://nango.dev/docs/api-integrations/theirstack/connect.md) | | marketing | | `thomson-reuters-legal-tracker` | Thomson Reuters Legal Tracker | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/thomson-reuters-legal-tracker.md) | [connect](https://nango.dev/docs/api-integrations/thomson-reuters-legal-tracker/connect.md) | | legal | | `thomson-reuters-legal-tracker-tr-account` | Thomson Reuters Legal Tracker (TR Account) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/thomson-reuters-legal-tracker-tr-account.md) | [connect](https://nango.dev/docs/api-integrations/thomson-reuters-legal-tracker-tr-account/connect.md) | | legal | | `thrivecart-api-key` | ThriveCart (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/thrivecart-api-key.md) | | | e-commerce, payment | | `thrivecart-oauth` | ThriveCart (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/thrivecart-oauth.md) | | | e-commerce, payment | | `ticktick` | TickTick | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ticktick.md) | | | productivity, ticketing | | `tiktok-accounts` | TikTok Accounts | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/tiktok-accounts.md) | | | social | | `tiktok-ads` | TikTok Ads | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/tiktok-ads.md) | | | social | | `tiktok-personal` | TikTok Personal | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/tiktok-personal.md) | | | social | | `timely` | Timely | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/timely.md) | | | productivity | | `timify` | Timify | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/timify.md) | [connect](https://nango.dev/docs/api-integrations/timify/connect.md) | | productivity | | `tldv` | tl;dv | API_KEY | [docs](https://nango.dev/docs/integrations/all/tldv.md) | [connect](https://nango.dev/docs/integrations/all/tldv/connect.md) | | productivity | | `toast` | Toast | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/toast.md) | [connect](https://nango.dev/docs/api-integrations/toast/connect.md) | | erp | | `todoist` | Todoist | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/todoist.md) | | | productivity, ticketing | | `toggl` | Toggl Track | BASIC | [docs](https://nango.dev/docs/api-integrations/toggl.md) | [connect](https://nango.dev/docs/api-integrations/toggl/connect.md) | | productivity | | `torii` | Torii | API_KEY | [docs](https://nango.dev/docs/api-integrations/torii.md) | [connect](https://nango.dev/docs/api-integrations/torii/connect.md) | | productivity | | `trafft` | Trafft | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/trafft.md) | [connect](https://nango.dev/docs/integrations/all/trafft/connect.md) | | productivity | | `trakstar-hire` | Trakstar Hire | BASIC | [docs](https://nango.dev/docs/integrations/all/trakstar-hire.md) | [connect](https://nango.dev/docs/integrations/all/trakstar-hire/connect.md) | | ats | | `trello` | Trello | OAUTH1 | [docs](https://nango.dev/docs/integrations/all/trello.md) | | | productivity, ticketing | | `trello-scim` | Trello (SCIM API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/trello-scim.md) | [connect](https://nango.dev/docs/integrations/all/trello-scim/connect.md) | | productivity, ticketing | | `tremendous` | Tremendous | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/tremendous.md) | | | payment | | `tremendous-sandbox` | Tremendous (Sandbox) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/tremendous-sandbox.md) | | | payment | | `trigger-dev` | Trigger.dev | API_KEY | [docs](https://nango.dev/docs/api-integrations/trigger-dev.md) | [connect](https://nango.dev/docs/api-integrations/trigger-dev/connect.md) | | dev-tools | | `trigify-io-mcp` | Trigify IO (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/trigify-io-mcp.md) | | | dev-tools, mcp | | `triple-whale` | Triple Whale | API_KEY | [docs](https://nango.dev/docs/api-integrations/triple-whale.md) | [connect](https://nango.dev/docs/api-integrations/triple-whale/connect.md) | | analytics, marketing | | `tsheetsteam` | TSheets | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/tsheetsteam.md) | | | hr, productivity | | `tumblr` | Tumblr | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/tumblr.md) | | | social, communication | | `twenty-crm` | Twenty CRM | API_KEY | [docs](https://nango.dev/docs/integrations/all/twenty-crm.md) | | | crm | | `twenty-crm-self-hosted` | Twenty CRM (Self Hosted) | API_KEY | [docs](https://nango.dev/docs/integrations/all/twenty-crm-self-hosted.md) | | | crm | | `twilio` | Twilio | BASIC | [docs](https://nango.dev/docs/integrations/all/twilio.md) | [connect](https://nango.dev/docs/integrations/all/twilio/connect.md) | | dev-tools, communication | | `twinfield` | Twinfield | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/twinfield.md) | | | accounting | | `twitch` | Twitch | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/twitch.md) | | | gaming, social, sports, video | | `twitter` | Twitter (v1) | OAUTH1 | [docs](https://nango.dev/docs/integrations/all/twitter.md) | | | marketing, social | | `twitter-oauth2-cc` | Twitter (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/twitter-oauth2-cc.md) | | | marketing, social | | `twitter-v2` | Twitter (v2) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/twitter-v2.md) | | [setup](https://nango.dev/docs/api-integrations/twitter-v2/how-to-register-your-own-twitter-v2-api-oauth-app.md) | marketing, social | | `typeform` | Typeform | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/typeform.md) | | | surveys | | `typefully` | Typefully | API_KEY | [docs](https://nango.dev/docs/integrations/all/typefully.md) | | | analytics, communication, social | | `typefully-v2` | Typefully (API v2) | API_KEY | [docs](https://nango.dev/docs/api-integrations/typefully-v2.md) | [connect](https://nango.dev/docs/api-integrations/typefully-v2/connect.md) | | analytics, communication, social | | `uber` | Uber | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/uber.md) | | | other | | `ukg-pro` | UKG Pro | BASIC | [docs](https://nango.dev/docs/integrations/all/ukg-pro.md) | [connect](https://nango.dev/docs/integrations/all/ukg-pro/connect.md) | | hr, popular | | `ukg-pro-cc` | UKG Pro HCM (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/ukg-pro-cc.md) | [connect](https://nango.dev/docs/api-integrations/ukg-pro-cc/connect.md) | | hr | | `ukg-pro-wfm` | UKG Pro (Workforce Management) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/ukg-pro-wfm.md) | [connect](https://nango.dev/docs/integrations/all/ukg-pro-wfm/connect.md) | | hr | | `ukg-ready` | UKG Ready | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/ukg-ready.md) | [connect](https://nango.dev/docs/integrations/all/ukg-ready/connect.md) | | hr | | `unanet` | Unanet | BASIC | [docs](https://nango.dev/docs/integrations/all/unanet.md) | [connect](https://nango.dev/docs/integrations/all/unanet/connect.md) | | crm, erp, accounting | | `unauthenticated` | Unauthenticated | NONE | [docs](https://nango.dev/docs/integrations/all/unauthenticated.md) | | | other | | `unipile` | Unipile | API_KEY | [docs](https://nango.dev/docs/integrations/all/unipile.md) | | | crm, marketing | | `upsales` | Upsales | API_KEY | [docs](https://nango.dev/docs/api-integrations/upsales.md) | [connect](https://nango.dev/docs/api-integrations/upsales/connect.md) | | crm | | `valley` | Valley (OAuth) | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/valley.md) | [connect](https://nango.dev/docs/integrations/all/valley/connect.md) | | marketing | | `valley-api-key` | Valley (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/valley-api-key.md) | [connect](https://nango.dev/docs/integrations/all/valley-api-key/connect.md) | | marketing | | `vanta` | Vanta | OAUTH2_CC | [docs](https://nango.dev/docs/api-integrations/vanta.md) | [connect](https://nango.dev/docs/api-integrations/vanta/connect.md) | | dev-tools | | `veeva-vault` | Veeva Vault | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/veeva-vault.md) | [connect](https://nango.dev/docs/api-integrations/veeva-vault/connect.md) | | crm | | `vercel` | Vercel | API_KEY | [docs](https://nango.dev/docs/api-integrations/vercel.md) | [connect](https://nango.dev/docs/api-integrations/vercel/connect.md) | | dev-tools | | `vercel-mcp` | Vercel (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/vercel-mcp.md) | | [setup](https://nango.dev/docs/api-integrations/vercel-mcp/how-to-register-your-own-vercel-mcp-oauth-app.md) | dev-tools, mcp | | `videoask` | VideoAsk | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/videoask.md) | | [setup](https://nango.dev/docs/api-integrations/videoask/how-to-register-your-own-videoask-api-oauth-app.md) | video | | `vimeo` | Vimeo (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/vimeo.md) | | | video | | `vimeo-basic` | Vimeo (Basic Auth) | BASIC | [docs](https://nango.dev/docs/integrations/all/vimeo-basic.md) | | | video | | `vtex` | VTEX | API_KEY | [docs](https://nango.dev/docs/api-integrations/vtex.md) | [connect](https://nango.dev/docs/api-integrations/vtex/connect.md) | | e-commerce | | `wakatime` | Wakatime | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/wakatime.md) | | | analytics, dev-tools | | `walmart` | Walmart Marketplace | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/walmart.md) | | [setup](https://nango.dev/docs/api-integrations/walmart/how-to-register-your-own-walmart-marketplace-oauth-app.md) | e-commerce | | `wappalyzer` | Wappalyzer | API_KEY | [docs](https://nango.dev/docs/api-integrations/wappalyzer.md) | [connect](https://nango.dev/docs/api-integrations/wappalyzer/connect.md) | | marketing, analytics | | `wave-accounting` | Wave Accounting | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/wave-accounting.md) | | | accounting | | `wealthbox` | Wealthbox | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/wealthbox.md) | | | crm | | `webex` | Webex | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/webex.md) | | | communication | | `webflow` | Webflow | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/webflow.md) | | | dev-tools, design, cms | | `webinarjam` | WebinarJam | API_KEY | [docs](https://nango.dev/docs/api-integrations/webinarjam.md) | [connect](https://nango.dev/docs/api-integrations/webinarjam/connect.md) | | marketing | | `wejam` | WeJam AI | API_KEY | [docs](https://nango.dev/docs/api-integrations/wejam.md) | [connect](https://nango.dev/docs/api-integrations/wejam/connect.md) | | productivity | | `whatsapp-business` | WhatsApp Business | API_KEY | [docs](https://nango.dev/docs/api-integrations/whatsapp-business.md) | | [setup](https://nango.dev/docs/api-integrations/whatsapp-business/how-to-register-your-own-whatsapp-business-api-oauth-app.md) | communication, marketing | | `whoop` | Whoop | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/whoop.md) | | | sports | | `wildix-pbx` | Wildix PBX | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/wildix-pbx.md) | | | communication | | `wise-api-key` | Wise (API Key) | API_KEY | [docs](https://nango.dev/docs/api-integrations/wise-api-key.md) | [connect](https://nango.dev/docs/api-integrations/wise-api-key/connect.md) | | banking, payment | | `wiseagent` | Wiseagent | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/wiseagent.md) | | [setup](https://nango.dev/docs/api-integrations/wiseagent/how-to-register-your-own-wiseagent-oauth-app.md) | crm | | `wiza` | Wiza | API_KEY | [docs](https://nango.dev/docs/integrations/all/wiza.md) | [connect](https://nango.dev/docs/integrations/all/wiza/connect.md) | | crm, marketing | | `woocommerce` | WooCommerce | BASIC | [docs](https://nango.dev/docs/integrations/all/woocommerce.md) | [connect](https://nango.dev/docs/integrations/all/woocommerce/connect.md) | | e-commerce, dev-tools, design | | `wordpress` | WordPress | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/wordpress.md) | | | dev-tools, design, cms | | `wordpress-mcp` | WordPress.com (MCP) | MCP_OAUTH2 | [docs](https://nango.dev/docs/api-integrations/wordpress-mcp.md) | | | cms, mcp | | `workable` | Workable (API Key) | API_KEY | [docs](https://nango.dev/docs/integrations/all/workable.md) | [connect](https://nango.dev/docs/integrations/all/workable/connect.md) | | ats | | `workable-oauth` | Workable (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/workable-oauth.md) | | | ats | | `workday` | Workday | BASIC | [docs](https://nango.dev/docs/integrations/all/workday.md) | [connect](https://nango.dev/docs/integrations/all/workday/connect.md) | | hr, popular | | `workday-adaptive-planning` | Workday Adaptive Planning | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/workday-adaptive-planning.md) | [connect](https://nango.dev/docs/api-integrations/workday-adaptive-planning/connect.md) | | accounting | | `workday-adaptive-planning-basic` | Workday Adaptive Planning (Basic Auth) | BASIC | [docs](https://nango.dev/docs/api-integrations/workday-adaptive-planning-basic.md) | [connect](https://nango.dev/docs/api-integrations/workday-adaptive-planning-basic/connect.md) | | accounting | | `workday-cc` | Workday (Client Credentials) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/workday-cc.md) | [connect](https://nango.dev/docs/api-integrations/workday-cc/connect.md) | | hr | | `workday-oauth` | Workday (OAuth) | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/workday-oauth.md) | [connect](https://nango.dev/docs/api-integrations/workday-oauth/connect.md) | [setup](https://nango.dev/docs/api-integrations/workday-oauth/how-to-register-your-own-workday-oauth-app.md) | hr | | `workday-refresh-token` | Workday (Refresh Token Auth) | TWO_STEP | [docs](https://nango.dev/docs/api-integrations/workday-refresh-token.md) | [connect](https://nango.dev/docs/api-integrations/workday-refresh-token/connect.md) | | hr | | `workos` | WorkOS | API_KEY | [docs](https://nango.dev/docs/api-integrations/workos.md) | [connect](https://nango.dev/docs/api-integrations/workos/connect.md) | | productivity, iam | | `workpath` | Workpath | API_KEY | [docs](https://nango.dev/docs/api-integrations/workpath.md) | [connect](https://nango.dev/docs/api-integrations/workpath/connect.md) | | productivity | | `wrike` | Wrike | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/wrike.md) | | | productivity | | `xai` | xAI | API_KEY | [docs](https://nango.dev/docs/integrations/all/xai.md) | [connect](https://nango.dev/docs/integrations/all/xai/connect.md) | | productivity, dev-tools | | `xero` | Xero | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/xero.md) | | [setup](https://nango.dev/docs/api-integrations/xero/how-to-register-your-own-xero-api-oauth-app.md) | accounting, popular | | `xero-oauth2-cc` | Xero (Client Credentials) | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/xero-oauth2-cc.md) | [connect](https://nango.dev/docs/integrations/all/xero-oauth2-cc/connect.md) | | accounting | | `yahoo` | Yahoo | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/yahoo.md) | | | social | | `yandex` | Yandex | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/yandex.md) | | | social | | `yotpo` | Yotpo | TWO_STEP | [docs](https://nango.dev/docs/integrations/all/yotpo.md) | [connect](https://nango.dev/docs/integrations/all/yotpo/connect.md) | | e-commerce | | `youcanbook-me` | YouCanBookMe | BASIC | [docs](https://nango.dev/docs/api-integrations/youcanbook-me.md) | [connect](https://nango.dev/docs/api-integrations/youcanbook-me/connect.md) | | productivity | | `youtube` | YouTube | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/youtube.md) | | [setup](https://nango.dev/docs/api-integrations/youtube/how-to-register-your-own-youtube-api-oauth-app.md) | video | | `zapier` | Zapier | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/zapier.md) | | [setup](https://nango.dev/docs/api-integrations/zapier/how-to-register-your-own-zapier-oauth-app.md) | dev-tools | | `zapier-nla` | Zapier NLA | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zapier-nla.md) | | | productivity | | `zapier-scim` | Zapier (SCIM API) | API_KEY | [docs](https://nango.dev/docs/integrations/all/zapier-scim.md) | [connect](https://nango.dev/docs/integrations/all/zapier-scim/connect.md) | | dev-tools | | `zendesk` | Zendesk | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/zendesk.md) | [connect](https://nango.dev/docs/api-integrations/zendesk/connect.md) | [setup](https://nango.dev/docs/api-integrations/zendesk/how-to-register-your-own-zendesk-api-oauth-app.md) | popular, support, ticketing | | `zendesk-api-key` | Zendesk (API Token) | BASIC | [docs](https://nango.dev/docs/api-integrations/zendesk-api-key.md) | [connect](https://nango.dev/docs/api-integrations/zendesk-api-key/connect.md) | | support, ticketing | | `zendesk-sell` | Zendesk Sell | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zendesk-sell.md) | | | crm | | `zenefits` | Zenefits | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zenefits.md) | | | hr | | `zigpoll` | Zigpoll | API_KEY | [docs](https://nango.dev/docs/api-integrations/zigpoll.md) | [connect](https://nango.dev/docs/api-integrations/zigpoll/connect.md) | | marketing | | `zoho` | Zoho | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho.md) | [connect](https://nango.dev/docs/integrations/all/zoho/connect.md) | | accounting | | `zoho-bigin` | Zoho Bigin | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-bigin.md) | [connect](https://nango.dev/docs/integrations/all/zoho-bigin/connect.md) | | crm | | `zoho-books` | Zoho Books | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-books.md) | [connect](https://nango.dev/docs/integrations/all/zoho-books/connect.md) | | accounting | | `zoho-calendar` | Zoho Calendar | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-calendar.md) | [connect](https://nango.dev/docs/integrations/all/zoho-calendar/connect.md) | | productivity | | `zoho-crm` | Zoho CRM | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/zoho-crm.md) | [connect](https://nango.dev/docs/api-integrations/zoho-crm/connect.md) | [setup](https://nango.dev/docs/api-integrations/zoho-crm/how-to-register-your-own-zoho-crm-api-oauth-app.md) | crm | | `zoho-desk` | Zoho Desk | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-desk.md) | [connect](https://nango.dev/docs/integrations/all/zoho-desk/connect.md) | | support, ticketing | | `zoho-inventory` | Zoho Inventory | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-inventory.md) | [connect](https://nango.dev/docs/integrations/all/zoho-inventory/connect.md) | | e-commerce | | `zoho-invoice` | Zoho Invoice | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-invoice.md) | [connect](https://nango.dev/docs/integrations/all/zoho-invoice/connect.md) | | invoicing | | `zoho-mail` | Zoho Mail | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-mail.md) | [connect](https://nango.dev/docs/integrations/all/zoho-mail/connect.md) | | productivity, communication | | `zoho-people` | Zoho People | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-people.md) | [connect](https://nango.dev/docs/integrations/all/zoho-people/connect.md) | | hr | | `zoho-recruit` | Zoho Recruit | OAUTH2 | [docs](https://nango.dev/docs/integrations/all/zoho-recruit.md) | [connect](https://nango.dev/docs/integrations/all/zoho-recruit/connect.md) | | ats | | `zoom` | Zoom | OAUTH2 | [docs](https://nango.dev/docs/api-integrations/zoom.md) | | [setup](https://nango.dev/docs/api-integrations/zoom/how-to-register-your-own-zoom-api-oauth-app.md) | video | | `zoominfo` | ZoomInfo | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/zoominfo.md) | [connect](https://nango.dev/docs/integrations/all/zoominfo/connect.md) | | crm, marketing | | `zorus` | Zorus | API_KEY | [docs](https://nango.dev/docs/api-integrations/zorus.md) | [connect](https://nango.dev/docs/api-integrations/zorus/connect.md) | | other | | `zuora` | Zuora | OAUTH2_CC | [docs](https://nango.dev/docs/integrations/all/zuora.md) | [connect](https://nango.dev/docs/integrations/all/zuora/connect.md) | | erp | ## Resources - [Blog](https://nango.dev/blog): Articles, product updates, and integration deep-dives from the Nango team. - [Pricing](https://nango.dev/pricing): Plans and pricing for Nango Cloud. - [Talk to the team](https://nango.dev/demo): Book a call with Nango for sales, onboarding, or technical questions. - [Community Slack](https://nango.dev/slack): Get help, share feedback, and connect with other Nango developers.