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

# API Reference

> Complete reference for the Veri REST API

## Base URL

```
https://api.veri.studio/v1
```

## Authentication

All API requests require a Bearer token. Create an API key from the dashboard or via the API keys endpoint.

```bash theme={null}
curl https://api.veri.studio/v1/training_jobs \
  -H "Authorization: Bearer vk_your_api_key"
```

API keys use the `vk_` prefix and are hashed server-side. Manage keys from the [Veri dashboard](https://veri.studio/dashboard/settings).

## Endpoints

### Datasets

| Method | Endpoint                        | Description                         |
| ------ | ------------------------------- | ----------------------------------- |
| `POST` | `/v1/datasets`                  | Upload a JSONL dataset              |
| `POST` | `/v1/datasets/connect`          | Register an external dataset source |
| `POST` | `/v1/datasets/connect/validate` | Validate a source connection        |
| `GET`  | `/v1/datasets`                  | List datasets                       |
| `GET`  | `/v1/datasets/{id}`             | Get dataset info                    |

### Reward Functions

| Method | Endpoint                    | Description              |
| ------ | --------------------------- | ------------------------ |
| `POST` | `/v1/reward_functions`      | Upload a reward function |
| `GET`  | `/v1/reward_functions`      | List reward functions    |
| `GET`  | `/v1/reward_functions/{id}` | Get reward function info |

### Training Jobs

| Method   | Endpoint                           | Description                                       |
| -------- | ---------------------------------- | ------------------------------------------------- |
| `POST`   | `/v1/training_jobs`                | Create a training job                             |
| `GET`    | `/v1/training_jobs`                | List training jobs (filter by `status`, `method`) |
| `GET`    | `/v1/training_jobs/{id}`           | Get job status                                    |
| `POST`   | `/v1/training_jobs/{id}/cancel`    | Cancel a job                                      |
| `DELETE` | `/v1/training_jobs/{id}`           | Hard-delete a finished job                        |
| `GET`    | `/v1/training_jobs/{id}/logs`      | Stream stdout tail                                |
| `GET`    | `/v1/training_jobs/{id}/logs/full` | Full stdout from S3 (after job ends)              |
| `GET`    | `/v1/training_jobs/{id}/events`    | Get job lifecycle events                          |
| `GET`    | `/v1/training_jobs/{id}/metrics`   | Sampled training metrics                          |
| `GET`    | `/v1/training_jobs/{id}/model`     | Get checkpoint download URL                       |

See the [Training jobs API](/training/api) for full schemas.

### Deployments

| Method | Endpoint                                | Description                           |
| ------ | --------------------------------------- | ------------------------------------- |
| `POST` | `/v1/deployments`                       | Create an inference deployment        |
| `GET`  | `/v1/deployments`                       | List deployments (filter by `status`) |
| `GET`  | `/v1/deployments/{id}`                  | Get deployment details                |
| `POST` | `/v1/deployments/{id}/stop`             | Stop a deployment                     |
| `POST` | `/v1/deployments/{id}/chat/completions` | OpenAI-compatible inference           |
| `GET`  | `/v1/deployments/{id}/requests`         | List per-request logs                 |
| `GET`  | `/v1/deployments/{id}/metrics`          | Get aggregate metrics                 |

### Volumes

See the [Volumes guide](/volumes) for usage.

| Method   | Endpoint                          | Description                |
| -------- | --------------------------------- | -------------------------- |
| `POST`   | `/v1/volumes`                     | Create a volume            |
| `GET`    | `/v1/volumes`                     | List volumes               |
| `GET`    | `/v1/volumes/{name}`              | Get a volume               |
| `DELETE` | `/v1/volumes/{name}`              | Delete a volume            |
| `GET`    | `/v1/volumes/{name}/files`        | List files                 |
| `DELETE` | `/v1/volumes/{name}/files/{path}` | Delete a file              |
| `POST`   | `/v1/volumes/{name}/upload-url`   | Get a presigned upload URL |

### Billing

| Method | Endpoint                   | Description               |
| ------ | -------------------------- | ------------------------- |
| `GET`  | `/v1/billing/balance`      | Get credit balance        |
| `GET`  | `/v1/billing/transactions` | List balance transactions |

### API Keys

| Method   | Endpoint            | Description                                    |
| -------- | ------------------- | ---------------------------------------------- |
| `POST`   | `/v1/api_keys`      | Create a new API key (plaintext returned once) |
| `GET`    | `/v1/api_keys`      | List active API keys (prefix only)             |
| `DELETE` | `/v1/api_keys/{id}` | Revoke an API key                              |

### SSH Keys

| Method   | Endpoint            | Description                                |
| -------- | ------------------- | ------------------------------------------ |
| `POST`   | `/v1/ssh_keys`      | Upload an SSH key pair (encrypted at rest) |
| `GET`    | `/v1/ssh_keys`      | List SSH keys                              |
| `DELETE` | `/v1/ssh_keys/{id}` | Delete an SSH key                          |

## Request Format

* **JSON endpoints** (`POST /v1/training_jobs`): Use `Content-Type: application/json` with a JSON body.
* **Upload endpoints** (`POST /v1/datasets`, `POST /v1/reward_functions`): Use `multipart/form-data`.
* **Connector endpoints** (`POST /v1/datasets/connect`, `POST /v1/datasets/connect/validate`): Send JSON bodies.

## Response Format

Responses are standard JSON. Every response object includes an `object` field that identifies the resource type (e.g., `"training_job"`, `"deployment"`, `"dataset"`, `"reward_function"`, `"api_key"`, `"ssh_key"`).

Single-resource endpoints return one object. List endpoints return cursor-paginated responses:

```json theme={null}
{
  "object": "list",
  "data": [...],
  "has_more": false
}
```

**Success (2xx)**

```json theme={null}
{
  "id": "job_abc123",
  "object": "training_job",
  "status": "queued",
  "base_model": "Qwen/Qwen3-4B",
  "method": "grpo",
  "output_name": "my-model",
  "gpu_requested": {
    "type": "A100-80GB",
    "count": 1
  },
  "gpu": null,
  "created_at": "2026-04-14T12:00:00Z"
}
```

**Error (4xx/5xx)**

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": null,
    "message": "Invalid API key",
    "param": null
  }
}
```

## Common Status Codes

| Status | Meaning                                         |
| ------ | ----------------------------------------------- |
| 400    | Invalid request or unsupported state transition |
| 401    | Missing or invalid API key                      |
| 402    | Insufficient credit balance                     |
| 404    | Resource not found or not owned by the caller   |
| 422    | Request body failed schema validation           |
| 500    | Unexpected server error                         |

## Pagination

List endpoints support cursor-based pagination with `limit` and `after` query parameters:

```bash theme={null}
curl "https://api.veri.studio/v1/datasets?limit=10&after=ds_abc123" \
  -H "Authorization: Bearer vk_your_api_key"
```

| Parameter | Type    | Default | Description                                         |
| --------- | ------- | ------- | --------------------------------------------------- |
| `limit`   | integer | 20      | Maximum number of items to return                   |
| `after`   | string  | —       | ID of the last item from the previous page (cursor) |

The response includes a `has_more` field indicating whether additional pages exist:

```json theme={null}
{
  "object": "list",
  "data": [...],
  "has_more": true
}
```

To paginate, pass the `id` of the last item in `data` as the `after` parameter in the next request.
