The file is streamed directly through the GraphQL mutation using the Upload
scalar type, following the GraphQL multipart request specification.
UploadFileToEntity
Quick Links
Accounts Accounting Entries Products Customers Sales quotes Sales invoices Suppliers Purchase invoicesThis feature is currently under development and, although not available in the current version, its preliminary documentation is provided to give you a preview of the enhancements that will be included in an upcoming update.
HTTP | Operation | Type | Object | DTO Why-DTOs? |
---|---|---|---|---|
For a query, it returns a result already prepared by Sage Active, with no possibility to add filters, sorts, or explore the data further, or it executes an action (e.g., sending an email). For a mutation, it executes a specific business action, also carried out by Sage Active, which will impact the data (e.g., generating a credit note from an invoice). |
uploadFileToEntity |
UploadFileInput |
Description

Required Parameters
You must provide the following information:
- file: The actual file to upload (GraphQL
Upload
scalar). - entityType: The business entity to associate the file with.
- entityId: The ID of the record within that entity.
- fileName: A name to assign to the file.
- businessDate: A business-relevant date associated with the file.
- comment: An optional comment or description for the file.
Upload logic and multipart structure
The UploadFileToEntity
mutation expects the file to be sent using the GraphQL multipart request specification, which is supported by HotChocolate.
In this approach, the mutation input includes a file
field set to null
, and the actual file content is passed separately using the multipart/form-data
protocol.
The structure of the request includes three main parts:
- operations: a JSON string containing the GraphQL query and variables, where the file field is set to
null
. - map: a JSON string linking the uploaded file to the correct variable path in the query (e.g.
"0": ["variables.input.file"]
). - file: the actual file data, associated with the key defined in the map (e.g.
0
).
This allows the server to inject the file stream into the mutation input at runtime, making it available as a stream on the backend for processing.
An additional HTTP header GraphQL-preflight: 1
is required when uploading a file.
This header is mandatory for security reasons and must be included in your multipart upload requests.
Header
Key | Value |
---|---|
Authorization |
Bearer Current access Token How to find? |
X-TenantId |
Current tenant id How to find? |
X-OrganizationId |
Current organization Id How to find? |
x-api-key |
Primary or secondary subscription key of your app How to find? |
GraphQL-preflight ⚠️ |
1 |
How to upload a pdf file to an accountingEntry ?
GraphQL Mutation
mutation($input: UploadFileInput!) {
uploadFileToEntity(input: $input)
{
id
}
}
GraphQL Variables (in operations
)
{
"input": {
"file": null,
"entityType": "ACCOUNTING_ENTRY",
"entityId": "8b21a143-4d9c-41ea-b1dc-1cf9d91a72f1",
"fileName": "invoice-q1.pdf",
"businessDate": "2025-04-04",
"comment": "Invoice for Q1"
}
}
File Binding Map (in map
)
{
"0": ["variables.input.file"]
}
File Part (in the multipart request)
The actual file is sent in the multipart body as a separate part, using the key 0
(as referenced in the map):
--boundary
Content-Disposition: form-data; name="0"; filename="invoice-q1.pdf"
Content-Type: application/pdf
(binary file content here)
--boundary--
Most GraphQL clients (like Postman, Insomnia, or Apollo Upload Client) handle this automatically when using the Upload
scalar. Just make sure:
- the field is named
0
, - the file is selected in the form-data body,
- and the
map
andoperations
fields are correctly provided.
Example Response
{
"data": {
"uploadFileToEntity": {
"id": "98f7adc7dd104cd984f9c113ef9a8e54.pdf"
}
}
}
How to test an upload with Postman ?
In Postman, uploading a file via GraphQL requires setting a multipart/form-data
request, adding the GraphQL-preflight: 1
header, and correctly populating the operations
, map
, and file parts of the request.
Steps:
- In Postman, open the Headers tab.
- Add the usual authentication headers required for any API call:
- Authorization:
Bearer {Current access Token}
- X-TenantId:
{Current tenant id}
- X-OrganizationId:
{Current organization id}
- x-api-key:
{Primary or secondary subscription key}
- Authorization:
- Add an additional header specifically required for uploads:
- Key:
GraphQL-preflight
- Value:
1
- Key:
- In the Body tab, select form-data.
-
Set the following three key/value pairs:
Key Type Value / Example operations Text GraphQL mutation + variables, with file: null
(see example below)map Text { "0": ["variables.input.file"] }
0 File The actual file to upload (e.g., invoice-q1.pdf
)
-
operations:
A JSON string containing the GraphQL mutation and its variables, as in the following example.
Thefile
field must be set tonull
in the variables.{ "query": "mutation($input: UploadFileInput!) { uploadFileToEntity(input: $input){id}}", "variables": { "input": { "file": null, "entityType": "Customer", "entityId": "8b21a143-4d9c-41ea-b1dc-1cf9d91a72f1", "fileName": "invoice-q1.pdf", "businessDate": "2025-04-04", "comment": "Customer invoice Q1" } } }
-
map:
A JSON string mapping the uploaded file (index0
) to the GraphQL variable path:{ "0": ["variables.input.file"] }
-
0:
The actual file upload. As the key, use0
(matching the map).
Select type File and upload your PDF, image, or other supported file.
Make sure that:
- Content-Type is correctly set to multipart/form-data (Postman does this automatically when using form-data body type).
- The key for the file (e.g.,
0
) matches exactly the one referenced in themap
. - Without the
GraphQL-preflight: 1
header, the server will reject the upload request.
UploadFileInput Input Parameters
Fields | Type | Description | Length |
---|---|---|---|
file | Upload | File content to upload | |
entityType |
|
Type of business entity (e.g., Customer, Supplier, etc.) | |
entityId | UUID | ID of the target entity record | |
fileName | String | Display name to assign to the uploaded file | 100 ?? |
businessDate | DateTime |
Relevant date associated with the file | |
comment | String (Optional) | Optional text or description | 255 ?? |
uploadFileToEntity Response
Returns a String
containing the unique ID of the uploaded file.
This ID includes the original file extension.
Fields | Type | Description |
---|---|---|
id | String | File identifier (e.g., "98f7adc7dd104cd984f9c113ef9a8e54.pdf" ) |
Example Response
{
"data": {
"uploadFileToEntity": {
"id": "98f7adc7dd104cd984f9c113ef9a8e54.pdf"
}
}
}
How to verify that a file was successfully uploaded
File uploads are processed asynchronously.
After the upload request is received, the file is placed in a queue for antivirus scanning to ensure it is not infected.
A file passes through several status values during this process:
Status | Description |
---|---|
Pending | Upload in progress |
ToCheck | File ready to be submitted to the antivirus solution |
Done | File is safe and has been stored |
Rejected | File is infected and was not stored |
To check the status of a file, use the files query, filtering by the file id
, and check the value of the status
field.
- If status is
Pending
orToCheck
, processing is still in progress. - When status is
Done
, the file has been successfully uploaded and is available. - If status is
Rejected
, the file was not stored due to infection.
GraphQL Query Example (filtering by file ID)
query {
files(
where: {
id: { eq: "98f7adc7dd104cd984f9c113ef9a8e54.pdf" }
}
) {
edges {
node {
fileName
status
}
}
}
}
Example Response
{
"data": {
"files": {
"edges": [
{
"node": {
"fileName": "invoice-q1.pdf",
"status": "Done"
}
}
]
}
}
}
By default, the files operation automatically filters out files whose status is not Done
, so that only accessible files are returned.
The only exception is when filtering by id
, in which case this automatic filter is not applied, allowing you to check the status of an uploaded file.