Skip to content

Project ​

Allowed query parameters ​

Filters ​

FilterTypeDescription
searchStringFull-text search
ownBooleanShow only own projects (as project leader)
involvedBooleanShow only projects where user is involved
assignedBooleanShow only projects assigned to the current user
contactIdFilter by Contact Id
is_archivedBooleanShow archived projects (completed)
is_internalBooleanShow only projects without billing
with_billable_costsBooleanShow only top-level projects with something left to invoice, including their sub-projects' work
companyIdFilter by Company Id
billing_typeStringFilter by billing type (flat, hourly)
statusStringFilter by status (active, completed)
custom_fieldKey,StringFilter by Custom Field (eg. my_internal_id,42)

Sorting ​

name, created_at, starts_at, company, budget_used, billable_costs, last_time_tracked

Includes ​

company.media, owner.user, times.rate, unbilledTimes.rate, offers, approvedOffers, expenses, invoices, rates, timesCount, offersCount, invoicesCount, expensesCount, overdueInvoicesCount, assignedMembers.user.media

offers, approvedOffers and offersCount are only returned to users with the show offer permission.

Appends ​

(Available append parameters for additional computed fields)

billing, budget, budget_total, budget_grouped, offered_rates, involved_members, costs_total, costs_limit_exceeded, billable_costs, billable_costs_breakdown, billable_total, billed_total, draft_invoices_total, turnover_total, sent_invoices_total, profit_total, expected_revenue_total, expected_profit_total, billing_waived_total, internal_costs_total, internal_costs_breakdown, return_on_investment_total

Budget and financial figures are only returned to users who can see the project's budget. billing returns billable_costs, billable_total, billed_total, draft_invoices_total, turnover_total, sent_invoices_total, profit_total, expected_revenue_total, expected_profit_total and billing_waived_total at once, and only to users with the supervise invoice permission and to the project leader.

Financial figures ​

All amounts are in the organization's currency and rounded to cents. Invoices and offers count net. Sub-projects hold no invoices or offers; their billable work and internal costs are included.

FieldTypeDescription
billable_costsNumberWork not invoiced yet: accountable hours at their rate's price, tracked products and billable expenses, including sub-projects
billable_costs_breakdownObjectWhat billable_costs is made of, see below
billable_totalNumberAmount left to invoice. Flat-rate projects: approved offers minus all invoices, drafts included, never below 0, and 0 once the billing is completed. Hourly projects: equal to billable_costs. Sub-projects of a flat-rate project: 0, the parent's offers cover their work
billed_totalNumberUnpaid invoices, drafts included
draft_invoices_totalNumberDraft invoices (neither sent nor paid). Part of billed_total
turnover_totalNumberPaid invoices
sent_invoices_totalNumberSent and paid invoices, including invoices scheduled for sending: billed_total − draft_invoices_total + turnover_total
profit_totalNumberRealised result: sent_invoices_total minus internal_costs_total
expected_revenue_totalNumberWhat the project brings in once everything recorded is invoiced: billable_total + draft_invoices_total + sent_invoices_total. Flat-rate projects: the approved offers, or more if more has been invoiced
expected_profit_totalNumberexpected_revenue_total minus internal_costs_total. Counts work not invoiced yet, but only the internal costs recorded so far. The result shown on the project's finances card
internal_costs_totalNumberSum of internal_costs_breakdown
internal_costs_breakdownObjecttimes: tracked hours at the internal hourly rate, products: tracked products at their purchase price (sales price if none), expenses: all expenses at their gross total. Including sub-projects
billing_waived_totalNumberFlat-rate projects with completed billing: the approved offers minus all invoices, left uninvoiced. 0 otherwise
return_on_investment_totalNumber?Sent and paid invoices relative to internal_costs_total in percent, e.g. 50 for invoices of 1.5 times the internal costs. null without internal costs or sent invoices

billable_costs_breakdown lists unbilled hours grouped by rate (largest first), tracked products, billable expenses (net if your organization is liable for VAT, gross otherwise), each sub-project with billable costs, and the date of the oldest unbilled time entry or expense:

json
{
    "billable_costs_breakdown": {
        "rates": [
            { "name": "Development", "price": 150.0, "hours": 3.0, "total": 450.0 },
            { "name": "Junior", "price": 100.0, "hours": 1.5, "total": 150.0 }
        ],
        "products": 80.0,
        "expenses": 100.0,
        "children": [
            { "id": "78gj0E32bW", "name": "Phase 2", "total": 150.0 }
        ],
        "oldest_unbilled_at": "2026-09-29"
    },
    "internal_costs_breakdown": {
        "times": 240.0,
        "products": 90.0,
        "expenses": 107.7
    }
}

Create a project ​

post
/project
Attribute (* required)TypeDescription
name *StringProject title
descriptionStringProject description
company_id *StringAn existing Company Id of your organization
member_id *StringA Member Id
starts_at *DateStart date
billing_typeStringBilling type (flat, hourly)
costs_limitNumberCost limit for the project (minimum: 0). Required with parent_id
assigned_member_idsArrayArray of Member IDs assigned to the project
statusStringStatus of project (active, completed, default: active)
custom_fieldsObject?Custom data as Custom Fields
parent_idString?Creates the project as a sub-project of this Project Id. See Sub-projects

Example response ​

json
// HTTP 201 Created
{
    // a project object
}

Update a project ​

