Crews.PlanningCenter.Api 2.0.0

Prefix Reserved
dotnet add package Crews.PlanningCenter.Api --version 2.0.0
                    
NuGet\Install-Package Crews.PlanningCenter.Api -Version 2.0.0
                    
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="Crews.PlanningCenter.Api" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Crews.PlanningCenter.Api" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Crews.PlanningCenter.Api" />
                    
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 Crews.PlanningCenter.Api --version 2.0.0
                    
#r "nuget: Crews.PlanningCenter.Api, 2.0.0"
                    
#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 Crews.PlanningCenter.Api@2.0.0
                    
#: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=Crews.PlanningCenter.Api&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Crews.PlanningCenter.Api&version=2.0.0
                    
Install as a Cake Tool

.NET Planning Center API Library

A client library for the Planning Center API built on the JSON:API Framework.

Installation

Crews.PlanningCenter.Api is available on NuGet:

dotnet add package Crews.PlanningCenter.Api

Configuration

This library uses the options pattern. By default, the following configuration sections are defined:

  • Authentication:PlanningCenter: mapped to the PlanningCenterOAuthOptions class. Used for defining OAuth credentials.
  • PlanningCenterApi: mapped to the PlanningCenterApiOptions class. Used for defining all other options for the library.

OAuth Options

The following are valid options for OAuth authentication:

Option Name Type Description
ClientId string The OAuth client ID.
ClientSecret string The OAuth client secret.

This class also accepts all options defined in the OAuthOptions class.

appsettings.json example:

{
  "Authentication": {
    "PlanningCenter": {
      "ClientId": "my-client-id",
      "ClientSecret": "my-client-secret"
    }
  }
}

Manual configuration example:

builder.Services.AddAuthentication(options =>
{
  options.DefaultScheme = "Cookies";
  options.DefaultChallengeScheme = PlanningCenterOAuthDefaults.AuthenticationScheme;
})
.AddCookie("Cookies")
.AddPlanningCenterOAuth(options =>
{
  // The strings here are hardcoded for example.
  // These credentials should never be stored in your code.
  options.ClientId = "my-client-id";
  options.ClientSecret = "my-secret";
});

API Options

Option Name Type Description Default
ApiBaseAddress Uri The base URL of the Planning Center API. https://api.planningcenteronline.com
HttpClientName string The name of an optional named HttpClient to use in the service.<br><br>NOTE: This client's BaseAddress property will be ignored in favor of the ApiBaseAddress option. N/A
UserAgent string The user agent string to send on API requests. This is required by Planning Center. Generic .NET Planning Center API Client
PersonalAccessToken PlanningCenterPersonalAccessToken An optional Planning Center personal access token. N/A

appsettings.json example:

{
  "PlanningCenterApi": {
    "ApiBaseAddress": "http://some-address.com",
    "HttpClientName": "my-custom-client",
    "UserAgent": "Some custom user agent string",
    "PersonalAccessToken": {
      "AppId": "my-app-id",
      "Secret": "my-secret"
    }
  }
}

Manual configuration example:

builder.Services.AddPlanningCenterApi(options =>
{
  options.ApiBaseAddress = new("http://some-address.com");
  options.HttpClientName = "my-custom-client";
  options.UserAgent = "Some custom user agent string";

  // The strings here are hardcoded for example.
  // These credentials should never be stored in your code.
  options.PersonalAccessToken = new()
  {
    AppId = "my-app-id",
    Secret = "my-secret"
  };
});

Setup

This library can be used with application builders (dependency injection), or as a standalone client.

ASP.NET Core Application Builder

