API
Send your price list to ADAM
One HTTP call replaces the spreadsheet. Your system posts the feed, we tell you row by row what we read and what we could not.
Validate a payload without creating anything in Settings → Integrations → Sandbox. OpenAPI document
Getting a key
Settings → Integrations → Connect. The secret is shown once at creation and stored only as a hash, so keep it where your ERP can read it. Send it as Authorization: Bearer ….
curl -X POST https://168-119-161-237.sslip.io/v1/products \
-H "Authorization: Bearer adam_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"items":[{"sku":"KRB-46","name":"Карбамід 46% гранульований","brand":"Sumykhimprom","category":"Добрива","unit":"кг","price":12.5,"stock":1000,"warehouse":"Київський склад","moq":100,"updated_at":"2026-08-24T14:02:31Z"}]}'The item, field by field
| Field | Type | Rule |
|---|---|---|
| name | string · ≤300 required | What the product is called in your system. Trimmed, non-empty. |
| brand | string · ≤120 required | Resolved within the category by the matcher. |
| category | string · ≤120 required | Human form. Resolved against the slugs and aliases listed below. |
| unit | string · ≤40 required | Human form: «шт», «шт.», «кг», «т», «каністра» all resolve. |
| price | number required | Greater than 0. A number, not a string; `.` as the decimal separator. |
| stock | number required | 0 or more. Zero means out of stock, not unknown. |
| sku | string · ≤100 optional | Your own article number. Optional by schema, but without it a later PATCH has nothing to resolve against. |
| warehouse | string · ≤200 optional | Matched against your own addresses. Ambiguity is a warning, not a rejection. |
| moq | number optional | Greater than 0. Defaults to 1. |
| description | string · ≤500 optional | Free text. Used when a row becomes a request for a new catalogue product. |
| updated_at | rfc3339 optional | When the item last changed in your system. Enables the ordering rule on PATCH. |
A field we do not read fails the whole request instead of being dropped. Dropping it quietly is how you come to believe you set a currency you did not set.
The response
202, because rows are accepted for processing with matching and review still ahead. Rejections name your own item index, so a feed you fix one pass at a time converges.
{
"request_id": "8f14e45f-ceea-467a-9c4c-1b0f0e5c9e2a",
"job_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accepted": 1198,
"rejected": [
{
"index": 42,
"sku": "XYZ-1",
"reason": "category_unresolved",
"detail": "«Хімія» matches no category"
},
{
"index": 87,
"sku": "KRB-99",
"reason": "required_field_missing",
"field": "brand"
}
],
"rejected_total": 2
}Reason codes
| Code | Scope | Meaning |
|---|---|---|
| invalid_envelope | whole request | The body is not an object with an items array. |
| unknown_field | whole request | A field we do not read. Sent back rather than dropped, so you never believe you set something you did not. |
| idempotency_key_reused | whole request | This Idempotency-Key was already used for a request with a different body. Use a new key — the stored result belongs to the earlier request, not this one. |
| no_items | whole request | items is empty. |
| too_many_items | whole request | More items than the per-request limit. Send fewer. |
| required_field_missing | one row | Carries |
| field_too_long | one row | Carries |
| price_invalid | one row | Not a number, or not greater than 0. |
| stock_invalid | one row | Not a number, or negative. Zero is valid. |
| moq_invalid | one row | Not a number, or not greater than 0. |
| sku_duplicate_in_payload | one row | The same SKU appears earlier in this request. The first occurrence wins. |
| updated_at_invalid | one row | Not RFC 3339, for example 2026-08-24T14:02:31Z. |
| review_backlog | whole request | Your unreviewed rows are at the limit. Finish reviewing before sending more. |
| category_unresolved | one row | The value matches no slug and no alias. |
| unit_unresolved | one row | The value matches no unit. |
| wrong_endpoint | one row | You sent |
A closed set, so your integration can branch on them. One code per rejected row: a row failing several checks reports the first in the order above.
Updating price and stock
Once a position is linked, PATCH /v1/offers moves its price and stock with no review and no human step. You send the article number you already sent to the product feed; we change the offer behind it. This is the call your ERP runs on a schedule.
curl -X PATCH https://168-119-161-237.sslip.io/v1/offers \
-H "Authorization: Bearer adam_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"items":[{"sku":"KRB-46","price":12.9,"stock":840,"updated_at":"2026-08-25T09:00:00Z"},{"sku":"KAS-32","stock":0},{"sku":"OLD-01","discontinued":true}]}'| Field | Type | Rule |
|---|---|---|
| sku | string · ≤100 required | Your own article number, as sent to POST /v1/products. The only way to name the position. |
| price | number optional | Greater than 0. Omit it and the price stays as it is. |
| stock | number optional | 0 or more. Zero sets the offer to out of stock and leaves it listed. |
| moq | number optional | Greater than 0. The minimum order quantity; the unit is not changed here. |
| discontinued | boolean optional | true withdraws the offer, false puts it back. Withdrawal always succeeds; relisting can be refused. |
| updated_at | rfc3339 optional | When the position last changed in your system. Send it and an out-of-order retry cannot overwrite a newer price; omit it and the last request to arrive wins. Equal to the stored one with the same values is a no-op; equal with different values is equal_timestamp_conflict. |
An item needs its sku plus at least one of price, stock, moq, discontinued. Everything you leave out stays as it is, so a stock-only sweep never touches a price. sku on its own is rejected rather than counted as applied.
Send updated_at and retries stop being dangerous: an item whose timestamp is older than the last one we applied changes nothing and comes back as stale_update. Omit it and the last request to arrive wins, which is fine for a single scheduled job and wrong for two.
unchanged counts items whose offer already held the values you sent. Nothing was written and nothing is wrong — a nightly feed that resends everything gets applied for what moved and unchanged for the rest, instead of thousands of errors. Sending the same updated_at with the same values lands here too; sending it with different values is equal_timestamp_conflict, because equal timestamps cannot say which version is current.
Withdrawal and relisting are not symmetric, and it is better you know why than discover it. discontinued: true always succeeds — taking your own offer down needs nobody's permission. discontinued: false puts it back only if the offer is complete and your account may publish; otherwise the row comes back as relist_blocked with the reason in detail.
not_published is the number of applied rows sitting on an offer a farmer cannot see — a draft, a paused offer. The price did change; nobody is looking at it. A patch never publishes on your behalf, so this count is how you find out rather than wondering why nothing moved.
{
"request_id": "1c9d4b7e-3f60-4a21-8de5-7a2b6c0f9e11",
"applied": 1198,
"unchanged": 613,
"not_published": 42,
"rejected": [
{
"index": 17,
"sku": "ZIR-20",
"reason": "sku_ambiguous",
"detail": "7 of your offers carry this code: Зірочка Z-20, Зірочка Z-22, …"
},
{
"index": 63,
"sku": "KRB-46",
"reason": "stale_update",
"detail": "last applied 2026-08-25T09:00:00+00"
}
],
"rejected_total": 2
}| Code | Scope | Meaning |
|---|---|---|
| no_change_requested | one row | sku and nothing changeable. A timestamp alone is not a change — send price, stock, moq or discontinued. |
| discontinued_invalid | one row | Present but not a boolean. |
| sku_unknown | one row | No live mapping for this code. Send the position through POST /v1/products once; after it is confirmed, PATCH works forever. |
| sku_ambiguous | one row | More than one of your offers carries this code, so we cannot tell which price to move. |
| offer_missing | one row | The mapping exists but its offer is gone. Send the position through POST /v1/products again. |
| stale_update | one row | Your updated_at is older than the last one we applied, so nothing changed. |
| equal_timestamp_conflict | one row | Your updated_at equals the one we already applied, but the values differ — so we cannot tell which is current and refuse rather than guess. Advance updated_at. If the values match, the item is counted in |
| relist_blocked | one row |
|
| price_tiered | one row | This offer has a quantity price ladder, and a single flat price cannot replace it. Remove the ladder in the dashboard, then the API manages this position. |
Only the codes this endpoint alone returns. Every request-level code and every value check in the table above applies here too, with field naming sku where the product feed would name a dozen fields.
Categories we recognise
Read live from the catalogue, so this list cannot drift from what the resolver accepts. Case and trailing punctuation do not matter.
- seedsSeeds
Hybrids · Seed · seeds · Sowing material · Гибриды · Гібриди · Кукурудза · Кукуруза · Насіннєвий матеріал · Насіння · Насіння кукурудзи · Насіння рапсу · Насіння сої · Насіння соняшника · Посевной материал · Посівний матеріал · Пшеница · Пшениця · Рапс · Семена · Семена кукурузы · Семена подсолнечника · Семена подсолнуха · Семена рапса · Семена сои · Семенной материал · Соняшник · Соя · Ячмень · Ячмінь
- fertilizersFertilizers
Fertiliser · Fertilizer · fertilizers · Growth stimulants · Micro fertilizers · Агрохимия · Агрохімія · Добрива · Микроудобрения · Микроудобрения и стимуляторы роста · Минеральные удобрения · Мікродобрива · Мікродобрива та стимулятори росту · Мінеральні добрива · Органические удобрения · Органічні добрива · Стимулятори росту · Стимуляторы роста · Удобрения
- pesticidesCrop Protection
Agrochemicals · CPP · Crop Protection · Fungicide · Fungicides · Herbicide · Herbicides · Insecticide · Insecticides · Pesticide · pesticides · Plant protection · Агрохимикаты · Агрохімікати · Адъюванты · Адьюванти · Акарициди · Акарициды · Гербицид · Гербициды · Гербіцид · Гербіциди · Десиканти · Десиканты · Засоби захисту · Засоби захисту рослин · ЗЗР · Инсектицид · Инсектициды · Інсектицид · Інсектициди · Пестициди · Пестициди та агрохімікати · Пестициды · Пестициды и агрохимикаты · Прилипатели · Прилипачі · Прилипачі (пав) · Протравители · Протруйник · Протруйники · Родентициди · Родентициды · СЗР · Средства защиты · Средства защиты растений · Фунгицид · Фунгициды · Фунгіцид · Фунгіциди
- fuel-lubricantsFuel & Lubricants
Diesel · Fuel · fuel-lubricants · Grease · Lubricants · Oils · Антифриз · Бензин · Горюче-смазочные материалы · ГСМ · Дизельне пальне · Дизельное топливо · ДТ · Масла · Мастила · Пальне · Пально-мастильні матеріали · ПММ · Смазки · Топливо
- machineryMachinery
Combines · Equipment · Implements · machinery · Tractors · Комбайни · Комбайны · Обладнання · Оборудование · Прицепы · Причепи · Сельхозтехника · Сільгосптехніка · Техника · Техніка · Трактори · Тракторы
- spare-partsSpare Parts
Bearings · Belts · Filters · Parts · spare-parts · Детали · Деталі · Запчасти · Запчастини · Ножи · Ножі · Підшипники · Подшипники · Ремені · Ремни · Фильтры · Фільтри
- tires-wheelsTires & Wheels
Rims · Tires · tires-wheels · Tyres · Wheels · Диски · Колеса · Колёса · Покришки · Покрышки · Шини · Шини та диски · Шины · Шины и диски
- suppliesSupplies
supplies · Витратні матеріали · Расходные материалы · Спецодежда · Спецодяг · Тара · Упаковка
- servicesServices
services · Послуги · Сервис · Сервіс · Услуги
- livestockLivestock
livestock · Ветпрепарати · Ветпрепараты · Животноводство · Корма · Корми · Тваринництво
- energy-systemsEnergy Systems
energy-systems · Генератори · Генераторы · Енергосистеми · Солнечные панели · Сонячні панелі · Энергосистемы
Units we recognise
- piece
шт · штука · штук · штуки · од · одиниця · pcs · pc · piece · unit
- kg
кг · кілограм · килограмм · кілограмів · kg
- ton
т · t · тонна · тонн · тонни · ton · tonne
- liter
л · l · літр · литр · літрів · liter · litre
- bag
мішок · мешок · мішків · bag
- pack
уп · пак · пачка · упаковка · паковання · pack
- canister
каністра · канистра · кан · canister
- bottle
пляшка · бутылка · флакон · bottle
- set
набір · набор · set
- hectare
га · гектар · гектарів · ha · hectare
Limits and idempotency
- POST /v1/products — 5000 × 10 min · 30 req / 10 min
- PATCH /v1/offers — 5000 × 60 s · 30 req / 60 s
- 20000 unresolved rows · 10 open jobs
Send Idempotency-Key on every real call. A repeat with the same key returns the stored result — the same job, the same rejections — and creates nothing, so a timeout on your side never becomes a second price list. A replay carries the first 20 rejections and rejected_total; page the rest from the rejections endpoint.
What happens next
Accepted rows are matched against our catalogue and land in your review screen. A position we recognise is confirmed there; one we do not becomes a request for a new catalogue product. Review is a human step and it is not instant — plan your first feed with that in mind. It is also a one-off: once a position is linked, its price and stock travel through PATCH /v1/offers and never queue for review again.
Not supported yet
currency, vat_rate and lead_time_days are not read on either endpoint, so sending them is an error rather than a silent drop. Quantity price ladders are not editable over the API either — an offer that has one refuses a flat price as price_tiered. Letting us pull the feed from your own URL on a schedule is the next phase.