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.
Which job to create
Section titled “Which job to create”There are three, differing only in what they attach the result to:
| Job | Create at | Produces |
|---|---|---|
| New document | POST .../tender-projects/{projectId}/upload-document-job | One new document on the project. |
| New revision | POST .../tender-projects/{projectId}/documents/{documentId}/upload-document-job | A new revision of an existing document. |
| Archive | POST .../tender-projects/{projectId}/upload-archive-job | Many 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.
The flow
Section titled “The flow”-
Create the job. You describe the file — its name, size and checksum — before sending it. The response is
201 Created,status: "pending", and anuploadUrl. -
Send the file to
uploadUrlwith an HTTPPUT. This goes to storage directly, not to the API, so none of the API’s own headers apply. -
Poll the job with
GETuntilstatusreachescompletedorfailed. -
Read the result. A completed document job carries
document.id; a completed archive job carriesdocuments[]. To get a file back, see Downloading the file.
Creating a job
Section titled “Creating a job”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"| Field | Type | Notes |
|---|---|---|
title | string | Human-readable title for the document. 1–255 characters. |
fileName | string | File name including extension. 1–255 characters. |
directoryName | string | Destination folder, e.g. /Architectural. 1–255 characters, with no . or .. segments, backslashes or control characters. |
drawingId | string | The builder’s drawing number. Up to 15 characters. |
revisionNumber | string | Revision label. Up to 10 characters. |
fileSize | number | Size in bytes, a whole number from 1 to 2,147,483,647 (2 GiB − 1). Must match the file exactly. |
fileHash | string | Hex-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:
shasum -a 256 A-101.pdf | cut -d' ' -f1Sending the file
Section titled “Sending the file”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.
Job status
Section titled “Job status”GET .../upload-document-job/{uploadDocumentJobId}GET .../upload-archive-job/{uploadArchiveJobId}Job IDs are UUIDs. The status field moves in one direction:
| Status | Meaning | Extra fields |
|---|---|---|
pending | Created; the file has not arrived. | uploadUrl |
uploaded | The file arrived and is queued. | |
processing | E1 is verifying and unpacking it. | |
processed | Processing finished; being attached to the project. | |
completed | Done. | document (document jobs) or documents[] (archive jobs) |
failed | Stopped. | 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.
Cancelling a job
Section titled “Cancelling a job”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 Allowedwith codeNOT_ALLOWED— the job is pastpending. The response carriesAllow: 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.
When a job fails
Section titled “When a job fails”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:
failureReason | Meaning |
|---|---|
DOCUMENT_HAS_PENDING_REVISION | The document already has a revision waiting outside the addendum, usually a draft package change in E1. |
ADDENDUM_FINALISING | The builder is issuing the addendum. Create the job again shortly. |