How Inngest runs .NET functions
Inngest runs a durable function by calling your app once per step. Every call runs the function from the top, and the steps that already finished return their saved results, so the function picks up where it stopped.
saved resultvar charge = await context.Step.RunAsync("charge", ct => pay.ChargeAsync(order, ct));saved resultawait context.Step.RunAsync("send-receipt", ct => mail.SendReceiptAsync(order, ct));saved resultawait context.Step.SleepAsync("cooling-off", TimeSpan.FromMinutes(30));runningawait context.Step.RunAsync("ship", ct => warehouse.ShipAsync(order, ct));not reachedreturn new { charge.Id };Call 4. Thirty minutes later, every finished step returns its saved result, and ship runs.
- Nothing is saved yet, so
chargeruns. The app answers 206 with its result, and Inngest saves it. chargereturns its saved result at once, without charging again, andsend-receiptruns.- Both finished steps return their saved results. The sleep is new: the app reports it, and Inngest calls again in 30 minutes.
- Thirty minutes later, every finished step returns its saved result, and
shipruns. - Every step returns its saved result, the function returns, and the app answers 200. The run is complete.
The loop
Section titled “The loop”-
Inngest calls your app. It sends a request to
/api/inngest, or to a Connect worker, with the saved result of every finished step. -
The function runs from the top. A step that finished in an earlier call returns its saved result at once. It does not run again.
-
The next step runs. The first step without a saved result runs, or the run reports a sleep or a wait.
-
The app answers.
206with the step’s result, which Inngest saves before it calls again, now or when the sleep is over.200with the output once the function returns.
Who does what
Section titled “Who does what”Event source
Your API, a webhook or a schedule
Inngest
Inngest Cloud, self-hosted or the dev server
chargedonesend-receiptdone
Over HTTP
Your ASP.NET Core app
MapInngest runs one step per call
Or over Connect, with no public endpoint
Your Connect worker
No public endpoint; runs behind a firewall
Your code sends the events, Inngest stores the run and decides when to call, and the app keeps no state between calls, so any instance can take any call.
Step through a real run
Section titled “Step through a real run”One run of FulfillOrder, call by call: what Inngest sent, which steps had saved results, and what the SDK
answered. The answers are what the SDK in this repository returns; the site’s snippet check fails if they ever
stop matching.
Call 1 of 7: The first call
Inngest calls the function for the first time, with no memoized steps. The function runs until reserve-stock. That is the only new step and a run step, so the SDK runs it at once and answers 206 with a StepRun op that carries the result.
public sealed class FulfillOrder( IInventory inventory, IPayments payments, IMailer mailer, IWarehouse warehouse) : InngestFunction<OrderPlaced>{ public override async Task<object?> ExecuteAsync( FunctionContext<OrderPlaced> context, CancellationToken cancellationToken) { var order = context.Event.Data; var reservation = await context.Step.RunAsync("reserve-stock", (ran in this call) ct => inventory.ReserveAsync(order.OrderId, ct)); var payment = await context.Step.RunAsync("charge-card", ct => payments.ChargeAsync(order.CustomerId, order.Total, ct)); await context.Step.SleepAsync("cooling-off", TimeSpan.FromMinutes(30)); await Task.WhenAll( context.Step.RunAsync("send-receipt", ct => mailer.SendReceiptAsync(order.OrderId, payment.PaymentId, ct)), context.Step.RunAsync("ship-order", ct => warehouse.ShipAsync(reservation.ReservationId, ct))); return new OrderFulfilled(order.OrderId, payment.PaymentId); }}Request from Inngest
POST /api/inngest?fnId=shop-fulfill-order&stepId=step
Memoized steps (0)
None yet: steps is an empty object.
ctx.stack lists 0 steps in the order they completed; disable_immediate_execution is false.
Full request body
{
"event": {
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
},
"events": [
{
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
}
],
"steps": {},
"ctx": {
"run_id": "01JDZ6Q3T8K3V2M4H5N6P7R8S9",
"attempt": 0,
"max_attempts": 4,
"stack": {
"stack": [],
"current": 0
},
"disable_immediate_execution": false,
"use_api": false
},
"version": 2
}The SDK answers
206One op for Inngest to act on.
- StepRun
reserve-stockdata: {"reservationId":"res_8c1f"}
Full response body
[
{
"id": "c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935",
"op": "StepRun",
"name": "reserve-stock",
"displayName": "reserve-stock",
"opts": {
"type": "step.run"
},
"userland": {
"id": "reserve-stock"
},
"data": {
"reservationId": "res_8c1f"
},
"timing": {
"a": 1793611800000000000,
"b": 142000000
}
}
]Call 2 of 7: Replaying a memoized step
Inngest saved the reservation and calls again. The SDK runs the function from the top, and reserve-stock returns its saved result without calling the inventory service. The function reaches charge-card, which runs the same way.
public sealed class FulfillOrder( IInventory inventory, IPayments payments, IMailer mailer, IWarehouse warehouse) : InngestFunction<OrderPlaced>{ public override async Task<object?> ExecuteAsync( FunctionContext<OrderPlaced> context, CancellationToken cancellationToken) { var order = context.Event.Data; var reservation = await context.Step.RunAsync("reserve-stock", (memoized) ct => inventory.ReserveAsync(order.OrderId, ct)); var payment = await context.Step.RunAsync("charge-card", (ran in this call) ct => payments.ChargeAsync(order.CustomerId, order.Total, ct)); await context.Step.SleepAsync("cooling-off", TimeSpan.FromMinutes(30)); await Task.WhenAll( context.Step.RunAsync("send-receipt", ct => mailer.SendReceiptAsync(order.OrderId, payment.PaymentId, ct)), context.Step.RunAsync("ship-order", ct => warehouse.ShipAsync(reservation.ReservationId, ct))); return new OrderFulfilled(order.OrderId, payment.PaymentId); }}Request from Inngest
POST /api/inngest?fnId=shop-fulfill-order&stepId=step
Memoized steps (1)
reserve-stockc3c77fb…{"data":{"reservationId":"res_8c1f"}}
ctx.stack lists 1 step in the order they completed; disable_immediate_execution is false.
Full request body
{
"event": {
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
},
"events": [
{
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
}
],
"steps": {
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935": {
"data": {
"reservationId": "res_8c1f"
}
}
},
"ctx": {
"run_id": "01JDZ6Q3T8K3V2M4H5N6P7R8S9",
"attempt": 0,
"max_attempts": 4,
"stack": {
"stack": [
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935"
],
"current": 1
},
"disable_immediate_execution": false,
"use_api": false
},
"version": 2
}The SDK answers
206One op for Inngest to act on.
- StepRun
charge-carddata: {"paymentId":"pay_31d9","amount":59.9}
Full response body
[
{
"id": "b2ae8f272ea660900fca3aeefbd39695c43f5cc9",
"op": "StepRun",
"name": "charge-card",
"displayName": "charge-card",
"opts": {
"type": "step.run"
},
"userland": {
"id": "charge-card"
},
"data": {
"paymentId": "pay_31d9",
"amount": 59.9
},
"timing": {
"a": 1793611800142000000,
"b": 418000000
}
}
]Call 3 of 7: A sleep ends the call
With two results memoized, the function reaches cooling-off. Inngest carries out sleeps, so the SDK answers with a Sleep op and the call ends. Nothing in the app waits for the 30 minutes.
public sealed class FulfillOrder( IInventory inventory, IPayments payments, IMailer mailer, IWarehouse warehouse) : InngestFunction<OrderPlaced>{ public override async Task<object?> ExecuteAsync( FunctionContext<OrderPlaced> context, CancellationToken cancellationToken) { var order = context.Event.Data; var reservation = await context.Step.RunAsync("reserve-stock", (memoized) ct => inventory.ReserveAsync(order.OrderId, ct)); var payment = await context.Step.RunAsync("charge-card", (memoized) ct => payments.ChargeAsync(order.CustomerId, order.Total, ct)); await context.Step.SleepAsync("cooling-off", TimeSpan.FromMinutes(30)); (sent to Inngest) await Task.WhenAll( context.Step.RunAsync("send-receipt", ct => mailer.SendReceiptAsync(order.OrderId, payment.PaymentId, ct)), context.Step.RunAsync("ship-order", ct => warehouse.ShipAsync(reservation.ReservationId, ct))); return new OrderFulfilled(order.OrderId, payment.PaymentId); }}Request from Inngest
POST /api/inngest?fnId=shop-fulfill-order&stepId=step
Memoized steps (2)
reserve-stockc3c77fb…{"data":{"reservationId":"res_8c1f"}}charge-cardb2ae8f2…{"data":{"paymentId":"pay_31d9","amount":59.9}}
ctx.stack lists 2 steps in the order they completed; disable_immediate_execution is false.
Full request body
{
"event": {
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
},
"events": [
{
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
}
],
"steps": {
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935": {
"data": {
"reservationId": "res_8c1f"
}
},
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9": {
"data": {
"paymentId": "pay_31d9",
"amount": 59.9
}
}
},
"ctx": {
"run_id": "01JDZ6Q3T8K3V2M4H5N6P7R8S9",
"attempt": 0,
"max_attempts": 4,
"stack": {
"stack": [
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935",
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9"
],
"current": 2
},
"disable_immediate_execution": false,
"use_api": false
},
"version": 2
}The SDK answers
206One op for Inngest to act on.
- Sleep
cooling-offduration: 30m
Full response body
[
{
"id": "d6ef22dea68b364acf8b96e9fa39dcb222867fdf",
"op": "Sleep",
"name": "30m",
"displayName": "cooling-off",
"opts": {
"duration": "30m"
},
"userland": {
"id": "cooling-off"
}
}
]Call 4 of 7: Two steps at once
Thirty minutes later Inngest memoizes the sleep as null and calls again. The function starts send-receipt and ship-order together and awaits Task.WhenAll. There are two new steps, so the SDK runs neither and answers with two StepPlanned ops.
public sealed class FulfillOrder( IInventory inventory, IPayments payments, IMailer mailer, IWarehouse warehouse) : InngestFunction<OrderPlaced>{ public override async Task<object?> ExecuteAsync( FunctionContext<OrderPlaced> context, CancellationToken cancellationToken) { var order = context.Event.Data; var reservation = await context.Step.RunAsync("reserve-stock", (memoized) ct => inventory.ReserveAsync(order.OrderId, ct)); var payment = await context.Step.RunAsync("charge-card", (memoized) ct => payments.ChargeAsync(order.CustomerId, order.Total, ct)); await context.Step.SleepAsync("cooling-off", TimeSpan.FromMinutes(30)); (memoized) await Task.WhenAll( context.Step.RunAsync("send-receipt", (planned) ct => mailer.SendReceiptAsync(order.OrderId, payment.PaymentId, ct)), context.Step.RunAsync("ship-order", (planned) ct => warehouse.ShipAsync(reservation.ReservationId, ct))); return new OrderFulfilled(order.OrderId, payment.PaymentId); }}Request from Inngest
POST /api/inngest?fnId=shop-fulfill-order&stepId=step
Memoized steps (3)
reserve-stockc3c77fb…{"data":{"reservationId":"res_8c1f"}}charge-cardb2ae8f2…{"data":{"paymentId":"pay_31d9","amount":59.9}}cooling-offd6ef22d…null
ctx.stack lists 3 steps in the order they completed; disable_immediate_execution is false.
Full request body
{
"event": {
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
},
"events": [
{
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
}
],
"steps": {
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935": {
"data": {
"reservationId": "res_8c1f"
}
},
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9": {
"data": {
"paymentId": "pay_31d9",
"amount": 59.9
}
},
"d6ef22dea68b364acf8b96e9fa39dcb222867fdf": null
},
"ctx": {
"run_id": "01JDZ6Q3T8K3V2M4H5N6P7R8S9",
"attempt": 0,
"max_attempts": 4,
"stack": {
"stack": [
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935",
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9",
"d6ef22dea68b364acf8b96e9fa39dcb222867fdf"
],
"current": 3
},
"disable_immediate_execution": false,
"use_api": false
},
"version": 2
}The SDK answers
2062 ops for Inngest to act on.
- StepPlanned
send-receipt - StepPlanned
ship-order
Full response body
[
{
"id": "179d7057db2cb69316c43e700089e6aeec07caff",
"op": "StepPlanned",
"name": "send-receipt",
"displayName": "send-receipt",
"opts": {
"type": "step.run"
},
"userland": {
"id": "send-receipt"
}
},
{
"id": "8b4b44df020db368ad1ab221cf78db64d9d78e13",
"op": "StepPlanned",
"name": "ship-order",
"displayName": "ship-order",
"opts": {
"type": "step.run"
},
"userland": {
"id": "ship-order"
}
}
]Call 5 of 7: One planned step per call
Inngest runs each planned step in a call of its own, and makes both calls at the same time. This one has stepId set to the hash of send-receipt. The SDK replays up to the parallel section, runs only that step and answers with its result.
public sealed class FulfillOrder( IInventory inventory, IPayments payments, IMailer mailer, IWarehouse warehouse) : InngestFunction<OrderPlaced>{ public override async Task<object?> ExecuteAsync( FunctionContext<OrderPlaced> context, CancellationToken cancellationToken) { var order = context.Event.Data; var reservation = await context.Step.RunAsync("reserve-stock", (memoized) ct => inventory.ReserveAsync(order.OrderId, ct)); var payment = await context.Step.RunAsync("charge-card", (memoized) ct => payments.ChargeAsync(order.CustomerId, order.Total, ct)); await context.Step.SleepAsync("cooling-off", TimeSpan.FromMinutes(30)); (memoized) await Task.WhenAll( context.Step.RunAsync("send-receipt", (ran in this call) ct => mailer.SendReceiptAsync(order.OrderId, payment.PaymentId, ct)), context.Step.RunAsync("ship-order", ct => warehouse.ShipAsync(reservation.ReservationId, ct))); return new OrderFulfilled(order.OrderId, payment.PaymentId); }}Request from Inngest
POST /api/inngest?fnId=shop-fulfill-order&stepId=179d705…
stepId is the hash of send-receipt, so the SDK runs only that step.
Memoized steps (3)
reserve-stockc3c77fb…{"data":{"reservationId":"res_8c1f"}}charge-cardb2ae8f2…{"data":{"paymentId":"pay_31d9","amount":59.9}}cooling-offd6ef22d…null
ctx.stack lists 3 steps in the order they completed; disable_immediate_execution is true.
Full request body
{
"event": {
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
},
"events": [
{
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
}
],
"steps": {
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935": {
"data": {
"reservationId": "res_8c1f"
}
},
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9": {
"data": {
"paymentId": "pay_31d9",
"amount": 59.9
}
},
"d6ef22dea68b364acf8b96e9fa39dcb222867fdf": null
},
"ctx": {
"run_id": "01JDZ6Q3T8K3V2M4H5N6P7R8S9",
"attempt": 0,
"max_attempts": 4,
"stack": {
"stack": [
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935",
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9",
"d6ef22dea68b364acf8b96e9fa39dcb222867fdf"
],
"current": 3
},
"disable_immediate_execution": true,
"use_api": false
},
"version": 2
}The SDK answers
206One op for Inngest to act on.
- StepRun
send-receiptdata: null
Full response body
[
{
"id": "179d7057db2cb69316c43e700089e6aeec07caff",
"op": "StepRun",
"name": "send-receipt",
"displayName": "send-receipt",
"opts": {
"type": "step.run"
},
"userland": {
"id": "send-receipt"
},
"data": null,
"timing": {
"a": 1793613600560000000,
"b": 236000000
}
}
]Call 6 of 7: The other planned step
The same for ship-order, in a parallel call that carries the same memoized steps. disable_immediate_execution is true while the parallel steps run.
public sealed class FulfillOrder( IInventory inventory, IPayments payments, IMailer mailer, IWarehouse warehouse) : InngestFunction<OrderPlaced>{ public override async Task<object?> ExecuteAsync( FunctionContext<OrderPlaced> context, CancellationToken cancellationToken) { var order = context.Event.Data; var reservation = await context.Step.RunAsync("reserve-stock", (memoized) ct => inventory.ReserveAsync(order.OrderId, ct)); var payment = await context.Step.RunAsync("charge-card", (memoized) ct => payments.ChargeAsync(order.CustomerId, order.Total, ct)); await context.Step.SleepAsync("cooling-off", TimeSpan.FromMinutes(30)); (memoized) await Task.WhenAll( context.Step.RunAsync("send-receipt", ct => mailer.SendReceiptAsync(order.OrderId, payment.PaymentId, ct)), context.Step.RunAsync("ship-order", (ran in this call) ct => warehouse.ShipAsync(reservation.ReservationId, ct))); return new OrderFulfilled(order.OrderId, payment.PaymentId); }}Request from Inngest
POST /api/inngest?fnId=shop-fulfill-order&stepId=8b4b44d…
stepId is the hash of ship-order, so the SDK runs only that step.
Memoized steps (3)
reserve-stockc3c77fb…{"data":{"reservationId":"res_8c1f"}}charge-cardb2ae8f2…{"data":{"paymentId":"pay_31d9","amount":59.9}}cooling-offd6ef22d…null
ctx.stack lists 3 steps in the order they completed; disable_immediate_execution is true.
Full request body
{
"event": {
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
},
"events": [
{
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
}
],
"steps": {
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935": {
"data": {
"reservationId": "res_8c1f"
}
},
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9": {
"data": {
"paymentId": "pay_31d9",
"amount": 59.9
}
},
"d6ef22dea68b364acf8b96e9fa39dcb222867fdf": null
},
"ctx": {
"run_id": "01JDZ6Q3T8K3V2M4H5N6P7R8S9",
"attempt": 0,
"max_attempts": 4,
"stack": {
"stack": [
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935",
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9",
"d6ef22dea68b364acf8b96e9fa39dcb222867fdf"
],
"current": 3
},
"disable_immediate_execution": true,
"use_api": false
},
"version": 2
}The SDK answers
206One op for Inngest to act on.
- StepRun
ship-orderdata: "trk_5b72"
Full response body
[
{
"id": "8b4b44df020db368ad1ab221cf78db64d9d78e13",
"op": "StepRun",
"name": "ship-order",
"displayName": "ship-order",
"opts": {
"type": "step.run"
},
"userland": {
"id": "ship-order"
},
"data": "trk_5b72",
"timing": {
"a": 1793613600796000000,
"b": 307000000
}
}
]Call 7 of 7: The function returns
Once both parallel steps are memoized, Inngest calls once more. Every step returns its saved result, the function returns OrderFulfilled, and the SDK answers 200 with the output. The run is complete.
public sealed class FulfillOrder( IInventory inventory, IPayments payments, IMailer mailer, IWarehouse warehouse) : InngestFunction<OrderPlaced>{ public override async Task<object?> ExecuteAsync( FunctionContext<OrderPlaced> context, CancellationToken cancellationToken) { var order = context.Event.Data; var reservation = await context.Step.RunAsync("reserve-stock", (memoized) ct => inventory.ReserveAsync(order.OrderId, ct)); var payment = await context.Step.RunAsync("charge-card", (memoized) ct => payments.ChargeAsync(order.CustomerId, order.Total, ct)); await context.Step.SleepAsync("cooling-off", TimeSpan.FromMinutes(30)); (memoized) await Task.WhenAll( context.Step.RunAsync("send-receipt", (memoized) ct => mailer.SendReceiptAsync(order.OrderId, payment.PaymentId, ct)), context.Step.RunAsync("ship-order", (memoized) ct => warehouse.ShipAsync(reservation.ReservationId, ct))); return new OrderFulfilled(order.OrderId, payment.PaymentId); (returned) }}Request from Inngest
POST /api/inngest?fnId=shop-fulfill-order&stepId=step
Memoized steps (5)
reserve-stockc3c77fb…{"data":{"reservationId":"res_8c1f"}}charge-cardb2ae8f2…{"data":{"paymentId":"pay_31d9","amount":59.9}}cooling-offd6ef22d…nullsend-receipt179d705…{"data":null}ship-order8b4b44d…{"data":"trk_5b72"}
ctx.stack lists 5 steps in the order they completed; disable_immediate_execution is false.
Full request body
{
"event": {
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
},
"events": [
{
"name": "shop/order.placed",
"data": {
"orderId": "ord_1042",
"customerId": "cus_7d2e",
"total": 59.9
},
"id": "01JDZ6Q2XF4H9K1M3N5P7R9T2V",
"ts": 1793611798000,
"user": {}
}
],
"steps": {
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935": {
"data": {
"reservationId": "res_8c1f"
}
},
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9": {
"data": {
"paymentId": "pay_31d9",
"amount": 59.9
}
},
"d6ef22dea68b364acf8b96e9fa39dcb222867fdf": null,
"179d7057db2cb69316c43e700089e6aeec07caff": {
"data": null
},
"8b4b44df020db368ad1ab221cf78db64d9d78e13": {
"data": "trk_5b72"
}
},
"ctx": {
"run_id": "01JDZ6Q3T8K3V2M4H5N6P7R8S9",
"attempt": 0,
"max_attempts": 4,
"stack": {
"stack": [
"c3c77fbce7bd0d6d6ed264a1e6030ce7955ef935",
"b2ae8f272ea660900fca3aeefbd39695c43f5cc9",
"d6ef22dea68b364acf8b96e9fa39dcb222867fdf",
"179d7057db2cb69316c43e700089e6aeec07caff",
"8b4b44df020db368ad1ab221cf78db64d9d78e13"
],
"current": 5
},
"disable_immediate_execution": false,
"use_api": false
},
"version": 2
}The SDK answers
200The function returned; the body is its output.
Full response body
{
"orderId": "ord_1042",
"paymentId": "pay_31d9"
}What the SDK answers
Section titled “What the SDK answers”| The function | The SDK answers |
|---|---|
reaches one new RunAsync step |
runs it and answers 206 with a StepRun op and its result |
| starts several new steps together | runs none and answers 206 with a StepPlanned op for each; Inngest runs each in its own call |
| reaches a sleep, a wait for an event or an invocation | answers 206 with that op; Inngest carries it out |
| returns | answers 200 with the output |
| throws | answers 500 if Inngest should retry, 400 if the failure is final |
With streaming on, a call that runs longer than three seconds
is answered with a 201 at once, and the same answer follows inside the stream when the call ends. The
design document lists every field.
What it means for your code
Section titled “What it means for your code”- Code outside steps runs on every call. Side effects and anything that differs between calls, such as the clock or a new id, belong in a step. See Steps.
- Step ids identify results. Keep them the same while runs are in flight.
- Results are JSON. Return types that round-trip through
System.Text.Json, such as records. - Nothing waits in your app. A week-long sleep is a timestamp in Inngest; deploys and restarts do not affect it.
Going deeper
Section titled “Going deeper”- Parallel steps explains the planned steps in call 4 of the run above.
- Errors and retries covers what happens when a step throws.
- Inngest’s durable execution concepts describe the model for all of its SDKs.
- The order fulfillment example is a production-style version of the function above.
inngest-dotnet is a community Inngest SDK for .NET. It is not affiliated with or endorsed by Inngest Inc.