Distribby Yamka API v2 Проверяем…

API лейбла

Каталог, статусы по площадкам, смарт-ссылки и аналитика вашего лейбла по ключу. Для сайта, CRM и дашбордов. Плюс события на ваш сервер.

REST + JSONБазовый адрес /api/v1, поля в snake_case, даты в ISO 8601.
Права по скоупамКлюч умеет ровно то, что вы отметили при выпуске. Отзыв — мгновенный.
Вебхуки с подписьюHMAC-SHA256, повторы, журнал доставок. Не опрашивайте, подпишитесь.

Быстрый старт

  1. Откройте кабинет лейбла → API и вебхуки → выпустите ключ с нужными правами.Ключ показывается один раз. У нас хранится только его хэш.
  2. Сделайте первый запрос.Ключ передаётся заголовком Authorization: Bearer dby_….
  3. Подпишитесь на события вместо опроса.Одобрение, отправка, выход на площадках придут на ваш адрес сами.
curl https://distribyamka.com/api/v1/releases?status=live \
  -H "Authorization: Bearer dby_ваш_ключ"
const res = await fetch('https://distribyamka.com/api/v1/releases?status=live', {
  headers: { Authorization: 'Bearer ' + process.env.DISTRIB_API_KEY },
});
const { releases, total } = await res.json();
import os, requests
r = requests.get('https://distribyamka.com/api/v1/releases',
                 params={'status': 'live'},
                 headers={'Authorization': f"Bearer {os.environ['DISTRIB_API_KEY']}"})
releases = r.json()['releases']
$ch = curl_init('https://distribyamka.com/api/v1/releases?status=live');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('DISTRIB_API_KEY')]]);
$releases = json_decode(curl_exec($ch), true)['releases'];
Ключ даёт доступ к каталогу всего лейбла. Не вставляйте его в код страниц сайта: вызывайте API со своего сервера и храните ключ в переменных окружения.

Статус API

Открытый адрес без ключа, для мониторинга и для вашей страницы статуса. Отвечает 200, пока база доступна, и 503, когда нет. Обновляется на этой странице каждые 30 секунд.

GET/statusбез ключа · 30 запросов в минуту с адреса
API—
База—
Дистрибьютор—
Вебхуки—
{ "status": "ok", "version": "1.1.0", "time": "2026-09-02T14:07:31.000Z", "uptime_seconds": 86400,
  "checks": { "api": { "ok": true },
              "database": { "ok": true, "latency_ms": 2 },
              "distributor": { "ok": true, "last_event_at": "2026-09-02T13:58:10.000Z" },
              "webhooks": { "ok": true, "deliveries_24h": 412, "failed_24h": 3 } } }

distributor.last_event_at — когда последний раз площадки сообщали о статусе релиза. webhooks — доставки за сутки по всем лейблам, чтобы вы видели, идут ли они вообще.

Ключи и права

Ключ привязан к лейблу и набору прав. Один ключ на интеграцию: сайту не нужны финансы, а бухгалтерии не нужны ссылки.

СкоупЧто открывает
catalog:readартисты, релизы, треки с ISRC, статусы по площадкам
stats:readстатистика страниц релизов и сводка по лейблу
finance:readбаланс, условия, заявки на вывод, последние выплаты
links:readсписок и карточки смарт-ссылок
links:writeсоздание, правка и отключение смарт-ссылок
catalog:writeартисты, черновики релизов, обложки и треки
releases:submitотправить релиз на модерацию
team:readсотрудники лейбла и их роли
support:writeзапрос на снятие релиза, письмо в поддержку
webhooks:manageадреса вебхуков, события, проверка, журнал доставок

Ключ только с ваших серверов

При выпуске ключа в кабинете можно перечислить адреса, с которых он принимается: IPv4, подсети вида 203.0.113.0/24 или IPv6. Запрос с другого адреса получит 403 с кодом ip_not_allowed, даже если ключ верный. Для серверных интеграций это дешёвая страховка от утечки ключа.

Сколько ключей и какие права доступны — решает тариф лейбла (ниже). Отозванный ключ перестаёт работать в ту же секунду. GET /me покажет права ключа, тариф, лимиты и сколько из них занято.

Тарифы и лимиты

Мы открыты всем лейблам, поэтому тарифов четыре, и каждый следующий даёт больше. Размер считается по каталогу, то есть по релизам; артисты без ограничений на всех тарифах. Актуальные цены и лимиты всегда в GET /plans (без ключа); здесь — состав на сегодня.

Старт
$19 / мес
Первые релизы
  • Каталог до 100 релизов, 2 сотрудника
  • Комиссия 25%
  • 50 смарт-ссылок
  • API: чтение каталога, ссылок, статистики
  • 1 ключ, 60 запросов/мин
