Skip to main content
RapidDev - Software Development Agency
replit-integrationsStandard API Integration

How to Integrate Replit with Teamwork

To integrate Replit with Teamwork, store your Teamwork API key in Replit Secrets (lock icon πŸ”’) and call the Teamwork REST API from your server-side Node.js or Python code. Teamwork combines project management with built-in time tracking and client billing features. From Replit you can create projects, tasks, milestones, log time entries, and receive webhook events when work items are updated. Use Autoscale deployment for webhook event receivers.

What you'll learn

  • How to store your Teamwork API key securely in Replit Secrets and authenticate with HTTP Basic Auth
  • How to create projects, task lists, and tasks using the Teamwork API from Node.js and Python
  • How to log time entries and retrieve time reports programmatically
  • How to set up Teamwork webhooks to receive task completion and project events
  • How to build automation workflows that keep Teamwork in sync with external tools
Book a free consultation
4.9Clutch rating ⭐
600+Happy partners
17+Countries served
190+Team members
Intermediate17 min read20 minutesProductivityMarch 2026RapidDev Engineering Team
TL;DR

To integrate Replit with Teamwork, store your Teamwork API key in Replit Secrets (lock icon πŸ”’) and call the Teamwork REST API from your server-side Node.js or Python code. Teamwork combines project management with built-in time tracking and client billing features. From Replit you can create projects, tasks, milestones, log time entries, and receive webhook events when work items are updated. Use Autoscale deployment for webhook event receivers.

Project Management and Time Tracking Automation from Replit with Teamwork

Teamwork is purpose-built for agencies and service businesses that need to track both the work being done and the time spent doing it. Unlike general-purpose project management tools, Teamwork includes client-facing features: client portals, invoicing, budget tracking, and profitability reports. This makes it popular for digital agencies, consulting firms, and managed service providers who bill clients by the hour or project. The Teamwork API exposes all of this functionality programmatically β€” from Replit you can automate the parts of project management that are repetitive and error-prone when done manually.

The most valuable integration patterns are project scaffolding (automatically creating project structures, task lists, and initial tasks when a new client is onboarded), time logging (capturing time entries from external tools like code editors or support tickets and logging them to the right Teamwork project automatically), and reporting (pulling time data, task completion rates, and billing summaries for custom dashboards or automated reports to clients).

Teamwork's API uses HTTP Basic Auth β€” your API key is the username and the password can be any string (many developers use 'x' or 'password'). This makes it straightforward to integrate: no OAuth flow, no token expiry, no refresh logic. The API has two versions: v1 (older, more complete feature coverage) and v3 (newer, cleaner design). This guide uses v3 where available and falls back to v1 for features not yet in v3.

Integration method

Standard API Integration

Teamwork integrates with Replit through Teamwork's REST API v1/v3 using HTTP Basic Auth with your API key as the username and any string as the password. Your Replit backend creates and manages projects, task lists, tasks, milestones, and time entries. Teamwork also supports webhooks that deliver real-time events when tasks are completed, projects are updated, or time is logged β€” these require a deployed Replit endpoint.

Prerequisites

  • A Replit account with a Node.js or Python Repl ready
  • A Teamwork account β€” free trial available at teamwork.com
  • Your Teamwork API key from your user profile in Teamwork
  • Your Teamwork site URL (the subdomain you use to access Teamwork, e.g., yourcompany.teamwork.com)

Step-by-step guide

1

Get Your Teamwork API Key and Store It in Replit Secrets

Teamwork API keys are user-specific β€” each user has their own API key that provides access to all projects visible to that user. For API integrations, best practice is to create a dedicated Teamwork user account (e.g., api@yourcompany.com) with the appropriate role and use that user's API key. This keeps API activity clearly attributed and means rotating the key does not affect a real user's sessions. To get your API key: log into Teamwork β†’ click your avatar in the top right β†’ Edit Profile β†’ scroll down to 'API Token' and click 'Show your token'. Copy the token. You will also need your Teamwork site URL. If you access Teamwork at https://yourcompany.teamwork.com, then yourcompany.teamwork.com is your site URL. Open your Replit project and click the lock icon (πŸ”’) in the left sidebar. Add: TEAMWORK_API_KEY: your Teamwork API token. TEAMWORK_SITE: your Teamwork site URL without https:// (e.g., yourcompany.teamwork.com). Teamwork API uses HTTP Basic Auth where the API key is the username and any string (conventionally 'x') is the password. Never include the API key in source code β€” Replit's Secret Scanner will flag it.

