То, что разработчики спрашивают до и во время подключения: ключи, ссылки, как решение попадает на ваш сайт, вебхуки, журнал, лимиты и ошибки. Все методы и цифры здесь те, с которыми API работает сейчас.
Ключ показывается один раз, сразу после создания, поэтому скопируйте его сразу: мы храним только его хэш. У аккаунта может быть до 10 действующих ключей. Ненужный ключ отзывается на той же странице и сразу перестаёт работать.
API находится по адресу https://api.consent.link/v3 и работает с JSON. Передавайте ключ в каждом запросе в заголовке Authorization: Bearer clk_live_… (заголовок X-AUTH-TOKEN из старого API v2 тоже принимается). Запрос без действующего ключа получает 401 unauthenticated.
Вызывайте API только со своего сервера. Тот, у кого ключ, может создавать ссылки и читать ваш журнал с полными данными посетителей.
Отправьте POST /v3/links с адресом назначения. Ответ 201 со ссылкой: short это адрес для рекламы, постов и рассылок, id это её код. Обязателен только destination.
Ссылки создаются только через API, в кабинете их не создать: каждый адрес проверяется при создании ссылки, это защищает от фишинга.
destination: куда посетитель попадёт после решения. Обязательно.
type: подпись на странице согласия, одно из ad, sponsored, affiliate, none. Без него берётся подпись из «Оформление».
template: текст страницы, ID шаблона из нашей библиотеки (таблица ниже). Без него берётся шаблон из «Оформление».
name: ваше название ссылки, до 255 символов, видно в кабинете.
external_id: ваш собственный ID, например поста или кампании, до 255 символов. По нему можно искать ссылки; изменить его потом нельзя.
metadata: ваш JSON-объект, хранится и возвращается как есть.
В ответе также есть resolved_destination (для трекера или короткой ссылки, см. ниже), status (active, disabled или blocked), mode (live или test) и created_at, а warnings — когда есть о чём предупредить.
На абсолютный адрес http или https с публичным доменным именем, до 2 048 символов: без IP-адресов, без имени пользователя и пароля, без пробелов и обратных слешей. Мы храним его байт в байт.
Каждый новый адрес проверяется по нашему списку блокировок и Google Web Risk, опасный отклоняется с 422 destination_blocked. Если адрес окажется опасным позже, ссылка блокируется: она перестаёт открываться, изменить её больше нельзя, а ваш вебхук получает link.blocked.
Если хост адреса назначения — адрес клика партнёрской сети или сервиса коротких ссылок, например awin1.com, sjv.io, prf.hn, admitad.com, amzn.to, bit.ly (поддомены тоже), мы сами проходим по его перенаправлениям: при создании ссылки, в том числе пакетом, и когда PATCH меняет её destination. Это до 5 перенаправлений за несколько секунд. Адрес, на котором цепочка уходит с трекеров на другой сайт, сохраняется байт в байт как resolved_destination. Другие адреса назначения мы никогда не запрашиваем, и resolved_destination у них остаётся null.
destination остаётся ровно таким, каким вы его прислали. С согласием посетитель попадает туда, через трекер. Без согласия он идёт сразу на resolved_destination, минуя трекер, и без ID рекламных кликов.
Если пройти цепочку не удалось (трекер не перенаправляет, отвечает слишком медленно, перенаправляет слишком много раз, ведёт на внутренний или заблокированный адрес или так и не уводит с трекеров), ссылка всё равно создаётся, но отказ пока идёт через трекер. Тогда в каждом ответе со ссылкой есть warnings с кодом tracker_unresolved, и страница ссылки в кабинете тоже об этом говорит. Мы повторяем в фоне, выжидая между попытками 5 минут, 1 час; когда получится, resolved_destination заполняется и предупреждение пропадает. После последней попытки проверьте адрес и отправьте его снова через PATCH. Когда предупреждать не о чем, warnings в ответе нет.
Наши запросы к трекерам идут с User-Agent Consent.link-Resolver/1 (+https://consent.link/faq/api).
Ответ
201 Created
{
"id": "pR3xd",
"short": "https://consent.link/pR3xd",
"destination": "https://www.awin1.com/cread.php?awinmid=1234&awinaffid=56789&ued=https%3A%2F%2Fshop.example%2Fboots",
"resolved_destination": null,
"type": "affiliate",
"template": "statsads",
"name": "Winter boots",
"external_id": "post-8815",
"metadata": {"campaign": "winter"},
"profile": "main",
"status": "active",
"mode": "live",
"created_at": "2026-11-03T12:00:00.000Z",
"warnings": [
{
"code": "tracker_unresolved",
"message": "The destination is a tracker redirect that could not be unwound yet: a visitor who refuses will go through the tracker. It is tried again later; resolved_destination is filled in when it works."
}
]
}
Передайте заголовок Idempotency-Key в POST /v3/links и POST /v3/links/bulk: любые от 1 до 255 печатных символов ASCII, например UUID или ваш ID поста. Повтор с тем же ключом и тем же телом в течение 24 часов возвращает уже созданные ссылки с заголовком Idempotent-Replayed: true и ничего не создаёт.
Тот же ключ с другим телом отклоняется с 422 idempotency_key_reused. Если первый запрос ещё выполняется, повтор получает 409 idempotency_in_progress: подождите немного и отправьте снова.
Да: POST /v3/links/bulk с {"links": [ … ]}, до 500 ссылок за запрос, у каждой те же поля, что у одиночной ссылки. Ответ 201 с data, ссылки в том порядке, в каком вы их отправили.
Всё или ничего: если один элемент неверен, ничего не создаётся, а ошибка называет его, например links.3.destination.
PATCH /v3/links/{id} только с теми полями, которые меняются: destination, type, template, name, metadata. null очищает необязательное поле, metadata заменяется целиком, а external_id остаётся таким, каким был при создании. Страница согласия показывает изменение сразу. Новый адрес проверяется так же, как у новой ссылки, а если он на другом домене, прежние согласия «Не спрашивать снова» для старого домена больше не пропускают посетителей.
DELETE /v3/links/{id} отключает ссылку: она перестаёт открываться, её нельзя изменить или включить снова, а её записи остаются в журнале. Если ссылка снова нужна, создайте новую.
GET /v3/links выдаёт ваши ссылки, сначала новые, по 50 на странице (до 100 с limit). Фильтры: status, mode, type, template, external_id, domain, а также from и to по времени создания (2026-10-31 или 2026-10-31T14:00:00Z). Для следующей страницы передайте next_cursor из ответа как cursor; null значит, что страниц больше нет.
GET /v3/links/{id} возвращает одну ссылку со stats с момента создания: shown, granted, denied, left (ушли со страницы без решения), reused и withdrawn. Цифры отстают от журнала на несколько минут.
Страницу, которую вы настроили в «Оформление» в кабинете: шаблон текста из нашей библиотеки, получатели данных, подпись, ссылка на вашу политику конфиденциальности, а на Business ваши логотип и цвет. Собственные type и template ссылки важнее оформления. Когда вы публикуете новую версию, страницы ваших ссылок меняются в течение минуты.
Язык выбирается по браузеру посетителя: английский, испанский, немецкий, французский, итальянский, португальский или русский. Вы выбираете шаблон, а не слова: тексты берутся из нашей библиотеки, свой написать нельзя.
Пока в «Оформление» ничего не опубликовано, страница показывается без ссылки на вашу политику конфиденциальности и без получателей данных. Ссылки при этом создаются, а ответы POST /v3/links, POST /v3/links/bulk и PATCH /v3/links/{id} несут заголовок Consent-Link-Warning: design_unpublished, пока у конфигурации ссылки нет опубликованной версии.
Сохраните несколько конфигураций страницы в «Оформление» или через API, у каждой свой ID, например black-friday, и укажите нужную при создании ссылки: "profile": "black-friday" в POST /v3/links или в элементе массового создания. Без profile ссылка получает основную конфигурацию.
Ссылка запоминает свою конфигурацию: если потом сделать основной другую, старые ссылки не меняются. PATCH /v3/links/{id} с profile переводит одну ссылку, а "profile": null переводит её на основную конфигурацию этого момента. Неизвестный ID даёт 422 по полю profile. В объекте ссылки видно её поле profile, а GET /v3/links?profile=black-friday выдаёт ссылки одной конфигурации. Сколько конфигураций можно держать, зависит от тарифа: 1 на Free, 5 на Pro, 50 на Business, включая основную.
GET /v3/profiles выдаёт список, основная первой. POST /v3/profiles создаёт конфигурацию с id и policy_url (обязательны) и, по желанию, name, template, vendors, type, remember, brand_name и default. Она публикуется сразу. PATCH /v3/profiles/{id} меняет её: поля страницы сразу публикуют новую версию, а одни name и "default": true ничего не публикуют.
DELETE /v3/profiles/{id} удаляет конфигурацию, если она не основная (409 profile_is_default) и на неё не ведёт ни одна действующая ссылка (409 profile_in_use). Создание сверх лимита тарифа или использование конфигурации, оказавшейся сверх лимита после понижения тарифа, даёт 402 profiles_limit_reached. Изменения требуют боевого ключа.
С согласием посетитель попадает на ваш адрес ровно в том виде, в каком вы его дали. Без согласия utm_* и остальные ваши параметры тоже остаются, убираются только ID рекламных кликов: gclid, gbraid, wbraid, fbclid, msclkid, ttclid, twclid, li_fat_id. В обоих случаях мы добавляем один параметр, cl_ct, это токен решения.
Учитываются только параметры самого адреса назначения: то, что дописано к короткой ссылке (https://consent.link/nk7Qa?utm_source=…), дальше не передаётся. Кладите всё, что нужно сайту, в destination или делайте ссылку на каждую кампанию.
Каждый переход со страницы согласия несёт cl_ct, подписанный токен (JWT, ES256), который живёт 10 минут. Проверьте его со своего сервера через POST /v3/consents/verify: в ответе решение, а каждый токен проверяется один раз, поэтому скопированный адрес ничего не даёт. Прочитайте токен и уберите его из адреса.
Что в нём: decision (granted или denied), purposes (что посетитель разрешил), cid (запись в журнале), sid (псевдоним посетителя в вашем аккаунте, одинаковый на всех ваших ссылках), cexp (до какого времени действует согласие, Unix-время), link, mode, iat и exp. Ни IP, ни браузера, ни других персональных данных. Посетитель, который уже соглашался и оставил «Не спрашивать снова», проходит сразу, с granted.
Подпись можно проверить и самому ключами с https://app.consent.link/.well-known/jwks.json и любой библиотекой JWT: проверьте exp и что link ваша. Собственная проверка не узнает, использовался ли токен раньше, метод API узнает. Ошибки: 422 token_invalid, 422 token_expired, 409 token_used.
Создайте вебхук: POST /v3/webhooks с url и нужными events (таблица ниже). Адрес должен быть https с публичным именем хоста. В ответе secret (whsec_…) для проверки подписи: он показывается один раз, сохраните его. У аккаунта может быть до 10 действующих вебхуков.
Журнал доставок, кнопка «Повторить» и включение и отключение вебхука есть в кабинете в разделе «Аккаунт» → «Вебхуки».
Каждое событие это POST с телом в JSON: id (evt_…), type, created_at, mode и data. Запись о согласии в data.consent содержит id, link, decision, purposes, scope (domain, если посетитель оставил «Не спрашивать снова», иначе link), sid, dest_domain, country, notice_version, created_at и expires_at. Никогда IP, браузер или куку.
Событие
Когда
data
consent.recorded
Новая запись в журнале: согласие, отказ или посетитель, который уже соглашался
consent
consent.withdrawn
Согласие отозвано: посетителем на странице «Мои согласия» или вами через API
consent, withdraws
link.blocked
Мы заблокировали ссылку, потому что её адрес оказался опасным
В каждом запросе есть заголовки Consent-Event, Consent-Delivery и Consent-Signature: t=1793712000,v1=5f3a…. v1 это HMAC-SHA256 в hex от текста t, точки и сырого тела запроса, с секретом вашего вебхука в качестве ключа.
Считайте подпись от тела ровно в том виде, в каком оно пришло (до разбора JSON), сравнивайте за постоянное время и отклоняйте t, который расходится с вашими часами больше чем на пять минут, чтобы записанный запрос нельзя было повторить.
Доставка засчитывается, когда ваш сервер отвечает кодом 2xx за 10 секунд; перенаправления не выполняются. Иначе мы повторяем, выжидая между попытками 1 минута, 5 минут, 30 минут, 2 часа, 12 часов, всего 6 попыток. После последней доставка считается неудачной и остаётся в журнале доставок, где кнопка «Повторить» отправляет её снова в течение 3 дней после события.
Поэтому отвечайте быстро, а работу делайте потом. Одно и то же событие может прийти больше одного раза, например после тайм-аута: пропускайте уже обработанные id. События не всегда приходят в том порядке, в каком произошли.
Посетители видят и отзывают свои согласия на странице «Мои согласия». Если посетитель обратился к вам, отзовите согласие за него: POST /v3/consents/{id}/withdraw без тела, с ID его записи granted или reused. Это работает для любого действующего согласия, как бы далеко назад ни показывал журнал ваш тариф. Ответ 201 с новой записью withdrawn, её first_consent_id — отозванное согласие. Запись, которая не является действующим согласием (отказ, уже отозванное или истёкшее), получает 409 consent_not_in_force; запись, которую вы не можете прочитать, получает 404, как у GET /v3/consents/{id}.
Оба способа ведут к одному: запись withdrawn в журнале, consent.withdrawn на ваш вебхук с data.withdraws, ID отозванного согласия, и с этого момента ваши ссылки снова спрашивают этого посетителя. Отзыв закрывает все согласия, которые посетитель дал для этого домена до него, и не расходует лимит.
Перестаньте использовать его данные у себя: сопоставляйте по sid или по ID записи.
Запрос
curl -X POST https://api.consent.link/v3/consents/01jbz8m3k4x9w2r7d5t6q8v1yh/withdraw \
-H "Authorization: Bearer clk_live_…"
Согласие действует 6 месяцев с момента выдачи: expires_at в записи, cexp в токене решения. Запись reused несёт expires_at того согласия, которое она переиспользует. После этого ваши ссылки снова спрашивают посетителя.
Когда согласие granted истекает, не будучи отозванным, ваш вебхук получает consent.expired с data.consent, самой записью, и data.expired_at. Мы проверяем раз в час, так что событие приходит в течение часа после expires_at, по одному на согласие. У записей reused своего события нет: их покрывает событие согласия, которое они переиспользуют. Событие уходит только на вебхуки, у которых в events есть consent.expired. Об истечении сообщается один раз, даже если тогда его не ждал ни один вебхук: позже оно не повторяется.
Поставьте наш скрипт в <head> раньше GTM и gtag: https://consent.link/js/consent.js. Он ставит Consent Mode v2 по умолчанию (всё запрещено, кроме functionality_storage и security_storage, с wait_for_update 500 мс), читает cl_ct из адреса, проверяет подпись ключами с https://app.consent.link/.well-known/jwks.json прямо в браузере и обновляет согласие.
Решение запоминается в куке cl_consent на вашем домене: до cexp при согласии и на 180 дней при отказе, так что на следующих страницах баннер не нужен. После проверки cl_ct убирается из адресной строки, остальные параметры остаются. Скрипт ничего нам не отправляет.
Если вы записываете решение у себя, проверяйте токен и на сервере через POST /v3/consents/verify: скрипт не знает, использовался ли токен раньше.
Скачайте шаблон consent-link.tpl, в GTM откройте «Шаблоны» → «Новый» → ⋮ → «Импорт» и сохраните. Создайте тег из шаблона с триггером «Consent Initialization – All Pages» («Инициализация согласия — все страницы»).
Тег ставит значения по умолчанию, применяет решение из куки cl_consent и загружает скрипт, который проверяет свежий cl_ct. Отдельно ставить скрипт в <head> тогда не нужно.
Цели из purposes токена включают такие сигналы. Всё, что не включено, остаётся запрещённым; при отказе (denied) запрещено всё, кроме двух всегда разрешённых.
Не показывайте второй баннер тому, кто уже решил на нашей странице. Скрипт отдаёт решение в window.consentLink.decision (granted, denied или null) и window.consentLink.has('stats'); window.consentLink.ready срабатывает, когда токен из адреса проверен. Ещё есть событие consentlink:update на document и consent_link_update в dataLayer. Ставьте наш скрипт раньше скрипта баннера.
Cookiebot: передайте решение через Cookiebot.submitCustomConsent, и баннер не появится. У OneTrust нет публичной функции, чтобы записать согласие со стороны сайта, поэтому загружайте его скрипт, только когда решения нет. С другими баннерами так же: передайте цели через их API или не загружайте баннер.
Cookiebot
window.addEventListener('CookiebotOnLoad', function () {
window.consentLink.ready.then(function (cl) {
if (cl.decision && !Cookiebot.hasResponse) {
Cookiebot.submitCustomConsent(cl.has('personalization'), cl.has('stats'), cl.has('ads'));
}
});
});
OneTrust
window.consentLink.ready.then(function (cl) {
if (cl.decision) return; // decided on consent.link: no second banner
var s = document.createElement('script');
s.src = 'https://cdn.cookielaw.org/scripttemplates/otSDKStub.js';
s.setAttribute('data-domain-script', 'YOUR-ONETRUST-ID');
document.head.appendChild(s);
});
GET /v3/consents выдаёт записи, сначала новые, с фильтрами decision (granted, denied, reused, withdrawn), link, country (DE), from, to и теми же страницами, что у ссылок. GET /v3/consents/{id} возвращает одну запись с доказательствами (IP-адрес, браузер, язык, хэш текста страницы, когда страница была показана и сколько посетитель думал) и её местом в хэш-цепочке, chain.
Отказ хранит только решение, время, ссылку и страну, поэтому его evidence равно null. API отдаёт данные целиком; кабинет скрывает их часть, пока вы не включите вход с кодом из приложения. Насколько далеко назад видно, зависит от тарифа (таблица в разделе «Тарифы и лимиты»).
GET /v3/consents/{id}/proof возвращает запись в PDF: решение, кто, что было показано, хэши и как их проверить. Документ на языке вашего кабинета. Доступно на Pro и Business (на Free 402 plan_required), до 20 в минуту. Если PDF сейчас не получается сделать, ответ 503 unavailable с Retry-After.
Каждая запись granted несёт evidence.snapshot_hash: SHA-256 страницы согласия в том виде, в каком её увидел посетитель, в HTML. Один снимок служит всем посетителям одной и той же страницы, то есть одной версии оформления и текста, языка и типа ссылки. GET /v3/consents/{id}/snapshot отдаёт этот HTML с хэшем в заголовке X-Snapshot-Hash, а GET /v3/consents/{id}/snapshot.png — его скриншот. Для обоих действуют правила GET /v3/consents/{id}, включая глубину журнала.
Чтобы проверить, посчитайте SHA-256 от HTML ровно в том виде, в каком он пришёл: он равен snapshot_hash, а собственный хэш записи в цепочке покрывает snapshot_hash, так что подменить страницу задним числом нельзя. То, что меняется от показа к показу, в снимке зафиксировано: хост адреса назначения выглядит как example.com, подпись формы пустая, её action — #. Настоящий адрес — в dest_domain и evidence.destination.
У записей reused, отказов и записей, сделанных до появления снимков, snapshot_hash равен null, а их снимок отвечает 404 not_found («This record has no page snapshot.»). Сразу после решения файлы могут ещё записываться: 404 not_found с «The page snapshot is not ready yet.» значит, что стоит повторить чуть позже. HTML появляется за секунды, скриншот немного позже. Страница записи в кабинете и PDF-доказательство тоже показывают скриншот и хэш.
POST /v3/exports с фильтрами GET /v3/consents (decision, link, country, from, to) сразу отвечает 202 с id выгрузки. Файл собирается в фоне: придёт вебхук export.ready, или проверяйте GET /v3/exports/{id}, пока status не станет ready, затем скачайте GET /v3/exports/{id}/file. У выгрузки также есть rows и sha256 файла.
Файлы хранятся 7 дней (потом 410 export_expired). Одновременно может готовиться до 3 выгрузок (429 too_many_exports). Pro и Business.
GET /v3/stats/summary даёт shown, granted, denied, left, reused и withdrawn за дни по UTC от from до to (2026-10-31, оба включительно; по умолчанию последние 30 дней, не больше 366 дней и не дальше, чем тариф показывает журнал). Добавьте link для одной ссылки. GET /v3/stats/daily даёт те же цифры по дням, а на Pro и Business по странам с by=country. Боты не считаются никогда.
GET /v3/usage показывает согласия за этот период, ваш limit, остаток remaining, начало и конец периода.
API, вебхуки и страница согласия на семи языках есть на любом тарифе. Тарифы отличаются числом согласий в месяц, глубиной журнала, частотой запросов и дополнительными функциями. Цены в евро без НДС.
Тариф
Цена в месяц
Согласий в месяц
Журнал
Запросов в минуту
Конфигураций
Ещё
Free
0 €
1 000
Журнал за 30 дней
60
1
—
Pro
9 €
10 000
Журнал за 1 год
300
5
Выгрузка CSV и доказательство в PDF · Статистика по дням и странам
Business
99 €
250 000
Журнал за 3 года
1 000
50
Выгрузка CSV и доказательство в PDF · Статистика по дням и странам · Ваш логотип и цвет · Ссылки на своём домене
Считаются только новые согласия: отказы, повторные переходы с «Не спрашивать снова», боты не считаются. На 80 % и 100 % лимита приходит письмо и вебхук usage.warning.
Когда лимит исчерпан, новые боевые ссылки отклоняются с 402 limit_reached. Всё остальное работает: старые ссылки по-прежнему спрашивают и записывают, вебхуки и токены приходят как обычно, ничего не скрывается. Создание ссылок открывается в начале следующего периода (на Free 1-го числа, на платных тарифах в день оплаты) или сразу после перехода на тариф выше.
На аккаунт, все ключи вместе: 60 в минуту на Free, 300 на Pro, 1 000 на Business. Сверх этого ответ 429 rate_limited с заголовком Retry-After; заголовки X-RateLimit-Limit и X-RateLimit-Remaining показывают, сколько осталось. У некоторых методов есть и свой лимит: PDF-доказательства 20 в минуту, новые выгрузки 10, скачивание выгрузок 30.
Всегда одинаково: HTTP-статус и тело в JSON с постоянным code и сообщением message на английском. Ориентируйтесь на code, текст сообщения может меняться. Неверные поля дают 422 validation_failed с fields: каждое поле со своими ошибками.
Статус
Код
Что значит
401
unauthenticated
Нет ключа, ключ неверный или отозван
402
limit_reached
Согласия периода закончились: новых боевых ссылок нет
402
plan_required
Функции нужен тариф выше
402
profiles_limit_reached
Конфигураций больше, чем позволяет тариф
403
account_blocked
Аккаунт заблокирован: напишите в поддержку
403
links_blocked
Аккаунту нельзя создавать ссылки: напишите в поддержку
Создайте новый ключ на странице «Подключение по API», переключите на него свой сервер и отзовите старый: он сразу перестанет работать. Отзыв ключа не затрагивает ссылки, журнал и вебхуки.
Храните ключи в переменных окружения сервера, никогда в браузере, мобильном приложении или публичном репозитории. Если мог утечь секрет вебхука, отключите этот вебхук в кабинете и создайте новый.
Готовы подключиться?
Зарегистрируйтесь, создайте ключ на странице «Подключение по API» и отправьте первый запрос за несколько минут.