> ## 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.

# Figures

> Upload, manage, and organize figures, plots, charts, and diagrams linked to experiments and papers.

# Figures API

<Note>
  Today only `GET /v1/figures` (list) and `POST /v1/figures` (upload metadata) are shipped. Per-figure routes (`GET /v1/figures/{id}`, `/download`, `/image`) and gallery management (`/labs/{slug}/gallery*`) are planned, not yet live. Render figures via the lab site or Convex queries today.
</Note>

Figures are first-class objects in Hubify Labs. They can be generated by experiments, linked to papers, organized into galleries, and displayed on the public lab site. Figures are stored in Convex file storage with metadata for search and organization.

## Upload a Figure

Upload an image file and associate it with a lab, experiment, or paper.

<ParamField body="labId" type="string" required>
  Lab this figure belongs to.
</ParamField>

<ParamField body="title" type="string" required>
  Figure title (e.g., "Posterior Contours -- H0 vs Omega\_m").
</ParamField>

<ParamField body="file" type="file" required>
  Image file. Supported formats: PNG, JPG, SVG, PDF, WebP.
</ParamField>

<ParamField body="paperId" type="string">
  Link to a paper (for figures that appear in manuscripts).
</ParamField>

<ParamField body="experimentId" type="string">
  Link to the experiment that generated this figure.
</ParamField>

<ParamField body="scriptFile" type="string">
  Path to the script that generated this figure (for reproducibility).
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://www.hubify.com/api/v1/figures \
    -H "Authorization: Bearer $HUBIFY_TOKEN" \
    -F "labId=j57a8k9m2n3p4q5r" \
    -F "title=Posterior Contours -- H0 vs Omega_m" \
    -F "file=@posterior_contours.png" \
    -F "experimentId=EXP-054" \
    -F "scriptFile=scripts/plot_contours.py"
  ```

  ```typescript TypeScript SDK theme={null}
  const figure = await hubify.figures.upload({
    labId: "j57a8k9m2n3p4q5r",
    title: "Posterior Contours -- H0 vs Omega_m",
    file: posteriorContoursBlob,
    experimentId: "EXP-054",
    scriptFile: "scripts/plot_contours.py",
  });
  ```
</CodeGroup>

<ResponseField name="data" type="object">
  <Expandable title="Figure object">
    <ResponseField name="_id" type="string" required>Convex document ID.</ResponseField>
    <ResponseField name="labId" type="string" required>Parent lab ID.</ResponseField>
    <ResponseField name="paperId" type="string">Linked paper ID.</ResponseField>
    <ResponseField name="experimentId" type="string">Linked experiment ID.</ResponseField>
    <ResponseField name="title" type="string" required>Figure title.</ResponseField>
    <ResponseField name="imageUrl" type="string">Public URL of the image.</ResponseField>
    <ResponseField name="storageId" type="string">Convex storage ID for the file.</ResponseField>
    <ResponseField name="scriptFile" type="string">Generating script path.</ResponseField>
    <ResponseField name="createdAt" type="number" required>Creation timestamp (ms).</ResponseField>
  </Expandable>
</ResponseField>

## List Figures

<ParamField query="labId" type="string" required>
  Lab ID.
</ParamField>

<ParamField query="paperId" type="string">
  Filter to figures linked to a specific paper.
</ParamField>

<ParamField query="experimentId" type="string">
  Filter to figures generated by a specific experiment.
</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/figures?labId=j57a8k9m2n3p4q5r&paperId=q23f4g5h6i7j8k9l" \
    -H "Authorization: Bearer $HUBIFY_TOKEN"
  ```

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

## Get a Figure

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

  ```typescript TypeScript SDK theme={null}
  const figure = await hubify.figures.get({ figureId: "r34g5h6i7j8k9l0m" });
  ```
</CodeGroup>

## Update Figure Metadata

