> ## 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.

# Discord Developer Platform

> Build bots and integrations on Discord, or connect your game with rich presence, voice chat, and more.

export const YouTubePlaylistCarousel = ({list, description = "", videos = null}) => {
  const playlistId = useMemo(() => {
    if (!list) return "";
    const match = list.match(/[?&]list=([^&]+)/);
    return match ? match[1] : list;
  }, [list]);
  const playlistUrl = `https://www.youtube.com/playlist?list=${playlistId}`;
  const cacheKey = `yt-playlist-${playlistId}`;
  const cacheTtlMs = 60 * 60 * 1000;
  const pinnedVideos = useMemo(() => {
    if (Array.isArray(videos)) return videos;
    if (typeof videos === "string" && videos.trim()) return videos.trim().split(/[\s,]+/);
    return null;
  }, [videos]);
  const playerRef = useRef(null);
  const playingRefs = useRef({});
  const trackRef = useRef(null);
  const [videoIds, setVideoIds] = useState(pinnedVideos || []);
  const [playing, setPlaying] = useState(new Set());
  const [channel, setChannel] = useState(null);
  const [atStart, setAtStart] = useState(true);
  const [atEnd, setAtEnd] = useState(true);
  const readCache = () => {
    try {
      return JSON.parse(localStorage.getItem(cacheKey));
    } catch {
      return null;
    }
  };
  const writeCache = patch => {
    try {
      const previous = readCache() || ({});
      localStorage.setItem(cacheKey, JSON.stringify({
        ...previous,
        ...patch,
        fetchedAt: Date.now()
      }));
    } catch {}
  };
  useEffect(() => {
    if (!playlistId) return;
    const cached = readCache();
    if (cached?.channel) setChannel(cached.channel);
    if (!pinnedVideos && Array.isArray(cached?.videos) && cached.videos.length > 0) {
      setVideoIds(cached.videos);
    }
    if (cached?.fetchedAt && Date.now() - cached.fetchedAt < cacheTtlMs) return;
    fetch(`https://www.youtube.com/oembed?url=${encodeURIComponent(playlistUrl)}&format=json`).then(response => response.ok ? response.json() : null).then(data => {
      if (!data?.author_name) return;
      const url = (data.author_url || "").startsWith("http") ? data.author_url : `https://www.youtube.com${data.author_url || ""}`;
      const freshChannel = {
        name: data.author_name,
        url
      };
      setChannel(freshChannel);
      writeCache({
        channel: freshChannel
      });
    }).catch(() => {});
  }, [playlistId]);
  useEffect(() => {
    if (!playlistId) return;
    const onMessage = event => {
      if (event.origin !== "https://www.youtube.com") return;
      if (!playerRef.current || event.source !== playerRef.current.contentWindow) return;
      let data;
      try {
        data = JSON.parse(event.data);
      } catch {
        return;
      }
      const ids = data?.info?.playlist;
      if (Array.isArray(ids) && ids.length > 0 && ids.every(id => typeof id === "string")) {
        if (!pinnedVideos) setVideoIds(ids);
        writeCache({
          videos: ids
        });
      }
    };
    window.addEventListener("message", onMessage);
    return () => window.removeEventListener("message", onMessage);
  }, [playlistId, pinnedVideos]);
  const pauseAll = exceptVideoId => {
    const pause = iframe => iframe?.contentWindow?.postMessage(JSON.stringify({
      event: "command",
      func: "pauseVideo",
      args: []
    }), "https://www.youtube.com");
    Object.entries(playingRefs.current).forEach(([videoId, iframe]) => {
      if (videoId !== exceptVideoId) pause(iframe);
    });
    pause(playerRef.current);
  };
  useEffect(() => {
    const onVisibilityChange = () => {
      if (document.hidden) pauseAll();
    };
    document.addEventListener("visibilitychange", onVisibilityChange);
    return () => document.removeEventListener("visibilitychange", onVisibilityChange);
  }, []);
  const handlePlayerLoad = () => {
    playerRef.current?.contentWindow?.postMessage(JSON.stringify({
      event: "listening",
      id: "ytplaylist",
      channel: "widget"
    }), "https://www.youtube.com");
  };
  const updateArrows = () => {
    const track = trackRef.current;
    if (!track) return;
    setAtStart(track.scrollLeft <= 4);
    setAtEnd(track.scrollLeft + track.clientWidth >= track.scrollWidth - 4);
  };
  useEffect(() => {
    updateArrows();
    window.addEventListener("resize", updateArrows);
    return () => window.removeEventListener("resize", updateArrows);
  }, [videoIds]);
  const scrollByPage = direction => {
    pauseAll();
    const track = trackRef.current;
    track?.scrollBy({
      left: direction * track.clientWidth,
      behavior: "smooth"
    });
  };
  const handleTrackScroll = () => {
    updateArrows();
    pauseAll();
  };
  return <div className="yt-carousel">
      {description && <p className="yt-carousel-description">{description}</p>}
      <div className="yt-carousel-viewport">
        <div className="yt-carousel-track" ref={trackRef} onScroll={handleTrackScroll}>
          {videoIds.length === 0 && <div className="yt-carousel-slide">
              <iframe ref={playerRef} src={`https://www.youtube.com/embed/videoseries?list=${playlistId}&enablejsapi=1`} title="YouTube playlist player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen onLoad={handlePlayerLoad}></iframe>
            </div>}
          {videoIds.map(videoId => playing.has(videoId) ? <div className="yt-carousel-slide" key={videoId}>
                <iframe ref={element => {
    if (element) playingRefs.current[videoId] = element; else delete playingRefs.current[videoId];
  }} src={`https://www.youtube.com/embed/${videoId}?autoplay=1&enablejsapi=1`} title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen></iframe>
              </div> : <div className="yt-carousel-slide" key={videoId}>
                <button type="button" className="yt-carousel-thumb" aria-label="Play video" onClick={() => {
    pauseAll(videoId);
    setPlaying(previous => new Set(previous).add(videoId));
  }}>
                  <img src={`https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`} alt="" loading="lazy" />
                  <span className="yt-carousel-play" aria-hidden="true">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor">
                      <path d="M8 5.5v13l11-6.5z" />
                    </svg>
                  </span>
                </button>
              </div>)}
        </div>
        {!(atStart && atEnd) && <>
            <button type="button" className="yt-carousel-arrow yt-carousel-arrow-left" aria-label="Previous videos" disabled={atStart} onClick={() => scrollByPage(-1)}>
              ‹
            </button>
            <button type="button" className="yt-carousel-arrow yt-carousel-arrow-right" aria-label="Next videos" disabled={atEnd} onClick={() => scrollByPage(1)}>
              ›
            </button>
          </>}
      </div>
      {channel && <div className="yt-carousel-footer">
          <a className="yt-carousel-subscribe" href={`${channel.url}?sub_confirmation=1`} target="_blank" rel="noreferrer">
            <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true">
              <path d="M23.5 6.2a3 3 0 0 0-2.1-2.1C19.5 3.5 12 3.5 12 3.5s-7.5 0-9.4.6A3 3 0 0 0 .5 6.2 31.4 31.4 0 0 0 0 12c0 1.9.2 3.9.5 5.8a3 3 0 0 0 2.1 2.1c1.9.6 9.4.6 9.4.6s7.5 0 9.4-.6a3 3 0 0 0 2.1-2.1c.3-1.9.5-3.9.5-5.8s-.2-3.9-.5-5.8zM9.6 15.6V8.4l6.2 3.6-6.2 3.6z" />
            </svg>
            Subscribe to {channel.name}
          </a>
        </div>}
    </div>;
};

