Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Teamwork with V0

To integrate Teamwork with a V0 by Vercel app, create a Next.js API route that calls the Teamwork REST API using HTTP Basic authentication with your API token. Generate your project management dashboard UI with V0, then connect it to API routes that fetch projects, tasks, milestones, and time entries from Teamwork. Store your API token and site name in Vercel environment variables to keep credentials secure.

What you'll learn

  • How to generate a PM dashboard UI with task tables and time tracking views using V0
  • How to authenticate to the Teamwork REST API using HTTP Basic auth with your API token
  • How to fetch projects, tasks, milestones, and time entries from Teamwork's v3 API
  • How to store Teamwork credentials securely as Vercel environment variables
  • How to build a real-time task status board driven by Teamwork data
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate13 min read45 minutesProductivityMarch 2026RapidDev Engineering Team
TL;DR

To integrate Teamwork with a V0 by Vercel app, create a Next.js API route that calls the Teamwork REST API using HTTP Basic authentication with your API token. Generate your project management dashboard UI with V0, then connect it to API routes that fetch projects, tasks, milestones, and time entries from Teamwork. Store your API token and site name in Vercel environment variables to keep credentials secure.

Build Custom Project Management Dashboards Powered by Teamwork

Teamwork is the go-to project management platform for agencies, consultancies, and client services businesses. With robust task management, Gantt charts, time tracking, and invoicing built in, it handles the full project lifecycle. But Teamwork's interface can feel overwhelming for clients who just need to see project status, or too limited for internal teams that want custom cross-project views combining Teamwork data with metrics from other tools.

The Teamwork REST API v3 provides comprehensive access to all Teamwork data: projects, task lists, tasks, milestones, time entries, comments, files, and people. Your V0-generated Next.js app can surface exactly the data your team or clients need — a focused client status page showing only completed deliverables, a cross-project resource dashboard showing each person's task load, or a weekly billing report aggregating time entries.

Authentication is straightforward: Teamwork uses HTTP Basic auth where the username is your API token and the password is literally the letter 'x'. This means no OAuth flows to implement — just store your token in a Vercel environment variable and include it in every API route request.

Integration method

Next.js API Route

Teamwork integrates with V0 apps through Next.js API routes that authenticate to the Teamwork v3 REST API using HTTP Basic auth with your API token. The API route fetches projects, tasks, time entries, and milestones, transforming the data into a frontend-friendly shape before returning it to your V0-generated dashboard components. All API calls happen server-side so your Teamwork credentials never appear in the browser.

Prerequisites

  • A Teamwork account with projects and tasks set up (any plan including free trial)
  • Your Teamwork API token — available in Teamwork under your Profile → API Keys
  • Your Teamwork site name (the subdomain from your Teamwork URL, e.g., 'yourcompany' from yourcompany.teamwork.com)
  • A V0 project exported to GitHub and deployed on Vercel
  • Basic understanding of Next.js API routes and REST API calls

Step-by-step guide

1

Generate the Dashboard UI with V0

Use V0 to generate your project management dashboard UI before connecting it to real data. Describe the specific views you need — task tables, project cards, time tracking summaries — with TypeScript interfaces that match the Teamwork API response shapes. For a standard agency PM dashboard, you'll typically want three views: an all-projects overview showing project health and milestone progress, a task detail view with filtering and sorting, and a time entries report. Ask V0 to generate these as separate route components that share a navigation sidebar. Describe the data shapes explicitly in your V0 prompt so it generates correct TypeScript interfaces. A Teamwork task has: `id`, `name`, `status` (new/inprogress/completed), `assignedTo` array, `dueDate`, `priority` (none/low/medium/high), `estimatedTime`, and `loggedTime`. A project has `id`, `name`, `status`, `startDate`, `endDate`, and `completedTaskCount`/`totalTaskCount`. Ask V0 to include visual indicators for task priority (colored dots or badges), overdue status (red date badge when past due date), and project completion percentage (progress bar). These visual elements make PM dashboards useful at a glance without requiring users to read every number.

V0 Prompt

