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.
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.
Create an account
A normal TrackOrigin signup — it's how you get API access.
Get API keys
One publishable, one secret. Two minutes.
Know the two layers
Showing the mark vs proving the audio.
Show the seal
One field at upload + one line in your track row.
Test in sandbox
Prove the flow with a test certificate.
Prove the audio
Verify served audio → bind → gate the seal. Self-serve.
Go live
Checklist, then ship.
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.
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:
to_pk_live_…to_sk_live_…KNOW THE TWO LAYERS.
This trips everyone up, so read it once. There are two separate things:
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.
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.
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.
<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 -->
{% 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>
window.TrackOrigin.mountAll().
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.
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.
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.
Content-address
Store each served file by the SHA-256 of its exact bytes.
Verify each artifact
Fingerprint the served file's own bytes; call verify-audio.
Bind
Re-check the cert fresh, then write an immutable binding.
Gate render
Show the seal only while the binding, cert and audio all still match — never from the pasted ID.
# 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
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.
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).
GO-LIVE CHECKLIST.
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.
Secret key is server-side only
No to_sk_live_… in any client bundle; rate limits understood; keys stored in your secrets manager.
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.