Check out the newest way to compare different models for a task/agent harness: AutoEvals
Async API

API

Webhooks: Quick Reference

Quick reference of webhook support for asynchronous inference

Dashboard Management

Webhooks are managed through the inference.net dashboard:

  1. Navigate to SettingsIntegrations
  2. Create, test, archive, or restore webhooks through the UI
  3. Copy your webhook identifier for use in generation requests

Payload Structures

generation.completed

JSON
{
  "event": "generation.completed",
  "timestamp": "ISO 8601 timestamp",
  "webhookId": "webhook identifier",
  "data": {
    "id": "generation ID",
    "state": "Success|Failed|Queued|In Progress",
    "stateMessage": "Human readable status",
    "request": { /* Original request or null */ },
    "response": { /* OpenAI format response or null */ },
    "model": "model ID",
    "createdAt": "ISO 8601 timestamp",
    "dispatchedAt": "ISO 8601 timestamp or null",
    "firstChunkAt": "ISO 8601 timestamp or null",
    "finishedAt": "ISO 8601 timestamp or null",
    "usage": { /* Token usage */ }
  }
}

The generation ID is data.id. The response object is compatible with the OpenAI SDK types.

async-embedding.completed

JSON
{
  "event": "async-embedding.completed",
  "timestamp": "ISO 8601 timestamp",
  "webhookId": "webhook identifier",
  "data": {
    "id": "generation ID",
    "state": "Success|Failed|Queued|In Progress",
    "stateMessage": "Human readable status",
    "request": { /* Original embeddings request or null */ },
    "response": { /* OpenAI format embeddings response or null */ },
    "model": "model ID",
    "createdAt": "ISO 8601 timestamp",
    "dispatchedAt": "ISO 8601 timestamp or null",
    "firstChunkAt": "ISO 8601 timestamp or null",
    "finishedAt": "ISO 8601 timestamp or null",
    "usage": { /* Token usage */ }
  }
}

slow-group.completed

JSON
{
  "event": "slow-group.completed",
  "timestamp": "ISO 8601 timestamp",
  "groupId": "group ID",
  "data": {
    "groupSize": 2,
    "status": "completed|failed",
    "generations": [
      {
        "id": "generation ID",
        "state": "Success|Failed|Queued|In Progress",
        "stateMessage": "Human readable status",
        "request": { /* Original request or null */ },
        "response": { /* OpenAI format response or null */ },
        "model": "model ID",
        "createdAt": "ISO 8601 timestamp",
        "dispatchedAt": "ISO 8601 timestamp or null",
        "firstChunkAt": "ISO 8601 timestamp or null",
        "finishedAt": "ISO 8601 timestamp or null",
        "usage": { /* Token usage */ }
      }
    ]
  }
}

webhook.test (dashboard Test button)

JSON
{
  "event": "webhook.test",
  "timestamp": "ISO 8601 timestamp",
  "webhook_id": "webhook identifier",
  "data": {
    "message": "test message"
  }
}

Note that this test payload uses the legacy webhook_id field (snake_case), unlike delivery payloads which use webhookId.

Headers

HeaderDescriptionExample
X-Inference-EventEvent typegeneration.completed, async-embedding.completed, or slow-group.completed
X-Inference-Webhook-IDWebhook identifierAhALzdz8S
X-Inference-Generation-IDGeneration ID (generation events)XBKcs7F1s2oJ_AHiLMbF4
X-Inference-Embedding-IDGeneration ID (embedding events)XBKcs7F1s2oJ_AHiLMbF4
X-Inference-Group-IDGroup ID (group events)GRP_XYZ123
User-Agentinference.net webhook agentKuzco-Webhook/1.0
Content-TypeAlways application/jsonapplication/json

Using Webhooks in Generations

Include the webhook identifier in your generation request metadata:

Chat Completions

TypeScript
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: "gemma-3-27b-it",
  messages: [{ role: "user", content: "Hello!" }],
  // @ts-expect-error metadata is not in the OpenAI SDK types
  metadata: { webhook_id: "YOUR_WEBHOOK_IDENTIFIER" },
});

Embeddings

TypeScript
const embeddingResponse = await fetch(
  "https://api.inference.net/v1/async/embeddings",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.INFERENCE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "qwen/qwen3-embedding-4b",
      input: ["Text to embed", "Another text to embed"],
      metadata: { webhook_id: "YOUR_WEBHOOK_IDENTIFIER" },
    }),
  },
);

Minimal Webhook Handler Examples

TypeScript
app.post("/webhook", express.json(), (req, res) => {
  res.status(200).json({ received: true });

  if (req.body.event === "generation.completed") {
    setImmediate(() => {
      console.log("Generation completed:", req.body.data.id);
      // Your processing logic here
    });
  } else if (req.body.event === "async-embedding.completed") {
    setImmediate(() => {
      console.log("Embedding completed:", req.body.data.id);
      console.log("Number of embeddings:", req.body.data.response.data.length);
    });
  } else if (req.body.event === "slow-group.completed") {
    setImmediate(() => {
      console.log("Group completed:", req.body.groupId);
      console.log("Group size:", req.body.data.groupSize);
      req.body.data.generations.forEach((gen: any) => {
        console.log(`Generation ${gen.id}: ${gen.state}`);
      });
    });
  }
});

Timing & Limits

MetricValueNotes
Response timeout60 secondsPOST and OPTIONS must respond in time
Generation retriesUp to 1200Fixed 30-second intervals
Group retriesUp to 1200Fixed 5-minute intervals
Max payload sizeLarge payloads supportedPayload size scales with generation size

Retries are triggered on a non-2xx response or a timeout. Each delivery is preceded by an OPTIONS preflight request; if the preflight does not return a 2xx, the delivery is retried rather than skipped.

Response Codes

CodeMeaningRetry?
200-299SuccessNo
400-599Non-success statusYes
TimeoutNo response in 60sYes

Any response outside the 200-299 range is treated as a failed delivery and retried.

Best Practices Checklist

  • Respond with 200 OK immediately
  • Process webhook data asynchronously
  • Implement idempotency with data.id (generation events) or groupId (group events)
  • Validate webhook source via headers
  • Handle errors gracefully
  • Monitor webhook processing
  • Use HTTPS endpoint
  • Set up proper error logging
  • Test webhook with dashboard test feature
  • Implement timeout handling
  • Handle both individual and group completions

Common Issues & Solutions

IssueSolution
Not receiving webhooksCheck webhook not disabled in dashboard, test connectivity, verify HTTPS URL
Duplicate webhooksImplement idempotency, ensure 200 OK response
Webhooks timing outRespond immediately, process asynchronously
Invalid payloadValidate against documented schema
Test webhook failsCheck endpoint is publicly accessible, returns 200 OK

Support Resources

On this page