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

# Experiments

> Create, run, monitor, and manage GPU-powered experiments with automatic logging, QC gates, and status lifecycle.

# Experiments API

<Note>
  Shipped today: `GET /v1/experiments`, `POST /v1/experiments`, plus the sub-collections `/experiments/costs`, `/experiments/queues`, `/experiments/templates` (GET + POST each). Per-experiment routes (`GET /v1/experiments/{id}`, `/logs`, `/metrics`) are planned, not yet live. Stream logs via the experiment-detail view in the web app or the Convex `experiments.getLogs` query.
</Note>

Experiments are the core research unit in Hubify. Each experiment runs on GPU compute (or CPU), streams logs in real time, passes through QC gates, and stores results back to the lab.

## Experiment Status Lifecycle

```
queued → running → pass / fail / blocked
```

| Status    | Description                                 |
| --------- | ------------------------------------------- |
| `queued`  | Waiting for a compute pod or manual trigger |
| `running` | Actively executing on a pod                 |
| `pass`    | Completed successfully and passed QC        |
| `fail`    | Completed with errors or failed QC          |
| `blocked` | Waiting on a dependency or manual unblock   |

## Create an Experiment

<ParamField body="labId" type="string" required>
  Convex ID of the lab this experiment belongs to.
</ParamField>

<ParamField body="title" type="string" required>
  Human-readable experiment title (e.g., "MCMC Chain, Planck 2018 + BAO").
</ParamField>

<ParamField body="projectId" type="string">
  Convex ID of the parent project, if applicable.
</ParamField>

<ParamField body="experimentId" type="string">
  Custom identifier (e.g., "EXP-054"). Auto-generated if omitted.
</ParamField>

<ParamField body="computeMode" type="string" default="CPU">
  Compute target: `H200`, `H100`, `A100`, or `CPU`.
</ParamField>

<ParamField body="config" type="object">
  Experiment configuration as a JSON object. Stored as a string internally.

  <Expandable title="Config fields">
    <ParamField body="gpu_type" type="string">GPU type override.</ParamField>
    <ParamField body="batch_size" type="number">Batch size for data loading.</ParamField>
    <ParamField body="epochs" type="number">Number of training epochs.</ParamField>
    <ParamField body="script" type="string">Script path to execute on the pod.</ParamField>
    <ParamField body="env" type="object">Environment variables to set.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="template" type="string">
  Experiment template name (e.g., `mcmc`, `anomaly-sweep`, `fisher-forecast`).
</ParamField>

<ParamField body="priority" type="string" default="normal">
  Queue priority: `critical`, `high`, `normal`, `low`.
</ParamField>

<ParamField body="estimatedSeconds" type="number">
  Estimated runtime in seconds. Used for scheduling and cost projection.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://www.hubify.com/api/v1/experiments \
    -H "Authorization: Bearer $HUBIFY_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "labId": "j57a8k9m2n3p4q5r",
      "title": "MCMC Chain, Planck 2018 + BAO",
      "experimentId": "EXP-054",
      "computeMode": "H200",
      "config": {
        "script": "run_cobaya.py",
        "batch_size": 1,
        "env": {"COBAYA_PACKAGES": "/workspace/packages"}
      },
      "priority": "high",
      "estimatedSeconds": 7200
    }'
  ```

  ```typescript TypeScript SDK theme={null}
  const experiment = await hubify.experiments.create({
    labId: "j57a8k9m2n3p4q5r",
    title: "MCMC Chain, Planck 2018 + BAO",
    experimentId: "EXP-054",
    computeMode: "H200",
    config: {
      script: "run_cobaya.py",
      batch_size: 1,
      env: { COBAYA_PACKAGES: "/workspace/packages" },
    },
    priority: "high",
    estimatedSeconds: 7200,
  });
  ```
</CodeGroup>

<ResponseField name="data" type="object">
  <Expandable title="Experiment object">
    <ResponseField name="_id" type="string" required>Convex document ID.</ResponseField>
    <ResponseField name="labId" type="string" required>Parent lab ID.</ResponseField>
    <ResponseField name="projectId" type="string">Parent project ID.</ResponseField>
    <ResponseField name="experimentId" type="string" required>Display identifier (e.g., "EXP-054").</ResponseField>
    <ResponseField name="title" type="string" required>Experiment title.</ResponseField>
    <ResponseField name="status" type="string" required>Current status.</ResponseField>
    <ResponseField name="result" type="string">Summary of results after completion.</ResponseField>
    <ResponseField name="computeMode" type="string">GPU type or CPU.</ResponseField>
    <ResponseField name="config" type="string">JSON-encoded configuration.</ResponseField>
    <ResponseField name="priority" type="string">Queue priority.</ResponseField>
    <ResponseField name="queuePosition" type="number">Position in the queue (when queued).</ResponseField>
    <ResponseField name="estimatedSeconds" type="number">Estimated runtime.</ResponseField>
    <ResponseField name="runTimeSeconds" type="number">Actual runtime (after completion).</ResponseField>
    <ResponseField name="createdAt" type="number">Creation timestamp (ms).</ResponseField>
    <ResponseField name="completedAt" type="number">Completion timestamp (ms).</ResponseField>
  </Expandable>
</ResponseField>

## List Experiments

<ParamField query="labId" type="string" required>
  Lab ID to list experiments for.
</ParamField>

<ParamField query="status" type="string">
  Filter by status: `queued`, `running`, `pass`, `fail`, `blocked`.
</ParamField>

<ParamField query="projectId" type="string">
  Filter by parent project.
</ParamField>

<ParamField query="limit" type="number" default="50">
  Results per page.
</ParamField>

<ParamField query="cursor" type="string">
  Pagination cursor.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl "https://www.hubify.com/api/v1/experiments?labId=j57a8k9m2n3p4q5r&status=running" \
    -H "Authorization: Bearer $HUBIFY_TOKEN"
  ```

  ```typescript TypeScript SDK theme={null}
  const experiments = await hubify.experiments.list({
    labId: "j57a8k9m2n3p4q5r",
    status: "running",
  });
  ```
