Skip to main content

Reading organization data

Resource endpoints sit under /v1/, not /api/v1/ — that prefix belongs to the OAuth endpoints in Authorization Code + PKCE and Client Credentials. They take a bearer token the same way, and unlike userinfo they accept a Client Credentials token, which is what makes unattended access possible.
GET /v1/ping accepts any valid token and touches no organization data — the cheapest way to confirm a token works before debugging anything else.
Every response nests its payload under data, alongside a meta.requestId worth logging — quote it when asking Soundlink about a specific request.
The examples below cover the three read endpoints most integrations start with. See Scopes and the endpoints they unlock below for the full list — including the write scopes — and the API reference for every parameter and response schema.

List campaigns

GET /v1/campaigns · scope campaigns:read
200 Response
integer
default:"1"
Minimum 1.
integer
default:"10"
Between 1 and 100. Values outside the range are rejected with 400.
string
default:"createdAt"
createdAt or status.
string
default:"desc"
asc or desc.

One campaign

GET /v1/campaigns/{campaignId} · scope campaigns:read
cURL
The list summary’s fields, plus an optional strategyType. A campaign the token’s organization does not own answers 404 — the same as one that does not exist, so ownership is not enumerable.
creating · active · paused · stopped · completed · failed · ended
1, 2 or 3. Writes are supported only for generation 3.

Campaign metrics

GET /v1/campaigns/{campaignId}/metrics/overview · scope metrics:read Totals for a date range, with no per-day rows.
cURL
200 Response
string
default:"campaign start"
YYYY-MM-DD, inclusive.
string
default:"today"
YYYY-MM-DD, inclusive. Must be greater than or equal to startDate, or you get 400.
cpl, cpf and streams_per_listener are derived server-side — media spend per listener, per follower, and streams per listener, each 0 when listeners or followers are 0. Handle impressions, ad_clicks and link_clicks as nullable: a platform that did not report them sends null, which is not the same as zero.
campaigns:read does not imply metrics:read. A token holding only the first reads campaigns and gets 403 insufficient_scope on metrics — so request both if you show them together, and handle each independently if you request them separately.

Scopes and the endpoints they unlock

Request the minimum you need — the consent screen shows each scope to the user, and a shorter list converts better. scope values are space-separated, must be unique, and must fall within your client’s registered allowedScopes.
scopes_supported in the discovery document is the authoritative list — it currently returns openid, email, campaigns:read, campaigns:write, metrics:read and videos:write.

No token — client authentication only

Any scope

openid

email

Adds the email claim to the userinfo response. Unlocks no endpoint of its own.

campaigns:read

campaigns:write

Wallet-funded campaigns only. Same endpoints and rules as the API key scope of the same name — see Creating campaigns and Managing campaigns.

metrics:read

videos:write

Same endpoints and rate limit as the API key scope of the same name — see Importing videos.

API reference

Request parameters, response schemas and error codes for every endpoint above.
The reference documents these endpoints with Authorization: Bearer sk_... authentication (the x-api-key header also still works but is deprecated, retiring 2026-08-17). The same endpoints accept an OAuth bearer token too — send Authorization: Bearer <access_token> in place of the API key, and the scopes above apply.

Errors and rate limits

All OAuth errors return { "error": "<code>" }.

Resource errors have a different shape

The /v1/ resource endpoints wrap the error in an object and add a request id, rather than returning the flat { "error": "<code>" } the OAuth endpoints use. Branch on the nested error.code:
401
400 (bad query), 401, 403, 404, 429 and 500 are all possible. A 404 carries campaign_not_found — returned both for a campaign that does not exist and for one your organization does not own.

Authorize error behaviour

Two distinct behaviours, which affect how you handle failures:
When client_id and redirect_uri are both valid, validation failures redirect to your callback with ?error=<code>&state=<state>. Handle these in your callback route.

Rate limits

/authorize and /token are rate limited per client; exceeding the limit returns 429 with temporarily_unavailable and a Retry-After header. Respect it with backoff. Caching Client Credentials tokens for their full hour (see Client Credentials) keeps normal usage far below any limit.

Security checklist

Generate an unguessable state per authorization request, bind it to the user’s session, and reject any callback whose state does not match. This is your CSRF defence on the redirect.
The code_verifier must never reach the browser, a URL, or client-side storage. Hold it in a signed httpOnly cookie or server session with a short TTL, and consume it once.
Never commit it, never ship it in a client bundle, never expose it through an API response. Load it from the environment at runtime and rotate by requesting a new client. Exchange codes and request tokens only from your backend.
Use the fewest possible, all HTTPS, each a complete URL. Never build a redirect target from user input or an open redirector, and do not rely on prefix matching — none is performed.
Treat code, code_verifier, client_secret, access_token and full Authorization headers as secrets. Log the token’s exp and organization_id if you need traceability, not the token. Watch for accidental capture in request-body logs, error reporters and APM traces — a 400 from the token endpoint often carries the whole form body.
No JWKS is published, so token signatures cannot be verified by clients. Decode claims only to read values from a token you just received over TLS, and never as an authorization decision in your own system.

Next

Back to the OAuth overview for discovery, client onboarding, and the reference app.