Skip to content

Supplier API

The Supplier API exposes Speci-finder matched project results for a supplier user.

Open the live API reference

GET /api/tender/supplier/v1/companies/{companyId}/speci-finder/results
RequirementValue
Scopespeci-finder/read
HeadersAuthorization: Bearer <access_token>, x-e1-client-id: <client_id>
NameTypeDescription
companyIdstringE1 company identifier for the supplier — the company account number shown in the E1 web app. Must match a registration on your application.

All are optional. Selection of “currently open” tenders is scoped server-side. createdSince narrows that selection to recently added projects; cursor and limit control paging.

NameTypeDescription
createdSinceRFC 3339 date-timeOnly return projects added to E1 on or after this instant (inclusive), e.g. 2026-05-01T00:00:00Z. The UTC offset (Z or +10:00) is required; fractional seconds are ignored. When set, results are ordered by createdAt, oldest first. It matches on the project’s creation, so a new tender added to an existing project isn’t returned; a full sync picks that up. A malformed value returns 400. See Incremental CRM sync.
cursorstringOpaque pagination cursor. Pass the _metadata.nextCursor value from a previous response to fetch the following page. Omit on the first request; an empty value is treated the same as omitting it. Treat it as opaque — do not construct or parse it.
limitintegerPage size. Defaults to 25; must be between 1 and 100. An out-of-range or non-numeric value returns 400.

Returns one page of matched projects, each with one or more matched tenders, plus a _metadata object describing the page. A project appears once per response; if a single project has matched tenders under different builders, those tenders are grouped under the same project entry.

{
"data": [
{
"projectId": "12345",
"projectName": "Main Street Development",
"createdAt": "2026-04-20T03:04:05Z",
"address": "123 Collins St, Melbourne Victoria 3000 Australia",
"category": "Commercial",
"budget": { "low": 1000000, "high": 5000000 },
"status": "Active",
"consultants": [
{
"name": "Smith Engineers",
"website": "https://smith.example",
"types": ["ENGINEER_STRUCTURAL", "ENGINEER_CIVIL"]
},
{ "name": "Jones Architects", "website": null, "types": ["ARCHITECT"] }
],
"tenders": [
{
"id": "67890",
"name": "Bulk Earthworks",
"type": "Tender",
"builder": { "name": "ABC Constructions", "website": "https://abc.example" },
"tenderOpenAt": "2026-05-01",
"quotesDueAt": "2026-05-15",
"description": "Bulk excavation of the site.\nIncludes retention and shoring.",
"estimatedStartDate": "2026-07-01",
"estimatedEndDate": "2027-06-30",
"status": "Open",
"matchedKeywords": ["concrete", "formwork"]
}
],
"tenderingSubcontractors": [
{ "name": "Apex Concrete", "website": "https://apex.example" },
{ "name": "Pour & Set", "website": null }
],
"url": "https://app.estimateone.com/find-tenders?projectId=12345"
}
],
"_metadata": {
"nextCursor": "MjAyNi0wNS0xNXwxMjM0NQ",
"hasNextPage": true
}
}

Project

FieldTypeNotes
projectIdstringE1 project identifier.
projectNamestringHuman-readable project name.
createdAtRFC 3339 date-time (UTC)When the project was added to E1, to the second, e.g. 2026-04-20T03:04:05Z. It doesn’t change when tenders are added to the project later; each tender’s tenderOpenAt says when that tender opened.
addressstringFull international postal address (street, suburb, state, postcode, country).
categorystringProject category (e.g. Commercial, Residential).
budget{ low: number, high: number | null } | nullBanded budget range in dollars. null when no budget is set; high may be null for an open-ended top end.
status"Active" | "Closed"Project-level status. Closed indicates the project itself is closed (regardless of any individual tender status).
consultantsarray of ConsultantConsultants engaged on the project. Empty for partners without the consultant-details add-on. Excludes unverified entries.
tendersarray of TenderOne or more matching tenders. Never empty for a returned project.
tenderingSubcontractorsarray of PartySubcontractors who have responded affirmatively to the builder on this project (currently quoting / quoted / quoted outside E1, or signalled interest via watchlist). Subject to the E1 SubbieSupplierConnection unlock model — for projects you haven’t unlocked, this lists only previously-viewed subbies. Deduplicated by company.
urlstringDeep link into the E1 web app for this project.

Tender

FieldTypeNotes
idstringE1 tender identifier.
namestringTender name (typically a trade package, e.g. “Bulk Earthworks”).
type"Tender" | "Procurement"Which product surface the tender belongs to.
builderPartyThe lead builder running this tender.
tenderOpenAtISO 8601 date (YYYY-MM-DD) or nullWhen the tender opened.
quotesDueAtISO 8601 date (YYYY-MM-DD) or nullWhen quotes close. null if no due date is set.
descriptionstring | nullThe builder’s description of the works, as plain text with a line break (\n) between paragraphs and list items. Each builder writes their own, so it can differ between tenders on the same project. null when the builder hasn’t provided one.
estimatedStartDateISO 8601 date (YYYY-MM-DD) or nullThe builder’s estimated construction start date. null if not set.
estimatedEndDateISO 8601 date (YYYY-MM-DD) or nullThe builder’s estimated construction end date. null if not set.
status"Open" | "Closed" | "Awarded"Tender-level status.
matchedKeywordsarray of stringThe subset of the supplier account’s Speci-finder keywords that matched this tender.

