Tutorial¶
End-to-end walkthrough of distributing a product through the limbo/Beat API. The flow is:
authenticate → create label → create product → create tracks → create records → assign rights → update & validate the default offer → activate the product → create a DSP-specific offer → create a send task → poll the send task status.
All resource endpoints live under /api/v1 and follow the JSON:API convention (Content-Type: application/vnd.api+json). Replace https://domain.com with your environment host and <token> with the bearer token obtained in the first step.
Authentication¶
@username = email@email.com
@password = password
POST https://domain.com/oauth/token
Content-Type: application/json
{
"grant_type": "password",
"email": "{{username}}",
"password": "{{password}}"
}
Note: Your distributor ID and company ID are delivered together with your credentials.
See authentication.md.
Create Label¶
A label must be associated with a company.
@urlBase = https://domain.com/api/v1
@token = <token>
@company = 1
POST {{urlBase}}/labels
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "labels",
"attributes": {
"name": "Label name",
"description": "Label description"
},
"relationships": {
"company": {
"data": {
"type": "companies",
"id": "{{company}}"
}
}
}
}
}
See labels.md.
Create Product¶
The product must be associated with a label and must declare its product type (Album, Single, etc.) and its language.
@urlBase = https://domain.com/api/v1
@token = <token>
@label = 1
@product_type = 1
@language = 5
POST {{urlBase}}/products
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "products",
"attributes": {
"name": "Product title",
"upc-code": "8088111222333",
"release-date": "2023-10-01",
"front-cover": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...",
"copyright-line": "Records Music",
"production-line": "2023 Records Music",
"production-year": 2023,
"status": "draft",
"artist": [
"Artist name"
]
},
"relationships": {
"product-type": {
"data": {
"type": "product-types",
"id": "{{product_type}}"
}
},
"language": {
"data": {
"type": "languages",
"id": "{{language}}"
}
},
"label": {
"data": {
"type": "labels",
"id": "{{label}}"
}
}
}
}
}
Important: On creation the product
statusmust be"draft". The product is moved to"active"later, in a separate update (see Validate and update the product). Genre and subgenre are not product attributes — they are set on the offer.
See products.md, labels.md, product-types.md, languages.md.
Create Track¶
Create as many tracks as the album contains. Every track is associated with a product and declares its audio language.
@urlBase = https://domain.com/api/v1
@token = <token>
@product = 1
@language = 5
POST {{urlBase}}/tracks
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "tracks",
"attributes": {
"title": "Track title",
"track-number": 1,
"volume-number": 1,
"isrc-code": "ESA112314511",
"artist": ["Artist name"]
},
"relationships": {
"product": {
"data": {
"type": "products",
"id": "{{product}}"
}
},
"audio-language": {
"data": {
"type": "languages",
"id": "{{language}}"
}
}
}
}
}
See tracks.md, products.md, languages.md.
Create Record¶
A record holds the FLAC audio file associated with a track (creator). Create as many records as there are tracks associated with the product.
@urlBase = https://domain.com/api/v1
@token = <token>
@track = 1
POST {{urlBase}}/records
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "records",
"attributes": {
"new-file": "ZkxhQwAAACISABIAAAAQACNMC7gDcAABN0su1..."
},
"relationships": {
"creator": {
"data": {
"type": "tracks",
"id": "{{ track }}"
}
}
}
}
}
See records.md, tracks.md.
Create Right¶
Rights are associated with an email, declaring which distributor they belong to, the product, the DSPs, and the territories (either continents or territories).
@urlBase = https://domain.com/api/v1
@token = <token>
@distributor = 1
@product = 1
@dsp = 23
@continent = 1
POST {{urlBase}}/rights
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "rights",
"attributes": {
"action": "add",
"user-email": "user-email@user-email.com"
},
"relationships": {
"distributor": {
"data": {
"type": "distributors",
"id": "{{ distributor }}"
}
},
"distributed": {
"data": {
"type": "products",
"id": "{{ product }}"
}
},
"dsps": {
"data": [{
"type": "dsps",
"id": "{{ dsp }}"
}]
},
"continents": {
"data": [{
"type": "continents",
"id": "{{ continent }}"
}]
}
}
}
}
Note: The product relationship is named
distributed(notproduct). At least one DSP is required.
See products.md, continents.md, territories.md, dsps.md.
Update and validate the default offer¶
Each product has a default offer. This offer must be updated and validated by changing its status to active.
The default offer requires a release date, genre and subgenre.
Note: The default offer must NOT contain any DSP-related information.
Find the default offer¶
@urlBase = https://domain.com/api/v1
@token = <token>
@product = 1
GET {{urlBase}}/offers?filter[product-id]={{product}}&filter[is-default]=true
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
Look up DistributorProductSubgenres¶
@urlBase = https://domain.com/api/v1
@token = <token>
GET {{urlBase}}/distributor-product-subgenres
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
See distributor-product-subgenres.md.
Update the default offer¶
Assign a release date, genre and subgenre.
@urlBase = https://domain.com/api/v1
@token = <token>
@default_offer = 1
@subgenre = 1
@subgenre2 = 2
PATCH {{urlBase}}/offers/{{default_offer}}
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "offers",
"id": "{{ default_offer }}",
"attributes": {
"release-date": "2023-10-01"
},
"relationships": {
"subgenre": {
"data": {
"type": "distributor-product-subgenres",
"id": "{{ subgenre }}"
}
},
"subgenre2": {
"data": {
"type": "distributor-product-subgenres",
"id": "{{ subgenre2 }}"
}
}
}
}
}
See offers.md, distributor-product-subgenres.md.
Validate the default offer by setting its status to active¶
If the offer data is complete, it becomes active.
@urlBase = https://domain.com/api/v1
@token = <token>
@default_offer = 1
PATCH {{urlBase}}/offers/{{default_offer}}
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "offers",
"id": "{{ default_offer }}",
"attributes": {
"status": "active"
}
}
}
See offers.md.
Validate and update the product¶
If the product is complete, set its status to active.
@urlBase = https://domain.com/api/v1
@token = <token>
@product = 1
PATCH {{urlBase}}/products/{{product}}
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "products",
"id": "{{ product }}",
"attributes": {
"status": "active"
}
}
}
See products.md.
Create a DSP-specific offer¶
The specific offer is the one that has the DSP(s) you want to distribute the product to associated with it.
Important! The status must be active and you must indicate that it is not a default offer.
status:
"active"is-default:false
Look up DspUploadIdentifications¶
@urlBase = https://domain.com/api/v1
@token = <token>
GET {{urlBase}}/dsp-upload-identifications
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
Note: Filter by
filter[dsp-id]for reliable results.
See dsp-upload-identifications.md.
Create the offer¶
@urlBase = https://domain.com/api/v1
@token = <token>
@product = 1
@subgenre = 1
@subgenre2 = 2
@dsp_upload_identification = 1876
POST {{urlBase}}/offers
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "offers",
"attributes": {
"release-date": "2023-10-01",
"is-default": false,
"status": "active",
"usage": {
"download": true,
"copy_scan": true,
"on_demand_stream": true,
"non_interactive_stream": true
}
},
"relationships": {
"product": {
"data": {
"type": "products",
"id": "{{ product }}"
}
},
"subgenre": {
"data": {
"type": "distributor-product-subgenres",
"id": "{{subgenre}}"
}
},
"subgenre2": {
"data": {
"type": "distributor-product-subgenres",
"id": "{{subgenre2}}"
}
},
"dsp-upload-identifications": {
"data": [{
"type": "dsp-upload-identifications",
"id": "{{dsp_upload_identification}}"
}]
}
}
}
}
Important: A non-default offer set to
"active"requires at least onedsp-upload-identifications. If omitted, the request is rejected with a422.
See offers.md, products.md, distributor-product-subgenres.md, dsp-upload-identifications.md.
Send Task¶
To deliver to the DSP, create a task indicating the product and which DSP to send it to.
Note: Deliveries to DSPs are performed individually (one task per DSP).
The delivery types are:
Important: The product must already be
"active"before creating the send task (except fordeletedeliveries). Whendeliverableistrue, the delivery starts on creation — there is no separate "trigger" step. You cannot create a second task for the same product + DSP + delivery type while a previous one is still in progress (i.e. noterrororcompleted).
@urlBase = https://domain.com/api/v1
@token = <token>
@product = 1
@dsp_upload_identification = 1876
POST {{urlBase}}/send-tasks
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
{
"data": {
"type": "send-tasks",
"attributes": {
"deliverable": true,
"delivery-type": "delivery"
},
"relationships": {
"product": {
"data": {
"type": "products",
"id": "{{ product }}"
}
},
"dsp-upload-identification": {
"data": {
"type": "dsp-upload-identifications",
"id": "{{ dsp_upload_identification }}"
}
}
}
}
}
See send-tasks.md, products.md, dsp-upload-identifications.md.
Check the task status¶
@urlBase = https://domain.com/api/v1
@token = <token>
@send_task_id = 1
GET {{urlBase}}/send-tasks/{{send_task_id}}
Content-Type: application/vnd.api+json
Authorization: Bearer {{token}}
Poll this endpoint until the task reaches a terminal state. The status attribute describes progress; ack-status-message carries the acknowledgment payload returned by the DSP.
Send task status values¶
The platform uses the following values:
status |
Meaning |
|---|---|
pending |
Initial state — the task has been created but the delivery has not started |
waiting |
Delivery in progress — awaiting the DSP acknowledgment |
procesing |
The delivery is being processed |
completed |
The DSP acknowledged the delivery successfully — terminal success state |
error |
The delivery or acknowledgment failed |
A successful delivery ends with status: "completed". A failed one ends with status: "error".
Example response¶
"attributes": {
"deliverable": true,
"delivery-type": "delivery",
"status": "completed",
"send-date": "2023-08-14 18:11:51",
"batch-id": "1ccf3f14-03a1-4001-9218-f7e06c725a13",
"ack-status-message": {
"FileStatus": [
"FileOK"
]
},
"sent-at": null,
"packaged-by": null,
"sent-by": null,
"message-id": "PADPIDA200010120333312786830",
"sent-on": "20230814181151288",
"created-by": "robot",
"created-at": "2023-08-14T18:11:48.000000Z",
"updated-at": "2023-08-14T18:12:33.000000Z"
}
See send-tasks.md.