# Job Requeue API

Gensail allows you to requeue jobs that were automatically skipped due to processing filters. This enables you to adjust filter settings and reprocess calls without re-ingesting them.

## Overview

When a job is created, it enters the processing pipeline. Before transcription begins, the worker validates the job against **processing filters** configured for your workspace. If a job doesn't meet the filter criteria (e.g., call duration is too short), it's marked as `skipped` instead of being processed.

Skipped jobs remain in the system and can be requeued at any time. When requeued, the job is re-validated against **current** filter settings, allowing you to:

1. Lower filter thresholds and reprocess previously skipped calls
2. Retry calls after fixing configuration issues
3. Batch requeue calls after bulk filter changes


## Job Lifecycle

```
         ┌─────────────────────────────────────────┐
         │                                         │
         ▼                                         │
      queued ───► transcribing ───► analyzing ───► publishing ───► completed
         │
         │ (filter validation fails)
         ▼
      skipped ───────────────────────────────────────────────────────────►
                          │                                              │
                          └──────────── requeue (API) ───────────────────┘
```

## Processing Filters

Processing filters are evaluated by the worker before transcription. Currently supported filters:

| Filter | Config Path | Default | Description |
|  --- | --- | --- | --- |
| `min_duration_seconds` | `processing.filters.min_duration_seconds` | 30 | Minimum call duration in seconds |


Filters are inherited through the configuration hierarchy:

- Platform defaults (30 seconds)
- Partner configuration
- Organization configuration
- Workspace configuration


## Requeuing Jobs

### Endpoint

```
PATCH /api/v1/jobs/{job_id}/status
```

### Request

```json
{
  "status": "queued"
}
```

### Response

```json
{
  "job_id": "12345",
  "previous_status": "skipped",
  "new_status": "queued",
  "message": "Job requeued successfully (requeue #1). Job will be re-validated against current processing filters."
}
```

### Authorization

Requires an API key with access to the job's workspace:

- Workspace-scoped API keys can requeue jobs in their workspace
- Organization-scoped API keys can requeue jobs in any workspace within the organization
- Partner-scoped API keys can requeue jobs in any workspace under the partner


### Error Responses

| Status | Reason |
|  --- | --- |
| `400 Bad Request` | Invalid status (only `queued` supported) |
| `400 Bad Request` | Job is not in `skipped` status |
| `403 Forbidden` | No access to job's workspace |
| `404 Not Found` | Job not found |


## Code Examples

### Python

```python
import httpx

async def requeue_job(api_key: str, job_id: int) -> dict:
    """Requeue a skipped job."""
    async with httpx.AsyncClient() as client:
        response = await client.patch(
            f"https://analytics-api.gensail.com/api/v1/jobs/{job_id}/status",
            headers={"Authorization": f"Bearer {api_key}"},
            json={"status": "queued"},
        )
        response.raise_for_status()
        return response.json()

# Usage
result = await requeue_job("gsk_xxx", 12345)
print(f"Job {result['job_id']} requeued: {result['message']}")
```

### cURL

```bash
curl -X PATCH "https://analytics-api.gensail.com/api/v1/jobs/12345/status" \
  -H "Authorization: Bearer gsk_xxx" \
  -H "Content-Type: application/json" \
  -d '{"status": "queued"}'
```

## Bulk Requeue

To requeue multiple skipped jobs, first list them using the jobs endpoint:

```bash
# Get all skipped jobs for a workspace
curl "https://analytics-api.gensail.com/api/v1/jobs?status=skipped&workspace_id=xxx" \
  -H "Authorization: Bearer gsk_xxx"
```

Then iterate and requeue each job:

```python
async def bulk_requeue_skipped(api_key: str, workspace_id: str):
    """Requeue all skipped jobs for a workspace."""
    async with httpx.AsyncClient() as client:
        # List skipped jobs
        response = await client.get(
            "https://analytics-api.gensail.com/api/v1/jobs",
            headers={"Authorization": f"Bearer {api_key}"},
            params={"status": "skipped", "workspace_id": workspace_id},
        )
        response.raise_for_status()
        jobs = response.json()["jobs"]

        # Requeue each job
        results = []
        for job in jobs:
            result = await requeue_job(api_key, int(job["job_id"]))
            results.append(result)

        return results
```

## Metadata Tracking

When a job is requeued, the following metadata is recorded in `source_metadata`:

| Field | Description |
|  --- | --- |
| `requeue_count` | Number of times this job has been requeued |
| `last_requeued_at` | ISO 8601 timestamp of most recent requeue |
| `last_requeued_by` | Scope of API key used for requeue (workspace/organization/partner) |
| `previous_skipped_reason` | The skip reason before requeue |
| `previous_skipped_at` | When the job was previously skipped |


## Common Skip Reasons

When a job is skipped, the `skipped_reason` field indicates why:

| Reason | Source | Description |
|  --- | --- | --- |
| `skip_all_on_ingest` | Poller | All calls are skipped when `skip_all_on_ingest: true` is configured |
| `rate_limit` | Poller | Rate limits (`max_calls_per_workspace` or `max_total_calls`) were exceeded |
| `duration_below_minimum:{actual}<{min}` | Worker | Call duration is shorter than configured minimum |


### Skip-All-On-Ingest Mode

When `skip_all_on_ingest: true` is configured in the CallRail integration settings, ALL calls are created with status `skipped` regardless of rate limits. This is useful for:

- Loading all historical calls for manual review
- Bulk ingestion without automatic processing
- Allowing selective processing via the API


**Configuration:**

```json
{
  "integrations": {
    "callrail": {
      "skip_all_on_ingest": true
    }
  }
}
```

**Workflow:**

1. Poller ingests all calls as `skipped` with reason `skip_all_on_ingest`
2. Review calls in your application or via `GET /jobs?status=skipped`
3. Selectively queue jobs for processing via `PATCH /jobs/{job_id}/status`
4. Worker picks up queued jobs and applies processing filters


## Re-validation Behavior

When a requeued job is picked up by a worker:

1. The job is re-validated against **current** processing filters
2. If filters pass, processing continues to transcription
3. If filters still fail, the job is marked as `skipped` again


This means:

- If you haven't changed filter settings, the job will be skipped again
- To process previously skipped calls, lower the filter threshold first
- Requeue count tracks how many times a job has been requeued


## Best Practices

1. **Adjust filters before bulk requeue**: Lower `min_duration_seconds` before requeuing to avoid immediate re-skip
2. **Monitor requeue counts**: High requeue counts may indicate configuration issues
3. **Use bulk operations carefully**: Rate limiting applies to API requests
4. **Check job status after requeue**: Jobs may be skipped again if filters haven't changed