.NET SDK

Integrate Perly churn prevention into your ASP.NET Core application with dependency injection, middleware, and a typed user resolver.

Perly.AspNetCorev1.0.05 minutes

Installation

dotnet add package Perly.AspNetCore

Usage

Register Perly services and middleware in your Program.cs. The SDK integrates with ASP.NET Core's built-in dependency injection and middleware pipeline.

// Program.cs
using Perly.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddPerly(options =>
{
    options.ApiKey = builder.Configuration["PERLY_API_KEY"];
});
builder.Services.AddScoped<IPerlyUserResolver, AppPerlyUserResolver>();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();
app.UsePerlyMiddleware();

app.MapControllers();
app.Run();

Add the API key to your configuration:

// appsettings.json (or use environment variables)
{
  "PERLY_API_KEY": "your-api-key-here"
}

User Resolver

Implement IPerlyUserResolver to map the authenticated user from HttpContext to a Perly user profile.

// Resolvers/AppPerlyUserResolver.cs
using Perly.AspNetCore;

public class AppPerlyUserResolver : IPerlyUserResolver
{
    private readonly IUserService _userService;

    public AppPerlyUserResolver(IUserService userService)
    {
        _userService = userService;
    }

    public PerlyUser? Resolve(HttpContext context)
    {
        var claimsPrincipal = context.User;
        if (claimsPrincipal?.Identity?.IsAuthenticated != true)
            return null;

        var userId = claimsPrincipal.FindFirst("sub")?.Value;
        var user = _userService.GetById(userId);

        return new PerlyBuilder()
            .SetId(userId)
            .SetMetadata(new Dictionary<string, object>
            {
                { "plan", user.Plan },
                { "region", user.Region },
                { "companyId", user.CompanyId }
            })
            .LinkStripeById(user.StripeCustomerId)
            .LinkHubspotById(user.HubspotContactId)
            .Build();
    }
}

Tracking Events

Inject IPerlyClient into your controllers or services to track customer engagement events.

using Perly.AspNetCore;

[ApiController]
[Route("api/[controller]")]
public class OnboardingController : ControllerBase
{
    private readonly IPerlyClient _perly;

    public OnboardingController(IPerlyClient perly) => _perly = perly;

    [HttpPost("complete")]
    public async Task<IActionResult> Complete()
    {
        var userId = User.FindFirst("sub")?.Value;
        await _perly.TrackAsync(userId, "onboarding_completed");
        return Ok(new { success = true });
    }

    [HttpPost("export-report")]
    public async Task<IActionResult> ExportReport([FromBody] ExportRequest request)
    {
        var userId = User.FindFirst("sub")?.Value;
        await _perly.TrackAsync(userId, "report_exported", new { format = request.Format });
        return Ok(new { url = reportUrl });
    }
}

Expansion Signals

Send signals from controllers, background services, or hosted services when customers approach plan limits.

public class UsageCheckService
{
    private readonly IPerlyClient _perly;

    public UsageCheckService(IPerlyClient perly) => _perly = perly;

    public async Task CheckSeatLimitAsync(string userId, int current, int limit)
    {
        if (current > limit * 0.9)
        {
            await _perly.SignalAsync(userId, "seat_limit_near", new
            {
                current,
                limit
            });
        }
    }
}

// Other signal types
await _perly.SignalAsync(userId, "api_usage_high", new { current = 9500, limit = 10000 });
await _perly.SignalAsync(userId, "rate_limit_hit", new { endpoint = "/api/search" });
await _perly.SignalAsync(userId, "token_usage_high", new { current = 950000, limit = 1000000 });
await _perly.SignalAsync(userId, "billing_retry_failed", new { attempt = 3 });