ExpoPushNotifications 1.0.2

dotnet add package ExpoPushNotifications --version 1.0.2
                    
NuGet\Install-Package ExpoPushNotifications -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ExpoPushNotifications" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ExpoPushNotifications" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="ExpoPushNotifications" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ExpoPushNotifications --version 1.0.2
                    
#r "nuget: ExpoPushNotifications, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ExpoPushNotifications@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ExpoPushNotifications&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=ExpoPushNotifications&version=1.0.2
                    
Install as a Cake Tool

expo-push-notifications-dotnet

CI NuGet License: MIT

Server-side library for sending Expo push notifications from .NET applications.

Installation

dotnet add package ExpoPushNotifications

Quick Start

Basic Usage

using ExpoPushNotifications;
using ExpoPushNotifications.Models;

// Create a client
var expo = new Expo(new ExpoClientOptions
{
    AccessToken = Environment.GetEnvironmentVariable("EXPO_ACCESS_TOKEN")
});

// Build messages
var messages = new List<ExpoPushMessage>();

foreach (var pushToken in somePushTokens)
{
    // Validate the token
    if (!Expo.IsExpoPushToken(pushToken))
    {
        Console.WriteLine($"Invalid push token: {pushToken}");
        continue;
    }

    messages.Add(new ExpoPushMessage
    {
        To = [pushToken],
        Title = "Hello",
        Body = "World",
        Sound = "default",
        Data = new Dictionary<string, object> { ["key"] = "value" }
    });
}

// Send notifications in chunks
foreach (var chunk in expo.ChunkPushNotifications(messages))
{
    try
    {
        var tickets = await expo.SendPushNotificationsAsync(chunk);

        foreach (var ticket in tickets)
        {
            if (ticket is ExpoPushErrorTicket errorTicket)
            {
                Console.WriteLine($"Error: {errorTicket.Message}");
            }
        }
    }
    catch (ExpoApiException ex)
    {
        Console.WriteLine($"API error: {ex.Message}");
    }
}

With Dependency Injection (ASP.NET Core)

// In Program.cs
builder.Services.AddExpoClient(options =>
{
    options.AccessToken = builder.Configuration["Expo:AccessToken"];
    options.MaxConcurrentRequests = 6;
    options.AttemptTimeout = TimeSpan.FromSeconds(10);
    options.TotalRequestTimeout = TimeSpan.FromSeconds(100);
});

// In your service
public class NotificationService
{
    private readonly IExpoClient _expo;

    public NotificationService(IExpoClient expo)
    {
        _expo = expo;
    }

    public async Task SendNotificationAsync(string token, string title, string body)
    {
        var messages = new[]
        {
            new ExpoPushMessage
            {
                To = [token],
                Title = title,
                Body = body,
                Sound = "default"
            }
        };

        var tickets = await _expo.SendPushNotificationsAsync(messages);
        // Process tickets...
    }
}

Checking Delivery Receipts

// Collect receipt IDs from successful tickets
var receiptIds = tickets
    .OfType<ExpoPushSuccessTicket>()
    .Select(t => t.Id)
    .ToList();

// Wait some time for delivery (15+ minutes recommended)
await Task.Delay(TimeSpan.FromMinutes(15));

// Retrieve receipts
foreach (var chunk in expo.ChunkPushNotificationReceiptIds(receiptIds))
{
    var receipts = await expo.GetPushNotificationReceiptsAsync(chunk);

    foreach (var (id, receipt) in receipts)
    {
        if (receipt is ExpoPushErrorReceipt errorReceipt)
        {
            Console.WriteLine($"Failed to deliver {id}: {errorReceipt.Message}");

            if (errorReceipt.Details?.Error == PushErrorCode.DeviceNotRegistered)
            {
                // Remove the token from your database
            }
        }
    }
}

API Reference

Expo Class

Method Description
SendPushNotificationsAsync(messages) Sends push notifications and returns tickets
GetPushNotificationReceiptsAsync(receiptIds) Retrieves delivery receipts for sent notifications
ChunkPushNotifications(messages) Splits messages into chunks of 100 recipients max
ChunkPushNotificationReceiptIds(receiptIds) Splits receipt IDs into chunks of 300 max
IsExpoPushToken(token) Validates Expo push token format (static)

Configuration Options

Option Default Description
AccessToken null Expo access token for authenticated requests
MaxConcurrentRequests 6 Maximum concurrent HTTP requests
RetryMinTimeout 1 second Minimum timeout between retries
MaxRetryAttempts 2 Maximum retry attempts for rate-limited requests
AttemptTimeout 10 seconds Timeout for each HTTP attempt (DI only)
TotalRequestTimeout 100 seconds Total timeout including retries (DI only)
BaseUrl https://exp.host API base URL (for testing)

Push Message Properties

Property Type Description
To IReadOnlyList<string> Recipient token(s) - required
Title string? Notification title
Body string? Notification body text
Sound object? Sound configuration ("default" or ExpoPushSound object)
Data IReadOnlyDictionary<string, object>? Custom data payload
Ttl int? Time to live in seconds
Priority PushPriority? Delivery priority (default, normal, high)
Badge int? Badge number to display
ChannelId string? Android notification channel ID
InterruptionLevel InterruptionLevel? iOS interruption level
RichContent ExpoPushRichContent? Rich notification content (images)

Error Codes

Code Description
DeviceNotRegistered The device can no longer receive notifications
MessageTooBig Payload exceeds 4096 bytes
MessageRateExceeded Too many messages sent to this device
InvalidCredentials Push credentials are invalid
ExpoError Expo service error
ProviderError APNs or FCM provider error
DeveloperError SDK/API usage error

Error Handling

The SDK automatically retries rate-limited requests (HTTP 429) with exponential backoff.

try
{
    var tickets = await expo.SendPushNotificationsAsync(messages);
}
catch (ExpoApiException ex) when (ex.IsRateLimitError)
{
    // All retries exhausted
    Console.WriteLine("Rate limited after retries");
}
catch (ExpoApiException ex) when (ex.IsAuthenticationError)
{
    // Invalid access token
    Console.WriteLine("Authentication failed");
}
catch (ExpoApiException ex)
{
    Console.WriteLine($"API error: {ex.Message}, Code: {ex.ErrorCode}");
}

Requirements

  • .NET 8.0, 9.0, or 10.0

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 55 2/7/2026
1.0.1 55 2/5/2026
1.0.0 57 2/2/2026