SDKs

.NET

Read identification events with a secret key and verify signed webhooks from .NET 6 and newer, with async APIs and dependency injection.

The Fingerly NuGet package reads stored events by request ID and verifies webhook signatures. Every call is asynchronous and accepts a CancellationToken.

Requirements

Install

dotnet add package Fingerly

Read an event

Create one client with your secret key and reuse it. The key decides the regional API and the environment the client reads.

.NET
using Fingerly;

var fingerly = new FingerlyClient(Environment.GetEnvironmentVariable("FINGERLY_SECRET_KEY"));
var ev = await fingerly.Events.GetAsync("01a0a84b-e6a2-7c09-9f51-0b3d7a26c8e4");

An event has the fields listed in Get an event. suspect_score is null when the request was not scored.

Verify a checkout

Read the event your client identified, check it belongs to this action and is recent, then decide on its level. See server-side verification.

.NET
public async Task<string> DecideAsync(string orderId, string requestId, CancellationToken ct)
{
    Event ev;
    try
    {
        ev = await _fingerly.Events.GetAsync(requestId, ct);
    }
    catch (FingerlyApiException e) when (e.Status == 404)
    {
        return "refuse";
    }

    if (ev.Tag != $"checkout:{orderId}") return "refuse";
    if (DateTimeOffset.UtcNow - ev.OccurredAt > TimeSpan.FromMinutes(2)) return "refuse";

    return ev.SuspectLevel switch
    {
        "high" => "review",
        "medium" => "challenge",
        _ => "allow",
    };
}

Verify a webhook

Check the signature over the raw request body before parsing it. The helper rejects timestamps more than five minutes from now.

.NET
app.MapPost("/webhooks/fingerly", async (HttpRequest request, IEventQueue queue) =>
{
    using var reader = new StreamReader(request.Body);
    var body = await reader.ReadToEndAsync();

    var valid = FingerlyWebhook.Verify(
        secret: builder.Configuration["Fingerly:WebhookSecret"]!,
        payload: body,
        timestamp: request.Headers["x-fingerly-timestamp"],
        signature: request.Headers["x-fingerly-signature"]);
    if (!valid) return Results.BadRequest();

    var ev = FingerlyWebhook.Parse(body);
    await queue.EnqueueAsync(ev.Id, body);   // deduplicate on the event ID
    return Results.NoContent();
});

Dependency injection

Program.cs
builder.Services.AddFingerly(options =>
{
    options.SecretKey = builder.Configuration["Fingerly:SecretKey"];
});

// Inject FingerlyClient wherever it is needed. It is registered as a singleton.

API

MemberReturnsNotes
new FingerlyClient(secretKey)FingerlyClientOr new FingerlyClient(new FingerlyClientOptions { ... }) for Endpoint and HttpClient.
Events.GetAsync(requestId, ct)Task<Event>Throws FingerlyApiException with Status for a non-2xx response.
Events.ListAsync(EventListOptions, ct)Task<EventPage>Options: From, To (DateTimeOffset), Page, Limit, Visitor, Level.
FingerlyWebhook.Verify(secret, payload, timestamp, signature)boolFive minutes of tolerance. FingerlyWebhook.Parse reads the envelope.

Event properties are PascalCase: ev.RequestId, ev.SuspectLevel, ev.OccurredAt.