Skip to contentNewDeepSeek V4.1 Flash 1017 is now availableTry it now
Docs / Webhooks

Webhooks

Get a push notification when an asynchronous generation or group finishes instead of polling for its status.

Webhooks notify your application when an asynchronous generation completes. Instead of repeatedly polling the API for the status of a /v1/slow or group request, your endpoint receives a POST when the result is ready.

Why use webhooks

  • Resource efficiency: no API calls spent on status checks
  • Real-time updates: a notification arrives about one second after a generation completes
  • Scalability: handle thousands of concurrent generations
  • Better user experience: update your UI as soon as results are ready

Set up a webhook

  1. Create a webhook endpoint

    Your application needs an HTTPS endpoint that accepts POST requests. The endpoint should:

    1. Accept JSON payloads
    2. Respond with HTTP 200 immediately
    3. Process the webhook data asynchronously
    import express from "express";
    
    const app = express();
    
    app.post("/webhooks/inference", express.json(), (req, res) => {
      const { event, data } = req.body;
    
      // Verify webhook source via headers
      const webhookId = req.headers["x-inference-webhook-id"];
    
      if (event === "generation.completed") {
        console.log(`Generation ${data.id} completed with status: ${data.state}`);
    
        // Process asynchronously
        setImmediate(() => {
          processGenerationResult(data);
        });
      } else if (event === "async-embedding.completed") {
        console.log(`Embedding ${data.id} completed with status: ${data.state}`);
    
        setImmediate(() => {
          processEmbeddingResult(data);
        });
      }
    
      // Always respond immediately
      res.status(200).json({ received: true });
    });
    
    app.listen(3000, () => {
      console.log("Webhook receiver listening on port 3000");
    });
  2. Deploy your endpoint

    Your webhook endpoint must be publicly reachable over HTTPS. For development, consider:

    • ngrok: ngrok http 3000
    • Cloudflare Tunnel: provides a stable URL
    • localtunnel: lt --port 3000
  3. Register the webhook

    1. Go to Settings → Webhooks in the dashboard.
    2. Create a webhook.
    3. Enter a descriptive name and your HTTPS endpoint URL.
    4. Save the webhook.

    The URL must use HTTPS and point to a public host. URLs for localhost, private networks, or IP addresses in private ranges are rejected.

    You get a webhook identifier (for example AhALzdz8S) to use when you create generations.

  4. Link the webhook to generations

    Include the webhook identifier in the request metadata when you create a generation:

    import OpenAI from "openai";
    
    const client = new OpenAI({
      baseURL: "https://api.inference.net/v1/slow",
      apiKey: process.env.INFERENCE_API_KEY,
    });
    
    const response = await client.chat.completions.create({
      model: "kimi-k3-fast",
      messages: [{ role: "user", content: "Explain quantum computing" }],
      // @ts-expect-error metadata is not in the OpenAI SDK types
      metadata: {
        webhook_id: "AhALzdz8S",
      },
    });

    When the generation completes, your endpoint receives a notification.

    If webhook_id does not match a webhook in your team, the request fails with 400 and error type WebhookNotFound. A disabled webhook is accepted, but no notification is sent.

Webhook events

generation.completed

Sent when a generation finishes processing, successfully or with a failure:

json
{
  "event": "generation.completed",
  "timestamp": "2025-01-03T06:46:22.838Z",
  "webhookId": "AhALzdz8S",
  "data": {
    "id": "XBKcs7F1s2oJ_AHiLMbF4",
    "state": "Success",
    "stateMessage": "Generation successful",
    "request": {
      "messages": [
        { "role": "system", "content": "You are a helpful assistant." },
        { "role": "user", "content": "Explain quantum computing" }
      ],
      "model": "kimi-k3-fast",
      "stream": false,
      "max_tokens": 100,
      "metadata": {
        "webhook_id": "AhALzdz8S"
      }
    },
    "response": {
      "id": "XBKcs7F1s2oJ_AHiLMbF4",
      "object": "chat.completion",
      "created": 1748933182,
      "model": "kimi-k3-fast",
      "choices": [
        {
          "index": 0,
          "message": {
            "role": "assistant",
            "content": "Quantum computing is a revolutionary approach..."
          },
          "finish_reason": "stop"
        }
      ],
      "usage": {
        "prompt_tokens": 28,
        "completion_tokens": 42,
        "total_tokens": 70
      }
    },
    "model": "kimi-k3-fast",
    "createdAt": "2025-01-03T06:46:18.000Z",
    "dispatchedAt": "2025-01-03T06:46:19.100Z",
    "firstChunkAt": "2025-01-03T06:46:20.200Z",
    "finishedAt": "2025-01-03T06:46:22.307Z",
    "usage": {
      "prompt_tokens": 28,
      "completion_tokens": 42,
      "total_tokens": 70
    }
  }
}

The generation ID is the data.id field. The response object is compatible with the types exported from the official OpenAI SDKs.

import type { OpenAI } from "openai";

const response = responseJsonObject as OpenAI.Chat.Completions.ChatCompletion;

async-embedding.completed

Sent when an async embedding request finishes processing. The payload has the same shape as generation.completed, with an embeddings request in request and a standard OpenAI embeddings response in response. The generation ID is the data.id field. See the webhooks reference for the full structure.