</CodeGroup>

## Get an Experiment

<CodeGroup>
  ```bash curl theme={null}
  curl https://www.hubify.com/api/v1/experiments/EXP-054 \
    -H "Authorization: Bearer $HUBIFY_TOKEN"
  ```

  ```typescript TypeScript SDK theme={null}
  const experiment = await hubify.experiments.get({ experimentId: "EXP-054" });
  ```
</CodeGroup>

## Update an Experiment

Update metadata or status of an experiment. Use this to manually transition status (e.g., unblock a blocked experiment).

<ParamField body="title" type="string">Updated title.</ParamField>
<ParamField body="status" type="string">New status. Valid transitions depend on current status.</ParamField>
<ParamField body="result" type="string">Result summary (typically set on completion).</ParamField>
<ParamField body="priority" type="string">Updated priority.</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl -X PATCH https://www.hubify.com/api/v1/experiments/EXP-054 \
    -H "Authorization: Bearer $HUBIFY_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "status": "queued",
      "priority": "critical"
    }'
  ```

  ```typescript TypeScript SDK theme={null}
  await hubify.experiments.update({
    experimentId: "EXP-054",
    status: "queued",
    priority: "critical",
  });
  ```
</CodeGroup>

## Delete an Experiment

Delete an experiment and its associated logs. Does not delete figures generated by the experiment.

<CodeGroup>
  ```bash curl theme={null}
  curl -X DELETE https://www.hubify.com/api/v1/experiments/EXP-054 \
    -H "Authorization: Bearer $HUBIFY_TOKEN"
  ```

  ```typescript TypeScript SDK theme={null}
  await hubify.experiments.delete({ experimentId: "EXP-054" });
  ```
</CodeGroup>

## Experiment Logs

Stream or retrieve logs for a running or completed experiment.

### List Logs

<ParamField query="experimentId" type="string" required>
  Experiment Convex ID.
</ParamField>

<ParamField query="level" type="string">
  Filter by log level: `info`, `warn`, `error`, `debug`.
</ParamField>

<ParamField query="limit" type="number" default="100">
  Number of log entries to return.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl "https://www.hubify.com/api/v1/experiments/EXP-054/logs?level=error&limit=50" \
    -H "Authorization: Bearer $HUBIFY_TOKEN"
  ```

  ```typescript TypeScript SDK theme={null}
  const logs = await hubify.experiments.getLogs({
    experimentId: "EXP-054",
    level: "error",
    limit: 50,
  });
  ```
</CodeGroup>

### Subscribe to Logs (Real-Time)

```typescript theme={null}
// Real-time log subscription via Convex
client.onUpdate(
  api.experimentLogs.list,
  { experimentId: "j57a8k9m2n3p4q5r" },
  (logs) => {
    logs.forEach((log) => console.log(`[${log.level}] ${log.message}`));
  }
);
```

## Experiment Metrics

After an experiment completes, metrics are available in the result and associated log entries.

<CodeGroup>
  ```bash curl theme={null}
  curl https://www.hubify.com/api/v1/experiments/EXP-054/metrics \
    -H "Authorization: Bearer $HUBIFY_TOKEN"
  ```

  ```typescript TypeScript SDK theme={null}
  const metrics = await hubify.experiments.getMetrics({
    experimentId: "EXP-054",
  });
  // { runtime: 3847, gpuUtilization: 0.94, peakVram: 78.2, costUsd: 4.27 }
  ```
</CodeGroup>

<ResponseField name="data" type="object">
  <Expandable title="Metrics object">
    <ResponseField name="runtime" type="number">Total runtime in seconds.</ResponseField>
    <ResponseField name="gpuUtilization" type="number">Average GPU utilization (0-1).</ResponseField>
    <ResponseField name="peakVram" type="number">Peak VRAM usage in GB.</ResponseField>
    <ResponseField name="costUsd" type="number">Total compute cost in USD.</ResponseField>
    <ResponseField name="logCount" type="number">Total log entries.</ResponseField>
    <ResponseField name="errorCount" type="number">Error-level log entries.</ResponseField>
  </Expandable>
</ResponseField>
