.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.AspNetCoreUsage
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 });