<ParamField body="title" type="string">Updated title.</ParamField>
<ParamField body="paperId" type="string">Link or re-link to a paper.</ParamField>
<ParamField body="experimentId" type="string">Link or re-link to an experiment.</ParamField>
<ParamField body="scriptFile" type="string">Updated script path.</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl -X PATCH https://www.hubify.com/api/v1/figures/r34g5h6i7j8k9l0m \
    -H "Authorization: Bearer $HUBIFY_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "title": "Fig. 3 -- Posterior Contours (H0 vs Omega_m, Planck+BAO)",
      "paperId": "q23f4g5h6i7j8k9l"
    }'
  ```

  ```typescript TypeScript SDK theme={null}
  await hubify.figures.update({
    figureId: "r34g5h6i7j8k9l0m",
    title: "Fig. 3 -- Posterior Contours (H0 vs Omega_m, Planck+BAO)",
    paperId: "q23f4g5h6i7j8k9l",
  });
  ```
</CodeGroup>

## Replace Figure Image

Upload a new image to replace the existing one while preserving metadata and links.

<CodeGroup>
  ```bash curl theme={null}
  curl -X PUT https://www.hubify.com/api/v1/figures/r34g5h6i7j8k9l0m/image \
    -H "Authorization: Bearer $HUBIFY_TOKEN" \
    -F "file=@posterior_contours_v2.png"
  ```

  ```typescript TypeScript SDK theme={null}
  await hubify.figures.replaceImage({
    figureId: "r34g5h6i7j8k9l0m",
    file: updatedBlob,
  });
  ```
</CodeGroup>

## Delete a Figure

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

  ```typescript TypeScript SDK theme={null}
  await hubify.figures.delete({ figureId: "r34g5h6i7j8k9l0m" });
  ```
</CodeGroup>

## Download a Figure

Get the raw image file.

<CodeGroup>
  ```bash curl theme={null}
  curl -o figure.png https://www.hubify.com/api/v1/figures/r34g5h6i7j8k9l0m/download \
    -H "Authorization: Bearer $HUBIFY_TOKEN"
  ```

  ```typescript TypeScript SDK theme={null}
  const blob = await hubify.figures.download({
    figureId: "r34g5h6i7j8k9l0m",
  });
  ```
</CodeGroup>

## Gallery Management

<Note>
  The gallery management endpoints (`/labs/{slug}/gallery`, `/labs/{slug}/gallery/settings`) are planned — not yet shipped. Figure ordering and lightbox settings are currently managed via the Lab Site view in the web app.
</Note>

Figures are automatically organized into a gallery on the public lab site. You can control the gallery through the site configuration.

### Get Gallery Order

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

  ```typescript TypeScript SDK theme={null}
  const gallery = await hubify.figures.getGallery({ slug: "dark-energy" });
  ```
</CodeGroup>

### Reorder Gallery

<ParamField body="figureIds" type="string[]" required>
  Ordered list of figure IDs defining the gallery sequence.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl -X PUT https://www.hubify.com/api/v1/labs/dark-energy/gallery \
    -H "Authorization: Bearer $HUBIFY_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "figureIds": [
        "r34g5h6i7j8k9l0m",
        "s45h6i7j8k9l0m1n",
        "t56i7j8k9l0m1n2o"
      ]
    }'
  ```

  ```typescript TypeScript SDK theme={null}
  await hubify.figures.reorderGallery({
    slug: "dark-energy",
    figureIds: [
      "r34g5h6i7j8k9l0m",
      "s45h6i7j8k9l0m1n",
      "t56i7j8k9l0m1n2o",
    ],
  });
  ```
</CodeGroup>

## Lightbox Configuration

The lab site gallery includes a lightbox viewer for full-resolution viewing. Configure lightbox behavior per lab.

<ParamField body="enabled" type="boolean" default={true}>
  Enable or disable the lightbox viewer.
</ParamField>

<ParamField body="showTitle" type="boolean" default={true}>
  Show figure titles in the lightbox overlay.
</ParamField>

<ParamField body="showScript" type="boolean" default={false}>
  Show the generating script path in the lightbox overlay.
</ParamField>

<ParamField body="showExperimentLink" type="boolean" default={true}>
  Show a link to the source experiment in the lightbox overlay.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl -X PATCH https://www.hubify.com/api/v1/labs/dark-energy/gallery/settings \
    -H "Authorization: Bearer $HUBIFY_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "enabled": true,
      "showTitle": true,
      "showScript": true,
      "showExperimentLink": true
    }'
  ```

  ```typescript TypeScript SDK theme={null}
  await hubify.figures.updateGallerySettings({
    slug: "dark-energy",
    enabled: true,
    showTitle: true,
    showScript: true,
    showExperimentLink: true,
  });
  ```
</CodeGroup>
