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

# Jobs API

> Async job management API for long-running agent tasks

# Jobs API

The Jobs API provides asynchronous job management for long-running agent tasks with progress tracking and streaming.

## Overview

Jobs API enables:

* Async job submission with immediate response
* Job status polling
* Progress streaming via SSE
* Job cancellation and cleanup
* Idempotency for safe retries

## When to Use

* **Long-running tasks**: Tasks that take more than a few seconds
* **Background processing**: Fire-and-forget with status polling
* **Progress tracking**: Real-time progress updates
* **Reliable execution**: Idempotent submissions

## Base URL + Playground

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Start jobs server (part of unified server)
praisonai serve unified --host 127.0.0.1 --port 8765
```

**Base URL:** `http://127.0.0.1:8765`

## Endpoints

### POST /api/v1/runs

Submit a new job for async execution.

<ParamField header="Idempotency-Key" type="string">
  Optional key to prevent duplicate submissions
</ParamField>

<ParamField body="prompt" type="string" required>
  The prompt/query for the agent
</ParamField>

<ParamField body="agent_file" type="string">
  Path to agents YAML file
</ParamField>

<ParamField body="agent_yaml" type="string">
  Inline agents YAML content
</ParamField>

<ParamField body="framework" type="string" default="praisonai">
  Framework to use: `praisonai`, `crewai`, `autogen`
</ParamField>

<ParamField body="config" type="object">
  Additional configuration
</ParamField>

<ParamField body="webhook_url" type="string">
  URL to POST results when complete
</ParamField>

<ParamField body="timeout" type="integer" default="3600">
  Timeout in seconds
</ParamField>

<ParamField body="session_id" type="string">
  Session ID for grouping jobs
</ParamField>

<CodeGroup>
  ```bash curl theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  curl -X POST http://127.0.0.1:8765/api/v1/runs \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: unique-key-123" \
    -d '{
      "prompt": "Research AI agents",
      "agent_file": "agents.yaml",
      "framework": "praisonai"
    }'
  ```

  ```python Python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  import requests

  response = requests.post(
      "http://127.0.0.1:8765/api/v1/runs",
      headers={"Idempotency-Key": "unique-key-123"},
      json={
          "prompt": "Research AI agents",
          "agent_file": "agents.yaml"
      }
  )
  job = response.json()
  print(f"Job ID: {job['job_id']}")
  print(f"Poll URL: {job['poll_url']}")
  ```
</CodeGroup>

**Response (202 Accepted):**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "job_id": "job-abc123",
  "status": "pending",
  "created_at": "2024-01-15T10:30:00Z",
  "poll_url": "http://127.0.0.1:8765/api/v1/runs/job-abc123",
  "stream_url": "http://127.0.0.1:8765/api/v1/runs/job-abc123/stream"
}
```

**Headers:**

* `Location`: URL to poll for status
* `Retry-After`: Suggested seconds before first poll (default: 2)

### GET /api/v1/runs

List jobs with optional filters.

<ParamField query="status" type="string">
  Filter by status: `pending`, `running`, `succeeded`, `failed`, `cancelled`
</ParamField>

<ParamField query="session_id" type="string">
  Filter by session ID
</ParamField>

<ParamField query="page" type="integer" default="1">
  Page number
</ParamField>

<ParamField query="page_size" type="integer" default="20">
  Jobs per page (max 100)
</ParamField>

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl "http://127.0.0.1:8765/api/v1/runs?status=running&page=1"
```

**Response:**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "jobs": [
    {
      "job_id": "job-abc123",
      "status": "running",
      "progress_percentage": 45,
      "progress_step": "Analyzing data...",
      "created_at": "2024-01-15T10:30:00Z",
      "started_at": "2024-01-15T10:30:02Z"
    }
  ],
  "total": 15,
  "page": 1,
  "page_size": 20
}
```

### GET /api/v1/runs/{job_id}

Get job status.

<ParamField path="job_id" type="string" required>
  Job ID
</ParamField>

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl http://127.0.0.1:8765/api/v1/runs/job-abc123
```

