API лейбла
Каталог, статусы по площадкам, смарт-ссылки и аналитика вашего лейбла по ключу. Для сайта, CRM и дашбордов. Плюс события на ваш сервер.
/api/v1, поля в snake_case, даты в ISO 8601.Быстрый старт
- Откройте кабинет лейбла → API и вебхуки → выпустите ключ с нужными правами.Ключ показывается один раз. У нас хранится только его хэш.
- Сделайте первый запрос.Ключ передаётся заголовком
Authorization: Bearer dby_…. - Подпишитесь на события вместо опроса.Одобрение, отправка, выход на площадках придут на ваш адрес сами.
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
Открытый адрес без ключа, для мониторинга и для вашей страницы статуса. Отвечает 200, пока база доступна, и 503, когда нет. Обновляется на этой странице каждые 30 секунд.
/statusбез ключа · 30 запросов в минуту с адреса{ "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 (без ключа); здесь — состав на сегодня.
- Каталог до 100 релизов, 2 сотрудника
- Комиссия 25%
- 50 смарт-ссылок
- API: чтение каталога, ссылок, статистики
- 1 ключ, 60 запросов/мин
- Каталог до 500 релизов, 5 сотрудников
- Комиссия 15%
- Ссылки без ограничений
- Полный API и лента событий
- 5 ключей, 3 вебхука, 120/мин
- Свой Telegram-бот и персональная почта
- Каталог до 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" }
| HTTP | code | Когда |
|---|---|---|
| 400 | invalid | ошибка в данных; имя поля в field |
| 401 | unauthorized | нет ключа, не найден или отозван |
| 403 | forbidden | у ключа нет права; required_scope говорит какого |
| 404 | not_found | объект не принадлежит лейблу или не существует |
| 403 | plan_limit · plan_required | лимит тарифа исчерпан или функции нет на тарифе; required_plan говорит, с какого можно |
| 409 | conflict | действие невозможно в текущем состоянии |
| 422 | idempotency_mismatch | тот же Idempotency-Key с другим телом запроса |
| 429 | rate_limited | слишком много запросов, или слишком много неудачных попыток входа с адреса |
Безопасность
Что делаем мы и что стоит сделать вам, чтобы ключ и данные лейбла остались вашими.
С нашей стороны
- Ключ хранится хэшем. SHA-256 и первые символы для узнавания; сам ключ восстановить нельзя, только выпустить новый.
- Ключ ходит только по HTTPS. Запрос по http не дойдёт до API.
- Каждый маршрут — ровно один скоуп, и ключ без него получает
403с именем недостающего. Тариф ограничивает права ключа даже после смены тарифа. - Данные строго в пределах лейбла. Чужой релиз, ссылка или вебхук по id отвечают
404, а не403: по ответу нельзя узнать, что объект существует. - Лимиты. Запросы в минуту по ключу; неудачные попытки входа по адресу (60 в минуту); открытые адреса — по адресу.
- Вебхуки подписаны HMAC-SHA256 с меткой времени, секрет шифруется в базе и показывается один раз. Адрес вебхука проверяется на этапе добавления: только https, не на нас, не в частные сети и не в облачные метаданные; редиректы не выполняются.
- Idempotency-Key привязан к ключу и к телу запроса: повтор с другим телом —
422, а не тихий старый ответ. - Ничего лишнего наружу. Ответы с
Cache-Control: no-store; в логах нет ключей и секретов; тела запросов не больше 100 КБ.
С вашей стороны
- Вызывайте API со своего сервера. Ключ в JavaScript страницы — это ключ у всех, кто открыл страницу.
- Один ключ на одну интеграцию и минимальные права: сайту не нужен
finance:read. - Ключ в переменных окружения или менеджере секретов, не в репозитории. Утёк — отзовите в кабинете, это мгновенно.
- Проверяйте подпись вебхука по сырому телу и отбрасывайте метки старше пяти минут. Отсеивайте повторы по
idсобытия. - Отвечайте на вебхук быстро и делайте работу после ответа: у нас таймаут 10 секунд.
Нашли уязвимость? Напишите на [email protected] с темой «security». Отвечаем в тот же день.
Кто я
/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" }
Артисты
/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 }
/artists/{id}catalog:readАртист и все его релизы одним ответом: { "artist": { …, "releases": [ … ] } }.
Создание и правка
/artistscatalog:writePOST /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 не плодила дублей. Артисты без предела на всех тарифах.
/artists/{id}catalog:writeЛюбое подмножество: name, spotify_url, apple_url (null очищает).
Релизы
/releasescatalog:read| Параметр | Значение |
|---|---|
status | draft · submitting · submitted · live · failed · takedown |
artist_id | только релизы артиста |
upc | найти релиз по UPC: upc=5063000000012 |
ids | через запятую, до 100: ids=68,69,93 |
updated_since | ISO-время: только изменённые после него. Удобно для синхронизации по расписанию |
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 }
/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": "…" } ] } }
Правка черновика
/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.
- Создайте черновик
POST /releasesс артистом, типом, названием, датой и жанром. - Загрузите обложку
PUT /releases/{id}/cover: квадрат от 1400 px, лучше 3000×3000, JPG или PNG. - Добавьте треки
POST /releases/{id}/tracks: WAV или FLAC плюс название, ISRC, авторы. - Отправьте на модерацию
POST /releases/{id}/submitсconfirm_rights: true. Нужен подписанный договор лейбла.
/releasescatalog:writePOST /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 мы присвоим свой при отправке. Музыка, созданная нейросетями, не принимается. Черновик считается в лимит каталога тарифа.
/releases/{id}/covercatalog:writecurl -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 с размерами в ответе.
/releases/{id}/trackscatalog:writecurl -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}. У сингла не больше трёх треков.
/releases/{id}/tracks/{trackId}catalog:write/releases/{id}/tracks/{trackId}/audiocatalog:write/releases/{id}/tracks/{trackId}catalog:writeМетаданные и звук меняются, пока релиз черновик или ждёт модерации. Удалять треки можно только из черновика.
/releases/{id}/submitreleases:submitPOST /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 — намеренно. Ключ, который заводит черновики и грузит файлы, не обязан уметь отправлять их на площадки: разделите роли между интеграциями./releases/{id}/dsp-statuscatalog:readТолько статусы по площадкам, без остального. Стадии: submitted → accepted → live; отказы rejected, failed; снятие taken_down. Приходят от дистрибьютора по мере продвижения релиза, поэтому у свежего релиза список может быть пустым.
Треки
/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 }
Поиск
/search?q=…catalog:readОдин запрос по релизам, артистам и трекам сразу: по названию, имени, UPC или ISRC. От двух символов, до десяти результатов каждого вида.
{ "query": "ночной", "releases": [ … ], "artists": [ … ],
"tracks": [ { "id": 65, "title": "Ночной рейс", "isrc": "QZK6P2600123", "release_id": 68, "release_title": "Ночной рейс" } ] }
Экспорт каталога
/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_…"
Смарт-ссылки
/smart-linkslinks:read/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
/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/…" } ] }
Создание и правка
/smart-linkslinks:writecurl -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();/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/…" } ] }
/smart-links/{id}links:writeВыключает страницу (она начинает отвечать 404). Данные и статистика остаются: включить обратно можно через PATCH { "enabled": true }.
Статистика ссылки
/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.
Аналитика по лейблу
/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 } ] }
/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 } ] }
Здоровье каталога
/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": "…" } ], … } }
Команда
/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 } }
Финансы
/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": "…" } ] }
Суммы в долларах. Баланс считается так же, как в кабинете лейбла: начислено минус выведено и минус заявки в обработке. Считается по лейблу, а не по человеку, выпустившему ключ.
/finance/payoutsfinance:readНачисления по отчётным периодам: номер выплаты, период, сумма до и после комиссии, зачислено ли. Постранично.
/finance/withdrawalsfinance:readВсе заявки на вывод лейбла со статусами pending → processing → paid (или rejected). Постранично.
Обращения
/releases/{id}/takedown-requestsupport:writeЗаводит обращение на снятие релиза с площадок. Сам релиз не снимает: его разбирает команда, как и из кабинета. Тело: { "reason": "…" } (необязательно). Для черновиков и неотправленных релизов — 409.
/supportsupport:writeПисьмо в поддержку от лейбла: { "message": "…", "release_id": 68 }. Ответ придёт в кабинет и в Telegram, как обычно. Возвращает { "request": { "id", "status": "pending", "created_at" } }.
/support/requestssupport:writeОбращения, заведённые через API, с их статусом и ответом поддержки в поле reply. Так интеграция видит, что стало с запросом на снятие.
Лента событий
/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 | поддержка ответила на обращение из API | request {id, type, status, reply} |
webhook.test | проверка из кабинета или API | message |
Тело доставки
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
/webhookswebhooks:manage/webhookswebhooks:managePOST /webhooks { "url": "https://ваш-сервер/hooks/distrib", "events": ["release.live", "release.dsp_status"] }
→ 201 { "webhook": { "id": 5, "url": "…", "events": [ … ], "secret": "dbywh_…", "created_at": "…" } }
/webhooks/{id}/testwebhooks:manage/webhooks/{id}/deliverieswebhooks:manage{ "deliveries": [ { "delivery_id": "6f1c…", "event": "release.live", "attempt": 1,
"status": 200, "ok": true, "duration_ms": 184, "created_at": "…" } ] }
/webhooks/{id}webhooks:manageПоменять url, events или включить обратно после автоматического отключения: { "active": true } обнуляет счётчик сбоев.
/webhooks/{id}/rotate-secretwebhooks:manageНовый секрет в ответе, старый гаснет сразу. Сначала обновите секрет у себя, потом вызывайте: доставки между этими моментами не пройдут проверку.
/webhooks/{id}/redeliverwebhooks:manage{ "event_id": 4181 } из ленты событий. В теле доставки будет "redelivery": true и event_id.
/webhooks/{id}webhooks:manage/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_… и не входит в лимит ключей тарифа. Такой ключ работает с копией каталога, которая делается при первом запросе: те же артисты, релизы и треки, но с пометкой. Настоящие релизы через него не видны и не меняются.
- Всё, что создаёт тестовый ключ, помечено
sandbox: true, а в каждом ответе есть заголовокX-Sandbox: true. POST /releases/{id}/submitничего не отправляет дистрибьютору: релиз сразу «принят», а через 45 секунд становитсяlive, и приходят событияrelease.dsp_statusиrelease.liveс полемsandbox: true.- Обращения в поддержку и запросы на снятие возвращают ответ, но тикет не создают. Финансы и отчёты по площадкам отдают правдоподобные выдуманные цифры.
- Вебхуки общие: подпишитесь один раз и отличайте учебные события по
data.sandbox. Лента/eventsу тестового ключа показывает только события песочницы. - Копия делается один раз. Чтобы начать заново, удалите тестовые черновики через
DELETE /releases/{id}или напишите в поддержку.
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:
GET /finance/platforms— суммы по площадкам с долей от периода;GET /finance/countries— по странам (ISO-коды);GET /finance/statements— по отчётным месяцам: сумма, число площадок и стран;GET /finance/lines— сами строки с привязкой к релизу и треку; фильтрыrelease_id,dsp,country,from,to.
Период задаётся датой или месяцем: ?from=2026-03&to=2026-06. Строки появляются после первых выплат площадок; до этого списки пустые, а в ответе есть note. Сводка по одному релизу вместе со ссылками и статусами площадок — GET /analytics/releases/{id}.
Что дальше
- Отчёты по трекам внутри релиза в
/finance/linesпоявятся автоматически, когда дистрибьютор начнёт передавать ISRC в строках. - Массовые операции: правка метаданных у нескольких релизов одним запросом.
- Экспорт отчётов в CSV прямо из
/finance.
Сделано из прежних планов: создание релизов через API (v2), ключи с ограничением по адресам, песочница, тексты и кредиты, отчёты по площадкам и странам. Вопросы и пожелания: [email protected]
Изменения
v2.1: песочница с тестовыми ключами dby_test_…; тексты треков с таймкодами и LRC; кредиты треков (участники, авторы, приглашённые); отчёты по площадкам, странам и периодам в /finance; справочники /lookups/*; карточка трека, история релиза, релизы артиста, удаление черновика, /me/usage; у треков version_type и preview_start_sec.
v2.0: создание релизов через API — черновик, обложка, треки со звуком, правка и порядок треков, отправка на модерацию с отдельным скоупом releases:submit; событие release.review_pending; ключи с ограничением по адресам; справочник всех адресов, собранный из OpenAPI.
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.
v1.1: открытый /status и /plans; тарифы лейблов с лимитами и правами, тарифный лимит запросов; /tracks, /search, фильтр upc; экспорт каталога CSV; лента событий /events; /support/requests; /finance/payouts и /finance/withdrawals, баланс строго по лейблу; вебхуки: правка, смена секрета, повторная доставка; защита от адресов внутрь сети и редиректов; Idempotency-Key проверяет тело (422); лимит неудачных попыток входа по адресу.
v1.0: ключи со скоупами, каталог, статусы по площадкам, смарт-ссылки (создание, правка, отключение, статистика), сводная аналитика, финансы, обращения, вебхуки с подписью и журналом доставок, Idempotency-Key, OpenAPI.