Лейбл
$49 / мес
Растущий ростер
  • Каталог до 500 релизов, 5 сотрудников
  • Комиссия 15%
  • Ссылки без ограничений
  • Полный API и лента событий
  • 5 ключей, 3 вебхука, 120/мин
  • Свой Telegram-бот и персональная почта
Про
$99 / мес
Большой каталог
  • Каталог до 2000 релизов, 10 сотрудников
  • Комиссия 15%
  • Экспорт каталога в CSV
  • 10 ключей, 10 вебхуков, 300/мин
  • Приоритетная поддержка
Энтерпрайз
по договорённости
Свои условия
  • Каталог без ограничений, 50 сотрудников
  • Комиссия по договору
  • 25 ключей, 25 вебхуков, 600/мин
  • SLA и персональный менеджер

Когда лимит исчерпан, API отвечает 403 с кодом plan_limit; когда функции нет на тарифе — plan_required. В обоих случаях в теле есть required_plan: с какого тарифа можно.

{ "error": "На тарифе «Старт» до 100 релизов в каталоге. Больше — на тарифе «Лейбл»",
  "code": "plan_limit", "plan": "start", "limit": 100, "used": 100, "required_plan": "label" }

Лимит запросов в минуту тоже тарифный: смотрите X-RateLimit-Limit в ответах. Смена тарифа — через кабинет лейбла или поддержку.

Соглашения

snake_caseво всех полях ответов и тел запросов ISO 8601даты и время: 2026-09-02T14:07:31.000Z; даты релизов без времени: 2026-09-15 page, per_pageстраницы в списках, до 100 на страницу; общее число в total и в заголовке X-Total-Count Idempotency-Keyлюбой уникальный строковый ключ в заголовке любого POST; повтор в течение суток вернёт тот же ответ с заголовком Idempotent-Replayed: true, повтор с другим телом — 422 Linkв постраничных списках: rel="next", rel="prev", rel="last", как у GitHub. Можно не считать страницы руками X-API-Versionверсия API в каждом ответе; X-RateLimit-Reset — unix-время, когда окно лимита обнулится If-None-Matchответы несут ETag; повторный запрос с ним вернёт 304 без тела Cache-Controlвсегда no-store: ответы не кэшируются по дороге UTF-8названия и имена приходят как есть, включая кириллицу

Лимиты и ошибки

120 запросов в минуту на ключ, окно скользящее. Остаток — в X-RateLimit-Remaining, при превышении 429 и Retry-After в секундах. Ответ с ошибкой всегда одной формы:

{ "error": "У ключа нет права links:write", "code": "forbidden", "required_scope": "links:write" }
HTTPcodeКогда
400invalidошибка в данных; имя поля в field
401unauthorizedнет ключа, не найден или отозван
403forbiddenу ключа нет права; required_scope говорит какого
404not_foundобъект не принадлежит лейблу или не существует
403plan_limit · plan_requiredлимит тарифа исчерпан или функции нет на тарифе; required_plan говорит, с какого можно
409conflictдействие невозможно в текущем состоянии
422idempotency_mismatchтот же Idempotency-Key с другим телом запроса
429rate_limitedслишком много запросов, или слишком много неудачных попыток входа с адреса

Безопасность

Что делаем мы и что стоит сделать вам, чтобы ключ и данные лейбла остались вашими.

С нашей стороны

С вашей стороны

Нашли уязвимость? Напишите на [email protected] с темой «security». Отвечаем в тот же день.

Кто я

GET/meлюбой ключ
{ "label": { "id": 12, "name": "Ночная Смена", "slug": "nochnaya" },
  "key": { "prefix": "dby_3f9a2c1e", "scopes": ["catalog:read", "stats:read"], "created_at": "2026-09-02T10:00:00.000Z" },
  "plan": { "key": "label", "name": "Лейбл", "commission_rate": 0.15,
            "limits": { "releases": 500, "artists": null, "members": 5, "links": null, "api_keys": 5, "webhooks": 3, "rate": 120 },
            "usage":  { "releases": 42, "artists": 9, "members": 3, "links": 14, "api_keys": 2, "webhooks": 1 },
            "features": { "bot": true, "events_feed": true, "csv_export": false, … } },
  "rate_limit": { "per_minute": 120 }, "api_version": "1.1.0",
  "docs": "https://distribyamka.com/api-docs", "openapi": "https://distribyamka.com/api/v1/openapi.json" }

Артисты

GET/artistscatalog:read

Параметры: q (поиск по имени), page, per_page.

{ "artists": [ { "id": 401, "name": "Айгерим", "spotify_url": "https://open.spotify.com/artist/…",
                 "releases_count": 3, "created_at": "…" } ],
  "page": 1, "per_page": 50, "total": 9 }
GET/artists/{id}catalog:read

