Documents
Documents are the drawings and specifications issued on a project. This page covers reading, downloading and managing documents that already exist; getting a new file onto a project is a separate flow, described in Upload jobs.
GET /api/tender/builder/v1/companies/{companyId}/tender-projects/{projectId}/documentsGET /api/tender/builder/v1/companies/{companyId}/tender-projects/{projectId}/documents/{documentId}GET /api/tender/builder/v1/companies/{companyId}/tender-projects/{projectId}/documents/{documentId}/downloadPUT /api/tender/builder/v1/companies/{companyId}/tender-projects/{projectId}/documents/{documentId}DELETE /api/tender/builder/v1/companies/{companyId}/tender-projects/{projectId}/documents/{documentId}Reads and downloads need documents/read; update and delete need documents/write.
The document object
Section titled “The document object”| Field | Type | Notes |
|---|---|---|
id | string | Numeric string. |
name | string | File name as issued, including extension. |
directory | string | Folder path within the project, e.g. /Architectural. / is the root. |
title | string | null | Human-readable title, independent of the file name. |
drawingId | string | null | The builder’s drawing number. |
revision | string | null | Revision label, e.g. C, 2. |
size | number | Size in bytes. |
addendumId | string | null | The addendum the document was added or revised in; null for the original issue. |
addendumStatus | string | confirmed once issued; new or revised while it is in the pending addendum. |
supersedesDocumentId | string | null | The document this one revises, or will revise once its addendum is issued. |
Listing and reading
Section titled “Listing and reading”GET .../documents returns every current document on the project. GET .../documents/{documentId} returns one.
curl -sS \ -H "Authorization: Bearer $E1_ACCESS_TOKEN" \ -H "x-e1-client-id: $E1_CLIENT_ID" \ "$E1_API_BASE/api/tender/builder/v1/companies/$E1_COMPANY_ID/tender-projects/198578/documents"{ "data": [ { "id": "8074957", "name": "A-101 Ground Floor Plan.pdf", "directory": "/Architectural", "title": "Ground Floor Plan", "drawingId": "A-101", "revision": "C", "size": 482113, "addendumId": null, "addendumStatus": "confirmed", "supersedesDocumentId": null } ]}Downloading the file
Section titled “Downloading the file”GET .../documents/{documentId}/download gets the file itself. The file is not
returned in the response body. Instead the API responds 302 Found, and the
Location header holds a short-lived URL that serves the file. Most HTTP
clients follow the redirect for you:
curl -sS -L -o "A-101 Ground Floor Plan.pdf" \ -H "Authorization: Bearer $E1_ACCESS_TOKEN" \ -H "x-e1-client-id: $E1_CLIENT_ID" \ "$E1_API_BASE/api/tender/builder/v1/companies/$E1_COMPANY_ID/tender-projects/198578/documents/8074957/download"If your client can’t follow redirects, or you want to hand the URL to something
else, add ?redirect=false to get it as JSON instead:
{ "data": { "url": "https://fm.prod.filemanager.estimateone.com/download?token=...", "expiresAt": "2026-10-06T01:05:00.000Z", "name": "A-101 Ground Floor Plan.pdf", "contentType": "application/pdf", "size": 482113 }}The URL is valid for 5 minutes and needs no API credentials, so anyone holding
it can download the file until expiresAt. Don’t log or store it; request a
new one each time you need the file. The redirect goes to a different host from
the API, and your client should not forward your Authorization header to it.
To get every document in a package at once, download the package’s
documents as a
.zip.
Updating metadata
Section titled “Updating metadata”PUT .../documents/{documentId} changes a document’s metadata. It does not
replace the file — to issue a new revision of the file itself, run an upload job
against the document.
The body carries every field except id, size and the addendum fields, which
are all derived:
curl -sS -X PUT \ -H "Authorization: Bearer $E1_ACCESS_TOKEN" \ -H "x-e1-client-id: $E1_CLIENT_ID" \ -H "content-type: application/json" \ -d '{ "data": { "name": "A-101 Ground Floor Plan.pdf", "directory": "/Architectural", "title": "Ground Floor Plan (revised)", "drawingId": "A-101", "revision": "D" } }' \ "$E1_API_BASE/api/tender/builder/v1/companies/$E1_COMPANY_ID/tender-projects/198578/documents/8074957"Returns 200 OK with the updated document. Like the project update, this is a
full replacement: omitted nullable fields are read as null.
If the document has a pending revision in the project’s pending addendum, the change is made to that revision and the response carries the revision.
Deleting
Section titled “Deleting”DELETE .../documents/{documentId} returns 204 No Content.
While the project has a pending addendum, only documents added or revised in it can be deleted. Deleting a revision leaves the document it was revising as it was. Deleting any other document is refused, because the deletion would take effect immediately rather than being issued with the addendum.
When writes are refused
Section titled “When writes are refused”A pending addendum can refuse a write with 400 Bad Request, code
UPDATE_FAILED. detail says why:
| Situation | Applies to |
|---|---|
| The document isn’t part of the pending addendum. | DELETE |
| The document already has a pending revision. Change or delete the revision instead. | PUT, DELETE |
| The project has unconfirmed package changes. | DELETE |
| The pending addendum is being issued. Retry shortly. | PUT, DELETE |
Two project states change what the write endpoints do:
| State | Behaviour |
|---|---|
| Archived project | Every request for the project returns 404 Not Found, as though it did not exist. |
| Connected project | Reads work normally. PUT and DELETE on documents, and PUT on the project, return 403 Forbidden with code FORBIDDEN. |
A connected project is one whose documents are maintained through a linked system rather than directly on E1, so E1 is not the source of truth for them.