export const CircleQuestionIcon = props => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><circle cx="12" cy="12" r="10" fill="#fff" fill-opacity=".01" /><path fill="currentColor" fill-rule="evenodd" d="M12 23a11 11 0 1 0 0-22 11 11 0 0 0 0 22Zm-.28-16c-.98 0-1.81.47-2.27 1.14A1 1 0 1 1 7.8 7.01 4.73 4.73 0 0 1 11.72 5c2.5 0 4.65 1.88 4.65 4.38 0 2.1-1.54 3.77-3.52 4.24l.14 1a1 1 0 0 1-1.98.27l-.28-2a1 1 0 0 1 .99-1.14c1.54 0 2.65-1.14 2.65-2.38 0-1.23-1.1-2.37-2.65-2.37ZM13 17.88a1.13 1.13 0 1 1-2.25 0 1.13 1.13 0 0 1 2.25 0Z" clip-rule="evenodd" /></svg>;

export const BugIcon = props => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="currentColor" d="M9.54 3.28a3 3 0 0 0-.37.74c-.19.52.28.98.83.98h4c.55 0 1.02-.46.83-.98a2.96 2.96 0 0 0-.37-.74c.23-.16.56-.28.99-.28a1 1 0 1 0 0-2c-1 0-2.01.4-2.68 1.1a3 3 0 0 0-1.54 0A3.76 3.76 0 0 0 8.55 1a1 1 0 0 0 0 2c.43 0 .76.12 1 .28ZM18.01 18.85c-.04.1-.03.22.05.3l1.51 1.5a1 1 0 0 1-1.41 1.42l-1.18-1.18a.26.26 0 0 0-.37 0 6.7 6.7 0 0 1-2.8 1.82c-.42.14-.83-.2-.86-.64l-.42-8.57a.53.53 0 0 0-1.05 0l-.43 8.57c-.03.45-.44.78-.87.64A6.7 6.7 0 0 1 7.4 20.9a.26.26 0 0 0-.37-.01l-1.18 1.18a1 1 0 0 1-1.41-1.41l1.51-1.51c.08-.08.1-.2.05-.3-.47-.94-.78-2-.92-3.12a.25.25 0 0 0-.25-.23H3a1 1 0 1 1 0-2h1.82c.13 0 .24-.1.25-.23.14-1.13.45-2.18.92-3.12a.25.25 0 0 0-.05-.3l-1.51-1.5a1 1 0 1 1 1.41-1.42L7.02 8.1c.1.1.27.1.37 0a6.66 6.66 0 0 1 2.95-1.87c.38-.11.75.14.85.52l.57 2.27c.06.25.42.25.48 0l.57-2.27c.1-.38.47-.63.85-.52 1.1.33 2.11.98 2.95 1.86.1.1.26.11.37.01l1.18-1.18a1 1 0 1 1 1.41 1.41l-1.51 1.51c-.08.08-.1.2-.05.3.47.94.78 2 .92 3.12.01.13.12.23.25.23H21a1 1 0 1 1 0 2h-1.82c-.13 0-.24.1-.25.23a9.68 9.68 0 0 1-.92 3.12Z" /></svg>;

