C# API overview
Every public type of the SDK, grouped by what it is for. The guides explain how to use them; this page is for
looking things up. Types are in the Inngest namespace unless noted.
Registration
Section titled “Registration”| Member | Purpose |
|---|---|
IServiceCollection.AddInngest(Action<InngestOptions>?) |
Registers the SDK and returns an IInngestBuilder. In Microsoft.Extensions.DependencyInjection. |
IServiceCollection.AddInngest(IConfiguration, Action<InngestOptions>?) |
The same, with settings read from a configuration section such as Inngest in appsettings.json. |
IInngestBuilder.AddFunction<TFunction>(FunctionOptions) |
Registers a class function with these options, resolved from a scope for every call. Attributes on the class are ignored. |
IInngestBuilder.AddFunction<TFunction>() |
Registers a class function configured by its attributes. |
IInngestBuilder.AddFunction<TFunction>(Func<FunctionOptions, FunctionOptions>) |
Registers a class function with the options its attributes give, as the callback changes them. |
IInngestBuilder.AddInngestFunctions() |
Written into your project by the source generator, once the project sets InngestGenerateFunctionRegistration to true: registers every class that has [InngestFunction], as AddFunction<TFunction>() does. See Source generator and analyzers. |
IInngestBuilder.AddFunction<TEventData>(FunctionOptions, handler, onFailure?) |
Registers a delegate function with typed event data. |
IInngestBuilder.AddFunction(FunctionOptions, handler, onFailure?) |
Registers a delegate function with JsonElement event data. |
IInngestBuilder.AddMiddleware<TMiddleware>() |
Registers middleware, one instance per call. |
IInngestBuilder.AddMiddleware(InngestMiddleware) |
Registers one middleware instance for all calls. |
IInngestBuilder.AddConnect(Action<ConnectOptions>?) |
Runs the functions in a Connect worker. Package Inngest.Connect. |
IInngestBuilder.AddEntityFrameworkOutbox<TContext>(Action<InngestOutboxOptions>?) |
Adds the transactional outbox and its hosted dispatcher for a DbContext. Package Inngest.EntityFrameworkCore. See Entity Framework Core outbox. |
IEndpointRouteBuilder.MapInngest(string pattern = "/api/inngest") |
Maps the endpoint Inngest calls. Package Inngest.AspNetCore, namespace Microsoft.AspNetCore.Builder. |
IEndpointRouteBuilder.MapInngestRealtime(string pattern, Func<HttpContext, ValueTask<RealtimeSubscription?>> subscription) |
Serves realtime messages to browsers as server-sent events; the callback returns the RealtimeSubscription(string Channel, IReadOnlyList<string> Topics) a request may receive, or null for 403. Package Inngest.AspNetCore, namespace Microsoft.AspNetCore.Builder. See Realtime. |
IHealthChecksBuilder.AddInngest(string? name = null, HealthStatus? failureStatus = null, IEnumerable<string>? tags = null, TimeSpan? timeout = null) |
Adds a health check that tells a readiness probe whether an app that serves functions can work with Inngest: its keys are configured and Inngest answers a request that carries the signing key. Name "inngest" unless given. Package Inngest.AspNetCore, in Microsoft.Extensions.DependencyInjection. See Health checks. |
IHealthChecksBuilder.AddInngest(Action<InngestHealthCheckOptions>, string? name = null, HealthStatus? failureStatus = null, IEnumerable<string>? tags = null, TimeSpan? timeout = null) |
The same, with the options set by the callback. |
IApplicationBuilder.UseInngestDurableEndpoints() |
Runs the endpoints marked as durable as Inngest runs. Add it after routing. Package Inngest.AspNetCore, namespace Microsoft.AspNetCore.Builder. See Durable endpoints. |
IEndpointConventionBuilder.WithDurableEndpoint(DurableEndpointOptions? options = null) |
Marks an endpoint as durable, so that its handler can take IStepTools and run steps. Also WithDurableEndpoint(string id). Package Inngest.AspNetCore, namespace Microsoft.AspNetCore.Builder. |
IServiceCollection.AddInngestRealtimeServerSource() |
Makes Blazor components read realtime messages from Inngest through IInngestRealtime, on the server. Package Inngest.Blazor. In Microsoft.Extensions.DependencyInjection. See Realtime in Blazor. |
IServiceCollection.AddInngestRealtimeRelaySource(Action<RealtimeRelayOptions>) |
Makes Blazor components read the app’s MapInngestRealtime endpoint over an HttpClient, in WebAssembly. Returns the IHttpClientBuilder of that client. Package Inngest.Blazor. The source registered last, of the two, is the one components get. |
IEndpointRouteBuilder.MapInngestWebhook(string pattern, WebhookProvider provider, WebhookOptions? options = null) |
Maps a POST endpoint that verifies a provider’s webhooks and sends each as an event with the delivery id as its id. Package Inngest.AspNetCore, which brings in Inngest.Webhooks, namespace Microsoft.AspNetCore.Builder. See Webhooks. |
IHttpClientBuilder.AddInngestStepFetch() |
Turns the requests an HttpClient sends from a running function into fetch steps, which Inngest makes. Requests from step handlers and from outside functions go out as usual. Add it last. In Microsoft.Extensions.DependencyInjection. |
IInngestBuilder.Services |
The service collection the SDK is registered in. |
AddInngest also registers IInngestClient, IInngestRunsClient and IInngestRealtime, and IStepTools, which
resolves to the steps of the durable endpoint or function that is running.
Options
Section titled “Options”InngestOptions: AppId, AppVersion, EventKey, SigningKey, SigningKeyFallback, Environment,
IsDev, DevServerUrl, BaseUrl, ApiBaseUrl, EventApiBaseUrl, ServeOrigin, ServePath, AllowInBandSync,
AllowUnsignedSync, Streaming, SessionPropagation, Checkpointing, SerializerOptions. See
Configuration.
ConnectOptions: InstanceId, MaxWorkerConcurrency, GatewayUrl. See Connect.
InngestHealthCheckOptions: RequireEventKey, CheckSync, CacheDuration (30 seconds) and FailureCacheDuration
(5 seconds). See Health checks.
FunctionOptions, an immutable record:
| Property | Type |
|---|---|
Id (required) |
string |
Name |
string? |
Triggers (required) |
IReadOnlyList<FunctionTrigger> |
Retries |
int? |
Concurrency |
IReadOnlyList<ConcurrencyLimit>? |
Throttle, RateLimit, Debounce, BatchEvents, Priority |
records of the same names |
Idempotency |
string? |
CancelOn |
IReadOnlyList<CancelOn>? |
Timeouts |
FunctionTimeouts? |
Singleton |
Singleton? |
Middleware |
IReadOnlyList<FunctionMiddleware>?; see Middleware |
Checkpointing |
CheckpointingOptions? |
The flow control records and their constructor arguments and properties:
| Record | Constructor | Properties |
|---|---|---|
ConcurrencyLimit |
(int Limit); 0 means no limit |
Key, Scope (ConcurrencyScope.Function, Environment, Account) |
Throttle |
(int Limit, TimeSpan Period) |
Burst, Key |
RateLimit |
(int Limit, TimeSpan Period) |
Key |
Debounce |
(TimeSpan Period) |
Key, Timeout |
BatchEvents |
(int MaxSize, TimeSpan Timeout) |
Key, If |
Priority |
(string Run) |
|
CancelOn |
(string Event) |
If, Match, Timeout |
FunctionTimeouts |
Start, Finish |
|
Singleton |
(SingletonMode Mode) |
Key; modes Skip and Cancel |
Triggers: Trigger.Event(string name, string? @if = null) and Trigger.Event<T>(string? @if = null), for a
typed event T, return an EventTrigger. Trigger.Cron(string schedule, string? timezone = null) and
Trigger.Cron(string schedule, TimeSpan jitter, string? timezone = null) return a CronTrigger, whose Jitter, from
1 second to 5 minutes, starts each scheduled run up to that much later. EventTrigger and CronTrigger derive from
FunctionTrigger. CancelOn.For<T>() returns a CancelOn for a typed event.
CheckpointingOptions, a record. Checkpointing runs the steps of a function one after another in the same call
from Inngest and saves each to Inngest as it completes, instead of ending the call after every step. It is off by
default. InngestOptions.Checkpointing sets it for every function, and FunctionOptions.Checkpointing, which wins
over it, for one function.
| Property | Default | Meaning |
|---|---|---|
Enabled |
true |
false turns checkpointing off for one function when the app turns it on. |
MaxRuntime |
null: 10 seconds for the serve endpoint, 5 minutes for Connect workers | How long one call may keep running steps. Timeout.InfiniteTimeSpan removes the limit. |
BufferedSteps |
1 |
How many completed steps are saved to Inngest together. |
MaxInterval |
null | The longest a completed step waits to be saved while BufferedSteps is not reached; null waits until the buffer is full or the call ends. |
Attributes
Section titled “Attributes”Each sets the FunctionOptions member of the same name; durations are time strings such as "30s" or "1h30m".
AddFunction<TFunction>() reads them when the app starts: it starts from [InngestFunction] and applies the others,
which derive from FunctionOptionsAttribute, in ascending Order, then in the order they are declared.
| Attribute | Constructor | Properties |
|---|---|---|
InngestFunctionAttribute |
(string id) |
Name, Retries, Idempotency |
EventTriggerAttribute, EventTriggerAttribute<T> |
(string name), () |
If; repeatable |
CronTriggerAttribute |
(string schedule) |
Timezone, Jitter; repeatable |
ConcurrencyAttribute |
(int limit) |
Key, Scope; repeatable |
ThrottleAttribute |
(int limit, string period) |
Burst, Key |
RateLimitAttribute |
(int limit, string period) |
Key |
DebounceAttribute |
(string period) |
Key, Timeout |
BatchEventsAttribute |
(int maxSize, string timeout) |
Key, If |
PriorityAttribute |
(string run) |
|
SingletonAttribute |
(SingletonMode mode) |
Key |
CancelOnAttribute, CancelOnAttribute<T> |
(string name), () |
If, Match, Timeout; repeatable |
TimeoutsAttribute |
Start, Finish |
|
FunctionOptionsAttribute |
Order; Apply(FunctionOptions) returns the options with the attribute applied. Derive from it and override Configure(FunctionOptions) for an attribute of your own. |
The generic forms take the event’s name from T, which implements IInngestEventData.
Functions
Section titled “Functions”| Type | Purpose |
|---|---|
InngestFunction<TEventData> |
Base class of class functions. Override ExecuteAsync(FunctionContext<TEventData>, CancellationToken), which returns Task<object?>. |
InngestFunction<TEventData, TResult> |
Base class of class functions with a result type: ExecuteAsync returns Task<TResult>. InngestFunction<TEventData> derives from it with object?. |
InngestFunction |
The base of both, which the methods that take a function class require. A class cannot derive from it directly. |
IReturns<TResult> |
Implemented by InngestFunction<TEventData, TResult>, so that InvokeAsync<TFunction, TResult> can require the result type. |
FunctionContext |
What every call has: FunctionId, RunId, Attempt, MaxAttempts, Step, Logger, Services, CancellationToken, RequestId, JobId and Sessions, the run’s sessions that what it sends inherits. For code that does not know the event type, such as middleware: EventName, EventId and RawEvent, the triggering event as JSON. |
FunctionContext<TEventData> |
Adds Event and Events. |
IFailureHandler |
Implement OnFailureAsync(FunctionContext<FunctionFailure>, CancellationToken) to handle final failures. |
FunctionFailure |
The failure handler’s event data: FunctionId, RunId, Error, Event. |
FunctionError |
A failure as Inngest stores it: Name, Message, Stack, and Cause, the error that caused it. |
SystemEvents |
The names of the events Inngest sends about runs: FunctionFailed, FunctionFinished, FunctionCancelled, FunctionInvoked, ScheduledTimer. |
FunctionFinished |
The data of inngest/function.finished, sent when a run ends however it ends: FunctionId, RunId, Result, Error, Event, CorrelationId. |
FunctionCancelled |
The data of inngest/function.cancelled: FunctionId, RunId, Event, CorrelationId. |
IStepTools, the type of context.Step:
| Method | Returns |
|---|---|
RunAsync<T>(string id, Func<CancellationToken, Task<T>> handler) |
Task<T> |
SleepAsync(string id, TimeSpan duration) |
Task |
SleepUntilAsync(string id, DateTimeOffset until) |
Task |
WaitForEventAsync<TData>(string id, WaitForEventOptions options) |
Task<InngestEvent<TData>?> |
InvokeAsync<TResult>(string id, InvokeOptions options) |
Task<TResult> |
SendEventAsync(string id, IReadOnlyList<InngestEvent> events) |
Task<SendEventsResult> |
InvokeAsync<TFunction, TResult>(string id, object? data = null, TimeSpan? timeout = null) |
Task<TResult>; runs the function registered as TFunction, which must return a TResult |
WaitForSignalAsync<TData>(string id, WaitForSignalOptions options) |
Task<InngestSignal<TData>?>; null when the timeout passes first |
SendSignalAsync(string id, InngestSignal signal) |
Task<string?>: the id of the run the signal resumed, or null when no run waits for it |
FetchAsync(string id, HttpRequestMessage request) |
Task<HttpResponseMessage>; Inngest makes the request, and the response is memoized |
InferAsync<TResponse>(string id, InferOptions options) |
Task<TResponse>; Inngest sends the request to the AI provider, and the response is memoized |
AIWrapAsync<TInput, TResult>(string id, TInput input, Func<TInput, CancellationToken, Task<TResult>> handler) |
Task<TResult>; runs your own model call like a run step that the Inngest UI shows as one, with the input; a rerun with an input edited there passes the edited input |
RunScopedAsync<T>(string id, Func<IServiceProvider, CancellationToken, Task<T>> handler) |
Task<T>; runs the handler once like a run step, in a dependency injection scope of its own that is created when the handler starts and disposed when it ends. See Scoped steps |
PublishAsync<T>(string id, RealtimeTopic topic, T data) |
Task<T>; publishes a realtime message once, tagged with the run; also for a RealtimeTopic<T> |
PublishStreamAsync(string id, RealtimeTopic topic, IAsyncEnumerable<string> chunks) |
Task<string>; streams the text to the topic as it arrives and memoizes all of it, so a replay does not read the chunks again |
ParallelAsync<T>(ParallelMode mode, Func<Task<T>> steps) |
Task<T>; runs the callback with the mode applied to every step it creates |
The members after the first six have default implementations, so a fake only has to implement the six. The defaults
of InvokeAsync<TFunction, TResult>, WaitForSignalAsync, SendSignalAsync, FetchAsync, InferAsync,
AIWrapAsync, RunScopedAsync, PublishAsync and PublishStreamAsync throw NotSupportedException. Every method but ParallelAsync also has an overload that takes a StepOptions in place of
the id.
StepToolsExtensions adds RunAsync overloads for handlers that take no token, return no value or are
synchronous, a non-generic WaitForEventAsync that returns JsonElement data, and SendEventAsync for a single
event. For a typed event T it adds WaitForEventAsync<T>(string id, TimeSpan timeout, string? match = null, string? @if = null) and SendEventAsync<T> for one T or several. It also adds a non-generic WaitForSignalAsync
with JsonElement data, an InferAsync that returns JsonElement, an AIWrapAsync whose handler takes no token,
and ParallelAsync(ParallelMode, Func<Task>) for steps without a result. For scoped steps it adds a RunScopedAsync
whose handler returns no result, and RunScopedAsync<TService, T> and RunScopedAsync<TService>, whose handler takes
the TService that the SDK resolves from the step’s scope with GetRequiredService. These methods also take a StepOptions in place of the id, except ParallelAsync.
| Record | Constructor | Properties |
|---|---|---|
StepOptions |
(string Id) |
Name, shown in the Inngest UI; ParallelMode, for this step instead of the mode of an enclosing ParallelAsync. A string converts to it implicitly. |
WaitForEventOptions |
(string Event, TimeSpan Timeout) |
Match, If |
InvokeOptions |
(string FunctionId) |
AppId, Data, User, Timeout, Meta, the sessions of the invoked run |
WaitForSignalOptions |
(string Signal, TimeSpan Timeout) |
OnConflict: SignalConflict.Fail, the default, fails the run when another run already waits for the signal; Replace takes the signal over |
InngestSignal |
(string Name, object? Data = null) |
A signal to send. |
InngestSignal<TData> |
(string Name, TData Data) |
A signal as the waiting run receives it. |
InferOptions |
(AIFormat Format, Uri Url, object Body) |
AuthKey, Headers |
ParallelMode is Wait, the default, where Inngest calls the function again once all the steps started together
have finished, or Race, where it calls the function again each time one of them finishes. See
Parallel steps. AIFormat names the provider API: OpenAIChat,
OpenAIResponses, Anthropic, Gemini, Grok or AzureOpenAI.
Fan-out
Section titled “Fan-out”FanOutExtensions adds methods to IStepTools that run a step for each item of an IReadOnlyList<TItem>, with the id
id[index], and return the results, as arrays like Task.WhenAll, in the order of the items. They need nothing from the
engine’s public surface, so they also work on a fake of IStepTools. Each takes FanOutOptions? options = null and CancellationToken cancellationToken = default
after the arguments listed here. See Fan-out over a list.
| Method | Returns |
|---|---|
MapAsync<TItem, TResult>(string id, IReadOnlyList<TItem> items, Func<TItem, CancellationToken, Task<TResult>> handler) |
Task<TResult[]>; the first item that fails for good stops the list, and its StepFailedException is thrown |
ForEachAsync<TItem>(string id, IReadOnlyList<TItem> items, Func<TItem, CancellationToken, Task> handler) |
Task; the same for a handler without a result |
MapOutcomesAsync<TItem, TResult>(string id, IReadOnlyList<TItem> items, Func<TItem, CancellationToken, Task<TResult>> handler) |
Task<FanOutOutcome<TResult>[]>; every item runs, and each has an outcome |
MapInChunksAsync<TFunction, TItem, TResult>(string id, IReadOnlyList<TItem> items, int chunkSize) |
Task<TResult[]>; each chunk of chunkSize items runs in a run of the FanOutFunction<TItem, TResult> class TFunction, with the id id[first-last] |
MapOutcomesInChunksAsync<TFunction, TItem, TResult>(string id, IReadOnlyList<TItem> items, int chunkSize) |
Task<FanOutOutcome<TResult>[]> |
Inngest fails a run once 1,000 of its steps have completed, and every step of the run counts. So the methods without
chunks refuse a list of 1,000 items or more, and the chunked methods a chunkSize of 1,000 or more and a list that makes
1,000 chunks or more, with a NonRetriableException, which is not retried. Fewer fit when the function has other steps.
| Type | Members |
|---|---|
FanOutOptions, a record |
MaxParallelism: how many items, or chunks, run at once, all of them by default. ParallelMode: Wait, Race, or null for the mode of the enclosing ParallelAsync. ChunkTimeout: how long a chunk’s run may take. |
FanOutOutcome<TResult>, a record struct |
Index, Value, Error (a FunctionError?) and Succeeded |
FanOutFunction<TItem, TResult>, an abstract class |
The function of a chunk. Implement ProcessAsync(TItem item, CancellationToken cancellationToken); override MaxParallelism, the number of items of a chunk that run at once. Register it with AddFunction<T>(), with no trigger. |
FanOutChunk<TItem>, a record |
(int Offset, IReadOnlyList<TItem> Items, bool FailFast): the event data of a chunk’s run. A source-generated JsonSerializerContext needs it, and FanOutOutcome<TResult>[], the output of the run. |
Events
Section titled “Events”| Type | Purpose |
|---|---|
IInngestClient |
SendAsync for one InngestEvent or several, each also with a SendOptions; SendSignalAsync(InngestSignal, CancellationToken), also with a SendOptions, which returns the id of the run the signal resumed, or null when no run waits for it; AppId. |
IInngestEventData |
Implemented by an event’s data record: static abstract string EventName and static virtual string? EventVersion. |
InngestClientExtensions |
SendAsync<T>(T, CancellationToken) and SendAsync<T>(IEnumerable<T>, CancellationToken) for typed events. |
InngestEvent |
An event to send: (string Name, object? Data = null) with Id, User, Timestamp, Version, Meta. |
InngestEvent<TData> |
A received event: Name, Data, Id, User, Timestamp, Version, Meta. |
EventMeta |
An event’s sessions: Sessions, its own, where a null id drops an inherited session; PropagatedSessions, those it inherits from the run that sends it, which the SDK fills in; InheritSessions, false to inherit none. See Sending events. |
SendEventsResult |
Ids of the sent events, in order. |
SendOptions |
Options for one send: Environment, a branch environment to send to instead of the app’s. |
IInngestRunsClient reads and cancels runs through the Inngest REST API, with the app’s signing key:
| Method | Returns |
|---|---|
GetRunAsync(string runId, CancellationToken) |
Task<FunctionRun>: the run’s status and, once it has ended, its output |
GetEventRunsAsync(string eventId, CancellationToken) |
Task<IReadOnlyList<FunctionRun>>: the runs an event started, by the event id that sending it returned |
CancelRunAsync(string runId, CancellationToken) |
Task; a step that is running finishes, and no further steps run |
FunctionRun, a record: RunId, Status, FunctionId (Inngest’s own id of the function, a UUID),
FunctionVersion, StartedAt, EndedAt, EventId, BatchId, OriginalRunId, Cron, EnvironmentId, Output
and GetOutput<T>(), which reads the output with the app’s serializer options. Status is a FunctionRunStatus:
Unknown, Scheduled, Running, Completed, Failed or Cancelled.
Inngest caches these answers for a few seconds, a run for 3 and the runs of an event for 15, and only EndedAt says
for sure that a run is over. Failures throw InngestException with Inngest’s message.
Realtime
Section titled “Realtime”IInngestRealtime publishes, streams and receives realtime messages and mints subscription tokens through the
Inngest API, with the app’s signing key. See Realtime.
| Method | Returns |
|---|---|
PublishAsync<T>(RealtimeTopic topic, T data, CancellationToken) |
Task; publishes at once, tagged with the run when called inside one. Text goes out as it is, anything else as JSON. Also for a RealtimeTopic<T>. |
PublishStreamAsync(RealtimeTopic topic, IAsyncEnumerable<string> chunks, CancellationToken) |
Task<string>: all the text, streamed to the topic in one request as it arrives |
SubscribeAsync<T>(RealtimeTopic<T> topic, CancellationToken) |
IAsyncEnumerable<RealtimeMessage<T>>: the topic’s messages from now on, with the data read as a T |
SubscribeAsync(string channel, IReadOnlyList<string> topics, CancellationToken) |
IAsyncEnumerable<RealtimeMessage<JsonElement>> for several topics of a channel |
GetSubscriptionTokenAsync(string channel, IReadOnlyList<string> topics, CancellationToken) |
Task<RealtimeToken> for a browser to subscribe to these topics of the channel |
RealtimeTopic, a record (string Channel, string Name), and RealtimeTopic<T>, the same for data of type
T, with Untyped for the members that take any topic. RealtimeMessage<T>, a record (RealtimeMessageKind Kind, string Channel, string Topic) with Data, StreamId, Text, RunId and CreatedAt; RealtimeMessageKind is
Data, StreamStart, StreamChunk, StreamEnd, Run or Other. RealtimeToken, a record (string Channel, IReadOnlyList<string> Topics, string Key) with ApiBaseUrl; serialized with camelCase names, it is the token object
that useRealtime and subscribe of the TypeScript SDK take.
Durable endpoints
Section titled “Durable endpoints”Package Inngest.AspNetCore. The API is marked [Experimental("INNGEST001")], because Inngest calls durable endpoints
beta; Durable endpoints shows how to suppress the diagnostic.
DurableEndpointOptions, the argument of WithDurableEndpoint:
| Property | Type | Default |
|---|---|---|
Id |
string? |
The method and the route pattern, such as POST /orders/{id} |
Retries |
int? |
3 |
AsyncResponse |
DurableEndpointResponse |
Redirect |
RedirectUrl |
Func<DurableEndpointRun, Uri>? |
The output URL on the Inngest API |
MaxRequestBodySize |
int |
1 MiB; a larger request gets 413 |
DurableEndpointResponse is what the client gets when Inngest takes the run over: Redirect, a 303 to the URL that
answers with the endpoint’s response, or Token, a 200 with {"run_id", "token"}. DurableEndpointRun, a record
(string RunId, string Token), is what RedirectUrl receives.
DurableEndpointAttribute marks a controller or an action, as WithDurableEndpoint does for a minimal API
endpoint. Its constructor is (string? id = null), and its properties are Retries, where a negative number means the
default, and AsyncResponse. DurableEndpointDiagnostics.Id is "INNGEST001", the diagnostic that marks the API.
Errors
Section titled “Errors”| Exception | Meaning |
|---|---|
NonRetriableException |
Thrown by your code to fail a step or run without retrying. |
RetryAfterException |
Thrown by your code to retry after Delay or at RetryAt. |
StepFailedException |
Thrown by the SDK where a step that failed for good is awaited: StepId, Error. When the recorded error has a cause, InnerException is a FunctionErrorException for it. |
FunctionErrorException |
An error Inngest recorded, rebuilt as an exception: Error holds the recorded name, message and stack, and InnerException the recorded cause. |
InngestException |
Thrown by the SDK when talking to Inngest fails, with StatusCode when there is one. |
Middleware
Section titled “Middleware”InngestMiddleware has virtual hooks that do nothing by default: OnRunStartAsync, OnMemoizationEndAsync,
OnRunCompleteAsync, OnRunErrorAsync, OnStepStartAsync, OnStepCompleteAsync, OnStepErrorAsync,
WrapFunctionAsync, WrapStepAsync, WrapStepHandlerAsync and WrapSendEventAsync. StepInfo describes a step:
Id, HashedId, Type, Memoized, and Name, the display name the code gave it. Type is a StepType: Run,
Sleep, WaitForEvent, Invoke, SendEvent, WaitForSignal, SendSignal, Fetch, AIInfer, AIWrap or
RealtimePublish. See
Middleware.
FunctionMiddleware is middleware for one function, listed in FunctionOptions.Middleware. It runs inside the
app’s middleware, also for the function’s failure handler. FunctionMiddleware.Of<TMiddleware>() resolves
TMiddleware from the call’s scope and registers it as scoped unless the app registered it;
FunctionMiddleware.Of(Func<IServiceProvider, InngestMiddleware>) calls the factory with the call’s scope on every
call.
IPayloadTransform, implemented by a middleware, rewrites the raw JSON between Inngest and the function:
| Method | Rewrites |
|---|---|
TransformEvent(JsonElement @event, Utf8JsonWriter writer) |
A triggering event, before the function’s event type is read from it |
TransformStepResult(FunctionContext context, StepInfo step, JsonElement result, Utf8JsonWriter writer) |
A memoized result of any step type, before the step’s result type is read from it |
TransformStepOutput(FunctionContext context, StepInfo step, JsonElement output, Utf8JsonWriter writer) |
What a step handler returned, before Inngest memoizes it |
TransformFunctionOutput(FunctionContext context, JsonElement output, Utf8JsonWriter writer) |
The function’s output, before it is sent to Inngest |
Each method writes one replacement value and returns true, or writes nothing and returns false to keep the value;
the defaults keep every value. JSON coming in reaches the transforms in registration order, and JSON going out in
reverse order.
Connect
Section titled “Connect”Package Inngest.Connect. AddConnect registers IInngestConnection, the worker’s connection to Inngest:
State, and WaitForStateChangedAsync(InngestConnectionState lastObservedState, CancellationToken), which completes
with the state the connection moves to. InngestConnectionState is Connecting, Active, Reconnecting, Closing
or Closed.
IHealthChecksBuilder.AddInngestConnect(string name = "inngest-connect", HealthStatus? failureStatus = null, IEnumerable<string>? tags = null), in Microsoft.Extensions.DependencyInjection, adds a health check that is healthy
only while the connection is Active. See Deploying.
Aspire
Section titled “Aspire”Package Inngest.Aspire.Hosting, for the AppHost project. The resource type is in Aspire.Hosting.ApplicationModel and
the extension methods, on IDistributedApplicationBuilder and IResourceBuilder<T>, are in Aspire.Hosting, the
namespaces an AppHost imports. See Aspire.
| Member | Purpose |
|---|---|
AddInngest(string name, int? port = null, int? connectPort = null) |
Adds the dev server as a container running inngest dev --no-discovery, with health checks on /health and on the gateway’s /ready and its UI linked in the dashboard. port and connectPort fix the host ports of the API and the gateway. The dev server is left out of a manifest. Returns an IResourceBuilder<InngestServerResource>. |
AddInngestSelfHosted(string name, IResourceBuilder<ParameterResource>? signingKey = null, IResourceBuilder<ParameterResource>? eventKey = null, int? port = null, int? connectPort = null) |
Adds a self-hosted server, inngest start, that signs its requests and wants an event key. Keys that are not given are generated and kept in the user secrets of the AppHost. A key that inngest start would refuse is reported when the server starts. The server is part of a manifest, with the keys as parameters. |
IResourceBuilder<InngestServerResource>.WithApp<TApp>(IResourceBuilder<TApp> app, string path = "/api/inngest", string endpointName = "http") |
Makes the server sync an app that serves functions, at the address its container reaches the endpoint on. Call it once for every app. |
IResourceBuilder<TDestination>.WithReference(IResourceBuilder<InngestServerResource> inngest) |
Gives a project or container INNGEST_DEV (the dev server) or INNGEST_DEV=0, INNGEST_SIGNING_KEY and INNGEST_EVENT_KEY (the self-hosted server), and for both INNGEST_BASE_URL, INNGEST_API_BASE_URL and INNGEST_EVENT_API_BASE_URL with the server’s address, and INNGEST_CONNECT_GATEWAY_URL for workers. In a manifest a reference to the dev server adds nothing. |
InngestServerResource, a ContainerResource: IsSelfHosted; SigningKeyParameter and EventKeyParameter, which are
null for the dev server; PrimaryEndpoint, the endpoint named http that serves the REST API, the event API and the UI;
and ConnectEndpoint, the endpoint named connect that is the gateway of Connect workers. Its constructor takes the
name and, for a self-hosted server, both keys.
AWS Lambda
Section titled “AWS Lambda”Package Inngest.AwsLambda. See Running on AWS Lambda.
| Member | Purpose |
|---|---|
InngestLambdaHandler(IServiceProvider services) |
Takes the SDK from the service provider of an app that called AddInngest, and fails at once when it did not. Build one when the function starts. |
HandleAsync(APIGatewayProxyRequest request, ILambdaContext context) |
Serves an event of an API Gateway REST API (payload format 1.0) and returns an APIGatewayProxyResponse. |
HandleAsync(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) |
Serves an event of an API Gateway HTTP API or a Lambda function URL (payload format 2.0) and returns an APIGatewayHttpApiV2ProxyResponse. |
Each invocation runs in a service scope of its own. The cancellation token that functions receive is never cancelled,
and InngestOptions.Streaming has no effect, since the response is buffered; building the handler logs a warning when
it is on. The event types are those of Amazon.Lambda.APIGatewayEvents, and the package ships no
JsonSerializerContext for them.
Azure Functions
Section titled “Azure Functions”Package Inngest.AzureFunctions.
| Member | Purpose |
|---|---|
HttpRequestData.ServeInngestAsync() |
Serves the Inngest endpoint from an HTTP-triggered function of the isolated worker: GET answers introspection, PUT syncs the app and POST runs functions. Returns the HttpResponseData the function returns. It works with the built-in HTTP model and with the ASP.NET Core integration, where InngestOptions.Streaming streams a long call. Requires services.AddInngest(). In Microsoft.Azure.Functions.Worker.Http. |
The function that calls it is declared in the app. See Azure Functions.
OpenTelemetry
Section titled “OpenTelemetry”Package Inngest.OpenTelemetry.
| Member | Purpose |
|---|---|
IInngestBuilder.AddInstrumentation() |
Adds the middleware that traces and measures runs. Add it before other middleware, so that their work falls inside its spans. |
TracerProviderBuilder.AddInngestInstrumentation() |
Collects the SDK’s spans. In OpenTelemetry.Trace. |
MeterProviderBuilder.AddInngestInstrumentation() |
Collects the SDK’s metrics. In OpenTelemetry.Metrics. |
InngestInstrumentation.ActivitySourceName, InngestInstrumentation.MeterName |
"inngest", for AddSource, AddMeter or a listener of your own. |
The spans are inngest.execution, one per call from Inngest, and inngest.step, one per step the app executes; a
Connect worker adds inngest.connect.request for every request it receives. The metrics are inngest.run.started,
inngest.run.completed, inngest.run.failed, inngest.retries, inngest.execution.duration and
inngest.step.duration, with durations in seconds. The tracing middleware example
shows how to write such a middleware yourself.
Encryption
Section titled “Encryption”Package Inngest.Encryption. See Encryption.
| Member | Purpose |
|---|---|
IInngestBuilder.AddEncryption(Action<EncryptionOptions>?) |
Adds the middleware that encrypts event data, step results and function output and decrypts what comes back, in the format of @inngest/middleware-encryption. Register it after middleware that add or change events. |
EncryptionOptions:
| Member | Purpose |
|---|---|
Key |
The key that encrypts, and the first one tried when decrypting. Required unless Service is set. |
FallbackDecryptionKeys |
Keys that only decrypt, for replacing a key. Empty by default. |
DecryptOnly |
Reads encrypted data and writes none. false by default. |
EventEncryptionField |
The top-level field of an event’s data that is encrypted when the event is sent. "encrypted" by default. |
Service |
An EncryptionService that encrypts in place of the default strategy. |
EncryptionService, an abstract class: Identifier, which the envelope’s __STRATEGY__ carries;
Encrypt(ReadOnlySpan<byte> json), which returns the text of the data field; and Decrypt(string data), which
returns the UTF-8 JSON of the value.
Sentry
Section titled “Sentry”Package Inngest.Sentry.
| Member | Purpose |
|---|---|
IInngestBuilder.AddSentryReporting(Action<SentryReportingOptions>?) |
Adds the middleware that reports the errors of functions and steps to Sentry. Add it before other middleware, so that their work falls inside its scope. |
SentryReportingOptions: OnlyCaptureFinalAttempt (default true), CaptureStepErrors (false),
DisableAutomaticFlush (false) and FlushTimeout (2 seconds). See Middleware
for the tags, the trace and the options.
AI chat clients
Section titled “AI chat clients”Package Inngest.Extensions.AI. The extension methods are in Microsoft.Extensions.AI. See
AI chat clients.
| Member | Purpose |
|---|---|
ChatClientBuilder.UseInngestSteps(Action<InngestStepsOptions>?) |
Makes each call to the model inside a run a step: the messages and options are its input, the ChatResponse as JSON its result, and a replay returns the saved response without calling the model. A streaming call runs the whole stream in its step. Outside a run, and inside a step handler, calls pass through. |
ChatClientBuilder.UseInngestFunctionInvocation(Action<FunctionInvokingChatClient>?, Action<InngestStepsOptions>?) |
Adds a FunctionInvokingChatClient, as UseFunctionInvocation does, whose invocations of tools inside a run are steps named tool <name>, with the arguments as input and {"result": ...} saved. A tool that throws is retried like any step, except for an ArgumentException, which fails its step at once. |
ChatOptions.WithInngestStepId(string) |
Names the step of one call, in place of chat, for calls that reach the client in no fixed order, such as ones made after a request that finishes at varying times. The steps of its tools carry the name. The client removes the name before the options reach the model. |
ChatOptions.WithInngestRealtimeStream(RealtimeTopic) |
Publishes the text of a streaming call to the topic while the model produces it, gathered into chunks of up to 1 KB, at most 1,000 to a stream. Failing to publish does not fail the call. |
InngestInferChatClient(Uri endpoint, string model, string? apiKey = null, IReadOnlyDictionary<string, string>? headers = null),
in the Inngest namespace, is an IChatClient whose calls are inference steps in the OpenAI chat completions format: Inngest
makes the request, and no worker waits for it. It works inside a run only, sends max_completion_tokens, and fails for good on an answer that is an error or has no choices. A strict additional property of the options, or of a function, asks for strict structured outputs.
InngestStepsOptions, in the Inngest namespace: RecordInput (default true) records the messages and options of a
model call, or the arguments of a tool call, as the input of its step; without it the step is a plain run step.
Source generator and analyzers
Section titled “Source generator and analyzers”Part of the Inngest package, which carries them in analyzers/dotnet/cs for the compiler to load. See
Source generator and analyzers.
| Member | Purpose |
|---|---|
IInngestBuilder.AddInngestFunctions() |
An extension method that the generator writes into each project that has turned generation on and has function classes, in the namespace Inngest, in a static class named after the assembly. It calls AddFunction<T>() for every class with [InngestFunction] that derives from InngestFunction<TEventData> or InngestFunction<TEventData, TResult> and that generated code in the project can create and name, in the order of their names, and returns the builder. internal unless the project says public. |
The MSBuild properties of the project that the generator and the analyzers read:
| Property | Default | Purpose |
|---|---|---|
InngestGenerateFunctionRegistration |
false |
true writes the method and reports the classes it leaves out (INNGEST001 to INNGEST005). Nothing is written or reported until it is set. |
InngestFunctionsMethodName |
AddInngestFunctions |
The name of the generated method. |
InngestFunctionsAccessibility |
internal |
public or internal. |
DisableInngestAnalyzers |
false |
true removes everything the package runs in the compiler, the generator and both analyzers, from the build. |
PublishAot, PublishTrimmed, JsonSerializerIsReflectionEnabledByDefault |
Read by the JSON analyzer, which is active when reflection-based JSON is off. |
The diagnostics:
| Id | Severity | Reported when |
|---|---|---|
INNGEST001 |
Warning | A class has [InngestFunction] but is not a function. |
INNGEST002 |
Warning | An abstract class has [InngestFunction]; it is not registered. |
INNGEST003 |
Warning | A generic class has [InngestFunction]; it is not registered. |
INNGEST004 |
Warning | A private, protected, private protected or file-local class, or one inside such a type, has [InngestFunction]; it is not registered. |
INNGEST005 |
Warning | A function class has no public constructor; it is not registered. |
INNGEST006 |
Warning | InngestGenerateFunctionRegistration, InngestFunctionsMethodName or InngestFunctionsAccessibility has a value the generator cannot use. |
INNGEST010 |
Warning | The event data or result of a function, or a type a typed step saves (RunAsync<T>, WaitForEventAsync<T>, InvokeAsync, PublishAsync<T> and the others), is in no JsonSerializerContext, and reflection-based JSON is off. A code fix lists it. |
INNGEST011 |
Warning | The same type is anonymous, which no context can list. |
Entity Framework Core outbox
Section titled “Entity Framework Core outbox”Package Inngest.EntityFrameworkCore. See Transactional outbox.
| Member | Purpose |
|---|---|
ModelBuilder.AddInngestOutbox(Action<EntityTypeBuilder<InngestOutboxMessage>>?) |
Maps the InngestOutbox table in the model of a DbContext; the callback changes the mapping, for example its name or schema. In Microsoft.EntityFrameworkCore. |
DbContextOptionsBuilder.UseInngestOutbox() |
Lets saves wake the dispatcher once they have committed, instead of at the next poll. Works wherever the options are built, with or without a service provider. In Microsoft.EntityFrameworkCore. |
DbContextOptionsBuilder.UseInngestOutbox(IServiceProvider) |
The same, and fails when the options are built if AddEntityFrameworkOutbox was not called. In Microsoft.EntityFrameworkCore. |
DbContext.AddInngestEvent<T>(T data, string? id = null) |
Adds the data as an event named T.EventName to the unit of work, serialized with the SDK’s serializer options, and returns the row. The id is generated unless given. In Microsoft.EntityFrameworkCore. |
DbContext.AddInngestEvent(InngestEvent @event) |
The same for an InngestEvent, which keeps its Id, User, Timestamp, Version and the sessions of Meta. In Microsoft.EntityFrameworkCore. |
InngestOutboxMessage, the entity of the table, with all times in UTC: Id (the key, and the id the event is sent
under; at most 255 characters, as Name and Version are), Name, Data, User and Sessions (JSON text), Version,
Timestamp (the event’s own time, which the code may set), CreatedAt (when the row was added), NextAttemptAt,
Attempts, LastError, LeaseToken, LeaseExpiresAt and SentAt.
InngestOutboxOptions:
| Member | Purpose |
|---|---|
DispatcherEnabled |
Runs the dispatcher in this process. true by default. |
PollInterval |
How long the dispatcher waits for a wake-up before it looks anyway. 5 seconds by default. |
BatchSize |
The most events one claim takes and one request sends. 100 by default. |
LeaseDuration, SendTimeout |
How long a dispatcher owns the events it claimed (1 minute) and how long one request may take (20 seconds). The lease must be at least twice the timeout. |
RetryDelay, MaxRetryDelay |
The wait after the first failed attempt, doubled with each failure, and its cap. 5 seconds and 5 minutes by default. |
Retention, CleanupInterval, CleanupBatchSize |
How long sent events stay (1 day), how often older ones are deleted (5 minutes) and how many one DELETE removes (1000). |
SignalR
Section titled “SignalR”Package Inngest.SignalR, which also brings in Inngest.AspNetCore. See SignalR.
| Member | Purpose |
|---|---|
IServiceCollection.AddInngestSignalR(Action<InngestSignalROptions>) |
Registers the bridge for InngestRealtimeHub. Call AddSignalR and AddInngest as well. In Microsoft.Extensions.DependencyInjection. |
IServiceCollection.AddInngestSignalR<THub>(Action<InngestSignalROptions>) |
Registers the bridge for a hub of your own, which injects InngestRealtimeBridge<THub>. The options are read at once, so a missing Authorize fails the app at startup. |
IEndpointRouteBuilder.MapInngestRealtimeHub(string pattern) |
Maps InngestRealtimeHub and returns its HubEndpointConventionBuilder. In Microsoft.AspNetCore.Builder. |
IEndpointRouteBuilder.MapInngestRealtimeHub(string pattern, Action<HttpConnectionDispatcherOptions>) |
The same, with the options of the connections that MapHub takes, such as CloseOnAuthenticationExpiration. |
InngestRealtimeHub, a sealed hub with Subscribe(string channel, string[] topics) and Unsubscribe(string channel, string[] topics).
InngestRealtimeBridge<THub> forwards the messages to the clients of THub. It sends to connections by id, never to
a group, so that instances on a backplane deliver each message once.
| Member | Purpose |
|---|---|
SubscribeAsync(HubCallerContext caller, string channel, IReadOnlyList<string> topics, CancellationToken) |
ValueTask<RealtimeSubscription>: asks Authorize, joins the caller and returns what it joined. Joining again changes nothing. Throws HubException when the request is incomplete, refused, over the limit, or the app is stopping. |
SubscribeAsync<T>(HubCallerContext caller, RealtimeTopic<T> topic, CancellationToken) |
The same for a typed topic. |
Unsubscribe(HubCallerContext caller, string channel, IReadOnlyList<string> topics) |
Takes the caller out of what it joined with these arguments, or with the subscription Authorize returned. Leaving what it is not in changes nothing. |
Unsubscribe<T>(HubCallerContext caller, RealtimeTopic<T> topic) |
The same for a typed topic. |
InngestSignalROptions:
| Member | Purpose |
|---|---|
Authorize |
Func<RealtimeSubscribeRequest, ValueTask<RealtimeSubscription?>>, required: returns the subscription to join, which may have fewer topics or another channel, or null to refuse. |
MessageMethod |
The client method that receives each message as a string of JSON. "Realtime" by default. |
ErrorMethod |
The client method that hears of a subscription the client lost, with the channel and topics it asked for and the reason: "behind", "send-failed" or "inngest-ended". The connection stays open. "RealtimeError" by default. |
MaxSubscriptionsPerConnection |
How many subscriptions one connection may be in, counting each way it asked to join. 32 by default. |
MaxQueuedMessagesPerConnection |
How many messages may wait for one connection. A connection that reaches it loses its subscriptions and is told. At least MaxSubscriptionsPerConnection. 512 by default. |
RealtimeSubscribeRequest(HubCallerContext caller, RealtimeSubscription requested, IServiceProvider services, CancellationToken cancellationToken = default), with Caller, Requested, Services (a scope of its own for each
request) and CancellationToken. The bridge makes one for each client that asks to join, and a test can make one to call
Authorize directly.
Blazor
Section titled “Blazor”Package Inngest.Blazor, for Blazor Server, WebAssembly and interactive Auto. See
Realtime in Blazor.
RealtimeView<TData>, a component that draws nothing of its own. It subscribes after its first interactive render,
never while prerendering, and unsubscribes when it is disposed.
| Member | Purpose |
|---|---|
Topic |
The RealtimeTopic<TData> to watch. |
Channel, Topics |
Several topics of a channel, instead of Topic. TData must then be JsonElement. |
MaxMessages |
How many of the newest messages RealtimeState.Messages keeps. 100 by default. |
OnMessage |
An EventCallback<RealtimeMessage<TData>> called for each message, in order. |
ThrowOnError |
Hands a failed subscription to the nearest ErrorBoundary. false by default. |
ChildContent |
A RenderFragment<RealtimeState<TData>>. |
Restart() |
Subscribes again with the messages cleared, such as after a failure. |
RealtimeState<TData>: Connection, Error, Messages, Latest, LatestByTopic and TextByTopic, the text of
the newest stream of each topic. RealtimeConnectionState is Idle, Connecting, Open, Reconnecting,
Closed or Failed.
IRealtimeSource is what the component asks for, and what code outside components can inject:
| Method | Returns |
|---|---|
SubscribeAsync<T>(RealtimeTopic<T> topic, CancellationToken) |
IAsyncEnumerable<RealtimeMessage<T>>: the topic’s messages from now on |
SubscribeAsync(string channel, IReadOnlyList<string> topics, CancellationToken) |
IAsyncEnumerable<RealtimeMessage<JsonElement>> for several topics of a channel |
The same two with an Action<RealtimeConnectionState>? onConnectionChanged before the token |
The same, and a source that can tell calls the callback with Connecting, Open and Reconnecting. An implementation writes these two. |
Each side registers its own source. An app that registers both gets the one registered last.
RealtimeRelayOptions: Url, which maps a channel and its topics to the address of the endpoint, and
SerializerOptions, which read the data of typed topics.
User context
Section titled “User context”Packages Inngest.UserContext and Inngest.UserContext.AspNetCore. See User context.
| Member | Purpose |
|---|---|
IInngestBuilder.AddUserContext(Action<UserContextOptions>?) |
Adds the middleware that attaches the allowed claims of the current user to the events an app sends and restores them in runs, and registers ICurrentUser and a scoped ClaimsPrincipal. Needs Data Protection, or an IUserContextProtector. Register it before middleware that needs the user. |
IInngestBuilder.AddUserContextFromHttpContext() |
Package Inngest.UserContext.AspNetCore. Makes HttpContext.User the user of events sent while serving a request. |
InvokeOptions.WithUser(FunctionContext) |
Extension method. Passes the user of the run to the function being invoked. Invocations do not pass through middleware, so they carry the user only when asked. |
ICurrentUser: Principal, a ClaimsPrincipal that is anonymous without a user; IsAuthenticated; and IssuedAt,
when the sender took the claims. It is the user of the run whose code is running, in every scope.
UserContextOptions:
| Member | Purpose |
|---|---|
Claims |
The claim types that are attached, matched exactly. sub and ClaimTypes.NameIdentifier by default. |
Lifetime |
How long after the claims were taken a run can still start with them, judged by the later of its creation and its event’s timestamp. Seven days by default. |
MaxSize |
The largest size of the payload’s JSON in UTF-8 bytes. 4096 by default; a larger user is not attached. |
Propagate |
Whether the events a run sends carry its user. true by default. |
EventFilter |
A Func<InngestEvent, bool> that picks the events that carry the user. null means all. |
OnInvalid |
An InvalidUserContextAction: Anonymous (default) runs without a user and logs a warning, FailRun fails the run. A failure handler never fails on its user. |
IUserContextSource: GetUserAsync(CancellationToken) returns the ClaimsPrincipal an event sent outside a run is
sent for, or null. IUserContextProtector: Protect(string) and TryUnprotect(string, out string?), which turn the
JSON of the claims into text for the event and back; ASP.NET Core Data Protection by default.
Webhooks
Section titled “Webhooks”Package Inngest.Webhooks, which Inngest.AspNetCore brings in; MapInngestWebhook is in Inngest.AspNetCore. See
Webhooks.
WebhookProvider says how a source of webhooks is verified and how its deliveries become events. Its constructor
(string name, IWebhookVerifier verifier, Func<WebhookDelivery, InngestEvent?> createEvent) takes any other source. The
factories:
| Factory | Event |
|---|---|
Stripe(string secret, TimeSpan? tolerance = null) |
stripe/<type>, the Stripe event as data, its id as the id. Also with IEnumerable<string> secrets. |
GitHub(string secret) |
github/<X-GitHub-Event>, the payload as data, X-GitHub-Delivery as the id; the name and the id are unsigned headers, and a delivery without the id is a 400. Also with IEnumerable<string> secrets. |
Shopify(string secret) |
shopify/<topic> with dots for slashes, the payload as data, X-Shopify-Webhook-Id as the id; the topic and the id are unsigned headers, and a delivery without the id is a 400. Also with IEnumerable<string> secrets. |
StandardWebhooks(string eventPrefix, string secret, TimeSpan? tolerance = null) |
<prefix>/<type>, the data of the payload as data, webhook-id or svix-id as the id. Also with IEnumerable<string> secrets. |
Hmac(string eventName, HmacWebhookOptions options, string secret) |
The one name, the payload as data, the header options names as the id, which a delivery must then have; no id without such a header. Also with IEnumerable<string> secrets. |
WebhookOptions, an immutable record:
| Member | Purpose |
|---|---|
MaxBodySize |
The largest body accepted, in bytes; a larger request is answered with 413. 1 MiB by default. |
SendTimeout |
How long to wait for Inngest to accept the event before answering 503. Four seconds by default. |
Transform |
Func<WebhookDelivery, InngestEvent, InngestEvent?>: changes the event before it is sent, or returns null to drop the delivery with 204. A JsonElement as the Data or User it returns is copied before the send. |
WebhookDelivery, what a Transform and a provider’s createEvent see: Provider, DeliveryId, Payload (a
JsonElement, valid while the request is handled), Services and GetHeader(string name). Its constructor
(string provider, string? deliveryId, JsonElement payload, IWebhookRequest? request = null) is public, to try a
Transform in a test.
An event that cannot be sent is answered with a client error, not a 503: 413 when Inngest says it is too large, 400 for another client error of Inngest, for a refusal of the client and for a payload that cannot be written as JSON. See Webhooks.
Verifiers. Each takes a secret or several, so that a secret can be replaced without downtime, and checks a request
with Verify(IWebhookRequest request, DateTimeOffset now), which returns a WebhookVerification and never throws for
what a request contains.
| Type | Scheme |
|---|---|
StandardWebhooksVerifier |
webhook-id, webhook-timestamp and webhook-signature, or the svix- headers: HMAC-SHA256 of {id}.{timestamp}.{body}, keyed with the base64 after whsec_; only v1 signatures; a tolerance of five minutes by default |
StripeWebhookVerifier |
Stripe-Signature, t= and v1=: HMAC-SHA256 of {t}.{body}, keyed with the text of the secret; a tolerance of five minutes by default |
GitHubWebhookVerifier |
X-Hub-Signature-256, sha256= and the hex HMAC-SHA256 of the body; delivery id from X-GitHub-Delivery, which the signature does not cover |
ShopifyWebhookVerifier |
X-Shopify-Hmac-Sha256, the base64 HMAC-SHA256 of the body; delivery id from X-Shopify-Webhook-Id, which the signature does not cover |
HmacWebhookVerifier |
Any HMAC of the body, with HmacWebhookOptions |
HmacWebhookOptions, an immutable record: SignatureHeader (required), Algorithm (HashAlgorithmName, SHA-256 by
default), SignatureEncoding (HmacSignatureEncoding.Hex or Base64), SignaturePrefix, TimestampHeader,
SignedContentPrefix, TimestampSeparator, Tolerance and DeliveryIdHeader.
To verify another source, implement IWebhookVerifier. IWebhookRequest is the request it sees, independent of
the web framework: Body (the raw bytes, owned by the host and read in place), GetHeader(string name) and Services.
WebhookVerification is a result:
Valid(string? deliveryId = null) or Invalid(string reason), with IsValid, DeliveryId and Reason; the default
value is not valid.
Test host
Section titled “Test host”Package Inngest.Testing, namespace Inngest.Testing. See Testing functions.
| Member | Purpose |
|---|---|
InngestTestHost(IServiceCollection services) |
Builds the app’s services from a copy of the collection, with nothing sent to Inngest. |
RunAsync(string functionId, InngestEvent @event, StepMocks mocks, CancellationToken) |
Runs the function until it completes, fails for good or waits on a step without a mock. Also without mocks, and with an IReadOnlyList<InngestEvent> for a batch. |
RunAsync<TFunction>(InngestEvent @event, StepMocks mocks, CancellationToken) |
The same for the function registered as TFunction, with the same overloads. |
ExecuteStepAsync(string functionId, string stepId, InngestEvent @event, StepMocks mocks, CancellationToken) |
Runs the function until the step has run, or has been reached when Inngest carries it out. Also without mocks. |
ExecuteStepAsync<TFunction>(string stepId, InngestEvent @event, StepMocks mocks, CancellationToken) |
The same for the function registered as TFunction. Also without mocks. |
SentEvents |
Every event sent through the app’s client since the host was created. |
SentSignals |
Every signal sent through the app’s client since the host was created. None resumes a run. |
Published |
Every realtime message published since the host was created, as PublishedMessage(RealtimeTopic Topic, JsonElement Data) with IsStream for a stream, recorded with all its text. Tokens the host mints are placeholders, and subscribing fails. |
Services |
The app’s services, as functions resolve them. Disposing the host, which is IAsyncDisposable and IDisposable, disposes them. |
StepMocks answers steps by id, and every method returns a new set: Run<T>(id, T result),
Run<T>(id, Func<int, T> handler), RunThrows(id, exception), Sleep(id), AllSleeps(),
WaitForEvent(id, received), WaitForEventTimeout(id), Invoke<T>(id, T result),
Invoke<TFunction, TResult>(id, TResult result), InvokeThrows(id, exception), WaitForSignal<T>(id, T data),
WaitForSignalTimeout(id), SendSignal(id, runId), Fetch(id, statusCode, body, headers) and
Infer<T>(id, T response). A Run mock also answers an AI wrap step.
TestRun: RunId, FunctionId, Status, Output, GetOutput<T>(), Error, Steps, GetStep(id),
WaitingOn, SentEvents, SentSignals, Published and FailureHandler; ToString() describes the outcome. Status is a TestRunStatus:
Completed, Failed, Waiting or Stopped.
TestStep: Id, HashedId, Type, Op, Options, Status, Attempts, Mocked, Output,
GetOutput<T>(), Error and Errors. Status is a TestStepStatus: Pending, Completed or Failed.
Test containers
Section titled “Test containers”Package Inngest.Testing.Containers, namespace Inngest.Testing.Containers. See
Testing with containers. It follows the Testcontainers module
pattern: a builder, a container and a configuration.
InngestServerBuilder(string image) builds an InngestServerContainer for the dev server. There is no default image; the module
is tested with inngest/inngest:v1.49.0. It takes an IImage as well, and has the methods of every Testcontainers
builder.
| Member | Purpose |
|---|---|
WithSelfHosting() |
Runs inngest start, the self-hosted server, in place of the dev server. |
WithSigningKey(string), WithEventKey(string) |
Sets the keys of the self-hosted server. Keys left out are generated. The signing key must be hexadecimal digits in pairs. |
WithAppUrl(Uri), WithAppUrl(string) |
Has the server sync the app at this address when it starts and retry every second until the app answers. A loopback address becomes host.docker.internal. |
WithConnectGateway(int? port = null) |
Publishes the Connect gateway on the same port inside the container and on the host, and waits for it. A free port when none is given, kept from other processes until the container starts. |
WithFastTiming(bool = true) |
The timing flags that make runs finish quickly: a sync retried and a step retried after a second, the queue polled every 50 ms. On by default. |
InngestServerBuilder.InngestPort |
The port of the server’s API inside the container, 8288. |
InngestServerContainer, a DockerContainer:
| Member | Purpose |
|---|---|
GetBaseAddress() |
The address of the server’s API as the test reaches it, as a Uri. GetConnectionString() returns it as text. |
Configure(InngestOptions) |
Sets an app’s options to use the server: dev mode and the address, or cloud mode with the keys and the address. Replaces addresses set in code. |
GetSdkEnvironment() |
The same settings as INNGEST_* variables, for a process that inherits others: INNGEST_DEV (the dev server’s address, or 0 for the self-hosted server), INNGEST_BASE_URL, INNGEST_API_BASE_URL and INNGEST_EVENT_API_BASE_URL, and INNGEST_SIGNING_KEY and INNGEST_EVENT_KEY for the self-hosted one. |
SyncAsync(Uri appUrl, CancellationToken) |
Has the server register the app that listens at this address. A loopback address, or 0.0.0.0 or [::], goes out as host.docker.internal; any other address as given. The request names the kind of server (dev or cloud) and is signed for the self-hosted server. Throws when the app does not register. |
IsDevServer, SigningKey, EventKey, ConnectGatewayPort |
Which server it is, the keys of the self-hosted server (null for the dev server) and the gateway’s port (null without one). |
StartAsync(CancellationToken) |
Starts the container. A server that does not become ready fails with its log in the exception. |
InngestServerConfiguration holds what the builder keeps for the container: SelfHosted, SigningKey, EventKey,
AppUrls, ConnectGateway, ConnectGatewayPort and FastTiming, next to the settings of any container.
ASP.NET Core test factory
Section titled “ASP.NET Core test factory”Package Inngest.Testing.Containers.AspNetCore for .NET 10, namespace Inngest.Testing.Containers. See
An app under WebApplicationFactory.
| Member | Purpose |
|---|---|
InngestWebApplicationFactory<TEntryPoint>(InngestServerContainer inngest, string servePath = "/api/inngest") |
A WebApplicationFactory that runs the app on Kestrel, on all interfaces and a free port, and applies inngest.Configure to the app’s options after the app’s own. The container must be running when the app starts. |
StartAsync(CancellationToken) |
Starts the app and has the container sync it, so the server knows its functions when this returns. |
GetAppUrl() |
The address of the app’s Inngest endpoint as the test reaches it. |
CreateClient(), CreateClient(WebApplicationFactoryClientOptions) |
Declared again, so that a client reaches the app at localhost and its port, also with options that have no base address. CreateDefaultClient gets the same address. |
Container |
The container the app is configured for. |
Templates and tool
Section titled “Templates and tool”Packages Inngest.Templates and dotnet-inngest. They have no C# API: the first holds dotnet new templates and the
second is a .NET tool. See Templates and tool.
Templates, installed with dotnet new install Inngest.Templates:
| Short name | Project SDK | References |
|---|---|---|
inngest-web |
Microsoft.NET.Sdk.Web |
Inngest.AspNetCore, at the version of the template package |
inngest-worker |
Microsoft.NET.Sdk.Worker |
Inngest.Connect, at the version of the template package, and Microsoft.Extensions.Hosting |
| Option | Templates | Meaning |
|---|---|---|
-n, --name |
both | The project name, and the app id in kebab case. |
-f, --framework |
both | net10.0 (the default) or net8.0. |
--http-port |
inngest-web |
The port of the http launch profile. A free port from 5000 to 5300 by default. |
--no-restore |
both | Skips the restore after the project is made. |
dotnet inngest, installed with dotnet tool install --global dotnet-inngest (the command is dotnet-inngest):
| Command | Purpose |
|---|---|
dotnet inngest dev [options] [-- <options of inngest dev>] |
Downloads the Inngest CLI release on first use, checks its SHA-256 and runs inngest dev. |
dotnet inngest --version |
Prints the version of the tool and of the CLI it is pinned to. |
dotnet inngest --help |
Prints the usage. |
Option of dev |
Meaning |
|---|---|
-u, --sdk-url <url> |
The app’s Inngest endpoint, passed to the CLI. Without it, and without --no-discovery, the tool reads the http address of the first profile that runs the project in Properties/launchSettings.json and appends /api/inngest. |
--project <path> |
The folder or project file whose launch settings are read. The current folder by default. |
--cli-version <version> |
Runs another release of the CLI. Its download is checked against the checksums.txt of the release; the pinned version against checksums built into the tool. |
Other options go to inngest dev as they are, in order. INNGEST_DOTNET_CACHE sets the folder the CLI is kept in. The
exit code is the CLI’s; the tool’s own are 1 when the CLI cannot be had (a request failed, or a download does not
match its SHA-256), 2 for a command line it cannot read and 130 when Ctrl+C stops a download.
inngest-dotnet is a community Inngest SDK for .NET. It is not affiliated with or endorsed by Inngest Inc.