Skip to main content
Webhooks let Inference.net notify your application when background work completes. They are primarily used with asynchronous inference flows, including background requests, grouped jobs, and async embeddings.

What a webhook does

When a configured request finishes, Inference.net sends an HTTP POST to your webhook endpoint with the completion payload.

Typical setup flow

  1. Create a webhook in the dashboard.
  2. Copy the webhook identifier.
  3. Attach that identifier to the request as webhook_id.
  4. Handle the completion event in your endpoint.

Supported event types

EventWhen it fires
generation.completedA background generation completed
async-embedding.completedAn async embedding request completed
slow-group.completedA grouped async job completed
webhook.testA test event triggered from the dashboard

Delivery behavior

  • Inference.net performs an OPTIONS readiness check before trying to send event payloads.
  • The actual event is delivered with an HTTP POST containing JSON.
  • Your endpoint should return a 2xx response when the event was handled successfully.
  • You should treat webhook handlers as idempotent because retries can happen.

Source-backed receiver example

The e2e test suite uses a small Bun server to receive webhook traffic. This is the minimal working shape.
const server = Bun.serve({
  port: 3000,
  fetch: async (req) => {
    const url = new URL(req.url);

    if (req.method === "OPTIONS") {
      return new Response(null, {
        status: 200,
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "POST, OPTIONS",
          "Access-Control-Allow-Headers": "Content-Type",
        },
      });
    }

    if (req.method === "POST" && url.pathname === "/webhook") {
      const body = await req.json();
      const headers: Record<string, string> = {};

      req.headers.forEach((value, key) => {
        headers[key] = value;
      });

      console.log("Webhook received", { headers, body });

      return new Response(JSON.stringify({ success: true }), {
        status: 200,
        headers: { "Content-Type": "application/json" },
      });
    }

    return new Response("Not Found", { status: 404 });
  },
});

console.log(`Listening on http://localhost:${server.port}/webhook`);

Payload shapes

Single-generation completion

This payload shape is enforced by WebhookGenerationCompletionPayloadSchema and matches the e2e webhook assertions:
{
  "event": "generation.completed",
  "timestamp": "2026-03-13T12:00:00.000Z",
  "webhookId": "wh_123",
  "data": {
    "id": "gen_123",
    "state": "Success",
    "stateMessage": "Success",
    "request": {},
    "response": {},
    "model": "meta-llama/llama-3.2-3b-instruct/fp-16",
    "createdAt": "2026-03-13T12:00:00.000Z",
    "dispatchedAt": "2026-03-13T12:00:01.000Z",
    "finishedAt": "2026-03-13T12:00:04.000Z",
    "usage": {}
  }
}

Group completion

{
  "event": "slow-group.completed",
  "timestamp": "2026-03-13T12:00:00.000Z",
  "groupId": "grp_123",
  "data": {
    "groupSize": 3,
    "status": "completed",
    "generations": []
  }
}

Async embedding completion

Async embedding completions use the same top-level structure as generation.completed, but the event name is async-embedding.completed.

Test webhook payload

The webhook.test schema is slightly different from completion payloads. It currently uses webhook_id instead of webhookId.

Testing webhooks

The dashboard lets you send test events to verify that your endpoint is reachable and correctly configured.

Best practices

  • verify the event type before processing the payload
  • log the webhook identifier and request identifier on your side
  • make the handler idempotent
  • keep processing lightweight and hand off heavier work to a queue if needed