Артист и все его релизы одним ответом: { "artist": { …, "releases": [ … ] } }.

Создание и правка

POST/artistscatalog:write
POST /artists
{ "name": "Айгерим", "spotify_url": "https://open.spotify.com/artist/…" }
→ 201 { "artist": { "id": 401, "name": "Айгерим", "spotify_url": "…", "apple_url": null, "releases_count": 0 } }

Имя уникально внутри лейбла: повтор отвечает 409 с artist_id существующего, чтобы синхронизация из CRM не плодила дублей. Артисты без предела на всех тарифах.

PATCH/artists/{id}catalog:write

Любое подмножество: name, spotify_url, apple_url (null очищает).

Релизы

GET/releasescatalog:read
ПараметрЗначение
statusdraft · submitting · submitted · live · failed · takedown
artist_idтолько релизы артиста
upcнайти релиз по UPC: upc=5063000000012
idsчерез запятую, до 100: ids=68,69,93
updated_sinceISO-время: только изменённые после него. Удобно для синхронизации по расписанию
sort-created_at (по умолчанию) · created_at · -release_date · release_date · title
{ "releases": [ { "id": 68, "title": "Ночной рейс", "type": "single", "status": "live",
    "release_date": "2026-08-18", "upc": "5063…", "genre": "Hip-Hop/Rap",
    "artist": { "id": 401, "name": "Айгерим" },
    "cover_url": "https://distribyamka.com/cover/…?w=640",
    "tracks_count": 1, "created_at": "…", "submitted_at": "…", "updated_at": "…" } ],
  "page": 1, "per_page": 50, "total": 24 }
GET/releases/{id}catalog:read

Полная карточка: треки с ISRC и авторами, территории, ссылки на площадки (когда проиндексированы), статус модерации и статус по каждой площадке.

{ "release": { …,
    "original_release_date": null, "territories": "worldwide",
    "review_status": "approved", "review_comment": null,
    "store_links": { "spotify": "https://open.spotify.com/album/…", "apple": "https://music.apple.com/…" },
    "tracks": [ { "id": 65, "number": 1, "title": "Ночной рейс", "version": null, "isrc": "QZ…",
                  "explicit": false, "language": "ru", "instrumental": false,
                  "writers": [ { "name": "Михаил Пономарёв", "roles": ["Composer/Instrumentalist"] } ] } ],
    "dsp_statuses": [ { "dsp": "spotify", "stage": "live", "detail": null, "updated_at": "…" },
                      { "dsp": "apple-music", "stage": "accepted", "detail": null, "updated_at": "…" } ] } }

Правка черновика

PATCH/releases/{id}catalog:write

Только пока релиз — черновик: title, version, genre, release_date, original_release_date, upc, territories ("worldwide" или список кодов стран). Всё, что уже на модерации или на площадках, отвечает 409: такие правки идут через поддержку, иначе они обошли бы проверку.

PATCH /releases/68
{ "release_date": "2026-10-03", "territories": ["KZ", "RU", "UZ"] }

Релизы: создание и отправка

Полный путь релиза через API, в ту же очередь модерации, что и из приложения. Четыре шага: черновик, обложка, треки со звуком, отправка. Итог модерации придёт вебхуком release.approved или release.rejected, дальше release.submitted, статусы площадок и release.live.

  1. Создайте черновикPOST /releases с артистом, типом, названием, датой и жанром.
  2. Загрузите обложкуPUT /releases/{id}/cover: квадрат от 1400 px, лучше 3000×3000, JPG или PNG.
  3. Добавьте трекиPOST /releases/{id}/tracks: WAV или FLAC плюс название, ISRC, авторы.
  4. Отправьте на модерациюPOST /releases/{id}/submit с confirm_rights: true. Нужен подписанный договор лейбла.
POST/releasescatalog:write
POST /releases
Idempotency-Key: rel-2026-10-03-nochnoy-reys
{ "artist_id": 401, "type": "single", "title": "Ночной рейс", "genre": "Hip-Hop/Rap",
  "release_date": "2026-10-03", "upc": null, "territories": "worldwide",
  "featured_artists": [ { "name": "Гость" } ] }

→ 201 { "release": { "id": 91, "status": "draft", …,
        "next_steps": [ "PUT /releases/91/cover", "POST /releases/91/tracks", "POST /releases/91/submit" ] } }

Тип single, ep или album. Без UPC мы присвоим свой при отправке. Музыка, созданная нейросетями, не принимается. Черновик считается в лимит каталога тарифа.

PUT/releases/{id}/covercatalog:write
curl -X PUT https://distribyamka.com/api/v1/releases/91/cover \
  -H "Authorization: Bearer dby_…" -H "Content-Type: image/jpeg" \
  --data-binary @cover.jpg

