Skip to content

Quotes

A quote is what a subcontractor sent in response to an invitation, or one you uploaded yourself. Each time a subcontractor revises their quote, the new revision gets its own ID and the old one is kept.

GET /api/tender/builder/v1/companies/{companyId}/tender-projects/{projectId}/quotes
GET /api/tender/builder/v1/companies/{companyId}/tender-projects/{projectId}/quotes/{quoteId}
GET /api/tender/builder/v1/companies/{companyId}/tender-projects/{projectId}/quotes/{quoteId}/download

All need quotes/read, and quotes must be enabled for your company in E1.

FieldTypeNotes
idstringNumeric string. Each revision has its own ID.
invitationIdstring | nullThe invitation the quote was received against; null for a quote you uploaded yourself.
packageIdstring | nullnull for an uncategorised quote, which isn’t assigned to a package yet.
packageNamestring | null
versioninteger1 for the original quote, incremented by each revision.
isLatestRevisionbooleanfalse when a newer revision has been received.
amountCentsinteger | nullThe quoted amount in cents, excluding tax (GST/VAT). null if the quote has no amount.
submittedAtstring | nullISO 8601 timestamp. When this revision was received.
inviteeobject | nullWho sent it, in the same shape as the invitation’s invitee. null when it wasn’t received against an invitation.
filesobject[]The quote’s files. See below.
viewedbooleanWhether anyone in your company has downloaded the quote.
lastDownloadedAtstring | nullWhen someone in your company last downloaded it.
FieldTypeNotes
idstringprimary for the quote document itself, otherwise the additional file’s ID.
namestringThe file name.
sizeinteger | nullSize in bytes.
primarybooleanWhether this is the quote document rather than an additional file.

Only file metadata is returned. To get the files, download the quote.

GET .../quotes returns the latest revision of each quote on the project, oldest first. Deleted quotes are not included.

Query parameterNotes
packageIdOptional. Only list quotes for this package.
uncategorisedOptional. true to only list quotes not yet assigned to a package. Can’t be combined with packageId.
limitOptional. Page size, 1 to 100. Defaults to 25.
cursorOptional. The _metadata.nextCursor from the previous page.

The list is paginated. While _metadata.hasNextPage is true, request the next page with cursor set to _metadata.nextCursor and the same filter. The cursor is signed and tied to the project and filter it came from: pass it back unchanged. A cursor that was modified, or that came from a different listing, returns 400.

Terminal window
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/quotes?packageId=1930318"
{
"data": [
{
"id": "9001",
"invitationId": "7001",
"packageId": "1930318",
"packageName": "Joinery",
"version": 2,
"isLatestRevision": true,
"amountCents": 12500000,
"submittedAt": "2026-09-05T12:00:00+10:00",
"invitee": {
"addressBookCompanyId": "3001",
"addressBookContactId": "4001",
"companyName": "Acme Joinery",
"contactName": "Jo Smith"
},
"files": [
{ "id": "primary", "name": "Acme Joinery quote.pdf", "size": 482133, "primary": true },
{ "id": "6001", "name": "Schedule of rates.xlsx", "size": 20480, "primary": false }
],
"viewed": true,
"lastDownloadedAt": "2026-09-06T08:00:00+10:00"
}
],
"_metadata": {
"nextCursor": null,
"hasNextPage": false
}
}

GET .../quotes/{quoteId} returns one quote as data, without _metadata. It works for older revisions too. As well as the fields above, it includes:

FieldTypeNotes
notesobject[]Your team’s notes on the quote, oldest first: id, text, authorName and createdAt. Mentions of colleagues appear in text as @[Name](userId).
revisionsobject[]Every revision of the quote, oldest first, including this one: id, version, amountCents and submittedAt. Read any of them by its id.
{
"data": {
"id": "9001",
"version": 2,
"isLatestRevision": true,
"amountCents": 12500000,
"notes": [
{
"id": "5101",
"text": "Cheapest so far, includes pump hire.",
"authorName": "Pat Estimator",
"createdAt": "2026-09-06T09:00:00+10:00"
}
],
"revisions": [
{ "id": "9000", "version": 1, "amountCents": 13000000, "submittedAt": "2026-09-04T12:00:00+10:00" },
{ "id": "9001", "version": 2, "amountCents": 12500000, "submittedAt": "2026-09-05T12:00:00+10:00" }
]
}
}

The other quote fields are omitted from this example.

GET .../quotes/{quoteId}/download gets every file on the quote in one download. A quote with only its quote document downloads as that file. A quote with additional files downloads as a .zip holding all of them.

The files are not returned in the response body. Instead the API responds 302 Found, and the Location header holds a short-lived URL that serves the download. Most HTTP clients follow the redirect for you:

Terminal window
curl -sS -L -OJ \
-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/quotes/9001/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+00:00",
"name": "Acme_Joinery_quote_pdf.zip"
}
}

name is the file name for a single-file quote, otherwise the name of the .zip.

The URL is valid for 5 minutes and needs no API credentials, so anyone holding it can download the quote until expiresAt. Don’t log or store it; request a new one each time you need the files. The redirect goes to a different host from the API, and your client should not forward your Authorization header to it.

Each request counts as your company downloading the quote, so it marks the quote as viewed and updates lastDownloadedAt, the same as downloading it in E1.

StatusWhen
400limit, packageId, uncategorised or the quote ID isn’t valid, packageId was combined with uncategorised=true, or the cursor was modified or came from a different listing (INVALID_CURSOR).
401The token doesn’t carry quotes/read.
403Quotes aren’t enabled for your company, or the builder restricts the project to certain team members.
404The project, package or quote doesn’t exist, or belongs to another company.