Party

FieldTypeNotes
namestringDisplay name of the company (preferring the linked E1 account’s name when available).
websitestring | nullCompany website URL, normalised to include an https:// scheme. null when no website is on file.

Consultant

A Party plus the disciplines it’s engaged under on this project.

FieldTypeNotes
namestringAs for Party.
websitestring | nullAs for Party.
typesarray of stringThe consultant’s disciplines on this project, in E1’s ranking order. A firm engaged under several disciplines (say, architect and landscape architect) appears once and lists each one. One of: ARCHITECT, ARCHITECT_INTERIOR, ARCHITECT_LANDSCAPE, ENGINEER_ACOUSTIC, ENGINEER_CIVIL, ENGINEER_ELECTRICAL, ENGINEER_FIRE, ENGINEER_GEOTECHNICAL, ENGINEER_HYDRO, ENGINEER_MECH, ENGINEER_STRUCTURAL, ENGINEER_THERMAL, CONSULTANT_ACCESS, CONSULTANT_ARBORICULTURE, CONSULTANT_ENVIRONMENTAL, CONSULTANT_FIRE, CONSULTANT_HERITAGE, CONSULTANT_LIGHTING, CONSULTANT_PLANNING, CONSULTANT_SURVEY, CONSULTANT_SUSTAINABILITY, CONSULTANT_TECHNOLOGY, CONSULTANT_TRAFFIC. New disciplines may be added, so ignore a value you don’t recognise rather than failing.

_metadata

FieldTypeNotes
nextCursorstring | nullPass as the cursor query parameter to fetch the next page. null when there are no more pages.
hasNextPagebooleantrue when another page is available. Equivalent to nextCursor !== null.

Results are paged with an opaque cursor. Without createdSince they’re ordered by each project’s earliest tender quotesDueAt (ascending, undated projects last). With createdSince they’re ordered by createdAt (oldest first), then projectId. To walk every page:

  1. Make the first request with no cursor (optionally set limit).
  2. If _metadata.hasNextPage is true, repeat the request with cursor set to _metadata.nextCursor, keeping the same limit and createdSince.
  3. Stop when hasNextPage is false (nextCursor is null).

The cursor encodes a position in the ordering, so pages never overlap or skip entries even as limit varies between requests. A cursor is only valid with the createdSince it was issued for: change or drop createdSince mid-walk and the request returns 400 INVALID_CURSOR, so start again from the first page. Treat the cursor as opaque — its contents are not part of the contract. Re-requesting the same cursor/limit within a short window is served from cache, so a paged sweep stays cheap and stable.

  • Results only include tenders that are currently open — quotes are not past due and fall within the near-term window. The window is server-side; partners do not control it.
  • Matching is driven by the supplier account’s Speci-finder keywords (account-wide; resolved via the assigned user on the credential). If the account has no keywords configured, the response will be 200 OK with data: [].
  • See Data freshness for how quickly new data propagates.

First page (default limit):

Terminal window
curl -X GET \
"https://api.estimateone.com/api/tender/supplier/v1/companies/{companyId}/speci-finder/results?limit=25" \
-H "Authorization: Bearer $TOKEN" \
-H "x-e1-client-id: $CLIENT_ID"

Projects added since a given instant, oldest first:

Terminal window
curl -X GET \
"https://api.estimateone.com/api/tender/supplier/v1/companies/{companyId}/speci-finder/results?createdSince=2026-05-01T00:00:00Z&limit=100" \
-H "Authorization: Bearer $TOKEN" \
-H "x-e1-client-id: $CLIENT_ID"

Next page — pass the previous response’s _metadata.nextCursor:

Terminal window
curl -X GET \
"https://api.estimateone.com/api/tender/supplier/v1/companies/{companyId}/speci-finder/results?limit=25&cursor=MjAyNi0wNS0xNXwxMjM0NQ" \
-H "Authorization: Bearer $TOKEN" \
-H "x-e1-client-id: $CLIENT_ID"

See Errors for the full catalogue. Common failures for this endpoint:

StatuscodeCause
400CLIENT_ID_HEADER_MISSINGx-e1-client-id header not sent.
400INVALID_CURSORThe cursor was tampered with, or was issued for a different company or createdSince.
401CLIENT_ID_HEADER_MISMATCHx-e1-client-id header does not match the token.
401UnauthorisedToken invalid, company not registered, permission not published, or scope missing.
5xxFETCH_FAILED / INTERNAL_SERVER_ERRORSomething failed downstream. Retry with backoff.

The full OpenAPI 3.1 spec is published at https://api.estimateone.com/swagger/doc. Use it to drive client code generation in the language of your choice. You can also browse it interactively.