Fields and what breaks without them
There is no field here that is merely nice to have. Required fields mean nothing works; recommended fields mean something specific gets worse, and the last column says exactly what.
required
Without it the endpoint is unusable and we reject the response.
recommended
Everything keeps running, and something measurable gets worse. This is the silent kind of damage.
optional
Send it if you have it. Nothing depends on it today.
Active offers snapshot OffersSnapshot
JSON Schema · endpoint reference
| Field | Type | Level | What it does | When missing |
|---|---|---|---|---|
| items[].external_id | string | required | Stable id of the offer. Never reused for a different offer. | Response is rejected. |
| items[].name | string | recommended | Offer title, so the listing renders without a detail call. | One extra detail request per offer just to show a name. |
| items[].company.name | string | recommended | Company name for the same reason. | Same as above. |
Offer detail Offer
JSON Schema · endpoint reference
| Field | Type | Level | What it does | When missing |
|---|---|---|---|---|
| external_id | string | required | Same id as in the snapshot. | Response is rejected. |
| version | string | required | Opaque content fingerprint. Changes whenever the offer content changes. | Response is rejected. |
| name | string | required | Offer title. Becomes the headline of the creative. | Response is rejected. |
| description | string | required | Plain text or HTML. Everything the ad copy is written from — the richer, the better the ad. | Response is rejected. |
| status | string published · pending · closed |
required | Only `published` offers get a running campaign; `closed` pauses it within minutes. | Response is rejected. |
| updated_at | string (ISO 8601) | required | When the offer last changed. | Response is rejected. |
| company.name | string | required | Advertiser name. Appears in the creative and prefixes the ad copy. | Response is rejected. |
| company.logo_url | string (URL) | recommended | Publicly reachable HTTPS image. Square or wide, at least 200 px. | Creative is rendered without a logo — it works, it just looks generic. |
| company.introduction | string | optional | Short company blurb; gives the ad copy something to work with beyond the offer itself. | — |
| company.logo_approved | boolean | optional | Set false if the logo is unverified; we then keep it out of rendered creative. | — |
| area_id | string | recommended | Your category id for the offer. Any stable identifier from your own taxonomy. | Every campaign starts immediately instead of giving the offer a few days to fill organically, and the audience has no interest targeting — so it is wider and more expensive. |
| area_root_id | string | recommended | Root of your category tree for the same offer. | Ads run from the default page instead of the one matching the field. |
| locations[].city | string | recommended | City as you write it in the listing. We map it to ad platform ids ourselves. Send the array even when empty — that tells us "remote", not "not implemented". | Targeting falls back to the whole country instead of an 80 km radius around the cities — impressions paid for people who could never commute there. |
| language_code | string (ISO 639-1) | optional | Language of the content. Without it we detect the language from the text. | — |
| salaries[].minimum | integer | recommended | Lower bound of the offered amount, in whole units of the currency. | The creative cannot show an amount. Ads that name a number perform measurably better. |
| salaries[].maximum | integer | optional | Upper bound, when you publish a range. | — |
| salaries[].currency | string (ISO 4217) | recommended | Currency of the amount, e.g. `CZK`. | The amount is dropped from the creative rather than shown without a currency. |
| salaries[].measure | string hour · day · month · year · once |
recommended | Period the amount applies to. | We assume a monthly amount, which reads as nonsense on hourly pay. |
Change list OfferChanges
JSON Schema · endpoint reference
| Field | Type | Level | What it does | When missing |
|---|---|---|---|---|
| items[].external_id | string | required | Which offer changed. | Response is rejected. |
| items[].version | string | required | Its current content fingerprint. | Response is rejected. |
| items[].status | string published · pending · closed |
recommended | Saves a detail call when all we need is the status. | One detail request per changed offer just to learn whether it is still open. |
| items[].updated_at | string (ISO 8601) | recommended | When it changed. | The watermark can only move to the moment of the walk, so a slow walk re-reads offers it already saw. |
| next_page | scalar | null | required | Handed back verbatim as `page`. `null` ends the walk. | Response is rejected. |
Campaign candidates CampaignCandidates
JSON Schema · endpoint reference
| Field | Type | Level | What it does | When missing |
|---|---|---|---|---|
| items[].source_type | string visibility · ppc |
required | Which kind of product produced the candidate. | Response is rejected. |
| items[].external_key | string | required | Id of that product purchase on your side. Opaque to us; a number is fine. | Response is rejected. |
| items[].offer_external_id | string | required | The offer this campaign is for. | Response is rejected. |
| items[].product | string | required | Product key agreed with us — it selects budget, objective and ad account. | Response is rejected. |
| items[].window.since | string (ISO 8601) | required | Campaign may not run before this. | Response is rejected. |
| items[].window.until | string (ISO 8601) | null | recommended | End of the window. May be null for `ppc`, where we derive it from the product configuration. | The campaign runs for the product default length instead of the window you sold. |
| items[].status | string active · cancelled |
required | `cancelled` on the same pair stops the campaign. Never express cancellation by omission. | Response is rejected. |
| items[].updated_at | string (ISO 8601) | required | Drives `updated_since` paging. | Response is rejected. |
| items[].budget_override | integer | null | optional | Budget in minor units (hundredths). Overrides the product default. | — |
| items[].delay_override_days | integer | null | optional | Overrides how long we wait before starting. | — |
| items[].first_campaign_boost | boolean | optional | New client — start immediately, skip the waiting period. Default false. | — |
| next_page | scalar | null | required | Handed back verbatim as `page`. `null` ends the walk. | Response is rejected. |
Response counts ApplicationsStats
JSON Schema · endpoint reference
| Field | Type | Level | What it does | When missing |
|---|---|---|---|---|
| external_id | string | required | Echo of the offer id. | Response is rejected. |
| from | string (ISO 8601) | optional | Echo of the requested lower bound. Handy when debugging, nothing depends on it. | — |
| count | integer | required | Quality responses since `from`. Must be an integer — a numeric string is ignored. | Response is rejected. |
| total_count | integer | recommended | All applications since `from`. Must be greater than or equal to `count`, otherwise we treat it as unknown. | Charts show the quality count alone, with no idea how many applications it came out of. |
Error Error
JSON Schema · endpoint reference
| Field | Type | Level | What it does | When missing |
|---|---|---|---|---|
| error.code | string | required | Short machine-readable code. | Response is rejected. |
| error.message | string | required | Human explanation. Ends up in our operator UI, so write it for a person. | Response is rejected. |
Unknown fields are ignored.
Send whatever else your system has — extra keys never break the parser. That also means we can add fields to this contract without breaking your implementation.