check-teamwork-secrets.js
1// check-teamwork-secrets.js
2const required = ['TEAMWORK_API_KEY', 'TEAMWORK_SITE'];
3for (const key of required) {
4 if (!process.env[key]) {
5 throw new Error(`Missing: ${key}. Add it in Replit Secrets (lock icon πŸ”’).`);
6 }
7}
8const site = process.env.TEAMWORK_SITE;
9if (site.startsWith('http')) {
10 console.warn('Warning: TEAMWORK_SITE should be the subdomain only, e.g., yourcompany.teamwork.com β€” not https://...');
11}
12console.log('Teamwork secrets configured.');
13console.log('Site:', process.env.TEAMWORK_SITE);
14console.log('API Key prefix:', process.env.TEAMWORK_API_KEY.slice(0, 8) + '...');

Pro tip: TEAMWORK_SITE should be just the subdomain and domain (yourcompany.teamwork.com), not the full URL with https://. Your code will prepend https:// when constructing API request URLs.

Expected result: Both secrets appear in Replit Secrets. The verification script prints the site URL and API key prefix without errors.

2

Create Projects, Task Lists, and Tasks with Node.js

Teamwork's API base URL is https://{your-site}/projects/api/v3 for v3 endpoints and https://{your-site} for v1 endpoints. Authentication uses HTTP Basic Auth β€” encode the API key and password 'x' as Base64 in the Authorization header: 'Basic {base64(apiKey:x)}'. Teamwork's v3 API uses a consistent JSON structure for creating resources: each POST body has a top-level key matching the resource type (e.g., 'project' for projects, 'task' for tasks). Responses follow the same structure with the created resource nested under its type key. Project creation requires a name and optionally a description, start date, end date, and company ID (client). Task lists are created within projects, and tasks are created within task lists. This hierarchy (project β†’ task list β†’ task) mirrors Teamwork's interface. For v1 endpoints (used for time logging and some advanced features), the base URL format is https://{your-site}/projects/{projectId}/time_entries.json and authentication is the same Basic Auth pattern. The Node.js fetch API (Node 18+) or the axios library work well for Teamwork API calls. The examples below use fetch for zero dependencies.

