API documentation

Tender portal exports

Your bank keeps its tender searches on its own portal accounts. Stonewake never logs into a portal on your behalf. You export the hits from the portal and upload the file, and the notices are stored for your team alone, and no other team and no other bank can see them. In the dashboard the upload sits on the Tenders page under Upload a portal export, for analysts and admins. A team without the Tenders page finds it on the settings page under Tender portal exports.

Files

  • A CSV or XLSX export with one notice per row, or the XML of one notice in the eForms format that TED and the Bekanntmachungsservice publish.
  • The file name ends in .csv, .xlsx or .xml and must match the content. The old binary .xls format is not read, so save such a file as .xlsx first.
  • At most 700,000 bytes and 2,000 rows per file. A larger export is uploaded in parts, and uploading a part twice stores nothing twice.
  • A worksheet with more than 1,000 columns is refused with "The worksheet has more than 1,000 columns. Save it with at most 1,000 columns and upload it again."
  • A CSV file may be separated by commas, semicolons or tabs, saved in UTF8 or in the Windows code page a German spreadsheet uses. Of a workbook the first worksheet is read.
  • A file that cannot be read as a whole is refused with the reason, and nothing in it is read.

The portal you exported from

Every upload names the portal the file was exported from, in the dashboard and as source_portal over the API. The name is stored with every notice of the upload and in its audit entry.

`source_portal`Portal
dtvpDeutsches Vergabeportal (DTVP), refused for now
vergabe24Vergabe24, refused for now
evergabe_bunde-Vergabe des Bundes
vergabe_nrwVergabe.NRW
rib_bayernvergabe.bayern (RIB Tender)
subreportsubreport
cosinex_laenderState procurement marketplaces (cosinex)
dabDeutsches Ausschreibungsblatt, refused for now
dtadDTAD Deutscher Auftragsdienst, refused for now
hadHAD Hessische Ausschreibungsdatenbank, refused for now
vergabe_saarlandvergabe.saarland, refused for now
oeffentlichevergabe_deBekanntmachungsservice (oeffentlichevergabe.de)
other_publicAnother public procurement portal
manualA file your bank compiled itself, from no portal

An upload from a portal marked refused is refused whole, before the file is read, and nothing of it is stored. Uploads from these portals are not accepted for now.

Portal profiles and your own mapping

The header of the file decides how its columns are read. A header is recognised only when it holds every column a portal profile requires.

ProfileHeader
templateThe Stonewake template, whose columns are the field names below. It needs reference and title.
oeffentlichevergabe_de_purposepurpose.csv of the CSV export of the Bekanntmachungsservice on oeffentlichevergabe.de.

When no profile or more than one profile matches, the preview lists the columns of your file and asks you to map them to fields. Only the columns you map are read. A column that neither the profile nor your mapping names is listed as unknown and never read, and nothing is guessed from a column's name.

Fields

FieldContent
referenceThe row's identity in its portal, such as the portal's notice number. Without it the row needs notice_id and notice_version.
titleThe title of the notice. Required.
notice_id, notice_versionThe notice identifier as eForms states it, or the numeric id of a national notice, and its version from 1 to 99.
procedure_idThe eForms procedure identifier, which every notice of one procedure shares.
publication_numberThe TED publication number, such as 670716-2026.
form_type, notice_type, notice_subtypeThe eForms codes. A form or notice type that disagrees with the subtype is dropped.
published_on, tender_deadlineA day, written 2026-11-14 or 14.11.2026.
buyer_name, buyer_identifier, buyer_countryThe contracting authority, its stated identifier and its three letter country code.
contract_nature, procedure_typeThe eForms codes, such as works or open.
cpvOne or more CPV codes of eight digits, the check digit allowed, separated by semicolons or spaces. The first is the main code.
place_of_performanceNUTS codes or three letter country codes.
estimated_value, estimated_currencyDigits with at most two decimals and no thousands separator, and a three letter ISO currency. An amount of 1 or less is a placeholder and is not stored.
descriptionThe description of the notice.
text_langThe language of title and description as a three letter code, such as deu.
source_urlThe notice link the file states, https only.
lotMarks a lot row. A lot row is skipped, because the notice is read from the row of its procedure.

A value that does not fit its field is dropped alone and counted. It never takes the row with it.

Contact data

No field holds a contact person, a phone number or an email address. A title, description or buyer name that carries an email address, a phone number in international form or written as 0711 123456, 0711/123456 or 0711-123456, or a contact label such as Telefon, Fax, Ansprechpartner or Kontakt is withheld whole and counted. A buyer that an eForms notice marks as a natural person keeps only its country.

What each row answers

OutcomeMeaning
newYour team has no notice with this reference from this portal yet. Confirming stores it.
updateStored before with other content. Confirming replaces it.
unchangedStored before with the same content. Nothing changes.
duplicateThe same reference appears on an earlier line, and the first line is kept.
skippedA lot row.
invalidThe row has no reference or no title, or more cells than the header has columns.

counters names every dropped or withheld value and every refused row, such as estimated_value_placeholder or contact_data_withheld, with its count.

Preview first, then confirm

The preview writes nothing. When you confirm, the upload runs again against the notices your team has stored, stores new and changed rows and records one audit entry with the counts. Every stored notice keeps its source, which is the profile it was read as, the portal you named, the file and its line, who uploaded it and when, and the notice link when the file states one. Your team's tender profiles then judge the stored rows like any notice, so an uploaded notice can become a tender match; see Tender profiles and matches.

Over the API and MCP

  • POST /v1/tenders/portals/upload previews an upload. Send the file name in file_name, the portal it was exported from in source_portal, the file base64 encoded in content_base64 and, if you like, mapping, an object from column name to field. dry_run is always true on /v1, so nothing is written, and an upload is confirmed in the dashboard. The rows come in pages in file order, chosen by limit (default and maximum 100) and offset, while counts, counters and total cover the whole file. A file refused as a whole answers a 422 whose body names the problem.
  • The MCP tool tender_export_preview sends the same request and pages the rows the same way.