Skip to content

Challenges

dblike edited this page Jan 20, 2026 · 1 revision

Challenges API

Send and receive challenges to play games.

Namespace: LichessSharp.Api.Contracts Access: client.Challenges Required Scope: challenge:read, challenge:write, bot:play, or board:play


Quick Start: Challenge a Player

using LichessSharp;
using LichessSharp.Api.Contracts;

using var client = new LichessClient(token);

// Challenge someone to a 5+3 blitz game
var challenge = await client.Challenges.CreateAsync("opponent_username", new ChallengeCreateOptions
{
    Rated = true,
    ClockLimit = 300,    // 5 minutes in seconds
    ClockIncrement = 3,  // 3 second increment
    Color = ChallengeColor.Random
});

Console.WriteLine($"Challenge sent: {challenge.Url}");
Console.WriteLine($"Status: {challenge.Status}");

Managing Challenges

GetPendingAsync

Get all pending challenges (incoming and outgoing).

Required Scope: challenge:read

Task<ChallengeList> GetPendingAsync(CancellationToken cancellationToken = default)

Example:

using var client = new LichessClient(token);

var pending = await client.Challenges.GetPendingAsync();

Console.WriteLine("Incoming challenges:");
foreach (var challenge in pending.In ?? [])
{
    Console.WriteLine($"  From {challenge.Challenger?.Name}: {challenge.TimeControl?.Show} {challenge.Variant?.Name}");
}

Console.WriteLine("\nOutgoing challenges:");
foreach (var challenge in pending.Out ?? [])
{
    Console.WriteLine($"  To {challenge.DestUser?.Name}: {challenge.Status}");
}

ShowAsync

Get details of a specific challenge.

Required Scope: challenge:read

Task<ChallengeJson> ShowAsync(string challengeId, CancellationToken cancellationToken = default)

Example:

using var client = new LichessClient(token);

var challenge = await client.Challenges.ShowAsync("challengeId");

Console.WriteLine($"Challenge: {challenge.Id}");
Console.WriteLine($"Status: {challenge.Status}");
Console.WriteLine($"Challenger: {challenge.Challenger?.Name} ({challenge.Challenger?.Rating})");
Console.WriteLine($"Opponent: {challenge.DestUser?.Name} ({challenge.DestUser?.Rating})");
Console.WriteLine($"Time Control: {challenge.TimeControl?.Show}");
Console.WriteLine($"Rated: {challenge.Rated}");
Console.WriteLine($"Variant: {challenge.Variant?.Name}");

AcceptAsync

Accept an incoming challenge.

Task<bool> AcceptAsync(string challengeId, CancellationToken cancellationToken = default)

Example:

using var client = new LichessClient(token);

// Accept a specific challenge
await client.Challenges.AcceptAsync("challengeId");

// Accept all incoming challenges
var pending = await client.Challenges.GetPendingAsync();
foreach (var challenge in pending.In ?? [])
{
    await client.Challenges.AcceptAsync(challenge.Id);
    Console.WriteLine($"Accepted challenge from {challenge.Challenger?.Name}");
}

DeclineAsync

Decline an incoming challenge with an optional reason.

Task<bool> DeclineAsync(
    string challengeId,
    ChallengeDeclineReason? reason = null,
    CancellationToken cancellationToken = default)

ChallengeDeclineReason Values:

Value Description
Generic Generic decline
Later Will play later
TooFast Time control too fast
TooSlow Time control too slow
TimeControl Time control not acceptable
Rated Only accepting rated games
Casual Only accepting casual games
Standard Only accepting standard chess
Variant Variant not acceptable
NoBot Not accepting challenges from bots
OnlyBot Only accepting challenges from bots

Example:

using var client = new LichessClient(token);

// Decline with a reason
await client.Challenges.DeclineAsync("challengeId", ChallengeDeclineReason.TooFast);

// Decline bullet challenges automatically
var pending = await client.Challenges.GetPendingAsync();
foreach (var challenge in pending.In ?? [])
{
    if (challenge.Speed == "bullet")
    {
        await client.Challenges.DeclineAsync(challenge.Id, ChallengeDeclineReason.TooFast);
    }
}

CancelAsync

Cancel a challenge you created.

Task<bool> CancelAsync(
    string challengeId,
    string? opponentToken = null,
    CancellationToken cancellationToken = default)

Example:

using var client = new LichessClient(token);

await client.Challenges.CancelAsync("challengeId");
Console.WriteLine("Challenge canceled");

Creating Challenges

CreateAsync

Challenge another player to a game.

Task<ChallengeJson> CreateAsync(
    string username,
    ChallengeCreateOptions? options = null,
    CancellationToken cancellationToken = default)

ChallengeCreateOptions:

