Prerequisites
- A personal access token with the
streams:writescope - A Speckle project, where you have a
Can editrole
Get your personal access token from: Avatar → Profile → Personal Access Tokens in the Speckle
web app.
Authorization:
On app.speckle.systems, this presigned-URL workflow is the default
— no feature flag is required. On self-hosted public Speckle Server releases, set
FF_NEXT_GEN_FILE_IMPORTER_ENABLED=true so the server queues jobs for ifc-import-service. Leave
FF_LEGACY_FILE_IMPORTS_ENABLED off unless you need the legacy REST path — see the FAQ below.Overview
Uploading a file and triggering Speckle’s automatic ingestion pipeline is a multi-step process:-
Ask Speckle Server for a presigned upload URL
(GraphQL mutation:
fileUploadMutations.generateUploadUrl) - Upload the file directly to blob storage (simple HTTP PUT to the presigned URL)
-
Create a model (if needed)
(GraphQL mutation:
modelMutations.create- optional if you already have a model) -
Tell Speckle to parse and ingest the file
(GraphQL mutation:
fileUploadMutations.startFileIngestion) - Check the ingestion status (GraphQL query for the ingestion progress)
- Large files are never sent through the Speckle Server REST endpoints.
- The upload URL points to your server’s configured blob storage (S3, S3 compatible, or Azure).
- The ETag returned from Blob storage is required for step 4 (file ingestion).
- Successful ingestion results in a new Model Version.
Step 1: Generate an upload URL
Request an upload URL from Speckle Server. The response contains the URL and a unique ID for the file. POST/graphql
Mutation:
fileId from the response - you must use this exact value in Step 4 when triggering the file ingestion. Do not use the filename; use the fileId returned here.
Step 2: Upload the file to the presigned URL
PUT{presignedURL}
Headers required:
- Blob storage returns an ETag in the headers of the response.
- The ETag is a unique identifier for the uploaded file content, which Speckle uses to verify the file integrity before ingesting the file.
- The ETag is required for the final step.
200 status code, the response headers will contain an ETag header. Record this value:
"\"ad13b92e173...\"").
Step 3: Create a model (if needed)
This step requires a target model, where the file data should be ingested to. If you don’t have a model yet, you can create one programmatically: POST/graphql
Mutation:
model.id as it will be used as modelId in the next step.
Step 4: Trigger the file ingestion
Once the file is uploaded, tell Speckle to parse, convert, index and create a new model version. POST/graphql
Mutation:
fileId must be the exact value returned from Step 1’s generateUploadUrl response. Do not use the filename - use the fileId from that response.
Response example:
status enum signals the job status where queued, processing, success, failed, and cancelled are possible values.
Your file is now in the ingestion pipeline. Once ingested, a new Version will appear under the referenced Model.
Step 5: Check the ingestion status
Use the ID returned byfileUploadMutations.startFileIngestion.id to check progress. A successful result includes the new version ID; a failed result includes the error reason.
POST /graphql
Query:
Upload linked files with a model
Some formats keep part of a model in separate files. To convert those files into the same version as the model, upload them as linked files of the model file.
Upload the model file and its linked files in this order:
- Request an upload URL for the model file as in Step 1.
- Request an upload URL for each linked file. Set
linkedToFileTypeto the extension of the model file, without the dot, such as"edb". WithoutlinkedToFileType, the server refuses a file that is not a model of its own, such as a.mshfile. - Upload each file as in Step 2 and record each ETag.
- In
startFileIngestion, pass the linked files inlinkedFiles, each with itsfileIdandetag. One upload takes at most 100 linked files. For the ETABS file size limits, see Upload an ETABS model.
Self-hosted servers on 2026.9 and earlier do not accept
linkedToFileType or linkedFiles.Example: upload an ETABS model with its results
This script uploads an.edb and the result files that share its name, then starts the
ingestion. It needs curl and jq, and a model to upload into.
An ETABS upload without its result files publishes the model without analysis results and reports
no error. Upload every result file ETABS saved for the model.
.edb. The result files must be in the same folder:
What developers need to know
Which file formats can I upload?
Which file formats can I upload?
app.speckle.systems supports IFC, RVT, NWC, NWD, DWG, 3DM, and
others. See Direct uploads for the current list and
the formats that changed in 2026.9.Open-source self-hosted servers support IFC only.
Extended formats on self-hosted deployments require Speckle Enterprise Server and a
license key. See File imports
setup.
Why are different formats supported in self-hosted vs cloud?
Why are different formats supported in self-hosted vs cloud?
Self-hosted open-source servers use open-source dependencies, which limits supported formats.
Formats such as RVT, NWC, NWD, DWG, DXF, and 3DM need proprietary libraries, so they are available
on app.speckle.systems and on Speckle Enterprise Server — not in
the public open-source distribution.
Where did the StartFileImport mutation go?
Where did the StartFileImport mutation go?
startFileImport has been removed. Use
fileUploadMutations.startFileIngestion, which gives more granular status updates and real-time
subscription updates, with ingestion queries shared across file import and other Speckle
integrations. As of the 2026.9 server release (1 September 2026), calling startFileImport
fails with a 400 error (UNSUPPORTED_FILE_TYPE) rather than falling back to the old
behavior — there is no compatibility period for this mutation.Update your code to use fileUploadMutations.startFileIngestion and the ingestion status
queries in this guide.Should I ever use the old REST upload?
Should I ever use the old REST upload?
For app.speckle.systems, no. The old REST upload is gone.For self-hosted instances, the old REST upload is only relevant if:
- Your server runs with the
FF_LEGACY_FILE_IMPORTS_ENABLEDfeature flag enabled (it is off by default), which proxies uploads through the server instead of sending them straight to blob storage - Your blob storage is not reachable by clients, so presigned URLs cannot work
/api/file/...).
However, we recommend leaving FF_LEGACY_FILE_IMPORTS_ENABLED off and making your S3-compatible storage reachable by clients, so the modern workflow described above works.Can I automate file uploads with SpecklePy?
Can I automate file uploads with SpecklePy?
SpecklePy centres on sending and receiving Speckle objects rather than this
presigned-upload pipeline. You can still implement this guide in Python by combining GraphQL
(
fileUploadMutations.generateUploadUrl, model creation if needed,
fileUploadMutations.startFileIngestion, ingestion status query) with HTTP PUT of file bytes
to the presigned URL via requests or httpx. For GraphQL steps, use
SpeckleClient.execute_query() as in Custom GraphQL
Queries, or POST the same
JSON payloads your tool would send to {host}/graphql.