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,.xlsxor.xmland must match the content. The old binary.xlsformat is not read, so save such a file as.xlsxfirst. - 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 |
|---|---|
dtvp | Deutsches Vergabeportal (DTVP), refused for now |
vergabe24 | Vergabe24, refused for now |
evergabe_bund | e-Vergabe des Bundes |
vergabe_nrw | Vergabe.NRW |
rib_bayern | vergabe.bayern (RIB Tender) |
subreport | subreport |
cosinex_laender | State procurement marketplaces (cosinex) |
dab | Deutsches Ausschreibungsblatt, refused for now |
dtad | DTAD Deutscher Auftragsdienst, refused for now |
had | HAD Hessische Ausschreibungsdatenbank, refused for now |
vergabe_saarland | vergabe.saarland, refused for now |
oeffentlichevergabe_de | Bekanntmachungsservice (oeffentlichevergabe.de) |
other_public | Another public procurement portal |
manual | A 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.
| Profile | Header |
|---|---|
template | The Stonewake template, whose columns are the field names below. It needs reference and title. |
oeffentlichevergabe_de_purpose | purpose.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
| Field | Content |
|---|---|
reference | The row's identity in its portal, such as the portal's notice number. Without it the row needs notice_id and notice_version. |
title | The title of the notice. Required. |
notice_id, notice_version | The notice identifier as eForms states it, or the numeric id of a national notice, and its version from 1 to 99. |
procedure_id | The eForms procedure identifier, which every notice of one procedure shares. |
publication_number | The TED publication number, such as 670716-2026. |
form_type, notice_type, notice_subtype | The eForms codes. A form or notice type that disagrees with the subtype is dropped. |
published_on, tender_deadline | A day, written 2026-11-14 or 14.11.2026. |
buyer_name, buyer_identifier, buyer_country | The contracting authority, its stated identifier and its three letter country code. |
contract_nature, procedure_type | The eForms codes, such as works or open. |
cpv | One or more CPV codes of eight digits, the check digit allowed, separated by semicolons or spaces. The first is the main code. |
place_of_performance | NUTS codes or three letter country codes. |
estimated_value, estimated_currency | Digits 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. |
description | The description of the notice. |
text_lang | The language of title and description as a three letter code, such as deu. |
source_url | The notice link the file states, https only. |
lot | Marks 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
| Outcome | Meaning |
|---|---|
new | Your team has no notice with this reference from this portal yet. Confirming stores it. |
update | Stored before with other content. Confirming replaces it. |
unchanged | Stored before with the same content. Nothing changes. |
duplicate | The same reference appears on an earlier line, and the first line is kept. |
skipped | A lot row. |
invalid | The 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/uploadpreviews an upload. Send the file name infile_name, the portal it was exported from insource_portal, the file base64 encoded incontent_base64and, if you like,mapping, an object from column name to field.dry_runis 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 bylimit(default and maximum 100) andoffset, whilecounts,countersandtotalcover the whole file. A file refused as a whole answers a 422 whose body names the problem.- The MCP tool
tender_export_previewsends the same request and pages the rows the same way.