--- title: "Nango OAuth with Trigger.dev" sidebarTitle: "Nango OAuth guide" description: "Use Nango to authenticate API calls inside a Trigger.dev task, no token management required." icon: "key" --- [Nango](https://www.nango.dev/) handles OAuth for 250+ APIs, storing and automatically refreshing access tokens on your behalf. This makes it a natural fit for Trigger.dev tasks that need to call third-party APIs on behalf of your users. In this guide you'll build a task that: 1. Receives a Nango `connectionId` from your frontend 2. Fetches a fresh GitHub access token from Nango inside the task 3. Calls the GitHub API to retrieve the user's open pull requests 4. Uses Claude to summarize what's being worked on This pattern works for any API Nango supports. Swap GitHub for HubSpot, Slack, Notion, or any other provider. ## Prerequisites - A Next.js project with [Trigger.dev installed](/guides/frameworks/nextjs) - A [Nango](https://app.nango.dev/) account - An [Anthropic](https://console.anthropic.com/) API key ## How it works ```mermaid sequenceDiagram participant User participant Frontend participant API as Next.js API participant TD as Trigger.dev task participant Nango participant GH as GitHub API participant Claude User->>Frontend: Clicks "Analyze my PRs" Frontend->>API: POST /api/nango-session API->>Nango: POST /connect/sessions (secret key) Nango-->>API: session token (30 min TTL) API-->>Frontend: session token Frontend->>Nango: OAuth connect (frontend SDK + session token) Nango-->>Frontend: connectionId Frontend->>API: POST /api/analyze-prs { connectionId, repo } API->>TD: tasks.trigger(...) TD->>Nango: getConnection(connectionId) Nango-->>TD: access_token TD->>GH: GET /repos/:repo/pulls GH-->>TD: open pull requests TD->>Claude: Summarize PRs Claude-->>TD: Summary ``` ## Step 1: Connect GitHub in Nango 1. In your [Nango dashboard](https://app.nango.dev/), go to **Integrations** and click **Set up new integration**. 2. Search for **GitHub** and select GitHub (User OAuth). 3. Create and add a test connection Install the Nango frontend SDK in your Next.js project: ```bash npm install @nangohq/frontend ``` The frontend SDK requires a short-lived **connect session token** issued by your backend. Add an API route that creates the session: ```ts app/api/nango-session/route.ts import { NextResponse } from "next/server"; export async function POST(req: Request) { const { userId } = await req.json(); if (!userId || typeof userId !== "string") { return NextResponse.json({ error: "Missing or invalid userId" }, { status: 400 }); } const response = await fetch("https://api.nango.dev/connect/sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.NANGO_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ end_user: { id: userId }, }), }); if (!response.ok) { const text = await response.text(); console.error("Nango error:", response.status, text); return NextResponse.json({ error: text }, { status: response.status }); } const { data } = await response.json(); return NextResponse.json({ token: data.token }); } ``` Then add a connect button to your UI that fetches the token and opens the Nango OAuth flow: ```tsx app/page.tsx "use client"; import Nango from "@nangohq/frontend"; export default function Page() { async function connectGitHub() { // Get a short-lived session token from your backend const sessionRes = await fetch("/api/nango-session", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId: "user_123" }), // replace with your actual user ID }); const { token } = await sessionRes.json(); const nango = new Nango({ connectSessionToken: token }); // Use the exact integration slug from your Nango dashboard const result = await nango.auth(""); // result.connectionId is what you pass to your task await fetch("/api/analyze-prs", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ connectionId: result.connectionId, repo: "triggerdotdev/trigger.dev", }), }); } return ; } ``` ## Step 2: Create the Trigger.dev task Install the required packages: ```bash npm install @nangohq/node @anthropic-ai/sdk ``` Create the task: ```ts trigger/analyze-prs.ts import { task } from "@trigger.dev/sdk"; import { Nango } from "@nangohq/node"; import Anthropic from "@anthropic-ai/sdk"; const nango = new Nango({ secretKey: process.env.NANGO_SECRET_KEY! }); const anthropic = new Anthropic(); export const analyzePRs = task({ id: "analyze-prs", run: async (payload: { connectionId: string; repo: string }) => { const { connectionId, repo } = payload; // Fetch a fresh access token from Nango. It handles refresh automatically. // Use the exact integration slug from your Nango dashboard, e.g. "github-getting-started" const connection = await nango.getConnection("", connectionId); if (connection.credentials.type !== "OAUTH2") { throw new Error(`Unexpected credentials type: ${connection.credentials.type}`); } const accessToken = connection.credentials.access_token; // Call the GitHub API on behalf of the user const response = await fetch( `https://api.github.com/repos/${repo}/pulls?state=open&per_page=20`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/vnd.github.v3+json", }, } ); if (!response.ok) { throw new Error(`GitHub API error: ${response.status} ${response.statusText}`); } const prs = await response.json(); if (prs.length === 0) { return { summary: "No open pull requests found.", prCount: 0 }; } // Use Claude to summarize what's being worked on const prList = prs .map( (pr: { number: number; title: string; user: { login: string }; body: string | null }) => `#${pr.number} by @${pr.user.login}: ${pr.title}\n${pr.body?.slice(0, 200) ?? ""}` ) .join("\n\n"); const message = await anthropic.messages.create({ model: "claude-opus-4-6", max_tokens: 1024, messages: [ { role: "user", content: `Here are the open pull requests for ${repo}. Give a concise summary of what's being worked on, grouped by theme where possible.\n\n${prList}`, }, ], }); const summary = message.content[0].type === "text" ? message.content[0].text : ""; return { summary, prCount: prs.length }; }, }); ``` ## Step 3: Create the API route Add a route handler that receives the `connectionId` from your frontend and triggers the task: ```ts app/api/analyze-prs/route.ts import { analyzePRs } from "@/trigger/analyze-prs"; import { NextResponse } from "next/server"; export async function POST(req: Request) { const { connectionId, repo } = await req.json(); if (!connectionId || !repo) { return NextResponse.json({ error: "Missing connectionId or repo" }, { status: 400 }); } const handle = await analyzePRs.trigger({ connectionId, repo }); return NextResponse.json(handle); } ``` ## Step 4: Set environment variables Add the following to your `.env.local` file: ```bash NANGO_SECRET_KEY= # From Nango dashboard → Environment → Secret key TRIGGER_SECRET_KEY= # From Trigger.dev dashboard → API keys ANTHROPIC_API_KEY= # From Anthropic console ``` Add `NANGO_SECRET_KEY` and `ANTHROPIC_API_KEY` as [environment variables](/deploy-environment-variables) in your Trigger.dev project too. These are used inside the task at runtime. ## Test it ```bash npm run dev npx trigger.dev@latest dev ``` Open your app, click **Analyze my PRs**, and complete the GitHub OAuth flow. The task will be triggered automatically. Open your [Trigger.dev dashboard](https://cloud.trigger.dev/) and navigate to **Runs** to see the task execute. You'll see the PR count and Claude's summary in the output. Your task is now fetching a fresh GitHub token from Nango, calling the GitHub API on behalf of the user, and using Claude to summarize their open PRs. No token storage or refresh logic required. ## Next steps - **Reuse the `connectionId`**: Once a user has connected, store their `connectionId` and pass it in future task payloads. No need to re-authenticate. - **Add retries**: If the GitHub API returns a transient error, Trigger.dev [retries](/errors-retrying) will handle it automatically. - **Switch providers**: The same pattern works for any Nango-supported API. Change `"github"` to `"hubspot"`, `"slack"`, `"notion"`, or any other provider. - **Stream the analysis**: Use [Trigger.dev Realtime](/realtime/overview) to stream Claude's response back to your frontend as it's generated.