> ## Documentation Index
> Fetch the complete documentation index at: https://discord-markm-docs-lobby-membership-disconnect.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Provisional Accounts

> Implement provisional Discord accounts for users who don't have Discord accounts yet.

export const YouTubeEmbed = ({src, title = "YouTube video player", showDuration = false, chapters = ""}) => {
  const iframeRef = useRef(null);
  const detailsRef = useRef(null);
  const [duration, setDuration] = useState(null);
  const [activeChapter, setActiveChapter] = useState(-1);
  const formatDuration = seconds => {
    const h = Math.floor(seconds / 3600);
    const m = Math.floor(seconds % 3600 / 60);
    const s = Math.round(seconds % 60);
    const pad = n => String(n).padStart(2, "0");
    return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`;
  };
  const parsedChapters = useMemo(() => {
    if (!chapters) return [];
    return chapters.split("\n").map(line => {
      const match = line.match(/^\s*[-*•]?\s*(?:(\d+):)?(\d{1,2}):(\d{2})\s*[-–—:]?\s*(\S.*)$/);
      if (!match) return null;
      const [, h, m, s, chapterTitle] = match;
      const seconds = (h ? parseInt(h, 10) * 3600 : 0) + parseInt(m, 10) * 60 + parseInt(s, 10);
      return {
        seconds,
        label: formatDuration(seconds),
        title: chapterTitle.trim()
      };
    }).filter(Boolean);
  }, [chapters]);
  const jsApiEnabled = showDuration || parsedChapters.length > 0;
  useEffect(() => {
    if (parsedChapters.length === 0) return;
    const onPointerDown = event => {
      const details = detailsRef.current;
      if (details?.open && !details.contains(event.target)) details.open = false;
    };
    document.addEventListener("pointerdown", onPointerDown);
    return () => document.removeEventListener("pointerdown", onPointerDown);
  }, [parsedChapters.length]);
  useEffect(() => {
    if (!jsApiEnabled) return;
    const onMessage = event => {
      if (event.origin !== "https://www.youtube.com") return;
      if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) return;
      let data;
      try {
        data = JSON.parse(event.data);
      } catch {
        return;
      }
      const seconds = data?.info?.duration;
      if (typeof seconds === "number" && seconds > 0) setDuration(seconds);
      const currentTime = data?.info?.currentTime;
      if (typeof currentTime === "number" && parsedChapters.length > 0) {
        let index = -1;
        for (let i = 0; i < parsedChapters.length; i++) {
          if (parsedChapters[i].seconds <= currentTime) index = i;
        }
        setActiveChapter(index);
      }
    };
    window.addEventListener("message", onMessage);
    return () => window.removeEventListener("message", onMessage);
  }, [jsApiEnabled, parsedChapters]);
  const postToPlayer = payload => {
    iframeRef.current?.contentWindow?.postMessage(JSON.stringify(payload), "https://www.youtube.com");
  };
  const handleLoad = () => {
    postToPlayer({
      event: "listening",
      id: "ytembed",
      channel: "widget"
    });
  };
  const seekTo = seconds => {
    postToPlayer({
      event: "command",
      func: "seekTo",
      args: [seconds, true]
    });
    postToPlayer({
      event: "command",
      func: "playVideo",
      args: []
    });
  };
  const embedSrc = jsApiEnabled ? `${src}${src.includes("?") ? "&" : "?"}enablejsapi=1` : src;
  return <div>
      <iframe ref={iframeRef} className="w-full aspect-video rounded-xl" src={embedSrc} title={title} frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen onLoad={jsApiEnabled ? handleLoad : undefined}></iframe>
      {(showDuration || parsedChapters.length > 0) && <div className="relative mt-2 flex items-start justify-between gap-4 text-sm text-gray-500 dark:text-zinc-500">
          {parsedChapters.length > 0 && <details ref={detailsRef}>
              <summary className="cursor-pointer select-none">Chapters</summary>
              <ul className="absolute left-0 top-full z-10 mt-1 mb-0 max-h-72 w-max max-w-full list-none overflow-y-auto rounded-xl border border-gray-200 bg-white p-3 pl-3 shadow-lg dark:border-zinc-700 dark:bg-zinc-800">
                {parsedChapters.map((chapter, index) => <li key={chapter.seconds} className="m-0 pl-0">
                    <button type="button" className={`cursor-pointer rounded-md px-1.5 py-0.5 text-left hover:text-gray-700 dark:hover:text-zinc-300 ${index === activeChapter ? "bg-gray-100 text-gray-800 dark:bg-zinc-700 dark:text-zinc-200" : ""}`} onClick={() => {
    seekTo(chapter.seconds);
    if (detailsRef.current) detailsRef.current.open = false;
  }}>
                      <span className="font-medium text-primary dark:text-primary-light">
                        {chapter.label}
                      </span>{" "}
                      {chapter.title}
                    </button>
                  </li>)}
              </ul>
            </details>}
          {showDuration && duration !== null && <p className="m-0 ml-auto">Duration: {formatDuration(duration)}</p>}
        </div>}
    </div>;
};

export const LinkIcon = props => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="currentColor" d="M16.32 14.72a1 1 0 0 1 0-1.41l2.51-2.51a3.98 3.98 0 0 0-5.62-5.63l-2.52 2.51a1 1 0 0 1-1.41-1.41l2.52-2.52a5.98 5.98 0 0 1 8.45 8.46l-2.52 2.51a1 1 0 0 1-1.41 0ZM7.68 9.29a1 1 0 0 1 0 1.41l-2.52 2.51a3.98 3.98 0 1 0 5.63 5.63l2.51-2.52a1 1 0 0 1 1.42 1.42l-2.52 2.51a5.98 5.98 0 0 1-8.45-8.45l2.51-2.51a1 1 0 0 1 1.42 0Z" /><path fill="currentColor" d="M14.7 10.7a1 1 0 0 0-1.4-1.4l-4 4a1 1 0 1 0 1.4 1.4l4-4Z" /></svg>;

export const WrenchIcon = props => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="currentColor" d="M7.8 15.77c.7.43 1.2 1.14 1.2 1.96V21a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3.27c0-.82.5-1.53 1.2-1.96a8.06 8.06 0 0 0 .12-13.63c-.6-.39-1.32.09-1.32.8v5.98a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1V2.94c0-.71-.72-1.19-1.32-.8a8.06 8.06 0 0 0 .12 13.63Z" /></svg>;

export const RobotIcon = props => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="currentColor" d="M7.89 13.46a1 1 0 0 1-1.78-.9L7 13l-.9-.45.01-.01.01-.02a2.42 2.42 0 0 1 .14-.23c.1-.14.23-.31.4-.5.37-.36.98-.79 1.84-.79.86 0 1.47.43 1.83.8a3.28 3.28 0 0 1 .55.72v.02h.01v.01L10 13l.9-.45a1 1 0 0 1-1.79.9 1.28 1.28 0 0 0-.19-.25c-.14-.13-.28-.2-.42-.2-.14 0-.28.07-.42.2a1.28 1.28 0 0 0-.19.25ZM13.55 13.9a1 1 0 0 0 1.34-.44c0-.02.02-.04.04-.06.03-.05.08-.13.15-.2.14-.13.28-.2.42-.2.14 0 .28.07.42.2a1.28 1.28 0 0 1 .19.25 1 1 0 0 0 1.78-.9L17 13l.9-.45-.01-.01-.01-.02a2.26 2.26 0 0 0-.14-.23 3.28 3.28 0 0 0-.4-.5c-.37-.36-.98-.79-1.84-.79-.86 0-1.47.43-1.83.8a3.28 3.28 0 0 0-.55.72v.02h-.01v.01L14 13l-.9-.45a1 1 0 0 0 .45 1.34Z" /><path fill="currentColor" fill-rule="evenodd" d="M12 21c5.52 0 10-1.86 10-6 0-5.59-2.8-10.07-4.26-11.67a1 1 0 1 0-1.48 1.34 14.8 14.8 0 0 1 2.35 3.86A10.23 10.23 0 0 0 12 6C9.47 6 7.15 7.02 5.4 8.53a14.8 14.8 0 0 1 2.34-3.86 1 1 0 1 0-1.48-1.34A18.65 18.65 0 0 0 2 15c0 4.14 4.48 6 10 6Zm0-12c3.87 0 7 2 7 4.2S15.87 17 12 17s-7-1.6-7-3.8C5 11 8.13 9 12 9Z" clip-rule="evenodd" /></svg>;

export const ManualAnchor = ({id}) => {
  return <div className="MDXManualAnchor" id={id}></div>;
};

## Overview

<YouTubeEmbed
  src="https://www.youtube.com/embed/frILI2uDhOk"
  title="Deep Dive Into Social SDK Provisional Accounts"
  showDuration
  chapters={`00:00 - Introductions
01:18 - Overview: Social SDK and Provisional Accounts
04:21 - Creating a Provisional Account via the Bot Token Endpoint
08:20 - C++ Implementation: Authentication and Access Tokens
14:38 - Verifying Provisional Account Status
16:44 - Q&A Break
19:16 - Sending Friend Requests and DMs from Provisional Accounts
28:00 - Alternative Authentication Flows
30:54 - Handling Token Expiration with Callbacks
31:43 - Account Merging (Account Linking) Lifecycle
39:58 - Server-Side Implementation for Merging
45:03 - Data Migration: What Transfers During Merges
46:03 - Unmerging Discord Accounts
48:11 - Out-of-Band Unmerges and Banning
50:11 - Using Webhooks to Track Unmerges
54:23 - Summary and Best Practices
56:23 - Q&A and Resources`}
/>

Provisional accounts let players use Social SDK features in your game without linking a Discord account so all players can have a consistent gameplay experience.

With provisional accounts, players can:

* Add friends and communicate with other players
* Join voice chats in game lobbies
* Send direct messages to other players
* Appear in friends lists and game lobbies

All of this works seamlessly whether your players have Discord accounts or not.

<Tip>
  Two terms show up throughout these guides: *linking* is the player-facing action of connecting a Discord account,
  and *merging* is the operation that carries it out — the provisional account's data is merged into the Discord
  account. They describe the same flow from different angles.
</Tip>

This section will show you how to:

1. [Choose an authentication method](#choosing-an-authentication-method) and [configure your identity provider](/developers/discord-social-sdk/development-guides/provisional-accounts/identity-providers)
2. [Create](/developers/discord-social-sdk/development-guides/provisional-accounts/bot-token-endpoint) and [manage](/developers/discord-social-sdk/development-guides/provisional-accounts/managing-accounts) provisional accounts for your game
3. [Merge](/developers/discord-social-sdk/development-guides/provisional-accounts/merging-accounts) accounts when users link a full Discord account, and [unmerge](/developers/discord-social-sdk/development-guides/provisional-accounts/unmerging-accounts) them whenvoEc21roLas the link is severed

## Prerequisites

Before you begin, make sure you have:

* A basic understanding of how the SDK works from the [Getting Started Guide](/developers/discord-social-sdk/getting-started)
* An external authentication provider set up for your game

***

<ManualAnchor id="what-are-provisional-accounts" />

## What Are Provisional Accounts?

Think of provisional accounts as temporary Discord accounts that:

* Work only with your game
* Can be merged into a full Discord account later
* Persist between game sessions
* Use your game's authentication system

With provisional accounts, players can use Discord features like chat and voice and interact with game friends without creating a full Discord account. They are "placeholder" Discord accounts for the user that your game owns and manages.

For existing Discord users who have added a provisional account as a game friend, the provisional account will appear in their friend list, allowing you to send direct messages and interact with them for text and voice in lobbies.

### Benefits

* Instant Access: Players can use social features immediately
* Seamless Experience: Works the same for all players
* Easy Linking: Simple to merge into a full Discord account
* Data Persistence: Friends and history are preserved
* Cross-Platform: Works on all supported platforms

### Provisional Account Lifecycle

The diagram below shows the general lifecycle of a provisional account, from creation through an optional merge with a
full Discord account.

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor P as Player
    participant G as Your Game
    participant D as Discord

    P->>G: Sign in with your auth<br/>(OIDC, Steam, EOS, ...)
    G->>D: Request provisional token<br/>(external identity)
    alt No account for identity
        D->>D: Create provisional account
        D-->>G: Access token (new provisional account)
    else Provisional account exists
        D-->>G: Access token (existing provisional account)
    else Full Discord account exists
        D-->>G: Error — use OAuth2 flow
    end
    G->>D: Use Social SDK features<br/>(friends, DMs, voice)
    Note over P,D: Later, the player can link a full Discord account
    P->>G: Choose to link a Discord account
    G->>D: Merge provisional account into Discord account
    D-->>G: Full Discord account<br/>(friends, DMs, lobbies migrated)
```

***

## Choosing an Authentication Method

Discord offers a number of authentication methods, the one you use depends on how your game and account system are
set up:

1. Use the [Bot Token Endpoint](/developers/discord-social-sdk/development-guides/provisional-accounts/bot-token-endpoint)
   if your game has an account system which uniquely identifies users. **This is the recommended approach when possible.**
2. Use [Server Authentication with External Credentials Exchange](/developers/discord-social-sdk/development-guides/provisional-accounts/external-credentials-exchange)
   if you have a hard requirement for a server side custom OIDC integration.
3. Use the [Public Client Integration](/developers/discord-social-sdk/development-guides/provisional-accounts/public-client)
   method if you don't have a server authoritative backend, and therefore require using a Public Client for
   authentication.

If you are using (2) or (3), you must [configure your identity provider](/developers/discord-social-sdk/development-guides/provisional-accounts/identity-providers) before being able to create provisional accounts.

## Implementing Provisional Accounts

Whichever method you choose, creating a provisional account and requesting an access token for it always happens in a single step.

You provide external authentication that uniquely identifies the user, and Discord finds a user associated with that identifier.

* If there is no account associated with the identity, a new provisional account is created along with a new access token for the user.
* If there is a provisional account associated with the identity, an access token is returned.
* If there is an existing *full Discord account* associated with the identity, the request is aborted.

Once authentication is complete, you can use the access token as you would a full Discord user's access token.

***

## Next Steps

<CardGroup cols={3}>
  <Card title="Bot Token Endpoint" href="/developers/discord-social-sdk/development-guides/provisional-accounts/bot-token-endpoint" icon={<RobotIcon />}>
    Recommended. Create accounts from your game's own unique user IDs using your bot token.
  </Card>

  <Card title="Managing Provisional Accounts" href="/developers/discord-social-sdk/development-guides/provisional-accounts/managing-accounts" icon={<WrenchIcon />}>
    Refresh access tokens and set display names.
  </Card>

  <Card title="Merging Accounts" href="/developers/discord-social-sdk/development-guides/provisional-accounts/merging-accounts" icon={<LinkIcon />}>
    Merge a provisional account into a full Discord account.
  </Card>
</CardGroup>

Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community.

If you encounter a bug while working with the Social SDK, please report it here:  [https://dis.gd/social-sdk-bug-report](https://dis.gd/social-sdk-bug-report)

***

## Change Log

| Date | Changes |
| - | - |
| July 14, 2026 | Split the provisional accounts guide into its own section |
| March 17, 2025 | Initial release |