export const ClydeIcon = props => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="currentColor" d="M19.73 4.87a18.2 18.2 0 0 0-4.6-1.44c-.21.4-.4.8-.58 1.21-1.69-.25-3.4-.25-5.1 0-.18-.41-.37-.82-.59-1.2-1.6.27-3.14.75-4.6 1.43A19.04 19.04 0 0 0 .96 17.7a18.43 18.43 0 0 0 5.63 2.87c.46-.62.86-1.28 1.2-1.98-.65-.25-1.29-.55-1.9-.92.17-.12.32-.24.47-.37 3.58 1.7 7.7 1.7 11.28 0l.46.37c-.6.36-1.25.67-1.9.92.35.7.75 1.35 1.2 1.98 2.03-.63 3.94-1.6 5.64-2.87.47-4.87-.78-9.09-3.3-12.83ZM8.3 15.12c-1.1 0-2-1.02-2-2.27 0-1.24.88-2.26 2-2.26s2.02 1.02 2 2.26c0 1.25-.89 2.27-2 2.27Zm7.4 0c-1.1 0-2-1.02-2-2.27 0-1.24.88-2.26 2-2.26s2.02 1.02 2 2.26c0 1.25-.88 2.27-2 2.27Z" /></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 GameControllerIcon = props => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="currentColor" fill-rule="evenodd" d="M20.97 4.06c0 .18.08.35.24.43.55.28.9.82 1.04 1.42.3 1.24.75 3.7.75 7.09v4.91a3.09 3.09 0 0 1-5.85 1.38l-1.76-3.51a1.09 1.09 0 0 0-1.23-.55c-.57.13-1.36.27-2.16.27s-1.6-.14-2.16-.27c-.49-.11-1 .1-1.23.55l-1.76 3.51A3.09 3.09 0 0 1 1 17.91V13c0-3.38.46-5.85.75-7.1.15-.6.49-1.13 1.04-1.4a.47.47 0 0 0 .24-.44c0-.7.48-1.32 1.2-1.47l2.93-.62c.5-.1 1 .06 1.36.4.35.34.78.71 1.28.68a42.4 42.4 0 0 1 4.4 0c.5.03.93-.34 1.28-.69.35-.33.86-.5 1.36-.39l2.94.62c.7.15 1.19.78 1.19 1.47ZM20 7.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM15.5 12a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM5 7a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2H7v1a1 1 0 1 1-2 0v-1H4a1 1 0 1 1 0-2h1V7Z" clip-rule="evenodd" /></svg>;

