Skip to content

Project External References

A project external reference is one allowed value for one of the external reference fields on a project or work order. Together they form per-company lists — the valid external project numbers for a company, the valid sub project numbers for a project, and so on.

They matter because they are enforced. When a company has values configured for a type, Ditio rejects project and work order writes carrying anything else, so keeping these lists in step with your ERP is what keeps your integration writing successfully.

All endpoints use the ditioapiv3 scope — see Which scope for which host.

Examples use the environment variables from Environments and default to the test environment.

externalRefType is one of five values (matched case-insensitively):

TypeWhat it governs
ExternalProjectNumberValid values for a project’s externalProjectNumber
ExternalSubProjectNumberValid sub project numbers within one project
ProcessCodeProcess codes available on a project’s work orders
WBSCodeWork breakdown structure codes
ExternalDim02Values for the second external dimension
FieldTypeNotes
idstringDitio id. Omit when creating; required when updating
externalRefTypestringRequired. One of the five types above
externalRefValuestringRequired. The value itself — this is what gets matched
projNumberstringRequired for ExternalSubProjectNumber and ProcessCode. Scopes the value to one project — but the two types expect different numbers, see the note below
namestringDisplay name shown next to the value
groupNamestringOptional grouping for long lists
disabledboolHides the value from the pickers in Ditio’s own UI. It does not stop the API accepting it — see Disable and enable
invoiceableboolMarks hours booked to a work order carrying this code as invoiceable
structureIdstringOptional. Narrows which references are offered on a project
companyIdstringOptional on create — see Company scoping
financialEndDatedatetimeOptional, and not inert on ExternalProjectNumber — see Financial end date
Terminal window
curl -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/type/ExternalProjectNumber"

This is the list a project write is validated against. Call it first when a write fails — if your value is not in the response, that is why.

Terminal window
curl -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/type/ExternalSubProjectNumber/project/40001"

The path segment is the project’s externalProjectNumber, per the note above.

Terminal window
# By Ditio id
curl -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/{id}"
# By value
curl -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/type/ExternalProjectNumber/externalRef/40001"
# By value within a project
curl -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/type/ExternalSubProjectNumber/project/40001/externalRef/40001-01"
Terminal window
curl -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/structure-ids/type/WBSCode"

Returns the distinct non-empty structureId values in use for a type.

Terminal window
curl -X POST "$DITIO_API_BASE/api/project-external-reference" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"externalRefType": "ExternalProjectNumber",
"externalRefValue": "40001",
"name": "Example project"
}'

Sub project numbers additionally need projNumber:

Terminal window
curl -X POST "$DITIO_API_BASE/api/project-external-reference" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"externalRefType": "ExternalSubProjectNumber",
"externalRefValue": "40001-01",
"projNumber": "40001",
"name": "Groundworks"
}'

Posting a body that carries an id updates that reference instead of creating a new one.

Terminal window
curl -X PUT "$DITIO_API_BASE/api/project-external-reference/{id}" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "id": "{id}", "externalRefType": "ExternalProjectNumber", "externalRefValue": "40001", "name": "Example project (updated)" }'

PUT replaces the whole document — every field you omit is cleared, including invoiceable, structureId, groupName, disabled and financialEndDate. Read the reference first and send it back in full with your changes applied, or you will silently reset the fields you left out. Clearing invoiceable stops hours on that code being marked invoiceable, and clearing financialEndDate has the side effect described below.

You cannot change an existing reference’s externalRefType; that is rejected. Its owning company is also fixed at creation and cannot be moved.

Terminal window
curl -X PUT -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/disable/{id}"
curl -X PUT -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/enable/{id}"

disabled is a presentation flag for Ditio’s own UI. It marks the value as disabled in the pickers so people stop choosing it by hand, and the read endpoints keep returning it so historical data still resolves to a name.

It does not stop the value being written. The validation that rejects external project and sub project numbers compares against the whole list without looking at disabled, so an integration can keep posting a disabled value indefinitely and it will be accepted. If you need a value to stop being written through the API, removing it is currently the only way — see the constraints on Delete.

Terminal window
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
"$DITIO_API_BASE/api/project-external-reference/{id}"

For ExternalProjectNumber, ExternalSubProjectNumber and ProcessCode this is rejected with 403 while the value is still in use on any project or work order, naming how many use it. Clear the value from those records first.

WBSCode and ExternalDim02 have no such guard and are deleted immediately; existing references to them keep the raw value but stop resolving to a name.

Reads return the references available in your company context, and creates land on the company that owns the list:

  • If you send a companyId, it must be part of your own company structure.
  • A parent company id, or a project company id, is stored against the parent company — the lists live at parent level and are shared by its project companies.
  • A subsidiary company id is kept as sent.
  • If you omit companyId, your own company is used.

This is worth knowing when your integration authenticates as a project company: values you create are visible across that parent’s project companies, and values you read may have been created against the parent rather than the company you authenticated as.

Both messages are returned with 400 Bad Request:

MessageMeaning
Extern prosjektnummer <value> er ikke en gyldig. Velg fra listenThe project’s externalProjectNumber is not in the company’s ExternalProjectNumber list
Delprosjektnummer <value> er ikke en gyldig. Velg fra listenThe work order’s sub project number is not in the list configured for the project’s external project number

Both fire only when the company has this validation enabled and has values configured for that type — a populated list alone does not mean writes are checked. Where they do apply, both treat an empty value as invalid, so the field is effectively required.

List the relevant type first. If your value is not in the response, that is why the write failed, and creating it is the fix. Note that disabled is never the reason — disabled values still pass validation.

Setting financialEndDate on an ExternalProjectNumber reference is not just metadata on the reference. Ditio finds every project in the company structure whose externalProjectNumber matches the value and writes that same date onto each of them.

This happens on create and on any update that changes the value — including clearing it, which writes an empty date onto every matching project. Combined with PUT being a full replace, an update that simply omits financialEndDate will do exactly that.

Treat it as a bulk update of the matching projects rather than a property of the list entry, and leave it unset unless you intend that.