Create a project management dashboard with a left sidebar showing project list with colored status dots. Main area: a task table with columns for task name, assignee, due date (red if overdue), priority badge (none/low/medium/high color-coded), status pill, and logged hours. Add a summary row at top showing total tasks, completed, in progress, overdue counts. Include column sorting and a search filter above the table.

Paste this in V0 chat

Pro tip: Ask V0 to generate the task table with server-side data in mind — no client-side sorting logic that might break when paginating. Define the sort/filter parameters as URL query params so they can be passed through to the API route.

Expected result: A styled PM dashboard with task table, project sidebar, status badges, and summary metrics using realistic placeholder data.

2

Create the Teamwork API Routes

Create a Next.js API route at `app/api/teamwork/route.ts` that authenticates to Teamwork's v3 REST API using HTTP Basic auth. The authentication format is straightforward: encode `{YOUR_API_TOKEN}:x` (literal 'x' as password) in Base64 and pass it as the Authorization header. Teamwork's API v3 base URL format is `https://{your-site}.teamwork.com/projects/api/v3/` — replace `{your-site}` with your Teamwork subdomain. Store this in an environment variable as `TEAMWORK_SITE` (just the subdomain, e.g., 'yourcompany'). Key Teamwork v3 API endpoints: - `GET /projects.json` — list all projects with stats - `GET /tasks.json` — list tasks (supports `projectId`, `assignedTo`, `status` filters) - `GET /projects/{id}/tasks.json` — tasks for a specific project - `GET /time/entries.json` — time entries (supports `startDate`, `endDate`, `projectId`) - `GET /projects/{id}/milestones.json` — milestones for a project Teamwork's API returns paginated results with a default page size of 50. Include `page` and `pageSize` parameters in your API route to support pagination. The response includes a `meta.page` object with `pageOffset`, `pageSize`, and `count` fields. For your dashboard's main query patterns, the most useful endpoints are tasks with assigned-to and project filters for the task board view, and time entries with date range filters for the reporting view.

app/api/teamwork/route.ts
1// app/api/teamwork/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4const SITE = process.env.TEAMWORK_SITE!; // e.g., 'yourcompany'
5const TOKEN = process.env.TEAMWORK_API_TOKEN!;
6const BASE_URL = `https://${SITE}.teamwork.com/projects/api/v3`;
7
8function getAuthHeader() {
9 return 'Basic ' + Buffer.from(`${TOKEN}:x`).toString('base64');
10}
11
12async function teamworkFetch(endpoint: string, params?: Record<string, string>) {
13 const url = new URL(`${BASE_URL}${endpoint}`);
14 if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
15
16 const res = await fetch(url.toString(), {
17 headers: {
18 Authorization: getAuthHeader(),
19 'Content-Type': 'application/json',
20 },
21 next: { revalidate: 60 }, // cache for 1 minute
22 });
23
24 if (!res.ok) throw new Error(`Teamwork API error: ${res.status}`);
25 return res.json();
26}
27
28export async function GET(request: NextRequest) {
29 const { searchParams } = new URL(request.url);
30 const resource = searchParams.get('resource');
31 const projectId = searchParams.get('projectId') || '';
32 const startDate = searchParams.get('startDate') || '';
33 const endDate = searchParams.get('endDate') || '';
34 const page = searchParams.get('page') || '1';
35
36 try {
37 switch (resource) {
38 case 'projects': {
39 const data = await teamworkFetch('/projects.json', {
40 includeStats: 'true',
41 page,
42 pageSize: '50',
43 });
44 return NextResponse.json(data);
45 }
46 case 'tasks': {
47 const params: Record<string, string> = { page, pageSize: '100' };
48 if (projectId) params.projectId = projectId;
49 const data = await teamworkFetch('/tasks.json', params);
50 return NextResponse.json(data);
51 }
52 case 'time': {
53 const params: Record<string, string> = {};
54 if (projectId) params.projectId = projectId;
55 if (startDate) params.startDate = startDate;
56 if (endDate) params.endDate = endDate;
57 const data = await teamworkFetch('/time/entries.json', params);
58 return NextResponse.json(data);
59 }
60 default:
61 return NextResponse.json({ error: 'resource must be: projects, tasks, or time' }, { status: 400 });
62 }
63 } catch (err: any) {
64 return NextResponse.json({ error: err.message }, { status: 500 });
65 }
66}

