Skip to main content
Use this recipe to run a deferred batch job, receive webhook notifications, and keep a polling fallback for recovery. It is designed for workloads where throughput and cost matter more than immediate latency. Phaseo batch jobs currently support OpenAI, Anthropic, Google Gemini, Mistral, xAI, Groq, and Together AI through the same /v1/batches API. Send the model and prompts you want; Phaseo infers the upstream provider, creates files when the provider requires files, and uses inline native batch APIs when the provider supports them. Use provider only when you need an advanced routing constraint. Inline request batches use one endpoint shape throughout the batch. Choose /v1/chat/completions, /v1/responses, /v1/messages, or /v1/embeddings, then submit request bodies in that endpoint’s normal format. The top-level model is inherited by rows that omit body.model.

1. Create the batch

For an inline Responses batch:
Use GET /v1/batches/capabilities before selecting an advanced endpoint and provider combination. For a prebuilt JSONL file:
Then pass the returned file id:

2. Use the TypeScript SDK helper

The SDK keeps the create, poll, per-request result, and webhook verification steps together:
Use client.batches.wait(...) only after you have persisted the batch id. It polls an existing job; it does not create a replacement job.

3. Poll status for recovery

Always keep your own polling path even when webhooks are enabled:
Use the normalized gateway fields when they are present:
  • lifecycle_status for a stable cross-job status
  • polling_url as the canonical status endpoint
  • cancel_url when the batch is still cancellable
That polling loop is your fallback when webhook delivery is delayed or your consumer is temporarily unavailable.

4. Recover owned jobs after restarts

Workers should list owned jobs during startup before creating replacements:
The list response uses the same public batch object shape as create and retrieve, including lifecycle, webhook delivery, and billing reservation metadata. Resume work from the returned gateway id values. Provider-native ids are exposed for diagnostics, but they are not the durable handle for Phaseo polling or cancellation.

5. Cancel stale work when needed

If the batch is still pending or processing and your application no longer wants the result:
Treat cancellation as another asynchronous state transition. Poll again until the batch reaches its next terminal lifecycle state.

6. Consume webhook deliveries

Gateway-managed async webhook payloads are normalized around:
  • the job id and job kind
  • lifecycle_status
  • sanitized webhook configuration
  • delivery summary fields
  • recent delivery attempts
  • whether signing is enabled
Your webhook consumer should:
  1. verify the signature
  2. process deliveries idempotently
  3. treat retries as normal
  4. fetch the latest batch status when the payload and local state disagree
When you configure webhook.secret, Phaseo signs each delivery with:
  • x-phaseo-timestamp: Unix timestamp in seconds
  • x-phaseo-signature: hex HMAC-SHA256 of ${timestamp}.${rawBody} using your webhook secret
  • x-phaseo-event-id: stable event id for this batch/event
  • x-phaseo-event-type: event type such as batch.progress or batch.completed
  • x-phaseo-delivery-key: idempotency key, including progress bucket when applicable
  • x-phaseo-attempt and x-phaseo-max-attempts: retry attempt metadata
Verify the signature against the exact raw request body before parsing JSON:
Store x-phaseo-delivery-key or the payload id before side effects. Return any 2xx status only after your durable state has been updated; non-2xx responses are retried with the same event id and an incremented attempt number. Webhook subscriptions accept generic job.* events or matching batch.* events, including batch.progress / job.progress for request-count progress. Progress notifications are bucketed for idempotency and may be skipped when the provider has not reported usable request_counts yet. If you omit events, Phaseo subscribes the webhook to terminal job.completed, job.failed, job.cancelled, and job.expired notifications. If you provide events, at least one event must be valid for batch jobs; all-invalid or cross-kind-only lists such as ["video.completed"] are rejected instead of being broadened silently. Webhook callback URLs must use HTTPS. Literal private, loopback, link-local, and wildcard hosts are rejected; http://localhost, http://127.0.0.1, and http://[::1] are accepted only for local development callbacks.

7. Fetch outputs and reconcile failures

Use the terminal batch object to decide the next step:
  • completed batches should move on to output retrieval and result ingestion
  • failed batches should capture both the batch failure state and the webhook delivery state
  • cancelled batches should stop downstream fan-out cleanly
Operations should distinguish:
  • upstream batch execution failures
  • cancellation requested by operators or automation
  • webhook delivery failures after the batch itself already finished

8. What to monitor

  • batch lifecycle_status
  • provider and request correlation ids
  • webhook delivery success and retry counts
  • last delivery HTTP status
  • last failure timestamp and message
  • whether cancel_url was still available when cancellation was requested
Put those signals in the same async-jobs dashboard so operators can tell whether the failure is in execution, reconciliation, or webhook delivery.
Last modified on August 4, 2026