---
title: SDK jobs
description: >-
  JobManager methods for submission, polling, real-time monitoring, listing, and
  deletion
---

The `JobManager` — reached through `ivy.jobs` — is how you run computational work: submit a tool, watch its progress, list or cancel jobs, and delete them when done. Reach for it whenever you're driving a compute job from TypeScript, from a fire-and-forget `submit()` to the all-in-one `submitAndWait()` that submits and blocks until the job finishes. It sits on the [Client](/docs/api/sdk-client) you construct first; once a job completes, the [Artifacts](/docs/api/sdk-artifacts) reference covers downloading its output files.

## SDK Jobs Reference

> **Planned specification.** `@ivybiosciences/sdk` is not released, and there is no package to install. This page describes the SDK's planned interface. To call the platform today, use the [REST API](/docs/api/rest-jobs), the [ivybloom CLI](/docs/cli), or the [MCP server](/docs/cli/mcp).

The `JobManager` handles the full lifecycle of computational jobs: submission, status polling, real-time monitoring, listing, and deletion.

Access via `ivy.jobs`.

---

## `jobs.submit(params)`

Submit a new computational job. Returns immediately with the job ID.

```ts
async submit(params: SubmitJobParams): Promise<{
  id: string
  status: string
  taskId?: string
}>
```

**Parameters:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `params.toolName` | `string` | Yes | Computational tool (e.g., `"esmfold"`, `"diffdock"`) |
| `params.parameters` | `Record<string, unknown>` | Yes | Tool-specific input parameters |
| `params.projectId` | `string` | No | Project to associate with |
| `params.jobTitle` | `string` | No | Human-readable title |
| `params.orgId` | `string` | No | Organization ID for team-scoped jobs |
| `params.analysisLevel` | `"basic" \| "standard" \| "comprehensive"` | No | Analysis depth (default: `"standard"`) |

**Returns:**

| Field | Type | Description |
|-------|------|-------------|
| `id` | `string` | Unique job identifier for status queries |
| `status` | `string` | Initial status (typically `"pending"`) |
| `taskId` | `string?` | Modal task ID for infrastructure tracking |

**Throws:** `IvyApiError` on validation (422), auth (401/403), or server (5xx) errors.

### Examples

**TypeScript**

```ts
const { id, taskId } = await ivy.jobs.submit({
  toolName: "esmfold",
  parameters: { protein_sequence: "MKFLILLFNILCLFPVLAADNH" },
  analysisLevel: "comprehensive",
})
console.log(`Job ${id} submitted (task: ${taskId})`)
```

**Python**

```python
import requests

response = requests.post(f"{BASE_URL}/api/v1/jobs", json={
    "tool_name": "esmfold",
    "tool_input_parameters": {"protein_sequence": "MKFLILLFNILCLFPVLAADNH"},
    "analysisLevel": "comprehensive",
}, headers=headers)

job = response.json()
print(f"Job {job['id']} submitted (task: {job.get('taskId')})")
```

**React**

```tsx
import { useIvyJob } from "@ivybiosciences/sdk/react"

function SubmitPanel() {
  const { submit, status, isLoading } = useIvyJob()

  return (
    <button
      disabled={isLoading}
      onClick={() => submit(
        "esmfold",
        { protein_sequence: "MKFLILLFNILCLFPVLAADNH" },
        { analysisLevel: "comprehensive" },
      )}
    >
      {isLoading ? `Running ${status?.jobId ?? ""}...` : "Submit Job"}
    </button>
  )
}
```

---

## `jobs.getStatus(jobId)`

Get the current status of a job. Lightweight endpoint suitable for polling.

```ts
async getStatus(jobId: string): Promise<JobProgress>
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `jobId` | `string` | The job identifier |

**Returns:** `JobProgress` — current status, progress %, phase, and results.

**Throws:** `IvyApiError` with `isNotFound` if the job doesn't exist.

### Examples

**TypeScript**

```ts
let status = await ivy.jobs.getStatus("job_abc123")
while (status.status !== "COMPLETED" && status.status !== "FAILURE") {
  await new Promise(r => setTimeout(r, 3000))
  status = await ivy.jobs.getStatus("job_abc123")
}
console.log(status.result)
```

**Python**

```python
import time
import requests

job_id = "job_abc123"
while True:
    status = requests.get(
        f"{BASE_URL}/api/v1/jobs/{job_id}", headers=headers
    ).json()
    if status["status"] in ("COMPLETED", "FAILURE"):
        break
    time.sleep(3)