→ { "ok": true, "cover_url": "https://distribyamka.com/cover/…", "width": 3000, "height": 3000, "bytes": 812340 }
await fetch('https://distribyamka.com/api/v1/releases/91/cover', {
  method: 'PUT',
  headers: { Authorization: 'Bearer ' + KEY, 'Content-Type': 'image/jpeg' },
  body: fs.readFileSync('cover.jpg'),
});

Тело запроса — сама картинка (image/jpeg или image/png), либо multipart с полем cover. Не квадрат или меньше 1400 px — 400 с размерами в ответе.

POST/releases/{id}/trackscatalog:write
curl -X POST https://distribyamka.com/api/v1/releases/91/tracks \
  -H "Authorization: Bearer dby_…" \
  -F "[email protected]" -F "title=Ночной рейс" -F "language=ru" -F "explicit=false" \
  -F 'writers=[{"name":"Михаил Пономарёв","roles":["Composer/Instrumentalist"]}]'

→ 201 { "track": { "id": 120, "number": 1, "title": "Ночной рейс", "isrc": null, "explicit": false,
        "language": "ru", "instrumental": false, "writers": [ … ], "audio": { "filename": "nochnoy-reys.wav", "bytes": 42313044 } } }

Multipart: поле audio (WAV или FLAC, до 200 МБ) и текстовые поля title, version, isrc, explicit, language, instrumental, lyrics, writers (JSON). Порядок треков — порядок добавления; поменять можно через PATCH … {"number": 1}. У сингла не больше трёх треков.

PATCH/releases/{id}/tracks/{trackId}catalog:write
PUT/releases/{id}/tracks/{trackId}/audiocatalog:write
DELETE/releases/{id}/tracks/{trackId}catalog:write

Метаданные и звук меняются, пока релиз черновик или ждёт модерации. Удалять треки можно только из черновика.

POST/releases/{id}/submitreleases:submit
POST /releases/91/submit   { "confirm_rights": true }
→ 202 { "ok": true, "release_id": 91, "status": "submitting", "review": "pending",
        "message": "Релиз в очереди модерации. Итог придёт событием release.approved или release.rejected" }

Проверяем: есть треки и у каждого звук, есть обложка, у лейбла подписан договор с сервисом (иначе 428), права подтверждены. Дальше релиз идёт той же дорогой, что из приложения: отпечатки, оценка доверия, ручная проверка. Сразу после отправки уходит событие release.review_pending.

Отдельный скоуп releases:submit — намеренно. Ключ, который заводит черновики и грузит файлы, не обязан уметь отправлять их на площадки: разделите роли между интеграциями.
GET/releases/{id}/dsp-statuscatalog:read

Только статусы по площадкам, без остального. Стадии: submitted → accepted → live; отказы rejected, failed; снятие taken_down. Приходят от дистрибьютора по мере продвижения релиза, поэтому у свежего релиза список может быть пустым.

Создание релизов через API открыто с версии 2: черновик, обложка, треки, отправка в ту же очередь модерации. См. Релизы: создание и отправка.

Треки

GET/trackscatalog:read

Все треки лейбла с релизом в каждом. Параметры: isrc (точное совпадение), release_id, q (по названию), page, per_page. Удобно для сверки ISRC с отчётами площадок.

GET /tracks?isrc=QZK6P2600123
{ "tracks": [ { "id": 65, "number": 1, "title": "Ночной рейс", "version": null, "isrc": "QZK6P2600123",
                "explicit": false, "language": "ru", "instrumental": false, "writers": [ … ],
                "release": { "id": 68, "title": "Ночной рейс", "status": "live", "upc": "5063…", "artist_name": "Айгерим" } } ],
  "page": 1, "per_page": 50, "total": 1 }
GET/search?q=…catalog:read

Один запрос по релизам, артистам и трекам сразу: по названию, имени, UPC или ISRC. От двух символов, до десяти результатов каждого вида.

{ "query": "ночной", "releases": [ … ], "artists": [ … ],
  "tracks": [ { "id": 65, "title": "Ночной рейс", "isrc": "QZK6P2600123", "release_id": 68, "release_title": "Ночной рейс" } ] }

Экспорт каталога

GET/catalog/export.csvcatalog:read · тариф «Про»

Весь каталог одним CSV: строка на трек, UTF-8 с BOM, открывается в Excel и Numbers без плясок с кодировкой. Колонки: release_id, release_title, artist, type, status, release_date, upc, genre, track_no, track_title, version, isrc, explicit, language.