**Response:**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "job_id": "job-abc123",
  "status": "running",
  "progress_percentage": 75,
  "progress_step": "Generating report...",
  "created_at": "2024-01-15T10:30:00Z",
  "started_at": "2024-01-15T10:30:02Z"
}
```

### GET /api/v1/runs/{job_id}/result

Get job result (only for completed jobs).

<ParamField path="job_id" type="string" required>
  Job ID
</ParamField>

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl http://127.0.0.1:8765/api/v1/runs/job-abc123/result
```

**Response (200 OK):**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "job_id": "job-abc123",
  "status": "succeeded",
  "result": "Research findings on AI agents..."
}
```

**Response (409 Conflict - not complete):**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "detail": "Job is not complete. Current status: running"
}
```

### POST /api/v1/runs/{job_id}/cancel

Cancel a running job.

<ParamField path="job_id" type="string" required>
  Job ID
</ParamField>

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl -X POST http://127.0.0.1:8765/api/v1/runs/job-abc123/cancel
```

**Response:**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "job_id": "job-abc123",
  "status": "cancelled"
}
```

### DELETE /api/v1/runs/{job_id}

Delete a completed job.

<ParamField path="job_id" type="string" required>
  Job ID
</ParamField>

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl -X DELETE http://127.0.0.1:8765/api/v1/runs/job-abc123
```

**Response:** 204 No Content

### GET /api/v1/runs/{job_id}/stream

Stream job progress via SSE.

<ParamField path="job_id" type="string" required>
  Job ID
</ParamField>

<CodeGroup>
  ```bash curl theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  curl -N http://127.0.0.1:8765/api/v1/runs/job-abc123/stream
  ```

  ```python Python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  import requests

  response = requests.get(
      "http://127.0.0.1:8765/api/v1/runs/job-abc123/stream",
      stream=True
  )
  for line in response.iter_lines():
      if line:
          print(line.decode())
  ```

  ```javascript JavaScript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  const eventSource = new EventSource(
    "http://127.0.0.1:8765/api/v1/runs/job-abc123/stream"
  );

  eventSource.addEventListener("progress", (event) => {
    const data = JSON.parse(event.data);
    console.log(`Progress: ${data.percentage}% - ${data.step}`);
  });

  eventSource.addEventListener("result", (event) => {
    const data = JSON.parse(event.data);
    console.log("Result:", data.result);
    eventSource.close();
  });
  ```
</CodeGroup>

**SSE Events:**

```
event: status
data: {"status": "running", "job_id": "job-abc123"}

event: progress
data: {"percentage": 25, "step": "Researching..."}

event: progress
data: {"percentage": 50, "step": "Analyzing..."}

event: progress
data: {"percentage": 100, "step": "Complete"}

event: result
data: {"result": "Research findings..."}
```

## Job Status Values

| Status      | Description                     |
| ----------- | ------------------------------- |
| `pending`   | Job submitted, waiting to start |
| `running`   | Job is executing                |
| `succeeded` | Job completed successfully      |
| `failed`    | Job failed with error           |
| `cancelled` | Job was cancelled               |

## Errors

| Status | Description                                  |
| ------ | -------------------------------------------- |
| 202    | Job accepted                                 |
| 200    | Success                                      |
| 204    | Deleted                                      |
| 400    | Invalid request                              |
| 404    | Job not found                                |
| 409    | Conflict (job not complete or still running) |
| 500    | Server error                                 |

## Idempotency

Use `Idempotency-Key` header to prevent duplicate job submissions:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl -X POST http://127.0.0.1:8765/api/v1/runs \
  -H "Idempotency-Key: my-unique-key" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Research AI"}'
```

If the same key is used again, the existing job is returned instead of creating a new one.

## Notes

* Jobs are stored in memory by default
* Configure persistent storage for production
* Webhook notifications are optional
* SSE streaming includes heartbeats every 5 seconds

## Related

* [A2U API](/docs/deploy/api/a2u-api) - Event streaming
* [Recipe API](/docs/deploy/api/recipe-api) - Sync recipe execution
