Skip to content
AXOWLDevelopers
Quickstart

Eventing SDK

Terminal window
dotnet add package Axowl.Sdk.Eventing.Client

The SDK is bi-directional:

  • Publish customer-defined events (custom.wallet.topup, …) — Axowl seals and archives them in the same chain as managed events (user.login, korean_identity.verified, …).
  • Poll events back — cursor-based, unary RPC (no streaming, so it works behind free-tier proxies).
builder.Services.AddAxowlEventing(opts =>
{
opts.ApiKey = builder.Configuration["Axowl:ApiKey"]!; // org API key (ah_live_...)
opts.Transport = TransportMode.GrpcWithFallback;
});
// inject IAxowlEventClient
var sealedEvent = await axowl.PublishAsync(
eventType: "custom.wallet.topup",
businessDataJson: JsonSerializer.Serialize(new { req.UserId, req.Amount }),
triggeredBy: principal.ConnectedId?.ToString()); // the actor's ConnectedId
var page = await axowl.PollAsync(cursor: null, eventTypes: new[] { "custom.wallet.*" });
// page.Events, page.NextCursor (pass it to the next call), page.HasMore

PublishAsync returns the sealed proof — LogId, IntegrityHash, Signature, SealedAt — which you can store next to your own row. Rules the server enforces:

  • eventType must start with custom.; any other name is refused with PERMISSION_DENIED.
  • businessDataJson must be valid JSON (or empty); otherwise INVALID_ARGUMENT.
  • triggeredBy is the actor’s ConnectedId (a GUID), or empty for a system actor; any other value is INVALID_ARGUMENT.
  • A key that lists allowed scopes must include ledger.write to publish; otherwise PERMISSION_DENIED. A key with no allowed-scope list can publish.

PollAsync returns events after cursor in time order. eventTypes accepts wildcards (custom.*); limit defaults to 100 and is capped at 500. Polling serves the most recent three days of events; with no cursor and no sinceUtc it starts from the oldest event in that window. Polling returns the organization’s events, so it needs an org-wide key: a key scoped to one App Group or one application is refused with PERMISSION_DENIED.

OptionDefaultWhat it is
ApiKey— (required)Org API key, sent as Authorization: Bearer ….
TransportTransportMode.GrpcGrpc, GrpcWithFallback (gRPC first, REST when gRPC cannot get through) or RestOnly.
ServerAddresshttps://grpc.axowl.comgRPC endpoint.
RestServerAddresshttps://api.axowl.comREST endpoint (POST /v1/events/publish, POST /v1/events/poll).
CallTimeout10 secondsDeadline for each call.
PollInterval5 secondsPolling service: wait between polls when nothing new arrived.
Limit100Polling service: batch size.
EventTypes / Categoriesempty (everything)Polling service: filters.
SinceUtcnullPolling service: where to start when there is no cursor yet (null = the oldest event in the three-day window).

Pass enablePollingService: true to run a hosted background poller that hands each event to your handlers:

builder.Services.AddAxowlEventing(opts =>
{
opts.ApiKey = builder.Configuration["Axowl:ApiKey"]!;
opts.EventTypes = new[] { "custom.*", "korean_identity.*" };
}, enablePollingService: true);
builder.Services.AddSingleton<IAxowlEventHandler, WalletHandler>();
public sealed class WalletHandler : IAxowlEventHandler
{
public IReadOnlyList<string> EventTypePatterns => new[] { "custom.wallet.*" };
public Task HandleAsync(AxowlEvent e, CancellationToken ct)
{
// e.LogId, e.EventType, e.BusinessData (raw JSON), e.IntegrityHash
return Task.CompletedTask;
}
}

The poller keeps its cursor in memory, so after a restart it starts again from SinceUtc. AxowlEvent.LogId is the idempotency key — de-duplicate on it in your handler. A handler that throws is logged and the event is not delivered to it again.

Events published through the SDK join the sealed audit chain (see Events catalog).