Diil Docs
  1. Документация
  2. С чего начать

Доступ по ключу сайта

Обновлено:

Каждый запрос к Content API несёт ключ сайта. По нему мы понимаем, чей контент вы читаете, — и, в общем-то, это всё, что он делает. Никакого OAuth, обновления токенов и подписи запросов в полночь. Одна строка в одном заголовке — и вы внутри.

Что такое ключ сайта

Выглядит ключ так:

pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40
  • Префикс pk_ и ровно 32 шестнадцатеричных символа в нижнем регистре: ^pk_[a-f0-9]{32}$.
  • Ключ принадлежит одному сайту. Только он определяет, чей контент вы получите, — параметра «id сайта» в API нет вообще.
  • Он только читает и видит только опубликованное.
  • У сайта может быть несколько ключей одновременно — на этом держится безболезненная замена ключа (о ней ниже).

pk_ — привет «публикуемым ключам» (publishable keys) платёжных сервисов: это ключ, который изначально рассчитан на жизнь в открытом коде. Почему это нормально, расскажем чуть дальше.

Где взять ключ

  1. Откройте сайт в CRM

    Войдите в Diil и выберите сайт, контент которого хотите читать.
  2. Перейдите в «Настройки → Ключи контентного API»

    Здесь собраны все ключи сайта.
  3. Создайте ключ

    Вы получите свежую строку pk_…. Скопируйте её.
  4. Положите ключ в переменную окружения

    Не прямо в код — вы же из будущего, меняющий ключ, скажете спасибо. Готовые варианты для популярных фреймворков — ниже.

Как передать ключ

Передавайте ключ в заголовке запроса x-crm-key. Это основной способ, и он одинаково работает и с сервера, и из браузера: CORS разрешает этот заголовок с любого домена.

curl "https://back.sitecog.com/content/v1/pages/home?lang=en" \
  -H "x-crm-key: pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40"

Запасной вариант: ?key=

Если заголовок поставить никак нельзя, передайте ключ параметром в адресе:

curl "https://back.sitecog.com/content/v1/pages/home?lang=en&key=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40"

Когда так можно?

  • Быстрая проверка — вставить адрес в строку браузера и посмотреть, что отдаёт запрос.
  • Инструменты, которые принимают только адрес — no-code интеграция, импорт ленты, плагин генератора статических сайтов без настройки заголовков.

В остальных случаях лучше заголовок. Адреса любят оседать в логах сервера, истории браузера и аналитике, и хотя ключ не секрет, разбрасывать его повсюду незачем. К тому же адреса короче, а код аккуратнее.

Почему ключ не страшно держать в браузере

Коротко: он не умеет ничего такого, чего не может любой посетитель, открыв ваш сайт. Ключ сайта публичный по замыслу. Вот что ему доступно, а что нет:

Ключ сайта…
читает опубликованные страницы, секции, блоки и статьи блога своего сайтада
что-то меняет, создаёт или удаляетнет — API только для чтения
видит черновики и неопубликованные статьинет — только опубликованное
читает контент других ваших сайтовнет — один ключ, один сайт

Та же идея, что у публикуемого ключа платёжного сервиса: он говорит, чьи данные показать, но не даёт над ними власти. Всё, что он может прочитать, и так окажется на вашем публичном сайте.

Единственное, что можно сделать чужим ключом, — потратить ваш лимит запросов: у каждого ключа свои 600 запросов в минуту (см. Лимиты). Если кто-то этим увлёкся, замените ключ — это пара минут.

Ключ в переменных окружения

Раз ключ публичный, зачем переменные окружения? Затем, что ключи меняются, и замена ключа должна быть правкой настроек, а не кода. К тому же большинство фреймворков пускают переменную в браузерный код только с определённым префиксом:

.envbash
# Next.js — серверные компоненты и Route Handlers (в браузер не попадает)
CRM_KEY=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40

# Next.js — клиентские компоненты (вшивается в бандл при сборке)
NEXT_PUBLIC_CRM_KEY=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40

# Vite (React, Vue, Svelte…) — вшивается при сборке
VITE_CRM_KEY=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40

# Nuxt — подменяет runtimeConfig.public.crmKey
NUXT_PUBLIC_CRM_KEY=pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40
// app/page.tsx — серверный компонент
export default async function Home() {
  const res = await fetch('https://back.sitecog.com/content/v1/pages/home?lang=en', {
    headers: { 'x-crm-key': process.env.CRM_KEY! },
    next: { revalidate: 60 },
  });
  const page = await res.json();

  return <h1>{page.content.hero.content.hero_title.content.en}</h1>;
}

Отзыв и замена ключа

Любой ключ можно отозвать в CRM, в том же списке «Настройки → Ключи контентного API». Отозванный ключ перестаёт работать: на каждый запрос с ним приходит 401 invalid_key. Чтобы сменить ключ без единого упавшего запроса, дайте старому и новому поработать вместе:

  1. Создайте новый ключ

    Старый пока не трогайте — оба работают параллельно.
  2. Выкатите сайт с новым ключом

    Обновите переменную окружения везде, где был старый ключ, пересоберите, если фреймворк вшивает её в бандл, и выкатите.
  3. Убедитесь, что контент на месте

    Откройте пару страниц, загляните в логи. Нет 401? Отлично.
  4. Отзовите старый ключ

    Теперь его можно спокойно выключать.

Ошибки ключа

СтатусТело ответаЧто случилось
401{"message":"invalid_key"}Ключа нет, он неверного вида (не pk_ + 32 hex) или отозван.
429{"message":"rate_limit_exceeded"}Слишком много запросов — или слишком много неверных ключей с вашего IP за эту минуту (см. ниже).

Блокировка за неверные ключи

Чтобы подбирать ключи было бессмысленно, мы считаем запросы с неверным ключом по каждому IP. Больше 20 таких запросов за минуту с одного IP — и до конца минуты этот IP получает 429, даже с правильным ключом. Заголовка Retry-After нет: счётчик обнуляется в начале следующей минуты.

Классический способ попасться случайно: старый ключ отозвали, а один сервер или забытая cron-задача всё ещё ходят с ним. Серверный рендеринг сыплет 401, и через минуту заблокирован весь сервер — вместе с новым ключом. Поэтому:

401 не повторяемjs
const res = await fetch(url, { headers: { 'x-crm-key': process.env.CRM_KEY } });

if (res.status === 401) {
  // Неверный или отозванный ключ сам не починится. Повторы только
  // расходуют счётчик неверных ключей и приводят к блокировке IP.
  throw new Error('Content API: неверный ключ сайта, проверьте CRM_KEY');
}

if (res.status === 429) {
  // Подождите до следующей минуты (около 60 с плюс немного случайной задержки).
}

Остальные коды — на странице Ошибки.

Вопросы о безопасности