API
Getting Started With Webhooks
Everything you need to know to get started with webhooks.
Webhook support is available for all slow endpoints: /chat/completions, /completions, and /embeddings calls.
Overview
Webhooks provide an efficient push-based notification system for tracking generation completions in real-time. Rather than repeatedly polling the API to check generation status, webhooks automatically notify your application when generations complete, enabling streamlined workflows and better resource utilization.
Key Benefits
- Resource Efficiency: Eliminate unnecessary API calls for status checks
- Real-time Updates: Receive notifications within milliseconds of generation completion
- Scalability: Handle thousands of concurrent generations efficiently
- Improved User Experience: Update your UI instantly when results are ready
Getting Started
Step 1: Create a Webhook Endpoint
Your application needs an HTTPS endpoint capable of receiving POST requests. The endpoint should:
- Accept JSON payloads
- Respond with HTTP 200 status immediately
- 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");
});Go Example
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type WebhookPayload struct {
Event string `json:"event"`
Timestamp string `json:"timestamp"`
WebhookID string `json:"webhookId"`
Data map[string]any `json:"data"`
}
func handleWebhook(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read body", http.StatusBadRequest)
return
}
var payload WebhookPayload
if err := json.Unmarshal(body, &payload); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// Verify webhook source
webhookID := r.Header.Get("X-Inference-Webhook-ID")
// Process asynchronously
go func() {
switch payload.Event {
case "generation.completed":
fmt.Printf("Processing generation %s\n", payload.Data["id"])
// Your processing logic here
case "async-embedding.completed":
fmt.Printf("Processing embedding %s\n", payload.Data["id"])
// Your embedding processing logic here
}
}()
// Respond immediately
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
func main() {
http.HandleFunc("/webhooks/inference", handleWebhook)
fmt.Println("Webhook server listening on :3000")
http.ListenAndServe(":3000", nil)
}Step 2: Deploy Your Endpoint
Your webhook endpoint must be publicly accessible via HTTPS. For development environments, consider using:
- ngrok:
ngrok http 3000 - Cloudflare Tunnel: Provides a stable URL
- localtunnel:
lt --port 3000
Step 3: Register Your Webhook
- Navigate to the inference.net dashboard
- Go to Settings → Integrations in the sidebar
- Click Create Webhook
- Enter a descriptive name and your HTTPS endpoint URL
- Save your webhook
You'll receive a webhook identifier (e.g., AhALzdz8S) that you'll use when creating generations.
Step 4: Link Webhook to Generations
Include the webhook identifier in the metadata when creating 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: "gemma-3-27b-it",
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 webhook endpoint will receive a notification.
Webhook Events
generation.completed
Sent when a generation finishes processing (successfully or with failure):
{
"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": "gemma-3-27b-it",
"stream": false,
"max_tokens": 100,
"metadata": {
"webhook_id": "AhALzdz8S"
}
},
"response": {
"id": "XBKcs7F1s2oJ_AHiLMbF4",
"object": "chat.completion",
"created": 1748933182,
"model": "gemma-3-27b-it",
"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": "gemma-3-27b-it",
"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:
{
"event": "async-embedding.completed",
"timestamp": "2025-01-15T10:30:00Z",
"webhookId": "AhALzdz8S",
"data": {
"id": "EMB_abc123",
"state": "Success",
"stateMessage": "Embeddings generated successfully",
"request": {
"model": "qwen/qwen3-embedding-4b",
"input": ["text1", "text2"],
"metadata": { "webhook_id": "AhALzdz8S" }
},
"response": {
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [0.0023064255, -0.009327292]
}
],
"model": "qwen/qwen3-embedding-4b",
"usage": {
"prompt_tokens": 100,
"total_tokens": 100
}
},
"model": "qwen/qwen3-embedding-4b",
"createdAt": "2025-01-15T10:29:55Z",
"dispatchedAt": "2025-01-15T10:29:56Z",
"firstChunkAt": "2025-01-15T10:29:58Z",
"finishedAt": "2025-01-15T10:30:00Z",
"usage": {
"prompt_tokens": 100,
"total_tokens": 100
}
}
}The generation ID is the data.id field. The response object follows the standard OpenAI embeddings format.
Headers
All webhook requests include the following headers:
| Header | Description | Example |
|---|---|---|
X-Inference-Event | Event type | generation.completed |
X-Inference-Webhook-ID | Webhook identifier | AhALzdz8S |
X-Inference-Generation-ID | Generation ID (for generation events) | XBKcs7F1s2oJ_AHiLMbF4 |
X-Inference-Embedding-ID | Generation ID (for embedding events) | EMB_abc123 |
User-Agent | inference.net webhook agent | Kuzco-Webhook/1.0 |
Content-Type | Always application/json | application/json |
Before each delivery, the service also sends an OPTIONS preflight request to your endpoint with the same X-Inference-* headers. The preflight must return a 2xx status or the delivery is retried.
(Security) Verifying the request source
The X-Inference-Webhook-ID is a good way to verify that the payload you're receiving is officially coming from our API.
This ID is unique to your webhook, and is completely private to you and your team.
If the ID does not match what you see in the dashboard, your endpoint has most likely been discovered by a malicious actor.
Testing Webhooks
You can test your webhook endpoint from the dashboard:
- Navigate to Settings → Integrations in the dashboard
- Find your webhook in the list
- Click the menu and select Test
- Check your endpoint logs for the test payload
A successful test will show a green success indicator in the dashboard.
Best Practices
1. Respond Immediately
Your endpoint must respond within 60 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");
});2. Implement Idempotency
Failed webhooks may be retried. Use data.id to ensure you don't process the same event twice:
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.
3. Validate Webhook Source
Always verify that webhooks originate from inference.net by checking the presence of expected headers:
function validateWebhookSource(headers: Record<string, string>): boolean {
const requiredHeaders = ["x-inference-webhook-id", "x-inference-event"];
return requiredHeaders.every((header) => headers[header]);
}4. Handle Errors Gracefully
Implement proper error handling to prevent individual failures from affecting your entire 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
}
}5. Monitor Webhook Processing
Track key metrics to ensure reliable webhook processing:
- Webhook receipt rate
- Processing success/failure rates
- Average processing time
- Queue depth (if using queues)
Troubleshooting
Not Receiving Webhooks
- Check webhook status: Ensure your webhook is not disabled in the dashboard
- Test connectivity: Use the test feature in the dashboard
- Verify URL: Confirm your endpoint is publicly accessible via HTTPS
- Check logs: Review both your server logs and any reverse proxy logs
- Validate metadata: Ensure you're including the correct
webhook_idin generation requests
Webhooks Arriving Late
- Verify your endpoint responds quickly (< 1 second ideally)
- Check that you're not performing heavy processing before responding
- Monitor your server load and resource usage
Duplicate Webhook Deliveries
- Implement idempotency using
data.id(orgroupIdfor group events) - Ensure your endpoint always returns 200 OK for successful receipt
- Check for any errors in your webhook processing that might trigger retries
Frequently Asked Questions
Q: What happens if my endpoint is down? A: Failed webhook deliveries are retried up to 1200 times at fixed intervals — 30 seconds for generation events and 5 minutes for group events. After all attempts are exhausted, the delivery is marked as failed.
Q: What's the webhook timeout? A: Webhook endpoints must respond within 60 seconds. A timeout is treated as a failure and will trigger retries.
Q: Can I filter which events I receive? A: Currently, webhooks receive all event types. Event filtering is planned for a future update.
Q: How secure are webhooks? A: All webhooks are sent over HTTPS. You should validate the webhook source using the provided headers. HMAC signature verification is planned for additional security.
Q: What's the maximum payload size? A: 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.
Q: Can I replay missed webhooks? A: Webhook replay functionality is not currently available. As a fallback, you can poll the generation status endpoint.
Support
For assistance with webhooks:
- Email: support@inference.net
- Discord: Join our developer community
- Documentation: https://docs.inference.net
- Issues: Report bugs via our support portal