Projects
Projects are the top-level organizational unit in Ditio. Every work order (task), time registration, and resource allocation belongs to a project — so most ERP integrations start here: create projects, then add work orders inside them.
Base URL: api/v4/integration/projects · Scope: ditioapiv3
Examples use the environment variables from Environments and default to the test environment.
Create a project
Section titled “Create a project”POST /api/v4/integration/projectscurl -X POST "$DITIO_API_BASE/api/v4/integration/projects" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "companyId": "YOUR_COMPANY_ID", "projectNumber": "P-2026-001", "name": "Prosjekt Fjellhall", "active": true, "startDate": "2026-03-01T00:00:00Z", "finishedDatePlanned": "2026-12-31T00:00:00Z", "projectCustomerName": "Eksempel Kommune", "projectAddress": "Fjellveien 1, Trondheim", "mainContactEmail": "[email protected]" }'Response: the created project with its generated id.
{ "id": "65f1a2b3c4d5e6f7a8b9c0d1", "companyId": "YOUR_COMPANY_ID", "projectNumber": "P-2026-001", "name": "Prosjekt Fjellhall", "active": true}Required fields
Section titled “Required fields”| Field | Type | Description |
|---|---|---|
companyId | string | Your Ditio company ID |
projectNumber | string | Your internal project number (must be unique within the company) |
Optional fields
Section titled “Optional fields”| Field | Type | Description |
|---|---|---|
name | string | Project name |
externalProjectNumber | string | Project number in your external system. Free text for most companies, but validated against a configured list for some — see External project number validation |
externalDimension1 | string | External dimension 1 (for accounting/ERP mapping) |
externalDimension2 | string | External dimension 2 (for accounting/ERP mapping) |
description | string | Project description |
active | bool | Whether the project is active (default: true). On a create in a company with no template project this is forced to true and an active: false in the payload is ignored — send a follow-up PATCH to deactivate. With a template project the value is honoured. |
requireTaskNumber | bool | Require a work order number for time registrations |
blockedForRegistration | bool | Block all time registrations on this project |
startDate | datetime | Project start date |
finishedDatePlanned | datetime | Planned completion date |
finishedDate | datetime | Actual completion date |
estimatedHourQty | double | Estimated total hours |
mainForemanId | string | Main foreman (user ID, phone number, name, or employee number) |
projectLeaderId | string | Project leader (user ID, phone number, name, or employee number) |
payrollResponsibleId | string | Payroll responsible (user ID, phone number, name, or employee number) |
mainContactEmail | string | Contact email for the project |
projectCustomerName | string | Customer name |
customerNumber | string | External customer number. When provided, the project is linked to an existing company customer with this number, or a new customer is created (named with projectCustomerName) and linked. Omit to leave any existing customer link unchanged. |
projectAddress | string | Project address |
projectLocationLatitude | double | GPS latitude |
projectLocationLongitude | double | GPS longitude |
workBreakLunchStartTime | string | Lunch break start time (HH:mm) |
workBreakLunchLength | int | Lunch break duration in minutes |
workBreakDinnerStartTime | string | Dinner break start time (HH:mm) |
workBreakDinnerLength | int | Dinner break duration in minutes |
External project number validation
Section titled “External project number validation”For most companies externalProjectNumber is a free-text reference field — send
any value your system uses, or omit it.
For some companies Ditio instead validates the field against a configured list of valid external project numbers. Two things have to be true for that to happen — the company must have the validation enabled and have values configured — so the presence of a list does not by itself mean writes are checked. Where it does apply:
-
The value must match one of the configured values exactly.
-
The field becomes required — an empty or omitted value is rejected too.
-
Anything else fails with
400 Bad Requestand a message of the form:Extern prosjektnummer <value> er ikke en gyldig. Velg fra listen(“External project number
<value>is not valid. Choose from the list.”)
If you hit that error, the value you are sending is not in the company’s list.
You can read and manage that list yourself — see
Project External References.
List the ExternalProjectNumber type to see which values are accepted, and
create the missing one if your value is not there.
Template projects
Section titled “Template projects”A company can designate one project as a template project. When a project is created through this endpoint and a template project exists in the same company, the new project is copied from that template instead of being created blank. No request field asks for the copy — it applies automatically whenever the company has a template.
This is how you give every project an identical starting point: the same
standard work orders (for example Kontraktsarbeid and Tilleggsarbeid), the
same lunch break, the same automatic-checkout limit, the same checklists and
the same project personnel — without sending any of it in the payload.
Setting up a template project
Section titled “Setting up a template project”In Ditio Web, open the project you want to use as the master and tick Template on the project detail page. Ticking it sets the project inactive and blocked for registration and then locks both of those fields, so a template never appears for field workers and nobody can register time against it. It is also listed separately from your ordinary projects.
The Template checkbox is only visible to company administrators.
Mark only one project per company as a template. If several are marked, which one gets used is undefined.
What is inherited
Section titled “What is inherited”| Area | Copied from the template |
|---|---|
| Work orders | The work-order structure, including nested sub-orders, with per-order settings such as unit, type and prices, and any images attached to them — though not the project’s own logo (see the caveat below) |
| Working hours | Lunch and dinner break start and length, automatic checkout, checkout push-reminder, shift settings, automatic overtime calculation |
| Project settings | Period planning on/off, work-order auto-planning, require work order number, show chapters in apps, allow work-order creation in the app, max work orders and resources per user, max days inactive, contact email, project address (text only — see below) |
| Notifications | The project’s notification setup |
| Checklists | The project’s checklist (quality assurance) setup |
| Personnel | The users on the template with their project roles and planning, the assigned machines, and the role holders: main foreman, project leader and payroll responsible. Only users still active in the template’s company are copied, and none when the template belongs to another company than the one the project is created in (see below) |
| Recipients and prices | Contact persons, who receive the automatic reports and keep any crew-list and feed access set on the template (copied under the same company condition as the personnel), and the machine price agreements |
| More settings | General HSE instruction, project links, require overtime reason, log user location, business area and WBS structure |
Several of these have no equivalent field on this endpoint — shift settings, automatic overtime calculation and automatic checkout among them. A template project is the only way to set them on projects created through the Integration API.
What is not inherited
Section titled “What is not inherited”- The external HSE register ID
- Subcontractor structure
- Customer info
- Project documents — drawings and contracts attached to the template are not copied; push them per project with Documents
- Machine price agreements — note that this is only the project’s machine pricing; unit prices on the work orders themselves are copied
- The map coordinate — the template’s project address is copied as text,
but its coordinate is not, so a copied project has an address and no point on
the map. Send
projectAddress(orprojectLocationLatitude/projectLocationLongitude) in your create request to get one. The geofence setting is not copied either, so geofenced check-in is off on the copy. - The piece-work prefix — it decides how the project’s hours are spread in payroll, so set it per project in Ditio Web.
This covers the settings integrators ask about; it is not a field-by-field schema. Treat the inherited table above as the definitive list — anything not in it starts blank on the copy.
Precedence and timing
Section titled “Precedence and timing”- Your payload wins. Any field you send on create overrides the value
copied from the template. A copy is created active and open for registration
unless your payload sets
activeorblockedForRegistration. - Most omitted fields do not fall back to the template. Only
name,requireTaskNumber,mainContactEmail,projectAddress, the fourworkBreak*fields and the role holdersmainForemanId,projectLeaderIdandpayrollResponsibleIdcarry over — so omittingnamegives you a project named after your template. Every other optional field —description,startDate,finishedDate,finishedDatePlanned,estimatedHourQty,customerNumber,projectCustomerName,projectLocationLatitude/projectLocationLongitude— starts blank unless you send it. - A role holder you send replaces the template’s. When you send
mainForemanIdorprojectLeaderId, your user takes that role. The template’s own foreman or project leader stays in the project personnel as an ordinary user, without foreman or project-leader rights. - Updates never read the template. Only a create copies it, so a value changed on the project afterwards is never overwritten from the template.
- External ids are written from your payload unconditionally.
externalProjectNumber,externalDimension1andexternalDimension2are applied after the copy whether or not you sent them, so omitting them clears the template’s values. Send them explicitly if the new project needs them. - Creates, not just
POST. The copy runs whenever a request resolves to no existing project — and that includesPUT /{id}, where the route id is ignored and the body is matched exactly as onPOST. APUTthat matches nothing therefore creates a project and copies the template. OnlyPATCHis exempt. - The template is looked up in the
companyIdyou send, but the project is created in the company your token belongs to. These are the same for a single-company integration. In a mother / project-company setup they can differ, so keep them the same unless you specifically know otherwise. When they differ, the template’s personnel, role holders and contact persons are not copied.
Work orders have an equivalent mechanism of their own — see Template work orders for the settings a new work order inherits within a project.
Get all projects
Section titled “Get all projects”GET /api/v4/integration/projectsReturns every project owned by your token’s company, active and inactive. Projects owned by your project companies or subsidiaries are not included; to read across a company structure, use project personnel or the Project Data guide.
Get a project by ID
Section titled “Get a project by ID”GET /api/v4/integration/projects/{id}Returns 404 Not Found unless the project is owned by your token’s company.
Get a project by project number
Section titled “Get a project by project number”GET /api/v4/integration/projects/by-project-number/{projectNumber}curl "$DITIO_API_BASE/api/v4/integration/projects/by-project-number/P-2026-001" \ -H "Authorization: Bearer $TOKEN"Useful when you know the project number from your external system but not the Ditio ID. The number is looked up among the projects owned by your token’s company.
| Result | Response |
|---|---|
| One project has the number | 200 with the project |
| No project has the number | 200 with the body null (not 404) |
| Several projects have the number, exactly one of them active | 200 with the active project |
| Several projects have the number, and not exactly one is active | 400 Bad Request |
Get project personnel and roles
Section titled “Get project personnel and roles”GET /api/v4/integration/projects/{id}/personnelGET /api/v4/integration/projects/personnelReturns the people on a project’s personnel list and the role each has on the project, as set up in Period Planning in Ditio Web. Use it to sync site managers, foremen and HSE contacts to your own system. Like Period Planning, it lists only people with an active or upcoming employment in the project’s company; people whose employment has ended are left out.
The first form returns one project. The second returns every project you
can reach in one call, active projects only unless you add
?includeInactive=true.
The company scope here is wider than on the other project endpoints, which
only see projects owned by your token’s company. Personnel follows the same
convention as the Data Extraction API: a
parent company gets the projects of its whole structure, its project
companies and its subsidiaries. Any other company gets only its own
projects. On the
company-wide form, add ?excludeDataFromSubsidiaries=true to leave
subsidiaries out, or ?companyId=<id> for one company only.
projectCompanyId tells you which company owns each project. Projects
another company has only shared with you are not included.
curl "$DITIO_API_BASE/api/v4/integration/projects/65f1a2b3c4d5e6f7a8b9c0d1/personnel" \ -H "Authorization: Bearer $TOKEN"Response:
{ "projectId": "65f1a2b3c4d5e6f7a8b9c0d1", "projectCompanyId": "YOUR_PROJECT_COMPANY_ID", "projectNumber": "P-2026-001", "externalProjectNumber": "EXT-001", "projectName": "Prosjekt Fjellhall", "active": true, "personnel": [ { "userId": "3f2a9c14-8b7d-4e52-9a01-6c4d2e8f1b73", "name": "Kari Nordmann", "companyId": "YOUR_COMPANY_ID", "role": "ProjectManager", "roleName": "Anleggsleder", "isMainRole": true }, { "userId": "8c1d5e7a-2b4f-4c9e-b0a3-7d6e5f4c3b2a", "name": "Ola Nordmann", "companyId": "YOUR_COMPANY_ID", "role": "Foreman", "roleName": "Formann", "isMainRole": false } ]}| Field | Description |
|---|---|
projectCompanyId | The company that owns the project: yours, or one of your project companies or subsidiaries |
userId | The person’s identity id — the same value as identityId in the Users API |
name | Display name from the person’s profile in the project’s company |
companyId | The person’s employer as recorded on that profile, which Period Planning groups people by: the subcontractor for subcontractor personnel, otherwise the profile’s own company (also for a subcontractor registered only by name) |
role | ProjectManager, Hse, Foreman, Bas or User |
roleName | The role as Ditio Web shows it: Anleggsleder, Hms, Formann, Bas or Bruker |
isMainRole | true only for the project manager in the project’s projectLeaderId and the foreman in its mainForemanId |
A project can have several people in each role. Only the main project
manager and the main foreman also appear on the project itself, as
projectLeaderId and mainForemanId; everyone else with a role is only
available from this endpoint. People on the personnel list without a role
are returned with role: "User".
The company-wide form returns a JSON array of these project objects,
ordered by project number. Within a project, people are ordered by role
(ProjectManager, Hse, Foreman, Bas, User) and then by name. It
returns every project in one response, so expect a large body for a
company with many projects.
The single-project form returns the same 404 Not Found for a project that
does not exist and for one you cannot reach.
Update a project (PUT)
Section titled “Update a project (PUT)”PUT /api/v4/integration/projects/{id}Despite the method, PUT is not a full replace: fields you omit (or send as
null) keep their stored value. It is the same create-or-update as POST.
The {id} in the route is ignored, and the project is found from the body
as described in What counts as “already exists”.
A PUT that matches no project creates one.
curl -X PUT "$DITIO_API_BASE/api/v4/integration/projects/65f1a2b3c4d5e6f7a8b9c0d1" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "companyId": "YOUR_COMPANY_ID", "projectNumber": "P-2026-001", "name": "Prosjekt Fjellhall - fase 2", "active": true, "startDate": "2026-03-01T00:00:00Z", "finishedDatePlanned": "2027-06-30T00:00:00Z" }'Update a project (partial)
Section titled “Update a project (partial)”PATCH /api/v4/integration/projects/{id}Updates only the fields you include; all other fields remain unchanged.
Unlike PUT, the project is the one in the route, and it must be owned by
your token’s company, otherwise the API returns 404 Not Found.
curl -X PATCH "$DITIO_API_BASE/api/v4/integration/projects/65f1a2b3c4d5e6f7a8b9c0d1" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Prosjekt Fjellhall - nytt navn", "active": false }'Delete a project
Section titled “Delete a project”DELETE /api/v4/integration/projects/{id}On success the API returns 200 with the deleted project. The delete
permanently removes the project and its work orders, plans, notifications
and settings. This cannot be undone. Consider deactivating the project
(active: false) instead.
Only a project owned by your API client’s company is deleted. For a project
owned by any other company, including other companies in your structure,
DELETE removes your company from the project’s sharing and returns 200
with the project; the project itself is not deleted.
Attaching documents
Section titled “Attaching documents”Projects can carry documents (drawings, contracts) that field workers see in the mobile app — see Documents.
Related
Section titled “Related”- Work Orders (Tasks) — the units of work inside a project
- Documents — push files to a project
- Project Data guide — bulk export of the full project catalogue
- Help centre: Prosjekt — project setup in Ditio Web (Norwegian)