Visena Dokumentasjon
Partner API

Ressurs

Document

Filer knyttet til en person, et firma eller et prosjekt. Opplasting og nedlasting med multipart, endring av metadata, utbytting av innholdet, og flytting mellom mapper.

Et dokument er en lagret fil pluss metadataene rundt den: et navn som inneholder filendelsen, en MIME-type, en størrelse i byte, en valgfri fritekstbeskrivelse, og en plassering i mappetreet til den eiende posten. Hvert dokument hører til én eiende enhet — en Company, en Person eller et Project — og entityType+entityId identifiserer den. Det finnes ingen dokumentliste for hele organisasjonen: hver lesing er avgrenset til én eier.

Det finnes ingen opprettelse uten innhold. POST /document er en multipart/form-data-opplasting som skriver filen og relasjonen i ett kall, og PUT /document/{documentId}/content bytter ut filen på plass. Metadata-endepunktene (erstatt, patch) rører aldri bytene, og bytene kommer bare tilbake fra GET /document/{documentId}/content, som strømmer dem som et vedlegg og ikke som JSON.

Plassering og eierskap er to forskjellige ting. PUT /document/{documentId}/folder flytter et dokument mellom mapper inne i den eiende enheten; ingenting her flytter et dokument til en annen eier. Ett dokument kan ha flere plasseringer — en hovedkopi pluss snarveier på andre poster — og det er derfor slettingen er relasjonsbevisst og ber om bekreftelse før den fjerner noe som følger med.

Endepunktreferansen under står på engelsk i begge språktrærne. Den gjengir API-kontrakten ordrett, og det finnes ingen generator å bygge den på nytt fra, så en oversatt kopi ville drevet fra API-et første gang et endepunkt endres.

Endepunkter

Elleve endepunkter, i samme rekkefølge som de er dokumentert nedenfor.

Alle endepunktene på denne siden kan i tillegg svare med fellesfeilene — 401, 403, 404 (ukjent instans) og 500. De er dokumentert én gang, under Feil og feilsøking.

To nabofamilier av ruter er dokumentert på egne sider: de seks /document/folder-endepunktene som bygger og river ned mappetreet står på Document folder, og den registrerte endringsstrømmen — den eneste lesestien der en sletting er synlig — står på Document changes.

Upload a document

POST /{instanceName}/api/v1/{locale}/document

Multipart create: writes the uploaded blob and relates it to the resolved owning entity (entityType/entityId). 201 with a Location header. There is no content-free create.

onConflict is applied when a folderId is supplied and that folder already holds a document of the same name: skip leaves the existing document untouched and returns it, creating nothing; replace (the default) overwrites the existing document's content in place, preserving its id and its placements; keepAll creates a second document under a name de-duplicated against the names already in that folder. With no folderId, or no same-name document, all three behave alike. Concurrent creates of the same name in one folder are not serialised — a rare duplicate is possible.

created (ISO-8601) and createdBy (a masked person id) set the creation attribution of a restored document; omitting either defaults it to now / the calling credential's person. Both apply ONLY when a document is actually created: the skip and replace conflict branches create no document and ignore both values. A backdated created also backdates the document's position on GET /document/timeline, so it can land behind a watermark a consumer has already passed and never be seen there. The supplied created is likewise carried as the change timestamp of the recorded creation event on GET /document/changes, so that event is backdated too; only the feed's ordering is unaffected, because that ordering is the insertion sequence and never the timestamp, which is why the feed's cursor is on insertion order and its reported changed timestamp is never a resumption watermark.

Forespørselskropp

multipart/form-dataDocumentCreateForm

Multipart form: the file part is the uploaded blob; the remaining parts are the non-file create fields.

Svar

Status Beskrivelse
201 Document created.
DocumentResponseDto · application/json
400 Missing/empty file, unrecognised entityType/onConflict, or the id is not a valid id.
422 The referenced entityId does not exist for the given entityType, or the supplied createdBy is not an existing person.

Eksempel

curl
curl -X POST "https://api.visena.example/acme/api/v1/en/document" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@contract.pdf" \
  -F "entityType=Company" \
  -F "entityId=bQ4wR8"

Get a document

GET /{instanceName}/api/v1/{locale}/document/{documentId}

Returns the full entity. 404 if unknown; 400 if the id cannot be unmasked.