print(status["result"])
```

**React**

```tsx
import { useIvyWebSocket } from "@ivybiosciences/sdk/react"

function JobStatus({ jobId }: { jobId: string }) {
  const { status, isConnected } = useIvyWebSocket(jobId)

  if (!isConnected) return <p>Connecting...</p>

  return (
    <div>
      <p>Status: {status?.status ?? "unknown"}</p>
      <p>Progress: {status?.progress ?? 0}%</p>
      {status?.status === "COMPLETED" && (
        <pre>{JSON.stringify(status.result, null, 2)}</pre>
      )}
    </div>
  )
}
```

---

## `jobs.getDetails(jobId)`

Get full job details including parameters, timestamps, and metadata.

```ts
async getDetails(jobId: string): Promise<Record<string, unknown>>
```

More expensive than `getStatus` — use for detailed inspection, not polling.

**Throws:** `IvyApiError` with `isNotFound` if the job doesn't exist.

### Examples

**TypeScript**

```ts
const details = await ivy.jobs.getDetails("job_abc123")
console.log(details.created_at, details.tool_name, details.analysis_level)
```

**Python**

```python
response = requests.get(
    f"{BASE_URL}/api/v1/jobs/job_abc123", headers=headers
)
details = response.json()
print(details["id"], details["status"])
```

**React**

```tsx
import { useIvyClient } from "@ivybiosciences/sdk/react"
import { useState, useEffect } from "react"

function JobDetails({ jobId }: { jobId: string }) {
  const ivy = useIvyClient()
  const [details, setDetails] = useState`<Record<string, unknown>` | null>(null)

  useEffect(() => {
    ivy.jobs.getDetails(jobId).then(setDetails)
  }, [jobId, ivy])

  if (!details) return <p>Loading...</p>

  return (
    <dl>
      <dt>Tool</dt><dd>{String(details.tool_name)}</dd>
      <dt>Created</dt><dd>{String(details.created_at)}</dd>
      <dt>Analysis</dt><dd>{String(details.analysis_level)}</dd>
    </dl>
  )
}
```

---

## `jobs.list(options?)`

List the authenticated user's jobs with pagination.

```ts
async list(options?: { skip?: number; limit?: number }): Promise<Record<string, unknown>[]>
```

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `options.skip` | `number` | `0` | Number of jobs to skip (offset) |
| `options.limit` | `number` | `100` | Maximum jobs to return |

**Returns:** Array of job records, ordered by creation time (newest first).

### Examples

**TypeScript**

```ts
const jobs = await ivy.jobs.list({ limit: 10 })
for (const job of jobs) {
  console.log(`${job.id}: ${job.status} (${job.tool_name})`)
}
```

**Python**

```python
response = requests.get(
    f"{BASE_URL}/api/v1/jobs", params={"pageSize": 10}, headers=headers
)
jobs = response.json()["jobs"]

for job in jobs:
    print(f"{job['id']}: {job['status']} ({job['tool_name']})")
```

**React**

```tsx
import { useIvyClient } from "@ivybiosciences/sdk/react"
import { useState, useEffect } from "react"

function JobList() {
  const ivy = useIvyClient()
  const [jobs, setJobs] = useState`<Record<string, unknown>`[]>([])

  useEffect(() => {
    ivy.jobs.list({ limit: 10 }).then(setJobs)
  }, [ivy])

  return (
    <ul>
      {jobs.map((job) => (
        <li key={String(job.id)}>
          {String(job.tool_name)} — {String(job.status)}
        </li>
      ))}
    </ul>
  )
}
```

---

## `jobs.delete(jobId)`

Delete a job and its associated S3 artifacts. Irreversible.

```ts
async delete(jobId: string): Promise<void>
```

**Throws:**
- `IvyApiError` with `isNotFound` if the job doesn't exist.
- `IvyApiError` with `isForbidden` if the job belongs to another user.

### Examples

**TypeScript**

```ts
await ivy.jobs.delete("job_abc123")
```

**Python**

```python
# The REST API has no delete operation; cancel an unfinished job instead.
response = requests.post(
    f"{BASE_URL}/api/v1/jobs/job_abc123/cancel", headers=headers
)
response.raise_for_status()  # Raises on 404 or 403
print("Job cancelled")
```

**React**

```tsx
import { useIvyClient } from "@ivybiosciences/sdk/react"

