Overview
REST API Documentation.
Authentication
Send an API key in the Authorization header of every request:
Authorization: Bearer ncapi_3f9a0c51e2b7d846_Xk3v9QpL0bN7wYt2RcZf8uHs1aJm4EoD6iGqTnVyB5w
A user creates a key in My API Keys of the user menu, and a supervisor creates the keys of the dedicated users of integrations. The key is shown only once, when it is created.
A request runs with the rights of the user who owns the key,
and only on the APIs of the key's scopes; this API needs the rest scope.
A revoked or expired key, a key of a disabled user, or a key without the rest scope
is rejected with 403 and the code forbidden.
An installation without API keys rejects every key.
Conventions
Errors are returned as RFC 9457 Problem Details with the application/problem+json media type.
type is always about:blank,
so use code to tell errors apart;
title and detail are human-readable text and may change.
The HTTP status is only in the status line:
the body has no status member.
A pointer is an RFC 6901 JSON Pointer, such as /attributes/dueDate.
In an error's errors list it points into the request body,
and in a response's warnings list it points into that response.
For a multipart/form-data request, the body is treated as an object keyed by part name:
/document/title points into the JSON of the document part,
and /contractPdf points to the part named contractPdf itself.
A / inside a name is escaped as ~1, and a ~ as ~0.
A date is written as YYYY-MM-DD.
A timestamp in a request is an RFC 3339 date-time with an explicit Z or numeric offset,
such as 2026-09-25T14:30:00+03:00, with at most millisecond precision;
a timestamp without an offset is rejected.
Responses always return timestamps in UTC as YYYY-MM-DDTHH:mm:ss.sssZ.
This API is described by an OpenAPI 3.1 document, published at /api-docs/openapi.json.
Selecting fields
An operation that returns a resource representation may accept a select query parameter
that narrows the response to exactly the properties the caller needs,
instead of always returning every property.
select is a comma-separated list of top-level property names, where each item is one of:
- a bare name, such as
title, to include that single property - a bare group name, such as
participants, to include every applicable entry of that group - a group name narrowed to explicit codes, such as
attributes(dueDate,amount), to include only those codes from that group
Whitespace around names, commas, and parentheses is ignored,
and only a group name may take parentheses.
When select is omitted, an operation returns its default representation,
documented on the parameter itself.
For example, select=title,documentType,attributes(dueDate,amount),participants(approver)
returns the title and documentType fields, the dueDate and amount attributes,
and the approver participants role.
An explicit select replaces the default representation instead of adding to it.
The response contains only the selected properties:
id and other default properties are not added implicitly,
so select id when you need it.
Selections combine into one union.
Repeating a property or a code has no further effect,
and repeated narrowed groups merge their codes:
attributes(amount),attributes(dueDate) selects both attributes.
A bare group together with a narrowed one, such as attributes,attributes(amount),
returns the whole group,
but every explicitly named code is still validated:
an unknown code is rejected even next to its bare group.
A selected property keeps its own presence rule.
A conditional property, such as folder for a document outside any folder,
is absent from the response even when selected,
so a valid selection may return an empty object.
A response may contain warnings in addition to the selected properties,
when a selected value could not be read exactly from storage.
warnings is not a selector: select=warnings is rejected.
An ordinary unset value is returned as null without a warning.
Uploading files
A client can transfer a file before it creates a document, and then create the document by referencing the completed upload. A simple client sends the whole file in one request. A client that must survive an interrupted transfer uses the core protocol of tus 1.0.0, so a stock tus client can resume where it stopped. Both ways produce the same upload, referenced the same way.
An upload is temporary content of the user who created it: no other user can see or use it, whatever identifier they know. It expires 24 hours after its creation by default, and an environment can configure a different time.
Uploading without chunking
Send the file as the body of POST /api/v1/uploads/content, with its name in the filename query parameter.
No size, offset, or tus header is needed:
curl -X POST 'https://example.com/api/v1/uploads/content?filename=contract.pdf' \
-H 'Content-Type: application/pdf' \
--data-binary @contract.pdf
The body is stored exactly as received, even when it is declared as application/json;
a multipart/form-data envelope is rejected.
The upload's size is the number of bytes received,
and its media type comes from the filename's extension, not from the request's Content-Type.
The response is 201 Created with the representation of a ready upload;
pass its id as "primaryFile": {"uploadId": "<id>"} when creating the document.
Resumable flow
- Create the upload with
POST /api/v1/uploads. The response'sLocationheader holds the upload URL,/api/v1/uploads/{id}. - Send the content with
PATCHto the upload URL, in one request or in chunks, each with theUpload-Offsetit continues at. After an interruption,HEADon the upload URL reports theUpload-Offsetto continue at. The request that sends the last declared byte makes the uploadready. - Create the document with
POST /api/v1/documentsand"primaryFile": {"uploadId": "<id>"}. A successful creation consumes the upload; a failed creation leaves itready, so the request can be sent again.
Creating an upload
A JSON request declares the filename and the size in bytes:
POST /api/v1/uploads
Content-Type: application/json
{"filename": "supply-agreement.pdf", "size": 1048576}
A tus client uses the tus creation form instead:
an empty body with Upload-Length, and the filename in the filename key of Upload-Metadata,
or in its name key when filename is absent.
The media type is always derived from the filename's extension.
Sending content
A PATCH body has Content-Type: application/offset+octet-stream.
A request whose Upload-Offset differs from the bytes received so far is rejected with 409 code offset_mismatch,
and appends nothing.
When the bytes received so far can no longer be appended to,
because an earlier request failed after storing its bytes,
or because the server fails to store the body of this request,
the upload is reset with the same 409:
HEAD then reports offset 0, and the client sends the content again from the start.
A failure to store the body at offset 0 has nothing to start over,
so it is answered with 500 code internal_error instead;
an upload without chunking is then not created at all.
tus-js-client retries a 409 by asking HEAD for the offset, so it needs no code for this.
Content cannot be replaced: to send another file, create another upload.
tus requires Tus-Resumable: 1.0.0 on every request;
this API also accepts a request without it, as an extension for plain HTTP clients.
PATCH /api/v1/uploads/Hh3rN0bQ4kqZ3t2sYw9x1A
Content-Type: application/offset+octet-stream
Upload-Offset: 0
Tus-Resumable: 1.0.0
<the file's bytes>
With tus-js-client, pass the upload URL of a JSON creation as uploadUrl,
or the collection URL as endpoint to let the client create the upload itself.
Set the chunk size below the environment's request limit, 24 MiB by default,
and set the authentication header of each request in onBeforeRequest:
const upload = new tus.Upload(file, {
uploadUrl: 'https://example.com/api/v1/uploads/Hh3rN0bQ4kqZ3t2sYw9x1A',
chunkSize: 8 * 1024 * 1024,
retryDelays: [0, 1000, 3000, 5000],
onBeforeRequest (req) {
req.setHeader('Authorization', getAuthorizationHeader())
},
onSuccess () {
// Create the document with {"primaryFile": {"uploadId": "Hh3rN0bQ4kqZ3t2sYw9x1A"}}
}
})
upload.start()
Verifying the content
The upload operations support the RFC 9530 Digest Fields:
Repr-Digeston the creation request or on anyPATCHdeclares the digest of the whole file, with thesha-256orsha-512algorithm. The request that completes the upload verifies it; on a mismatch it is rejected with400codedigest_mismatch, and the upload is deleted.Content-Digeston aPATCHdeclares the digest of its body, with thesha-256algorithm. On a mismatch the request is rejected with400codedigest_mismatchand appends nothing, so the client can send the same chunk again.
A ready upload reports the SHA-256 digest of the stored content
in its digest member and in the Repr-Digest header of GET and HEAD,
so a client can compare it without having declared a digest.
Security schemes
- HTTP: Bearer Auth
An API key with the rest scope, sent as Authorization: Bearer <key>. See Authentication.
Security Scheme Type: | http |
|---|---|
HTTP Authorization Scheme: | bearer |
Bearer format: | API key |