Async API
API
Webhooks: Quick Reference
Quick reference of webhook support for asynchronous inference
Dashboard Management
Webhooks are managed through the inference.net dashboard:
- Navigate to Settings → Integrations
- Create, test, archive, or restore webhooks through the UI
- Copy your webhook identifier for use in generation requests
Payload Structures
generation.completed
{
"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
{
"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
{
"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)
{
"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
| Header | Description | Example |
|---|---|---|
X-Inference-Event | Event type | generation.completed, async-embedding.completed, or slow-group.completed |
X-Inference-Webhook-ID | Webhook identifier | AhALzdz8S |
X-Inference-Generation-ID | Generation ID (generation events) | XBKcs7F1s2oJ_AHiLMbF4 |
X-Inference-Embedding-ID | Generation ID (embedding events) | XBKcs7F1s2oJ_AHiLMbF4 |
X-Inference-Group-ID | Group ID (group events) | GRP_XYZ123 |
User-Agent | inference.net webhook agent | Kuzco-Webhook/1.0 |
Content-Type | Always application/json | application/json |
Using Webhooks in Generations
Include the webhook identifier in your generation request metadata:
Chat Completions
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
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
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
| Metric | Value | Notes |
|---|---|---|
| Response timeout | 60 seconds | POST and OPTIONS must respond in time |
| Generation retries | Up to 1200 | Fixed 30-second intervals |
| Group retries | Up to 1200 | Fixed 5-minute intervals |
| Max payload size | Large payloads supported | Payload 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
| Code | Meaning | Retry? |
|---|---|---|
| 200-299 | Success | No |
| 400-599 | Non-success status | Yes |
| Timeout | No response in 60s | Yes |
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) orgroupId(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
| Issue | Solution |
|---|---|
| Not receiving webhooks | Check webhook not disabled in dashboard, test connectivity, verify HTTPS URL |
| Duplicate webhooks | Implement idempotency, ensure 200 OK response |
| Webhooks timing out | Respond immediately, process asynchronously |
| Invalid payload | Validate against documented schema |
| Test webhook fails | Check endpoint is publicly accessible, returns 200 OK |
Support Resources
- Full Documentation
- API Reference
- Support
- Discord Community