- Docs
- Connect your users
Handle the callback
After the sign-in, the user's browser opens your returnUrl with one extra query parameter, data64.
https://app.example.com/callback?data64=eyJpbnRlZ3JhdGlvbiI6eyJ0eXBlIjoic3BvdGlmeSIs...
Decode data64
data64 is base64-encoded JSON.
{
"integration": {
"type": "spotify",
"isSuperPrivate": false,
"returnUrl": "https://api.musicapi.com/callback/spotify"
},
"authModel": {
"uuid": "a1869581-38b2-4268-aee2-a4c2355efeb0",
"status": "success",
"error": null,
"errorType": null
},
"integrationUserUUID": "3f0b8c1e-6a4d-4c7a-9a55-2f1e7d9b0c11",
"oneTimeToken": "e50cc5e7-254a-4cff-a000-457ae53a059d"
}
spotify.success, error, or initialised (started but never finished; rare in a callback).accessDenied (the user said no), notAllowedInYourCountry, missingScopes (the user unticked a permission), deniedSubscriptionTier (the service needs a paid plan). null on success or for other failures.null when the sign-in failed.requestOneTimeToken=true. It lives 30 seconds.Check it on your backend
data64 is not signed. Anybody can open your return URL with made-up data. Before you link a connected user to your own user, confirm the result with your Client Secret:
curl "https://api.musicapi.com/public/integrations/auth/a1869581-38b2-4268-aee2-a4c2355efeb0" \
-u "$MUSICAPI_CLIENT_ID:$MUSICAPI_CLIENT_SECRET"
{
"authModel": {
"uuid": "a1869581-38b2-4268-aee2-a4c2355efeb0",
"status": "success",
"error": null,
"errorType": null
},
"integrationUser": {
"uuid": "3f0b8c1e-6a4d-4c7a-9a55-2f1e7d9b0c11",
"integrationUserId": "mica.nowak",
"name": "Mica Nowak",
"email": "[email protected]",
"imageUrl": "https://images.example.com/mica.jpg",
"country": "PL",
"authData": { "accessToken": "BQD…" },
"authDataExpiresAt": 1792137600000
}
}
With Basic the answer also holds authData, the raw service tokens without the refresh token. Leave them alone unless you call the service yourself. The other credentials get the profile only.
The profile fields depend on the service. Some services give no name or no email.
Store the result
Store the integrationUserUUID with your own user, together with integration.type. It is the key for every library call:
https://api.musicapi.com/api/3f0b8c1e-6a4d-4c7a-9a55-2f1e7d9b0c11/playlists
Failures
status: "error"witherrorType: "accessDenied": the user cancelled. Offer the button again.missingScopes: the user unticked a permission. Ask them to sign in again and keep every box ticked.deniedSubscriptionTier: the service needs a paid plan for API access.- No callback at all: the user closed the tab. Use
uniqueIdpolling if you need to know.
By default the hosted page shows the failure to the user. Add forwardFailures=true to the sign-in URL to send the user back to your returnUrl at once, with status: "error" in data64.
No credentials in the browser
GET/app/integrations/{authModelUUID} reads the same attempt with no credential at all: the authModelUUID is the secret. It never returns authData. Keep the authModelUUID away from logs and third parties.