From a brand-new sandbox account to a live release delivered to every DSP — in about 6 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 → api.tonegrid.pro and the same flow ships music for real.
timer ~6 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_ API key — minted in step 2>"
export REL="" # release UUID — populated after step 4
export TRACK="" # track UUID — populated after step 5
1
Create a sandbox account
1 min
Sign up on the sandbox to get a tenant. No card required. Sandbox is identical to production but isolated — no real money, no real DSP pushes, but full lifecycle simulation.
Save the access_token — you'll use it as a Bearer token in every subsequent call. It's a JWT with a 1-hour TTL; the refresh_token is set as an httpOnly cookie scoped to /api/auth.
2
Mint an API key (recommended for backend integrations)
1 min
For server-to-server work, prefer a long-lived tgk_ API key over the JWT. Key never expires, can be rotated independently, and supports per-key IP allow-lists.
The raw key is returned once at creation. Store it in your secret manager immediately. We only persist its SHA-256 hash, so a lost key cannot be recovered — only revoked.
3
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.
4
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.
5
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/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/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.
6
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.
7
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.
8
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.
9
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.