API documentation

For agencies and organisations with their own software: send requests directly via the API instead of the form. The same rules apply as in the form; vetted printers and lettershops submit their quotes as usual.

Getting started

  1. Log in or create an account, then generate a key under API access.
  2. Send the key in the Authorization header with every call.
  3. Create a request. It belongs to your account straight away and also shows up under "My requests".
Base URL
https://mailingbid.com/api/v1
Authentication
Authorization: Bearer mb_live_…
Format
JSON (UTF-8), dates as YYYY-MM-DD
Limits
120 calls per minute per key; 60 new requests per hour per account

Treat the key like a password: server-side only, never in a browser or an app. You can revoke a key at any time.

Endpoints

MethodPathPurpose
POST/api/v1/requestsCreate a request
GET/api/v1/requestsAll requests of your account
GET/api/v1/requests/{id}One request with its parts, countries and published offers
PATCH/api/v1/requests/{id}Add or change a request's details
GET/api/v1/catalogueAccepted values (parts, formats, countries); no key needed

Create a request

At least one country with a quantity (tranches) and at least one part (positions) are required. Everything else is optional and can be added later.

Without them, the same defaults apply as in the form: a title from the posting date, an offer deadline in 5 working days, and production, lettershop and posting as services. Contact details and language come from your account. The response is 201 with the stored request.

Requests sent through the API are subject to our terms.

curl -X POST https://mailingbid.com/api/v1/requests \
  -H "Authorization: Bearer $MAILINGBID_API_KEY" \
  -H "Content-Type: application/json" \
  --data @anfrage.json

Example: anfrage.json

{
  "title": "Spring mailing 2027",
  "defaultPostingDate": "2027-03-12",
  "tranches": [
    {
      "country": "AT",
      "quantity": 30000
    },
    {
      "country": "CZ",
      "quantity": 10000,
      "postProduct": "standard"
    }
  ],
  "positions": [
    {
      "type": "envelope",
      "format": "C6/5",
      "paperGsm": 80,
      "spec": {
        "window": "left"
      }
    },
    {
      "type": "letter",
      "format": "A4",
      "colors": "4/4",
      "spec": {
        "fold": "wrap_dl",
        "personalization": "address"
      }
    },
    {
      "type": "payment_slip",
      "spec": {
        "standards": {
          "AT": "at_sepa",
          "CZ": "cz_pk_a"
        },
        "execution": "attached"
      }
    }
  ],
  "serviceScope": [
    "production",
    "lettershop",
    "posting"
  ],
  "contractingParty": "agency",
  "onBehalfOf": "Organisation A",
  "iso27001Required": false
}

Response (shortened)

{
  "request": {
    "id": 123,
    "status": "received",
    "title": "…",
    "offerDeadline": "2027-01-15",
    "serviceScope": ["production", "lettershop", "posting"],
    "url": "https://mailingbid.com/anfragen/123",
    …
  },
  "tranches": [{ "id": 401, "country": "AT", "quantity": 30000, … }],
  "positions": [{ "id": 901, "type": "envelope", "format": "C6/5", … }],
  "offers": [],
  "files": [],
  "decision": null,
  "details": { "editable": true, "lock": null }
}

Request fields

FieldRequiredDescription
tranchesyesList of countries with quantities, 1 to 50 entries.
positionsyesList of parts, 1 to 30 entries, at least one not supplied by you.
titlenoYour own title, 3 to 200 characters.
defaultPostingDatenoShared posting date.
offerDeadlinenoOffers due by; at least 3 working days from today.
serviceScopenoServices wanted: production, lettershop, posting.
postingAtnoCountry of posting, if not always the destination country.
contractingPartynoContracting party: ngo, agency or company.
onBehalfOfnoRequired for agency: who you are requesting for. Suppliers only see it once the job is awarded.
paymentTermsnoPayment terms you want, as free text.
iso27001Requirednotrue: only ask ISO 27001 certified suppliers.
notesnoNotes on the job, up to 4,000 characters. Please no addresses or donor data.
contactNamenoContact person; default: your account's name.
contactEmailnoContact person's email; default: your account's email. Confirmations always go to your account.
contactOrganizationnoOrganisation; default: from your account.
contactPhonenoPhone; default: from your account.
localenoLanguage of the emails: de, cs or en; default: your account's language.

Countries and quantities (tranches)

FieldDescription
countryDestination country, two-letter ISO code (required).
quantityQuantity (required).
postingDateOwn posting date, otherwise the shared one.
graphicVariantsNumber of design variants, default 1.
postProductPostal product; empty: the supplier suggests one.
codeYour own reference, unique within the request; suppliers don't see it.
returnAddressReturn address for undeliverables; suppliers only see it once the job is awarded.

Parts of the mailing (positions)

FieldDescription
typeType of part (required), see values below.
labelYour own label, e.g. "blue pen".
formatFormat from the type's list; custom for your own size.
formatMmOnly with custom: width×height in mm, e.g. 210x297.
paperGsmPaper weight in gsm within the type's range.
colorsColours, e.g. 4/4; special for a spot colour.
piecesPerMailingPieces per mailing, default 1.
suppliedByCustomertrue: you supply this part yourself.
trancheScopeOnly for certain countries: indexes into tranches (from 0); without it the part applies to all.
detailsFurther details, up to 2,000 characters.
specDetails per type, see values below. Unknown keys are rejected.

