OpenAI - Webhook Events
OpenAI calls a webhook endpoint when something it runs for you finishes: a background response, a batch, a fine-tuning job, an eval run. The webhook operation receives those events, so a route reacts to them instead of polling.
It is the only operation of camel-openai that is consumed from. The endpoint is served by the HTTP server of the runtime through its REST consumer factory, which is camel-platform-http unless another component is configured, and every request is verified against the signing secret before the route runs.
Route
-
Java
-
XML
-
YAML
from("openai:webhook?webhookSecret={{openai.webhook.secret}}")
.choice()
.when(header(OpenAIConstants.WEBHOOK_EVENT_TYPE).isEqualTo("response.completed"))
.to("direct:response-completed")
.when(header(OpenAIConstants.WEBHOOK_EVENT_TYPE).isEqualTo("batch.completed"))
.to("direct:batch-completed")
.end(); <route>
<from uri="openai:webhook?webhookSecret={{openai.webhook.secret}}"/>
<choice>
<when>
<simple>${header.CamelOpenAIWebhookEventType} == 'response.completed'</simple>
<to uri="direct:response-completed"/>
</when>
<when>
<simple>${header.CamelOpenAIWebhookEventType} == 'batch.completed'</simple>
<to uri="direct:batch-completed"/>
</when>
</choice>
</route> - route:
from:
uri: openai:webhook
parameters:
webhookSecret: "{{openai.webhook.secret}}"
steps:
- choice:
when:
- simple: "${header.CamelOpenAIWebhookEventType} == 'response.completed'"
steps:
- to:
uri: direct:response-completed
- simple: "${header.CamelOpenAIWebhookEventType} == 'batch.completed'"
steps:
- to:
uri: direct:batch-completed The response.completed event carries the id of the response, not the response itself: read it with the responses-retrieve operation, which takes that id.
-
Java
-
YAML
from("openai:webhook?webhookSecret={{openai.webhook.secret}}")
.filter(header(OpenAIConstants.WEBHOOK_EVENT_TYPE).isEqualTo("response.completed"))
.setHeader(OpenAIConstants.RESPONSE_ID, header(OpenAIConstants.WEBHOOK_OBJECT_ID))
.to("openai:responses-retrieve")
.to("direct:answer"); - route:
from:
uri: openai:webhook
parameters:
webhookSecret: "{{openai.webhook.secret}}"
steps:
- filter:
simple: "${header.CamelOpenAIWebhookEventType} == 'response.completed'"
steps:
- setHeader:
name: CamelOpenAIResponseId
simple: "${header.CamelOpenAIWebhookObjectId}"
- to:
uri: openai:responses-retrieve
- to:
uri: direct:answer Configuration
| Option | Description |
|---|---|
| The signing secret of the endpoint in the OpenAI dashboard, which starts with |
| The HTTP path to listen on, |
| The component, or the bean, that serves the endpoint. It must implement |
| The largest request body that is read, 1 MiB by default. A bigger request is answered with 413 and is not verified. |
Configure the same path in the OpenAI dashboard, on a URL that OpenAI can reach, and put the secret it shows there in webhookSecret. A route that only receives events needs no API key; one that calls the API afterwards, such as the responses-retrieve example above, needs one as usual.
Message
The body is the parsed event, an UnwrapWebhookEvent of the OpenAI SDK, with the type of the event to read it from:
UnwrapWebhookEvent event = exchange.getMessage().getBody(UnwrapWebhookEvent.class);
if (event.isResponseCompleted()) {
String responseId = event.asResponseCompleted().data().id();
} The headers describe the event without unwrapping it, and an event type the SDK does not know yet is described the same way:
| Header | Description |
|---|---|
| The type of the event, such as |
| The id of the event |
| The id of the object the event is about, such as the response id |
| When the event was created, in seconds since the epoch |
The request itself does not reach the route: the exchange carries only the event and these headers.
What OpenAI gets back
-
200when the route handled the event. -
400when the signature does not match, a signature header is missing, or the event is older than the tolerance of the SDK (5 minutes). The route does not run. -
413when the body is larger thanwebhookMaxPayloadSize. The body is not verified. -
500when the route failed. OpenAI retries the event, so make the route idempotent, or handle the error inside it.
A route that takes long should hand the event over (a seda or direct route, a queue) and let the webhook answer quickly, as it does for any HTTP endpoint.