# Create transfer session
URL: https://musicapi.com/docs/reference/create-transfer-session.md

`POST https://api.musicapi.com/public/transfer-sessions`

Section: Managed Transfers.
Product: MusicAPI Managed Transfers.
Auth: Basic, DevToken (any one of them). Use Basic with your Client ID and Client Secret from your server only.

Creates a pre-authorized session of the hosted transfer wizard. Your backend sends the access token of a user who is already signed in to your destination service in your app. We check the token with the service, store it encrypted, attach the account as the destination and answer with a launchUrl. The user then connects only the source. Requires Client Secret (Basic) or a DevToken without `sub`. See [Pre-authorized Sessions](/docs/managed-transfers/preauthorized-sessions).

## Request body

`application/json`, required.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `target` | object | yes |  |
| `target.authData` | PreauthorizedAuthData | yes |  |
| `target.authData.accessToken` | string | yes | The user's access token for your destination service. |
| `target.authData.refreshToken` | string | no | The refresh token. Used only when your destination integration has OAuth client credentials that can renew it. |
| `target.authData.expiresAt` | date-time \| number | no | When the access token expires: an ISO 8601 date, or a Unix time in seconds or milliseconds. |
| `externalUserId` | string | no | Your own reference for the user. Sent back in the webhooks of this session. |
| `returnUrl` | uri | no | The results page links back to this URL. It must match your Return URLs; with none set, it must be https. |
| `locale` | string | no | The user's language. The wizard is in English today. |

## Response 201

The session is created.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `sessionUuid` | uuid | no |  |
| `launchUrl` | uri | no | Opens the hosted wizard. Works once, for 15 minutes. The single-use code is in the URL fragment. |
| `launchExpiresAt` | date-time | no |  |
| `expiresAt` | date-time | no | When the session ends (24 hours after creation). |
| `externalUserId` | string \| null | no |  |
| `target` | PreauthorizedTarget | no |  |
| `target.integrationType` | string | no |  |
| `target.integrationUserUuid` | uuid | no |  |
| `target.displayName` | string \| null | no |  |

```json
{
  "sessionUuid": "5b0c6f0e-2c8a-4d0e-9a51-7e3f1c2d9b44",
  "launchUrl": "https://example.com",
  "launchExpiresAt": "2026-10-16T09:41:07.000Z",
  "expiresAt": "2026-10-16T09:41:07.000Z",
  "externalUserId": "string",
  "target": {
    "integrationType": "spotify",
    "integrationUserUuid": "5b0c6f0e-2c8a-4d0e-9a51-7e3f1c2d9b44",
    "displayName": "Jane Listener"
  }
}
```

## Errors

- 400: The body is not valid, no transfer destination is set, or the destination does not support pre-authorized sessions.
- 401: The request has no credentials.
- 403: The credential is not secret, or the transfer service is not enabled for your account.
- 422: The music service refused the access token. Nothing is stored.
- 503: The music service could not be reached to check the token. Try again later.

## Example

```bash
curl -X POST "https://api.musicapi.com/public/transfer-sessions" \
  -u "$MUSICAPI_CLIENT_ID:$MUSICAPI_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"target":{"authData":{"accessToken":"string","refreshToken":"string","expiresAt":"2026-10-16T09:41:07.000Z"}},"externalUserId":"string","returnUrl":"https://example.com","locale":"en"}'
```
