Skip to content

Upload jobs

Files are not posted to the API directly. Instead you create an upload job, which hands back a short-lived URL to send the file to, and then poll the job until E1 has processed it. The indirection keeps large files off the API and lets E1 verify and unpack them before they appear on the project.

Every endpoint on this page needs the documents/write scope — including the reads, because a job is part of a write in progress rather than published data.

There are three, differing only in what they attach the result to:

JobCreate atProduces
New documentPOST .../tender-projects/{projectId}/upload-document-jobOne new document on the project.
New revisionPOST .../tender-projects/{projectId}/documents/{documentId}/upload-document-jobA new revision of an existing document.
ArchivePOST .../tender-projects/{projectId}/upload-archive-jobMany documents, unpacked from one archive.

Archive jobs exist so you can issue a whole document set in one operation instead of one job per file.

If the project has a pending addendum, every job’s result joins it. A new document or archive file is added to the addendum. A new revision becomes a revising document in the addendum, so the completed job’s document.id is the new document’s id, not the one in the job’s path. The original stays current until the builder issues the addendum. Without a pending addendum, results are issued straight away and a revision replaces the file on the existing document.

  1. Create the job. You describe the file — its name, size and checksum — before sending it. The response is 201 Created, status: "pending", and an uploadUrl.

  2. Send the file to uploadUrl with an HTTP PUT. This goes to storage directly, not to the API, so none of the API’s own headers apply.

  3. Poll the job with GET until status reaches completed or failed.

  4. Read the result. A completed document job carries document.id; a completed archive job carries documents[]. To get a file back, see Downloading the file.

Terminal window
curl -sS -X POST \
-H "Authorization: Bearer $E1_ACCESS_TOKEN" \
-H "x-e1-client-id: $E1_CLIENT_ID" \
-H "content-type: application/json" \
-d '{
"data": {
"title": "Ground Floor Plan",
"fileName": "A-101.pdf",
"directoryName": "/Architectural",
"drawingId": "A-101",
"revisionNumber": "C",
"fileSize": 482113,
"fileHash": "0b5c9e2a1f4d7c3e8a6b2d5f9c1e4a7b3d6f8c2e5a9b1d4f7c3e6a8b2d5f9c1e"
}
}' \
"$E1_API_BASE/api/tender/builder/v1/companies/$E1_COMPANY_ID/tender-projects/198578/upload-document-job"
FieldTypeNotes
titlestringHuman-readable title for the document. 1–255 characters.
fileNamestringFile name including extension. 1–255 characters.
directoryNamestringDestination folder, e.g. /Architectural. 1–255 characters, with no . or .. segments, backslashes or control characters.
drawingIdstringThe builder’s drawing number. Up to 15 characters.
revisionNumberstringRevision label. Up to 10 characters.
fileSizenumberSize in bytes, a whole number from 1 to 2,147,483,647 (2 GiB − 1). Must match the file exactly.
fileHashstringHex-encoded SHA-256 digest of the file — 64 hex characters, as produced by shasum -a 256 <file>.

An archive job takes only fileName (1–255 characters), fileSize (a whole number from 1 to 5,000,000,000) and fileHash; the documents inside are described by the archive itself. An archive fails if it holds more than 10,000 files, or a file in a .. or absolute folder.

A request outside these limits is refused with a 400.

fileSize is signed into the upload URL, so it is a commitment rather than a hint: send a file of a different length and the upload is rejected before it reaches us.

fileHash is checked after the upload, when we recompute the digest of what arrived. A mismatch does not fail the PUT — it fails the job, which ends up failed with File hash mismatch in failureReason. Get it from:

Terminal window
shasum -a 256 A-101.pdf | cut -d' ' -f1
Terminal window
curl -sS -X PUT \
--upload-file A-101.pdf \
"$UPLOAD_URL"

Do not send your API bearer token to uploadUrl. Everything needed to authorize the upload is already in the signed URL, and adding headers can invalidate the signature.

GET .../upload-document-job/{uploadDocumentJobId}
GET .../upload-archive-job/{uploadArchiveJobId}

Job IDs are UUIDs. The status field moves in one direction:

StatusMeaningExtra fields
pendingCreated; the file has not arrived.uploadUrl
uploadedThe file arrived and is queued.
processingE1 is verifying and unpacking it.
processedProcessing finished; being attached to the project.
completedDone.document (document jobs) or documents[] (archive jobs)
failedStopped.failureReason

Only completed and failed are terminal. uploadUrl appears only while pending, and failureReason only on failed — so read status first and let it tell you which shape you have, rather than testing for the presence of fields.

{
"data": {
"id": "8ebda39d-a2db-40f8-9094-99903b560ae3",
"title": "Ground Floor Plan",
"fileName": "A-101.pdf",
"directoryName": "/Architectural",
"drawingId": "A-101",
"revisionNumber": "C",
"fileSize": 482113,
"fileHash": "0b5c9e2a1f4d7c3e8a6b2d5f9c1e4a7b3d6f8c2e5a9b1d4f7c3e6a8b2d5f9c1e",
"status": "completed",
"document": { "id": "8074957" }
}
}

Poll at a steady interval rather than tightly — processing time scales with file size, and archives take longer than single documents. The rate limits apply to polling like any other call.

DELETE .../upload-document-job/{uploadDocumentJobId}
DELETE .../upload-archive-job/{uploadArchiveJobId}

A job can only be deleted while it is pending — that is, before the file arrives. Once the file is in, the work is under way and cancelling is not possible:

  • 204 No Content — deleted.
  • 405 Method Not Allowed with code NOT_ALLOWED — the job is past pending. The response carries Allow: GET, meaning reading it is all that is left.
  • 404 Not Found — no such job for this project.

There is no way to cancel after upload. Let the job finish and then delete the resulting document if you need to undo it.

failed is a normal outcome, not an error to retry blindly — failureReason says what went wrong. A checksum or size that disagrees with the file you sent fails the job at verification, after a successful upload, so a 200 from the upload URL does not by itself mean the file was accepted.

Fix the cause and create a new job; a failed job cannot be restarted.

While the project has a pending addendum, a revision job can also fail with:

failureReasonMeaning
DOCUMENT_HAS_PENDING_REVISIONThe document already has a revision waiting outside the addendum, usually a draft package change in E1.
ADDENDUM_FINALISINGThe builder is issuing the addendum. Create the job again shortly.