Pro tip: Teamwork's v1 API (the older /tasks.json format) and v3 API (/projects/api/v3/) use different URL schemes. The v3 API is the current standard — use it unless you're working with an existing v1 integration.

Expected result: A functional API route that returns projects, tasks, and time entries from Teamwork using Basic authentication.

3

Add Vercel Environment Variables

Open your Vercel project dashboard and navigate to Settings → Environment Variables. Add two required variables. First, add `TEAMWORK_API_TOKEN` — get this from your Teamwork account by going to your profile picture in the top-right corner → Edit my Details → API & Mobile → scroll down to 'Your API Token'. Click to reveal the token and copy it. This is a long alphanumeric string. Set this as a server-only variable (no NEXT_PUBLIC_ prefix). Second, add `TEAMWORK_SITE` — this is your Teamwork subdomain. If your Teamwork URL is `yourcompany.teamwork.com`, the value is `yourcompany`. If you're using a custom domain or Teamwork Desk, check with your Teamwork administrator for the correct subdomain value. Both variables should be set for the Production environment. If you want to test against a Teamwork staging or sandbox project, you can create a separate Teamwork account for development and set different values for the Preview environment. After adding environment variables, trigger a redeployment. In Vercel Dashboard, go to Deployments → click the three dots next to your latest deployment → Redeploy. Or push a new commit to trigger an automatic deployment. Verify the integration works by visiting your deployed URL and navigating to a test page that calls `/api/teamwork?resource=projects` — you should see your actual Teamwork projects in the JSON response.

Pro tip: Teamwork generates one API token per user. If you want the dashboard to show data visible to a specific person (e.g., a project manager), generate the token from that person's Teamwork account rather than an admin account.

Expected result: TEAMWORK_API_TOKEN and TEAMWORK_SITE visible in Vercel environment variables, with a successful test call to /api/teamwork?resource=projects returning your actual projects.

4

Connect Dashboard Components to the API

Update the V0-generated dashboard components to fetch from your Teamwork API routes. Start with the projects overview — fetch `/api/teamwork?resource=projects` and map the response to your project card components. Teamwork's v3 API returns project data nested under a `projects` key, with each project having `stats` for task counts. Map these to your UI like this: `stats.tasksCounts.completed` for completed tasks, `stats.tasksCounts.active` for in-progress tasks, and calculate completion percentage as `(completed / total) * 100`. For the task table, the tasks endpoint returns tasks under a `tasks` key. Each task has `assignees` (array of user objects), `dueDate`, `priority`, `status` ('new', 'inprogress', 'completed'), and `timeEstimatesSum` for estimated hours. Map these to table rows with priority color coding. For time entries, fetch `/api/teamwork?resource=time&startDate=2026-01-01&endDate=2026-01-31` (pass current week dates dynamically). Time entries return under a `timeEntries` key with `hours`, `minutes`, `billable`, `projectId`, and `person` fields. Group by project on the client side using `Array.reduce()` to build your project subtotals. Implement optimistic filtering in the UI by storing the full task array in state and filtering/sorting client-side. For large Teamwork accounts with thousands of tasks, add server-side filtering by passing `projectId` or `assignedTo` query params and fetching on filter change rather than loading everything upfront.

V0 Prompt

Connect the dashboard to the Teamwork API. Fetch /api/teamwork?resource=projects on load and display project cards with completion percentages. Fetch /api/teamwork?resource=tasks and display in the task table with assignee names, due dates colored red if overdue (before today), and priority badges. Add a project selector that re-fetches tasks with ?projectId=X when changed. Show total logged hours per project in the project cards.

Paste this in V0 chat

