FoPost.AspNetCore 0.1.1

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

FoPost for ASP.NET Core

NuGet Downloads CI License

Official ASP.NET Core integration for the FoPost API. Schedule and publish to +30 social platforms from your code.

This package is a thin wrapper around FoPost.Sdk. It adds nothing to the API surface — no models, no HTTP, no retries, no error types. What it adds is the wiring an ASP.NET Core app expects:

  • services.AddFoPost(…) — the options pattern, validated at startup, client registered in DI
  • an IHttpClientFactory client under the hood, so handlers and DNS behave
  • app.MapFoPostWebhook("/fopost/webhook") — raw-body signature verification and typed handlers
  • services.AddFoPostHealthCheck() — a health check that never mentions your key

Requirements

Installation

dotnet add package FoPost.AspNetCore

FoPost.Sdk comes with it.

Quick start

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddFoPost();                       // binds the "FoPost" configuration section
builder.Services.AddFoPostHealthCheck(tags: ["ready"]);

var app = builder.Build();

app.MapHealthChecks("/health");
app.MapFoPostWebhook("/fopost/webhook");

app.MapGet("/workspaces", async (FoPostClient fopost, CancellationToken cancellationToken) =>
    await fopost.Workspaces.ListAsync(cancellationToken));

app.Run();

Configuration

{
  "FoPost": {
    "ApiKey": "fp_your_key_here",
    "BaseUrl": "https://api.fopost.com",
    "Timeout": "00:00:30",
    "MaxRetries": 3,
    "WebhookSecret": "whsec_your_secret_here"
  }
}
Setting Default What it does
ApiKey FOPOST_API_KEY env var Sent as X-API-Key. Required unless BearerToken is set
BearerToken none Dashboard session token for the few endpoints that need one
BaseUrl https://api.fopost.com API root — override for staging or a self-hosted deployment
Timeout 00:00:30 Per-request timeout
MaxRetries 3 Attempts for a rate-limited request, honouring Retry-After
WebhookSecret none Verifies X-FoPost-Signature. Required only for MapFoPostWebhook

Never commit the key. Use user-secrets in development and an environment variable in production — FoPost__ApiKey, or the SDK's own FOPOST_API_KEY, which is consulted when nothing is configured.

The options are validated with ValidateDataAnnotations().ValidateOnStart(), so a missing key or a malformed base URL fails the host at startup rather than on the first request.

Registering without configuration binding

builder.Services.AddFoPost(options =>
{
    options.ApiKey = keyFromYourVault;
    options.MaxRetries = 5;
});

// or bind an explicit section, then adjust
builder.Services.AddFoPost(
    builder.Configuration.GetSection("Integrations:FoPost"),
    options => options.Timeout = TimeSpan.FromSeconds(10));

Injecting the client

FoPostClient is registered as a singleton and behaves like any other service.

[ApiController]
[Route("posts")]
public sealed class PostsController : ControllerBase
{
    private readonly FoPostClient _fopost;

    public PostsController(FoPostClient fopost) => _fopost = fopost;

    [HttpPost]
    public async Task<IActionResult> Create(DraftRequest request, CancellationToken cancellationToken)
    {
        var post = await _fopost.Posts.CreateAsync(
            new CreatePostOptions
            {
                WorkspaceId = request.WorkspaceId,
                Content = [new PostContent(request.Text)],
                Accounts = request.Accounts,
            },
            cancellationToken);

        return Created($"/posts/{post.Id}", post);
    }
}

HTTP client

The client is built on a named IHttpClientFactory client, FoPostDefaults.HttpClientName, so you can decorate it:

builder.Services
    .AddHttpClient(FoPostDefaults.HttpClientName)
    .AddHttpMessageHandler<MyTracingHandler>();

Because FoPostClient is a singleton it holds its HttpClient for the life of the process, which would defeat the factory's handler rotation. So the registration turns rotation off and sets SocketsHttpHandler.PooledConnectionLifetime to two minutes instead — connections are recycled and DNS changes are picked up, which is what the rotation was for.

Receiving webhooks

FoPost signs every delivery with HMAC-SHA256 over the raw request body and sends it as X-FoPost-Signature: sha256=<hex>, alongside X-FoPost-Event and X-FoPost-Delivery.

MapFoPostWebhook reads the raw bytes before anything can re-encode them, compares the digest in constant time, and answers 401 when it does not match — no handler runs. The endpoint is anonymous by design: the signature is its authentication.

builder.Services.AddFoPostWebhookHandler<PublishedPostHandler>();

app.MapFoPostWebhook("/fopost/webhook");

internal sealed class PublishedPostHandler : IFoPostWebhookHandler
{
    // Leave Events empty to receive every event.
    public IReadOnlyCollection<string> Events =>
        [FoPostWebhookEvents.PostPublished, FoPostWebhookEvents.PostFailed];

    public Task HandleAsync(FoPostWebhookEvent webhookEvent, CancellationToken cancellationToken)
    {
        var postId = webhookEvent.Data?["post_id"]?.GetValue<string>();
        // …
        return Task.CompletedTask;
    }
}

Handlers are scoped, so they may take a DbContext or anything else scoped. They run in registration order. Throwing surfaces as a 500, which FoPost retries with backoff — so throw when the work should be retried and swallow when it should not. DeliveryId is unique per send and is what to key on for idempotency.

Events: post.published, post.failed, post.partially_failed, delivery.published, delivery.failed, delivery.delayed, account.health_changed — all on FoPostWebhookEvents.

Health checks

builder.Services.AddFoPostHealthCheck(tags: ["ready"]);

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = registration => registration.Tags.Contains("ready"),
});

The check lists the workspaces the credential can see — the cheapest authenticated call the API offers, so it proves reachability and the credential in one request. A 429 reports Degraded; anything else reports the registered failure status. Descriptions never include the key, the base URL, or a raw provider message.

Errors, retries, and the rest of the API

All of that lives in FoPost.Sdk and is unchanged here: the FoPostException hierarchy (FoPostValidationException, FoPostAuthenticationException, FoPostPaymentRequiredException, FoPostPermissionDeniedException, FoPostNotFoundException, FoPostRateLimitException), automatic retries on 429, the Posts / Accounts / Workspaces / Labels / Ai resources, and RequestAsync for endpoints the SDK does not wrap yet.

See the FoPost.Sdk README for the full API surface, and fopost.com/docs for the product documentation.

Examples

examples/MinimalApi is a runnable app that registers the client, exposes a health endpoint, receives webhooks, and creates a draft post.

Support

Open an issue at fopost/fopost-aspnet, or use the contact form at fopost.com/contact.

Contributing

dotnet build
dotnet test
dotnet pack src/FoPost.AspNetCore/FoPost.AspNetCore.csproj -c Release

FoPost.Sdk is not on NuGet yet, so a local build needs the parent packed into the local-packages/ feed first — see CLAUDE.md, "Parent dependency".

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 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
0.1.1 48 8/31/2026
0.1.0 47 8/31/2026