From a brand-new sandbox account to a live release delivered to every DSP — in about 5 minutes of copy-paste. Every step runs against the real sandbox; lifecycle webhooks fire on the real channels. When you're ready for production, swap api-sandbox.tonegrid.pro/api/v1 → api.tonegrid.pro/v1 and the same flow ships music for real.
timer ~5 minutesscience sandbox-safe (no real money / no real DSP push)code copy-paste curl
Set these env vars first
export API="https://api-sandbox.tonegrid.pro/api"
export TG="<your tgk_ sandbox API key — see step 1>"
export REL="" # release UUID — populated after step 3
export TRACK="" # track UUID — populated after step 4
1
Get your sandbox API key
1 min
Every call authenticates with a tgk_ API key sent as a Bearer token. There is no self-service signup endpoint: keys are issued per workspace. Workspace owners on the Pro, Scale, API Pro and API Scale plans (see API pricing) create, rotate and revoke keys under Dashboard → API Keys; on other plans, ask ToneGrid support and we will issue one. Use the Sandbox key while you build. Sandbox is isolated from production: no real money and no real store deliveries, but the full lifecycle is simulated.
The raw key is shown once when it is created. Store it in your secret manager straight away: we keep only a hash, so a lost key cannot be recovered, only revoked and replaced. Keep keys server-side and never ship one in browser or mobile code.
2
Create an artist
An artist is the named entity all releases roll up under. Slugs are unique per tenant.
Capture the artist uuid — you'll attach releases to it in the next step.
3
Create a release (draft)
1 min
New releases start in draft. You can edit freely until you call /submit. Set a release date at least 7 days in the future to give DSPs ingest lead time.
Pass localized_titles as a BCP-47 keyed object (eg {"fr": "...", "yo": "..."}) and ToneGrid will emit per-locale DisplayTitle blocks in the DDEX automatically.
4
Add a track + upload audio
1 min
Track metadata first, then upload the audio file. Files over 100 MB trigger AWS S3 Multipart Upload automatically (peak memory pinned at 16 MB regardless of file size).
bash
# 1. Create the track row
curl -X POST "https://api-sandbox.tonegrid.pro/api/v1/releases/$REL/tracks" \
-H "Authorization: Bearer $TG" \
-d '{
"title": "Lagos Skyline",
"artist_id": "a-3c2b...",
"isrc": "NGTON2600001",
"duration_seconds": 218,
"track_number": 1
}'
# 2. Upload the audio (multipart form-data, field name `audio`)
curl -X POST "https://api-sandbox.tonegrid.pro/api/v1/tracks/$TRACK/audio" \
-H "Authorization: Bearer $TG" \
-F "audio=@./lagos-skyline.wav"
ffprobe runs on the uploaded file and back-fills audio_format / sample-rate / bit-depth / channels / MD5 in ~5 seconds. Then the audio-validator gates: tracks under 44.1 kHz / 16-bit / 2 channels are auto-rejected.
5
Attach DSPs
Tell ToneGrid which stores to ship to. List all active DSPs via GET /dsps first.
Pass per-DSP overrides (territory, monetization, pricing, release-date) via PUT /releases/:uuid/dsps/:dsp_slug AFTER attaching — useful for Japan-only physical exclusives, US-only pre-orders, etc.
6
Validate the DDEX before approving (optional but recommended)
Run the generated NewReleaseMessage through the official DDEX ERN 4.3 XSD validator. Catches schema-breaking metadata before any DSP sees it.
Hook this into your CI: if validation.valid !== true, surface the per-error line/column/message in your release-approval UI and block the user from submitting. Most aggregators ship blind — ToneGrid lets you XSD-check before push.
7
Approve + watch the lifecycle flow
2 min
Approving a release queues per-DSP delivery and fires release.dsp.<slug>.submitted for each one. The DDEX worker advances rows through submitted → accepted → live over ~2 minutes (sim mode); on real DSPs it's identical but driven by real ingestion ACKs.
// Right after approve:
{ "dsp": "spotify", "status": "submitted" }
{ "dsp": "apple-music", "status": "submitted" }
{ "dsp": "tidal", "status": "submitted" }
// ... 9 rows
// T+1 min: status flips to "accepted" + release.dsp.<slug>.accepted webhook fires
// T+2 min: status flips to "live" + dsp_release_id populated + release.dsp.<slug>.live fires
{ "dsp": "spotify", "status": "live", "dsp_release_id": "SIM-spot-9f3a2c", "live_at": "2026-06-17T00:02:11Z" }
lightbulb
Subscribe to webhooks via POST /webhooks with events: ["release.dsp.*.*"] to be pushed every transition instead of polling. Use the webhook tester to verify your HMAC signature handling before going live.
8
Preview the literal XML being shipped
See the exact NewReleaseMessage XML each DSP receives. This is the supply-chain proof — what you see is byte-identical to what lands at the DSP's ingestion endpoint.
For takedowns use POST /releases/:uuid/ddex/purge (PurgeReleaseMessage); for metadata corrections use POST /releases/:uuid/ddex/redeliver (re-pushes NewReleaseMessage on the same MessageThreadId). Both are in the DDEX section.
That's it. You shipped a release.
The same flow runs unchanged on production — swap the host, plug in your prod API key, and you're shipping real music to real stores. Subscribe to webhooks (or verify your signature handling first) and you're done.