Beta Release of TrackOrigin is live. We are still running verifications throughout June.
[ MUSIC PLATFORM INTEGRATION 101 ]

FOR PLATFORMS.
STEP BY STEP.

For DSPs, distributors, marketplaces and catalogue tools. This is the no-assumptions path: create an account, get a key, show the seal next to verified tracks, and test it all in a sandbox before you touch production. Follow it in order.

TrackOrigin Origin Seal
[ THE WHOLE PATH ]

SEVEN STEPS.

Steps 1–5 you can do today, end to end, in a sandbox. Step 6 is the deeper "prove the audio" integration — fully self-serve, straight from these docs. Step 7 is launch.

01

Create an account

A normal TrackOrigin signup — it's how you get API access.

02

Get API keys

One publishable, one secret. Two minutes.

03

Know the two layers

Showing the mark vs proving the audio.

04

Show the seal

One field at upload + one line in your track row.

05

Test in sandbox

Prove the flow with a test certificate.

06

Prove the audio

Verify served audio → bind → gate the seal. Self-serve.

07

Go live

Checklist, then ship.

[ STEP 1 ]

CREATE AN ACCOUNT.

Sign up for a normal TrackOrigin account — this is what gives your platform API access. It's free to create. Create your account → then come back here.

[ STEP 2 ]

GET YOUR API KEYS.

Once you're logged in, open API & Webhooks (it's in the top nav next to Dashboard, and in your profile menu). Mint two keys:

KEY TYPE
USE IT FOR
Publishable · to_pk_live_…
Read-only, browser-safe. Front-end / widget calls. 600 req/min. Fine to ship in client code.
Secret · to_sk_live_…
Server-side only. Authenticated calls (exact scores, future writes). 3,000 req/min. Never put this in a client bundle.
[ STEP 3 ]

KNOW THE TWO LAYERS.

This trips everyone up, so read it once. There are two separate things:

LAYER 1 · PRESENTATION

Show the mark

Render the seal next to a track from its certificate ID. Confirms the cert is real and active, links to the public proof page. This is Steps 4–5 — do it today.

LAYER 2 · PROOF

Prove the audio

Verify that the file you serve actually matches the certificate's master, and only then show "Verified human-made." This is Step 6 — the deeper integration.

[ STEP 4 ]

SHOW THE SEAL.

Two small changes. (a) Add one optional field to your upload form so artists can attach their certificate ID. (b) In your track-listing template, render the mini Disc when a track has one.

(a) Upload form — store the cert ID with the track
<label>TrackOrigin Certificate ID (optional)</label>
<input name="trackorigin_cert_id" placeholder="cer_01KS9QWHV7PHETYGQAYMHB3DM6"
       pattern="^cer_[A-Za-z0-9_]+$">
<!-- IDs are cer_ + an uppercase ULID (e.g. cer_01KS9QWHV7PHETYGQAYMHB3DM6).
     validate ^cer_[A-Za-z0-9_]+$ and save it on the track row -->
(b) Track row — render the Disc when present (Jinja/Django/Twig)
{% for track in tracks %}
  <span class="title">{{ track.title }}</span>
  {% if track.trackorigin_cert_id %}
    <span data-trackorigin-cert="{{ track.trackorigin_cert_id }}"
          data-variant="mini" data-size="16"></span>
  {% endif %}
{% endfor %}

<!-- load once per page -->
<script src="https://trackorigin.io/static/embed-widget.js" async></script>
→ The widget reads the cert state at render time and caches it ~5 min. No per-row API calls from your server. React/Vue: after dynamically adding rows, call window.TrackOrigin.mountAll().
[ STEP 5 ]

TEST IN SANDBOX.

Don't test against real certs. Use the built-in sandbox certificate cer_test_synthetic (works for anyone, no key needed) to prove your wiring, then seed your own fixtures with your secret key.

Read a sandbox certificate (no auth)
curl https://trackorigin.io/api/v1/certificates/cer_test_synthetic
curl "https://trackorigin.io/api/v1/certificates/cer_test_synthetic/status?no_cache=true"
# Render the widget against cer_test_synthetic in a test page and confirm
# the Disc shows and links to /cert/cer_test_synthetic.
[ STEP 6 ]