teamwork-client.js
1// teamwork-client.js β€” Teamwork REST API client
2const SITE = process.env.TEAMWORK_SITE;
3const API_KEY = process.env.TEAMWORK_API_KEY;
4const AUTH = 'Basic ' + Buffer.from(`${API_KEY}:x`).toString('base64');
5const BASE_V3 = `https://${SITE}/projects/api/v3`;
6const BASE_V1 = `https://${SITE}`;
7
8async function twRequest(method, url, body = null) {
9 const options = {
10 method,
11 headers: {
12 'Authorization': AUTH,
13 'Content-Type': 'application/json',
14 'Accept': 'application/json'
15 }
16 };
17 if (body) options.body = JSON.stringify(body);
18
19 const response = await fetch(url, options);
20 if (!response.ok) {
21 const errText = await response.text();
22 throw new Error(`Teamwork API ${response.status}: ${errText}`);
23 }
24 if (response.status === 204) return null;
25 return response.json();
26}
27
28// Create a project
29async function createProject(name, description = '', companyId = null) {
30 const body = {
31 project: { name, description }
32 };
33 if (companyId) body.project.company = { id: companyId };
34 const result = await twRequest('POST', `${BASE_V3}/projects.json`, body);
35 return result?.project;
36}
37
38// Get all projects
39async function getProjects() {
40 const result = await twRequest('GET', `${BASE_V3}/projects.json`);
41 return result?.projects || [];
42}
43
44// Create a task list within a project
45async function createTaskList(projectId, name) {
46 const result = await twRequest('POST', `${BASE_V1}/projects/${projectId}/tasklists.json`, {
47 'todo-list': { name }
48 });
49 return result?.['TASKLISTID'];
50}
51
52// Create a task within a task list
53async function createTask(taskListId, title, description = '', assigneeId = null, dueDate = null) {
54 const task = { 'content': title, 'description': description };
55 if (assigneeId) task['responsible-party-id'] = assigneeId;
56 if (dueDate) task['due-date'] = dueDate; // YYYYMMDD format
57 const result = await twRequest('POST', `${BASE_V1}/tasklists/${taskListId}/tasks.json`, {
58 'todo-item': task
59 });
60 return result?.id || result?.['TASKID'];
61}
62
63// Log a time entry (v1 API)
64async function logTime(projectId, taskId, hours, minutes, date, description, personId) {
65 const body = {
66 'time-entry': {
67 description,
68 'person-id': personId,
69 date, // YYYYMMDD
70 hours: String(hours),
71 minutes: String(minutes),
72 isbillable: '1'
73 }
74 };
75 const url = taskId
76 ? `${BASE_V1}/tasks/${taskId}/time_entries.json`
77 : `${BASE_V1}/projects/${projectId}/time_entries.json`;
78 return twRequest('POST', url, body);
79}
80
81// Get time entries for a project
82async function getTimeEntries(projectId) {
83 const result = await twRequest('GET', `${BASE_V1}/projects/${projectId}/time_entries.json`);
84 return result?.['time-entries'] || [];
85}
86
87// Example
88(async () => {
89 try {
90 const projects = await getProjects();
91 console.log(`Found ${projects.length} projects`);
92 projects.slice(0, 3).forEach(p => console.log(` - ${p.id}: ${p.name}`));
93 } catch (err) {
94 console.error('Error:', err.message);
95 }
96})();
97
98module.exports = { createProject, getProjects, createTaskList, createTask, logTime, getTimeEntries };

Pro tip: Teamwork uses two date formats depending on the API version. The v3 API uses ISO 8601 dates (YYYY-MM-DD). The v1 API uses YYYYMMDD (no dashes) for task due dates and time entries. Match the format to the endpoint version to avoid validation errors.

Expected result: Running the script retrieves and prints the first three Teamwork projects. If you have no projects yet, the output shows 'Found 0 projects' β€” you can then run createProject() to create one.

3

Manage Projects and Time Entries with Python

The Python client uses the requests library with HTTP Basic Auth. The requests library supports Basic Auth natively through the auth tuple parameter β€” pass (api_key, 'x') and requests handles the Base64 encoding and Authorization header automatically. Teamwork's API returns slightly different JSON structure in v1 vs v3. The v1 API wraps objects in XML-like keys (e.g., 'todo-item', 'time-entry', 'todo-list') while v3 uses cleaner JSON with plural keys (e.g., 'tasks', 'projects'). Your Python client should handle both response formats for full compatibility. For batch operations like creating multiple tasks at once, use Teamwork's v3 batch endpoint which accepts an array of task objects, or loop with individual creates and track errors per task. Flask makes it easy to build a project management API proxy on top of Teamwork β€” expose simplified endpoints that your frontend or automation tools call, and your Replit server handles the Teamwork API complexity.

