Inngest

Docs › Flow control

Debounce

Handle a burst of updates once using its latest event.

Wait until a user stops editing a record, then process only the latest update. Each matching event restarts the quiet period for its key, so the function runs once for the burst.

EventReplaced by a later eventWaiting for a quiet periodStep executingCompletedEach event restarts the 2s quiet period and replaces the pending event. The first burst settles, so a run starts with event 3. The steady stream never settles, so the 5s timeout starts a run with the latest event, 10.

Configure a quiet period

This TypeScript v4 function waits five minutes after the most recent company/updated event for an account. Each new event for that account restarts the five-minute wait. The optional ten-minute timeout stops a continuous stream from postponing the run indefinitely.

TypeScript
import { Inngest } from "inngest";

const inngest = new Inngest({ id: "customer-sync" });

export const syncCompany = inngest.createFunction(
  {
    id: "sync-company",
    triggers: { event: "company/updated" },
    debounce: {
      key: "event.data.account_id",
      period: "5m",
      timeout: "10m",
    },
  },
  async ({ event, step }) => {
    await step.run("process-latest-update", async () => {
      console.log(event.data.account_id);
    });
  }
);
Python
import datetime

import inngest

inngest_client = inngest.Inngest(app_id="customer-sync")

@inngest_client.create_function(
    fn_id="sync-company",
    trigger=inngest.TriggerEvent(event="company/updated"),
    debounce=inngest.Debounce(
        key="event.data.account_id",
        period=datetime.timedelta(minutes=5),
        timeout=datetime.timedelta(minutes=10),
    ),
)
async def sync_company(ctx: inngest.Context) -> None:
    async def process_latest_update() -> None:
        ctx.logger.info(ctx.event.data["account_id"])

    await ctx.step.run("process-latest-update", process_latest_update)
Go
import (
	"context"
	"log"
	"net/http"
	"time"

	"github.com/inngest/inngestgo"
	"github.com/inngest/inngestgo/step"
)

type CompanyUpdated struct {
	AccountID string `json:"account_id"`
}

func main() {
	client, err := inngestgo.NewClient(inngestgo.ClientOpts{AppID: "customer-sync"})
	if err != nil {
		panic(err)
	}

	_, err = inngestgo.CreateFunction(
		client,
		inngestgo.FunctionOpts{
			ID: "sync-company",
			Debounce: &inngestgo.ConfigDebounce{
				Key:     "event.data.account_id",
				Period:  5 * time.Minute,
				Timeout: inngestgo.Ptr(10 * time.Minute),
			},
		},
		inngestgo.EventTrigger("company/updated", nil),
		func(ctx context.Context, input inngestgo.Input[CompanyUpdated]) (any, error) {
			_, err := step.Run(ctx, "process-latest-update", func(ctx context.Context) (any, error) {
				log.Println(input.Event.Data.AccountID)
				return nil, nil
			})
			return nil, err
		},
	)
	if err != nil {
		panic(err)
	}

	_ = http.ListenAndServe(":8080", client.Serve())
}

Replace the step body with your application work. The handler receives the last event in the burst, not the first event and not an array of every event.

The optional key is a Common Expression Language (CEL) expression string evaluated against each event. Each unique value gets an independent debounce period. In the example, updates for two accounts can settle and run separately. Without a key, matching events share the function's debounce period.

How the wait changes

  1. The first matching event starts a debounce period.
  2. Another matching event within that period restarts the wait. The new event replaces the pending one.
  3. When the full period passes with no new matching event, Inngest starts one run using the last event received.
  4. If you set timeout, Inngest stops extending the wait once that maximum time passes and runs with the latest event. The next matching event starts a new debounce period.

Choose the right behavior

Need

Use

Which event runs?

Wait for updates to stop, then use the latest state

Debounce

The last event in the burst

Start work when an event arrives and skip events beyond a limit

Rate limiting

An event admitted under the limit

Debounce delays the run. Rate limiting does not wait for a quiet period. If you need to process every event, neither behavior fits; consider Throttling, which queues excess runs.

Configuration and limits

Option

Meaning

period

Required quiet period. TypeScript v4 permits 1s through 7d (168h).

key

Optional CEL expression that creates a separate debounce period per value.

timeout

Optional maximum time that incoming events can keep extending the wait.

Debounce cannot be combined with batching. Debounce keeps one latest event; batching collects multiple events for one run.

Updated Nov 10, 2026

Powered byZunoy