Implementation:Openai Openai node Webhooks Resource
| Knowledge Sources | |
|---|---|
| Domains | SDK, Webhooks, Security |
| Last Updated | 2026-02-15 12:00 GMT |
Overview
The Webhooks resource class provides methods for verifying OpenAI webhook signatures and securely parsing webhook event payloads.
Description
The Webhooks class extends APIResource and implements webhook signature verification using HMAC-SHA256 via the Web Crypto API. It protects against replay attacks by validating the webhook-timestamp header against a configurable tolerance window (default: 300 seconds / 5 minutes), and it supports multiple signature formats in the webhook-signature header, accepting any valid v1,<base64> signature.
The class provides two main methods: verifySignature performs pure validation and throws InvalidWebhookSignatureError on failure, while unwrap verifies and then parses the payload into a typed UnwrapWebhookEvent discriminated union. The webhook secret can be provided directly, configured on the client via webhookSecret, or set through the OPENAI_WEBHOOK_SECRET environment variable. Secrets prefixed with whsec_ are automatically base64-decoded.
The module also exports a comprehensive set of webhook event type interfaces covering batch operations (BatchCancelledWebhookEvent, BatchCompletedWebhookEvent, BatchExpiredWebhookEvent, BatchFailedWebhookEvent), eval runs (EvalRunCanceledWebhookEvent, EvalRunFailedWebhookEvent, EvalRunSucceededWebhookEvent), fine-tuning jobs (FineTuningJobCancelledWebhookEvent, FineTuningJobFailedWebhookEvent, FineTuningJobSucceededWebhookEvent), realtime calls (RealtimeCallIncomingWebhookEvent), and background responses (ResponseCancelledWebhookEvent, ResponseCompletedWebhookEvent, ResponseFailedWebhookEvent, ResponseIncompleteWebhookEvent).
Usage
Use this resource in server-side webhook handlers to verify that incoming HTTP requests genuinely originated from OpenAI. Call unwrap for a single verify-and-parse step, or verifySignature if you only need validation without automatic parsing. The Web Crypto API (crypto.subtle) must be available in the runtime environment.
Code Reference
Source Location
- Repository: openai-node
- File: src/resources/webhooks/webhooks.ts
Signature
class Webhooks extends APIResource {
unwrap(
payload: string,
headers: HeadersLike,
secret?: string | undefined | null,
tolerance?: number,
): Promise<UnwrapWebhookEvent>;
verifySignature(
payload: string,
headers: HeadersLike,
secret?: string | undefined | null,
tolerance?: number,
): Promise<void>;
}
Import
import OpenAI from 'openai';
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| payload | string |
Yes | The raw webhook request body as a string. |
| headers | HeadersLike |
Yes | The request headers, must include webhook-id, webhook-timestamp, and webhook-signature.
|
| secret | undefined | null | No | The webhook secret. Falls back to client.webhookSecret or OPENAI_WEBHOOK_SECRET env var.
|
| tolerance | number |
No | Maximum age of the webhook in seconds (default: 300). |
Outputs
| Name | Type | Description |
|---|---|---|
| UnwrapWebhookEvent | Discriminated union | A typed event object. Discriminated by the type field (e.g., 'batch.completed', 'fine_tuning.job.succeeded').
|
| event.id | string |
The unique event ID. |
| event.created_at | number |
Unix timestamp (seconds) when the event was created. |
| event.type | string |
The event type string (e.g., 'batch.completed').
|
| event.data | { id: string; ... } |
Event-specific data payload containing the resource ID. |
| event.object | 'event' |
Always 'event'.
|
Usage Examples
import OpenAI from 'openai';
const client = new OpenAI({ webhookSecret: 'whsec_myBase64Secret==' });
// In an Express webhook handler
app.post('/webhooks/openai', async (req, res) => {
try {
const event = await client.webhooks.unwrap(
req.body, // raw string body
req.headers, // request headers
);
switch (event.type) {
case 'batch.completed':
console.log('Batch completed:', event.data.id);
break;
case 'fine_tuning.job.succeeded':
console.log('Fine-tuning job succeeded:', event.data.id);
break;
case 'response.completed':
console.log('Background response completed:', event.data.id);
break;
}
res.status(200).send('OK');
} catch (err) {
console.error('Webhook verification failed:', err);
res.status(400).send('Invalid signature');
}
});
// Verify signature only (without parsing)
await client.webhooks.verifySignature(rawBody, headers, 'whsec_mySecret');