---
title: "Keep parties together"
description: "Invite friends, accept join codes, and move a party into a game."
seo:
  noindex: true
---

{/* Copyright (c) 2026 Betide Studio. All Rights Reserved. */}

Use a [lobby](/crossplay/lobbies) as the party. Keep it while players choose a mode or wait for a match; your game owns ready states and party rules.

A Discord friend receives a Discord activity invite carrying the primary lobby's secret, whether Discord is a social provider or a lobby mirror; accepting it in Discord raises **On Lobby Join Requested** with the primary lobby and UCIK joins it and its mirrors. Discord shows the invite itself, so **On Lobby Invite Received** does not fire for Discord. The join only works after the player has signed in; an invite accepted on the login screen is rejected until the game logs in and the player presses Join again.

## Invite a player

**Blueprint**

<BlueprintViewer title="Invite a player" src="/blueprints/crossplay-send-party-invite.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

void InviteToParty(UGameInstance &GameInstance, const FUCIK_PlayerHandle &Player)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Service)
        return;

    Service->SendInvite(
        TEXT("GameLobby"), Player, EUCIK_InvitePriority::Auto,
        FBetideCallback<TMap<FBetideProviderId, bool>>::CreateWeakLambda(
            &GameInstance, [](const TBetideResult<TMap<FBetideProviderId, bool>> &Result) {
                if (!Result.bOk)
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                    return;
                }
                for (const TPair<FBetideProviderId, bool> &Receipt : Result.GetValue())
                    UE_LOG(LogUCIK, Log, TEXT("%s: %d"), *Receipt.Key.ToString(), Receipt.Value);
            }));
}
```

Use the player's full handle from friends or profiles. **Auto** chooses a compatible invite route; a successful provider map may contain only some requested providers.

## Accept an invite

**Blueprint**

<Accordion>
  <AccordionItem title="Read pending party invites">
<BlueprintViewer title="Read pending party invites" src="/blueprints/crossplay-pending-party-invites.txt" />
  </AccordionItem>
  <AccordionItem title="Accept an invite">
<BlueprintViewer title="Accept an invite" src="/blueprints/crossplay-accept-party-invite.txt" />
  </AccordionItem>
</Accordion>

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

void ReadPartyInvites(UGameInstance &GameInstance)
{
    if (auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>())
    {
        for (const FUCIK_LobbyInvite &Invite : Service->GetPendingInvites())
            UE_LOG(LogUCIK, Log, TEXT("%s"), *Invite.From.ToString());
    }
}

void AcceptPartyInvite(UGameInstance &GameInstance, const FUCIK_LobbyHandle &InvitedLobby)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Service)
        return;

    Service->AcceptInvite(
        TEXT("GameLobby"), InvitedLobby, false,
        FBetideCallback<FUCIK_Lobby>::CreateWeakLambda(
            &GameInstance, [](const TBetideResult<FUCIK_Lobby> &Result) {
                if (!Result.bOk)
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                    return;
                }
                UE_LOG(LogUCIK, Log, TEXT("Joined party: %s"), *Result.GetValue().Name.ToString());
            }));
}
```

Bind **On Lobby Invite Received** before showing the menu. Read pending invites when reopening it. Accepting joins the lobby and removes the pending invite; rejecting removes it and informs the service where supported.

## Join with a code

**Blueprint**

<BlueprintViewer title="Join with a code" src="/blueprints/crossplay-join-party-code.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