put
/project/{id}
Attribute (* required)TypeDescription
nameStringProject title
descriptionStringProject description
company_idStringAn existing Company Id of your organization (the project's current company is always accepted)
member_idStringA Member Id
starts_atDateStart date
billing_typeStringBilling type (flat, hourly)
costs_limitNumberCost limit for the project (minimum: 0). Required with parent_id
assigned_member_idsArrayArray of Member IDs assigned to the project
statusStringStatus of project (active, completed)
custom_fieldsObject?Custom data as Custom Fields
parent_idString?Moves the project below this Project Id, or makes it a top-level project with null. The project's current parent is always accepted. See Sub-projects

A sub-project that keeps its parent cannot switch to flat billing or change is_internal (422).

Example response ​

json
// HTTP 200 OK
{
    // a project object
}

Sub-projects ​

Projects have one level of sub-projects. When parent_id is set or changed, the request returns 422 unless:

  • the parent is a top-level project of your organization, and not the project itself,
  • the project has no sub-projects of its own, including deleted ones,
  • the project belongs to the parent's company (company_id),
  • the project is billed hourly,
  • the project has the same is_internal as the parent.
  • the project has no invoices or offers, including deleted ones.

Sub-projects only hold time entries and expenses. They are billed through their parent, which includes their work: an invoice or offer created or updated with a sub-project's project_id is assigned to its parent.

Retrieve a project ​

get
/project/{id}

Example response ​

json
// HTTP 200 OK
{
    "id": "EO6YGvqMRV",
    "name": "CMS Upgrade",
    "description": null,
    "starts_at": "2022-02-09T08:52:00.000000Z",
    "due_at": null,
    "billing_type": "flat",
    "company_id": "PQYLdqm4wR",
    "company": {
        // a company object
    },
    "owner": {
        // a member object
    },
    "involved_members": [
        // an array of member objects with user data
    ],
    "assigned_members": [
        // an array of assigned member objects
    ],
    "assigned_member_ids": [1, 2, 3],
    "times": [
        // an array of time objects
    ],
    "invoices": [
        // an array of invoice objects
    ],
    "expenses": [
        // an array of expense objects
    ],
    "times_count": 45,
    "invoices_count": 3,
    "expenses_count": 12,
    "status": "active",
    "is_billing_completed": false,
    "billing_completed_at": null,
    "billing_completed_reason": null,
    "is_archived": false,
    "is_ongoing": true,
    "is_internal": false,
    "costs_limit": 5000.0,
    "custom_fields": {},
    "created_at": "2021-12-09T09:05:28.000000Z",
    "updated_at": "2022-02-09T08:52:00.000000Z"
}

Delete a project ​

delete
/project/{id}

Example response ​

json
// HTTP 204 No Content

List projects ​

get
/project

The list endpoint accepts the same parameters as in Retrieve a project and returns a paginated array of the same project object in the data property.

Read more about Pagination, Filtering, Sorting and Includes on the Introduction page.

Merge a project ​

post
/project/{id}/merge

Merges a project into another active, top-level project of the same company. All related resources — offers, invoices, times, expenses, media and assigned members — are moved from the source project into the target project. The source project is then archived (status set to completed).

Required Permission

This endpoint requires the supervise project permission. Users without it receive a 403 Forbidden response.

The target must be a top-level project — a sub-project can never be a merge target.

Attribute (* required)TypeDescription
target_project_id *StringA Project Id to merge into. Must be an active, top-level project of the same company (a sub-project cannot be a target)
move_sub_projectsBooleanRe-parent the source's sub-projects under the target. If omitted or false, the sub-projects are archived together with the source project (default: false)

Example request ​

json
{
    "target_project_id": "PQYLdqm4wR",
    "move_sub_projects": true
}

Example response ​

The target project is returned.

json
// HTTP 200 OK
{
    // a project object
}

Complete the billing ​

post
/project/{id}/complete-billing

Leaves the uninvoiced rest of a flat-rate project's approved offers unbilled, for example when positions were never used. The project's billable_total drops to 0 and the rest is returned as billing_waived_total (see Financial figures). The project status does not change: a project can be active with completed billing, or completed with work still to invoice. Completing an already completed billing changes nothing.

Required Permission

This endpoint requires the edit project permission, and either the supervise invoice permission or being the project leader. Other users receive a 403 Forbidden response.

Only top-level, non-internal projects with the billing type flat can complete their billing; others return 422. Hourly work is taken out of billing per time entry instead (is_accountable).

AttributeTypeDescription
reasonStringWhy the rest is not invoiced (max. 255 characters)

The project then returns is_billing_completed: true, the date in billing_completed_at and the reason in billing_completed_reason (to users who can see the project's budget).

Example request ​

json
{
    "reason": "Phase 2 not ordered"
}

Example response ​

json
// HTTP 200 OK
{
    // a project object
    "is_billing_completed": true,
    "billing_completed_at": "2026-10-09T16:12:00.000000Z",
    "billing_completed_reason": "Phase 2 not ordered"
}

Reopen the billing ​

post
/project/{id}/reopen-billing

Counts the uninvoiced rest of the approved offers as still to invoice again. Requires the same permissions as completing the billing. Reopening a project whose billing is not completed changes nothing.

Example response ​

json
// HTTP 200 OK
{
    // a project object
    "is_billing_completed": false,
    "billing_completed_at": null,
    "billing_completed_reason": null
}