teamwork_client.py
1# teamwork_client.py β€” Teamwork REST API client for Replit (Python)
2import os
3import requests
4from datetime import datetime
5
6SITE = os.environ['TEAMWORK_SITE']
7API_KEY = os.environ['TEAMWORK_API_KEY']
8AUTH = (API_KEY, 'x') # Teamwork Basic Auth: api_key as username, 'x' as password
9BASE_V3 = f'https://{SITE}/projects/api/v3'
10BASE_V1 = f'https://{SITE}'
11
12HEADERS = {'Content-Type': 'application/json', 'Accept': 'application/json'}
13
14def tw_request(method: str, url: str, body: dict = None) -> dict:
15 """Make an authenticated Teamwork API request."""
16 response = requests.request(method, url, auth=AUTH, headers=HEADERS, json=body)
17 response.raise_for_status()
18 if response.status_code == 204:
19 return {}
20 return response.json()
21
22def get_projects() -> list:
23 """Get all projects the API user has access to."""
24 result = tw_request('GET', f'{BASE_V3}/projects.json')
25 return result.get('projects', [])
26
27def create_project(name: str, description: str = '') -> dict:
28 """Create a new Teamwork project."""
29 result = tw_request('POST', f'{BASE_V3}/projects.json', {
30 'project': {'name': name, 'description': description}
31 })
32 return result.get('project', {})
33
34def create_task_list(project_id: str, name: str) -> str:
35 """Create a task list within a project. Returns the task list ID."""
36 result = tw_request('POST', f'{BASE_V1}/projects/{project_id}/tasklists.json', {
37 'todo-list': {'name': name}
38 })
39 return str(result.get('TASKLISTID', ''))
40
41def create_task(task_list_id: str, title: str,
42 description: str = '', due_date: str = None) -> str:
43 """Create a task in a task list. due_date format: YYYYMMDD"""
44 task = {'content': title, 'description': description}
45 if due_date:
46 task['due-date'] = due_date
47 result = tw_request('POST', f'{BASE_V1}/tasklists/{task_list_id}/tasks.json', {
48 'todo-item': task
49 })
50 return str(result.get('TASKID', result.get('id', '')))
51
52def log_time(project_id: str, hours: int, minutes: int,
53 description: str, date: str = None, task_id: str = None) -> dict:
54 """Log a time entry. date format: YYYYMMDD (defaults to today)."""
55 if not date:
56 date = datetime.now().strftime('%Y%m%d')
57 entry = {
58 'time-entry': {
59 'description': description,
60 'date': date,
61 'hours': str(hours),
62 'minutes': str(minutes),
63 'isbillable': '1'
64 }
65 }
66 url = (f'{BASE_V1}/tasks/{task_id}/time_entries.json'
67 if task_id else f'{BASE_V1}/projects/{project_id}/time_entries.json')
68 return tw_request('POST', url, entry)
69
70def get_time_entries(project_id: str) -> list:
71 """Get all time entries for a project."""
72 result = tw_request('GET', f'{BASE_V1}/projects/{project_id}/time_entries.json')
73 return result.get('time-entries', [])
74
75if __name__ == '__main__':
76 projects = get_projects()
77 print(f'Found {len(projects)} projects:')
78 for p in projects[:5]:
79 print(f' {p["id"]}: {p["name"]}')

Pro tip: Teamwork's v1 API returns task IDs in the response body as 'TASKID' and task list IDs as 'TASKLISTID' (uppercase). The v3 API uses lowercase 'id'. Always check both if you are supporting both API versions in the same codebase.

Expected result: Running python teamwork_client.py retrieves and prints the first five Teamwork projects with their IDs and names.

4

Set Up Webhooks and Build an Automation Server

Teamwork webhooks deliver real-time events to your Replit server when work items change β€” tasks are created, completed, or reassigned; time is logged; projects are updated; or milestones are reached. This lets you build responsive automations that react immediately to team activity. To set up webhooks in Teamwork: go to Settings (gear icon) β†’ Webhooks β†’ Add Webhook. Enter your deployed Replit URL, select the event types you want to receive, and save. Teamwork delivers webhook payloads as JSON POST requests to your endpoint. Teamwork webhook payloads include the event type (e.g., 'task.completed', 'time.created'), the entity that changed, and contextual data about the project and user involved. Use the event type to route different events to different handlers. IMPORTANT: Configure the webhook URL to your deployed Replit URL, not a development URL. Development Repls go offline when you close your browser β€” Teamwork will mark deliveries as failed and retry with exponential backoff, but events fired during the offline period may be lost. Use Autoscale deployment for Teamwork webhook receivers. Project management events (task completions, time logs) are infrequent and bursty β€” Autoscale handles this load pattern efficiently.