void JoinPartyCode(UGameInstance &GameInstance, const FString &Code)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Service)
        return;

    Service->JoinLobbyByCode(
        TEXT("GameLobby"), Code, false,
        FBetideCallback<FUCIK_Lobby>::CreateWeakLambda(
            &GameInstance, [](const TBetideResult<FUCIK_Lobby> &Result) {
                if (!Result.bOk)
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                    return;
                }
                UE_LOG(LogUCIK, Log, TEXT("Joined party: %s"), *Result.GetValue().Name.ToString());
            }));
}
```

Set **Lobby Code** when creating the lobby: 4–60 letters, digits, `-`, or `_`. Steam code discovery requires a public lobby. Treat codes as a way to find a party, not a secret access-control system.

## Overlay and invite cleanup

**Blueprint**

<Accordion>
  <AccordionItem title="Open the invite picker">
<BlueprintViewer title="Open the invite picker" src="/blueprints/crossplay-show-invite-ui.txt" />
  </AccordionItem>
  <AccordionItem title="Reject a party invite">
<BlueprintViewer title="Reject a party invite" src="/blueprints/crossplay-reject-party-invite.txt" />
  </AccordionItem>
  <AccordionItem title="Send a session invite">
<BlueprintViewer title="Send a session invite" src="/blueprints/crossplay-send-session-invite.txt" />
  </AccordionItem>
  <AccordionItem title="Accept a session invite">
<BlueprintViewer title="Accept a session invite" src="/blueprints/crossplay-accept-session-invite.txt" />
  </AccordionItem>
</Accordion>

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

<Accordion>

  <AccordionItem title="Open the invite picker">

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

void ShowPartyInviteUI(UGameInstance &GameInstance)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Service)
        return;
    Service->ShowInviteUI(
        TEXT("GameLobby"),
        FBetideCallback<bool>::CreateWeakLambda(&GameInstance, [](const TBetideResult<bool> &Result) {
            if (!Result.bOk)
            {
                UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                return;
            }
            UE_LOG(LogUCIK, Log, TEXT("Operation completed: %d"), Result.GetValue());
        }));
}
```

  </AccordionItem>

  <AccordionItem title="Reject a party invite">

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

void RejectPartyInvite(UGameInstance &GameInstance, const FUCIK_LobbyHandle &InvitedLobby)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Service)
        return;
    Service->RejectInvite(
        InvitedLobby,
        FBetideCallback<bool>::CreateWeakLambda(&GameInstance, [](const TBetideResult<bool> &Result) {
            if (!Result.bOk)
            {
                UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                return;
            }
            UE_LOG(LogUCIK, Log, TEXT("Operation completed: %d"), Result.GetValue());
        }));
}
```

  </AccordionItem>

  <AccordionItem title="Send a session invite">

```cpp
#include "Engine/GameInstance.h"
#include "Sessions/UCIKSessionSubsystem.h"
#include "UCIKCore.h"

void SendSessionInvite(UGameInstance &GameInstance, const FUCIK_PlayerHandle &Player)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKSessionSubsystem>();
    if (!Service)
        return;
    Service->SendInvite(
        TEXT("GameSession"), Player,
        FBetideCallback<bool>::CreateWeakLambda(&GameInstance, [](const TBetideResult<bool> &Result) {
            if (!Result.bOk)
            {
                UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                return;
            }
            UE_LOG(LogUCIK, Log, TEXT("Operation completed: %d"), Result.GetValue());
        }));
}
```

  </AccordionItem>

  <AccordionItem title="Accept a session invite">

```cpp
#include "Engine/GameInstance.h"
#include "Sessions/UCIKSessionSubsystem.h"
#include "UCIKCore.h"

void AcceptSessionInvite(UGameInstance &GameInstance, const FUCIK_SessionHandle &InvitedSession)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKSessionSubsystem>();
    if (!Service)
        return;
    Service->AcceptInvite(TEXT("GameSession"), InvitedSession, true,
                          FBetideCallback<FUCIK_Session>::CreateWeakLambda(
                              &GameInstance, [](const TBetideResult<FUCIK_Session> &Result) {
                                  if (!Result.bOk)
                                  {
                                      UE_LOG(LogUCIK, Warning, TEXT("%s"),
                                             *Result.Error.GetLogString());
                                      return;
                                  }
                                  UE_LOG(LogUCIK, Log, TEXT("Operation completed: %d"), true);
                              }));
}
```

  </AccordionItem>

</Accordion>

Session invites require a player on the session’s primary provider. Read the handle from **On Session Invite Received** or **Get Pending Crossplay Session Invites**; **Reject Crossplay Session Invite** declines it (EOS tells the sender, other providers only clear it locally). The accept example enables automatic travel; keep it in a GameInstance Blueprint so it survives the map change.

For parties that follow a host into a match, use [lobby hosting and readiness](/crossplay/listen-server). Do not trigger a second travel if **Party Follows Host** already handles it.
