Resource
Project
Projects — the unit of work companies and activities hang off. Full CRUD; create requires a templateId, and delete closes the project instead of destroying it.
In origo terms a project is an engagement — the unit of work a customer engages the firm for. It belongs to a company, it is owned by a group, and activities hang off it; it is not a software project. projectType names the two shapes an engagement takes, PROJECT_TYPE_PROJECT and PROJECT_TYPE_ASSIGNMENT.
The endpoint reference below is kept in English in both language trees. It mirrors the API contract verbatim and there is no generator to rebuild it from, so a translated copy would drift from the API the first time an endpoint changes.
Endpoints
Eight endpoints, in the order they are documented below.
- POST
/project— Create a project - GET
/project/{projectId}— Get a project - GET
/project— List projects (offset-paginated) - GET
/project/cursor— List projects (cursor-paginated, insert-stable) - GET
/project/timeline— List projects (time-windowed, cursor-paginated) - PUT
/project/{projectId}— Replace a project - PATCH
/project/{projectId}— Partially update a project (RFC 7396 JSON Merge Patch) - DELETE
/project/{projectId}— Delete (close) a project
Every endpoint on this page can additionally answer the shared errors — 401, 403, 404 (unknown instance) and 500. They are documented once, under Errors and troubleshooting.
Create a project
POST /{instanceName}/api/v1/{locale}/project
Creates a project from a template and returns the full entity. 201 with a Location header.
Request body
application/json → ProjectCreateDto
Responses
| Status | Description |
|---|---|
| 201 | Project created. ProjectResponseDto · application/json |
| 400 | Request body failed validation. |
| 422 | A referenced entity (template, company, ownerGroup, parent, responsible, createdBy, modifiedBy) does not exist. |
Example
curl -X POST "https://api.visena.example/acme/api/v1/en/project" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Warehouse rollout",
"templateId": "tM2qL6",
"ownerGroupId": "gH8dF3",
"companyId": "bQ4wR8"
}'
Get a project
GET /{instanceName}/api/v1/{locale}/project/{projectId}
Returns the full entity. 404 if unknown; 400 if the id cannot be unmasked.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
projectId required |
path | string |
Masked project id. |
Responses
| Status | Description |
|---|---|
| 200 | The project. ProjectResponseDto · application/json |
| 400 | The id is not a valid id. |
| 404 | No project with that id. |
Example
curl "https://api.visena.example/acme/api/v1/en/project/pJ7wN2" \
-H "Authorization: Bearer $TOKEN"
List projects (offset-paginated)
GET /{instanceName}/api/v1/{locale}/project
Offset/limit paging with a real total. Optional filters: isActive, companyId; and a created/modified/name sort. RFC 8288 navigation links are in the body.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
offset |
query | integerdefault 0 |
Zero-based item offset. |
limit |
query | integer |
Maximum items to return per page. |
isActive |
query | boolean |
Filter on active state. |
companyId |
query | string |
Masked company id. |
sort |
query | string |
Sort key; the endpoint description lists the allowed values. |
Responses
| Status | Description |
|---|---|
| 200 | A page of projects with totals and navigation links. ProjectOffsetListResponse · application/json |
| 400 | Invalid paging or sort parameter, or a filter id is not a valid id. |
Example
curl "https://api.visena.example/acme/api/v1/en/project?offset=0&limit=20" \
-H "Authorization: Bearer $TOKEN"
List projects (cursor-paginated, insert-stable)
GET /{instanceName}/api/v1/{locale}/project/cursor
Keyset paging over the monotonic entity id (insertion order). Pass the opaque cursor from a previous page's nextCursor/prevCursor; omit it for the first page. No total. Same filters as the offset list: isActive, companyId.
Cursor paging and the timeline below are two different read modes, not two page shapes. Which one suits a single-pass extract, which one you resume from, whether a row can arrive twice, and what a list response says when a project disappears are all settled once for the whole API in Lists, paging and sync — read it before you build a synchronisation loop on this endpoint or on listProjectsByTime.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
cursor |
query | string |
Opaque page cursor from the previous response's links.next; omit for the first page. |
limit |
query | integer |
Maximum items to return per page. |
isActive |
query | boolean |
Filter on active state. |
companyId |
query | string |
Masked company id. |
Responses
| Status | Description |
|---|---|
| 200 | A page of projects with next/prev cursors and links. ProjectCursorListResponse · application/json |
| 400 | Invalid or expired cursor, or invalid limit. |
Example
curl "https://api.visena.example/acme/api/v1/en/project/cursor?limit=100" \
-H "Authorization: Bearer $TOKEN"
List projects (time-windowed, cursor-paginated)
GET /{instanceName}/api/v1/{locale}/project/timeline
Keyset paging from an optional since lower bound on the coalesced modified-or-created timestamp. RFC 9557 Z timestamps.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
since |
query | string (date-time) |
Inclusive lower bound — an RFC 3339 timestamp. |
cursor |
query | string |
Opaque page cursor from the previous response's links.next; omit for the first page. |
limit |
query | integer |
Maximum items to return per page. |
Responses
| Status | Description |
|---|---|
| 200 | A page of projects with next/prev cursors and links. ProjectCursorListResponse · application/json |
| 400 | Invalid timestamp, cursor, or limit. |
Example
curl "https://api.visena.example/acme/api/v1/en/project/timeline?since=2026-08-20T02:00:00Z" \
-H "Authorization: Bearer $TOKEN"
Replace a project
PUT /{instanceName}/api/v1/{locale}/project/{projectId}
Full replace (PUT): omitted optional fields are cleared.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
projectId required |
path | string |
Masked project id. |
Request body
application/json → ProjectUpdateDto
Responses
| Status | Description |
|---|---|
| 200 | The replaced project. ProjectResponseDto · application/json |
| 400 | Request body failed validation, or the id is not a valid id. |
| 404 | No project with that id. |
| 422 | A referenced entity (template, company, ownerGroup, parent, responsible, modifiedBy) does not exist. |
Example
curl -X PUT "https://api.visena.example/acme/api/v1/en/project/pJ7wN2" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Warehouse rollout",
"templateId": "tM2qL6",
"ownerGroupId": "gH8dF3",
"companyId": "bQ4wR8"
}'
Partially update a project (RFC 7396 JSON Merge Patch)
PATCH /{instanceName}/api/v1/{locale}/project/{projectId}
Absent fields are unchanged, JSON null clears, a value sets. An empty body {} is a no-op. Wrong content type → 415.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
projectId required |
path | string |
Masked project id. |
Request body
application/merge-patch+json → object
Responses
| Status | Description |
|---|---|
| 200 | The updated project. ProjectResponseDto · application/json |
| 400 | A patched field had an invalid value, or the id is not a valid id. |
| 404 | No project with that id. |
| 415 | Content-Type was not application/merge-patch+json. |
| 422 | A referenced entity (template, company, ownerGroup, parent, responsible, modifiedBy) does not exist. |
Example
curl -X PATCH "https://api.visena.example/acme/api/v1/en/project/pJ7wN2" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/merge-patch+json" \
-d '{
"name": "Warehouse rollout — phase 2"
}'
Delete (close) a project
DELETE /{instanceName}/api/v1/{locale}/project/{projectId}
Soft-close: sets the project inactive through the same flow the Visena UI uses. 204 on success.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
projectId required |
path | string |
Masked project id. |
Responses
| Status | Description |
|---|---|
| 204 | Project closed; no body. |
| 400 | The id is not a valid id. |
| 404 | No project with that id. |
Example
curl -X DELETE "https://api.visena.example/acme/api/v1/en/project/pJ7wN2" \
-H "Authorization: Bearer $TOKEN"
Object shapes
The request and response bodies used above, in the order they are first referenced.
ProjectCreateDto
| Field | Type | Description |
|---|---|---|
name required |
string |
Project display name. Required. |
companyId |
string (masked id) |
|
templateId required |
string (masked id) |
|
ownerGroupId required |
string (masked id) |
|
parentId |
string (masked id) |
|
responsiblePersonId |
string (masked id) |
|
projectYear |
object |
Project year. |
estimatedStart |
string (date-time) |
Estimated start (RFC 3339). |
estimatedEnd |
string (date-time) |
Estimated end (RFC 3339). |
projectStart |
string (date-time) |
Actual start (RFC 3339). Defaults to the creation timestamp when absent. |
projectEnd |
string (date-time) |
Actual end (RFC 3339). |
externalReference |
string |
External reference / foreign system key. |
internalReference |
string |
Internal reference. |
isPublicAccess |
object |
Whether the project is visible across the tenant (public access). Defaults from the template. |
isChargable |
object |
Whether the project is chargeable. Defaults from the template. |
created |
string (date-time) |
override: creation timestamp to attribute. Defaults to server-now (UTC) when absent. |
createdBy |
string (masked id) |
|
modified |
string (date-time) |
override: modification timestamp (set alongside creation when provided). |
modifiedBy |
string (masked id) |
ProjectResponseDto
| Field | Type | Description |
|---|---|---|
id |
string (masked id) |
|
number |
string |
Human-readable project number, assigned by Visena. |
name |
string |
|
company |
CompanyRefDto | |
template |
ProjectTemplateRefDto | |
ownerGroup |
GroupRefDto | |
projectYear |
object |
Project year, if set. |
parentId |
string (masked id) |
|
responsible |
string (masked id) |
|
projectType |
string, one of PROJECT_TYPE_PROJECT PROJECT_TYPE_ASSIGNMENT |
|
estimatedStart |
string (date-time) |
Estimated start (UTC), if set. |
estimatedEnd |
string (date-time) |
Estimated end (UTC), if set. |
projectStart |
string (date-time) |
Actual start (UTC). |
projectEnd |
string (date-time) |
Actual end (UTC), if set. |
externalReference |
string |
|
internalReference |
string |
|
isPublicAccess |
boolean |
|
isChargable |
boolean |
|
isActive |
boolean |
|
created |
string (date-time) |
Creation timestamp (UTC). |
createdBy |
string (masked id) |
|
modified |
string (date-time) |
Last-modification timestamp (UTC); null until first modified. |
modifiedBy |
string (masked id) |
|
actedBy |
string (masked id) |
CompanyRefDto
The company the owning project belongs to, if any.
| Field | Type | Description |
|---|---|---|
id |
string (masked id) |
|
name |
string |
Company display name. |
organizationalNumbers |
array of OrgNumberDto | Active organizational (registration) numbers; at most one per company. |
OrgNumberDto
Active organizational (registration) numbers; at most one per company.
| Field | Type | Description |
|---|---|---|
orgNumber |
string |
The organizational (registration) number. |
country |
CountryRefDto |
CountryRefDto
Country this organizational number is registered in.
| Field | Type | Description |
|---|---|---|
id |
string (masked id) |
|
name |
string |
Country display name. |
code |
string |
ISO 3166-1 alpha-2 country code, e.g. "NO". |
ProjectTemplateRefDto
Project template the project was created from.
| Field | Type | Description |
|---|---|---|
id |
string (masked id) |
|
name |
string |
Project template display name. |
GroupRefDto
Owning group / department.
| Field | Type | Description |
|---|---|---|
id |
string (masked id) |
|
name |
string |
Group display name. |
ProjectOffsetListResponse
| Field | Type | Description |
|---|---|---|
totalItems |
integer |
Total number of matching projects. |
totalPages |
integer |
Total number of pages at the current page size. |
page |
integer |
Zero-based index of the current page. |
size |
object |
Page size actually applied. |
items |
array of ProjectListItemDto | The projects on this page. |
links |
ProjectListLinks |
ProjectListItemDto
The projects on this page, in ascending key order.
| Field | Type | Description |
|---|---|---|
id |
string (masked id) |
|
name |
string |
Project display name. |
number |
string |
Human-readable project number, if assigned. |
isActive |
boolean |
Whether the project is active. |
projectYear |
object |
Project year, if set. |
projectType |
string, one of PROJECT_TYPE_PROJECT PROJECT_TYPE_ASSIGNMENT |
|
company |
CompanyRefDto | |
ownerGroup |
GroupRefDto | |
responsible |
string (masked id) |
|
projectStart |
string (date-time) |
Actual start (UTC). |
projectEnd |
string (date-time) |
Actual end (UTC), if set. |
created |
string (date-time) |
Creation timestamp (UTC). |
modified |
string (date-time) |
Last-modification timestamp (UTC); null until first modified. |
ProjectListLinks
RFC 8288 navigation links.
| Field | Type | Description |
|---|---|---|
self |
string |
URL of the current page. |
first |
string |
URL of the first page (offset endpoint). |
prev |
string |
URL of the previous page, if any. |
next |
string |
URL of the next page, if any. |
last |
string |
URL of the last page (offset endpoint). |
ProjectCursorListResponse
| Field | Type | Description |
|---|---|---|
items |
array of ProjectListItemDto | The projects on this page, in ascending key order. |
nextCursor |
string |
Opaque cursor for the next page; absent on the last page. |
prevCursor |
string |
Opaque cursor for the previous page; absent on the first page. |
links |
ProjectListLinks |
ProjectUpdateDto
| Field | Type | Description |
|---|---|---|
name required |
string |
Project display name. Required. |
companyId |
string (masked id) |
|
templateId required |
string (masked id) |
|
ownerGroupId required |
string (masked id) |
|
parentId |
string (masked id) |
|
responsiblePersonId |
string (masked id) |
|
projectYear |
object |
Project year. |
estimatedStart |
string (date-time) |
Estimated start (RFC 3339). |
estimatedEnd |
string (date-time) |
Estimated end (RFC 3339). |
projectStart |
string (date-time) |
Actual start (RFC 3339). Defaults to the creation timestamp when absent. |
projectEnd |
string (date-time) |
Actual end (RFC 3339). |
externalReference |
string |
External reference / foreign system key. |
internalReference |
string |
Internal reference. |
isPublicAccess |
object |
Whether the project is visible across the tenant (public access). |
isChargable |
object |
Whether the project is chargeable. |
created |
string (date-time) |
Audit override: creation timestamp. Immutable on update — ignored. |
createdBy |
string (masked id) |
|
modified |
string (date-time) |
Audit override: modification timestamp to attribute. Defaults to server-now (UTC) when absent. |
modifiedBy |
string (masked id) |