teamwork-server.js
1// teamwork-server.js β€” Express server for Teamwork API proxy and webhooks
2const express = require('express');
3const {
4 createProject, createTaskList, createTask, logTime, getProjects
5} = require('./teamwork-client');
6const app = express();
7app.use(express.json());
8
9// POST /projects β€” create a project with default task lists
10app.post('/projects', async (req, res) => {
11 try {
12 const { name, description, taskLists = [] } = req.body;
13 if (!name) return res.status(400).json({ error: 'name required' });
14
15 const project = await createProject(name, description);
16 const projectId = project.id;
17 const createdLists = [];
18
19 // Create standard task lists
20 for (const listName of taskLists) {
21 const listId = await createTaskList(projectId, listName);
22 createdLists.push({ name: listName, id: listId });
23 }
24
25 res.status(201).json({ projectId, taskLists: createdLists });
26 } catch (err) {
27 console.error('Create project error:', err.message);
28 res.status(500).json({ error: err.message });
29 }
30});
31
32// GET /projects β€” list projects
33app.get('/projects', async (req, res) => {
34 try {
35 const projects = await getProjects();
36 res.json(projects.map(p => ({ id: p.id, name: p.name, status: p.status })));
37 } catch (err) {
38 res.status(500).json({ error: err.message });
39 }
40});
41
42// POST /time β€” log a time entry
43app.post('/time', async (req, res) => {
44 try {
45 const { projectId, taskId, hours, minutes, description, date } = req.body;
46 if (!projectId || hours === undefined) {
47 return res.status(400).json({ error: 'projectId and hours required' });
48 }
49 const result = await logTime(projectId, taskId, hours, minutes || 0, description, date);
50 res.status(201).json({ success: true });
51 } catch (err) {
52 res.status(500).json({ error: err.message });
53 }
54});
55
56// POST /webhook/teamwork β€” receive Teamwork webhook events
57app.post('/webhook/teamwork', (req, res) => {
58 res.status(200).json({ received: true }); // Respond immediately
59
60 setImmediate(() => {
61 const event = req.body;
62 const eventType = event.eventType || event.type;
63 console.log('Teamwork webhook:', eventType);
64
65 if (eventType === 'task.completed' || eventType === 'TASK_COMPLETED') {
66 const task = event.task || event.item;
67 console.log('Task completed:', task?.name, 'in project:', task?.project?.name);
68 // TODO: notify Slack, update external systems, log to analytics
69 } else if (eventType === 'time.created' || eventType === 'TIME_CREATED') {
70 const entry = event.timeEntry || event.item;
71 console.log(`Time logged: ${entry?.hours}h ${entry?.minutes}m by ${entry?.person?.name}`);
72 // TODO: update billing dashboard, check against budget
73 } else if (eventType === 'milestone.completed' || eventType === 'MILESTONE_COMPLETED') {
74 const milestone = event.milestone || event.item;
75 console.log('Milestone reached:', milestone?.name);
76 // TODO: send client update email
77 }
78 });
79});
80
81app.listen(3000, '0.0.0.0', () => {
82 console.log('Teamwork integration server running on port 3000');
83 console.log('Set webhook URL in Teamwork: https://yourapp.replit.app/webhook/teamwork');
84});

Pro tip: When creating a full project scaffold (project + task lists + tasks) in sequence, use Promise chaining or async/await carefully since each step depends on the ID returned by the previous step. A failed intermediate step (e.g., task list creation) should be logged but should not block the overall onboarding flow.

Expected result: The Express server starts on port 3000. POST /projects creates a Teamwork project with the specified task lists. POST /time logs a time entry. Teamwork webhooks trigger the event handler and log event details.

Common use cases

Automated Client Onboarding Project Setup