curl -o catalog.csv https://distribyamka.com/api/v1/catalog/export.csv -H "Authorization: Bearer dby_…"
GET/smart-linkslinks:read
GET/smart-links/{id}links:read
{ "smart_link": { "id": 77, "code": "k3n9p2", "slug": "nochnoy-reys", "url": "https://distrib.link/nochnoy-reys",
    "enabled": true, "title": "Ночной рейс", "artist_name": "Айгерим", "release_date": "2026-09-15",
    "release_id": null, "presave_open": true, "views": 1240, "clicks": 412, "presaves": 18,
    "targets": [ { "id": 301, "platform": "spotify", "url": "https://open.spotify.com/track/…", "clicks": 300 } ] } }

presave_open: true означает, что дата релиза в будущем и страница собирает адреса на напоминание. Ключи площадок для целей: GET /platforms.

Площадки по UPC

POST/smart-links/lookuplinks:read

Тот же поиск, которым пользуется приложение: по UPC возвращает название, артиста, обложку и найденные адреса на площадках, уже проверенные на подлинность. Из ответа сразу собирается targets для создания ссылки.

POST /smart-links/lookup   { "upc": "5063000000012" }
→ { "upc": "5063000000012", "title": "Ночной рейс", "artist_name": "Айгерим", "cover_url": "…",
    "targets": [ { "platform": "spotify", "url": "https://open.spotify.com/album/…" },
                 { "platform": "apple",   "url": "https://music.apple.com/…" },
                 { "platform": "yandex",  "url": "https://music.yandex.ru/album/…" } ] }
POST/smart-linkslinks:write
curl -X POST https://distribyamka.com/api/v1/smart-links \
  -H "Authorization: Bearer dby_…" -H "Content-Type: application/json" \
  -H "Idempotency-Key: release-68-link" \
  -d '{ "title": "Ночной рейс", "artist_name": "Айгерим", "release_date": "2026-09-15",
        "targets": [ { "platform": "spotify", "url": "https://open.spotify.com/track/…" },
                     { "platform": "apple",   "url": "https://music.apple.com/…" } ] }'

→ 201 { "smart_link": { "id": 77, "code": "k3n9p2", "url": "https://distrib.link/k3n9p2", … } }
const r = await fetch('https://distribyamka.com/api/v1/smart-links', {
  method: 'POST',
  headers: { Authorization: 'Bearer ' + KEY, 'Content-Type': 'application/json',
             'Idempotency-Key': 'release-68-link' },
  body: JSON.stringify({ title: 'Ночной рейс', artist_name: 'Айгерим', release_date: '2026-09-15',
    targets: [{ platform: 'spotify', url: 'https://open.spotify.com/track/…' }] }),
});
const { smart_link } = await r.json();
PATCH/smart-links/{id}links:write