PROVE THE AUDIO.

This is Layer 2 — what makes a public "Verified human-made" seal honest. Your platform verifies the exact audio it serves against the certificate, and only shows the seal when the whole chain holds. Fully self-serve: integrate from these docs, test in sandbox, ship — no approval call, no account manager, no one to email.

A

Content-address

Store each served file by the SHA-256 of its exact bytes.

B

Verify each artifact

Fingerprint the served file's own bytes; call verify-audio.

C

Bind

Re-check the cert fresh, then write an immutable binding.

D

Gate render

Show the seal only while the binding, cert and audio all still match — never from the pasted ID.

B + C · Verify the served file, then bind (server-side, secret key)
# Fingerprint the SERVED file's own bytes  (install: brew install chromaprint)
# IMPORTANT: use -raw. TrackOrigin compares the RAW uint32 fingerprint packed
# little-endian then base64'd — NOT fpcalc's default compressed string. Both
# sides must encode identically or every score comes back as a non-match.
fpcalc -raw -json served/track.mp3   # -> { "duration": 187, "fingerprint": [12345, 67890, ...] }

# Pack it exactly the way TrackOrigin does (any language; Python shown):
import base64, json, struct, subprocess
out   = json.loads(subprocess.check_output(["fpcalc","-raw","-json","served/track.mp3"]))
items = [int(x) & 0xFFFFFFFF for x in out["fingerprint"]]
fp    = base64.b64encode(b"".join(struct.pack("<I", x) for x in items)).decode()
ms    = int(round(out["duration"] * 1000))

# Verify that fingerprint against the certificate (duration_ms is REQUIRED for a match)
curl -X POST https://trackorigin.io/api/v1/certificates/cer_01KS9QWHV7PHETYGQAYMHB3DM6/verify-audio \
  -H "Authorization: Bearer $TRACKORIGIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{ \"fingerprint\": \"$fp\", \"duration_ms\": $ms, \"algorithm\": \"chromaprint\" }"
# 200 -> { "matched": true, "policy_accepted": true, "score": 0.98, "cert_status": "active", ... }
# Note: until TrackOrigin's verification policy is calibrated/accepted, matched is
# always false and policy_accepted false — your "no seal" path already handles it.

# Bind ONLY when it really matched, then re-check the cert is still active:
if receipt["matched"] and receipt["policy_accepted"]:
    status = GET /api/v1/certificates/cer_01KS9QWHV7PHETYGQAYMHB3DM6/status?no_cache=true
    if status["status"] == "active":
        track.to_cert_id        = "cer_01KS9QWHV7PHETYGQAYMHB3DM6"   # verified, bound cert
        track.origin_seal_state = "verified"
        track.served_sha256     = sha256(served_file_bytes)          # exactly what you serve
D · Render gate — a DB read only, never the pasted ID
seal_visible = (
    track.origin_seal_state == "verified"
    and track.to_cert_id                                    # NEVER read requested_cert_id
    and track.served_sha256 == sha256(current_served_file)  # audio hasn't changed
    and track.to_cert_id not in revoked_cert_ids            # fresh revocation set
    and revocation_feed_is_fresh()                          # fail closed if stale
)
# Any line false -> render nothing.
Stay honest over time · revocation poller (every ≤ 5 min)
GET https://trackorigin.io/api/v1/certificates/changes?since_sequence=N   (Bearer key)
# store last_applied_sequence; reject any response where sequence <= last_applied;
# flip matching bindings to revoked; re-verify on any audio swap or re-transcode.
# if now - feed_fetched_at > 15 min: hide all seals platform-wide (fail closed).
[ STEP 7 ]

GO-LIVE CHECKLIST.

PRESENTATION

Seal renders from stored cert IDs

Disc shows in track rows, click opens /cert/<id>, the script loads once per page, and unverified rows show no badge.

KEYS

Secret key is server-side only

No to_sk_live_… in any client bundle; rate limits understood; keys stored in your secrets manager.

PROOF (IF DOING LAYER 2)

Seal only from a verified binding

Render gates on the verified binding, never the pasted ID; audio swap / cert change / revoke all remove the seal. Once the gate is verified in sandbox, ship — the public seal renders automatically the moment verify-audio returns a match.