Property Type Description
Rated bool? Whether the game is rated
ClockLimit int? Initial time in seconds (0-10800)
ClockIncrement int? Increment in seconds (0-180)
Days int? Days per turn for correspondence (1, 2, 3, 5, 7, 10, 14)
Color ChallengeColor? Color preference (Random, White, Black)
Variant string? Variant (standard, chess960, crazyhouse, etc.)
Fen string? Starting FEN for custom positions
Rules ChallengeRules? Extra game rules
Message string? Message to send with challenge

Example - Standard blitz challenge:

using var client = new LichessClient(token);

var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
    Rated = true,
    ClockLimit = 180,     // 3 minutes
    ClockIncrement = 0,
    Color = ChallengeColor.Random
});

Example - Chess960 rapid:

var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
    Rated = true,
    ClockLimit = 900,     // 15 minutes
    ClockIncrement = 10,
    Variant = "chess960"
});

Example - Correspondence:

var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
    Rated = true,
    Days = 3,  // 3 days per move
    Color = ChallengeColor.White
});

Example - Custom position:

var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
    Rated = false,
    ClockLimit = 300,
    ClockIncrement = 3,
    Variant = "fromPosition",
    Fen = "r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4"  // Italian Game
});

Example - With special rules:

var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
    Rated = true,
    ClockLimit = 600,
    ClockIncrement = 0,
    Rules = ChallengeRules.NoAbort | ChallengeRules.NoRematch | ChallengeRules.NoEarlyDraw
});

CreateOpenAsync

Create an open challenge that any two players can join.

Task<ChallengeOpenJson> CreateOpenAsync(
    ChallengeOpenOptions? options = null,
    CancellationToken cancellationToken = default)

ChallengeOpenOptions (extends ChallengeCreateOptions):

Property Type Description
Name string? Name for the challenge
Users IReadOnlyList? Users to notify
ExpiresAt int? Expiration in minutes

Example:

using var client = new LichessClient(token);

var open = await client.Challenges.CreateOpenAsync(new ChallengeOpenOptions
{
    Name = "Club Tournament Match",
    Rated = true,
    ClockLimit = 300,
    ClockIncrement = 3,
    Users = new[] { "player1", "player2" }
});

Console.WriteLine($"Challenge URL: {open.Url}");
Console.WriteLine($"White URL: {open.UrlWhite}");
Console.WriteLine($"Black URL: {open.UrlBlack}");

// Send UrlWhite to one player and UrlBlack to the other

ChallengeAiAsync

Challenge the Lichess AI (Stockfish).

Task<ChallengeAiResponse> ChallengeAiAsync(
    ChallengeAiOptions options,
    CancellationToken cancellationToken = default)

ChallengeAiOptions:

Property Type Description
Level int AI level 1-8 (default 8)
ClockLimit int? Initial time in seconds
ClockIncrement int? Increment in seconds
Days int? Days per turn for correspondence
Color ChallengeColor? Your color preference
Variant string? Variant to play
Fen string? Starting FEN

Example:

using var client = new LichessClient(token);

// Play against max strength AI
var game = await client.Challenges.ChallengeAiAsync(new ChallengeAiOptions
{
    Level = 8,
    ClockLimit = 300,
    ClockIncrement = 3,
    Color = ChallengeColor.White
});

Console.WriteLine($"Game started: {game.Id}");

// Play against lower level AI
var easyGame = await client.Challenges.ChallengeAiAsync(new ChallengeAiOptions
{
    Level = 1,
    ClockLimit = 600,
    ClockIncrement = 0
});

Clock Management

AddTimeAsync

Add time to your opponent's clock (sportsmanship gesture).

Task<bool> AddTimeAsync(
    string gameId,
    int seconds,
    CancellationToken cancellationToken = default)

Parameters:

Name Type Description
gameId string The game ID
seconds int Seconds to add (1-60)

Example:

using var client = new LichessClient(token);

// Give opponent 15 extra seconds
await client.Challenges.AddTimeAsync(gameId, 15);

StartClocksAsync

Start the clocks immediately, even if a player hasn't moved.

Task<bool> StartClocksAsync(
    string gameId,
    string? token1 = null,
    string? token2 = null,
    CancellationToken cancellationToken = default)

Example:

using var client = new LichessClient(token);

// Both players agree to start clocks
await client.Challenges.StartClocksAsync(gameId, player1Token, player2Token);

Integration with Bot/Board APIs

Challenges are commonly handled in the Bot or Board event loops:

using var client = new LichessClient(token);

await foreach (var evt in client.Bot.StreamEventsAsync())
{
    switch (evt.Type)
    {
        case "challenge":
            var challenge = evt.Challenge!;

            // Auto-accept rated blitz from non-bots
            if (challenge.Rated &&
                challenge.Speed == "blitz" &&
                challenge.Challenger?.Title != "BOT")
            {
                await client.Challenges.AcceptAsync(challenge.Id);
            }
            else
            {
                await client.Challenges.DeclineAsync(challenge.Id, ChallengeDeclineReason.Generic);
            }
            break;

        case "gameStart":
            // Handle the game
            break;
    }
}

See Also

Clone this wiki locally