When a new client contract is signed in your CRM, automatically create a Teamwork project with the standard task lists and initial tasks for your onboarding process. Instead of manually setting up the same project structure every time, a Replit webhook receives the contract-signed event and creates a fully populated project ready for the team to start working.

Replit Prompt

Build a webhook receiver that triggers when a new client is added to your CRM, creates a Teamwork project named after the client, adds standard task lists (Discovery, Design, Development, Launch), creates initial onboarding tasks in each list, and assigns the project manager as the default assignee.

Copy this prompt to try it in Replit

Automatic Time Logging from External Tools

Capture time entries from development tools, support platforms, or custom applications and log them directly to Teamwork. When a developer closes a GitHub issue, a support agent resolves a ticket, or a consultant marks work complete in another system, the Replit backend automatically creates a Teamwork time entry on the appropriate project and task β€” eliminating the manual time entry that many team members routinely forget.

Replit Prompt

Create a webhook endpoint that receives GitHub pull request closed events, calculates the time from PR open to merge, looks up the associated Teamwork project by repository name, and logs a time entry for the assigned developer on the relevant task.

Copy this prompt to try it in Replit

Client Project Status Report

Generate automated weekly project status reports by querying Teamwork for task completion rates, logged hours vs. budget, upcoming milestones, and overdue tasks. The Replit server assembles the report data and delivers it via email or Slack β€” giving clients and stakeholders regular visibility without manual report creation.

Replit Prompt

Build a scheduled report generator that queries Teamwork for all active projects, retrieves task completion percentages, logged hours vs. estimated budget, and upcoming milestone dates, formats the data into a project status report, and emails it to stakeholders every Monday morning.

Copy this prompt to try it in Replit

Troubleshooting

Teamwork API returns 401 Unauthorized on all requests

Cause: The TEAMWORK_API_KEY is incorrect, or the Basic Auth header is not constructed correctly. Teamwork's Basic Auth requires base64 encoding of 'apiKey:x' (with the colon and 'x' as the password).

Solution: In Teamwork, click your avatar β†’ Edit Profile β†’ API Token to verify your API key. In your code, confirm the Authorization header is constructed as 'Basic ' + Buffer.from(apiKey + ':x').toString('base64') in Node.js or by passing auth=(apiKey, 'x') in Python requests.

typescript
1// Verify the Authorization header is formatted correctly
2const auth = Buffer.from(`${process.env.TEAMWORK_API_KEY}:x`).toString('base64');
3console.log('Auth header:', `Basic ${auth.slice(0, 20)}...`);
4// Test by calling the /me.json endpoint
5fetch(`https://${process.env.TEAMWORK_SITE}/me.json`, {
6 headers: { 'Authorization': `Basic ${auth}` }
7}).then(r => r.json()).then(d => console.log('Auth test:', d?.person?.firstName || 'FAILED'));

Tasks are created but due dates are not being set correctly

Cause: Teamwork's v1 API uses YYYYMMDD date format (no dashes or slashes), while v3 uses YYYY-MM-DD. Using the wrong format for the endpoint version causes the date to be silently ignored or rejected.

Solution: For v1 API task creation (/tasklists/{id}/tasks.json), format due dates as YYYYMMDD (e.g., '20260401'). For v3 API, use YYYY-MM-DD. Check which API version endpoint you are calling and match the date format accordingly.

typescript
1// Format date for Teamwork v1 API (YYYYMMDD)
2const formatDateV1 = (date) => date.toISOString().split('T')[0].replace(/-/g, '');
3console.log(formatDateV1(new Date('2026-04-01'))); // '20260401'
4
5// Format date for Teamwork v3 API (YYYY-MM-DD)
6const formatDateV3 = (date) => date.toISOString().split('T')[0];
7console.log(formatDateV3(new Date('2026-04-01'))); // '2026-04-01'

Webhook events are not arriving at the Replit endpoint

Cause: The webhook URL is set to a development Replit URL that goes offline when the browser is closed, or webhooks are not enabled/configured in Teamwork Settings.

