Skip to main content
When a request can take a while to finish, you don’t have to poll GET /v1/responses/{response_id} in a loop. Attach a callback URL to the request and Valar delivers the finished result to your server as an HTTP POST. The feature works on POST /v1/responses and POST /v1/chat/completions, and is configured entirely through two metadata keys.

Parameters

Both keys live inside the metadata object on the create request.
string
The destination URL for the callback. Must be an http or https URL. If you omit it or pass a value that can’t be used, no callback is sent and the request itself is unaffected.
string
An optional shared secret. Valar sends its value as a Bearer token in the callback’s Authorization header so your endpoint can confirm the request is genuine.

Set up an endpoint

1

Expose a route that accepts POST

Your endpoint receives a JSON body and should respond as soon as it has accepted the payload. The body matches exactly what GET /v1/responses/{response_id} returns for the same request, whether the response completed or failed.
2

Verify the Authorization header

If you set webhook_token, reject any incoming call whose header doesn’t match. Compare against Authorization: Bearer <your token> and return a 4xx for anything else.
3

Submit a request that points at your endpoint

Set background=True and add the callback URL (and token, if you use one) to metadata.

What Valar sends

The callback is a single POST request.
POST
Always a POST to the URL in completion_webhook.
header
application/json.
header
Present only when webhook_token was set. Carries Bearer <webhook_token>.
object
The full response object, byte-for-byte identical to a GET /v1/responses/{response_id} call - including the status field and, for failed responses, the error envelope. The response id lives here and is your key for deduplication.

When a callback fires

Valar POSTs to your endpoint once a response reaches a terminal status:
  • Completed. The request finished normally. The body is the completed response object.
  • Failed. The body is the failed response object, including the error.code and error.message your GET /v1/responses/{response_id} would see. This includes responses Valar fails on your behalf when they get stranded short of terminal - for example, a background request that stays in_progress past its deadline arrives with status: "failed" and error.code: "timeout". Use the status field to branch on success vs. failure in your handler.

Delivery semantics

The same callback can be delivered more than once. Treat the response id in the body as an idempotency key and ignore any id you have already processed.
  • Retries. A delivery that returns a non-2xx status or hits a network error is retried up to 3 times. Return a 2xx as soon as you accept the payload to stop the retries.
  • Timeout. Each attempt has a 30 second ceiling. A timeout counts as a failed attempt and triggers the next retry.
  • Best-effort. Callbacks are best-effort. If every attempt fails the failure is logged but never touches the response record or the API, and the result stays available through GET /v1/responses/{response_id} regardless of whether delivery ever succeeded.

Test it locally with ngrok

You can point a real request at a listener on your own machine.
1

Run a listener that prints the body and returns 200

2

Tunnel to it

Copy the https://xxxx.ngrok-free.app forwarding URL from the output.
3

Submit a request against the tunnel

When the response finishes, Valar POSTs the full payload to your listener.
4

Submit a request against the tunnel

When the response finishes, Valar POSTs the full payload to your listener.