Parametere

Parameter Plassering Type Beskrivelse
documentId required path string Masked document id.

Svar

Status Beskrivelse
200 The document.
DocumentResponseDto · application/json
400 The id is not a valid id.
404 No document with that id.

Eksempel

curl
curl "https://api.visena.example/acme/api/v1/en/document/dN3pT7" \
  -H "Authorization: Bearer $TOKEN"

Download a document's content

GET /{instanceName}/api/v1/{locale}/document/{documentId}/content

Streams the stored blob as an attachment. Content-Type is the document's stored MIME type, falling back to application/octet-stream when it was never recorded or is unparseable; the file name is carried in Content-Disposition (RFC 5987-encoded), not in the path. Same authorization boundary as getDocument — a caller who cannot read the metadata cannot read the bytes.

Parametere

Parameter Plassering Type Beskrivelse
documentId required path string Masked document id.

Svar

Status Beskrivelse
200 The document's content, streamed.
file · application/octet-stream
400 The id is not a valid id.
404 No document with that id.

Eksempel

curl
curl "https://api.visena.example/acme/api/v1/en/document/dN3pT7/content" \
  -H "Authorization: Bearer $TOKEN"

List documents (offset-paginated)

GET /{instanceName}/api/v1/{locale}/document

Offset/limit paging with a real total. entityType+entityId are REQUIRED (documents are always listed scoped to one owning entity — no org-wide enumeration); optional query (full-text search); and a name/created/modified/modifiedOrCreated sort. RFC 8288 navigation links are in the body.

Parametere

Parameter Plassering Type Beskrivelse
offset query integer
default 0
Zero-based item offset.
limit query integer Maximum items to return per page.
query query string Free-text filter; the endpoint description lists the fields it matches.
entityType required query string, one of Company Person Project Owning entity type.
entityId required query string Masked id of the owning record.
sort query string Sort key; the endpoint description lists the allowed values.

Svar

Status Beskrivelse
200 A page of documents with totals and navigation links.
DocumentOffsetListResponse · application/json
400 Missing/unresolved required entityType/entityId, invalid paging or sort parameter, or a filter id is not a valid id.

Eksempel

curl
curl "https://api.visena.example/acme/api/v1/en/document?entityType=Company&entityId=bQ4wR8" \
  -H "Authorization: Bearer $TOKEN"

List documents (cursor-paginated, insert-stable)

GET /{instanceName}/api/v1/{locale}/document/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 — keyset skips the count. entityType+entityId are REQUIRED (documents are always listed scoped to one owning entity); optional query.

Parametere

Parameter Plassering Type Beskrivelse
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.
query query string Free-text filter; the endpoint description lists the fields it matches.
entityType required query string, one of Company Person Project Owning entity type.
entityId required query string Masked id of the owning record.

Svar

Status Beskrivelse
200 A page of documents with next/prev cursors and links.
DocumentCursorListResponse · application/json
400 Missing/unresolved required entityType/entityId, invalid or expired cursor, invalid limit, or a filter id is not a valid id.

Eksempel

curl
curl "https://api.visena.example/acme/api/v1/en/document/cursor?entityType=Company&entityId=bQ4wR8&limit=100" \
  -H "Authorization: Bearer $TOKEN"

List documents (time-windowed, cursor-paginated)

GET /{instanceName}/api/v1/{locale}/document/timeline

Keyset paging from an optional since lower bound on the single timeline key coalesce(modified, created) — Document has no client-selectable key, unlike Company/Project. entityType+entityId are REQUIRED (documents are always listed scoped to one owning entity). RFC 9557 Z timestamps. A cursor minted by the plain cursor endpoint is rejected with 400.

Sync contract: this feed is upsert-only and at-least-once, ordered on coalesce(modified, created), so creation, archival and renaming are the only writes it reports — renaming is the sole partner-reachable write the platform stamps modified on. Folder moves, content replacement, description-only PATCHes and folder renames/reparents are unreported: they leave the row in place. Deletions never appear here at all and are observable only via GET /document/changes.