Любое подмножество полей: enabled, title, artist_name, release_date, targets, а также оформление: slug (свой адрес), theme (auto | light | dark), accent (#rrggbb), cta_text, hide_branding. Оформление и свой адрес доступны с тарифа «Лейбл»; на «Старте» такие поля отвечают 403 plan_required, а не молча теряются. Список targets заменяется целиком, но клики по площадкам, которые остались, сохраняются.

PATCH /smart-links/77
{ "slug": "nochnoy-reys", "theme": "dark", "accent": "#ff5a1f", "cta_text": "Слушать", "hide_branding": true }
→ { "smart_link": { "url": "https://distrib.link/nochnoy-reys", … } }
PATCH /smart-links/77
{ "targets": [ { "platform": "spotify", "url": "https://open.spotify.com/track/…" },
               { "platform": "yandex",  "url": "https://music.yandex.ru/album/…" } ] }
DELETE/smart-links/{id}links:write

Выключает страницу (она начинает отвечать 404). Данные и статистика остаются: включить обратно можно через PATCH { "enabled": true }.

Статистика ссылки

GET/smart-links/{id}/statsstats:read
{ "totals": { "views": 1240, "clicks": 412, "rate_percent": 33.2, "presaves": 18 },
  "collecting_since": "2026-09-01T00:00:00.000Z",
  "days":      [ { "date": "2026-09-01", "views": 60, "clicks": 20 }, … ],
  "platforms": [ { "platform": "spotify", "title": "Spotify", "clicks": 300, "share_percent": 73 } ],
  "sources":   [ { "source": "instagram.com", "views": 800 }, { "source": "direct", "views": 300 } ],
  "devices":   [ { "device": "ios", "views": 900 }, { "device": "desktop", "views": 200 } ],
  "languages": [ { "language": "ru-ru", "views": 1100 } ] }

Счётчики totals ведутся с создания ссылки; разбивки — с collecting_since. Источник берётся из utm_source, если он есть в адресе, иначе из referrer; мессенджеры referrer часто не передают, такие заходы попадают в direct.

Аналитика по лейблу

GET/analytics/summary?days=30stats:read
{ "period_days": 30,
  "releases": { "total": 24, "by_status": { "live": 18, "submitted": 4, "draft": 2 } },
  "smart_links": { "total": 11, "views": 8420, "clicks": 2910, "presaves": 63, "rate_percent": 34.6 },
  "daily":     [ { "date": "2026-08-04", "views": 210, "clicks": 74 }, … ],
  "top_links": [ { "id": 77, "title": "Ночной рейс", "views": 1240, "clicks": 412 } ] }
GET/analytics/traffic?days=30stats:read

Откуда и с чего приходят на все ссылки лейбла вместе: источники, устройства, языки, часы (UTC) и ссылки-лидеры. Для планирования постов и рекламы.

{ "period_days": 30,
  "sources":   [ { "source": "instagram.com", "views": 3120 }, { "source": "direct", "views": 1890 } ],
  "devices":   [ { "device": "ios", "views": 4010 }, { "device": "android", "views": 2200 } ],
  "languages": [ { "language": "ru-ru", "views": 5400 }, { "language": "kk-kz", "views": 610 } ],
  "hours_utc": [ { "hour": 14, "views": 520 }, … ],
  "top_links": [ { "id": 77, "title": "Ночной рейс", "views": 1240 } ] }

Здоровье каталога

GET/reports/catalog-healthcatalog:read

Список дел по каталогу, а не оценка ради оценки: релизы без UPC, треки без ISRC, черновики старше месяца, отказы площадок, вышедшие релизы без ссылок на площадки, выключенные смарт-ссылки. score — доля релизов без замечаний. Удобно вешать на дашборд и в еженедельный отчёт.

{ "generated_at": "…", "catalog": { "releases": 24, "live": 18, "drafts": 2 }, "score": 92,
  "counts": { "releases_without_upc": 0, "tracks_without_isrc": 1, "stale_drafts": 1, "dsp_rejections": 0,
              "live_without_store_links": 0, "disabled_smart_links": 0 },
  "issues": { "tracks_without_isrc": [ { "track_id": 65, "track_title": "Ночной рейс", "id": 68, "title": "Ночной рейс", "status": "live" } ],
              "stale_drafts": [ { "id": 91, "title": "Демо", "status": "draft", "created_at": "…", "artist_name": "…" } ], … } }

Команда

GET/teamteam:read

Сотрудники лейбла с ролями и правами, без почты. seats — сколько мест занято и сколько даёт тариф.

{ "members": [ { "user_id": 12, "login": "aigerim", "name": "Айгерим С.", "role": "owner", "role_title": "Владелец",
                 "permissions": ["catalog.view", "catalog.edit", "finance.view", "finance.withdraw", "members.manage", "settings.manage", "label.admin"],
                 "joined_at": "…", "last_seen_at": "…" } ],
  "seats": { "used": 3, "max": 5 } }

Финансы

GET/finance/summaryfinance:read
{ "currency": "USD",
  "balance": { "available": 1284.50, "earned": 3960.10, "withdrawn": 2675.60 },
  "label_terms": { "commission_rate": 0.1, "min_withdrawal": 50 },
  "pending_withdrawals": [ { "id": 88, "amount": 500, "status": "processing", "created_at": "…" } ],
  "recent_payouts": [ { "number": "000214", "period": "Июль 2026", "amount": 812.30, "commission": 54.15, "applied_at": "…" } ] }

Суммы в долларах. Баланс считается так же, как в кабинете лейбла: начислено минус выведено и минус заявки в обработке. Считается по лейблу, а не по человеку, выпустившему ключ.

GET/finance/payoutsfinance:read

Начисления по отчётным периодам: номер выплаты, период, сумма до и после комиссии, зачислено ли. Постранично.

GET/finance/withdrawalsfinance:read

Все заявки на вывод лейбла со статусами pending → processing → paid (или rejected). Постранично.

Обращения

POST/releases/{id}/takedown-requestsupport:write

Заводит обращение на снятие релиза с площадок. Сам релиз не снимает: его разбирает команда, как и из кабинета. Тело: { "reason": "…" } (необязательно). Для черновиков и неотправленных релизов — 409.

POST/supportsupport:write

Письмо в поддержку от лейбла: { "message": "…", "release_id": 68 }. Ответ придёт в кабинет и в Telegram, как обычно. Возвращает { "request": { "id", "status": "pending", "created_at" } }.

GET/support/requestssupport:write

Обращения, заведённые через API, с их статусом и ответом поддержки в поле reply. Так интеграция видит, что стало с запросом на снятие.

Лента событий

GET/eventscatalog:read · тариф «Лейбл»

Те же события, что уходят вебхуками, но опросом: для интеграций без своего сервера, для скриптов по расписанию и для проверки, что вебхук ничего не пропустил. Хранятся 90 дней. Курсор — after: id последнего прочитанного события; types — фильтр через запятую; limit до 200.

GET /events?after=4180&types=release.live,release.dsp_status
{ "events": [ { "id": 4181, "event": "release.live", "created_at": "…",
                "data": { "release": { "id": 68, "title": "Ночной рейс", … }, "upc": "5063…" } } ],
  "next_after": 4181, "has_more": false }

Схема опроса: храните next_after, раз в минуту запрашивайте с ним, обрабатывайте по порядку. Событие из ленты можно отправить на вебхук ещё раз: POST /webhooks/{id}/redeliver с event_id.

Вебхуки

Мы шлём события на ваш https://-адрес. Адрес и события задаются в кабинете или через API; секрет показывается один раз. Отвечайте 2xx быстро и делайте работу после ответа.

СобытиеКогдаВ data
release.approvedрелиз прошёл модерацию и уходит на площадкиrelease, comment
release.rejectedмодерация отклонила релизrelease, comment
release.submittedрелиз у дистрибьютораrelease, upc, isrcs[]
release.dsp_statusстатус на конкретной площадке изменилсяrelease, dsp, stage, detail
release.liveрелиз вышелrelease, upc
presave.signupкто-то подписался на пресейв (без адреса)smart_link, presaves
support.repliedподдержка ответила на обращение из APIrequest {id, type, status, reply}
webhook.testпроверка из кабинета или APImessage

Тело доставки

POST https://ваш-сервер/hooks/distrib
X-Distrib-Event: release.live
X-Distrib-Delivery-Id: 6f1c…-…
X-Distrib-Signature: t=1756800000,v1=9c1a…

{ "id": "6f1c…", "event": "release.live", "created_at": "2026-09-02T14:07:31.000Z",
  "data": { "release": { "id": 68, "title": "Ночной рейс", "type": "single", "status": "live",
                         "upc": "5063…", "release_date": "2026-08-18",
                         "artist": { "id": 401, "name": "Айгерим" } },
            "upc": "5063…" } }

Проверка подписи

v1 = HMAC-SHA256(секрет, t + "." + сырое тело). Проверяйте по сырому телу до разбора JSON и отбрасывайте метки старше пяти минут.

const crypto = require('crypto');
function verify(rawBody, header, secret) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || '');
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const v1 = crypto.createHmac('sha256', secret).update(m[1] + '.' + rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(v1, 'hex'), Buffer.from(m[2], 'hex'));
}
// Express: app.post('/hooks/distrib', express.raw({ type: '*/*' }), (req, res) => { … verify(req.body.toString(), req.get('X-Distrib-Signature'), SECRET) … })
import hmac, hashlib, re, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
    m = re.match(r'^t=(\d+),v1=([0-9a-f]{64})$', header or '')
    if not m or abs(time.time() - int(m.group(1))) > 300:
        return False
    v1 = hmac.new(secret.encode(), (m.group(1) + '.').encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(v1, m.group(2))
function verify(string $raw, string $header, string $secret): bool {
  if (!preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $header, $m)) return false;
  if (abs(time() - (int)$m[1]) > 300) return false;
  $v1 = hash_hmac('sha256', $m[1] . '.' . $raw, $secret);
  return hash_equals($v1, $m[2]);
}