slow-group.completed

Sent when every request in a group finishes, or when the group fails. See the webhooks reference for the payload.

Headers

All webhook requests include these headers:

HeaderDescriptionExample
X-Inference-EventEvent typegeneration.completed
X-Inference-Webhook-IDWebhook identifierAhALzdz8S
X-Inference-Generation-IDGeneration ID (for generation events)XBKcs7F1s2oJ_AHiLMbF4
X-Inference-Embedding-IDGeneration ID (for embedding events)EMB_abc123
X-Inference-Group-IDGroup ID (for group events)GRP_XYZ123
X-Inference-Delivery-AttemptDelivery attempt, starting at 11
User-AgentInference.net webhook agentKuzco-Webhook/1.0
Content-TypeAlways application/jsonapplication/json

Embedding events send X-Inference-Generation-ID too, with the same value as X-Inference-Embedding-ID. Each delivery attempt is a single POST request. There is no OPTIONS preflight.

Verify the request source

Test a webhook

Test your endpoint from the dashboard:

  1. Go to Settings → Webhooks in the dashboard.
  2. Find your webhook in the list.
  3. Click Test, choose an event type, and click Send test.
  4. Check your endpoint logs for the test payload.

The test sends one POST with a sample payload for the event type you chose, in the same shape as a real delivery. Test IDs start with test-, so your endpoint can tell a test from a real event. See dashboard test events.

Manage webhooks

Settings → Webhooks lists your active and disabled webhooks. From there you can:

  • Create a webhook with a name and URL
  • Send a test event
  • Disable a webhook to stop notifications, and enable it again
  • Delete a webhook
  • See recent invocation stats

Best practices

Respond immediately

Your endpoint must respond within 30 seconds. Always return a 200 status immediately and process the webhook asynchronously:

// Correct approach
app.post("/webhook", (req, res) => {
  res.status(200).send("OK");
  processWebhookAsync(req.body);
});

// Incorrect approach — may timeout
app.post("/webhook", async (req, res) => {
  await heavyProcessing(req.body); // Risk of timeout
  res.status(200).send("OK");
});

Make processing idempotent

Failed webhooks are retried, and a retry can arrive after your endpoint already processed the event. Use data.id to make sure you don't process the same event twice. The timestamp field is the same on every attempt for an event, and X-Inference-Delivery-Attempt tells you which attempt you received:

const processedGenerations = new Set<string>();

async function processWebhook(payload: any) {
  const generationId = payload.data.id;
  if (processedGenerations.has(generationId)) {
    return; // Already processed
  }

  processedGenerations.add(generationId);
  // Process the generation
}

For slow-group.completed events, use the top-level groupId field for idempotency.

Validate the webhook source

Verify that webhooks come from Inference.net by checking for the expected headers:

function validateWebhookSource(headers: Record<string, string>): boolean {
  const requiredHeaders = ["x-inference-webhook-id", "x-inference-event"];
  return requiredHeaders.every((header) => headers[header]);
}

Handle errors gracefully

Handle errors so one failure doesn't affect the rest of your system:

async function handleWebhook(payload: any) {
  try {
    await processWebhook(payload);
  } catch (error) {
    console.error("Webhook processing failed:", error);
    // Log to monitoring service
    // Return 200 to prevent unnecessary retries
  }
}

Monitor webhook processing

Track these metrics to keep webhook processing reliable:

  • Webhook receipt rate
  • Processing success and failure rates
  • Average processing time
  • Queue depth (if you use queues)

Troubleshooting

Not receiving webhooks

  1. Check webhook status: make sure the webhook is not disabled in Settings → Webhooks.
  2. Test connectivity: send a test event from the dashboard.
  3. Verify the URL: confirm your endpoint is publicly reachable over HTTPS.
  4. Check logs: review your server logs and any reverse proxy logs.
  5. Validate metadata: make sure you include the correct webhook_id in generation requests.

Webhooks arriving late

  • Make sure your endpoint responds quickly (ideally under 1 second).
  • Don't do heavy processing before responding.
  • Monitor your server load and resource usage.

Duplicate deliveries

  • Make processing idempotent with data.id (or groupId for group events).
  • Make sure your endpoint always returns 200 OK for a successful receipt.
  • Check for errors in your webhook processing that might trigger retries.

FAQ

What happens if my endpoint is down? A failed delivery is retried after 5 seconds, 30 seconds, 2 minutes, 10 minutes, 30 minutes, 1 hour, and 2 hours, and then every 4 hours. Each delay varies by up to 20%. After 24 attempts (about 3 days), the event is dropped. Use the generation status endpoint to get results for a dropped event.

What's the webhook timeout? Webhook endpoints must respond within 30 seconds. A timeout is treated as a failure and triggers retries.

Can I filter which events I receive? No. Webhooks receive all event types.

How secure are webhooks? Webhook URLs must use HTTPS. Validate the webhook source with the headers above. Webhooks are not signed with an HMAC signature.

What's the maximum payload size? Payloads scale with the size of the generation they describe; a payload is larger for generations with long requests or responses. There is no fixed size limit.

Can I replay missed webhooks? No. As a fallback, poll the generation status endpoint.

Support

For help with webhooks, email support@inference.net.