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:
What developers need to know
Which file formats can I upload?
Which file formats can I upload?
Before 2026.9, app.speckle.systems supports IFC, RVT, NWC, NWD, DWG, DXF, OBJ, STL,
3DM, and others — see Supported Formats.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.