Consumer contract: since is inclusive, and cursor timestamps are millisecond-truncated relative to the stored precision, so a boundary row is re-served rather than skipped — overlap the window and de-duplicate by document id. A cross-owner relocation is never a move: it shows only its gaining side here (as a creation in the gaining owner's timeline), while the losing side is visible only as a deletion on GET /document/changes. Full synchronisation therefore needs three sources — this timeline for upserts, GET /document/changes for deletions, and a periodic per-owner snapshot diff for placement, content and description changes. That snapshot diff is the consumer's obligation, not a feature of this API.

Parametere

Parameter Plassering Type Beskrivelse
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.
query query string Free-text filter; the endpoint description lists the fields it matches.
entityType required query string, one of Company Person Project Owning entity type.
entityId required query string Masked id of the owning record.

Svar

Status Beskrivelse
200 A page of documents with next/prev cursors and links.
DocumentCursorListResponse · application/json
400 Missing/unresolved required entityType/entityId, invalid timestamp, cursor, limit, or a filter id is not a valid id.

Eksempel

curl
curl "https://api.visena.example/acme/api/v1/en/document/timeline?entityType=Company&entityId=bQ4wR8&since=2026-08-20T02:00:00Z" \
  -H "Authorization: Bearer $TOKEN"

Replace a document's metadata

PUT /{instanceName}/api/v1/{locale}/document/{documentId}

Full replace (PUT) of name/description: an omitted description clears the stored value.

Parametere

Parameter Plassering Type Beskrivelse
documentId required path string Masked document id.

Forespørselskropp

application/jsonDocumentUpdateDto

Svar

Status Beskrivelse
200 The replaced document.
DocumentResponseDto · application/json
400 Request body failed validation, or the id is not a valid id.
404 No document with that id.

Eksempel

curl
curl -X PUT "https://api.visena.example/acme/api/v1/en/document/dN3pT7" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "contract-signed.pdf",
    "description": "Signed version"
  }'

Partially update a document (RFC 7396 JSON Merge Patch)

PATCH /{instanceName}/api/v1/{locale}/document/{documentId}

Absent fields are unchanged, JSON null clears (name cannot be cleared), a value sets. An empty body {} is a no-op. Wrong content type → 415.

Parametere

Parameter Plassering Type Beskrivelse
documentId required path string Masked document id.

Forespørselskropp

application/merge-patch+jsonobject

Svar

Status Beskrivelse
200 The updated document.
DocumentResponseDto · application/json
400 A patched field had an invalid value (e.g. blank/cleared name), or the id is not a valid id.
404 No document with that id.
415 Content-Type was not application/merge-patch+json.

Eksempel

curl
curl -X PATCH "https://api.visena.example/acme/api/v1/en/document/dN3pT7" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/merge-patch+json" \
  -d '{
    "description": "Signed version"
  }'

Replace a document's content

PUT /{instanceName}/api/v1/{locale}/document/{documentId}/content

Multipart PUT: rewrites the stored blob in place, preserving the document id and its relations.

Parametere

Parameter Plassering Type Beskrivelse
documentId required path string Masked document id.

Forespørselskropp

multipart/form-dataDocumentContentReplaceForm

Multipart form: the file part is the replacement blob.

Svar

Status Beskrivelse
200 The document, with the replaced content.
DocumentResponseDto · application/json
400 Missing/empty file, or the id is not a valid id.
404 No document with that id.

Eksempel

curl
curl -X PUT "https://api.visena.example/acme/api/v1/en/document/dN3pT7/content" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@contract-v2.pdf"

Move a document to another folder

PUT /{instanceName}/api/v1/{locale}/document/{documentId}/folder

Changes this document's folder placement within its owning entity. A null folderId places it at top level. The destination must belong to the same owning entity — moving a document to another entity is not a move; archive a copy or a shortcut instead. Both the document and the destination folder are resolved only within the declared entity, so either one belonging to another entity is reported as 404 rather than confirming that the id exists elsewhere. A destination that already holds a placement of the same document is rejected with 409. Moving does not count as a modification: modified/modifiedBy are left exactly as the platform recorded them.

Parametere

Parameter Plassering Type Beskrivelse
documentId required path string Masked document id.
entityType required query string, one of Company Person Project Owning entity type.
entityId required query string Masked id of the owning record.

Forespørselskropp

application/jsonDocumentPlacementDto

Svar

Status Beskrivelse
200 The document, with its new placement.
DocumentResponseDto · application/json
400 An id is not a valid id.
404 No document with that id, or no folder with that folderId.
409 The destination folder already holds a placement of this document.
422 The document is not archived in a folder, so there is no placement to move.