components/TaskTable.tsx
1// Example: fetching and displaying Teamwork tasks
2'use client';
3import { useEffect, useState } from 'react';
4
5interface Task {
6 id: number;
7 name: string;
8 status: string;
9 dueDate: string | null;
10 priority: string;
11 assignees: { firstName: string; lastName: string }[];
12}
13
14export function TaskTable({ projectId }: { projectId?: string }) {
15 const [tasks, setTasks] = useState<Task[]>([]);
16 const [loading, setLoading] = useState(true);
17
18 useEffect(() => {
19 const params = projectId ? `&projectId=${projectId}` : '';
20 fetch(`/api/teamwork?resource=tasks${params}`)
21 .then(r => r.json())
22 .then(data => { setTasks(data.tasks || []); setLoading(false); });
23 }, [projectId]);
24
25 const isOverdue = (dueDate: string | null) =>
26 dueDate && new Date(dueDate) < new Date() && status !== 'completed';
27
28 if (loading) return <div>Loading tasks...</div>;
29
30 return (
31 <table className="w-full text-sm">
32 <thead>
33 <tr className="border-b">
34 <th className="text-left p-2">Task</th>
35 <th className="text-left p-2">Assignee</th>
36 <th className="text-left p-2">Due Date</th>
37 <th className="text-left p-2">Priority</th>
38 <th className="text-left p-2">Status</th>
39 </tr>
40 </thead>
41 <tbody>
42 {tasks.map(task => (
43 <tr key={task.id} className="border-b hover:bg-muted/50">
44 <td className="p-2">{task.name}</td>
45 <td className="p-2">{task.assignees.map(a => `${a.firstName} ${a.lastName}`).join(', ')}</td>
46 <td className={`p-2 ${isOverdue(task.dueDate) ? 'text-red-600 font-medium' : ''}`}>
47 {task.dueDate || '—'}
48 </td>
49 <td className="p-2">
50 <span className={`px-2 py-0.5 rounded text-xs font-medium ${
51 task.priority === 'high' ? 'bg-red-100 text-red-700' :
52 task.priority === 'medium' ? 'bg-yellow-100 text-yellow-700' : 'bg-gray-100 text-gray-600'
53 }`}>{task.priority}</span>
54 </td>
55 <td className="p-2">{task.status}</td>
56 </tr>
57 ))}
58 </tbody>
59 </table>
60 );
61}

Pro tip: Teamwork returns date strings in ISO format (YYYY-MM-DD). Compare with new Date() using date-only comparison to avoid timezone issues with overdue detection.

Expected result: The dashboard displays real Teamwork projects and tasks with correct overdue highlighting, priority badges, and assignee names.

Common use cases

Client Project Status Dashboard

Build a read-only dashboard for clients to see their project's progress — milestone completion, recent task updates, and upcoming deliverables — without giving them access to full Teamwork data including internal notes and billing.

V0 Prompt

Create a client project dashboard with: (1) a progress bar showing completed vs total milestones, (2) a 'Recent Completions' list of the last 5 completed tasks with completion date, (3) an 'Upcoming Deadlines' section showing tasks due in the next 7 days, (4) a project health indicator (green/yellow/red) based on overdue task count.

Copy this prompt to try it in V0

Team Task Board with Time Tracking

Generate a Kanban-style task board grouped by assignee or project phase, with time logged per task displayed alongside each card. Team members can see their task queue and logged hours across all active projects in one view.

V0 Prompt

Build a Kanban board with columns for New, In Progress, In Review, and Completed. Each task card shows: task title, project name, assignee avatar, due date badge, priority indicator, and hours logged badge. Add a filter bar at top to filter by project or assignee. Make the board scrollable with sticky column headers.

Copy this prompt to try it in V0

Weekly Time Entry Report

Create a time tracking report dashboard that aggregates Teamwork time entries by project and person for the current week. Shows billable vs non-billable hours, top time-consuming projects, and individual utilization rates for agency billing.

V0 Prompt

Build a weekly time report page with: (1) a summary row showing total hours, billable hours, and utilization percentage, (2) a table of time entries grouped by project with subtotals, (3) a bar chart comparing hours by team member, (4) a date range picker to view different weeks. Use recharts for the bar chart.

Copy this prompt to try it in V0

Troubleshooting

API returns 401 Unauthorized despite correct API token

Cause: The Authorization header format may be incorrect. Teamwork requires Base64-encoded 'token:x' — the password is literally the letter 'x', not your actual password.

