Skip to main content

Project list filters and gross profit in the API

Filter GET /api/v1/projects and MCP list_projects by project type, team, client, custom fields and completion to find similar past projects, and read their gross profit through the REST API and MCP.

Written by Ben Walker

Before you price a new job, it helps to look at finished projects like it and see how profitable they were. The project list in the API and the MCP tool list_projects filter the same way as the project list in Drum, and both return gross profit when you're allowed to see it. This page covers those filters and the gross profit fields. All REST endpoints sit under /api/v1/ and need an account-scoped bearer token. See Drum API authentication, permissions and rate limits.

GET /api/v1/projects and list_projects share one set of filter rules, so a value that works on one works on the other. list_projects and get_project are in the My work and Insights toolsets. See How to choose MCP toolsets for Connected Agents and API tokens.

Filters

These filters work on GET /api/v1/projects and list_projects. Every filter you send must match, and they work alongside the existing filters (status, assigned person, tags and project number). You only get projects you can already see.

On REST, send one tag as a plain string, like ?project_tag_list=Civil, or several as project_tag_list[]=Civil&project_tag_list[]=Fire. A project must carry all of them.

Parameter

Meaning

active_only

true lists only projects whose status isn't a completed status

completed_only

true lists only projects in a completed status. Can't be sent with active_only set to true

project_template_ids

Project types (project templates). A project on any of these ids matches

team_ids

Teams. A project in any of these teams matches

client_ids

Client companies. A project for any of these clients matches

completed_from

Completed on or after this date, as yyyy-mm-dd

completed_to

Completed on or before this date, as yyyy-mm-dd

custom_fields

Up to 10 custom field filters, each with a custom_field_id and 1 to 20 values. The field must be a select or multiselect custom field of a project template, the same ones you can filter by on the project list in Drum. A select field matches when its value is one of values. A multiselect field matches when it has any of values. A project has to match every custom field filter you send

The rules for values:

  • active_only and completed_only take true or false. Other values, such as 1, are refused

  • The id filters take a list of whole-number ids, up to 100 each. In a query string, send each id with [], for example team_ids[]=4&team_ids[]=7. A single value without [] is refused

  • Dates are ISO 8601 (yyyy-mm-dd). A value like May or 01/05/2025 is refused

  • In a query string, send custom fields with an index: custom_fields[0][custom_field_id]=58&custom_fields[0][values][]=Residential. On MCP, send [{"custom_field_id": 58, "values": ["Residential"]}]

Example: completed residential projects of one project type, finished in the 2024 to 2025 financial year:

GET /api/v1/projects?completed_only=true&project_template_ids[]=12&completed_from=2024-07-01&completed_to=2025-06-30&custom_fields[0][custom_field_id]=58&custom_fields[0][values][]=Residential

Change for existing integrations

The project list used to ignore these parameter names. It now checks them, so a value it can't read returns 422 instead of an unfiltered list. If your integration already sends any of these names, check that the values follow the rules above.

Since 9 October 2026, deleted projects no longer appear in GET /api/v1/projects, and a single tag sent as a plain string works instead of returning an error. GET /api/v1/projects/:id answers an empty 404 for a deleted project, an opportunity, or a project you can't see. A hidden project used to answer 403.

Finding the ids

Filter

Where to get the ids

project_template_ids

MCP get_project returns project_templates: the project types of that project, as {id, name}. The REST project responses don't list project types

custom_fields

Each custom field on get_project and on GET /api/v1/projects/:id has a custom_field_id. Use that id, not the id of the row, which is the value on that one project

team_ids

The team of a project row. On MCP, search_records with kind team

client_ids

The client of a project row, or GET /api/v1/companies (see Reference data API). On MCP, search_records with kind company

A handy way to start is to open a project that's like the new job with get_project, then reuse its project types, team, client and custom field values as filters.

Gross profit

Gross profit uses the same rule as the project list and project insights in Drum. You see it only when both of these are true:

  • Your account has Show Gross Profit Figures turned on (Settings > Features)

  • Your role has View Gross Profit. Account admins have every permission

It doesn't need View Project Financial Metrics. When either condition isn't met, the gross_profit field is left out of the response, not sent as zero. See Drum Permissions.

Where

What you get

GET /api/v1/projects

Each project row has gross_profit with realised_percentage and projected_percentage

GET /api/v1/projects/:id

The two percentages plus the amounts below, as decimals in the project currency

list_projects with response_format detailed

Each row has the two percentages

get_project

With the financials section (included by default), the two percentages plus the amounts below, in the same money format as the tool's other money fields

Field

Meaning

realised_percentage

Realised gross profit over invoiced revenue, as a percentage. 0 when nothing has been invoiced

projected_percentage

Projected gross profit over projected revenue, as a percentage. 0 when projected revenue is zero

realised

Invoiced revenue less realised costs

realised_costs

The cost of invoiced time at cost rates, plus invoiced expenses

projected

Projected revenue less actual costs

projected_revenue

Invoiced revenue plus unbilled work, capped at the budget on a fixed price project

actual_costs

All time at cost rates, plus expenses

These costs use staff cost rates, not billable rates, so they're different from costs_to_date. Percentages are rounded to two decimal places.

Example from a project show response:

"gross_profit": {
"realised_percentage": 32.5,
"projected_percentage": 30.1,
"realised": 48750.0,
"realised_costs": 101250.0,
"projected": 48762.0,
"projected_revenue": 162000.0,
"actual_costs": 113238.0
}

Errors

A filter value that can't be read returns 422 with code invalid_input and the field it applies to, for example:

{"error": "completed_from must be an ISO 8601 date", "code": "invalid_input", "field": "completed_from"}

When

field

active_only or completed_only isn't true or false

The parameter you sent

active_only and completed_only are both true

completed_only

An id filter isn't a list of whole-number ids, or has more than 100

The parameter you sent

A date isn't yyyy-mm-dd

completed_from or completed_to

More than 10 custom field filters, or custom_fields isn't a list

custom_fields

A custom_field_id that isn't a select or multiselect custom field of a project template

custom_fields[0].custom_field_id (with the position of that filter)

values is empty, has more than 20 entries, or has a blank value

custom_fields[0].values

MCP list_projects refuses the same values with an invalid tool error that names the same field.

Related

Did this answer your question?