Недоставленное повторяем через 30 секунд и через 5 минут (три попытки). После двадцати сбоев подряд адрес выключается; включить заново можно, добавив его снова. Гарантии «ровно один раз» нет: отсеивайте дубликаты по id события.

Управление через API

GET/webhookswebhooks:manage
POST/webhookswebhooks:manage
POST /webhooks  { "url": "https://ваш-сервер/hooks/distrib", "events": ["release.live", "release.dsp_status"] }
→ 201 { "webhook": { "id": 5, "url": "…", "events": [ … ], "secret": "dbywh_…", "created_at": "…" } }
POST/webhooks/{id}/testwebhooks:manage
GET/webhooks/{id}/deliverieswebhooks:manage
{ "deliveries": [ { "delivery_id": "6f1c…", "event": "release.live", "attempt": 1,
                    "status": 200, "ok": true, "duration_ms": 184, "created_at": "…" } ] }
PATCH/webhooks/{id}webhooks:manage

Поменять url, events или включить обратно после автоматического отключения: { "active": true } обнуляет счётчик сбоев.

POST/webhooks/{id}/rotate-secretwebhooks:manage

Новый секрет в ответе, старый гаснет сразу. Сначала обновите секрет у себя, потом вызывайте: доставки между этими моментами не пройдут проверку.

POST/webhooks/{id}/redeliverwebhooks:manage

{ "event_id": 4181 } из ленты событий. В теле доставки будет "redelivery": true и event_id.

DELETE/webhooks/{id}webhooks:manage
GET/webhooks/eventsлюбой ключ

Справочник: все адреса