Solution: Verify the Authorization header construction: encode `{TOKEN}:x` (with the literal letter x as password) in Base64. Test the token by calling the Teamwork API directly from a browser or curl using: curl -u 'YOUR_TOKEN:x' 'https://yoursite.teamwork.com/projects/api/v3/projects.json'

typescript
1// Correct auth header construction
2const auth = Buffer.from(`${process.env.TEAMWORK_API_TOKEN}:x`).toString('base64');
3const headers = { Authorization: `Basic ${auth}` };

API returns 404 for all endpoints

Cause: The TEAMWORK_SITE environment variable contains the wrong value or includes unnecessary parts of the URL.

Solution: TEAMWORK_SITE should be just the subdomain — e.g., 'yourcompany' from 'yourcompany.teamwork.com'. Do not include 'https://', '.teamwork.com', or trailing slashes. If you're using Teamwork on a custom domain, contact your Teamwork admin for the correct API endpoint URL.

typescript
1// Correct URL construction
2const BASE_URL = `https://${process.env.TEAMWORK_SITE}.teamwork.com/projects/api/v3`;
3// e.g., https://yourcompany.teamwork.com/projects/api/v3

Tasks endpoint returns empty array despite tasks existing in Teamwork

Cause: The tasks endpoint defaults to returning only active (non-completed) tasks unless you specify otherwise, and may filter by project if no projectId is specified.

Solution: Add the status parameter to include all task statuses, or verify which project the tasks belong to. Use `status=all` to return tasks of all statuses.

typescript
1const data = await teamworkFetch('/tasks.json', {
2 projectId,
3 includeCompletedTasks: 'true', // v3 param to include completed
4 pageSize: '250',
5});

Best practices

  • Cache Teamwork API responses with 60-second revalidation — task data changes frequently during workdays but hourly caching is too stale for active PM use
  • Never expose your Teamwork API token in client-side code — always proxy through Next.js API routes
  • Implement project-scoped data fetching rather than loading all tasks — large Teamwork accounts can have thousands of tasks across hundreds of projects
  • Display overdue tasks prominently with red date badges — this is the most actionable information in a PM dashboard
  • Store the Teamwork site subdomain in environment variables rather than hardcoding it — makes the app portable across different Teamwork accounts
  • Use Teamwork's built-in pagination parameters (page, pageSize) and implement infinite scroll or pagination UI for large task lists
  • Show last-updated timestamps on your dashboard so users know when the data was last fetched from Teamwork

Alternatives

Frequently asked questions

Which version of the Teamwork API should I use?

Use Teamwork API v3 (/projects/api/v3/) for new integrations. The v1 API is still accessible but is older and has less consistent response formats. The v3 API uses modern REST conventions with proper pagination metadata and consistent JSON key naming. All Teamwork plans including free provide v3 API access.

Can I create or update tasks via the API, not just read them?

Yes, the Teamwork v3 API supports full CRUD operations. Use POST /tasks.json to create tasks, PUT /tasks/{id}.json to update, and DELETE /tasks/{id}.json to delete. In your Next.js app, create additional API routes that handle POST and PUT requests, passing the Teamwork API the task data from your frontend forms. Always validate the request data server-side before forwarding to Teamwork.

Is there a rate limit on the Teamwork API?

Teamwork enforces rate limits but the specific numbers vary by plan. The API returns a 429 status code with a Retry-After header when you exceed the limit. For dashboard applications that display data to multiple users simultaneously, implement server-side caching with at least 60-second revalidation to reduce API calls. Avoid making one API call per user per page load.

Can multiple users see different projects based on their Teamwork access?

The current API route uses a single API token representing one Teamwork user. All requests see data visible to that user. For a multi-user app where each person sees their own Teamwork data, implement OAuth 2.0 with Teamwork's authorization flow — each user connects their own Teamwork account and you store their individual access tokens. This is significantly more complex but enables personalized views.

How do I show Teamwork time entries billed to clients?

The time entries endpoint (/time/entries.json) includes a `billable` boolean field and `rate` field on each entry. Filter entries by `billable=true` and multiply hours × rate to calculate billable amounts. Group by project to show per-project billing totals. Teamwork's invoicing features within the app are separate from the API and require using the Teamwork UI for invoice generation.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.