First, add OAuth authentication:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(options =>
{
  options.DefaultScheme = "Cookies";
  options.DefaultChallengeScheme = PlanningCenterOAuthDefaults.AuthenticationScheme;
})
.AddCookie("Cookies")
.AddPlanningCenterOAuth(options =>
{
  // Add scopes for the APIs you need access to
  // By default, the "People" scope is always included
  options.Scope.Clear();
  options.Scope.Add(PlanningCenterOAuthScope.People);
  options.Scope.Add(PlanningCenterOAuthScope.Calendar);
  options.Scope.Add(PlanningCenterOAuthScope.CheckIns);
});

Next, add the Planning Center API service:

builder.Services.AddPlanningCenterApi();

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();

Finally, add authentication endpoints to your app. The OAuth middleware will automatically handle token management and refreshing.

app.MapGet("/login", (HttpContext context, string? returnUrl = null) =>
{
  var properties = new AuthenticationProperties
  {
    RedirectUri = returnUrl ?? "/"
  };
  return Results.Challenge(properties, [PlanningCenterOAuthDefaults.AuthenticationScheme]);
});

app.MapPost("/logout", async (HttpContext context) =>
{
  await context.SignOutAsync("Cookies");
  return Results.Redirect("/");
});

Now you can inject IPlanningCenterApiService into your controllers or services.

[Authorize]
public class MyController(IPlanningCenterApiService _planningCenterApi) : Controller
{
  public async Task<IActionResult> Profile()
  {
    // API calls automatically use the authenticated user's OAuth token, or your personal access token
    var currentUser = await _planningCenterApi.People.LatestVersion
      .GetRelated<PersonResource>("me")
      .GetAsync();

    return View(currentUser.Data);
  }
}

Standalone

Start by creating an HttpClient instance:

HttpClient client = new();
client.SafelySetBaseAddress(new("https://api.planningcenteronline.com"));

// This example uses a personal access token.
// If you want to use OAuth in a standalone client, you're on your own.
PlanningCenterPersonalAccessToken token = new()
{
  AppID = "yourAppIdHere",
  Secret = "superSecretPasswordHere"
};
client.DefaultRequestHeaders.Authorization = token;

Use this HttpClient to create a new API client of your choice, and you're off to the races!

CalendarClient calendar = new(client);

var myEvent = await calendar.LatestVersion.Events.WithID("123").GetAsync();
Console.WriteLine($"My event is called {myEvent.Data.Name}!");

Examples

Fluent API Example

You can chain API resource calls to navigate the API:

var myEvent = await calendar.LatestVersion
  .Events
  .WithID("123")
  .Owner
  .EventResourceRequests
  .WithID("456")
  .ResourceBookings
  .WithID("789")
  .EventInstance
  .Event
  .GetAsync();

Querying Example

You can easily include related resources, or sort and query collections:

var myAttachments = await calendar.LatestVersion.Attachments
  .Include(AttachmentIncludable.Event)
  .OrderBy(AttachmentOrderable.FileSize, Order.Descending)
  .Query((AttachmentQueryable.Name, "myAttachment"), (AttachmentQueryable.Description, "The best attachment."))
  .GetAllAsync();

// Reading included resources must be done manually.
var includedResources = myAttachments.JsonApiDocument.GetIncludedResources();

Pagination Example

You can specify a count and an offset for collections of resources:

var myAttachments = await calendar.LatestVersion.Attachments.GetAllAsync();

Console.WriteLine(myAttachments.Metadata.TotalCount);  // Get total count of items available on the API
Console.WriteLine(myAttachments.Metadata.Next.Offset);  // Get item offset for next page of items

// Get only first five items
myAttachments = await calendar.LatestVersion.Attachments.GetAllAsync(count: 5);

// Get ten items, offset by five items
myAttachments = await calendar.LatestVersion.Attachments.GetAllAsync(count: 10, offset: 5);

Mutation Example

You can also POST, PATCH, and DELETE resources with these options:

var myEventConnection = calendar.LatestVersion
  .Events
  .WithID("123")
  .EventConnections
  .WithID("456");

EventConnection newConnection = new()
{
  ConectedToId = "123",
  ConnectedToName = "Test"
};

