Skip to the content
  1. Docs
  2. Connect your users

Handle the callback

Core + Managed TransfersGuide · 2 min
Read the data64 parameter on your return URL, check the result on your backend, and store the integrationUserUUID.

After the sign-in, the user's browser opens your returnUrl with one extra query parameter, data64.

The return URL
https://app.example.com/callback?data64=eyJpbnRlZ3JhdGlvbiI6eyJ0eXBlIjoic3BvdGlmeSIs...

Decode data64

data64 is base64-encoded JSON.

Decoded data64
{
  "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"
}
integration.type
string
The service the user connected, for example spotify.
integration.returnUrl
string
The MusicAPI callback URL of this service. You register it at the service when you use your own developer app.
authModel.uuid
string
The id of this sign-in attempt.
authModel.status
string
success, error, or initialised (started but never finished; rare in a callback).
authModel.errorType
string | null
Why it failed: 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.
authModel.error
string | null
A text for your logs.
integrationUserUUID
string | null
The connected user. null when the sign-in failed.
oneTimeToken
string
Only when you asked for one with 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"
200 OK
{
  "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:

Library calls
https://api.musicapi.com/api/3f0b8c1e-6a4d-4c7a-9a55-2f1e7d9b0c11/playlists

Failures

  • status: "error" with errorType: "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 uniqueId polling 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.