Skip to main content

Upload a file

This guide covers the supported strategies for uploading documents to the Stonal platform. It details the main combinations you can use to tell the system where and how to place your document during upload.


Prerequisites​

  • A valid OAuth access token (see Authentication)
  • Your organization code (used in the endpoint path)
  • A file to upload

1. Upload Strategies & Required Fields​

Each strategy maps to a combination of manifest fields. Use the table below to identify which payload suits your scenario.

StrategyRequired Manifest FieldsDescription
1. Asset UID + Documentation Template ⭐asset.uid, documentation.templateRecommended: let the platform resolve the correct documentation for the asset.
2. External Asset Code + Documentation Template ⭐asset.externalIds.{source}, documentation.templateRecommended when your integration does not use Stonal asset UIDs.
3. Asset + Documentation Template + Folder Template ⭐Strategy 1 or 2 + folder.templateRecommended when you want to target a specific folder type within the documentation.
4. Documentation UIDdocumentation.uidUse this when you already know the existing documentation you want to upload into.

linkedAssets, tags.documentation and documentClass are optional enrichments that can be added on top of these strategies.


2. Manifest Example Payloads​

Include only the fields you need for your chosen strategy.

2.1 By Asset UID + Documentation Template ⭐​

{
"asset": {
"uid": "822d8c50-0deb-4dae-8def-c9f9eab7f5be"
},
"documentation": {
"template": "f90ee77d-b6cc-41fa-8d1f-bea649e6d93d"
}
}

2.2 By External Asset Code + Documentation Template ⭐​

{
"asset": {
"externalIds": {
"code": "BUILDING_001"
}
},
"documentation": {
"template": "f90ee77d-b6cc-41fa-8d1f-bea649e6d93d"
}
}

2.3 By Asset + Documentation Template + Folder Template ⭐​

{
"asset": {
"externalIds": {
"code": "BUILDING_001"
}
},
"documentation": {
"template": "f90ee77d-b6cc-41fa-8d1f-bea649e6d93d"
},
"folder": {
"template": "e8df7c2a-b5bb-41fa-8d1f-bea649e6d93d"
}
}

2.4 By Documentation UID​

Use this when the documentation UID is already known, for example because you previously retrieved it from the asset documentations endpoint.

{
"documentation": {
"uid": "62c42614-31a8-43bb-a76f-8499c1b868e0"
}
}

2.5 Add linked assets​

linkedAssets can be combined with the recommended template-based strategies.

{
"asset": {
"externalIds": {
"code": "BUILDING_001"
}
},
"documentation": {
"template": "f90ee77d-b6cc-41fa-8d1f-bea649e6d93d"
},
"linkedAssets": [
{ "code": "RU-001" },
{ "uid": "fac98b98-c8ba-47e7-b5a6-2bc5222ae402" }
]
}

Important notes:

  • linkedAssets by code are resolved relative to the parent asset.
  • If you send linkedAssets without an asset, linked assets may be ignored.
  • Use the list format shown above for new integrations.

2.6 Add documentation tags and a document class​

Documentation tags are only associated if they already exist on the target documentation. The upload API does not create tags on the fly.

{
"asset": {
"uid": "822d8c50-0deb-4dae-8def-c9f9eab7f5be"
},
"documentation": {
"template": "f90ee77d-b6cc-41fa-8d1f-bea649e6d93d"
},
"documentClass": "123e4567-e89b-12d3-a456-426614174007",
"tags": {
"documentation": ["LEASE 2026", "SIGNED"]
}
}

Before using template-based strategies, discover available templates:

3.1 List available templates​

curl -H "Authorization: Bearer {accessToken}" \
"https://api.stonal.io/document-storage/v1/organizations/{organizationCode}/templates?language=fr-FR"

Response:

{
"templates": [
{
"id": "f90ee77d-b6cc-41fa-8d1f-bea649e6d93d",
"name": "DOE",
"organizationCode": "STONAL",
"attachmentType": "BUILDING_GROUP"
}
]
}

3.2 Get template details with folders​

curl -H "Authorization: Bearer {accessToken}" \
"https://api.stonal.io/document-storage/v1/organizations/{organizationCode}/templates/{templateId}?language=fr-FR"

Response:

{
"template": {
"id": "f90ee77d-b6cc-41fa-8d1f-bea649e6d93d",
"name": {
"fr-FR": "DOE"
},
"organizationCode": "STONAL",
"attachmentType": "BUILDING_GROUP",
"folders": [
{
"id": "e8df7c2a-b5bb-41fa-8d1f-bea649e6d93d",
"name": {
"fr-FR": "Documents généraux"
},
"parentId": null,
"documentClass": {
"identifier": "documentClassIdentifier",
"code": "DPE"
}
}
]
}
}

You can then use:

  • template.id as documentation.template
  • template.folders[].id as folder.template