// POST
var postResult = await myEventConnection.PostAsync(newConnection);

// PATCH
var patchResult = await myEventConnection.PatchAsync(newConnection);

// DELETE
await myEventConnection.DeleteAsync();

Custom Resource Example

You can define your own resource types by inheriting from either PlanningCenterSingletonFetchableResource or PlanningCenterPaginatedFetchableResource.

This provides forward compatibility by allowing you to define and fetch any resources not yet implemented in this library.

Model Creation

First, create a model class:

[JsonApiName("my_custom_resource")]
public record MyCustomModel
{
  [JsonApiName("my_first_property")]
  public string? MyFirstProperty { get; init; }

  [JsonProperty("my_second_property")]
  public int? MySecondProperty { get; init; }
}

Next, you can create your resource class.

Singleton Resource
public class MyCustomResource 
  : PlanningCenterSingletonFetchableResource<MyCustomModel, MyCustomResource, PeopleDocumentContext>,
  IIncludable<MyCustomResource, MyCustomResourceIncludable>
{
  // In this example, your resoruce has child resources of type "people"
  public PeopleResourceCollection People => GetRelated<PeopleResourceCollection>("people");

  public MyCustomSingletonResource(Uri uri, HttpClient client) : base(uri, client) { }

  // In this example, your resource has certain includable resources
  public MyCustomResource Include(params MyCustomResourceIncludable[] included) => base.Include(included);
}
Paginated Resource

Creating a paginated resource type requires the existence of a singleton resource type for the same model.

public class MyCustomResourceCollection 
  : PlanningCenterPaginatedFetchableResource<MyCustomModel, MyCustomResourceCollection, MyCustomResource, PeopleDocumentContext>,
  IIncludable<MyCustomResourceCollection, MyCustomResourceIncludable>,
  IOrderable<MyCustomResourceCollection, MyCustomResourceOrderable>,
  IFilterable<MyCustomResourceCollection, MyCustomResourceQueryable>
{
  public MyCustomResourceCollection(Uri uri, HttpClient client) : base(uri, client) { }

  // In this example, your resource has certain includable resources
  public MyCustomResourceCollection Include(params MyCustomResourceIncludable[] included) => base.Include(included);

  // In this example, your resource has certain orderable properties
  public MyCustomResourceCollection OrderBy(MyCustomResourceOrderable orderable, Order order = Order.Ascending) => base.OrderBy(orderable, order);

  // In this example, your resource has certain queryable properties
  public MyCustomResourceCollection Query(params (MyCustomResourceQueryable, string)[] queries) => base.Query(queries);
}
Accessing your custom resources

Simply call the GetRelated<T>() method on any singleton resource to access your custom resource:

// Singleton
var currentUser = await planningCenterApi.People.LatestVersion
  .GetRelated<MyCustomResource>("my_custom_resource");

// Paginated
var customResources = await planningCenterApi.People.LatestVersion
  .GetRelated<MyCustomResourceCollection>("my_custom_resources");

S.D.G.

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 was computed.  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 was computed.  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
2.0.0 130 9/25/2025
1.2.0 191 8/29/2025
1.1.0 142 1/23/2025
1.0.2 104 1/15/2025
1.0.0-preview.14 85 12/6/2024
1.0.0-preview.13 76 11/30/2024
1.0.0-preview.12 74 11/29/2024
1.0.0-preview.11 88 11/11/2024
1.0.0-preview.10 82 11/11/2024
1.0.0-preview.9 78 11/9/2024
1.0.0-preview.8 82 11/9/2024
1.0.0-preview.7 79 10/16/2024
1.0.0-preview.6 84 10/15/2024
1.0.0-preview.5 81 10/3/2024
1.0.0-preview.4 71 10/3/2024
1.0.0-preview.3 86 9/25/2024
1.0.0-preview.2 89 5/22/2024
1.0.0-preview.1 85 5/22/2024