Settings catalogs
A settings catalog is a list of records that projects, budgets and people pick from, such as resource types (staff roles) and rate cards. Each catalog has the same endpoints as its settings screen in Drum, and each write runs the same code as that screen.
These endpoints are in early access, and they follow the rules for every setup endpoint: money reads back as {cents, currency, formatted}, a link reads back as {id, name}, and unknown fields are refused.
Endpoints
| Catalog | Path | Settings screen |
|---|---|---|
| Custom fields | /api/v1/custom_fields |
Custom Fields |
| Resource types | /api/v1/resource_types |
Staff Roles/Resource Types |
| Cost types | /api/v1/settings/cost_types |
Cost Types |
| Rate cards | /api/v1/rate_cards |
Rate Cards |
| Project roles | /api/v1/project_roles |
Project Roles and Opportunity Roles |
| Booking types | /api/v1/booking_types |
Booking Types |
| Milestone types | /api/v1/milestone_types |
Milestone Types |
Every catalog path takes these six requests:
| Method and path | Does |
|---|---|
GET <path> |
Lists the catalog’s records |
GET <path>/:id |
Reads one record |
POST <path> |
Creates a record |
PATCH <path>/:id |
Changes the fields you send |
DELETE <path>/:id |
Deletes a record. It can be restored |
PATCH <path>/:id/restore |
Restores a deleted record |
One more endpoint deletes a rate card entry: DELETE /api/v1/rate_card_entries/:id (see Delete a rate card entry).
How the catalog endpoints behave
List
GET <path> returns every kept record in id order, in one response. It is not paginated.
| Parameter | Meaning |
|---|---|
include_deleted |
true also returns deleted records, with "deleted": true |
curl -H "Authorization: Bearer $DRUM_API_TOKEN" \
https://app.getdrum.com/api/v1/resource_types
{
"records": [
{
"id": 88,
"name": "Senior Engineer",
"description": null,
"colour": "blue",
"line_item_code": null,
"default_members": [{"id": 4821, "name": "Jordan Reed"}],
"optional_members": [],
"deleted": false
}
]
}
Read
GET <path>/:id returns one record, also when it is deleted. An id that is not in the catalog returns 404 with the catalog’s code, such as resource_type_not_found.
Create and change
POST <path> creates a record from the fields in the catalog’s table. PATCH <path>/:id changes only the fields you send. Send null to clear an optional field.
Both return the record with warnings (201 for a create, 200 for a change). There is no Idempotency-Key, so a retried POST makes a second record.
curl -X POST https://app.getdrum.com/api/v1/milestone_types \
-H "Authorization: Bearer $DRUM_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Handover", "colour": "emerald"}'
{"id": 12, "name": "Handover", "colour": "emerald", "deleted": false, "warnings": []}
Delete and restore
DELETE <path>/:id soft deletes the record and returns it with "deleted": true. Records already using it keep it. PATCH <path>/:id/restore brings it back. Deleting a deleted record, or restoring a kept one, changes nothing and returns 200.
Colours
A colour field takes one of: red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose, or null.
Permissions
| Request | API permission | Record permission |
|---|---|---|
GET list and read |
api_read |
customisation |
POST |
api_create |
customisation |
PATCH, including restore |
api_update |
customisation |
DELETE |
api_delete |
customisation |
GET /api/v1/cost_types (in Lookup lists) is a separate, read-only list that needs read_costs instead.
Custom fields
Fields that appear on projects and opportunities. Adding a field to a template is a separate step: see project template rows. Fields used only on contracts are not in this catalog.
| Field | Create | Rules |
|---|---|---|
label |
Required | The name shown on the field. Cannot be blank |
field_type |
Required | text, long_text, link, number, date, date_and_time, select, multiselect, single_file or multiple_files. Cannot change after create (422 invalid_input) |
options |
For select and multiselect |
An array of at least two strings. Refused on other types. Replaces the options |
helper_text, placeholder |
No | Text, or null |
number_unit |
No | For number fields: none (default), currency, percent, metres, square_metres, cubic_metres, feet, square_feet, cubic_feet, millimetres, kilograms, tonnes, hours, days or items. Another unit on another type returns 422 validation_failed |
POST /api/v1/custom_fields
{"label": "Council", "field_type": "select", "options": ["Northern Beaches", "Inner West", "Sutherland"]}
{
"id": 31,
"label": "Council",
"field_type": "select",
"helper_text": null,
"placeholder": null,
"number_unit": "none",
"options": ["Northern Beaches", "Inner West", "Sutherland"],
"deleted": false,
"warnings": []
}
Not found code: custom_field_not_found.
Resource types
Staff roles that people work and bill under. Rate cards price them, and budget resource lines use them.
| Field | Create | Rules |
|---|---|---|
name |
Required | Cannot be blank (422 validation_failed) |
description |
No | Text, or null |
colour |
No | See Colours |
line_item_code_id |
No | A line item code of the account, or null |
default_member_ids |
No | Members whose default billable role this is. Replaces the current list |
optional_member_ids |
No | Members who may also bill under this role. Replaces the current list |
Each member list is an array of member ids from GET /members. A removed member, or an id from another account, returns 404 member_not_found and nothing changes. Sending one list leaves the other as it is. Send [] to empty a list.
The record reads back with line_item_code, default_members and optional_members as links (see the list example).
Not found code: resource_type_not_found.
Cost types
Kinds of cost, such as subcontractors or travel. Every cost and every budget cost line has one.
The setup endpoints are under /api/v1/settings/cost_types. /api/v1/cost_types is the read-only lookup list for costs.
| Field | Create | Rules |
|---|---|---|
name |
Required | Cannot be blank |
description |
No | Text, or null |
line_item_code_id |
No | A line item code of the account, or null |
default_finance_account_id |
No | A visible expense account from GET /finance_accounts, or null. Any other account returns 404 finance_account_not_found |
show_on_cost_to_complete_estimates |
No | true or false. Default true |
include_summary_line |
No | true adds a summary line with the project number to each new cost. Default false |
default |
No | true makes this the account’s default cost type |
expense_default |
No | true makes this the default cost type for expense claims |
The flags refuse null.
Only one cost type can be the default. Setting default: true while another cost type is the default returns 422 validation_failed. Set default: false on the current default first. On a PATCH, expense_default: true moves the expense default from the other cost type. On a POST it does not, so create the cost type first and then set expense_default with a PATCH.
{
"id": 610,
"name": "Subcontractor",
"description": "Trade packages let to subcontractors",
"line_item_code": null,
"default_finance_account": {"id": 310, "name": "Subcontractors"},
"show_on_cost_to_complete_estimates": true,
"include_summary_line": false,
"default": false,
"expense_default": false,
"deleted": false
}
Not found code: cost_type_not_found.
Rate cards
The account’s rate cards: the billable rate of each resource type and of named members. These endpoints reach account rate cards only. A proposal template’s own card is under Proposal templates. Project rate cards are not reachable.
| Field | Create | Rules |
|---|---|---|
name |
Required | Cannot be blank (422 validation_failed) |
description |
No | Text, or null |
status |
No | draft, active or expired |
effective_date |
No | yyyy-mm-dd. Default: today in the account’s time zone. Cannot be null |
is_default |
No | true makes the card the account default |
entries |
No | Rates to add or change (see below) |
Entries
Send each entry as:
| Field | Rules |
|---|---|
rateable_type |
resource_type (a role rate) or member (a rate for one person). Required for a new entry |
rateable_id |
A kept resource type or member of the account. Required for a new entry |
billable_rate_cents |
Integer cents, 0 or more |
allow_zero_value |
true keeps an entry at a rate of 0 |
id |
An existing entry’s id, to change that entry |
entries merges into the card:
- An entry matches an existing one by
id, or else by itsrateable_typeandrateable_id. A match is changed. Anything else is added. - Entries you leave out stay on the card. No rate card write deletes an entry. Use Delete a rate card entry.
- An entry cannot change its role or person (
422 invalid_input). Add a new entry instead. - The same role or person twice in one request returns
422 invalid_input. - A rate of 0 needs
allow_zero_value: true, for a new or an existing entry. Without it the request returns422 invalid_input, with afieldsuch asentries[0].billable_rate_cents. The rate is never dropped silently.
An entry reads back as:
{
"id": 5120,
"record_kind": "rate_card_entry",
"rateable_type": "resource_type",
"rateable": {"id": 88, "name": "Senior Engineer"},
"billable_rate": {"cents": 18000, "currency": "AUD", "formatted": "$180.00"},
"allow_zero_value": false
}
Defaults and status
is_default: trueon an active card makes it the default straight away. The previous default card stops being the default and becomesexpired. Proposal templates that used the previous default move to the new card, and budget lines that still had the old card’s rate for their role take the new rate. This runs in the background.is_default: trueon adraftcard does not take effect yet. The card reads back withis_default: falseanddefault_on_activation: true. Drum activates draft cards on their effective date, and the card becomes the default then.- A default card is always
active. - The default card cannot be deleted:
409 default_rate_card. Make another card the default first.
POST /api/v1/rate_cards
{
"name": "2027 rates",
"status": "draft",
"effective_date": "2027-01-01",
"is_default": true,
"entries": [
{"rateable_type": "resource_type", "rateable_id": 88, "billable_rate_cents": 19500},
{"rateable_type": "member", "rateable_id": 4821, "billable_rate_cents": 21000}
]
}
{
"id": 96,
"name": "2027 rates",
"description": null,
"status": "draft",
"effective_date": "2027-01-01",
"is_default": false,
"default_on_activation": true,
"project": null,
"proposal_budget": null,
"entries": [
{"id": 5131, "record_kind": "rate_card_entry", "rateable_type": "member", "rateable": {"id": 4821, "name": "Jordan Reed"}, "billable_rate": {"cents": 21000, "currency": "AUD", "formatted": "$210.00"}, "allow_zero_value": false},
{"id": 5130, "record_kind": "rate_card_entry", "rateable_type": "resource_type", "rateable": {"id": 88, "name": "Senior Engineer"}, "billable_rate": {"cents": 19500, "currency": "AUD", "formatted": "$195.00"}, "allow_zero_value": false}
],
"deleted": false,
"warnings": []
}
project and proposal_budget are always null on an account card.
Not found codes: rate_card_not_found, rate_card_entry_not_found (an entry id not on this card), resource_type_not_found, member_not_found.
Delete a rate card entry
DELETE /api/v1/rate_card_entries/:id deletes one entry of an account rate card, or of a proposal template’s card, for good. Budget lines that already took the rate keep it.
Needs api_delete. The record permission is customisation for an account card, and update_budgets for a proposal template’s card.
{"id": 5131, "deleted": true, "permanently_deleted": true, "warnings": []}
An entry of a project rate card, or of a deleted proposal template, returns 404 rate_card_entry_not_found.
Project roles
The roles a member can hold on a project or an opportunity, and the project permissions each role gives.
| Field | Create | Rules |
|---|---|---|
name |
Required | Cannot be blank |
use_case |
No | project (default) or opportunity. Cannot change after create (422 invalid_input) |
position |
No | An integer, 0 or more. The role’s place in its list |
allow_multiple |
No | true lets more than one member hold the role on a project |
primary_role |
No | true or false |
account_role_id |
No | A kept account role, or null. Unknown: 404 account_role_not_found |
permissions |
No | An object of permission flags (see below). Flags you leave out stay the same |
The flags and the flags in permissions refuse null.
Permission flags: edit_all_time_entries, view_scheduled_invoices, create_scheduled_invoices, update_scheduled_invoices, delete_scheduled_invoices, view_invoices, create_invoices, update_invoices, delete_invoices, view_progress_claims, create_progress_claims, update_progress_claims, delete_progress_claims, view_budgets, update_budgets, delete_budgets, view_contract_value, view_internal_budget, view_resourcing, edit_resourcing and allow_over_budget_resourcing. An unknown flag returns 422 invalid_input.
POST /api/v1/project_roles
{"name": "Project Lead", "use_case": "project", "permissions": {"view_budgets": true, "update_budgets": true, "view_contract_value": true}}
{
"id": 14,
"name": "Project Lead",
"use_case": "project",
"position": 3,
"allow_multiple": false,
"primary_role": false,
"account_role": null,
"permissions": {
"edit_all_time_entries": false,
"view_scheduled_invoices": false,
"view_budgets": true,
"update_budgets": true,
"view_contract_value": true
},
"deleted": false,
"warnings": []
}
The response lists every permission flag. The example is shortened.
The account keeps at least one role of each use case. Deleting the last kept project role, or the last kept opportunity role, returns 409 last_project_role.
Not found code: project_role_not_found.
Booking types
Kinds of resource booking, such as site visits or leave cover.
| Field | Create | Rules |
|---|---|---|
name |
Required | Cannot be blank (422 validation_failed) |
description |
No | Text, or null |
colour |
No | See Colours |
default_resource_type_id |
No | A kept resource type, or null |
resource_type_ids |
No | Kept resource types that can take this booking. Replaces the current list. [] empties it |
An unknown or deleted resource type returns 404 resource_type_not_found.
{
"id": 20,
"name": "Site visit",
"description": null,
"colour": "amber",
"default_resource_type": {"id": 88, "name": "Senior Engineer"},
"resource_types": [{"id": 88, "name": "Senior Engineer"}, {"id": 91, "name": "Graduate Engineer"}],
"deleted": false
}
Not found code: booking_type_not_found.
Milestone types
Kinds of project milestone.
| Field | Create | Rules |
|---|---|---|
name |
Required | Cannot be blank |
colour |
No | See Colours |
The record reads back as {id, name, colour, deleted}.
Not found code: milestone_type_not_found.
Errors
Error bodies are {"error", "code"}, with field on input and lookup errors (see Errors).
| Status | code |
When |
|---|---|---|
403 |
not_authorized |
A missing API permission, or no customisation |
404 |
not_found |
The setup endpoints are not turned on for the account |
404 |
<catalog>_not_found |
The record, or a record a field names, is not in the account. The code names the record, such as cost_type_not_found or member_not_found |
409 |
default_rate_card |
The card is the account default |
409 |
last_project_role |
The role is the last kept role of its use case |
422 |
missing_input, invalid_input |
A required field is missing, a field is unknown or has the wrong type, or a rule above refused the value |
422 |
validation_failed |
The record would not save. errors lists why |