Account settings
The setup endpoints build and change an account’s settings: the settings catalogs, deliverable and project templates, proposal templates, and the settings on this page. Every write runs the same code as the settings screen in Drum, so the same rules apply.
These endpoints are in early access. An account that does not have them turned on gets 404 {"error": "Not found", "code": "not_found"} from every setup endpoint. Contact Drum support to turn them on.
Endpoints
| Method and path | Does |
|---|---|
GET /api/v1/account_settings |
Lists the ids and names of every template and catalog record |
GET /api/v1/project_settings |
Reads Project Settings (the project baseline template) |
PATCH /api/v1/project_settings |
Changes Project Settings |
GET /api/v1/opportunity_settings |
Reads Opportunity Settings (the opportunity baseline template) |
PATCH /api/v1/opportunity_settings |
Changes Opportunity Settings |
GET /api/v1/proposal_numbering |
Reads how proposal numbers are made |
PATCH /api/v1/proposal_numbering |
Changes how proposal numbers are made |
Rules for every setup endpoint
These rules apply to this page, Settings catalogs, Templates and Proposal templates.
Reading
- Money reads back as an object:
{"cents": 18000, "currency": "AUD", "formatted": "$180.00"}. Money you send is integer cents in a field ending_cents, such asbillable_rate_cents. All money is tax exclusive. - A link to another record reads back as
{"id": 88, "name": "Senior Engineer"}, ornull. You send the id in a field ending_id, such asresource_type_id. - Rows of a template carry
record_kind, the type of the row, such asdeliverable_template_taskorbudget_item_resource. - Lists are not paginated. A list endpoint returns every record in one response.
- Warnings. Most responses have
warnings, a list of{code, message}. Some warnings addsubject_id,countorsubject_ids. A warning never means the write failed. The template pages list the warnings they send.
Writing
- Send JSON fields at the top level. Do not wrap them in a root key such as
{"rate_card": {...}}. - A field that the endpoint does not take returns
422 invalid_input, with the field infield. - A field with a value of the wrong type returns
422 invalid_input. It is never ignored. For example,{"name": {"x": 1}}is refused. PATCHchanges only the fields you send. Sendnullto clear an optional field. A field that cannot be empty, such as a status or a flag, refusesnull: leave it out to keep its value.- A
POSTcan include the child rows of the new record, such as the tasks of a new deliverable template. A row in a create has noid. - A
PATCHchanges one record’s own fields. A list of child rows on aPATCHreturns422 invalid_input, and the message names the row endpoint to use instead. - Only a
DELETEremoves a row. No write removes a row because you left it out. - Retrying a
POSTcreates a second record. These endpoints do not take anIdempotency-Key. - Template and record names do not have to be unique.
Deleted records
Most setup records are soft deleted: DELETE hides them, and PATCH .../restore brings them back. A response for a deleted record has "deleted": true. Each page says which rows are deleted for good instead.
API permissions
Each request needs the API permission for its HTTP method (see The four API permissions). A few POST routes run a workflow that changes or removes many rows. They need the permissions of what they do, not api_create. Each one is listed on its page. Without them the request returns 403 {"error": "Insufficient API permissions", "code": "not_authorized"}.
The record permission (the role permission in Drum) is listed with each endpoint. Without it the request returns 403 {"error": "You do not have permission to do this", "code": "not_authorized"}.
account_id
account_id is accepted on every setup endpoint, but a customer token can only name its own account. Another account’s id returns 403 not_authorized. Leave it out.
Read the account catalog
GET /api/v1/account_settings returns the id and name of every kept template and catalog record. Use it to find the ids that the other setup endpoints need.
Needs api_read, and customisation or read_quotes.
curl -H "Authorization: Bearer $DRUM_API_TOKEN" \
https://app.getdrum.com/api/v1/account_settings
{
"account": {"id": 318, "name": "Harbour Design Studio"},
"deliverable_templates": [
{"id": 2210, "name": "Concept design", "status": "active", "use_case": "all_types", "default_budget_type": "fixed_fee"}
],
"project_templates": [
{"id": 640, "name": "Project Settings", "use_case": "project", "baseline": true, "default": false, "status": "active"},
{"id": 655, "name": "Residential new build", "use_case": "project", "baseline": false, "default": true, "status": "active"}
],
"project_statuses": {
"project": [{"id": 401, "name": "Not Started", "default": true}],
"opportunity": [{"id": 301, "name": "Enquiry", "default": true}]
},
"lead_scores": [{"id": 51, "name": "Warm", "percentage": 50, "default": true}],
"proposal_templates": [
{"id": 7702, "name": "Residential fee proposal", "project_template": {"id": 655, "name": "Residential new build"}}
],
"resource_types": [{"id": 88, "name": "Senior Engineer"}],
"cost_types": [{"id": 610, "name": "Subcontractor"}],
"rate_cards": [{"id": 95, "name": "2026 rates", "default": true}],
"custom_fields": [{"id": 31, "name": "Council"}],
"milestone_types": [{"id": 12, "name": "Handover"}],
"booking_types": [{"id": 20, "name": "Site visit"}],
"project_roles": [{"id": 14, "name": "Project Lead"}]
}
| Field | Meaning |
|---|---|
account |
The account the call acts on |
project_templates |
Includes the two baselines (baseline: true): Project Settings and Opportunity Settings |
project_statuses |
The statuses of Project Settings (project) and Opportunity Settings (opportunity), in their list order. A project and an opportunity never share a status |
lead_scores |
The lead scores of Opportunity Settings, in list order |
proposal_templates |
name is the template’s name. project_template is the linked project template, or null |
rate_cards |
Account rate cards only. default marks the account default |
custom_fields |
name is the field’s label |
Other lists are sorted by name. Deleted records are left out.
A list the token’s user cannot see in Settings is empty:
- Without
customisation:deliverable_templates,custom_fields,milestone_types,booking_typesandproject_rolesare empty. - Without
read_quotes:proposal_templatesis empty.
Read Project Settings or Opportunity Settings
GET /api/v1/project_settings reads Project Settings. GET /api/v1/opportunity_settings reads Opportunity Settings. Each is a baseline template: the statuses, custom fields and numbering that every project, or every opportunity, uses.
Needs api_read and customisation.
| Parameter | Meaning |
|---|---|
include_deleted |
true also returns deleted statuses, lead scores, deliverables and custom fields, each with "deleted": true |
The response is a project template document. Its fields are in The project template document. The id is the baseline’s id. Use it with the project template row endpoints to add, change or delete statuses, lead scores, deliverables and custom fields.
curl -H "Authorization: Bearer $DRUM_API_TOKEN" \
https://app.getdrum.com/api/v1/opportunity_settings
{
"id": 641,
"name": "Opportunity Settings",
"description": null,
"status": "active",
"use_case": "opportunity",
"baseline": true,
"default": false,
"code": "OPP",
"contract_type": "fixed_price",
"default_rate_card": {"id": 95, "name": "2026 rates"},
"numbering": {
"part_one_generator": "current_year_two_digit",
"part_two_generator": "sequential_number_current_year",
"part_three_generator": null,
"code_separator": "-",
"number_padding": 3,
"all_time_starting_number": 1,
"joining_year_starting_number": 1,
"number_prefix": "OPP",
"number_suffix": null,
"client_number_padding": 3,
"defer_number_until_quote": false
},
"phases": [],
"deliverables": [],
"statuses": [
{
"id": 301,
"record_kind": "project_status",
"name": "Enquiry",
"colour": "blue",
"position": 1,
"complete_status": false,
"default": true,
"default_successful_status": false,
"default_lost_status": false,
"sales_status_type": "standard",
"project_template_id": 641,
"deleted": false
}
],
"lead_scores": [
{"id": 51, "record_kind": "lead_score", "project_template_id": 641, "name": "Warm", "percentage": 50, "default": true, "position": 1, "deleted": false}
],
"custom_fields": [],
"warnings": []
}
If the account has no baseline of that kind, the read returns 404 baseline_template_not_found.
Change Project Settings or Opportunity Settings
PATCH /api/v1/project_settings and PATCH /api/v1/opportunity_settings change the baseline’s own fields. They never create a second baseline. Fields you leave out stay the same.
Needs api_update and customisation.
| Field | Rules |
|---|---|
name |
Cannot be blank |
description |
Text, or null |
status |
draft or active |
contract_type |
fixed_price or time_and_materials |
default |
true or false |
code |
The code used in numbers, such as PRJ |
default_rate_card_id |
An active account rate card whose effective date has passed, or null |
part_one_generator, part_two_generator, part_three_generator |
A number part (see below), or null for no part |
code_separator, number_prefix, number_suffix |
Text, or null |
number_padding, client_number_padding, all_time_starting_number, joining_year_starting_number |
Integers |
defer_number_until_quote |
true or false. Opportunity Settings only: an opportunity gets its number when its first proposal is made. On Project Settings, true returns 422 validation_failed |
Number parts: sequential_number_all_time, sequential_number_current_year, current_year_two_digit, current_year_four_digit, client_number, current_month_two_digit, sequential_number_current_month, client_code, project_type_code, location_code, location_sequential_number and july_to_june_financial_year. client_number, client_code and the two location parts are accepted only when the account uses client numbers, client codes or locations. Otherwise the save returns 422 validation_failed.
statuses, lead_scores, custom_fields, phases, deliverables and sync_custom_fields are refused with 422 invalid_input. Change those rows with the project template row endpoints, using the baseline’s id. To push custom field changes to active projects, use POST /project_templates/:id/sync_custom_fields.
PATCH /api/v1/project_settings
{
"code": "PRJ",
"part_one_generator": "current_year_two_digit",
"part_two_generator": "sequential_number_current_year",
"code_separator": "-",
"number_padding": 3
}
Returns 200 with the template document.
Read proposal numbering
GET /api/v1/proposal_numbering returns how new proposal numbers are made, as on Settings > Proposal Numbering.
Needs api_read and customisation.
{
"part_one_generator": "current_year_two_digit",
"part_two_generator": "sequential_number_current_year",
"part_three_generator": null,
"number_prefix": "Q",
"number_suffix": null,
"code_separator": "-",
"number_padding": 3,
"all_time_starting_number": 1,
"joining_year_starting_number": 1,
"generator_options": [
"sequential_number_all_time",
"sequential_number_current_year",
"current_year_two_digit",
"current_year_four_digit"
],
"warnings": []
}
| Field | Meaning |
|---|---|
part_one_generator, part_two_generator, part_three_generator |
The three parts of the number, in order. Each is one of generator_options, or null for no part |
number_prefix, number_suffix |
Text before and after the number, or null |
code_separator |
Text between the parts, or null |
number_padding |
The number of digits a sequential number is padded to, from 0 to 7 |
all_time_starting_number |
The first number of sequential_number_all_time |
joining_year_starting_number |
The first number of sequential_number_current_year in the year the Drum account was created. Later years start again from the beginning |
generator_options |
Every value a part can take |
Change proposal numbering
PATCH /api/v1/proposal_numbering changes the fields you send. Every field of the read is accepted except generator_options.
Needs api_update and customisation.
| Field | Rules |
|---|---|
part_one_generator, part_two_generator, part_three_generator |
One of generator_options, or null |
number_prefix, number_suffix, code_separator |
Text, or null |
number_padding |
An integer from 0 to 7 |
all_time_starting_number, joining_year_starting_number |
An integer, 0 or more |
curl -X PATCH https://app.getdrum.com/api/v1/proposal_numbering \
-H "Authorization: Bearer $DRUM_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"part_one_generator": "current_year_two_digit", "number_prefix": "Q", "number_padding": 3}'
Returns 200 with the numbering. A change applies to proposals numbered after it.
Permissions
| Endpoint | API permission | Record permission |
|---|---|---|
GET /account_settings |
api_read |
customisation or read_quotes |
GET /project_settings, GET /opportunity_settings |
api_read |
customisation |
PATCH /project_settings, PATCH /opportunity_settings |
api_update |
customisation |
GET /proposal_numbering |
api_read |
customisation |
PATCH /proposal_numbering |
api_update |
customisation |
Errors
Error bodies are {"error", "code"}, with field on input and lookup errors (see Errors).
| Status | code |
When |
|---|---|---|
403 |
not_authorized |
A missing API or record permission, or an account_id of another account |
404 |
not_found |
The setup endpoints are not turned on for the account |
404 |
baseline_template_not_found |
The account has no baseline of that kind |
404 |
rate_card_not_found |
default_rate_card_id is not an active account rate card |
422 |
invalid_input, missing_input |
A field is unknown, has the wrong type, or is out of range |
422 |
validation_failed |
The template would not save. errors lists why |