Webhooks reference
Purpose and Scope
Webhooks let an application receive real-time notifications from OpenAI without polling long-running resources. In this SDK, the webhook surface is exposed from the normal OpenAI client alongside the generated REST resources. A server receives an HTTP request, keeps the raw request body intact, verifies the Standard Webhooks signature headers, and then works with the parsed event object. This page focuses on the JavaScript and TypeScript SDK behavior that is visible in the repository: the public client orientation from the README, the generated webhooks resource entrypoint, and the API-resource tests that define verification and parsing expectations.
Sources: README.md, src/resources/webhooks/index.ts, tests/api-resources/webhooks.test.ts
The important operational distinction is that webhook handling is not just JSON parsing. Signature verification depends on the exact payload bytes or string that OpenAI signed. If middleware parses and re-serializes the body before verification, a valid delivery can become unverifiable. The official server examples therefore use raw text body handling for JavaScript web servers. After verification succeeds, the event can be treated as a normal typed object, and application code can branch on the event type, such as a completed response notification, to retrieve or process the related resource.
Sources: tests/api-resources/webhooks.test.ts
The SDK repository is generated from the OpenAI OpenAPI specification with Stainless, and the README positions the package as the official TypeScript and JavaScript library for convenient access to the REST API. That generation model matters for webhooks because event resource coverage, exported namespaces, and API reference entries follow the same source-of-truth approach as other resources. The small resource index shown in the source evidence re-exports the generated webhooks module, so application code should normally reach it through an OpenAI client instance rather than importing internal implementation files directly.
Sources: README.md, src/resources/webhooks/index.ts
Relevant Source Files
- README.md - Establishes the SDK as the official TypeScript and JavaScript OpenAI API library, explains that the API surface is generated from the OpenAPI specification with Stainless, and points readers to the full generated API reference in api.md.
- api.md - Serves as the generated API reference destination named by the README; use it as the broad reference companion when looking up generated types and resource signatures.
- src/resources/webhooks/index.ts - Provides the public resource index for webhooks by re-exporting the generated webhooks module.
- tests/api-resources/webhooks.test.ts - Defines the observable webhook behavior tested in the repository, including unwrapping a real event payload, verifying signatures, required headers, invalid-secret handling, invalid-signature handling, and non-string payload validation.
Public Surface and Event Shape
The primary public surface documented by the tests is the client webhooks namespace. The test constructs an OpenAI client with an API key and a test base URL, then calls methods on the webhooks resource. The representative payload is a JSON string for an event object containing an event id, the object kind, a Unix creation timestamp, an event type of response completion, and a data object containing the related response id. That fixture demonstrates the expected shape application handlers should be prepared to receive after a delivery has been verified and decoded.
Sources: tests/api-resources/webhooks.test.ts
The two central operations are unwrapping and signature verification. Unwrapping is the higher-level operation: it receives the raw payload string, request headers, and a webhook secret, verifies authenticity, and returns the deserialized event object. Signature verification is the lower-level operation: it checks the payload and headers against the secret but does not, by itself, represent business handling of the event. The tests show both methods as asynchronous operations, which means webhook handlers should await them before sending a success response or running event-specific side effects.
Sources: tests/api-resources/webhooks.test.ts
| Component | Role | Observable behavior |
|---|---|---|
| OpenAI client | Owns the webhooks namespace | Constructed in tests before calling webhook helpers |
| webhooks.unwrap | Verify then parse | Returns the event object for a valid signed payload |
| webhooks.verifySignature | Verify only | Resolves for a valid signature and rejects for invalid inputs |
| webhook-signature header | Signature material | Required by verification |
| webhook-timestamp header | Signed timestamp material | Required by verification |
| webhook-id header | Delivery identifier material | Required by verification |
| webhook secret | Shared signing secret | Must be configured or passed to the helper |
Request Handling Flow
A production handler should first ensure that the incoming request body is available as raw text. Do not install JSON body middleware for the webhook route unless it also preserves the original raw body. Next, pass that raw text and the incoming headers to the SDK helper. If the application configured a client-level webhook secret, the helper can use that configuration; the tests also show the secret being passed directly to each call. Only after verification succeeds should the handler deserialize-dependent behavior run, such as loading the response referenced by a response completion event.
Sources: tests/api-resources/webhooks.test.ts
A minimal Express-style flow looks like this: receive the request as text, call the unwrap helper, branch on the event type, and return a successful status only after the event has been accepted. If verification fails, return a client error and avoid performing the side effect. This sequencing prevents forged or malformed requests from causing application work. It also keeps retry behavior predictable: webhook providers generally retry when the receiver does not acknowledge a delivery, so handlers should make a deliberate choice about which failures are permanent validation failures and which failures are temporary server failures.
Sources: tests/api-resources/webhooks.test.ts
import OpenAI from 'openai';
import express from 'express';
const app = express();
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
webhookSecret: process.env.OPENAI_WEBHOOK_SECRET,
});
app.use('/webhook', express.text({ type: 'application/json' }));
app.post('/webhook', async (req, res) => {
try {
const event = await client.webhooks.unwrap(req.body, req.headers);
if (event.type === 'response.completed') {
const responseId = event.data.id;
console.log('completed response', responseId);
}
res.sendStatus(200);
} catch (error) {
res.status(400).send('Invalid webhook');
}
});The code above intentionally treats the webhook helper as the boundary between transport input and trusted application state. The payload that arrives from the network remains a string until the SDK has verified it. The parsed event is then safe to route through normal application logic. For larger systems, keep the same boundary but move event-specific code into separate handlers. For example, a response completion event can enqueue follow-up processing, while a fine-tuning completion event can update model availability. The webhooks page should remain the place where teams standardize verification, raw-body handling, and rejection behavior.
Sources: tests/api-resources/webhooks.test.ts
Signature Verification Contract
The repository tests encode several failure modes that should be treated as part of the SDK contract. Verification succeeds for the supplied real event payload, timestamp, webhook id, signature header, and signing secret. Verification rejects when the secret is not provided in an acceptable way, and the error message explains the supported configuration options: set an environment variable for the webhook secret, configure the client with a webhook secret, or pass the secret to the function. That behavior gives applications flexibility while keeping the failure explicit when no signing secret is available.
Sources: tests/api-resources/webhooks.test.ts
Header validation is strict. The tested helper rejects requests that omit the signature header, timestamp header, or webhook id header, and each missing-header case has a specific error message. This is useful for troubleshooting because it distinguishes a malformed delivery from a bad signature. It also means framework adapters should preserve these headers exactly enough for the standard header lookup path to find them. The tests use the platform Headers object, which is a useful signal that Fetch-style headers are supported in addition to the plain header maps commonly seen in server frameworks.
Sources: tests/api-resources/webhooks.test.ts
The tests also cover invalid signatures and non-string payload inputs. An invalid signature rejects with a message that the given webhook signature does not match the expected signature. A non-string payload is rejected before verification can meaningfully proceed. In application terms, this means the receiver should not pass already parsed objects to the verifier. If a framework gives you a JavaScript object, the route is already too late for robust signature checking unless the original raw text was saved separately. Prefer a dedicated webhook route with raw text parsing and minimal middleware.
Sources: tests/api-resources/webhooks.test.ts
Generated Resource Mapping
The resource index for webhooks is intentionally small because it participates in the generated SDK structure. It re-exports the generated webhooks module from the resource directory. That keeps the public namespace consistent with other resources while allowing the generated implementation to live behind the index. When reading the repository, do not mistake that small index for lack of behavior; the behavior is validated through the API-resource test file and surfaced through the generated client package. The README’s statement about Stainless generation explains why many resource files in this SDK are thin entrypoints over generated code.
Sources: README.md, src/resources/webhooks/index.ts, tests/api-resources/webhooks.test.ts
The README also points to the generated API reference file for the full API surface. For webhook work, use that reference together with the tests: the reference is where names and generated types are cataloged, while the tests show concrete runtime expectations around verification. This pairing is especially useful when building framework adapters. The adapter can use the reference to type its helper calls and use the tested behavior as a checklist for integration tests: raw payload remains a string, all three webhook headers are forwarded, the correct secret is selected, and invalid signatures fail closed.
Sources: README.md, api.md, tests/api-resources/webhooks.test.ts
Testing Signals and Edge Cases
The webhook tests use a payload, signature, timestamp, webhook id, and secret described as coming from a real webhook event. They mock the current time to match the event timestamp before verification. That detail signals that timestamp-sensitive verification is part of the behavior and that deterministic tests should control time when asserting webhook signatures. If your own application test suite verifies sample deliveries, freeze time or use a fixture whose timestamp is valid for the verifier’s tolerance window. Otherwise, a correct fixture can begin failing simply because wall-clock time moved forward.
Sources: tests/api-resources/webhooks.test.ts
The tested event type is a response completion event with a response id in the data object. That is a good starting example because it connects webhook delivery to the Responses API, the primary model interaction surface described in the README. In a real application, the webhook should usually do a small amount of synchronous validation, acknowledge receipt quickly, and hand the event to durable background processing if follow-up work may be slow. This avoids repeated deliveries caused by handler timeouts while preserving the verified event details needed by downstream workers.
Sources: README.md, tests/api-resources/webhooks.test.ts
Next Steps
Start implementation by adding a dedicated webhook route that receives raw text and forwards the raw body plus headers to the client webhooks helper. Configure the signing secret consistently across environments, either through the client configuration or through direct helper arguments in tests. Then add tests for valid delivery, missing headers, invalid secret configuration, invalid signature, and accidental parsed-body input. After the handler is reliable, connect event-specific branches to the relevant SDK resource pages, especially Responses for response completion notifications, Batches for batch completion notifications, and Fine-tuning or Evals pages when those event families are used.
Sources: README.md, api.md, tests/api-resources/webhooks.test.ts