Skip to content

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.

POST /api/v4/integration/projects
Terminal window
curl -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
}
FieldTypeDescription
companyIdstringYour Ditio company ID
projectNumberstringYour internal project number (must be unique within the company)
FieldTypeDescription
namestringProject name
externalProjectNumberstringProject number in your external system. Free text for most companies, but validated against a configured list for some — see External project number validation
externalDimension1stringExternal dimension 1 (for accounting/ERP mapping)
externalDimension2stringExternal dimension 2 (for accounting/ERP mapping)
descriptionstringProject description
activeboolWhether 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.
requireTaskNumberboolRequire a work order number for time registrations
blockedForRegistrationboolBlock all time registrations on this project
startDatedatetimeProject start date
finishedDatePlanneddatetimePlanned completion date
finishedDatedatetimeActual completion date
estimatedHourQtydoubleEstimated total hours
mainForemanIdstringMain foreman (user ID, phone number, name, or employee number)
projectLeaderIdstringProject leader (user ID, phone number, name, or employee number)
payrollResponsibleIdstringPayroll responsible (user ID, phone number, name, or employee number)
mainContactEmailstringContact email for the project
projectCustomerNamestringCustomer name
customerNumberstringExternal 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.
projectAddressstringProject address
projectLocationLatitudedoubleGPS latitude
projectLocationLongitudedoubleGPS longitude
workBreakLunchStartTimestringLunch break start time (HH:mm)
workBreakLunchLengthintLunch break duration in minutes
workBreakDinnerStartTimestringDinner break start time (HH:mm)
workBreakDinnerLengthintDinner break duration in minutes

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 Request and 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.

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.

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.

AreaCopied from the template
Work ordersThe 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 hoursLunch and dinner break start and length, automatic checkout, checkout push-reminder, shift settings, automatic overtime calculation
Project settingsPeriod 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)
NotificationsThe project’s notification setup
ChecklistsThe project’s checklist (quality assurance) setup
PersonnelThe 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 pricesContact 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 settingsGeneral 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.

  • 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 (or projectLocationLatitude / 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.

  • 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 active or blockedForRegistration.
  • Most omitted fields do not fall back to the template. Only name, requireTaskNumber, mainContactEmail, projectAddress, the four workBreak* fields and the role holders mainForemanId, projectLeaderId and payrollResponsibleId carry over — so omitting name gives 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 mainForemanId or projectLeaderId, 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, externalDimension1 and externalDimension2 are 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 includes PUT /{id}, where the route id is ignored and the body is matched exactly as on POST. A PUT that matches nothing therefore creates a project and copies the template. Only PATCH is exempt.
  • The template is looked up in the companyId you 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 /api/v4/integration/projects

Returns 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 /api/v4/integration/projects/{id}

Returns 404 Not Found unless the project is owned by your token’s company.

GET /api/v4/integration/projects/by-project-number/{projectNumber}
Terminal window
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.

ResultResponse
One project has the number200 with the project
No project has the number200 with the body null (not 404)
Several projects have the number, exactly one of them active200 with the active project
Several projects have the number, and not exactly one is active400 Bad Request
GET /api/v4/integration/projects/{id}/personnel
GET /api/v4/integration/projects/personnel

Returns 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.

Terminal window
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
}
]
}
FieldDescription
projectCompanyIdThe company that owns the project: yours, or one of your project companies or subsidiaries
userIdThe person’s identity id — the same value as identityId in the Users API
nameDisplay name from the person’s profile in the project’s company
companyIdThe 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)
roleProjectManager, Hse, Foreman, Bas or User
roleNameThe role as Ditio Web shows it: Anleggsleder, Hms, Formann, Bas or Bruker
isMainRoletrue 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.

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.

Terminal window
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"
}'
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.

Terminal window
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 /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.

Projects can carry documents (drawings, contracts) that field workers see in the mobile app — see Documents.