function DeleteJobButton({ jobId, onDeleted }: { jobId: string; onDeleted: () => void }) {
  const ivy = useIvyClient()

  const handleDelete = async () => {
    if (!confirm("Delete this job and all artifacts? This cannot be undone.")) return
    await ivy.jobs.delete(jobId)
    onDeleted()
  }

  return <button onClick={handleDelete}>Delete Job</button>
}
```

---

## Cancelling a job

`JobManager` has no `cancel` method. In React, the `cancel` function returned by `useIvyJob` stops the hook tracking the job on the client; it does not stop the job on the backend. To remove a job, use [`jobs.delete`](#jobsdeletejobid).

---

## `jobs.submitAndWait(params, options?)`

Submit a job and wait for it to reach a terminal state. The highest-level convenience method.

```ts
async submitAndWait(
  params: SubmitJobParams,
  options?: SubmitAndWaitOptions,
): Promise<JobProgress>
```

**Strategy:**
1. Submits the job via HTTP POST
2. Subscribes to WebSocket updates (if `useWebSocket` is true)
3. Falls back to HTTP polling if WebSocket fails
4. Resolves when status is `COMPLETED`, `FAILURE`, or `CANCELLED`

**Options:**

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `onProgress` | `(progress: JobProgress) => void` | — | Called on each status change |
| `pollInterval` | `number` | `3000` | Polling interval in ms (fallback mode) |
| `timeout` | `number` | `600000` | Max wait time in ms (10 minutes) |
| `useWebSocket` | `boolean` | `true` | Use WebSocket for real-time updates |

**Returns:** Final `JobProgress` with status, results, and/or errors.

**Throws:**
- `IvyTimeoutError` if the job doesn't finish within the timeout.
- `IvyApiError` on submission or polling errors.

### Examples

**TypeScript**

```ts
const result = await ivy.jobs.submitAndWait(
  {
    toolName: "diffdock",
    parameters: { protein_pdb: "...", ligand_smiles: "CCO" },
  },
  {
    onProgress: (p) => console.log(`[${p.phase}] ${p.progress}%`),
    timeout: 300_000, // 5 minutes
  }
)

if (result.status === "COMPLETED") {
  const pdbUrl = result.presignedUrl
  // Download the docked structure...
}
```

**Python**

```python
import time
import requests

# Submit
job = requests.post(f"{BASE_URL}/api/v1/jobs", json={
    "tool_name": "diffdock",
    "tool_input_parameters": {"protein_pdb": "...", "ligand_smiles": "CCO"},
}, headers=headers).json()

# Poll with progress logging
while True:
    status = requests.get(
        f"{BASE_URL}/api/v1/jobs/{job['id']}", headers=headers
    ).json()
    print(f"[{status.get('phase', '?')}] {status.get('progress', 0)}%")
    if status["status"] in ("COMPLETED", "FAILURE", "CANCELLED"):
        break
    time.sleep(3)

if status["status"] == "COMPLETED":
    pdb_url = status["presignedUrl"]
    # Download: requests.get(pdb_url).content
```

**React**

```tsx
import { useIvyJob } from "@ivybiosciences/sdk/react"

function DockingRunner() {
  const { submit, status, error } = useIvyJob({
    onProgress: (p) => console.log(`[${p.phase}] ${p.progress}%`),
  })

  return (
    <div>
      <button onClick={() => submit("diffdock", { protein_pdb: "...", ligand_smiles: "CCO" })}>
        Run Docking
      </button>
      {status?.phase && <p>{status.phase}: {status.progress}%</p>}
      {status?.status === "COMPLETED" && <a href={status.presignedUrl}>Download PDB</a>}
      {error && <p>Error: {error.message}</p>}
    </div>
  )
}
```

---

## Job Lifecycle

 STARTED
    STARTED --> PROCESSING
    PROCESSING --> COMPLETED
    PROCESSING --> FAILURE
    PROCESSING --> CANCELLED
    PROCESSING --> PROGRESS : repeated`}
  caption="Job status lifecycle."
/>

### Standard Phases

| Phase | Progress | Description |
|-------|----------|-------------|
| `initialization` | ~5% | Input validation |
| `data_preprocessing` | ~15% | Data loading and prep |
| `model_loading` | ~10% | ML model initialization |
| `main_computation` | ~50% | Core processing |
| `post_processing` | ~15% | Result analysis |
| `result_generation` | ~5% | Format and store |

### Analysis Levels

| Level | Description | Data Fields |
|-------|-------------|-------------|
| `basic` | Fast results, minimal metrics | Standard output only |
| `standard` | Balanced analysis (default) | Key quality metrics |
| `comprehensive` | Full analytical suite | 5-10x more fields |