Полный список адресов по группам, из той же спецификации, что отдаёт /api/v1/openapi.json: параметры, поля тела и коды ответов. Разверните адрес, чтобы увидеть детали; примеры запросов и ответов — в руководствах выше.

OpenAPI и Postman

Полное машинное описание: /api/v1/openapi.json (OpenAPI 3.0). Импортируйте в Postman, Insomnia или сгенерируйте клиент: npx openapi-typescript https://distribyamka.com/api/v1/openapi.json -o distrib.d.ts.

Песочница

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

curl -H "Authorization: Bearer dby_test_…" https://distribyamka.com/api/v1/me
# → { "sandbox": true, "label": { … }, "key": { "prefix": "dby_test_a1b2c3", "sandbox": true } }

Тексты и кредиты треков

У каждого трека есть текст в трёх видах и кредиты. Текст: GET/PUT /releases/{id}/tracks/{trackId}/lyrics. Пришлите text (обычный текст), lines (строки с таймкодами в секундах) или lrc — из строк с таймкодами обычный текст соберётся сам, а в ответе всегда есть все три вида.

PUT /api/v1/releases/512/tracks/2048/lyrics
{ "lrc": "[00:12.40] Первая строка\n[00:18.05] Вторая строка" }
# → { "ok": true, "text": "Первая строка\nВторая строка", "lines": [{ "time": 12.4, "text": "Первая строка" }, …], "lrc": "…" }

Кредиты: GET/PUT /releases/{id}/tracks/{trackId}/credits — contributors (участники с ролью из /lookups/contributor-roles: Producer, Performer, Vocalist, Mixing Engineer…), writers (авторы музыки и слов) и featured (приглашённые). Хотя бы один участник нужен каждому треку: без него площадки возвращают релиз. Русские названия ролей («продюсер», «сведение») тоже распознаются.

PUT /api/v1/releases/512/tracks/2048/credits
{ "contributors": [{ "name": "Айгерим", "role": "Performer" }, { "name": "Beatmaker", "role": "Producer" }],
  "writers": [{ "name": "Михаил", "roles": ["Composer/Instrumentalist"] }, { "name": "Айгерим", "roles": ["Lyricist"] }] }

Те же поля принимают POST /releases/{id}/tracks и PATCH /releases/{id}/tracks/{trackId}: contributors, version_type, preview_start_sec. Карточка трека целиком — GET /releases/{id}/tracks/{trackId}.

Отчёты по площадкам и странам

Начисления теперь приходят построчно: площадка, страна, отчётный период, количество и сумма. Четыре адреса под скоупом finance:read:

Период задаётся датой или месяцем: ?from=2026-03&to=2026-06. Строки появляются после первых выплат площадок; до этого списки пустые, а в ответе есть note. Сводка по одному релизу вместе со ссылками и статусами площадок — GET /analytics/releases/{id}.

Что дальше

Сделано из прежних планов: создание релизов через API (v2), ключи с ограничением по адресам, песочница, тексты и кредиты, отчёты по площадкам и странам. Вопросы и пожелания: [email protected]

Изменения

2026-09-03

v2.1: песочница с тестовыми ключами dby_test_…; тексты треков с таймкодами и LRC; кредиты треков (участники, авторы, приглашённые); отчёты по площадкам, странам и периодам в /finance; справочники /lookups/*; карточка трека, история релиза, релизы артиста, удаление черновика, /me/usage; у треков version_type и preview_start_sec.

2026-09-02

v2.0: создание релизов через API — черновик, обложка, треки со звуком, правка и порядок треков, отправка на модерацию с отдельным скоупом releases:submit; событие release.review_pending; ключи с ограничением по адресам; справочник всех адресов, собранный из OpenAPI.

2026-09-02

v1.2: скоупы catalog:write и team:read; создание и правка артистов; правка метаданных черновиков; площадки по UPC; оформление и свой адрес смарт-ссылок через API; /reports/catalog-health; /analytics/traffic; /team; события presave.signup и support.replied; Idempotency-Key на всех POST; заголовки Link, X-API-Version, X-RateLimit-Reset.

2026-09-02

v1.1: открытый /status и /plans; тарифы лейблов с лимитами и правами, тарифный лимит запросов; /tracks, /search, фильтр upc; экспорт каталога CSV; лента событий /events; /support/requests; /finance/payouts и /finance/withdrawals, баланс строго по лейблу; вебхуки: правка, смена секрета, повторная доставка; защита от адресов внутрь сети и редиректов; Idempotency-Key проверяет тело (422); лимит неудачных попыток входа по адресу.

2026-09-02

v1.0: ключи со скоупами, каталог, статусы по площадкам, смарт-ссылки (создание, правка, отключение, статистика), сводная аналитика, финансы, обращения, вебхуки с подписью и журналом доставок, Idempotency-Key, OpenAPI.