Solution: Deploy your Replit app and use the deployed HTTPS URL in Teamwork Settings β†’ Webhooks. Verify the webhook is Active (not paused) in Teamwork's webhook management interface. Test the endpoint directly by sending a manual POST request to verify it is accessible.

Time entries are created but not appearing in project reports

Cause: Time entries logged without a person ID may not be attributed correctly, or the date format was wrong causing the entry to be filed on the wrong date.

Solution: Call GET /people.json to get all user IDs and pass the correct person-id when logging time. Verify the date format is YYYYMMDD for v1 API calls. Check Teamwork's time tracking view filtered by date range to find the misattributed entry.

typescript
1// Get people list to find valid person IDs
2fetch(`https://${process.env.TEAMWORK_SITE}/people.json`, {
3 headers: { 'Authorization': 'Basic ' + Buffer.from(`${process.env.TEAMWORK_API_KEY}:x`).toString('base64') }
4}).then(r => r.json()).then(d => d.people.forEach(p => console.log(`${p.id}: ${p['first-name']} ${p['last-name']}`)));

Best practices

  • Store TEAMWORK_API_KEY and TEAMWORK_SITE in Replit Secrets (lock icon πŸ”’) β€” the API key provides the same access as the user account that owns it
  • Create a dedicated Teamwork user account (api-bot@yourcompany.com) for API token ownership so that API activity is clearly labeled in Teamwork's audit logs
  • Be aware that Teamwork uses different API versions (v1 vs v3) for different features β€” always check which base URL to use for each operation
  • Match date formats to the API version: v1 requires YYYYMMDD (no dashes), v3 requires YYYY-MM-DD
  • Deploy as Autoscale for webhook receivers β€” project management events are infrequent and bursty, and Autoscale handles this pattern efficiently without keeping a server running 24/7
  • Log Teamwork entity IDs (project ID, task list ID, task ID) to your database when creating them β€” you need these for subsequent time logging, task updates, and deletion
  • Use Teamwork's batch task creation endpoint when onboarding new projects to avoid API rate limits from sequential individual creates
  • Implement idempotency checks before creating projects or tasks to prevent duplicates if your webhook triggers fire multiple times for the same event

Alternatives

Frequently asked questions

How do I find my Teamwork API key?

In Teamwork, click your avatar or profile picture in the top-right corner β†’ Edit Profile (or Profile Settings) β†’ scroll down to find 'API Token' β†’ click 'Show your token'. Copy the token and add it to Replit Secrets as TEAMWORK_API_KEY. The token is tied to your user account and provides API access at the same permission level as your account.

Does Teamwork have a free plan that includes API access?

Teamwork's Free Forever plan includes limited API access for basic operations. Paid plans (Deliver, Grow, Scale) unlock higher API rate limits and advanced features. For most integration use cases, even the free plan provides sufficient API access for testing. Check Teamwork's pricing page for current plan limits.

How do I log time to a specific task vs. a project in Teamwork?

Use the task-specific endpoint (POST /tasks/{taskId}/time_entries.json) to log time against a specific task, which is more precise for reporting. Use the project-level endpoint (POST /projects/{projectId}/time_entries.json) to log time at the project level without task attribution. Both endpoints require the same request body format with hours, minutes, date, and description fields.

What deployment type should I use for Teamwork webhook receivers on Replit?

Autoscale deployment is ideal for Teamwork webhook receivers. Project management events (task completions, time entries, milestone updates) are infrequent and bursty β€” the load pattern is well-suited to Autoscale which scales to zero when idle and scales up when events arrive. The 1-3 second cold start on Autoscale is well within Teamwork's webhook timeout window.

What is the difference between Teamwork's v1 and v3 APIs?

Teamwork's v3 API (base URL: /projects/api/v3) is the newer, more RESTful design with cleaner JSON responses and modern conventions. The v1 API (base URL at the domain root) is older but has broader feature coverage β€” some operations like time entry creation and task list management still use v1 endpoints. A complete integration often needs to use both versions. The v3 API is preferred when available.

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.