3.3 Retrieve existing documentations for an asset​

If you want to upload into an existing documentation, first retrieve the documentations attached to the asset:

curl -H "Authorization: Bearer {accessToken}" \
"https://api.stonal.io/document-storage/v1/organizations/{organizationCode}/assets/{assetId}/documentations"

Use the returned identifier as documentation.uid.

For the full retrieval workflow, see Retrieve documentations for an asset.


4. Single-Request API Call​

Send both your manifest and file in one multipart/form-data POST:

curl -X POST \
"https://api.stonal.io/document-storage/v1/organizations/{organizationCode}/files/upload" \
-H "Authorization: Bearer {accessToken}" \
-F 'manifest=@manifest.json;type=application/json' \
-F 'file=@/path/to/your/file.pdf;type=application/pdf'

Replace placeholders with your actual values.


5. Response Format​

200 OK with JSON:

{
"documentId": "123e4567-e89b-12d3-a456-426614174000",
"duplicateDocumentIds": []
}

The API may also return non-blocking warnings in the X-DOCUMENT-STORAGE-WARNING response header.

Examples:

  • LINKED_ASSETS_NOT_FOUND
  • LINKED_ASSETS_NOT_FOUND:RU-001,PARK-002
  • LINKED_ASSETS_SKIPPED_NO_PARENT

When is AI processing triggered?​

In public upload flows, an AI request is sent when the uploaded file is attached to a documentation context.

The requested treatment then depends on the upload context:

  • if the file is uploaded only into a documentation, AI classification is requested
  • if a document class is provided, metadata extraction is requested
  • OCR is always requested as part of the AI request

In practice, template-based uploads remain the main public way to place the document in the right documentation before AI processing starts.


6. Error Handling​

StatusWhen
400 Bad RequestMissing or invalid manifest fields, invalid multipart request, or a rejected file (see 6.1)
404 Not FoundReferenced asset, documentation, template folder, or matching documentation for the provided asset and documentation template was not found
409 ConflictSpecified asset and documentation are not linked
413 Payload Too LargeThe file exceeds the size allowed for its type
415 Unsupported Media TypeThe file type is not supported
422 Unprocessable EntityNo documentation template matches the asset

Handle these responses gracefully in your integration.

6.1 Rejected files​

When the file itself is rejected, the body follows the Stonal error envelope (RFC 9457):

{
"type": "tag:UnsupportedFileType",
"title": "Unsupported file type",
"status": 415,
"detail": "Unsupported file type: malware.exe"
}
typeStatusMeaning
tag:EmptyFile400The file is empty
tag:InvalidFileName400The file name is missing or contains a path
tag:InvalidContentType400The declared Content-Type does not match the file extension
tag:InvalidFileSignature400The file content does not match its extension, e.g. a JPEG photo renamed .pdf
tag:FileTooLarge413The file exceeds an upload size limit. When the per-file-type limit is exceeded, the body also carries fileName, documentType, fileSizeBytes and maxFileSizeBytes; these fields are absent when the server rejects the multipart body before validation.
tag:UnsupportedFileType415The file extension is not supported

Branch on type, never on the wording of detail, which may change. Manifest errors are still returned as plain text.

The body also contains error and message fields, kept for existing integrations. They are deprecated and will be removed: use type and detail instead.


7. Best Practices​

  • Prioritize templates: This is the main public integration path and the most future-proof one.
  • Discover templates first: Use the /templates API to explore available options.
  • Prefer asset UIDs when possible: They remain the clearest identifiers.
  • Use documentation UID only when already known: Retrieve it first from the asset documentations endpoint.
  • Use linked assets only with a parent asset: Code-based linked assets are resolved relative to that parent.
  • Remember that documentation tags are not created automatically: Only existing documentation tags are associated.

For more details, see our API specification.


8. Complete Example​

For this manifest example:

manifest.json
{
"asset": {
"externalIds": {
"code": "BUILDING_001"
}
},
"documentation": {
"template": "f90ee77d-b6cc-41fa-8d1f-bea649e6d93d"
},
"folder": {
"template": "e8df7c2a-b5bb-41fa-8d1f-bea649e6d93d"
},
"linkedAssets": [
{ "code": "RU-001" }
],
"tags": {
"documentation": ["DPE 2026"]
},
"documentClass": "123e4567-e89b-12d3-a456-426614174007"
}

We will use the following command to upload the file:

curl \
-X POST "https://api.stonal.io/document-storage/v1/organizations/{organizationCode}/files/upload" \
-H "Authorization: Bearer {accessToken}" \
-F 'manifest=@manifest.json;type=application/json' \
-F 'file=@sample.pdf;type=application/pdf'

8.2 Existing documentation already known​

For this manifest example:

manifest.json
{
"documentation": {
"uid": "62c42614-31a8-43bb-a76f-8499c1b868e0"
}
}

We will use the same upload command with that manifest.