Skip to main content
This is the consent-establishing flow — a user signs in, approves scopes, and your app receives a token that acts as them. Run Discovery and onboarding first if you haven’t registered a client yet.
1

Generate PKCE values and state

Create a code_verifier, derive its S256 challenge, and generate an unguessable state.
Store codeVerifier and state server-side, bound to the user’s session — for example in a signed, httpOnly cookie with a short TTL. Never put the verifier in the browser or in a URL.
2

Redirect the user to the authorization endpoint

On success the user is redirected (302) to the Soundlink consent screen. This endpoint sends Cache-Control: no-store.
3

The user approves, and Soundlink returns to your callback

Compare state against the value you stored before doing anything else, and reject the request if it does not match. If the user declines, you receive ?error=access_denied&state=… instead — treat this as a normal outcome, not a failure.
4

Exchange the code for a token

The request is application/x-www-form-urlencoded. This grant authenticates with the PKCE verifier and sends no client_secret.
200 Response
redirect_uri must be identical to the one used in the previous step. Authorization codes are single-use.
5

Record the organization id

organization_id is a claim on the access token, so no extra request is needed to read it.This is the one value you must persist. Soundlink provides no endpoint that lists which organizations have authorized your client, so an organization whose id you have not stored is one you can no longer act for. Keep it in your own database, keyed to your own user or tenant, along with grant_id if you intend to support disconnecting.

Access token claims

string
The user’s id for authorization_code tokens; your client id for client_credentials tokens — those have no user.
Access tokens are signed with a symmetric key and no JWKS endpoint is published, so you cannot verify their signature. Decode claims only for values you need (such as organization_id) from a token you have just received over TLS, and never treat a decoded claim as proof of anything. Treat tokens as opaque credentials otherwise.

Userinfo

GET /api/v1/oauth/userinfo requires a token from the authorization_code grant with the openid scope. A Client Credentials token is rejected with 403 access_denied regardless of its scopes, because it represents an application rather than a user.
cURL
200 Response
email is present only when the token carries the email scope. Responses send Cache-Control: no-store. Failures return { "error": "…" } with a WWW-Authenticate header: Send exactly one Authorization header — two are rejected rather than merged.

Next

Once the grant exists, mint unattended tokens with Client Credentials, or jump to Scopes, endpoints and errors to call campaign and metrics routes.