> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ai-stats.phaseo.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Run async batches with webhooks

> Create batch jobs, recover with polling, and consume standardized webhook deliveries without losing lifecycle visibility.

Batch execution is asynchronous and often fan-out heavy. This recipe shows the smallest production-safe loop for creating one batch, tracking it, and reconciling webhook delivery with your own status checks.

## 1. Upload the input file

```bash theme={null}
curl https://api.phaseo.app/v1/files \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "purpose=batch" \
  -F "file=@requests.jsonl"
```

Persist the returned `file_id`. The same file id becomes the batch input handle.

## 2. Create the batch

```bash theme={null}
curl https://api.phaseo.app/v1/batches \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input_file_id": "file_123",
    "endpoint": "/v1/chat/completions",
    "completion_window": "24h",
    "metadata": {
      "job": "nightly-evals",
      "owner": "ranking-worker"
    },
    "webhook": {
      "url": "https://example.com/api/batch-webhook",
      "events": ["batch.completed", "batch.failed"],
      "secret": "whsec_your_signing_secret"
    }
  }'
```

Store the returned `batch_id` immediately.

## 3. Poll status for control-plane recovery

Always keep your own polling path even when webhooks are enabled:

```bash theme={null}
curl https://api.phaseo.app/v1/batches/BATCH_ID \
  -H "Authorization: Bearer YOUR_API_KEY"
```

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. Cancel stale work when needed

If the batch is still pending or processing and your application no longer wants the result:

```bash theme={null}
curl -X POST https://api.phaseo.app/v1/batches/BATCH_ID/cancel \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Treat cancellation as another asynchronous state transition. Poll again until the batch reaches its next terminal lifecycle state.

## 5. 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

## 6. 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

## 7. 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.

## Related guides

* [API Reference: Batches](../api-reference/endpoint/batches.mdx)
* [API Reference: Batch Status](../api-reference/endpoint/batches-status.mdx)
* [API Reference: Async Jobs WebSocket](../api-reference/endpoint/async-jobs-ws.mdx)