Payment slips have no format, paper or colours: these follow from the country's standard. The server rejects such values.

Retrieve requests and offers

GET /api/v1/requests returns all requests of your account with their status and number of published offers. GET /api/v1/requests/ID returns one request in full.

curl https://mailingbid.com/api/v1/requests \
  -H "Authorization: Bearer $MAILINGBID_API_KEY"

Offers appear once we have published them: with supplier, covered parts and countries, the execution per part (positionConformity: as requested or deviating, with a description), price lines by type (total price excluding postage or price per part, postage separate), the postage statement (postageStatement: separate or not_offered), total, validity, price adjustment clause, payment terms, what's included and remarks. For older offers positionConformity and postageStatement are null. Amounts are in cents; EUR and CZK are never added together. Several offers from one provider are variants: variantLabel is the name of the alternative (null for the first offer), variantGroup is the same number for all offers of one provider (null for offers we entered ourselves). Only one variant per provider can be chosen in a selection.

Status of a request:

  • received – received
  • in_progress – in progress, offers are being gathered
  • offers_available – offers available
  • closed – decided

Choosing offers is binding, so it is done in the browser at the request's URL (field url).

Add details

PATCH /api/v1/requests/ID takes the same fields as creating a request, all optional: a missing field stays as it is; null clears it.

curl -X PATCH https://mailingbid.com/api/v1/requests/123 \
  -H "Authorization: Bearer $MAILINGBID_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{ "paymentTerms": "30", "notes": "…" }'

tranches and positions are replaced as a whole and always sent together. Existing entries keep their id: send the id to change an entry; without an id a new one is created, and missing ones are deleted.

Changes are possible while the request is received or in progress and no offer has been published; after that the server answers 409 details_locked.

Accepted values

GET /api/v1/catalogue returns the same values as JSON.

typeformatpaperGsmspec
envelope · EnvelopeDL, C6/5, C6, C5, C4, B4, custom70–120window: none | left | rightclosure: wet | peel | selfinnerPrint: booleanspecialColor: text
letter · LetterA4, A5, custom40–400pages: one_sided | two_sided | two_sheets | four_pagesfold: wrap_dl | single_a5 | none | crosspersonalization: none | address | variable_text | variable_text_imagespecialColor: text
payment_slip · Payment slip––standards: object: country -> standardexecution: attached | separate | qr_onlyprefill: reference | donor | amountqrCode: boolean
postcard · Postcard / reply cardA6, DL, MAXI, custom40–400replyMail: booleanfinishing: none | dispersion | uv | cello_matt | cello_glosspersonalization: none | senderspecialColor: text
certificate · CertificateA4, A5, custom40–400finishing: none | embossing | hot_foilpersonalizedName: booleanspecialColor: text
tax_receipt · Donation receiptA4, A540–400specialColor: text
insert · Insert / flyerDL, A6, A5, custom40–400fold: none | single | wrap | zigzagspecialColor: text
promo_item · Promotional item––article: textlengthMm: numberwidthMm: numberheightMm: numberweightG: numbermaterial: textbranding: none | print_1c | print_4c | engravingpackaging: loose | polybag | gluedsourcing: supplied | sourced
other · Other–––
colors
4/0, 4/4, 1/0, 1/1, special
serviceScope
production, lettershop, posting
contractingParty
ngo, agency, company
postProduct
standard, sponsoring_mail (AT), info_mail, euro_mail, postaim (IE), charity_acquisition_post (IE), other
spec.standards
at_sepa (AT), at_donation (AT), de_donation (DE), de_sepa (DE), cz_pk_a (CZ), sk_poukaz (SK), qr_only
country
AT, CZ, SK, DE, PL, BE, BG, HR, CY, DK, EE, FI, FR, GR, HU, IE, IT, LV, LT, LU, MT, NL, PT, RO, SI, ES, SE, IS, LI, NO, CH

Errors

Errors come with a matching HTTP status and a stable code in data.code. For validation_error, data.issues names the field that doesn't fit.

{
  "statusCode": 400,
  "statusMessage": "…",
  "data": {
    "code": "validation_error",
    "issues": { "fieldErrors": { "tranches": ["…"] } }
  }
}
data.codeDescription
validation_errorA field is missing or has an invalid value (400).
offer_deadline_too_earlyThe offer deadline is less than 3 working days away; data.earliest gives the earliest date (400).
api_key_invalidThe key is missing, wrong or revoked (401).
forbiddenThe request doesn't exist or doesn't belong to your account (403).
details_lockedThe request can no longer be changed (409).
invalid_details_referenceAn id doesn't belong to this request (400).
body_too_largeThe JSON body is larger than 1 MB; data.maxBytes gives the limit (413).
rate_limitedToo many calls; please try again later (429).

Good to know

  • Upload files (e.g. sample artwork) on the request's details page in the browser.
  • You make the binding choice of offer in the browser.
  • The confirmation of each request goes to your account's email address, even if you give a different contact address.
  • Until the job is awarded, suppliers see neither your name nor your organisation or contact details.

Rather not code? Use the request form.