Eksempel

curl
curl -X PUT "https://api.visena.example/acme/api/v1/en/document/dN3pT7/folder" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "folderId": "fR5sK1"
  }'

Delete a document (relation-aware)

DELETE /{instanceName}/api/v1/{locale}/document/{documentId}

Hard delete: no soft-delete/isActive column. When entityType+entityId are supplied, only that relation is removed; otherwise every relation is removed. The document itself is deleted once it is left orphaned (no remaining relations). 204 on success.

A delete that would cascade — no entityType/entityId, on a document holding shortcut placements on other entities — is refused with 409 until acknowledged, and deletes NOTHING when refused. The 409 states how many master documents and shortcut placements would go; re-send with acknowledgeCascade=true to proceed. The targeted (entityType+entityId) form removes exactly one placement, cannot cascade, and is never gated.

Parametere

Parameter Plassering Type Beskrivelse
documentId required path string Masked document id.
entityType query string, one of Company Person Project Owning entity type.
entityId query string Masked id of the owning record.
acknowledgeCascade query boolean
default false
Confirm a delete whose extent goes beyond the targeted placement; see the endpoint description.

Svar

Status Beskrivelse
204 The relation(s) — and the document, if orphaned — were removed; no body.
400 entityType and entityId were not both provided together, or an id is not a valid id.
404 No document with that id, or no matching relation.
409 The delete would cascade to shortcut placements and was not acknowledged; nothing was deleted. detail states how many placements would go.

Eksempel

curl
curl -X DELETE "https://api.visena.example/acme/api/v1/en/document/dN3pT7" \
  -H "Authorization: Bearer $TOKEN"

Objektstrukturer

Forespørsels- og svarkroppene som brukes over, i den rekkefølgen de først refereres.

DocumentCreateForm

Felt Type Beskrivelse
file required file The uploaded file content.
entityType required string, one of Company Person Project
entityId required string (masked id) Masked id of the owning record.
folderId string (masked id) Target folder within the owner's archive. Optional; without it the document is related to the owner but not archived into a folder.
onConflict string, one of replace skip keepAll
created string (date-time) Audit override: creation timestamp to attribute (ISO-8601). Applied only when a document is actually created.
createdBy string (masked id) Audit override: masked person id to attribute the creation to. Applied only when a document is actually created.

DocumentResponseDto

Felt Type Beskrivelse
id string (masked id)
name string Document display name (includes the file suffix).
suffix string File suffix (extension), if any.
mimeType string MIME type of the stored blob, if known.
size integer Blob size in bytes.
created string (date-time) Creation timestamp (UTC).
createdBy PersonRefDto
modified string (date-time) Last-modification timestamp (UTC); null until first modified.
modifiedBy PersonRefDto
description string Free-text description, if set.
folderId string (masked id)
folderPath read-only string Slash-joined folder path of the placement; empty at top level.
actedBy string (masked id)

PersonRefDto

Last modifier of the document, if any.

Felt Type Beskrivelse
id string (masked id)
name string Person display name.

DocumentOffsetListResponse

Felt Type Beskrivelse
totalItems integer Total number of matching documents.
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 DocumentListItemDto The documents on this page.
links DocumentListLinks

DocumentListItemDto

The documents on this page, in ascending key order.

Felt Type Beskrivelse
id string (masked id)
name string Document display name (includes the file suffix).
suffix string File suffix (extension), if any.
mimeType string MIME type of the stored blob, if known.
size integer Blob size in bytes.
created string (date-time) Creation timestamp (UTC).
createdBy PersonRefDto
modified string (date-time) Last-modification timestamp (UTC); null until first modified.
modifiedBy PersonRefDto

RFC 8288 navigation links: self, and next when more events remain.

Felt Type Beskrivelse
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).

DocumentCursorListResponse

Felt Type Beskrivelse
items array of DocumentListItemDto The documents 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 DocumentListLinks

DocumentUpdateDto

Felt Type Beskrivelse
name required string Document display name. Required.
description string Free-text description. Omitted on a replace clears the stored value.

DocumentContentReplaceForm

Felt Type Beskrivelse
file required file The replacement file content.
onConflict string, one of replace skip keepAll

DocumentPlacementDto

Felt Type Beskrivelse
folderId string (masked id)