Connect Retool to Teamwork using a REST API Resource with API key or OAuth authentication. Build client project tracking dashboards that show task status, milestone progress, time logs, and project health across all client accounts — giving your agency's operations team a unified view that goes far beyond Teamwork's built-in project views.
| Fact | Value |
|---|---|
| Tool | Teamwork |
| Category | Productivity |
| Method | REST API Resource |
| Difficulty | Intermediate |
| Time required | 25 minutes |
| Last updated | April 2026 |
Build a Cross-Client Project Operations Dashboard on Top of Teamwork
Teamwork is purpose-built for agencies and client services firms, but its native views are organized around individual projects — making it difficult to get a cross-client operational picture. Account managers and operations directors often need a single panel showing all active client projects, their milestone health, hours logged versus budgeted, and upcoming deadlines — a view that requires navigating through dozens of individual project pages in Teamwork's native interface. Retool solves this by connecting directly to Teamwork's REST API and building a customized portfolio view tailored to your team's exact operational reporting needs.
The integration architecture is simple: generate a Teamwork API key, configure Basic authentication in a Retool REST API Resource, and build queries for each data surface. Teamwork's API exposes projects, task lists, tasks, milestones, people, time entries, and comments — all returning paginated JSON that binds directly to Retool's Table and Chart components. JavaScript transformers then join data from multiple endpoints to produce calculated fields like project health scores, budget utilization percentages, and milestone completion rates that are not available from any single Teamwork API endpoint.
For agencies with 20 or more active client engagements, this dashboard becomes an operations command center. Project managers see their entire portfolio in one scroll, operations leads can identify projects approaching budget limits, and account managers can prepare client status updates in minutes instead of clicking through Teamwork project by project. The same Retool app can combine Teamwork data with billing data from your accounting system, giving a profitability view per client project that neither Teamwork nor your billing tool provides individually.
Integration method
Teamwork connects to Retool via a REST API Resource using Basic authentication with your Teamwork API key. You configure the base URL as your Teamwork site (yourcompany.teamwork.com) and authenticate with an API key from your Teamwork account settings. All queries for projects, tasks, milestones, time entries, and people are built visually in Retool's query editor. Retool proxies every request server-side, keeping your API key off the browser.
Prerequisites
- A Teamwork account with at least one active project containing tasks and milestones
- Your Teamwork site URL (e.g., yourcompany.teamwork.com)
- A Teamwork API key generated from your profile settings (Profile → API Tokens)
- A Retool account (Cloud or self-hosted) with permission to create Resources
- Basic familiarity with REST APIs and JSON data structures
Step-by-step guide
Generate a Teamwork API key
Teamwork authenticates API requests using an API token associated with your user account. To generate one, sign in to your Teamwork account and click your profile avatar in the top-right corner of the screen. Select 'Edit My Details' or 'Profile' from the dropdown. In your profile settings page, look for the 'API' tab or scroll down to find the 'API Tokens' section. Click 'Show your tokens' or 'Generate new API token'. Teamwork will display your API token — it is a long alphanumeric string. Copy it immediately and store it somewhere safe. Note that Teamwork API tokens are user-scoped, meaning the token has the same project and resource access as the account that generated it. For a cross-project portfolio dashboard, generate the API token from an admin account that has access to all client projects. If your Teamwork organization uses OAuth for integrations, you can alternatively set up an OAuth application in Teamwork's app settings — but for internal Retool dashboards, the API token approach is simpler and sufficient. Teamwork uses HTTP Basic authentication where the API token is used as the username and the letter 'x' is used as the password (this is a Teamwork-specific convention — the password field is ignored and 'x' is just a placeholder).
Pro tip: Use an admin account to generate the API token if you need access to all client projects. Tokens generated from restricted user accounts will only return projects that user can access, which may silently return incomplete data for your dashboard.
Expected result: A Teamwork API token string is copied from your profile settings and ready to configure in Retool.
Create a REST API Resource with Basic authentication
In Retool, navigate to the Resources tab from the left sidebar of your organization homepage. Click '+ Create new', scroll through the connector list, and select 'REST API'. On the configuration screen, set the Name to 'Teamwork API'. Set the Base URL to your Teamwork site URL in the format https://yourcompany.teamwork.com — replace yourcompany with your actual Teamwork subdomain. For Authentication, select 'Basic auth' from the dropdown. In the Username field, paste your Teamwork API token. In the Password field, type the letter x — this is required by Teamwork's API authentication convention but the value is ignored server-side. Add a header with key Content-Type and value application/json. Click 'Test connection' — if Retool can reach your Teamwork site and the API token is valid, the test should succeed. Click 'Save resource'. All queries made through this resource are proxied server-side by Retool, so the API token is never transmitted to browser clients. Note that Teamwork API responses return JSON by default, so you do not need a separate Accept header. If your Teamwork site uses a custom domain instead of the .teamwork.com subdomain, use that custom domain as the Base URL instead.
Pro tip: Teamwork's Basic auth convention is unusual: your API token goes in the username field and 'x' (literally the letter x) goes in the password field. The password value is ignored by Teamwork's servers — it is just a placeholder required by the Basic auth scheme.
Expected result: A REST API Resource named 'Teamwork API' with Basic authentication is saved in Retool. The test connection succeeds, confirming the site URL and API token are valid.
Query the projects endpoint
Open a Retool app and navigate to the Code panel at the bottom. Click '+ New query', select your Teamwork API resource, and name the query getProjects. Set the Method to GET and the URL path to /projects.json. Teamwork's API uses a .json suffix on endpoint paths to specify JSON format — this is a legacy convention but still required. Add URL parameters to control the response: set status to active to show only active projects (options: active, archived, current, late, upcoming), set orderBy to lastUpdated and orderMode to desc to see recently updated projects first, and set pageSize to 100 to maximize records per page. Click 'Run query'. Teamwork returns a projects array where each project has fields including id, name, description, company (nested object with name and id), status, created-on, last-changed-on, startDate, endDate, and subStatus. Note that Teamwork uses hyphenated field names (last-changed-on) rather than camelCase or snake_case — access them in JavaScript with bracket notation (project['last-changed-on']). Create a transformer on this query that flattens the company name out of the nested object and formats dates for display. Bind the transformed output to a Table component to show all active projects in a sortable list.
1// Transformer: flatten Teamwork projects with company names2const projects = data.projects || [];3return projects.map(p => ({4 id: p.id,5 name: p.name,6 company: p['company']?.name || 'Internal',7 company_id: p['company']?.id,8 status: p.status || 'active',9 sub_status: p['sub-status'] || '',10 start_date: p.startDate || 'Not set',11 end_date: p.endDate || 'No deadline',12 last_updated: p['last-changed-on'] ? new Date(p['last-changed-on']).toLocaleDateString() : 'N/A'13}));Pro tip: Teamwork uses hyphenated field names like 'last-changed-on' and 'start-date'. In JavaScript transformers, access these with bracket notation (project['last-changed-on']) rather than dot notation (project.last-changed-on), which JavaScript parses as subtraction.
Expected result: The getProjects query returns an array of active projects. The Table component displays all projects with company name, status, start/end dates, and last updated timestamp.
Fetch milestones and time entries for selected projects
With a project list displayed, build detail queries that run when a project is selected. Create a query named getMilestones with Method GET and path /projects/{{ projectsTable.selectedRow.id }}/milestones.json. Add parameters: set status to all (to show both completed and incomplete milestones), and order to deadline-asc to see upcoming deadlines first. Run after clicking a project row. Teamwork returns a milestones array where each milestone has id, title, deadline, completed (boolean), completed-on, and responsible-party-ids. Create a separate query named getTimeEntries with path /projects/{{ projectsTable.selectedRow.id }}/time_entries.json. Add parameters: set fromDate and toDate using a Retool DateRangePicker component to filter by billing period, and set billableType to billable to show only billable hours (or all for all entries). The time entries response includes hours, minutes, person-first-name, person-last-name, date, and isbillable fields. Create a JavaScript query named projectSummary that combines milestone and time data to produce summary statistics: total milestones, completed milestones, completion percentage, total hours logged (converting hours and minutes fields), and total billable hours. Display summary stats in Retool Stat components above the detail Tables.
1// Transformer: calculate project summary from milestones and time entries2const milestones = getMilestones.data?.milestones || [];3const timeEntries = getTimeEntries.data?.['time-entries'] || [];45const totalMilestones = milestones.length;6const completedMilestones = milestones.filter(m => m.completed === true).length;7const completionRate = totalMilestones > 08 ? Math.round((completedMilestones / totalMilestones) * 100)9 : 0;1011const totalHours = timeEntries.reduce((sum, t) => {12 return sum + (parseInt(t.hours) || 0) + (parseInt(t.minutes) || 0) / 60;13}, 0);1415const billableHours = timeEntries16 .filter(t => t.isbillable === '1')17 .reduce((sum, t) => sum + (parseInt(t.hours) || 0) + (parseInt(t.minutes) || 0) / 60, 0);1819return {20 total_milestones: totalMilestones,21 completed_milestones: completedMilestones,22 completion_rate: `${completionRate}%`,23 total_hours: totalHours.toFixed(1),24 billable_hours: billableHours.toFixed(1),25 next_deadline: milestones26 .filter(m => !m.completed && m.deadline)27 .sort((a, b) => new Date(a.deadline) - new Date(b.deadline))[0]?.deadline || 'None'28};Pro tip: Teamwork stores time entries with separate hours and minutes fields as strings (e.g., hours: '2', minutes: '30'). Always use parseInt() to convert them before arithmetic — and convert minutes to fractional hours (divide by 60) before summing total time.
Expected result: Selecting a project row triggers getMilestones and getTimeEntries queries. Stat components display milestone completion rate, total hours, and billable hours. A Table shows all milestones with completion status and deadlines.
Build an overdue and at-risk project alert panel
A key capability of the cross-client dashboard is identifying projects that are at risk before they become problems. Create a query named getAllMilestones with Method GET and path /milestones.json (the root milestones endpoint, not per-project). Add parameters: set status to incomplete to fetch only outstanding milestones across all projects, and set getProgress to true to include the project context with each milestone. This endpoint returns milestones across your entire Teamwork site — exactly what you need for a portfolio-level risk view. Create a JavaScript query named atRiskProjects that processes the all-milestones response to identify projects with overdue milestones. Calculate days overdue for each milestone where the deadline is in the past and completed is false. Group by project and count overdue milestones per project. Combine this with the time budget data from a separate query if available. Sort by number of overdue milestones descending. Display the results in a Table with a red badge for projects with any overdue milestones and yellow for projects with milestones due within 7 days. Add an event handler on this Table's Row Click that sets the selected project in the main projects Table, triggering the full project detail view. For teams managing complex multi-client portfolios with cross-system billing integrations and automated client reporting, RapidDev's team can help design the complete Retool workflow.
1// Identify at-risk and overdue milestones across all projects2const milestones = getAllMilestones.data?.milestones || [];3const today = new Date();45const enriched = milestones.map(m => {6 const deadline = m.deadline ? new Date(m.deadline) : null;7 const daysUntilDue = deadline ? Math.ceil((deadline - today) / (1000 * 60 * 60 * 24)) : null;8 return {9 milestone_title: m.title,10 project_name: m['project-name'] || 'Unknown Project',11 project_id: m['project-id'],12 deadline: m.deadline || 'No deadline',13 days_until_due: daysUntilDue,14 risk_level: daysUntilDue === null ? 'Unknown'15 : daysUntilDue < 0 ? 'Overdue'16 : daysUntilDue <= 7 ? 'At Risk'17 : 'On Track'18 };19});2021return enriched.sort((a, b) => {22 const order = { 'Overdue': 0, 'At Risk': 1, 'On Track': 2, 'Unknown': 3 };23 return order[a.risk_level] - order[b.risk_level];24});Pro tip: The root /milestones.json endpoint returns milestones across ALL projects you have access to — be aware it can return large amounts of data for organizations with many projects. Add status=incomplete to filter server-side and reduce response size.
Expected result: An alert panel shows all incomplete milestones across all projects sorted by risk level. Overdue milestones appear at the top with a red indicator. Clicking a milestone row navigates to the full project detail view.
Common use cases
Build a cross-client portfolio health dashboard
Your agency has thirty active client projects in Teamwork, and your operations director wants a single dashboard showing each project's status (on track, at risk, delayed), percentage of milestones completed, hours logged versus budget, and days until the next deadline. The Retool app queries all projects with their milestone and time data, calculates a health score for each using a JavaScript transformer, and displays them in a color-coded Table sorted by risk level.
Build a portfolio dashboard that queries all active Teamwork projects with milestone counts and time-logged data. Calculate a health score based on milestone completion rate and budget utilization. Display projects in a Table with red/yellow/green status badges. Add filter dropdowns for company name and project status, and a chart showing hours logged vs budgeted per project.
Copy this prompt to try it in Retool
Create a time log review and export panel
Your billing team needs to review all time entries logged against a specific client project each month before generating invoices. Teamwork's native time log view does not allow bulk export filtered by date range and billable status. The Retool app queries time entries for a selected project and date range, calculates total billable hours and dollar value at the project's billing rate, and displays individual entries in a Table that finance staff can review, filter by team member, and export.
Build a time log panel with a project selector dropdown, date range pickers, and a billable status filter. Query Teamwork time entries for the selected project and date range, calculate total hours and billable amount at a configurable rate, and display a Table of individual entries with person name, task, description, hours, and billable flag.
Copy this prompt to try it in Retool
Build a milestone tracking panel for account managers
Account managers need a weekly view of upcoming client milestones across all projects — what deliverables are due this week, next week, and in the next month. Teamwork's milestone view is per-project. The Retool app queries milestones across all active projects, filters and sorts by due date, groups them by client, and sends a Retool Workflow email summary every Monday morning so account managers start the week with a complete deliverables overview.
Build a milestone tracker that queries all milestones across active Teamwork projects, shows upcoming milestones sorted by due date with project name, responsible person, and completion status. Add date range filters and a 'This week' quick filter button. Build a companion Retool Workflow that emails this milestone list every Monday at 8 AM.
Copy this prompt to try it in Retool
Troubleshooting
Query returns 401 Unauthorized
Cause: The API token is invalid, has been regenerated, or the Basic authentication format is incorrect. Teamwork requires the API token as the username and exactly 'x' as the password — any other password value or missing password causes authentication failure.
Solution: Verify the API token in Teamwork (Profile → API Tokens → Show tokens). Check in your Retool Resource configuration that the Username field contains only the API token and the Password field contains exactly 'x' (the single letter x). If the token has changed, update the Resource: Resources tab → select Teamwork API → edit credentials → Save.
Teamwork API returns 404 Not Found for valid endpoints
Cause: The endpoint path is missing the required .json suffix. Teamwork's API requires all endpoints to end in .json (e.g., /projects.json, not /projects). Omitting it returns 404 even for valid resource paths.
Solution: Add .json to the end of every Teamwork API endpoint path in your Retool queries. For example, use /projects.json, /milestones.json, and /projects/{id}/time_entries.json. This is a Teamwork-specific API convention for specifying JSON response format.
Time entry hours and minutes fields return NaN or incorrect totals
Cause: Teamwork stores hours and minutes as string fields (e.g., '2' and '30'), not numbers. JavaScript's + operator concatenates strings instead of adding them, producing '230' instead of 2.5 hours.
Solution: Always use parseInt() or parseFloat() to convert Teamwork's hours and minutes strings before arithmetic. Convert minutes to fractional hours (minutes / 60) before adding to hours. Use reduce() with explicit parseInt conversions rather than direct summation.
1const totalHours = timeEntries.reduce((sum, t) => {2 return sum + (parseInt(t.hours) || 0) + (parseInt(t.minutes) || 0) / 60;3}, 0);Project fields with hyphens cause JavaScript errors in transformers
Cause: Teamwork uses hyphenated field names like 'last-changed-on' and 'sub-status'. Accessing these with dot notation (project.last-changed-on) causes JavaScript to interpret the hyphens as minus operators, producing NaN or syntax errors.
Solution: Use bracket notation to access hyphenated Teamwork field names: project['last-changed-on'] instead of project.last-changed-on. This applies to all hyphenated keys in Teamwork's API response, including 'sub-status', 'company-name', 'start-date', and 'time-entries'.
1// Correct access for hyphenated Teamwork fields:2const lastUpdated = project['last-changed-on'];3const subStatus = project['sub-status'];4const timeEntries = data['time-entries'] || [];Best practices
- Generate the Teamwork API token from an admin account that has access to all client projects — tokens from restricted accounts silently return partial data, making cross-client portfolio dashboards show incomplete project lists without any error.
- Store the Teamwork API token as a secret configuration variable in Retool (Settings → Configuration Variables, marked as secret) and reference it via Basic auth username in the Resource — never paste the token directly into a publicly viewable Resource configuration field.
- Always use parseInt() on Teamwork's hours and minutes fields before arithmetic — these fields return strings, and JavaScript's + operator will concatenate them rather than add them, producing silently incorrect time calculations.
- Add the .json suffix to every Teamwork API endpoint path — this is mandatory for JSON responses and a common source of 404 errors for developers unfamiliar with Teamwork's API conventions.
- Use bracket notation (object['hyphenated-key']) when accessing Teamwork API response fields with hyphens — dot notation interprets hyphens as subtraction operators, causing JavaScript runtime errors in transformers.
- Cache the getProjects query results (Query editor → Advanced → Cache results, 60 seconds) to prevent excessive API calls when multiple account managers use the portfolio dashboard simultaneously, and to avoid Teamwork's rate limits during busy periods.
- For billing-period time tracking dashboards, bind the fromDate and toDate parameters of time entry queries to Retool DateRangePicker components so finance team members can self-serve their billing period reports without needing to build separate queries for each month.
Alternatives
Asana is better suited for general-purpose project and task management without a client services focus, and connects to Retool via a native connector with pre-built action types.
Monday.com connects via REST API and is preferred when teams manage work in visual boards and need flexibility across project management, CRM, and operations use cases beyond agency client tracking.
Harvest connects via REST API and is the better choice when time tracking and invoicing are the primary requirements, rather than the full project and milestone management that Teamwork provides.
Frequently asked questions
Does Retool have a native Teamwork connector?
No. Retool does not have a dedicated native connector for Teamwork as of 2026. You connect via a generic REST API Resource using Teamwork's API with Basic authentication. The setup is straightforward — configure the Base URL as your Teamwork site URL, use your API token as the username, and 'x' as the password. All Teamwork API endpoints are then accessible through Retool's query editor.
Why do I use 'x' as the password for Teamwork API authentication?
Teamwork's API uses HTTP Basic authentication but only validates the username field (your API token). The password field is required by the Basic auth protocol but Teamwork ignores its value. By convention, Teamwork's documentation specifies using 'x' as a placeholder password — any non-empty string works, but 'x' is the documented convention.
Can I query Teamwork data across all client projects at once?
Yes, for certain resource types. Teamwork's root endpoints (/projects.json, /milestones.json, /time_entries.json) return data across all projects the authenticated user can access. For milestones and time entries, these cross-project endpoints are the most efficient way to build portfolio views. Task-level data typically requires querying per project or per task list.
How do I handle Teamwork rate limits?
Teamwork enforces rate limits that vary by plan — typically around 150-300 requests per minute. For portfolio dashboards that load data for many projects simultaneously, use Retool's query caching (Advanced → Cache results) with a 60-second window to reduce repeated calls. For Retool Workflows that process large amounts of Teamwork data, add a Wait block between bulk API calls or use batched JavaScript queries with Promise.all() limited to 5 concurrent requests.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation