Distovia Partner API

Read your catalogue, create and submit releases, pull royalties, and receive events — for labels and distributors working on top of Distovia.

Getting a key

Ask your account manager. Keys are issued per account with either read or read+write scope, and the secret is shown once at creation — we store only its hash and cannot recover it.

Authentication

curl https://app.bemusix.com/api/partner/v1/me \
  -H "Authorization: Bearer bk_live_your_key_here"

A revoked key stops working immediately, with 401.

Base URL

https://app.bemusix.com/api/partner/v1

Endpoints

PathScopeWhat it does
GET /me read The account this key belongs to.
{ user }
GET /releases read Every release on the account, newest first.
{ releases }
GET /releases/:id read One release with its tracks and per-store delivery state.
{ release }
POST /releases
{ "title": "Nodir Kule", "primaryArtist": "Hasan", "genre": "Folk", "releaseDate": "2026-09-01" }
write Create a release as a draft.
{ release }
POST /releases/:id/submit write Send a completed draft for review. Fails with the missing fields named.
{ release }
GET /earnings read Royalty lines, filterable by month.
{ earnings, totals }
GET /webhooks read The webhooks registered for this account.
{ webhooks }
POST /webhooks
{ "url": "https://you.example/hooks/backstage", "events": ["release.status"] }
write Register a URL to receive events.
{ webhook, secret }
DELETE /webhooks/:id write Stop sending to a webhook.
{ ok }

Webhooks

Register a URL and we POST a JSON body to it when something happens. Every request carries an x-backstage-signature header: the hex HMAC-SHA256 of the raw body, keyed with the secret you were given when the webhook was created.

const expected = crypto
  .createHmac("sha256", secret)
  .update(rawBody)
  .digest("hex");
if (expected !== req.headers["x-backstage-signature"]) return res.sendStatus(401);

We retry once on failure. Reply 2xx quickly and do your work afterwards.

EventSent when
release.statusA release moves between states — submitted, approved, delivered, live, taken down.
release.deliveredA store has accepted a delivery.
payout.paidA payout has been sent.

Errors

Errors are JSON and shaped the same everywhere. fields is present when something specific was wrong with the payload.

{
  "error": {
    "code": "BAD_REQUEST",
    "message": "Release is incomplete",
    "fields": { "artwork": "Artwork is required" }
  }
}

Rate limits

Requests are limited per key. A 429 means slow down — retry after a short pause rather than immediately.

Releases created through the API land as drafts and go through the same review as everything else. Submitting one that is incomplete returns 400 with each missing field named, so you can surface it in your own UI.