export const ShapesIcon = props => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="currentColor" d="M2.06 7.61c-.25.95.31 1.92 1.26 2.18l4.3 1.15c.94.25 1.91-.31 2.17-1.26l1.15-4.3c.25-.94-.31-1.91-1.26-2.17l-4.3-1.15c-.94-.25-1.91.31-2.17 1.26l-1.15 4.3ZM12.98 7.87a2 2 0 0 0 1.75 2.95H20a2 2 0 0 0 1.76-2.95l-2.63-4.83a2 2 0 0 0-3.51 0l-2.63 4.83ZM5.86 13.27a.89.89 0 0 1 1.28 0l.75.77a.9.9 0 0 0 .54.26l1.06.12c.5.06.85.52.8 1.02l-.13 1.08c-.02.2.03.42.14.6l.56.92c.27.43.14 1-.28 1.26l-.9.58a.92.92 0 0 0-.37.48l-.36 1.02a.9.9 0 0 1-1.15.57l-1-.36a.89.89 0 0 0-.6 0l-1 .36a.9.9 0 0 1-1.15-.57l-.36-1.02a.92.92 0 0 0-.37-.48l-.9-.58a.93.93 0 0 1-.28-1.26l.56-.93c.11-.17.16-.38.14-.59l-.12-1.08c-.06-.5.3-.96.8-1.02l1.05-.12a.9.9 0 0 0 .54-.26l.75-.77ZM18.52 13.71a1.1 1.1 0 0 0-2.04 0l-.46 1.24c-.19.5-.57.88-1.07 1.07l-1.24.46a1.1 1.1 0 0 0 0 2.04l1.24.46c.5.19.88.57 1.07 1.07l.46 1.24c.35.95 1.7.95 2.04 0l.46-1.24c.19-.5.57-.88 1.07-1.07l1.24-.46a1.1 1.1 0 0 0 0-2.04l-1.24-.46a1.8 1.8 0 0 1-1.07-1.07l-.46-1.24Z" /></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>;

<img src="https://mintcdn.com/discord-markm-docs-lobby-membership-disconnect/MXDLwy4pCNDMm72Q/images/headers/docs-home-hero.png?fit=max&auto=format&n=MXDLwy4pCNDMm72Q&q=85&s=ab8e90b97d8ea6ef92ee5fe921308c20" alt="Discord Developer Platform" style={{width: "100%", height: "auto"}} noZoom width="1480" height="600" data-path="images/headers/docs-home-hero.png" />

## What do you want to build?

<CardGroup cols={3}>
  <Card title="Bots & Apps" href="/developers/platform/bots" icon={<RobotIcon />}>
    Build automated apps, commands, and integrations that run inside Discord servers.
  </Card>

  <Card title="Activities" href="/developers/platform/activities" icon={<ShapesIcon />}>
    Create multiplayer games and social experiences that launch directly inside Discord.
  </Card>

  <Card title="Social Layer for Games" href="/developers/platform/social-layer" icon={<GameControllerIcon />}>
    Add rich presence, voice chat, and social features to your game across all platforms.
  </Card>
</CardGroup>

## New to Discord development?

<CardGroup cols={2}>
  <Card title="Overview of Apps" href="/developers/quick-start/overview-of-apps" icon={<RobotIcon />}>
    Understand what Discord apps are, the different types, and how to get started.
  </Card>

  <Card title="Build Your First App" href="/developers/quick-start/getting-started" icon={<WrenchIcon />}>
    Follow a step-by-step guide to build and run your first Discord bot.
  </Card>
</CardGroup>

## Resources

<CardGroup cols={3}>
  <Card title="Join the Developer Discord" href="https://discord.com/invite/discord-developers" icon={<ClydeIcon />}>
    Get support, API announcements, and participate in developer events.
  </Card>

  <Card title="Report Issues on GitHub" href="https://github.com/discord/discord-api-docs" icon={<BugIcon />}>
    Report issues with the Discord API and SDKs.
  </Card>

  <Card title="Visit the Developer Help Center" href="https://support-dev.discord.com/" icon={<CircleQuestionIcon />}>
    Find articles, FAQs, and reach out to Discord's developer support team.
  </Card>
</CardGroup>

## Featured Videos

<YouTubePlaylistCarousel list="PLZGLlvDRRGfw" description="A few of our favourite videos for you to get started on your journey with the Discord Developer Platform." />
