Neytron API · v1
REST API сети распределения лидов. Партнёр передаёт лид в /leads, рекламодатель возвращает статус в /leads/{id}/status. Один формат, один ключ, одна подпись.
https://api.neytron.com/api/v1X-Api-Key-Id + HMAC-SHA256600 req/min/key (default)Эндпоинты
Все пути относительно https://api.neytron.com/api/v1. Колонка scope — требуемая область действия ключа (см. Авторизация). Ключ с областью full подходит везде.
| Метод | Путь | Scope | Назначение |
|---|---|---|---|
| POST | /leads | ingest | Передача нового лида (партнёр → сеть). |
| GET | /leads | read | Список ваших лидов с фильтрами status и limit. |
| GET | /leads/{id} | read | Один лид с PII и custom-полями вашей стороны. |
| PATCH | /leads/{id} | ingest | Частичное обновление custom-полей. null удаляет ключ. |
| POST | /leads/{id}/status | postback | Постбэк статуса от рекламодателя. GET-вариант для legacy-трекеров. |
| POST | /leads/{id}/activity | postback | Одно событие активности (FTD, депозит, churn). Рекламодатель → сеть. |
| POST | /leads/{id}/metrics | postback | Снапшот метрик лида (сумма депозитов, NetRevenue). |
| POST | /postback | postback | Универсальный URL-постбэк: поиск лида по click_id, external_id или sub1-5. |
Дополнительно есть neutral-domain ingest POST /api/v1/in/{slug} и приёмник внешних webhook'ов POST /api/v1/hooks/{slug} — выдаются точечно под интеграцию. Спросите менеджера, если нужно скрыть бренд рекламодателя на стороне партнёра.
Авторизация
API-ключ и scope
Ключ выдаётся в админке (Integration → API keys). Публичный префикс (то, что передаётся как X-Api-Key-Id) зависит от типа организации: byr_ — партнёр, mrcht_ — рекламодатель, ntr_ — интродьюсер. Старые ключи могут начинаться с nk_. Полный ключ при первом показе — секрет; отдельно выдаётся HMAC-секрет для подписи, он не ходит в заголовках.
| Scope | Разрешает |
|---|---|
| ingest | POST /leads, PATCH /leads/{id}, /ping, /post/{pingId}, /in/{slug} |
| postback | /leads/{id}/status, /leads/{id}/activity, /leads/{id}/metrics, /postback |
| read | GET /leads, GET /leads/{id} |
| full | Все эндпоинты. |
Несоответствие области действия возвращает 403 с сообщением «API key scope … does not permit …».
HMAC-подпись (обязательна для write-операций)
Для ingest и postback подпись требуется всегда — без неё сервер возвращает 403 «Missing X-Signature or X-Timestamp». Для read подпись опциональна в legacy-режиме, но рекомендуется.
Заголовки и алгоритм (Stripe-style, разделитель — точка):
X-Api-Key-Id: byr_01234567 # пример партнёрского префикса
X-Timestamp: 1715628000 # Unix-секунды
X-Signature: <hex>
# signature = HMAC-SHA256(secret, timestamp + "." + body), hex
# для GET-запросов без тела используйте пустую строку: timestamp + "." + ""node.jsimport crypto from "node:crypto"; const KEY_ID = "byr_01234567"; const SECRET = process.env.NEYTRON_HMAC_SECRET; const body = JSON.stringify(payload); const ts = Math.floor(Date.now() / 1000).toString(); const signature = crypto .createHmac("sha256", SECRET) .update(ts + "." + body) .digest("hex"); await fetch("https://api.neytron.com/api/v1/leads", { method: "POST", headers: { "x-api-key-id": KEY_ID, "x-timestamp": ts, "x-signature": signature, "content-type": "application/json", }, body, });
Допуск по времени — ±300 секунд. Каждая пара (signature, timestamp) принимается один раз в окне 10 минут; повтор возвращает 409 «Signature already used». Если в момент запроса nonce-store недоступен — 503, повторите тот же запрос через несколько секунд.
Legacy-режим (только для read): заголовок X-Api-Key с полным ключом в открытом виде. Не используйте его для новых интеграций — ключ попадает в логи прокси и CDN.
Приём лидов
POST /leads. Обязательные поля: offer_code (либо offer_id), first_name, last_name, phone.
Опционально: email, country (ISO-3166-1 alpha-2), language, ip, user_agent, referer, landing_url, source, sub1…sub5, external_id, custom (объект кастом-полей вашей стороны; набор задаётся в админке).
Для трафика US/CA TCPA-комплаенс обязателен — присылайте хотя бы один из trusted_form_cert_url, jornaya_lead_id, либо тройку consent_timestamp + consent_text + consent_ip. Антифрод-сигнал: device_fingerprint (hex-хеш сигнатуры устройства, если ваш SDK его считает).
curlcurl -X POST https://api.neytron.com/api/v1/leads \ -H "x-api-key-id: byr_01234567" \ -H "x-timestamp: $TS" \ -H "x-signature: $SIG" \ -H "content-type: application/json" \ -d '{ "offer_code": "FX_EU_TIER1", "first_name": "Ivan", "last_name": "Petrov", "phone": "+4915123456789", "email": "ivan@example.com", "country": "DE", "source": "facebook", "sub1": "campaign-42" }'
Коды ответа
200— лид принят и доставлен рекламодателю синхронно.202— лид принят, доставка асинхронно через webhook-очередь.409— дубликат или контакт в чёрном списке.duplicate_ofсодержит publicId оригинала, только если оригинал ваш (privacy across partners).422— кастомные поля не прошли валидацию (детали вdetail).429— превышен лимит per-key или per-IP. Заголовокretry-afterуказывает паузу в секундах.
response · 200{ "lead_id": "LDABCDEFG123", "status": "routed", // routed | new | trash "antifraud_score": 12, // 0..100 "routed_to": { "offer_code": "FX_EU_TIER1" } }
response · 409{ "lead_id": "LDABCDEFG123", "status": "duplicate", // или "rejected" при blacklist "reason": "duplicate", // duplicate | blacklisted "duplicate_of": "LD3M7X9K2A" // null, если оригинал не ваш }
Постбэк статуса
POST /leads/{id}/status. Шлёт рекламодатель или партнёр (для возврата лида в trash/duplicate). {id} — публичный ID лида (LDABCDEFG123) либо внутренний CUID.
Поле status — строка из вашего трекера (например, approve, 1, sold). Маппинг во внутренние значения (approved, sold, rejected, trash) настраивается в админке → Status mapping. Если маппинга нет — возвращается 400 «Unknown status».
curlcurl -X POST https://api.neytron.com/api/v1/leads/LDABCDEFG123/status \ -H "x-api-key-id: mrcht_01234567" \ -H "x-timestamp: $TS" \ -H "x-signature: $SIG" \ -H "content-type: application/json" \ -d '{ "status": "approve", "payout": 150.00, "external_id": "advertiser-side-id-9871", "reason": null, "idempotency_key": "approve-9871" }'
Принимаемые поля: status, payout (число), external_id, reason, idempotency_key. Поле currency у лида фиксируется при создании из оффера — в постбэке его передавать не нужно.
Trackers без поддержки POST (Voluum, часть Keitaro-пресетов) могут использовать GET с теми же параметрами в query-строке:
legacy GETcurl "https://api.neytron.com/api/v1/leads/LDABCDEFG123/status?status=approve&payout=150" \ -H "x-api-key-id: mrcht_01234567" -H "x-timestamp: $TS" -H "x-signature: $SIG"
Универсальный URL-постбэк
Если ваш трекер не знает publicId лида, шлите на /postback и укажите идентификатор любым способом: click_id, lead_id, external_id или sub1…sub5. Сервер найдёт лид по совпадению.
Активность и метрики (для рекламодателей)
Чтобы не путать «статус сделки» и «жизненный цикл клиента», активность и метрики выделены в отдельные эндпоинты:
POST /leads/{id}/activity— одно событие:{ kind, amount, currency, occurred_at, meta }. Принимается массивом до 200 событий за запрос.POST /leads/{id}/metrics— снапшот:total_deposits_amount,total_deposits_count,ftd_amount,first_deposit_at,net_revenueи т.д. Перезаписывает текущий снапшот.
Webhook'и (от сети к вам)
Подпишитесь на события в админке (Integration → Outbound). Список доступных:
lead_created— лид создан в сети.lead_routed— лид доставлен рекламодателю.lead_status_changed— статус изменился (любая смена).lead_approved— рекламодатель подтвердил.lead_rejected— рекламодатель отклонил.lead_activity— пришло событие активности (FTD, депозит и т.п.).lead_metrics_updated— обновился снапшот метрик.domain_status_changed— статус routing-домена изменился (для партнёров, использующих neutral-domain ingest).partner_application_received— новая заявка в сеть (внутренний сценарий; внешним партнёрам обычно не подключается).
Тело JSON (format=json)
Если в endpoint не задан собственный payloadTemplate, тело — плоский объект с полями ниже. Поле event совпадает с именем события подписки. Для lead_activity и lead_metrics_updated в корень добавляются вложенные объекты activity / metrics (см. outbound-воркер ветки событий).
Для партнёра с фиксированной выплатой (CPA и т.п.) суммы в activity/metrics могут обнуляться — правилоpartnerCanSeeActivityAmounts, чтобы не утекала маржа рекламодателя.
json (базовые поля){ "event": "lead_routed", "lead_id": "LDABCDEFG123", "click_id": "<externalId или sub1>", "first_name": "…", "last_name": "…", "email": "…", "phone": "…", "country": "DE", "geo": "DE", "source": "facebook", "sub1": "…", "sub2": "…", "sub3": "…", "sub4": "…", "sub5": "…", "status": "routed", "price": 0, "payout": 0, "currency": "EUR", "offer": "FX_EU_TIER1", "offer_code": "FX_EU_TIER1", "offer_id": "<cuid>", "offer_name": "…", "campaign_id": "<cuid|null>", "campaign_name": "…", "merchant": "<slug>", "advertiser": "<slug>", "advertiser_id": "<cuid>", "buyer": "<slug|null>", "partner": "<slug|null>", "partner_id": "<cuid|null>", "external_id": "…", "buyer_external_id": "…", "fingerprint": "…", "hold_until": null, "referral_code": null, "custom": { } }
На каждый HTTP-запрос к вашему URL добавляется заголовок X-Neytron-Delivery-Id (id строки доставки в нашей БД) — удобно сопоставлять повтор при ретрае с тем же Idempotency-Key.
Подпись и заголовки
Подпись считается тем же алгоритмом, что и входящие запросы — точка как разделитель:
signature = HMAC-SHA256(webhook_secret, timestamp + "." + body), hexВ каждой доставке присутствуют заголовки:
X-Signature: <hex>
X-Timestamp: <unix-сек>
Idempotency-Key: <stable per (endpoint, lead, event)>
Content-Type: application/json # или x-www-form-urlencoded, если выбран form-форматIdempotency-Key стабилен для тройки (endpointId, leadId, event) — смело используйте для дедупликации на своей стороне.
Retry-расписание
1с → 5с → 30с → 5м → 1ч. После max_retries попыток (по умолчанию 5, настраивается до 20) доставка помечается как dead — в админке видны raw payload, статус, последняя ошибка.
Коды ошибок
Тело ошибки одинаковое для всех эндпоинтов: { "error": "<message>", "detail": <issues>? }. Для 422 в detail приходит результат ZodError.flatten() — fieldErrors по каждому проблемному полю.
| HTTP | Причина | Что проверить |
|---|---|---|
| 400 | Invalid JSON, invalid query или unknown status mapping | Сверьте payload со схемой; для постбэка — настройте mapping |
| 401 | Ключ не передан или невалиден | Проверьте X-Api-Key-Id и статус ключа в админке |
| 403 | Wrong scope · IP not in whitelist · Signature mismatch · Timestamp drift | Поле error детализирует причину. Часто — забыли подпись или склейка не та (timestamp + «.» + body). |
| 404 | Лид не существует или принадлежит другой стороне | Намеренно не различаем «нет лида» и «не ваш» — privacy across partners |
| 409 | Duplicate · Blacklisted · Signature replay detected | Поле reason в теле показывает, что именно |
| 413 | Тело запроса больше 64 KB | Срежьте custom-поля; raw HTML лендинга шлите отдельно |
| 422 | Custom-поля не прошли валидацию | detail.fieldErrors — что именно |
| 429 | Превышен rate-limit | Соблюдайте retry-after; x-ratelimit-remaining — ваш budget на минуту |
| 503 | Anti-replay store временно недоступен | Повторите тот же запрос (с тем же timestamp и подписью — fail-closed для write) |
Идемпотентность
Постбэк (/leads/{id}/status) и активность принимают ключ идемпотентности — либо в теле как idempotency_key, либо в заголовке Idempotency-Key. Сервер сохраняет результат первого запроса и возвращает его без побочного эффекта при повторах.
Дополнительно от replay-атак защищает nonce: каждая пара (signature, timestamp) принимается один раз за 10 минут. Повтор → 409 независимо от idempotency_key. Если хотите гарантированный повтор — сгенерируйте новый timestamp и новую подпись.
Reference
Полный список эндпоинтов, схемы запросов и ответов, try-it-out — ниже. Спецификация — OpenAPI 3.1, JSON: /api/v1/docs?format=json. В поле servers первым идёт NEXT_PUBLIC_API_V1_BASE_URL (или prod default); при открытии страницы с localhost вторым добавляется URL этого же хоста — чтобы try-it-out бил локальный Next.