To integrate Basecamp with V0 by Vercel, set up an OAuth2 flow to authenticate users with their Basecamp accounts, then fetch projects, to-dos, and messages through Next.js API routes. Store your Basecamp OAuth credentials in Vercel environment variables and use V0 to generate the project management dashboard UI.
Build a Custom Basecamp Dashboard with V0 and Next.js
Basecamp's REST API (version 4 as of early 2026) exposes all of its core functionality — projects (called 'campfires' internally), to-do lists, messages, schedules, and files — through a well-documented JSON API. Combined with V0-generated React components, you can build custom views of Basecamp data that are tailored specifically to your team's workflow, rather than being limited to Basecamp's own interface.
The most common use case is a unified dashboard that pulls together information from multiple Basecamp projects in one view — something Basecamp's native app does not offer. Marketing teams might want to see all pending to-dos across every project, engineering managers might want a cross-project status board, or a client-facing portal might show project progress without exposing the full Basecamp admin interface.
Basecamp uses OAuth2 for API access, which requires registering your app in the Basecamp developer portal and implementing the authorization code flow. This is slightly more complex than API key authentication but allows real users to authorize your app with their own Basecamp accounts — essential for multi-user tools. For single-developer integrations or internal tools where you are the only user, you can also use personal access tokens to skip the full OAuth flow.
Integration method
Basecamp uses OAuth2 for authentication, which means users authorize your app to access their Basecamp account. Your Next.js API routes handle the OAuth2 flow (authorization, token exchange, token refresh) and then use the access token to call the Basecamp API on the user's behalf. All tokens are stored server-side in environment variables or a database, never in the browser.
Prerequisites
- A Basecamp account (Basecamp 4 is the current version — ensure your account is on Basecamp 4 or Basecamp 3, as the API differs between versions)
- A registered Basecamp OAuth2 application — register at integrate.37signals.com to get a Client ID and Client Secret
- A V0 account at v0.dev and a Vercel account for deployment
- Your application's redirect URI configured in the 37signals integration settings (your Vercel deployment URL + /api/auth/callback/basecamp)
Step-by-step guide
Register Your App and Generate the Dashboard UI
Register Your App and Generate the Dashboard UI
Before writing any code, register your application with 37signals (the company behind Basecamp) at integrate.37signals.com. Click 'Register your application,' fill in your app name, your website URL, and your redirect URI. The redirect URI should be the URL of your Vercel deployment plus /api/basecamp/callback — for example, https://your-app.vercel.app/api/basecamp/callback. For local development, also add http://localhost:3000/api/basecamp/callback as an additional redirect URI. After registering, 37signals provides a Client ID and Client Secret — save both securely. Now open V0 and generate the dashboard UI. Describe the layout, the data you want to display, and the filters you need. Be specific about project cards, to-do list items, and navigation — V0 produces better results with detailed prompts. Push the generated code to GitHub via the V0 Git panel.
Create a project management dashboard with a top nav showing the app name and a user avatar with logout link. A left sidebar lists Basecamp projects with colored icons. The main content area shows the selected project's details: a header with project name and description, three metric cards for Open To-Dos, Completed This Week, and Team Members, and a table of recent to-dos with columns for Task, Assignee, Due Date, and Status. Add a loading state and an auth-required state for users who have not connected their Basecamp account.
Paste this in V0 chat
Pro tip: V0 will generate the UI without the auth logic — that is normal. Build the static UI first, then layer in the OAuth flow and data fetching. This makes debugging much easier since you can validate the UI before adding auth complexity.
Expected result: A project management dashboard renders in the V0 preview with sidebar, metric cards, and to-do table using mock data. The UI is ready for real Basecamp data once the OAuth flow is implemented.
Implement the Basecamp OAuth2 Flow
Implement the Basecamp OAuth2 Flow
The Basecamp API uses OAuth2 with the authorization code flow. This requires two API routes: one that redirects users to Basecamp to authorize your app, and one callback route that exchanges the authorization code for an access token. The Basecamp OAuth endpoints are at launchpad.37signals.com. The authorization URL is https://launchpad.37signals.com/authorization/new and the token exchange endpoint is https://launchpad.37signals.com/authorization/token. After a successful token exchange, Basecamp returns an access token, a refresh token, and a list of the user's accounts (they may belong to multiple Basecamp organizations). Store the access token and refresh token in a secure cookie or server-side session — never in localStorage or a non-HttpOnly cookie. For production apps, use a database like Neon (Vercel Marketplace) to persist tokens across serverless function invocations.
1// app/api/basecamp/auth/route.ts — redirect to Basecamp for authorization2import { NextResponse } from 'next/server';34export async function GET() {5 const clientId = process.env.BASECAMP_CLIENT_ID;6 const redirectUri = process.env.BASECAMP_REDIRECT_URI; // e.g. https://yourapp.vercel.app/api/basecamp/callback78 if (!clientId || !redirectUri) {9 return NextResponse.json({ error: 'Basecamp OAuth not configured' }, { status: 500 });10 }1112 const authUrl = new URL('https://launchpad.37signals.com/authorization/new');13 authUrl.searchParams.set('type', 'web_server');14 authUrl.searchParams.set('client_id', clientId);15 authUrl.searchParams.set('redirect_uri', redirectUri);1617 return NextResponse.redirect(authUrl.toString());18}Pro tip: Basecamp's OAuth token response includes an 'expires_in' field. The access token expires after a set time (typically 2 weeks). Store the expiry timestamp alongside the token and implement refresh logic in your API routes to renew the token automatically when it expires.
Expected result: Visiting /api/basecamp/auth in the browser redirects users to the Basecamp authorization page at launchpad.37signals.com, where they can grant your app permission to access their account.
Create the Token Callback and Basecamp Data Routes
Create the Token Callback and Basecamp Data Routes
After the user authorizes your app on Basecamp's OAuth page, Basecamp redirects them back to your /api/basecamp/callback route with an authorization code. This callback route exchanges the code for an access token and stores it, then redirects the user to your dashboard. Once you have the access token, create a general Basecamp API proxy route that fetches projects, to-dos, messages, and other resources. All Basecamp API requests must include the Authorization: Bearer header with the access token and a User-Agent header identifying your app (required by 37signals). Add your Vercel environment variables: BASECAMP_CLIENT_ID, BASECAMP_CLIENT_SECRET, and BASECAMP_REDIRECT_URI. For a simpler personal integration, you can skip the full OAuth flow and use a personal access token from Basecamp's profile settings.
1// app/api/basecamp/callback/route.ts — exchange code for token2import { NextRequest, NextResponse } from 'next/server';3import { cookies } from 'next/headers';45export async function GET(request: NextRequest) {6 const { searchParams } = new URL(request.url);7 const code = searchParams.get('code');89 if (!code) {10 return NextResponse.json({ error: 'No authorization code received' }, { status: 400 });11 }1213 const tokenResponse = await fetch('https://launchpad.37signals.com/authorization/token', {14 method: 'POST',15 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },16 body: new URLSearchParams({17 type: 'web_server',18 client_id: process.env.BASECAMP_CLIENT_ID ?? '',19 client_secret: process.env.BASECAMP_CLIENT_SECRET ?? '',20 redirect_uri: process.env.BASECAMP_REDIRECT_URI ?? '',21 code,22 }),23 });2425 if (!tokenResponse.ok) {26 return NextResponse.json({ error: 'Token exchange failed' }, { status: 400 });27 }2829 const tokenData = await tokenResponse.json();30 const cookieStore = cookies();31 cookieStore.set('basecamp_token', tokenData.access_token, {32 httpOnly: true,33 secure: process.env.NODE_ENV === 'production',34 maxAge: 60 * 60 * 24 * 14, // 14 days35 path: '/',36 });3738 return NextResponse.redirect(new URL('/dashboard', request.url));39}Pro tip: The Basecamp API requires your app to use the account ID in all API URLs. After the OAuth token exchange, make a GET request to https://launchpad.37signals.com/authorization.json with the access token to fetch the user's accounts and their IDs — you need this ID to construct all subsequent API URLs.
Expected result: After a user completes the Basecamp OAuth flow, they are redirected to your dashboard URL and an access token is stored in an HttpOnly cookie. Subsequent API route calls can read this token from the cookie to make authenticated Basecamp requests.
Add Vercel Environment Variables and Deploy
Add Vercel Environment Variables and Deploy
Add all required environment variables to your Vercel project. Open Vercel Dashboard → your project → Settings → Environment Variables and add the following: BASECAMP_CLIENT_ID (from integrate.37signals.com), BASECAMP_CLIENT_SECRET (same source, keep this secret), and BASECAMP_REDIRECT_URI (your Vercel deployment URL + /api/basecamp/callback, e.g. https://your-app.vercel.app/api/basecamp/callback). If you are also adding this redirect URI for preview deployments, you can add a Preview-environment-specific BASECAMP_REDIRECT_URI that uses your Vercel preview URL pattern. After adding the variables, trigger a redeployment. Once deployed, click the 'Connect Basecamp' button (or navigate to /api/basecamp/auth) to test the full OAuth flow. After authorizing, your dashboard should fetch and display your real Basecamp projects and to-dos.
Add a 'Connect Basecamp' button to the auth-required state of the dashboard that links to /api/basecamp/auth. When the user returns from Basecamp OAuth (check for a connected state), automatically fetch projects from /api/basecamp/projects and populate the sidebar. Show a success notification 'Basecamp connected successfully' after the OAuth redirect.
Paste this in V0 chat
1# .env.local (do NOT commit)2BASECAMP_CLIENT_ID=your_client_id_here3BASECAMP_CLIENT_SECRET=your_client_secret_here4BASECAMP_REDIRECT_URI=http://localhost:3000/api/basecamp/callbackPro tip: Basecamp's 37signals API documentation is at github.com/basecamp/bc3-api. The API URL pattern is https://3.basecampapi.com/{account_id}/projects.json for Basecamp 3/4. Replace {account_id} with the numeric account ID from the authorization response.
Expected result: The fully deployed app shows a 'Connect Basecamp' button for new users. Clicking it initiates the OAuth flow. After authorizing, users land on the dashboard with their real Basecamp projects displayed in the sidebar and their to-dos in the main content area.
Common use cases
Cross-Project To-Do Dashboard
Display all open to-dos across multiple Basecamp projects in a single prioritized view. Team members can see their assignments without switching between project-by-project views in Basecamp, and managers get a bird's-eye view of all outstanding work.
Create a to-do dashboard with a sidebar listing Basecamp projects, a main area showing to-dos filtered by the selected project with columns for task name, assigned person, and due date. Add filter buttons for All, Overdue, Due Today, and Upcoming. Use a checkbox to mark tasks complete. Fetch projects from /api/basecamp/projects and to-dos from /api/basecamp/todos.
Copy this prompt to try it in V0
Client-Facing Project Progress Portal
Build a read-only progress portal that clients can access to see the status of their project without needing a Basecamp account. Show milestones, completed to-dos, and recent messages without exposing the full project administration interface.
Build a client portal page showing a project header with name and start date, a progress bar based on completed vs total to-dos, a list of recent messages with author and date, and a milestone timeline. Keep the design clean and professional. Fetch from /api/basecamp/project/{id}/overview.
Copy this prompt to try it in V0
Team Activity Feed
Create a real-time feed of recent activity across all Basecamp projects — new to-dos added, messages posted, and files uploaded. This is useful for managers who need to stay informed about team activity without checking each project individually.
Design an activity feed page with a timeline of recent events, each showing an avatar, event description ('John added a to-do in Marketing Project'), timestamp, and a link to the item. Group events by day. Add a project filter dropdown. Fetch from /api/basecamp/events.
Copy this prompt to try it in V0
Troubleshooting
OAuth redirect returns 'redirect_uri_mismatch' error from Basecamp
Cause: The redirect URI in your OAuth request does not exactly match the URI registered in the 37signals integration settings. Even a trailing slash difference will cause this error.
Solution: Go to integrate.37signals.com, find your app, and verify the registered redirect URI exactly matches BASECAMP_REDIRECT_URI in your environment variables — including https:// vs http://, the exact path, and no trailing slash. For Vercel preview deployments, add a separate redirect URI for the preview domain.
API requests return 401 even with a valid access token
Cause: The access token has expired (Basecamp tokens expire after approximately 2 weeks), or the Authorization header format is incorrect.
Solution: Implement token refresh logic using the refresh token stored alongside the access token. Check that your Authorization header is formatted as 'Bearer {access_token}' exactly — no extra spaces or characters. Verify the token has not expired by checking the stored expiry timestamp.
1// Correct Authorization header format2const response = await fetch(basecampUrl, {3 headers: {4 'Authorization': `Bearer ${accessToken}`,5 'User-Agent': 'YourAppName (your@email.com)', // required by 37signals6 'Content-Type': 'application/json',7 },8});Basecamp API returns 'User-Agent required' or 403 error on all requests
Cause: 37signals requires a descriptive User-Agent header on all API requests. Requests without it are rejected.
Solution: Add a User-Agent header to all Basecamp API requests with your app name and contact email in the format 'YourAppName (contact@email.com)'. This is a firm requirement from 37signals, not optional.
1headers: {2 'Authorization': `Bearer ${token}`,3 'User-Agent': 'MyBasecampDashboard (developer@example.com)',4}Best practices
- Store Basecamp OAuth tokens in HttpOnly secure cookies or a server-side database — never in localStorage, sessionStorage, or non-HttpOnly cookies. HttpOnly cookies are inaccessible to JavaScript and prevent token theft via XSS attacks.
- Implement token refresh proactively: store the token expiry time alongside the access token and check it before each API request. Refresh the token before it expires rather than handling 401 errors reactively.
- Always include the User-Agent header on every Basecamp API request — 37signals enforces this and will reject requests without it. Use a descriptive name and contact email in the format required by 37signals API documentation.
- Cache Basecamp API responses for data that changes infrequently, like project lists and to-do counts. Use Vercel KV (Upstash Redis) with a 60-second TTL to avoid hammering the Basecamp API on every dashboard page load.
- Use the account ID from the authorization response when constructing Basecamp API URLs. Users may belong to multiple Basecamp accounts — always use the correct account ID for the selected account.
- Handle Basecamp API rate limits gracefully. The API is rate-limited and will return 429 responses during heavy usage. Add retry logic with exponential backoff for rate limit errors.
- For internal tools where only you use the app, consider using Basecamp's personal access tokens (from your Basecamp profile settings) instead of implementing the full OAuth2 flow — it is much simpler and does not require registering an OAuth app.
Alternatives
Teamwork offers traditional project management with Gantt charts and time tracking — better if your team needs milestone-based planning rather than Basecamp's conversational project approach.
Flock is a lighter team communication tool that is easier to integrate if you primarily need notifications and messaging rather than full project management data.
Microsoft Teams is the better choice for enterprise organizations already in the Microsoft 365 ecosystem who need project management alongside video calls and file sharing.
Doodle is a simpler integration if your primary need is team scheduling and meeting coordination rather than full project management workflows.
Frequently asked questions
Which version of the Basecamp API should I use?
Use the Basecamp 4 API (also referred to as Basecamp 3 API in many places — the API URL uses 3.basecampapi.com). The documentation is at github.com/basecamp/bc3-api. Avoid the legacy Basecamp 2 API if your account is on Basecamp 3 or 4.
Do I need a paid Basecamp account to use the API?
Yes, the Basecamp API requires an active paid Basecamp subscription. There is a 30-day free trial available, but after that you need a paid plan ($15/month for Basecamp Personal or $299/month for Basecamp for Business) to continue using the API.
Can I use Basecamp API without OAuth if I am just building a personal tool?
Yes. If you are the only user of your V0 app and want to skip the OAuth flow, you can generate a personal access token from your Basecamp profile settings. Use this token directly in your API routes as a Bearer token. This is much simpler than OAuth2 but only works for a single user.
How do I get the Basecamp account ID for API requests?
After completing the OAuth flow, make a GET request to https://launchpad.37signals.com/authorization.json with the access token in the Authorization header. The response includes an 'accounts' array. Each account has an 'id' field — this is your Basecamp account ID needed for all API endpoint URLs.
Can I write data back to Basecamp, or is this read-only?
The Basecamp API supports both read and write operations. You can create to-dos, post messages, upload files, create comments, and update task status via POST, PUT, and DELETE requests. Your Next.js API routes can accept POST requests from your V0 components and forward them to the appropriate Basecamp write endpoints.
What is the difference between integrating Basecamp vs Teamwork?
Basecamp is an opinionated all-in-one tool with a strong philosophy about how projects should be managed — flat structure, async-first, minimal notifications. Teamwork offers more traditional project management features like time tracking, budget tracking, and Gantt charts. Choose Basecamp if your team already uses it; choose Teamwork if you need more structured PM workflows.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation