# Welcome

Welcome to **GoFigr**—the platform for zero-effort reproducibility in data science and scientific computing.

## What is GoFigr?

GoFigr automatically captures every figure you create in Python, R, or Jupyter notebooks, along with the complete context: source code, data transformations, and execution environment. This enables:

* **Instant reproducibility** — Every figure is linked to the code that created it
* **Effortless collaboration** — Share figures with full provenance
* **AI-powered presentations** — Transform figure collections into polished stories
* **Complete traceability** — Track figures from exploration to publication

## Quick Links

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Get Started</strong></td><td>Install GoFigr and capture your first figure</td><td><a href="/pages/bTQzlB1qOPigd0rrsSaU">/pages/bTQzlB1qOPigd0rrsSaU</a></td></tr><tr><td><strong>Managed Compute</strong></td><td>Launch a cloud machine with GoFigr built in</td><td><a href="/pages/FLuucBzMaV2tFi4DWv7N">/pages/FLuucBzMaV2tFi4DWv7N</a></td></tr><tr><td><strong>Explore Features</strong></td><td>Discover AI Story Mode, Git import, and more</td><td><a href="/pages/6UqWXhqLwbiw8ZvukRkJ">/pages/6UqWXhqLwbiw8ZvukRkJ</a></td></tr></tbody></table>

## Install

| Platform         | Install                          |
| ---------------- | -------------------------------- |
| Python / Jupyter | `pip install gofigr && gfconfig` |
| R                | `install.packages("gofigR")`     |

Prefer not to install anything? [**Managed Compute**](/managed-compute/compute) gives you a cloud machine with the GoFigr client already built in.

## Getting Help

* **Support**: <support@gofigr.io>
* **Website**: [gofigr.io](https://gofigr.io)


# Quick Start

Get up and running with GoFigr in under 5 minutes.

## 1. Create an Account

Visit [app.gofigr.io/register](https://app.gofigr.io/register) and sign up for a free account.

## 2. Install the Package

{% tabs %}
{% tab title="Python" %}

```bash
pip install gofigr
```

This installs both the client library and the IPython extension (compatible with Jupyter, VSCode, and others).
{% endtab %}

{% tab title="R" %}

```r
# From CRAN
install.packages("gofigR")

# Or from GitHub (development version)
library(devtools)
devtools::install_github("gofigr/gofigR")
```

{% endtab %}
{% endtabs %}

## 3. Configure GoFigr

Run the configuration wizard to set up your credentials and default workspace.

{% tabs %}
{% tab title="Python" %}
Run in your terminal:

```bash
gfconfig
```

You'll be prompted for:

* **Username**: Your GoFigr username
* **Password**: Your GoFigr password
* **API Key**: Leave blank to generate a new key
* **Key Name**: A descriptive name (e.g., "My Laptop")
* **Default Workspace**: Select from your available workspaces

Example session:

```
$ gfconfig
------------------------------
GoFigr configuration
------------------------------
Username: alyssa
Password:
Verifying connection...
  => Authenticated successfully
API key (leave blank to generate a new key):
Key name: Alyssa's Macbook
  => Your new API key will be saved to /Users/alyssa/.gofigr

Please select a default workspace:
  [ 1] - Scratchpad - alyssa's personal workspace
Selection [1]: 1

Configuration saved to /Users/alyssa/.gofigr. Happy analysis!
```

{% endtab %}

{% tab title="R" %}

```r
library(gofigR)
gfconfig()
```

You'll be prompted for:

* **Username**: Your GoFigr username
* **Password** (for initial key generation)
* **API Key**: Leave blank to generate a new key
* **Key Name**: A descriptive name (e.g., "My Laptop")
* **Default Workspace**: Select from your available workspaces

Example session:

```
> gfconfig()
-------------------------------------------------------------------
Welcome to GoFigr! This wizard will help you get up and running.
-------------------------------------------------------------------

Username: alyssa
Testing connection...
  => Success

API key (leave blank to generate a new one): 
Key name (e.g. Alyssa's laptop): My laptop

1. Scratchpad - e5249bed-40f0-4336-9bd3-fef30d3ed10d

Please select a default workspace (1-1): 1

Configuration saved to /Users/alyssa/.gofigr. Happy analysis!
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Prefer a guided setup?** Use the [Real-Time Capture setup wizard](/features/realtime-capture) in the GoFigr web app to generate a ready-to-use starter file for your environment — no manual configuration needed.
{% endhint %}

## 4. Capture Your First Figure

{% tabs %}
{% tab title="Python (Jupyter)" %}

```python
%load_ext gofigr

import matplotlib.pyplot as plt

# Create a figure - it's automatically captured!
plt.plot([1, 2, 3], [1, 4, 9])
plt.title("My First GoFigr Figure")
```

That's it! GoFigr will:

* Automatically use your default workspace from `gfconfig`
* Create an analysis named after your notebook
* Capture all figures with their source code
  {% endtab %}

{% tab title="Python (Scripts)" %}

```python
import matplotlib.pyplot as plt
from gofigr.publisher import Publisher

# Initialize the publisher
pub = Publisher(workspace="My Workspace", analysis="Script Analysis")

# Create a figure
plt.plot([1, 2, 3], [1, 4, 9])
plt.title("My First GoFigr Figure")

# Publish it
pub.publish(plt.gcf())
```

{% endtab %}

{% tab title="R" %}

```r
library(gofigR)

# Enable GoFigr
gofigR::enable()

# Create and publish a figure
library(ggplot2)

p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  ggtitle("My First GoFigr Figure")

publish(p, "Weight vs MPG")
```

{% endtab %}
{% endtabs %}

## 5. View Your Figure

1. Go to [app.gofigr.io](https://app.gofigr.io)
2. Navigate to your workspace
3. Find your figure with full source code attached!

The figure will appear with a QR code and unique revision ID, allowing you to track it in the GoFigr web app.

***

## Next Steps

* [Installation Guide](/getting-started/installation) — Detailed setup and advanced configuration
* [Managed Compute](/managed-compute/compute) — Prefer a zero-setup cloud machine? Launch one with GoFigr already installed
* [AI Story Mode](/features/story-mode) — Turn figures into presentations
* [Git Import](/features/git-import) — Import existing notebooks


# Installation

Detailed installation instructions for all supported platforms.

## Prerequisites

Before you begin, create a free account at [app.gofigr.io/register](https://app.gofigr.io/register).

***

## Python

### Requirements

* Python 3.8 or higher
* pip

### Install via pip

```bash
pip install gofigr
```

This installs both the client library and the IPython extension (compatible with Jupyter, VSCode, and others).

### Configuration

After installation, run the `gfconfig` command-line tool:

```bash
gfconfig
```

This will prompt you for your credentials and save them to `~/.gofigr`.

For advanced options (custom API URL, auto-publish settings, default metadata):

```bash
gfconfig --advanced
```

### Jupyter Usage

The simplest way to use GoFigr in Jupyter is to load the extension:

```python
%load_ext gofigr
```

That's it! All figures you create will be automatically published.

For custom configuration:

```python
%load_ext gofigr

from gofigr.jupyter import configure, FindByName

configure(
    workspace=FindByName("My Workspace"),
    analysis=FindByName("My Analysis", create=True),
    auto_publish=True
)
```

### Script Usage

For standalone Python scripts, use the `Publisher` class:

```python
import matplotlib.pyplot as plt
from gofigr.publisher import Publisher

pub = Publisher(workspace="My Workspace", analysis="Script Analysis")

plt.plot([1, 2, 3], [1, 4, 9])
plt.title("My Plot")

pub.publish(plt.gcf())
```

### Environment Variables

Instead of using `gfconfig`, you can set environment variables:

| Variable          | Description                                     |
| ----------------- | ----------------------------------------------- |
| `GF_USERNAME`     | Your GoFigr username                            |
| `GF_PASSWORD`     | Your GoFigr password                            |
| `GF_API_KEY`      | Your API key (alternative to username/password) |
| `GF_WORKSPACE`    | Workspace API ID                                |
| `GF_ANALYSIS`     | Analysis API ID                                 |
| `GF_URL`          | API URL (default: `https://api.gofigr.io`)      |
| `GF_AUTO_PUBLISH` | `true` or `false`                               |

***

## R

### Requirements

* R 4.0 or higher (tested with R 4.3.2)

### Install from CRAN

```r
install.packages("gofigR")
```

### Install from GitHub (Development Version)

For the latest development version:

```r
library(devtools)
devtools::install_github("gofigr/gofigR")
```

### Configuration

On the R prompt, load the package and run the configuration wizard:

```r
library(gofigR)
gfconfig()
```

This will prompt you for your credentials and save them to `~/.gofigr`.

### R Markdown Usage

In your setup chunk, enable GoFigr:

````markdown
```{r setup, include=FALSE}
library(gofigR)
gofigR::enable()
```
````

You can optionally specify an analysis name:

```r
gofigR::enable(analysis_name = "My Analysis")
```

### Publishing Plots

Use the `publish()` function to capture figures:

```r
library(ggplot2)

# ggplot2
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
publish(p, "Weight vs MPG")

# Or with pipe
p %>% publish("Weight vs MPG")
```

For base R graphics, wrap the plotting code:

```r
publish({
  plot(pressure, main = "Pressure vs Temperature")
  text(200, 50, "Note the non-linear relationship")
}, figure_name = "Pressure Plot")
```

### Shiny Integration

Replace `plotOutput + renderPlot` with `gfPlot + gfPlotServer`:

```r
library(shiny)
library(gofigR)

gofigR::enable()

ui <- fluidPage(
  titlePanel("My App"),
  mainPanel(
    gfPlot("myPlot")
  )
)

server <- function(input, output) {
  gfPlotServer("myPlot", {
    hist(faithful$eruptions, main = "Eruption Duration")
  }, input, figure_name = "Faithful Histogram")
}

shinyApp(ui = ui, server = server)
```

***

## Configuration File

Both Python and R store configuration in `~/.gofigr`. This file is created automatically by the `gfconfig` command/function.

***

## Troubleshooting

### Authentication Errors

If you get authentication errors:

1. Run `gfconfig` again to update your credentials
2. Check that your API key is valid in the GoFigr web app
3. Verify environment variables if using them

### Connection Issues

* Verify you can reach `https://api.gofigr.io`
* Check firewall settings if on a corporate network
* For enterprise installations, ensure `GF_URL` is set correctly

For more help, visit [gofigr.io/support](https://gofigr.io/support).


# Data Model

GoFigr organizes your work into a hierarchy that mirrors how data science and scientific computing projects are naturally structured: teams own projects, projects contain analyses, and analyses contain figures.

## At a Glance

```
Organization
  └── Workspace
        ├── Analysis
        │     └── Figure
        │           └── Figure Revision  (images, code, metadata)
        ├── Asset
        │     └── Asset Revision  (data files)
        └── Story  (AI-generated presentations)
```

| Object              | What it represents                                                | Created by                 |
| ------------------- | ----------------------------------------------------------------- | -------------------------- |
| **Organization**    | A team, lab, or company                                           | GoFigr admin (Pro feature) |
| **Workspace**       | A project or area of work                                         | Web UI or client           |
| **Analysis**        | A notebook, script, or document                                   | Client (auto) or Web UI    |
| **Figure**          | A named visualization that evolves over time                      | Client (auto) or Web UI    |
| **Figure Revision** | A single snapshot of a figure—image, code, and metadata           | Client (auto)              |
| **Asset**           | A tracked data file (CSV, Excel, Parquet, Jupyter notebook, etc.) | Client                     |
| **Asset Revision**  | A specific version of an asset, identified by content hash        | Client (auto)              |
| **Story**           | An AI-generated presentation or report built from figures         | Web UI                     |

***

## Organizations (GoFigr Pro)

An **organization** represents a team, lab, department, or company. Organizations own workspaces and control who has access to them.

**Key points:**

* Every workspace belongs to exactly one organization.
* Organization members are assigned roles that determine what they can do across the organization's workspaces.
* You can belong to multiple organizations
* Organization admins can set custom branding (e.g. company logo)

**Membership roles** (from most to least privileged):

| Role              | Can manage org settings | Can create workspaces | Can view all workspaces |
| ----------------- | ----------------------- | --------------------- | ----------------------- |
| Admin             | ✅                       | ✅                     | ✅                       |
| Workspace Admin   | —                       | ✅                     | ✅                       |
| Workspace Creator | —                       | ✅                     | Own only                |
| Workspace Viewer  | —                       | —                     | Assigned only           |

Organizations are managed through the GoFigr web UI at [app.gofigr.io](https://app.gofigr.io).

***

## Workspaces

A **workspace** is your primary unit of organization in GoFigr. Think of it as a project folder—everything related to a particular project, study, or area of work lives in one workspace.

**Key points:**

* A workspace contains analyses, assets, and stories.
* Each workspace has its own set of members with independent access controls.
* When you run `gfconfig`, you select a default workspace that the Python and R clients use automatically.
* You can switch workspaces at any time in the web UI or override the default in your client code.

**What belongs in a workspace:**

A good rule of thumb is one workspace per project. For example, a clinical trial might have its own workspace, as would a machine learning experiment or a course you're teaching. Related notebooks, figures, data files, and presentations all live together.

**Workspace members** have one of these roles:

| Role    | View content | Create analyses & figures | Manage members | Full control |
| ------- | ------------ | ------------------------- | -------------- | ------------ |
| Viewer  | ✅            | —                         | —              | —            |
| Creator | ✅            | ✅                         | —              | —            |
| Admin   | ✅            | ✅                         | ✅              | —            |
| Owner   | ✅            | ✅                         | ✅              | ✅            |

{% tabs %}
{% tab title="Python" %}

```python
from gofigr.jupyter import configure, FindByName

configure(
    workspace=FindByName("Clinical Trial 42"),
    # ...
)
```

{% endtab %}

{% tab title="R" %}

```r
library(gofigR)
gofigR::enable(workspace_name = "Clinical Trial 42")
```

{% endtab %}
{% endtabs %}

***

## Analyses

An **analysis** groups related figures together. It typically corresponds to a single notebook, script, or document.

**Key points:**

* Analyses live inside a workspace.
* In Jupyter, an analysis is created automatically using the notebook's filename (e.g., `exploration.ipynb` becomes an analysis called "exploration").
* In R, the analysis name defaults to the R Markdown document title or can be set manually.
* You can also create analyses manually in the web UI—for example, when importing figures from PowerPoint or Word documents.
* Analyses can be shared independently via link sharing, even if the rest of the workspace is private.

**When are analyses created?**

{% tabs %}
{% tab title="Python (Jupyter)" %}
Automatically when you load the GoFigr extension. The analysis is named after your notebook file:

```python
%load_ext gofigr
# Analysis "My_Notebook" is created (or reused) automatically
```

You can override the name:

```python
from gofigr.jupyter import configure, FindByName

configure(analysis=FindByName("Dose Response Analysis", create=True))
```

{% endtab %}

{% tab title="Python (Scripts)" %}
Specified when you create a `Publisher`:

```python
from gofigr.publisher import Publisher

pub = Publisher(workspace="My Workspace", analysis="Batch Processing Results")
```

{% endtab %}

{% tab title="R" %}
Optionally specified when enabling GoFigr, or defaults to the document title:

```r
library(gofigR)
gofigR::enable(analysis_name = "Survival Analysis")
```

{% endtab %}
{% endtabs %}

***

## Figures

A **figure** represents a named visualization that can evolve over time. Each time you re-run a cell or script that produces the same plot, GoFigr creates a new revision under the same figure, building a complete version history.

**Key points:**

* Figures live inside an analysis.
* A figure has a name (e.g., "Survival Curve" or "PCA Scatter Plot") and accumulates revisions over time.
* In the web UI, you browse figures and can drill into individual revisions or compare versions.
* Figures can be shared via link sharing or with specific users, independent of the parent analysis or workspace.
* Figures can also be imported from Git repositories, PowerPoint files, or Word documents through the web UI.

**When are figures created?**

{% tabs %}
{% tab title="Python (Jupyter)" %}
Automatically each time a plotting cell executes. GoFigr names figures based on the plot title or assigns a default name:

```python
%load_ext gofigr

import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [1, 4, 9])
plt.title("Growth Curve")
# Figure "Growth Curve" is created (or a new revision is added if it already exists)
```

You can also publish manually with an explicit name:

```python
from gofigr.jupyter import publish, FindByName

publish(fig=plt.gcf(), target=FindByName("Growth Curve", create=True))
```

{% endtab %}

{% tab title="Python (Scripts)" %}
Created when you call `publish()`:

```python
from gofigr.publisher import Publisher

pub = Publisher(workspace="My Workspace", analysis="Analysis")

plt.plot([1, 2, 3], [1, 4, 9])
pub.publish(plt.gcf())
```

{% endtab %}

{% tab title="R" %}
Created when you call `publish()`:

```r
library(ggplot2)

p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  ggtitle("Weight vs MPG")

publish(p, "Weight vs MPG")
```

{% endtab %}
{% endtabs %}

***

## Figure Revisions

A **figure revision** is a single snapshot of a figure at a point in time. It is the core unit of reproducibility in GoFigr—each revision captures everything needed to understand and recreate a visualization.

**What a revision contains:**

| Component         | Description                                                                       |
| ----------------- | --------------------------------------------------------------------------------- |
| **Image**         | The rendered figure in one or more formats (PNG, SVG, HTML for interactive plots) |
| **Source code**   | The code cell or script that produced the figure                                  |
| **Metadata**      | Execution environment, library versions, timestamps, custom key-value pairs       |
| **Data**          | Inline data tables (DataFrames) embedded with the revision                        |
| **Linked assets** | References to tracked data files (see [Assets](#assets) below)                    |

**Key points:**

* Revisions are immutable—once created, they don't change. This guarantees reproducibility.
* Each revision has a unique ID and QR code, which appear below the figure in your notebook. Scanning the QR code or clicking the link takes you directly to that revision in the web UI.
* You can compare revisions side-by-side in the web UI to see how a figure evolved.
* Revisions inherit sharing permissions from their parent figure but can also be shared independently.

**When are revisions created?**

A new revision is created every time a figure is published. In Jupyter with auto-publish enabled, this means every time you execute a cell that produces a plot. In R, it happens every time you call `publish()`. Each run of your notebook or script adds a new revision, creating a full history.

***

## Assets

An **asset** is a tracked data file in your workspace. While figures capture visualizations, assets capture the data that feeds into them—CSV files, Excel spreadsheets, Parquet datasets, and more.

**Key points:**

* Assets live at the workspace level (not inside an analysis), because the same dataset is often used across multiple analyses.
* Like figures, assets have revisions. Each time the file content changes, a new asset revision is created.
* Asset revisions are identified by a content hash: if the file hasn't changed, no new revision is created. This is efficient and ensures you always know exactly which version of the data was used.
* When a figure is created from tracked data, the figure revision is automatically linked to the corresponding asset revision. This means you can always trace back from a figure to the exact data that produced it.

**How assets get tracked:**

{% tabs %}
{% tab title="Python" %}
Use GoFigr's tracked reading functions instead of pandas:

```python
%load_ext gofigr

# Instead of pd.read_csv():
df = gf.read_csv("data/patients.csv")

# Now any figure created from df is automatically linked to this data version
plt.scatter(df['age'], df['biomarker'])
plt.title("Age vs Biomarker")
```

GoFigr supports `read_csv`, `read_excel`, `read_json`, `read_parquet`, `read_feather`, and `read_pickle`—all with the same API as their pandas equivalents.

You can also sync files manually:

```python
gf.sync.sync("data/patients.csv")
```

{% endtab %}

{% tab title="R" %}
Use `sync_workspace_asset()` to track data files:

```r
library(gofigR)

asset_rev <- sync_workspace_asset(gf, workspace_id, "data/patients.csv")
```

{% endtab %}
{% endtabs %}

### Figures vs. Assets

|                       | Figures                                       | Assets                                                   |
| --------------------- | --------------------------------------------- | -------------------------------------------------------- |
| **What they store**   | Visualizations (plots, charts, images)        | Data files (CSV, Excel, Parquet, etc.)                   |
| **Where they live**   | Inside an analysis                            | At the workspace level                                   |
| **Revisions contain** | Images, code, metadata, data frames           | File content                                             |
| **Created by**        | Publishing a plot                             | Tracking/syncing a data file                             |
| **Relationship**      | A figure revision can link to asset revisions | An asset revision can be linked to many figure revisions |

The link between figures and assets is what makes GoFigr's traceability work: you can start from any figure and trace back through the code, the data, and the environment to fully understand how it was created.

***

## Stories

A **story** is an AI-generated presentation or report built from a collection of figures. Stories bring your figures together into a narrative, with AI-generated titles, descriptions, and transitions.

**Key points:**

* Stories are created from the web UI by selecting figures within an analysis.
* The AI analyzes not just the images but also the source code, metadata, and data context to generate accurate technical descriptions.
* Stories can be exported as PowerPoint or Word documents.
* Like other objects, stories can be shared via link or with specific users.

To learn more, see [AI Story Mode](/features/story-mode).

***

## Sharing & Permissions

GoFigr provides two ways to share content:

**Link sharing** — Generate a public link for any analysis, figure, or asset. Anyone with the link can view the content without needing a GoFigr account.

**User sharing** — Share with specific GoFigr users, granting them access through their account.

Sharing can be configured at any level of the hierarchy. For example, you can share a single figure revision without exposing the rest of the analysis or workspace.

***

## How It All Fits Together

Here's a typical workflow showing how the data model comes to life:

1. **You create a workspace** for your project (e.g., "Lung Cancer Biomarkers") through the web UI or during `gfconfig` setup.
2. **You open a Jupyter notebook** and load GoFigr. An analysis is automatically created from the notebook name.
3. **You load data** using `gf.read_csv()` (note `gf`, not `pd`) or similar. GoFigr creates an asset in your workspace and tracks the file version.
4. **You create plots.** Each plot becomes a figure, and each execution creates a new figure revision containing the image, code, and a link to the data asset.
5. **You iterate.** As you refine your analysis and re-run cells, new revisions accumulate under each figure, giving you a complete history.
6. **You share results.** In the web UI, you create a story from your figures. The AI generates a polished presentation that you export to PowerPoint and share with your team.
7. **A colleague has a question** about one of your figures. They scan the QR code below the figure, which takes them directly to the figure revision in GoFigr—complete with source code, data lineage, and execution context.


# Overview

GoFigr provides a comprehensive suite of tools for scientific figure management, reproducibility, and collaboration.

## Core Features

### 🤖 AI-Powered Story Mode

Transform your figure collections into polished presentations, reports, or tutorials with AI-generated narratives. The AI analyzes not just the images, but the complete context—source code, metadata, and data transformations—to create accurate, technically detailed descriptions.

[Learn more →](/features/story-mode)

### 💬 Comments & Collaboration

Add comments to any figure, document, or asset. Supports Markdown formatting, @mentions with email notifications, threaded replies, and emoji reactions. Perfect for team feedback and peer review.

[Learn more →](/features/comments)

### 📂 Git Repository Import

Import Jupyter notebooks directly from GitHub, GitLab, or Bitbucket. GoFigr extracts figures from every commit across selected branches, preserving complete version history and attribution.

[Learn more →](/features/git-import)

### 📄 Document Import

Extract figures from PowerPoint and Word documents with AI-powered title generation. Maintain links between figures and their source documents for complete traceability.

[Learn more →](/features/document-import)

### 🔍 Enhanced Search

Find figures by text or visual similarity. Search across your entire workspace with results grouped by source. Uses advanced vector search for finding visually similar figures.

[Learn more →](/features/search)

### 🏢 Workspace Management

Organize work by project, team, or client. Quick workspace switching, custom branding with organization logos, and flexible access controls.

[Learn more →](/features/workspaces)

### 📋 Document Assistant

Manage figure-document relationships with visual previews. Unlink figures when needed, with options to clean up associated revisions.

[Learn more →](/features/document-assistant)

### 🧪 Clean Room

Turn a Python or R function into a self-contained, reproducible, interactive application. GoFigr captures code, data, parameters, and environment automatically—stakeholders can explore "what if" scenarios in the browser without touching your notebook.

[Python →](/features/clean-room-python) · [R →](/features/clean-room-r)

### 🏷️ Auto-Assign

Publish figures without naming them. GoFigr uses AI to generate titles and automatically deduplicate—matching revisions are grouped under existing figures.

[Learn more →](/features/auto-assign)

### 🔗 Sharing & Short IDs

Share figures via compact short ID links and QR codes. Every published revision gets a permanent 9-character URL suitable for papers, slides, and social media.

[Learn more →](/features/sharing)


# Real-Time Capture Setup

GoFigr's Real-Time Capture automatically syncs every figure you create — along with its source code, data, and environment — to the GoFigr web app. Once enabled, there's nothing to do manually: figures appear in GoFigr as you create them.

The quickest way to get started is the **setup wizard** in the GoFigr web app. It generates a ready-to-use starter file for your environment in four steps.

## Launching the Wizard

Click **Real-Time Capture** under Quick Actions on the home page:

![Quick Actions section showing the Real-Time Capture button](/files/nPYIgZEh7ERPW6BijHqK)

## Walkthrough

### Step 1: Choose Your Environment

Select the environment that matches your workflow:

* **Jupyter Notebook** — JupyterLab, Jupyter Notebook, VS Code notebooks
* **Python Script** — Standalone `.py` files for scripts and applications
* **R Documents & Scripts** — R Markdown, R scripts, RStudio, Quarto

![Choose your environment - Jupyter Notebook, Python Script, or R Documents & Scripts](/files/MvB33YJ3grUg0q3v2CiS)

### Step 2: Install & Configure

The wizard shows the install commands for your chosen environment. For Python, this is:

```bash
$ pip install gofigr
$ gfconfig
```

`gfconfig` is a one-time setup that saves your API key and default workspace to `~/.gofigr`. If you've already run it, click "Skip to next step" to continue.

![Install and configure step showing pip install and gfconfig commands](/files/4csi2TDeexfaWhTEhy8u)

### Step 3: Configure Where Figures Go

Choose how your figures are routed:

* **Use my gfconfig defaults** — Uses the workspace and API key from your `gfconfig` setup (Step 2). The analysis will be named after your file.
* **API Key** — Optionally create a new API key dedicated to this setup, or use an existing one.

![Configure where figures go - use gfconfig defaults or create a new API key](/files/e0y5w3igH87g9u7kFjsl)

### Step 4: Download Your Starter File

Your starter file is ready. You can:

* **Download** the file directly
* **Copy to clipboard** and paste into your editor

The generated file includes:

* GoFigr initialization with your chosen settings
* An example plot to verify everything works
* Optional data tracking and Clean Room examples

![Download or copy the generated starter file with a code preview](/files/15BfQ9uKRbHmHHBe0BzB)

## What the Starter File Does

When you run the generated file, every figure you create is automatically captured and synced to GoFigr — including the source code that produced it. This means:

* **Full provenance**: Each figure is linked to the exact code, data, and environment that created it
* **Automatic organization**: Figures are grouped into analyses named after your file
* **QR codes**: Each figure gets a unique QR code and revision ID for easy tracking
* **No manual publishing**: Figures appear in GoFigr as you create them

## Next Steps

* [Clean Room (Python)](/features/clean-room-python) / [Clean Room (R)](/features/clean-room-r) — Make your figures fully reproducible and re-runnable in the browser
* [AI Story Mode](/features/story-mode) — Turn your captured figures into presentations
* [Auto-Assign](/features/auto-assign) — Let AI organize figures into analyses automatically


# AI Story Mode

## Overview

Story Mode uses AI to turn a selection of figures into a presentation, report, or step-by-step tutorial. Pick the figures you want and GoFigr generates a coherent narrative—drafting the descriptions and structure for a meeting, publication, or teaching session.

**What makes Story Mode unique:**

Unlike simple image captioning tools, GoFigr's AI analyzes the **complete context** behind each figure—including the source code that generated it, associated metadata, variable names, data transformations, and analysis parameters. This deep understanding enables the AI to produce accurate, technically detailed descriptions that capture not just what a figure shows, but *how* and *why* it was created.

![Story Mode Title Slide - AI-generated presentation from TCGA-LUAD genomic analysis](/files/hA2f58rhzWn7PoajPCAe)

## Key Benefits for Users

### Save Hours of Work

* **Instant Presentations**: Convert a collection of figures into a professional presentation in minutes, not hours
* **Automated Content Generation**: AI writes slide descriptions, introductions, conclusions, and methods sections based on your actual figure content
* **One-Click Export**: Generate PowerPoint (.pptx), Word (.docx), or Markdown files ready for immediate use

### Maintain Scientific Rigor

* **Full Context Analysis**: AI doesn't just look at the image—it analyzes the complete source code, data transformations, variable names, and metadata that created each figure
* **Code-Aware Descriptions**: Method descriptions accurately reflect the actual algorithms, libraries, and parameters used in your analysis
* **Editable Results**: Every piece of AI-generated content can be edited inline, giving you full control over the final output
* **Proper Attribution**: Figures are linked back to their original sources with full provenance tracking

### Interactive Presentations

* **Ask Questions Live**: During presentations, click "Ask AI" on any figure to get instant answers about the underlying analysis
* **Access Source Code**: View the exact code that generated each figure directly from the presentation
* **Deep Dive on Demand**: Audience members can explore methodology, data sources, and implementation details without leaving the presentation

### Customizable for Any Audience

* **Three Story Types**: Choose Presentation (slide-based), Report (document format), or Tutorial (step-by-step educational content)
* **Target Audience Specification**: Tell the AI who you're presenting to (e.g., "general audience", "domain experts", "graduate students") and content is tailored accordingly
* **Extra Instructions**: Provide custom guidance like "focus on clinical implications" or "simplify technical language"

## How It Works

### Step 1: Select Your Figures

From any analysis in GoFigr, click "Create story..." to open the figure selection modal. Choose which figures to include in your story—all figures are selected by default, but you can customize your selection.

![Analysis view with figures and Create Story button](/files/F1S7tty5dmYo44gAZapf)

![Figure selection modal](/files/xmJ2DqCLYLzC6bTgveWU)

### Step 2: Configure Your Story

After selecting figures, you'll see the Story Configuration panel:

* **Story Type**: Choose between Presentation, Report, or Tutorial
* **Target Audience**: Describe who will be viewing/reading your content (e.g., "Researchers, Students, Stakeholders")
* **Linked Analysis**: Associate the story with an analysis for figure tracking
* **Extra Instructions**: Add any specific requirements or focus areas (under Advanced Settings)
* **AI Model Selection**: Choose from available AI models (powered by Amazon Bedrock Nova)

![Story Configuration panel](/files/efuOX5OOzUzeU5N712K6)

### Step 3: Generate Content

Click "Generate" and watch as the AI processes your figures. A progress bar shows real-time status:

![Generation progress showing AI processing](/files/RUlXjq1TSu6p7wqQthcK)

The AI performs deep analysis of each figure using **the complete context**, not just the image:

1. **Analyzes Full Context**: Examines not only the figure image, but also the **source code** that generated it, variable names, data transformations, library calls, and all associated metadata
2. **Creates Code-Informed Descriptions**: Generates accurate, technically detailed descriptions that reflect the actual analysis—including specific statistical tests, data processing steps, and visualization parameters
3. **Extracts Real Methods**: Identifies the exact algorithms, libraries (e.g., ggplot2, matplotlib, seaborn), and parameters used by reading the source code
4. **Detects Data Inputs/Outputs**: Identifies actual dataset names, file paths, and data transformations from the code
5. **Generates Overview Slides**: Creates title, introduction, goals, data description, methods summary, conclusions, and future work sections—all informed by the code context
6. **Refines for Consistency**: A final refinement pass ensures smooth transitions and eliminates redundancy across all slides

### Step 4: Review and Edit

Once generation completes, you'll see fully-formed slides with AI-generated content:

![Introduction slide with AI-generated content](/files/2JabxkSys0c5gjJbl9NT)

![Goals and Data slides](/files/WERQsaBvfASTmBhq84In)

**Editing Options:**

* **Inline Editing**: Click "Edit..." on any slide to modify content directly
* **Auto-Save**: Changes are automatically saved as you work
* **Reset**: Revert to AI-generated content if needed
* **Detail Level**: Use "Less Detail" or "More Detail" buttons to adjust content verbosity
* **Regenerate**: Re-run AI generation for individual slides

### Step 5: Figure Slides with AI Descriptions

Each figure in your story gets its own slide with intelligent AI-generated descriptions:

![Figure slide showing a data table with AI descriptions](/files/ESSYWzGJ4cJ4ntM5oxsy)

![Chart slide with bar graph and key takeaways](/files/Vz34ORExneASl4l4jFiy)

**Figure Slide Features:**

* **Figure Image**: High-quality rendering of your visualization
* **Revision Navigation**: Browse through different versions of the figure
* **Key Takeaways**: AI-generated bullet points explaining what the figure shows—informed by the actual source code
* **"Ask AI" Button**: **Ask questions directly from the presentation!** Get instant AI-powered answers about methodology, data sources, statistical tests, or any aspect of the figure. Perfect for Q\&A sessions during presentations.
* **"More" Button**: **Access the full context**—view the exact source code that generated the figure, inspect metadata, download data, or navigate to the original figure in GoFigr
* **Custom Prompt**: Provide specific instructions for regenerating the description (e.g., "explain for a non-technical audience" or "focus on the statistical significance")

### Step 6: Present or Export

When your story is ready, use the action buttons in the header:

![Header with Save, Export, Present, Share, Delete buttons](/files/hA2f58rhzWn7PoajPCAe)

![Export dropdown showing PowerPoint option](/files/BByck42peF5tWfowfl1u)

**Export Options:**

* **Export to PowerPoint (.pptx)**: Professional slides with theme selection and organization logo
* **Export to Word (.docx)**: Document format for detailed reports
* **Export to Markdown**: Plain text for maximum flexibility
* **Present**: Full-screen presentation mode with keyboard navigation
* **Share**: Generate shareable links for collaboration

## Story Slide Types

### Overview Slides

* **Title Slide**: AI-generated title based on your analysis content
* **Introduction**: Context and background for your analysis
* **Goals**: What the analysis aims to achieve
* **Data Description**: Overview of datasets used
* **Methods**: Summary of analytical approaches
* **Conclusions**: Key findings and insights
* **Future Work**: Next steps and open questions

### Figure Slides

* **Figure Image**: High-quality rendering of your visualization
* **Description**: AI-generated bullet points explaining what the figure shows
* **Methods Detail**: Specific methods used for that figure (optional)
* **Data Slide**: Input/output datasets for that figure (optional)

## Export Features

### PowerPoint Export

* **Theme Selection**: Choose from multiple professional themes (Elegant Light, Elegant Dark, Minimal, Bold, Nature, Ocean, etc.)
* **Organization Logo**: Your organization's logo appears on the title slide
* **GoFigr Branding**: "Made with GoFigr" badge with link to gofigr.io
* **Smart Formatting**: Bullet points, headers, and content automatically formatted for optimal readability
* **Adaptive Font Sizing**: Long content automatically uses smaller fonts to fit

### Word Export

* **Professional Document Structure**: Proper heading hierarchy and formatting
* **Embedded Figures**: High-resolution figure images embedded in the document
* **Ready for Publication**: Format suitable for lab meetings, grant applications, or journal supplements

### Markdown Export

* **Plain Text Flexibility**: Copy into any system that supports markdown
* **Version Control Friendly**: Easy to track changes in git
* **Documentation Ready**: Perfect for GitHub READMEs or documentation sites

## Interactive Q\&A: Ask Questions During Presentations

One of Story Mode's most powerful features is the ability to **ask questions and explore context directly from your presentation**. This transforms static presentations into interactive, exploratory experiences.

### How It Works

On every figure slide, you'll find two key buttons:

**🤖 Ask AI Button**

* Click to open a chat interface where you can ask any question about the figure
* The AI has access to the complete context: source code, data, metadata, and the figure itself
* Perfect for handling audience questions during presentations
* Example questions:
  * "What statistical test was used here?"
  * "Why did you choose this color scheme?"
  * "What would happen if we filtered out the outliers?"
  * "Can you explain this for a non-technical audience?"

**📋 More Button**

* **View Source Code**: See the exact Python/R code that generated the figure
* **Inspect Metadata**: View all associated metadata, parameters, and settings
* **View Full Revision**: Navigate to the complete figure page in GoFigr
* **Access Data**: Download or view the underlying datasets

### Benefits for Presenters

* **Never be caught off-guard**: AI can answer technical questions you might not remember off the top of your head
* **Go deeper on demand**: When an audience member wants more detail, you can explore the code together
* **Maintain credibility**: Show exactly how analyses were performed with full transparency
* **Save preparation time**: No need to memorize every implementation detail

### Benefits for Audiences

* **Explore at your own pace**: Click into figures that interest you most
* **Verify methodology**: See the actual code, not just a description
* **Ask follow-up questions**: Get instant AI-powered clarifications
* **Learn from real examples**: Understand both *what* was done and *how*

## How to Access Story Mode

### From an Analysis

1. Navigate to any analysis containing figures
2. Click "Create story..." in the toolbar (shown above the figure list)
3. Select which figures to include
4. Configure story settings and click "Generate"

### From the Home View

1. Click "Create Story" from the main navigation or home view
2. Select an analysis from your workspace
3. Choose figures from that analysis
4. Configure and generate

### From Existing Stories

1. Go to "Stories" in the left navigation
2. Open an existing story to continue editing
3. Or create a new story from scratch

## Figure Tracking

Link your story to an analysis for automatic updates when figures change:

* Enable "Automatically check for new figures" in Story Configuration
* When tracked figures are updated, your story can reflect the latest versions
* Maintain consistency between your analysis and presentations

## Technical Details

* **AI Engine**: Powered by Amazon Bedrock Nova v2 models
* **Multi-Modal Analysis**: AI processes both the figure image AND the complete source code context simultaneously
* **Code Understanding**: Parses Python, R, and other languages to extract methods, libraries, parameters, and data transformations
* **Structured Output**: Uses JSON schema parsing for consistent AI responses
* **Batched Generation**: Overview slides generated in a single optimized API call
* **Refinement Pass**: Final content polish for narrative flow and consistency
* **Deep Insight Integration**: Leverages GoFigr's figure analysis capabilities for intelligent Q\&A

## Best Practices

1. **Use Descriptive Figure Names**: Better figure names lead to better AI-generated titles
2. **Specify Your Audience**: A clear target audience description dramatically improves content quality
3. **Review and Refine**: AI-generated content is a starting point—add your expert knowledge through editing
4. **Use the Refine Button**: After initial generation, click "Refine" to improve flow and consistency across slides
5. **Adjust Detail Level**: Use "Less Detail" for executive summaries, "More Detail" for technical audiences
6. **Leverage Ask AI in Presentations**: Prepare for Q\&A by familiarizing yourself with the Ask AI feature—it can answer technical questions on the fly
7. **Show Your Code**: Don't be afraid to click "More" and show the source code during presentations—it builds trust and enables deeper discussions

## See It In Action

Try Story Mode yourself with the [TCGA-LUAD demo analysis](https://app.gofigr.io/story/6b9f8350-651e-4bdf-884e-1fcf2c8a3251/p) to see how GoFigr transforms genomic figures into compelling presentations.


# Comments & Collaboration

## Overview

Comments let your team discuss and review figures in place. Add a comment directly on a figure, mention colleagues, react with emojis, and receive email notifications—without leaving GoFigr. Use them for peer review, methodology discussions, or feedback on a visualization.

## Key Benefits for Users

### Streamlined Communication

* **Contextual Discussions**: Comments are attached directly to specific figures or assets, keeping discussions focused and organized
* **No More Email Chains**: Replace scattered email threads with persistent, searchable comment threads
* **Real-Time Collaboration**: Team members can see and respond to comments immediately

### Stay Informed Without Effort

* **Email Notifications**: Get notified when someone comments on your figures or replies to your comments
* **@Mention Alerts**: Tag colleagues with @username or @email to bring them into discussions with instant notifications
* **Activity Feed Integration**: See all comment activity in your workspace activity feed

### Rich Communication Tools

* **Markdown Support**: Format comments with bold, italic, code blocks, lists, and more
* **Emoji Reactions**: React to comments with 👍 👎 ❤️ 😂 🚀 and more
* **Threaded Replies**: Keep conversations organized with inline replies to specific comments
* **AI-Generated Comments**: Request AI assistance to generate analytical comments (with clear attribution)

## How It Works

### Adding Comments

#### On Figures

1. Navigate to any figure or figure revision
2. Scroll to the Comments section below the figure
3. Type your comment in the text box
4. Use markdown formatting for rich text (see formatting guide below)
5. Click "Post Comment" to submit

#### On Assets (Documents)

1. Open any asset or asset revision (notebooks, PDFs, etc.)
2. Find the Comments section
3. Add your comment with the same rich formatting options

### Formatting Your Comments

GoFigr comments support full GitHub-Flavored Markdown:

````markdown
**Bold text** for emphasis
*Italic text* for subtle emphasis
`inline code` for variable names or short code
```python
# Code blocks for longer snippets
def analyze_data(df):
    return df.describe()
```
- Bullet points for lists
1. Numbered lists for sequences
> Blockquotes for referencing others
````

### @Mentioning Colleagues

Bring specific people into a conversation:

* **By username**: Type `@johndoe` to mention a user by their username
* **By email**: Type `@jane.doe@university.edu` to mention by email address

When you mention someone:

1. They receive an email notification with a preview of your comment
2. The email includes a direct link to the comment
3. Their name is highlighted in the comment thread
4. They can click through to respond immediately

### Reacting to Comments

Express quick feedback without writing a full response:

| Reaction       | Meaning                        |
| -------------- | ------------------------------ |
| ✅ Verified     | Confirms accuracy or agreement |
| ❌ Incorrect    | Flags potential errors         |
| 👍 Thumbs Up   | General approval               |
| ❤️ Heart       | Appreciation                   |
| 😂 Laugh       | Humor or levity                |
| 👎 Thumbs Down | Disagreement                   |
| 🚀 Rocket      | Excitement or great work       |

Click any reaction to toggle it on/off. Multiple users can add the same reaction, and counts are displayed.

### Threaded Replies

Keep conversations organized:

1. Click "Reply" on any comment
2. Your response appears nested under the original
3. Both the original commenter and mentioned users are notified
4. Threads can go multiple levels deep for complex discussions

### Editing and Deleting

* **Edit Your Comments**: Click the edit icon on any comment you authored to make changes
* **Edited Indicator**: Edited comments show "(edited)" to maintain transparency
* **Delete Your Comments**: Remove comments you no longer want visible

## Email Notifications

### When You're Notified

1. **Someone comments on your figure**: The figure's author receives a notification
2. **Someone replies to your comment**: The original comment author is notified
3. **Someone @mentions you**: Direct mentions trigger immediate notifications

### What's in the Email

* **Commenter's Name**: Who posted the comment
* **Comment Preview**: First 200 characters of the comment content
* **Figure/Asset Context**: Name of the figure or asset being discussed
* **Workspace Context**: Which workspace the discussion is in
* **Direct Link**: Click through to jump directly to the comment

### Managing Notifications

Notifications are designed to be helpful, not overwhelming:

* You won't be notified about your own comments
* Duplicate notifications are prevented (if you're mentioned AND you're the author, you only get one email)
* Emails include unsubscribe options (coming soon)

## AI-Generated Comments

For AI-analyzed figures, you can request AI-generated analytical comments:

1. Click "Generate AI Comment" (available when viewing an AI analysis)
2. The AI generates a detailed analytical comment based on the figure
3. AI comments are clearly marked with an "AI Generated" badge
4. Other users can verify or flag AI comments using the reaction system

### AI Comment Verification

* **✅ Verified**: Click to indicate the AI comment is accurate
* **❌ Incorrect**: Click to flag potential inaccuracies (hides comment content with option to view)

## Activity Feed Integration

All comment activity appears in your workspace activity feed:

* **New Comments**: See when team members add comments
* **Replies**: Track conversation threads
* **Filter Options**: Focus on comment activity or view all workspace activity
* **Exclude Deleted**: Option to hide activity for deleted items

## How to Access

### Viewing Comments

* Navigate to any figure, figure revision, asset, or asset revision
* Scroll to the Comments section
* All existing comments are displayed with newest first

### Adding Comments

* Click in the comment text box
* Type your comment
* Click "Post Comment"

### From Notifications

* Click the link in your email notification
* You'll be taken directly to the specific comment with automatic scrolling

## Permission-Based Access

Comments respect GoFigr's permission system:

* You can only comment on figures/assets you have view access to
* Your comments are visible to anyone who can view the target object
* Workspace administrators can moderate all comments in their workspace

## Best Practices

1. **Be Specific**: Reference specific aspects of the figure in your comments
2. **Use Formatting**: Code blocks for code, bullet points for multiple points
3. **@Mention Sparingly**: Only tag people who need to see the comment
4. **React Before Replying**: A quick 👍 can be more efficient than "I agree"
5. **Keep Threads Focused**: Start new top-level comments for different topics
6. **Review AI Comments**: Always verify AI-generated content before acting on it

## Technical Details

* **Supported Targets**: Figures, Figure Revisions, Assets, Asset Revisions
* **Markdown Parser**: GitHub-Flavored Markdown (GFM) with syntax highlighting
* **Email Delivery**: Real-time email notifications via templated HTML emails
* **Data Model**: Comments use GenericForeignKey for extensibility to future entity types
* **Indexing**: Comments are indexed for fast retrieval and search


# Git Repository Import

## Overview

Git Repository Import connects your code repositories to your figure library. Import Jupyter notebooks directly from GitHub, GitLab, Bitbucket, or any Git-compatible host, and GoFigr extracts every figure from every commit—preserving your version history with full attribution.

## Key Benefits for Users

### Preserve Your Research History

* **Full Commit History**: Import figures from all commits, not just the latest version
* **Version Tracking**: Each notebook revision becomes a tracked figure revision in GoFigr
* **Git Metadata Preserved**: Branch names, commit hashes, and timestamps are stored with each import
* **Author Attribution**: Git commit authors are automatically mapped to GoFigr users

### Seamless Integration with Your Workflow

* **No Workflow Changes**: Keep using Git as you always have—GoFigr pulls from your repositories
* **Multiple Git Hosts**: Support for GitHub, GitLab, Bitbucket, and any standard Git server
* **Both HTTPS and SSH**: Use public repos via HTTPS or private repos with SSH key authentication
* **Branch Selection**: Choose exactly which branches to import

### Automatic Figure Extraction

* **Jupyter Notebook Processing**: All output cells with images are automatically extracted
* **Code Association**: Each figure is linked to the code cell that generated it
* **Source Document Linking**: Figures are connected back to their source notebook files
* **Intelligent Deduplication**: Identical figures are detected and not duplicated

## How It Works

### Step 1: Navigate to Import

1. Go to the Import page from the main navigation
2. Select your target workspace
3. Choose the "Git Repository" import option

### Step 2: Enter Repository URL

Enter your Git repository URL. GoFigr supports multiple formats:

**HTTPS URLs (public repositories)**:

* `https://github.com/username/repository.git`
* `https://gitlab.com/username/repository.git`
* `https://bitbucket.org/username/repository.git`

**SSH URLs (private repositories)**:

* `git@github.com:username/repository.git`
* `git@gitlab.com:username/repository.git`
* `git+ssh://git@github.com/username/repository.git`

### Step 3: Configure SSH Key (for Private Repos)

For SSH-based URLs to private repositories:

1. The SSH Key selector appears automatically
2. Choose an existing SSH key or add a new one
3. SSH keys are stored encrypted and used securely for authentication
4. Click "Manage SSH Keys" to add, view, or remove keys

**Note**: SSH keys are optional for HTTPS URLs to public repositories.

### Step 4: Select Branches

Once the repository is validated:

1. GoFigr automatically fetches available branches
2. Main/master branches are selected by default if present
3. Use the multi-select dropdown to add or remove branches
4. Each selected branch will be scanned for notebooks

### Step 5: Start Import

Click "Import from Git" to begin:

1. GoFigr clones the repository to a secure temporary location
2. All commits in selected branches are scanned for `.ipynb` files
3. Each notebook file at each commit is processed
4. Figures are extracted from cell outputs
5. Progress is displayed in real-time

### Step 6: Monitor Progress

The import progress modal shows:

* **Overall Progress**: Percentage complete across all files
* **Current File**: Which notebook is being processed
* **Branch Progress**: Which branch/commit is being scanned
* **Log Messages**: Detailed status updates
* **Cancel Option**: Stop the import at any time if needed

## What Gets Imported

### From Each Notebook

* **All Figure Outputs**: PNG, JPEG, SVG, and other image outputs from cells
* **Cell Code**: The code that generated each figure is preserved
* **Notebook Metadata**: Kernel info, notebook version, and custom metadata

### From Git History

* **Commit Timestamps**: Each figure revision uses the original Git commit time
* **Author Information**: Commit authors are mapped to GoFigr users by email
* **Branch Context**: Which branch each version came from
* **Commit Hash**: Links back to the exact commit for provenance

### How Figures Are Organized

1. **One Analysis per Notebook Path**: Notebooks with the same path share an analysis
2. **One Figure per Output**: Each distinct figure in the notebook becomes a GoFigr figure
3. **Revisions by Commit**: Different commits create different figure revisions
4. **Source Linking**: Each figure links back to its source notebook asset

## Author Attribution

GoFigr intelligently maps Git authors to GoFigr users:

### Automatic Matching

* Git commit author emails are matched against GoFigr user emails
* Matching requires the importing user to have a confirmed email address
* Matched figures show the original author in GoFigr

### When No Match Is Found

* The "on behalf of" field stores the Git author name and email
* Full attribution is preserved even without a GoFigr account
* Future users can claim their figures when they join

### Importing User as Fallback

* If author matching is disabled or fails, the importing user is credited
* The original Git author info is still stored in metadata

## Real-Time Progress Tracking

### Progress Indicators

* **File Count**: "Processing file 5 of 23"
* **Branch Progress**: "Scanning branch: feature/analysis"
* **Commit Info**: "Processing commit a1b2c3d..."
* **Detailed Logs**: Timestamped log messages for debugging

### Error Handling

* **Graceful Failures**: Individual file failures don't stop the entire import
* **Error Messages**: Clear descriptions of what went wrong
* **Partial Success**: Successfully imported figures are kept even if some fail
* **Retry Option**: Failed imports can be retried after fixing issues

### Cancellation

* Click "Cancel" at any time during import
* Already-imported figures are preserved
* The repository clone is cleaned up automatically

## Import History

Recent imports are tracked and displayed:

* **Repository URL**: Which repository was imported
* **Import Time**: When the import occurred
* **Status**: Success, partial, or failed
* **Figure Count**: How many figures were extracted

## Best Practices

### Before Importing

1. **Clean Up Notebooks**: Clear unnecessary output cells to reduce processing time
2. **Organize by Project**: One repository = one logical project for cleaner organization
3. **Tag Important Commits**: Consider which commits contain meaningful figure changes

### SSH Key Management

1. **Use Deploy Keys**: GitHub/GitLab deploy keys limit access to specific repositories
2. **Read-Only Access**: GoFigr only needs read access to clone
3. **Rotate Periodically**: Update SSH keys regularly for security
4. **One Key Per Repository**: Easier to manage and audit

### Branch Selection

1. **Start with Main Branch**: Begin with main/master for the primary history
2. **Add Feature Branches Selectively**: Only import branches with meaningful figures
3. **Consider Tag-Based Workflows**: Some teams may want to import only tagged releases

## Security & Privacy

### SSH Key Storage

* SSH keys are encrypted at rest
* Keys are only decrypted during clone operations
* Temporary key files are securely deleted after use
* Thread-safe handling prevents key leakage

### Repository Access

* GoFigr clones to isolated temporary directories
* Clone directories are deleted after processing
* No repository data is stored except extracted figures
* Network access is limited to the clone operation

### Duplicate Import Prevention

* GoFigr prevents simultaneous imports of the same repository
* Avoids race conditions and duplicate figures
* Clear error messages if import already in progress

## Technical Details

### Supported Notebook Versions

* Jupyter Notebook (.ipynb) format versions 4.x
* JupyterLab notebooks
* Google Colab exports

### Processing Architecture

* **Parallel File Processing**: Multiple notebooks processed simultaneously (configurable)
* **Sequential Commit Processing**: Commits for each file processed in chronological order
* **Thread-Safe SSH**: Isolated SSH key handling per operation
* **Automatic Cleanup**: Temporary files removed even on errors

### Git Operations

* Full repository clone (not shallow) for complete history
* Remote branch fetching when needed
* Git protocol support: HTTPS, SSH, git://

### Figure Extraction

* All image MIME types from display\_data outputs
* Execute\_result outputs with image data
* Embedded images in markdown cells (future enhancement)

## Troubleshooting

### "Failed to clone repository"

* Check the URL format is correct
* Verify SSH key has access (for private repos)
* Ensure the repository exists and is accessible

### "SSH key required"

* Non-HTTPS URLs require an SSH key
* Add a key via "Manage SSH Keys"
* Verify the key has read access to the repository

### "No notebooks found"

* Ensure the repository contains `.ipynb` files
* Check selected branches contain notebooks
* Verify notebooks are committed (not just in working directory)

### Import Taking Too Long

* Large repositories with many commits may take time
* Consider importing specific branches only
* Check network connectivity to the Git host


# Document Import

## Overview

Document Import extracts figures from existing PowerPoint presentations and Word documents and brings them into your figure library. GoFigr pulls out the images, generates titles with AI, and links each figure back to its source document—useful for organizing past presentations or tracking figures from collaborators' files.

## Key Benefits for Users

### Rescue Figures from the Slide Deck Graveyard

* **Extract All Images**: Every image in your PowerPoint or Word document is automatically extracted
* **Organize Legacy Content**: Transform scattered presentations into a searchable figure library
* **No Manual Work**: Skip the tedious process of right-click-save-as for every figure

### AI-Powered Intelligence

* **Smart Figure Titles**: AI analyzes slide content and context to generate meaningful figure names
* **Context-Aware Naming**: Titles reflect what's on the slide, not generic names like "image1.png"
* **OCR-Based Matching**: QR codes with GoFigr UUIDs are detected to match figures to existing tracked figures

### Full Document Provenance

* **Source Linking**: Every extracted figure links back to its source document
* **Slide/Page Context**: Know exactly which slide or page each figure came from
* **Duplicate Detection**: Identical figures are recognized and not duplicated in your library

## How It Works

### Importing PowerPoint Files (.pptx)

#### Step 1: Upload Your Presentation

1. Navigate to the Import page
2. Select your workspace
3. Drag and drop or browse to select your .pptx file
4. Click "Upload"

#### Step 2: Automatic Processing

GoFigr processes your presentation:

1. **Document Storage**: The full presentation is stored as an asset
2. **Slide Scanning**: Each slide is examined for images
3. **Image Extraction**: All images are extracted with metadata
4. **AI Title Generation**: Slide text is used to generate descriptive figure titles
5. **UUID Detection**: QR codes are scanned for GoFigr figure UUIDs
6. **Figure Creation**: Each image becomes a tracked figure in your library

#### Step 3: Review Results

* View the extracted figures in your workspace
* Each figure shows its source presentation
* Click through to the original slide location
* Edit titles if the AI suggestions need refinement

### Importing Word Documents (.docx)

#### Step 1: Upload Your Document

1. Navigate to the Import page
2. Select your workspace
3. Upload your .docx file
4. Processing begins automatically

#### Step 2: Automatic Processing

GoFigr processes your document:

1. **Document Storage**: The Word file is stored as an asset
2. **Structure Analysis**: Document hierarchy is traversed
3. **Image Extraction**: All embedded images are extracted
4. **Context Capture**: Surrounding text is used for AI title generation
5. **Figure Creation**: Each image becomes a tracked figure

#### Step 3: Review and Organize

* Extracted figures appear in your workspace
* Linked to the source Word document
* Organized by the analysis (named after the document)

## AI-Powered Title Generation

### How It Works

When AI title generation is enabled:

1. **Context Extraction**: For PowerPoint, slide title and body text are captured
2. **Semantic Analysis**: AI understands what the slide/page is about
3. **Relevant Naming**: Titles describe the figure's content, not generic identifiers
4. **Batch Processing**: Multiple titles generated efficiently in a single AI call

### Examples

| Generic Name       | AI-Generated Title                       |
| ------------------ | ---------------------------------------- |
| image1.png         | "Survival curves by treatment group"     |
| Picture 3          | "Gene expression heatmap - top 50 genes" |
| Slide4\_shape2.png | "ROC curve comparison - Model A vs B"    |

### When AI Naming Helps Most

* Presentations with descriptive slide titles
* Documents with figure captions
* Scientific figures with contextual text nearby

## QR Code and UUID Detection

### Automatic Figure Matching

If your figures contain GoFigr QR codes:

1. **QR Scanning**: Images are scanned for QR codes
2. **UUID Extraction**: GoFigr UUIDs are extracted from detected codes
3. **Revision Matching**: UUIDs are matched against existing figure revisions
4. **Deduplication**: Matched figures link to existing revisions instead of creating duplicates

### When This Helps

* Re-importing presentations that contain tracked GoFigr figures
* Maintaining a single source of truth for each figure
* Preserving figure history across document versions

## Source Document Linking

### Bidirectional Connections

Every imported figure maintains links to its source:

**From Figure View:**

* "Source: Q4\_Results.pptx" with clickable link
* "Slide 7, Shape 3" position metadata
* Direct navigation to the document

**From Document View:**

* List of all figures extracted from this document
* Thumbnails with links to full figure views
* Extraction status and metadata

### Document Preview

Imported documents are viewable within GoFigr:

* **PowerPoint Preview**: Navigate through slides
* **Word Preview**: Scroll through document content
* **Specialized Views**: Optimized rendering for each format

## Import Metadata

### What's Captured

For each imported figure:

| Metadata         | Description                     |
| ---------------- | ------------------------------- |
| Source Type      | "powerpoint" or "word"          |
| File Name        | Original document filename      |
| File Size        | Document size in bytes          |
| Slide/Page Index | Position in the document        |
| Shape Index      | Which shape on the slide (PPT)  |
| Surrounding Text | Text context used for AI naming |
| Import Timestamp | When the import occurred        |

### Using Metadata

* Search for figures by source document
* Filter by import date
* Track provenance for compliance

## Handling Duplicates

### Hash-Based Detection

GoFigr uses content hashing to detect duplicates:

1. Each image's content is hashed
2. Hash is compared against existing figures in the workspace
3. Matching hash = existing figure is reused
4. New hash = new figure revision created

### UUID-Based Detection

For GoFigr-watermarked figures:

1. QR codes are scanned for UUIDs
2. UUIDs identify specific figure revisions
3. Matching UUID links to existing revision
4. No duplicate figures created

### Benefits

* Clean, deduplicated figure library
* Single source of truth for each figure
* History preserved across imports

## How to Access

### Via the Import Page

1. Click "Import" in the main navigation
2. Select your workspace
3. Choose "Upload Files"
4. Drop your .pptx or .docx files
5. Monitor progress in the task modal

### Via Drag and Drop

1. Navigate to your workspace view
2. Drag documents directly onto the page
3. Import processing begins automatically

### Supported Formats

| Format            | Extension | Notes                   |
| ----------------- | --------- | ----------------------- |
| PowerPoint        | .pptx     | Modern XML format       |
| Word              | .docx     | Modern XML format       |
| Legacy PowerPoint | .ppt      | Limited support         |
| Legacy Word       | .doc      | Not currently supported |

## Best Practices

### Before Importing

1. **Use Modern Formats**: Convert .ppt to .pptx and .doc to .docx for best results
2. **Clean Up Decorative Images**: Remove logos, backgrounds, and non-figure images that you don't want tracked
3. **Add Descriptive Slide Titles**: Better slide titles = better AI-generated figure names

### After Importing

1. **Review AI Titles**: Check and edit any titles that need refinement
2. **Organize into Analyses**: Group related figures if they span multiple documents
3. **Set Up Tracking**: Enable figure tracking for ongoing updates

### For Large Presentations

1. **Import in Batches**: Break very large presentations into smaller files if needed
2. **Monitor Progress**: Use the task modal to track import status
3. **Check Results**: Review extracted figures for completeness

## Technical Details

### Image Extraction

* **PowerPoint**: Uses python-pptx to access slide shapes and embedded images
* **Word**: Uses python-docx to traverse document structure and extract images
* **Formats Supported**: PNG, JPEG, GIF, TIFF, BMP, WMF, EMF

### AI Integration

* Powered by Amazon Bedrock
* Uses slide/document context for intelligent naming
* Respects AI quotas and rate limits

### Storage

* Original documents stored as assets
* Extracted images stored as figure revisions
* Full provenance chain maintained

### Processing

* Asynchronous processing via task queue
* Progress tracking via WebSocket updates
* Error recovery for partial failures


# Enhanced Search

## Overview

Enhanced Search helps you find figures across your library by text, by image, or both. Search by what a figure contains, find visually similar figures, or narrow results to a specific project.

## Key Benefits for Users

### Find Figures Instantly

* **Text Search**: Search by figure names, descriptions, or any text content
* **Image Search**: Upload a reference image to find visually similar figures
* **Cross-Workspace**: Search across all workspaces you have access to
* **Real-Time Results**: Results appear as you type

### Discover Related Content

* **Visual Similarity**: Find figures that look alike even if named differently
* **Semantic Understanding**: Search understands meaning, not just exact matches
* **OCR-Indexed Content**: Text within figures is searchable via OCR

### Organized Results

* **Grouped by Source**: Results organized by analysis and workspace
* **Relevance Ranked**: Most relevant matches appear first
* **Figure Counts**: See how many figures match in each analysis

## Search Modes

### Text Search

Find figures by name, description, or content:

**What's Indexed:**

* Figure names and titles
* Figure descriptions
* Analysis names
* Workspace names
* Story titles and slide content
* Target audience descriptions
* Text extracted from figures via OCR

**Search Tips:**

* Use natural language: "survival curve by treatment"
* Include project names: "phase 2 trial volcano plot"
* Search for methods: "PCA dimensionality reduction"

### Image Search

Upload a reference image to find similar figures:

**How It Works:**

1. Click "Image Search" or use the upload button
2. Select or paste an image
3. GoFigr extracts visual features using deep learning
4. Finds figures with similar visual characteristics
5. Results ranked by visual similarity

**What's Compared:**

* Overall visual structure
* Color distributions
* Shape patterns
* Plot types and layouts

**Best For:**

* Finding figures you remember seeing but can't name
* Locating all variations of a plot style
* Identifying duplicates across projects
* Finding inspiration from similar visualizations

### Combined Search

Use both text and image together:

1. Enter search text to narrow by topic
2. Upload a reference image to match visually
3. Results satisfy both criteria

## Search Interface

### Search Bar

* Located in the top navigation
* Type to begin searching
* Press Enter or click search icon
* Results appear in a dedicated view

### Search Results Page

**Result Cards Show:**

* Figure thumbnail
* Figure name
* Analysis/workspace context
* Relevance score indicator
* Quick actions (view, download)

**Grouping Options:**

* By Analysis: See all matching figures per analysis
* By Workspace: Group across projects
* Flat List: All results ungrouped

**Filtering Options:**

* Workspace filter: Limit to specific workspace
* Date range: Find recent or historical figures
* Figure type: Filter by image format

## Story Search

Stories are fully searchable:

**Indexed Content:**

* Story title
* Slide content (all text)
* Target audience description
* Figure descriptions within stories

**Find Stories By:**

* Topic: "survival analysis presentation"
* Audience: "for clinical investigators"
* Content: Any text from any slide

## QR Code Detection

Image search includes QR code detection:

1. Upload an image containing a GoFigr QR code
2. QR code is detected and decoded
3. UUID extracted identifies the exact figure revision
4. Direct link to the original tracked figure

**Perfect For:**

* Tracing figures back from printed materials
* Verifying figure provenance
* Linking external references to source data

## How to Access

### Quick Search

1. Press `/` or click the search icon
2. Start typing your query
3. Press Enter to see full results

### From Home View

1. Use the search bar on the home page
2. Enter text or click image search
3. Results display inline or in full page

### From Any Page

1. Search bar is always available in navigation
2. Search context follows your current workspace
3. Results open in dedicated search view

## Search Results

### Result Information

Each result shows:

* **Thumbnail**: Visual preview
* **Name**: Figure or story title
* **Context**: Analysis → Workspace breadcrumb
* **Score**: Relevance indicator
* **Date**: Last modified timestamp

### Actions on Results

* **Click**: Open the figure/story
* **Preview**: Quick view without navigating
* **Compare**: Select multiple to compare
* **Download**: Save the figure directly

### No Results?

If your search returns nothing:

* Try broader terms
* Check spelling
* Use image search if you have a reference
* Verify workspace permissions

## Technical Details

### Search Engine

GoFigr uses OpenSearch (Elasticsearch-compatible) for:

* Full-text search with relevance ranking
* Vector similarity search for images
* Faceted filtering
* Real-time indexing

### Image Features

Visual search uses:

* VGG19-based feature extraction
* 4,096-dimensional feature vectors
* Cosine similarity matching
* Normalized for consistent scoring

### Text Analysis

Text search includes:

* Tokenization and stemming
* Fuzzy matching for typos
* Phrase matching for exact terms
* Field boosting (names weighted higher)

### OCR Integration

Text extraction from figures:

* Tesseract OCR for text detection
* Indexed alongside other metadata
* Searchable even if figure lacks description

### Indexing

Content is indexed automatically when:

* Figures are created or updated
* Stories are saved
* Documents are imported
* Metadata is edited

### Performance

* Sub-second search response times
* Scales to millions of figures
* Cached results for repeated queries
* Background indexing doesn't affect UX

## Best Practices

### For Better Search Results

1. **Name Figures Descriptively**: Better names = better search
2. **Add Descriptions**: Rich descriptions improve discoverability
3. **Use Consistent Terminology**: Standard terms help team searches
4. **Tag with Keywords**: Include relevant keywords in descriptions

### For Image Search

1. **Use Clear Reference Images**: Higher quality = better matches
2. **Crop to the Figure**: Remove surrounding content
3. **Match the Style**: Similar plot types find better matches

### Organizing for Search

1. **Consistent Naming Conventions**: Team-wide standards help everyone
2. **Analysis Organization**: Group related figures for contextual results
3. **Regular Cleanup**: Remove or archive outdated figures


# Workspaces

## Overview

Workspaces organize your figures by project, team, or client. Create a workspace, switch between projects instantly, and GoFigr remembers where you left off.

## Key Benefits for Users

### Faster Navigation

* **Persistent Workspace Selection**: GoFigr remembers your last workspace across sessions
* **Quick Switching**: Change workspaces with a single click from the navigation bar
* **URL-Based Context**: Share links that automatically open in the right workspace

### Streamlined Organization

* **Create Workspaces Anywhere**: Add new workspaces directly from the workspace dropdown
* **Organization Branding**: See your organization's logo in the workspace selector
* **Direct Management Access**: Jump to workspace settings from the dropdown menu

### Better Team Collaboration

* **Organization Logos**: Visual identification of which organization owns each workspace
* **Workspace Overview**: Aggregated counts show activity at a glance
* **Consistent Context**: Everyone sees the same workspace structure

## Features

### Workspace Selector in Navigation

The new workspace selector sits prominently in the navigation bar:

**Display Elements:**

* Current workspace name
* Organization logo (if applicable)
* Grid icon for visual identification
* Dropdown indicator

**Dropdown Options:**

* List of all accessible workspaces
* Active workspace highlighted
* "Manage" link to workspace settings
* "Create new workspace" option

### Persistent Workspace Memory

GoFigr remembers your workspace preference:

**How It Works:**

1. When you select a workspace, it's saved to local storage
2. On your next visit, GoFigr automatically selects that workspace
3. URL parameters override stored preference when provided
4. Workspace context follows you across page navigations

**Priority Order:**

1. URL parameter `?workspace=xxx` (highest)
2. Local storage saved preference
3. First available workspace (fallback)

### Create Workspace from Dropdown

Add new workspaces without navigating away:

1. Click the workspace dropdown
2. Select "Create new workspace"
3. Enter a workspace name
4. Optionally select an organization
5. Click "Create"
6. New workspace is immediately selected

**Organization Selection:**

* If you belong to multiple organizations, choose which one owns the new workspace
* Organization determines logo, billing, and team access
* Personal workspaces have no organization

### Workspace Overview API

Get a quick summary of workspace contents:

**Aggregated Counts:**

* Total analyses
* Total figures
* Total assets
* Total stories
* Recent activity count

**Use Cases:**

* Dashboard widgets
* Activity monitoring
* Workspace selection previews

## How to Access

### From Navigation Bar

1. Look for the workspace selector (labeled "Workspace:")
2. Click to open the dropdown
3. Select a workspace to switch
4. Or click "Create new workspace" to add one

### From URL

Navigate directly with workspace parameter:

```
https://app.gofigr.io/home?workspace=ws_abc123
```

Links containing workspace IDs automatically set context.

### From Workspace Settings

1. Click the workspace name in dropdown
2. Select "Manage \[workspace name]"
3. Opens workspace settings page
4. Configure members, permissions, and details

## Organization Logos

### Where Logos Appear

Organization logos display in:

* Workspace selector dropdown
* Workspace header on main views
* Story exports (PowerPoint/Word)
* Shared figure views

### Logo Requirements

* Supported formats: PNG, JPEG, SVG
* Recommended size: 200x200 pixels
* Background: Transparent preferred

### Setting Up Logos

Organization administrators can:

1. Go to Organization Settings
2. Upload logo image
3. Logo automatically appears across the platform

## Workspace Switching

### Quick Switch

1. Click workspace name in navigation
2. Click any workspace in the list
3. Page reloads with new workspace context
4. All views update to show new workspace content

### What Changes

When you switch workspaces:

* Analysis list updates
* Figure library updates
* Activity feed shows new workspace
* URL updates with workspace parameter
* Recent items clear and reload

### What Persists

Across workspace switches:

* User preferences
* Search history (global)
* Navigation state

## URL Parameters

### Workspace in URLs

GoFigr uses URL parameters for workspace context:

**Pattern:**

```
https://app.gofigr.io/[view]?workspace=[workspace_id]
```

**Examples:**

* `/home?workspace=ws_abc123` - Home with specific workspace
* `/import?workspace=ws_abc123` - Import into specific workspace
* `/story/story_id?workspace=ws_abc123` - Story with workspace context

### Sharing Links

When sharing links:

* Workspace parameter is included
* Recipients see content in correct context
* Works even if recipient has different default workspace

## Best Practices

### Workspace Organization

1. **One Project Per Workspace**: Keep related figures together
2. **Meaningful Names**: Use descriptive names that identify the project
3. **Organization Assignment**: Use organizations for team workspaces
4. **Archive Old Workspaces**: Remove or archive completed projects

### For Teams

1. **Consistent Naming Conventions**: Team-wide standards help navigation
2. **Shared Workspaces**: Use organization workspaces for collaboration
3. **Permission Management**: Control access through workspace settings
4. **Document Purpose**: Add workspace descriptions for clarity

### Switching Contexts

1. **Bookmark Important Workspaces**: Save direct links with workspace params
2. **Use Keyboard Shortcuts**: Quick access to workspace switcher
3. **Check Context Before Actions**: Verify correct workspace for uploads

## Technical Details

### Storage

**Local Storage Key:** `gofigr_selected_workspace`

* Stores workspace API ID
* Persists across sessions
* Cleared on logout (optional)

### API Endpoints

**Workspace Overview:**

```
GET /api/v4/workspace/{id}/overview/
```

Returns aggregated counts and metadata.

**Create Workspace:**

```
POST /api/v4/workspace/
{
  "name": "New Project",
  "organization": "org_id" // optional
}
```

### URL Routing

* Workspace parameter read on page load
* Stored preference used as fallback
* Context propagates to child views
* API calls include workspace filter

### Performance

* Workspace list cached locally
* Overview counts cached with short TTL
* Lazy loading for workspace details
* Optimistic UI updates

## Troubleshooting

### Wrong Workspace Selected

If you're seeing the wrong workspace:

1. Check URL for workspace parameter
2. Clear local storage and refresh
3. Verify workspace access permissions
4. Try selecting workspace manually

### Can't Create Workspace

If workspace creation fails:

1. Check organization membership
2. Verify account permissions
3. Ensure workspace name is unique
4. Check network connectivity

### Logo Not Showing

If organization logo doesn't appear:

1. Verify logo is uploaded in org settings
2. Check image format and size
3. Clear browser cache
4. Verify organization assignment


# Document Assistant

## Overview

The Document Assistant controls how figures relate to their source documents. Unlink a figure from a document while choosing what to keep—the figure in your library, the imported copy, or neither—with visual previews so you can see what each option affects before you confirm.

## Key Benefits for Users

### Clean Up Your Library

* **Remove Unwanted Links**: Disconnect figures from documents without losing the figures
* **Delete Import Artifacts**: Remove figure revisions created during imports you want to undo
* **Maintain Organization**: Keep your figure library clean and well-organized

### Safe Operations

* **Visual Previews**: See exactly which figure you're about to unlink
* **Confirmation for Tracked Figures**: Extra confirmation prevents accidental deletion of important figures
* **Clear Options**: Understand exactly what will happen before you act

### Flexible Control

* **Unlink Only**: Keep the figure, just remove the document connection
* **Unlink and Delete**: Remove both the link and the figure revision
* **Per-Revision Control**: Act on specific revisions without affecting others

## Features

### Unlink Figures from Documents

When viewing a document (PowerPoint, Word, or Jupyter notebook), you can now unlink associated figures:

**The Unlink Button:**

* Located on each linked figure card in document view
* Red "Unlink" button with X icon
* Click to open the unlink modal

**What Unlinking Does:**

* Removes the connection between the figure and the document
* Figure remains in your library (unless you choose to delete)
* Document is unchanged
* Other figures remain linked

### Unlink Options

The unlink modal provides clear choices:

#### Option 1: Unlink Only (Default)

* Removes the figure-document association
* Figure revision remains in your library
* Safe for figures you want to keep
* No confirmation required

#### Option 2: Unlink and Delete Revision

* Removes the association AND deletes the figure revision
* For cleaning up unwanted imports
* Requires confirmation for tracked (non-imported) figures
* Imported figures can be deleted without extra confirmation

### Visual Preview

Before unlinking, you see:

**Figure Preview:**

* Thumbnail image of the figure
* Figure name
* Revision number
* Clickable link to the full figure view

**Context Information:**

* Which document the figure is linked to
* The anchor/location in the document
* Whether the figure is imported or tracked

### Confirmation for Tracked Figures

When deleting a tracked (non-imported) figure revision:

**Extra Safeguard:**

* Warning message explains the figure was created in your analysis (not imported)
* Text input requiring you to type "confirm delete"
* Prevents accidental deletion of original work
* Imported figures bypass this (easier to re-import if needed)

### Document View Integration

The unlink feature is seamlessly integrated into document views:

**From PowerPoint/Word View:**

* Open any imported document
* See list of extracted figures
* Each figure has an unlink button
* Actions apply per-figure

**From Notebook View:**

* Open any imported Jupyter notebook
* View code cells with their output figures
* Unlink individual figures while keeping the notebook

## How to Access

### From Document (Asset) View

1. Navigate to any asset (document) in your workspace
2. Click to view the asset revision
3. Scroll to see linked figures
4. Click "Unlink" on any figure card
5. Choose your options in the modal
6. Confirm the action

### From Document Tab

1. Open a figure that was imported from a document
2. Go to the "Source" or "Documents" tab
3. See all documents linked to this figure
4. Manage links from this view (reverse direction)

## Use Cases

### Cleaning Up Test Imports

After testing the import feature:

1. View the test document
2. Unlink all figures with "delete revision" checked
3. Figures are removed from your library
4. Document asset can also be deleted separately

### Removing Incorrect Links

If a figure was incorrectly linked:

1. Find the figure in the document view
2. Click Unlink
3. Keep "delete revision" unchecked
4. Link is removed, figure stays in library

### Reorganizing After Import

After importing, you may want different organization:

1. Unlink figures from original document
2. Move figures to a different analysis
3. Re-link to different documents if needed

### Undoing Accidental Imports

If you imported the wrong document:

1. Open the imported document
2. Unlink all figures (with delete)
3. Delete the document asset
4. Your library returns to its previous state

## Safety Features

### Imported vs. Tracked Figures

GoFigr distinguishes between:

**Imported Figures:**

* Created automatically during document import
* Marked with `is_imported = true`
* Can be deleted without extra confirmation
* Easy to re-import if needed

**Tracked Figures:**

* Created through the Python/R API or manual creation
* Represent original work
* Require extra confirmation to delete
* "Confirm delete" text input required

### What Confirmation Looks Like

For tracked figures:

```
This figure revision was created in your analysis (not imported).
Deleting it will permanently remove this revision.

To confirm deletion, please type "confirm delete" below:

[___________________]

[Cancel]  [Delete]
```

### Prevention of Mistakes

* Modal requires explicit action
* Checkbox for deletion is unchecked by default
* Visual preview shows exactly what's affected
* Link to view full figure before deciding

## Technical Details

### API Endpoint

```
POST /api/v4/asset_revision/{id}/unlink_figure/
{
  "figure_revision": "revision_id",
  "anchor": "optional_anchor",
  "delete_figure_revision": false
}
```

**Parameters:**

* `figure_revision`: API ID of the figure revision to unlink
* `anchor`: Optional anchor/position reference (for multi-position links)
* `delete_figure_revision`: Whether to delete the revision after unlinking

### Data Model

Links are stored in `AssetLinkedToFigure` model:

* `asset_revision`: The document revision
* `figure_revision`: The linked figure revision
* `use_type`: How the figure is used (e.g., "source")
* `anchor`: Position within the document

### Cache Invalidation

After unlinking:

* Asset revision cache is invalidated
* Linked figures list refreshes automatically
* UI updates without page reload

## Best Practices

### Before Unlinking

1. **Check the Preview**: Verify you're unlinking the right figure
2. **Consider Keeping**: Usually unlink-only is safer than delete
3. **Think About Re-Import**: Can you easily get this figure back?

### For Cleanup Operations

1. **Work Document by Document**: Clean up one import at a time
2. **Verify Before Bulk Actions**: Double-check before mass unlinking
3. **Keep Backups**: Important figures should have copies elsewhere

### For Organization

1. **Unlink Before Moving**: Clean up links before reorganizing
2. **Document Your Changes**: Note what you've unlinked for team awareness
3. **Regular Maintenance**: Periodically review and clean up links

## Related Features

### Document Preview

* View imported documents within GoFigr
* Navigate through slides/pages
* See which figures came from which locations

### Source Tracking

* Every figure knows its source document
* Bidirectional navigation between figures and documents
* Full provenance chain maintained

### Figure QC (Quality Control)

* Badges show figure import status
* Warning alerts for import-related issues
* Reproducibility tracking for non-imported figures


# Clean Room (Python)

> Working in R instead? See [Clean Room (R)](/features/clean-room-r).

## Overview

Clean Room turns a Python function into a self-contained, reproducible, interactive application. You decorate a function with `@reproducible`, and GoFigr draws a clean boundary around it: the function can only access the variables you pass in, the packages you declare, and nothing else. When the function produces a figure, everything—code, data, parameters, environment—is captured and published automatically.

The workflow:

1. **Explore** — work however you normally work in Jupyter
2. **Distill** — pull the core logic into a `@reproducible` function
3. **Run** — call the function; GoFigr captures everything automatically
4. **Share** — send a link; stakeholders interact with the figure in the browser

## Quick Start

Load the GoFigr extension and configure it:

```python
%load_ext gofigr
# configure() # only needed if overriding defaults
```

The `%load_ext gofigr` magic injects `reproducible`, `SliderParam`, `DropdownParam`, and other names into your notebook namespace.

### Simplest Example

```python
import seaborn as sns
penguins = sns.load_dataset("penguins")

@reproducible
def flipper_histogram(data, bins: int = 20):
    sns.histplot(data=data, x='flipper_length_mm', bins=bins)

flipper_histogram(penguins)
```

That's it. When the function runs, GoFigr captures:

* **Source code** — the function body, extracted from the notebook cell
* **Parameters** — types, defaults, and values for every argument
* **Data** — DataFrames passed as arguments, serialized alongside the revision
* **Environment** — package names and versions, Python version
* **Output** — the figures produced by the run

## Interactive Mode

Add `interactive=True` to render parameter widgets directly in Jupyter. Changing a widget re-runs the function immediately.

Requires the `anywidget` package:

```bash
pip install anywidget
```

**JupyterLab** — Restart JupyterLab (not just the kernel) and hard-reload the browser so the frontend extension registers.

**Classic Notebook (Jupyter Notebook <7)** — You must also enable the nbextension manually:

```bash
jupyter nbextension install --py anywidget --sys-prefix
jupyter nbextension enable --py anywidget --sys-prefix
```

Then restart the notebook server and refresh the browser.

If widgets still don't render in either environment, run the built-in health check:

```python
from gofigr.reproducible import check_anywidget_health
check_anywidget_health()
```

This verifies the Python package, traitlets, the Jupyter kernel, and prints guidance for fixing the frontend extension.

### Full Example

```python
from typing import Literal
import seaborn as sns

penguins = sns.load_dataset("penguins")

@reproducible(interactive=True)
def flipper_length_distribution(
    data,
    bins: int     = SliderParam(20, min=5, max=100, step=5),
    alpha: float  = SliderParam(0.7, min=0.1, max=1.0, step=0.05),
    show_kde: Literal["yes", "no", "auto"] = "yes",
    species: str  = DropdownParam("Adelie", choices=["Adelie", "Chinstrap", "Gentoo"]),
    show_grid: bool = True,
    title: str    = "Flipper Length Distribution"
):
    filtered = data[data['species'] == species]
    kde = True if show_kde == "yes" else (False if show_kde == "no" else None)

    ax = sns.histplot(
        data=filtered,
        x='flipper_length_mm',
        bins=bins,
        alpha=alpha,
        kde=kde,
    )
    ax.set_title(title)
    if show_grid:
        ax.grid(True, alpha=0.3)

flipper_length_distribution(penguins)
```

In Jupyter, this renders sliders, dropdowns, a checkbox, and a text input above the figure. Adjusting any control re-executes the function and updates the plot.

## Parameter Widgets

GoFigr maps Python types to interactive controls. You can rely on auto-inference or use explicit `Param` classes for more control.

### Auto-Inference

| Default value type       | Widget             | Example                         |
| ------------------------ | ------------------ | ------------------------------- |
| `int` or `float`         | Slider             | `bins: int = 20`                |
| `bool`                   | Checkbox           | `show_grid: bool = True`        |
| `str`                    | Text input         | `title: str = "My Chart"`       |
| `Literal[...]` type hint | Dropdown           | `mode: Literal["a", "b"] = "a"` |
| `pd.DataFrame`           | Static (read-only) | Passed at call time             |

### SliderParam

Numeric slider with explicit bounds.

```python
bins: int = SliderParam(20, min=5, max=100, step=5)
alpha: float = SliderParam(0.7, min=0.1, max=1.0, step=0.05)
```

If you omit bounds, they are resolved automatically:

|          | `int`                 | `float`               |
| -------- | --------------------- | --------------------- |
| **min**  | 0                     | 0                     |
| **max**  | `max(value * 2, 100)` | `max(value * 2, 1.0)` |
| **step** | 1                     | 0.1                   |

### DropdownParam

Categorical dropdown with explicit choices.

```python
species: str = DropdownParam("Adelie", choices=["Adelie", "Chinstrap", "Gentoo"])
```

You can also use a `Literal` type hint to create a dropdown automatically without `DropdownParam`:

```python
from typing import Literal

show_kde: Literal["yes", "no", "auto"] = "yes"
```

### CheckboxParam

Boolean toggle. Auto-inferred for `bool` defaults—you rarely need this explicitly.

```python
show_grid: bool = True  # auto-inferred as checkbox
```

### TextParam

Free-form text input. Auto-inferred for `str` defaults.

```python
title: str = "Flipper Length Distribution"  # auto-inferred as text input
```

### StaticParam

For DataFrames and other complex objects. Read-only in interactive mode—no widget is rendered. The value is available in the Clean Room studio for inspection.

```python
data = penguins  # DataFrame passed at call time → StaticParam
```

## Custom Packages

By default, the clean room environment includes:

| Alias | Package             |
| ----- | ------------------- |
| `pd`  | `pandas`            |
| `np`  | `numpy`             |
| `plt` | `matplotlib.pyplot` |
| `sns` | `seaborn`           |

### Adding Packages

Use the `packages` argument to add more. By default, your packages are merged with the defaults:

```python
@reproducible(packages={"gg": "plotnine"})
def penguin_plot(data, bins: int = 20):
    from plotnine import ggplot, aes, geom_histogram
    plot = (ggplot(data, aes(x='flipper_length_mm'))
            + geom_histogram(bins=bins))
    display(plot)
```

### Replacing Default Packages

Set `merge_packages=False` to use only the packages you specify:

```python
@reproducible(packages={"pd": "pandas", "gg": "plotnine"}, merge_packages=False)
def my_plot(data):
    ...
```

### Global Package Configuration

To change defaults for all `@reproducible` functions in a session:

```python
from gofigr.reproducible import set_default_packages, reset_default_packages

# Add plotnine to defaults
set_default_packages({"gg": "plotnine"})

# Replace defaults entirely
set_default_packages({"pd": "pandas", "gg": "plotnine"}, merge=False)

# Reset to built-in defaults
reset_default_packages()
```

## Publishing

### Automatic Capture (Jupyter)

With `configure(auto_publish=True)` (the default), figures are published automatically when a `@reproducible` function runs. No extra code needed.

### Explicit Publishing

For more control, use the `publisher` argument and call `publish()` inside the function:

```python
from gofigr.publisher import Publisher

pub = Publisher(workspace="Analytics", analysis="Penguins")

@reproducible(publisher=pub)
def flipper_histogram(data, bins: int = 20):
    sns.histplot(data=data, x='flipper_length_mm', bins=bins)
    publish(plt.gcf(), target="Flipper Histogram")

flipper_histogram(penguins)
```

The `publish()` function is injected into the clean room globals automatically. If a `publisher` is provided, it is used; otherwise the active GoFigr Jupyter extension's publisher is used. In scripts (no Jupyter extension), pass `publisher=` explicitly.

### What Gets Stored

Each published revision includes:

* **Source code** — the function body
* **Manifest** — JSON with parameter types, widget config, imports, and package versions
* **DataFrame parameters** — serialized as Parquet
* **Revision flag** — marks the revision as a Clean Room revision

## Nested Calls

Each `@reproducible` call gets its own context. If you nest `@reproducible` functions, the innermost context wins for `publish()`. The context is reset after the function returns or raises an exception.

## Edge Cases and Caveats

**Clean room isolation** — The function cannot access module-level variables from your notebook. Only declared packages, builtins, and function arguments are available. This is by design: it ensures the function is self-contained and reproducible.

**DataFrames are copied** — DataFrame arguments are round-tripped through Parquet serialization. The function receives a deserialized copy, not the original object. This ensures the clean room version matches what gets stored.

**100 MB limit** — Total DataFrame size (estimated via `memory_usage(deep=True)`) must be under 100 MB. If exceeded, a warning is issued and clean room is skipped—the function still runs normally but without isolation or capture.

**Unsupported parameter types** — Custom objects, numpy arrays, and lambdas cannot be serialized. If detected, a warning is issued and the function falls back to direct execution (no clean room).

**`interactive=True` outside Jupyter** — A warning is issued and the function runs non-interactively.

**`plt.show()` auto-called** — If matplotlib figures exist after execution, `plt.show()` is called automatically. You don't need to call it yourself.

**Source code extraction** — The function must be defined in importable source (a notebook cell or `.py` file). Dynamically created functions (e.g., via `exec`) are not supported.

**Return values** — In non-interactive mode, the function's return value is returned normally. In interactive mode, the return value is `None` (the output is rendered in the widget).

## Usage in Scripts

Clean Room works outside Jupyter with a few differences:

* **No interactive mode** — `interactive=True` is ignored (with a warning)
* **No automatic capture** — you must use the `publisher` argument explicitly
* **Explicit imports** — import from `gofigr.reproducible` instead of relying on the `%load_ext` magic

```python
from gofigr.publisher import Publisher
from gofigr.reproducible import reproducible, SliderParam
import seaborn as sns

penguins = sns.load_dataset("penguins")
pub = Publisher(workspace="Analytics", analysis="Penguins")

@reproducible(publisher=pub)
def flipper_histogram(data, bins: int = SliderParam(20, min=5, max=100, step=5)):
    sns.histplot(data=data, x='flipper_length_mm', bins=bins)
    publish(plt.gcf(), target="Flipper Histogram")

flipper_histogram(penguins)
```


# Clean Room (R)

> Working in Python instead? See [Clean Room (Python)](/features/clean-room-python).

## Overview

Clean Room turns an R function into a self-contained, reproducible, interactive application. You wrap a function with `reproducible()`, and GoFigr draws a clean boundary around it: the function can only access the parameters you declare, the packages you list, and `publish()`. When the function produces a figure, everything—source, data, parameters, environment—is captured and published automatically.

The workflow:

1. **Explore** — work however you normally work in RStudio or R Markdown
2. **Distill** — pull the core logic into a function with declared parameters
3. **Run** — wrap it in `reproducible()`; GoFigr captures everything
4. **Share** — send a link; stakeholders interact with the figure in the browser

## Quick Start

Load the package and configure it:

```r
library(gofigR)
library(ggplot2)

gofigR::enable(workspace_name = "Analytics",
               analysis_name  = "Penguins")
```

### Simplest Example

```r
reproducible(
  function(data = static(iris)) {
    p <- ggplot(data, aes(x = Sepal.Length, y = Petal.Length, color = Species)) +
      geom_point()
    publish(p, figure_name = "Iris Scatter")
  },
  packages = c("ggplot2")
)
```

That's it. When the function runs, GoFigr captures:

* **Source code** — the function body, extracted via R's AST
* **Parameters** — types, defaults, and widget metadata for every argument
* **Data** — data frames passed via `static()`, serialized as Parquet
* **Environment** — package names and versions, R version
* **Output** — the figures published by the run

## Interactive Mode

Add `interactive = TRUE` to launch a Shiny gadget with parameter widgets. Adjusting any control re-runs the function and updates the plot. Click **Publish** to push a revision with the current parameter values.

```r
reproducible(
  function(
      point_size = slider(2, min = 0.5, max = 5, step = 0.5),
      alpha      = slider(0.7, min = 0.1, max = 1.0, step = 0.1),
      color_by   = dropdown("Species",
                            choices = c("Species", "Sepal.Width", "Petal.Width")),
      show_smooth = checkbox(FALSE),
      data        = static(iris)
    ) {
      p <- ggplot(data, aes(x = Sepal.Length, y = Petal.Length,
                            color = .data[[color_by]])) +
        geom_point(size = point_size, alpha = alpha) +
        theme_minimal()

      if (show_smooth) {
        p <- p + geom_smooth(method = "lm", se = FALSE)
      }

      publish(p, figure_name = "Iris Scatter")
    },
    packages = c("ggplot2"),
    interactive = TRUE
)
```

Interactive mode requires a live, interactive R session. Inside `knitr`/R Markdown rendering or non-interactive scripts the gadget is skipped automatically and the function runs once with its default parameter values.

### Choosing the viewer

By default the gadget opens in the RStudio Viewer pane (when RStudio is available) or in a dialog window. Override with the `viewer` argument:

```r
# RStudio Viewer pane
reproducible(fn, packages = "ggplot2", interactive = TRUE,
             viewer = shiny::paneViewer())

# External browser
reproducible(fn, packages = "ggplot2", interactive = TRUE,
             viewer = shiny::browserViewer())

# Modal dialog
reproducible(fn, packages = "ggplot2", interactive = TRUE,
             viewer = shiny::dialogViewer("Clean Room", width = 1100, height = 700))
```

## Parameter Widgets

Parameters are declared as **function defaults** using one of the parameter constructors below. Plain defaults (e.g. `bins = 20`) are accepted and treated as static values, but for interactive mode you'll want explicit widget constructors.

### `slider()`

Numeric slider for `int` and `numeric` values.

```r
bins  = slider(20L, min = 5L,  max = 50L,  step = 5L)    # integer
alpha = slider(0.7, min = 0.1, max = 1.0,  step = 0.05)  # numeric
```

Pass an integer literal (`20L`) to get an integer slider; the gadget preserves integer type when feeding the value back into your function.

### `dropdown()`

Categorical dropdown with explicit choices.

```r
species = dropdown("Adelie", choices = c("Adelie", "Chinstrap", "Gentoo"))
```

### `checkbox()`

Boolean toggle.

```r
show_grid = checkbox(TRUE)
```

### `text_input()`

Free-form text input.

```r
title = text_input("Flipper Length Distribution")
```

### `static()`

Read-only value — no widget is rendered. Use this for data frames and any other value you want captured but not edited interactively. Plain defaults are wrapped in `static()` automatically, so `data = iris` and `data = static(iris)` are equivalent.

```r
data = static(penguins)
```

In the Clean Room studio the value is available for inspection, and data frames are serialized alongside the revision as Parquet.

## Packages

Declare every package the function needs in the `packages` argument. The clean execution environment will only have access to functions exported by those packages (plus base R and `publish()`).

```r
reproducible(fn, packages = c("ggplot2", "dplyr"))
```

### Pinning versions

`packages` accepts a named list to pin explicit versions in the manifest:

```r
reproducible(fn, packages = list(ggplot2 = "3.5.0", dplyr = "1.1.4"))
```

If you pass an unnamed character vector, the currently installed versions are resolved automatically.

### Imports / aliases

Use the `imports` argument to record alias-to-package mappings in the manifest (useful for parity with Python clients and for re-creating the environment elsewhere):

```r
reproducible(fn,
             packages = c("ggplot2"),
             imports  = list(plt = "ggplot2"))
```

## Publishing

Call `publish()` inside the function body. It is always injected into the clean environment — there is no separate "publisher" argument like in Python. The active session set up by `gofigR::enable()` determines where the figure goes.

```r
reproducible(
  function(data = static(mtcars)) {
    p <- ggplot(data, aes(mpg)) + geom_histogram(bins = 20)
    publish(p, figure_name = "MPG Distribution")
  },
  packages = c("ggplot2")
)
```

In interactive mode `publish()` is replaced with a no-op preview while you tweak parameters; clicking **Publish** in the gadget runs the function one more time with the real `publish()` and shows a link plus QR code to the new revision.

### What gets stored

Each published revision includes:

* **Source code** — the function body, extracted from the R AST
* **Manifest** — JSON with parameter types, widget metadata, package versions, and R version
* **DataFrame parameters** — serialized as Parquet
* **Revision flag** — marks the revision as a Clean Room revision

### Naming the function

Pass `name = "..."` to give the clean room function a display name in the webapp. Without it the function is anonymous.

```r
reproducible(fn, packages = c("ggplot2"), name = "Flipper Histogram")
```

## Edge Cases and Caveats

**Clean room isolation** — The function cannot access variables from your global R environment. Only declared package exports, base R, parameters, and `publish()` are available. This is by design.

**DataFrames are copied** — Data frame arguments are round-tripped through Parquet serialization before the function sees them. The function receives a deserialized copy, ensuring the clean room version matches what gets stored.

**Other params round-trip through JSON** — Atomic parameter values go through `jsonlite` so the function sees the same values that will be persisted in the manifest.

**100 MB limit** — Total data frame size (via `object.size()`) must be under 100 MB. If exceeded, a warning is issued and the function runs normally without clean room metadata.

**Unsupported parameter types** — Only atomic values (numeric, integer, logical, character), data frames, and nested lists of those types are serializable. Anything else triggers a warning and falls back to direct execution.

**`nanoparquet` required** — Clean room support requires the [`nanoparquet`](https://cran.r-project.org/package=nanoparquet) package. Without it a warning is issued and the function runs without clean room metadata.

**`interactive = TRUE` outside an interactive session** — Inside `knitr` or non-interactive R, the gadget is skipped and the function runs once with default parameters.

**Source code extraction** — The function body is captured from the R AST via `body(fn)`. Functions defined inline as the first argument to `reproducible()` work out of the box. Programmatically constructed functions also work as long as `body(fn)` returns a valid expression.

## Usage in R Markdown / knitr

`reproducible()` works inside R Markdown chunks. Interactive mode is automatically downgraded to a single-shot run during knitting:

````markdown
```{r setup, include=FALSE}
library(gofigR)
library(ggplot2)
gofigR::enable(workspace_name = "Scratchpad",
               analysis_name  = "Clean room in R")
```

```{r iris_scatter, fig.width=8, fig.height=6}
reproducible(
  function(
      point_size = slider(2, min = 0.5, max = 5, step = 0.5),
      data       = static(iris)
    ) {
      p <- ggplot(data, aes(Sepal.Length, Petal.Length, color = Species)) +
        geom_point(size = point_size) +
        theme_minimal()
      publish(p, figure_name = "Iris Scatter")
    },
    packages = c("ggplot2")
)
```
````

The chunk renders the figure inline, captures the clean room metadata, and publishes a revision to GoFigr — all from a single function call.


# Auto-Assign

## Overview

When publishing many figures, naming each one manually can be tedious. Auto-assign lets GoFigr use AI to automatically title your figures and organize them into the right place.

## How It Works

1. You publish a figure with `auto_assign=True`
2. GoFigr creates a temporary "Untitled" figure and processes the data
3. In the background, AI analyzes the figure image and generates a descriptive title
4. If an existing figure in the same analysis has a matching title, the revision is **moved** to that figure automatically
5. If the title is new, the temporary figure is **renamed** to the generated title

This means you can publish many figures in a loop without worrying about naming or deduplication—GoFigr handles it for you.

## Usage

### With a Publisher

Enable auto-assign on the publisher to apply it to all figures:

```python
from gofigr.publisher import Publisher

pub = Publisher(workspace="Analytics", analysis="Penguins", auto_assign=True)
pub.publish(fig)
```

Or enable it per call:

```python
pub.publish(fig, auto_assign=True)
```

### With Clean Room

Auto-assign works with `@reproducible` the same way:

```python
pub = Publisher(workspace="Analytics", analysis="Penguins", auto_assign=True)

@reproducible(publisher=pub)
def my_plot(data, species: str = "Adelie"):
    filtered = data[data['species'] == species]
    sns.histplot(data=filtered, x='flipper_length_mm')

my_plot(penguins)
```

### With Jupyter Auto-Publishing

If you use `configure(auto_publish=True)` in Jupyter, you can enable auto-assign globally:

```python
%load_ext gofigr
configure(auto_assign=True)
```

## What You See in the Browser

While the AI is assigning a title, the revision view shows a shimmer placeholder where the title would be, with a countdown indicator ("Assigning title in 5s"). Once the title is assigned, it appears automatically—no page reload needed.

If the revision is moved to an existing figure, the view updates to reflect the new figure seamlessly.

## Deduplication

Auto-assign doesn't just name figures—it also deduplicates them. If you publish a figure that looks like an existing one in the same analysis, the new revision is added to the existing figure rather than creating a duplicate. This keeps your analysis organized even when publishing from loops or automated pipelines.

## When to Use

* **Batch publishing** — publishing many figures in a loop where manual naming is impractical
* **Exploratory workflows** — when you're generating figures rapidly and want to organize later
* **Automated pipelines** — CI/CD or scheduled jobs that produce figures without human intervention
* **Jupyter notebooks** — publish every cell's output without interrupting your flow


# Sharing

## Overview

GoFigr provides multiple ways to share figures with collaborators, stakeholders, and the public. Every figure revision has a permanent link, and short IDs make those links compact enough for presentations, papers, and social media.

## Link Sharing

Any figure revision can be shared via its direct URL. The revision view includes a **Share** button that copies the link to your clipboard.

Shared links include Open Graph metadata, so they render previews in Slack, Teams, Twitter, and other platforms that support link unfurling.

## Short IDs

Every published figure revision gets a compact **short ID**—a 9-character alphanumeric code like `xK4mQ9bT0`. Short IDs are:

* **Compact** — short enough to include in papers, slides, or tweets
* **Permanent** — the same short ID always resolves to the same revision
* **Shareable** — works as a URL: `https://gofigr.io/r/<short_id>`

### How They Work

Short IDs are generated automatically when you publish a figure. Each user gets a unique prefix, and revisions are numbered sequentially within that prefix. You don't need to configure anything—short IDs are assigned behind the scenes.

### QR Codes

The revision view displays a QR code alongside the short URL. This is useful for:

* **Posters** — attendees can scan to see the interactive figure
* **Printed reports** — link back to the live, explorable version
* **Presentations** — audience can follow along on their own devices

### Accessing a Shared Figure

When someone opens a short ID link, GoFigr resolves it to the full revision and displays:

* The figure image (with interactive widgets for Clean Room figures)
* AI-generated description and key takeaways
* Technical context (source code, data files, metadata)
* Revision history

Access respects the figure's sharing settings—public figures are visible to anyone, while private figures require authentication.


# Python

GoFigr's Python package provides seamless figure capture for all major visualization libraries.

## Supported Libraries

| Library    | Auto-capture | Manual capture |
| ---------- | ------------ | -------------- |
| Matplotlib | ✅            | ✅              |
| Seaborn    | ✅            | ✅              |
| Plotly     | ✅            | ✅              |

## Jupyter Notebooks

### Auto-Configured Setup (Simplest)

Just load the extension:

```python
%load_ext gofigr
```

That's it! GoFigr will:

* Automatically use your default workspace from `gfconfig`
* Create or use an analysis named after your notebook
* Enable auto-publish (automatically captures all figures)

All figures you create will be automatically published:

```python
%load_ext gofigr

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

# Create a simple plot
df = pd.DataFrame({'x': np.random.randn(100), 'y': np.random.randn(100)})
plt.scatter(df['x'], df['y'])
plt.title('Random Scatter Plot')

# This figure is automatically published!
```

### Custom Configuration

For more control, use the `configure()` function:

```python
%load_ext gofigr

from gofigr.jupyter import configure, FindByName, ApiId, NotebookName

configure(
    workspace=FindByName("Primary Workspace", create=False),
    analysis=FindByName("My Analysis", create=True),
    auto_publish=True,
    default_metadata={
        'requested_by': "Alyssa",
        'study': 'Pivotal Trial 1'
    }
)
```

#### Configuration Options

| Option             | Description                                                             |
| ------------------ | ----------------------------------------------------------------------- |
| `workspace`        | `FindByName("Name")`, `ApiId("uuid")`, or `None` (use default)          |
| `analysis`         | `FindByName("Name", create=True)`, `NotebookName()`, or `ApiId("uuid")` |
| `auto_publish`     | If `True`, all figures are automatically published                      |
| `default_metadata` | Dictionary of metadata to store with each revision                      |
| `api_key`          | Override API key (if not using default from `gfconfig`)                 |

### Manual Publishing

If you set `auto_publish=False`, manually publish figures:

```python
%load_ext gofigr

from gofigr.jupyter import configure, FindByName, publish

configure(auto_publish=False, analysis=FindByName("My Analysis", create=True))

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
plt.title('Manual Publish Example')

# Manually publish
publish(fig=plt.gcf(), target=FindByName("My Figure", create=True))
```

***

## Standalone Scripts

Use the `Publisher` class for scripts outside Jupyter:

```python
import matplotlib.pyplot as plt
from gofigr.publisher import Publisher

# Initialize the publisher
pub = Publisher(workspace="My Workspace", analysis="Script Analysis")

# Create a figure
plt.plot([1, 2, 3], [1, 4, 9])
plt.title('Quadratic Function')

# Publish the figure
pub.publish(plt.gcf())
```

### Complete Script Example

```python
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.express as px
from gofigr.publisher import Publisher

# Setup GoFigr Publisher
pub = Publisher(workspace="My Workspace", analysis="Penguin Analysis")

# Load data
penguins = sns.load_dataset("penguins")

# Create and publish a Seaborn plot
sns.scatterplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species")
plt.title("Penguin Measurements (Seaborn)")
pub.publish(plt.gcf())

# Create and publish a Plotly plot
fig = px.scatter(penguins, x="flipper_length_mm", y="bill_length_mm", 
                 color="species", title="Penguin Measurements (Plotly)")
pub.publish(fig)
```

***

## Asset Tracking

GoFigr can automatically track data files used in your analyses.

### Using Tracked Data Reading

Use `gf.read_csv()` instead of `pd.read_csv()` to automatically track data:

```python
%load_ext gofigr

# gf is automatically available after loading the extension
df = gf.read_csv('data/penguins.csv')

# The DataFrame is now linked to the tracked asset
print(df.attrs.get('_gofigr_revision'))  # Shows the asset revision ID
```

### Supported Reading Methods

| Method              | File Type     |
| ------------------- | ------------- |
| `gf.read_csv()`     | CSV files     |
| `gf.read_excel()`   | Excel files   |
| `gf.read_json()`    | JSON files    |
| `gf.read_parquet()` | Parquet files |
| `gf.read_feather()` | Feather files |
| `gf.read_pickle()`  | Pickle files  |

All methods accept the same parameters as their pandas counterparts.

### Manual Asset Syncing

```python
# Sync a file without reading it
gf.sync.sync('data/penguins.csv')

# Or use as a context manager
with gf.sync.open('data/raw_data.txt', 'r') as f:
    content = f.read()
```


# R

GoFigr's R package (`gofigR`) provides automatic figure capture for ggplot2, base R graphics, and more.

## Compatibility

gofigR integrates with:

* R Markdown (knitr)
* Interactive sessions in RStudio
* Shiny applications
* Standalone scripts

Tested with R 4.3.2, but any reasonably recent version should work.

## Installation

```r
# From CRAN
install.packages("gofigR")

# Or from GitHub (development version)
library(devtools)
devtools::install_github("gofigr/gofigR")
```

## Configuration

Run the configuration wizard once:

```r
library(gofigR)
gfconfig()
```

This saves your credentials to `~/.gofigr`.

***

## Basic Usage

### Enable GoFigr

In your setup chunk or at the start of your script:

```r
library(gofigR)
gofigR::enable()
```

You can optionally specify an analysis name:

```r
gofigR::enable(analysis_name = "My Analysis")
```

### Publishing Plots

Use the `publish()` function:

```r
library(ggplot2)

# Create a plot
p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  ggtitle("Weight vs MPG")

# Publish it
publish(p, "Weight vs MPG")

# Or use the pipe
p %>% publish("Weight vs MPG")
```

### Base R Graphics

Wrap base R plotting code in `publish()`:

```r
publish({
  plot(pressure, main = "Pressure vs Temperature")
  text(200, 50, "Note the non-linear relationship")
}, figure_name = "Pressure Plot")
```

You can optionally attach data:

```r
publish({
  data <- as.matrix(mtcars)
  coul <- colorRampPalette(brewer.pal(8, "PiYG"))(25)
  heatmap(data, scale = "column", col = coul, main = "Visualizing mtcars")
}, data = mtcars, figure_name = "Cars Heatmap")
```

The `data` argument specifies data to associate with the figure—it will appear under "Files" (as `.RDS`) in GoFigr.

***

## R Markdown

In your R Markdown document:

````markdown
```{r setup, include=FALSE}
library(gofigR)
gofigR::enable()
```

```{r analysis}
library(ggplot2)

ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +
  geom_point() %>%
  publish("Iris Measurements")
```
````

***

## Shiny Integration

Replace `plotOutput + renderPlot` with `gfPlot + gfPlotServer`:

```r
library(shiny)
library(gofigR)

gofigR::enable()

ui <- fluidPage(
  titlePanel("Old Faithful Geyser Data"),
  
  sidebarLayout(
    sidebarPanel(
      sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30)
    ),
    
    mainPanel(
      gfPlot("distPlot")
    )
  )
)

server <- function(input, output) {
  gfPlotServer("distPlot", {
    x <- faithful[, 2]
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    hist(x, breaks = bins, col = 'darkgray', border = 'white',
         xlab = 'Waiting time to next eruption (in mins)',
         main = 'Histogram of waiting times')
  }, input, figure_name = "Old Faithful Waiting Times")
}

shinyApp(ui = ui, server = server)
```

Note: Pass `input` to `gfPlotServer` to capture Shiny inputs as metadata.

***

## Common Issues

### Duplicate Heatmaps with pheatmap

Some plotting functions like `pheatmap::pheatmap()` both draw immediately and return an object. This can cause duplicates in R Markdown.

**Problem:**

```r
pheatmap::pheatmap(mat) %>% publish("My heatmap")
# Shows heatmap twice!
```

**Solution:**

```r
hm <- pheatmap::pheatmap(mat, silent = TRUE)  # Don't draw immediately
publish(hm, "My heatmap")                      # Only GoFigr version appears
```


# Jupyter

GoFigr provides first-class support for Jupyter notebooks, capturing figures with complete execution context.

## What Gets Captured

When you create a figure in Jupyter with GoFigr enabled:

| Element              | Captured |
| -------------------- | -------- |
| Figure image         | ✅        |
| Cell source code     | ✅        |
| Cell execution order | ✅        |
| Variable values      | ✅        |
| Notebook metadata    | ✅        |
| Kernel info          | ✅        |

## Setup

Load the GoFigr extension in your first cell:

```python
%load_ext gofigr
```

That's it! GoFigr will:

* Use your default workspace from `gfconfig`
* Create an analysis named after your notebook
* Automatically capture all figures

## Supported Environments

| Environment                | Status                           |
| -------------------------- | -------------------------------- |
| JupyterLab                 | ✅ Full support                   |
| Jupyter Notebook (Classic) | ✅ Full support                   |
| VS Code Notebooks          | ✅ Full support                   |
| Google Colab               | ✅ Supported (with API key)       |
| Databricks                 | ✅ Supported (with configuration) |

## Example Workflow

```python
# Cell 1: Setup
%load_ext gofigr

import pandas as pd
import matplotlib.pyplot as plt

# Cell 2: Load data
df = pd.read_csv("experiment_results.csv")
print(f"Loaded {len(df)} rows")

# Cell 3: Visualize (automatically captured!)
plt.figure(figsize=(10, 6))
plt.scatter(df['x'], df['y'], c=df['category'], cmap='viridis')
plt.colorbar(label='Category')
plt.title("Experiment Results")
plt.xlabel("X Measurement")
plt.ylabel("Y Measurement")
```

The figure in Cell 3 is captured along with:

* The plotting code from Cell 3
* The data loading context from Cell 2
* The notebook's execution state

## Custom Configuration

For more control over workspace/analysis selection:

```python
%load_ext gofigr

from gofigr.jupyter import configure, FindByName

configure(
    workspace=FindByName("My Workspace"),
    analysis=FindByName("Data Analysis", create=True),
    auto_publish=True,
    default_metadata={'study': 'Trial 1'}
)
```

## Data Asset Tracking

Track the data files used in your analysis:

```python
%load_ext gofigr

# Use gf.read_csv instead of pd.read_csv
df = gf.read_csv('data/experiment.csv')

# The DataFrame is linked to the tracked asset
# Figures created from this data will be linked to the data version
```

When you publish a figure, GoFigr automatically tracks which data assets were used, ensuring complete reproducibility.

## QR Codes and Revision IDs

Each published figure displays:

* A **QR code** linking to the figure in GoFigr
* A **unique revision ID** for tracking

This allows anyone viewing your notebook to instantly access the full context in GoFigr.

***

## Git Import for Existing Notebooks

Already have notebooks in a Git repository? Import them directly without re-running:

1. Go to GoFigr → **Import** → **Git Repository**
2. Connect your GitHub/GitLab account
3. Select the repository and branches
4. GoFigr extracts all figures from all commits

See [Git Repository Import](/features/git-import) for details.


# Overview

Managed Compute gives you your own cloud machine for data science—running JupyterLab, code-server, and R Server in your browser, with the GoFigr client **already installed and signed in**. Launch one in a couple of clicks, work exactly as you would locally, and every figure you publish is captured with its code, data, and environment automatically.

No setup, no credentials to copy, nothing to install. Open a notebook and start working.

## Why use it

* **Zero setup.** The machine is pre-authenticated to your GoFigr account and comes with common Python and R packages already installed.
* **Reproducible by default.** Because the GoFigr client is built in, figures publish with full provenance from the moment you start.
* **Right-sized to the job.** Pick a small machine for light notebooks or a large one for memory-hungry workloads, and change it later.
* **Your work persists.** Files, notebooks, and packages you install live on a data volume that survives stop/start.
* **Costs stay in check.** Instances auto-stop when idle, and you only pay for compute while a machine is running.

## What you get

Every instance runs three editors against the same files—use whichever you prefer:

| Editor          | Best for                                           |
| --------------- | -------------------------------------------------- |
| **JupyterLab**  | Interactive Python notebooks                       |
| **code-server** | Scripts, multi-file projects, the GoFigr extension |
| **R Server**    | R notebooks, R Markdown, and Quarto                |

## Key concepts

* **Instance** — one cloud machine. You can have more than one.
* **Machine type (tier)** — how much CPU and memory the instance has. See [Choosing a machine type](/managed-compute/tiers).
* **Data volume** — your persistent home directory (`/home/gofigr/notebooks`). Files, installed packages, and data here survive stop/start; they're removed only when you delete the instance.
* **Lifecycle** — an instance is **Running** (you can connect and it bills for compute), **Stopped** (parked—no compute charges, files preserved), or somewhere in between. See [Lifecycle & persistence](/managed-compute/lifecycle).
* **Idle auto-stop** — instances stop themselves after a period of inactivity so you don't pay for a machine you forgot about.

## Start here

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Launch your first instance</strong></td><td>From launch to your first published figure in a few minutes</td><td><a href="/pages/Udm1YQfq3n9wSLEe6mJy">/pages/Udm1YQfq3n9wSLEe6mJy</a></td></tr><tr><td><strong>Lifecycle &#x26; persistence</strong></td><td>Running vs. stopped, what persists, and idle auto-stop</td><td><a href="/pages/I7y2OYGzCKnvGdDoYL10">/pages/I7y2OYGzCKnvGdDoYL10</a></td></tr><tr><td><strong>Compute &#x26; storage charges</strong></td><td>How compute and storage are billed</td><td><a href="/pages/3SUuNQhRYMt39Q5VwM2F">/pages/3SUuNQhRYMt39Q5VwM2F</a></td></tr></tbody></table>


# Launch Your First Instance

This walks you from an empty workspace to your first published figure on a managed compute instance. Plan for about 5–10 minutes—most of it is the machine provisioning while you grab a coffee.

## 1. Launch an instance

In the GoFigr web app, open the **Compute** view and click **Launch compute instance**. The launch dialog has three short sections:

* **Instance** — give it a **Name** you'll recognize (e.g. *Lung-cancer analysis*).
* **Machine** — pick a **Tier** (machine type) and a data **Volume** size. **Standard** (2 vCPU, 4 GB) is selected by default and is plenty for most notebooks; you can [change it later](/managed-compute/tiers). The default volume is 10 GiB.
* **Auto-shutdown** — leave **Auto-stop on idle** on so the machine parks itself when you step away. See [idle auto-stop](/managed-compute/lifecycle#idle-auto-stop).

The dialog shows an **Estimated cost** table (hourly, 24/7, and a typical 8 h/day) so there are no surprises. Click **Launch**.

{% hint style="info" %}
**Provisioning takes about 5–10 minutes.** You don't have to wait on the page—we'll email you when the instance is ready. You can watch it move through **Launching → Initializing → Starting services → Running**.
{% endhint %}

## 2. Connect

When the status reaches **Running**, the **Jupyter**, **code-server**, and **R Server** buttons light up. Click one to open that editor in a new browser tab:

* **Jupyter** — JupyterLab, for interactive Python notebooks.
* **code-server** — code-server in the browser, with the GoFigr extension installed.
* **R Server** — for R notebooks, R Markdown, and Quarto.

All three edit the same files, so it doesn't matter which you start with. See [Working in your instance](/managed-compute/working-in-the-workspace) for the details of each.

## 3. Run the example and publish a figure

You don't need to configure anything—this machine is **already connected to your GoFigr account**. Your home directory comes seeded with a welcome doc (`START-HERE.md`) and a ready-to-run example.

{% tabs %}
{% tab title="Python" %}
In JupyterLab, open **`examples/tcga_lung_classifier.ipynb`** and run it top to bottom. As it runs, each figure is published to your GoFigr account together with the code, data, and environment that produced it.
{% endtab %}

{% tab title="R" %}
In R Server, open **`examples/tcga_lung_analysis.qmd`**—the same analysis as a Quarto report—and render it. `library(gofigR)` is already installed and configured, so figures publish as the report runs.
{% endtab %}
{% endtabs %}

Open [app.gofigr.io](https://app.gofigr.io) and watch the figures appear in your workspace, each linked to the exact code that created it.

{% hint style="info" %}
New to publishing with GoFigr? The [Quick Start](/getting-started/quickstart) explains how capture works. The only difference on a managed instance: there's no `gfconfig` step—it's done for you.
{% endhint %}

## 4. Stop when you're done

Compute bills per minute **only while an instance is Running**. When you finish, use the instance's **Stop** action (or just let idle auto-stop handle it). Your files are preserved—start the instance again any time and pick up where you left off.

## Next steps

* [Working in your instance](/managed-compute/working-in-the-workspace) — Jupyter, code-server, and R Server
* [Lifecycle & persistence](/managed-compute/lifecycle) — what persists, and idle auto-stop
* [Choosing a machine type](/managed-compute/tiers) — sizing your instance
* [Compute & storage charges](/billing-and-plans/compute-usage) — how billing works


# Working in Your Instance

Every managed compute instance runs three editors—**JupyterLab**, **code-server**, and **R Server**—against the same files. Use whichever you prefer, or switch between them mid-project.

Connect from the GoFigr web app: when an instance is **Running**, click the **Jupyter**, **code-server**, or **R Server** button to open that editor in a new tab. (Connect links are tied to you as the owner.)

## Where your files live

Your home directory is **`/home/gofigr/notebooks`**. It's seeded on first boot with:

* **`START-HERE.md`** — a short welcome and orientation.
* **`examples/`** — a runnable example notebook (`tcga_lung_classifier.ipynb`) and its Quarto/R counterpart (`tcga_lung_analysis.qmd`).

Everything in your home directory lives on the instance's [data volume](/managed-compute/lifecycle#what-persists) and persists across stop/start.

## The editors

{% tabs %}
{% tab title="JupyterLab" %}
The GoFigr IPython extension is pre-installed. In a notebook, capture starts as soon as you load it:

```python
%load_ext gofigr
```

From there, figures you create are published automatically—no credentials to set up, because the instance is already signed in to your account. See the [Python integration](/integrations/python) for what's captured and how.
{% endtab %}

{% tab title="code-server" %}
code-server is a full-featured editor running in your browser, with the **GoFigr extension** already installed. Open notebooks or scripts, use the integrated terminal, and manage multi-file projects. The Python interpreter is pre-selected for you.
{% endtab %}

{% tab title="R Server" %}
R Server gives you a full R IDE in the browser. **`library(gofigR)`** is installed and configured, so it works the same as a local session:

```r
library(gofigR)
gofigR::enable()
```

Figures publish as you run R scripts, R Markdown, or Quarto documents. See the [R integration](/integrations/r) for usage details.
{% endtab %}
{% endtabs %}

## The Python environment

Python runs from a virtual environment at **`/home/gofigr/venv`**. It lives on your [data volume](/managed-compute/lifecycle#what-persists), so anything you install persists across restarts, and it's **already active** in JupyterLab and the code-server terminal—you don't need to activate it. The `gofigr` client is installed here, and `pip install` and the notebook kernel both resolve to this environment.

If you open a plain login shell and want it active explicitly:

```bash
source /home/gofigr/venv/bin/activate
```

## Installing extra packages

The instance comes pre-loaded with common Python and R packages. Need more? Add them from the integrated terminal or a notebook cell—they install to your data volume and **persist across restarts**:

{% tabs %}
{% tab title="Python" %}

```bash
pip install <package>
```

{% endtab %}

{% tab title="R" %}

```r
install.packages("<package>")
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Because installed packages live on your persistent data volume, you only install each one once—it'll be there the next time you start the instance.
{% endhint %}


# Lifecycle & Persistence

A managed compute instance moves between a handful of states over its life. This page explains what each one means, what's preserved when you stop an instance, and how idle auto-stop keeps costs down.

## Instance status

The status shown next to each instance tells you what it's doing:

| Status                | Meaning                                                                                                                            |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **Launching**         | The cloud machine is starting up.                                                                                                  |
| **Initializing**      | The machine booted; first-time setup is running.                                                                                   |
| **Starting services** | The editors (Jupyter, code-server, R Server) are coming up.                                                                        |
| **Running**           | Ready to use—connect and start working. Compute is billing.                                                                        |
| **Stopping**          | The machine is shutting down.                                                                                                      |
| **Stopped**           | Parked. No compute charges; your files are preserved.                                                                              |
| **Error**             | Something went wrong starting up (see [Troubleshooting](/managed-compute/troubleshooting)).                                        |
| **Pending deletion**  | You deleted the instance; it's in the recovery window before removal. See [Deleting & restoring](/managed-compute/delete-restore). |

A fresh launch walks through **Launching → Initializing → Starting services → Running**, which is why a cold start takes a few minutes.

## Stop and start

You control whether an instance is running:

* **Stop** parks the machine. Compute billing ends immediately, and your data volume—notebooks, files, installed packages—is kept intact. Storage continues to bill while stopped (see [Compute & storage charges](/billing-and-plans/compute-usage)).
* **Start** brings a stopped instance back. A restart is faster than the first launch because setup is already done.

Stop an instance whenever you're done for a while—there's no penalty, and nothing is lost.

## What persists

Each instance has a **data volume** mounted at your home directory (`/home/gofigr/notebooks`). Everything there—notebooks, scripts, data files, and packages you install with `pip` or `install.packages()`—**survives stop/start**.

The only thing that removes the data volume is **deleting** the instance. As long as the instance exists, your work is safe across any number of stop/start cycles.

{% hint style="warning" %}
The data volume is durable storage for an active instance—it is **not a backup**. Deleting an instance destroys its volume. Keep anything you can't lose in version control or publish it to GoFigr.
{% endhint %}

## Idle auto-stop

So you never pay for a machine you forgot about, instances **stop themselves after a period of inactivity**. By default the idle window is **60 minutes**.

You can adjust this per instance under **Auto-shutdown** in the launch dialog, or later via the **Auto-shutdown…** action:

* **Auto-stop on idle** — toggle the behavior on or off.
* **Idle window (minutes)** — how long the instance can sit idle before stopping.

A few rules apply:

* Your plan may **require** auto-stop, in which case you can't turn it off.
* Your plan may **cap** the idle window at a maximum.
* There's a **5-minute minimum** idle window.

Activity in any editor—running cells, editing files—resets the idle timer. If configured, you'll get a heads-up notification before an idle stop. Nothing is lost when an instance auto-stops; just start it again and your files are waiting.

## Other actions

From an instance's menu you can also:

* **Restart…** — stop and start in one step.
* **Rename…** — change the display name.
* **Change type…** — switch to a different [machine type](/managed-compute/tiers). Stop the instance first.

{% hint style="info" %}
**Change type** and some other actions require the instance to be stopped first. The menu will tell you when that's the case.
{% endhint %}


# Choosing a Machine Type

When you launch an instance you pick a **tier**—the machine type that sets how much CPU and memory it has. Start small; you can change the tier later if a workload needs more headroom.

## Available tiers

| Tier                     | vCPU | RAM   | Best for                                                  |
| ------------------------ | ---- | ----- | --------------------------------------------------------- |
| **Micro**                | 2    | 2 GB  | Quick experiments and light, short-lived notebooks.       |
| **Standard** *(default)* | 2    | 4 GB  | General-purpose notebooks and light analysis.             |
| **Pro**                  | 4    | 16 GB | Larger datasets and memory-hungry workloads.              |
| **Performance**          | 8    | 32 GB | Heavier compute and mid-size datasets on dedicated cores. |
| **Performance XL**       | 16   | 64 GB | Large, parallel, memory-hungry workloads.                 |

**Standard** is selected by default and handles most notebook work. Reach for a bigger tier when you hit memory limits or want more cores for parallel jobs. The tiers available to you depend on your plan.

## Changing the tier

To move an instance to a different machine type, **Stop** it, then choose **Change type…** from its menu and pick a new tier. Your data volume and files are unaffected—only the CPU and memory change.

## What it costs

Larger tiers cost more per minute of runtime. Rather than memorize numbers, check the live figures in the app:

* The **Estimated cost** table in the launch dialog shows the hourly rate plus typical monthly estimates (24/7 and 8 h/day) for the tier you've selected.
* The [**Compute Usage**](/billing-and-plans/compute-usage) card shows what you've actually used this period.

Compute bills per minute **only while an instance is Running**, so a bigger machine you stop when idle can still be economical. See [Compute & storage charges](/billing-and-plans/compute-usage) for the full model.


# Deleting & Restoring

When you no longer need an instance, you **delete** it. Deleting is intentional and final by design: it removes the instance **and its data volume**, so everything on it—notebooks, files, and installed packages—is permanently lost.

## Deleting an instance

Choose **Delete…** from an instance's menu. Because this destroys data, you'll be asked to **type the instance's name** to confirm.

Deleting first stops the instance (ending compute charges) and then schedules it for removal. **Deletion completes within 7 days.**

{% hint style="danger" %}
**Deleting is not the same as stopping.** [Stopping](/managed-compute/lifecycle#stop-and-start) parks an instance and keeps your files; deleting removes the machine and its data volume for good. If you just want to pause and avoid compute charges, **Stop** instead.
{% endhint %}

## Restoring by mistake

The window before final removal exists as a last-resort safety net—**not a backup**. If you delete an instance by accident, choose **Restore** from its menu before the window closes and the instance comes back with its data intact.

## Restore limit

Restore is meant for genuine mistakes, so it's capped at **3 self-service restores** per instance. After that, the **Restore** action is disabled and you'll need to email <support@gofigr.io> to recover the instance.

{% hint style="info" %}
For anything you can't afford to lose, don't rely on the restore window. Keep it in version control, or publish your figures and data to GoFigr where they're preserved independently of the instance.
{% endhint %}


# Troubleshooting & FAQ

Common questions and what to do when an instance doesn't behave as expected.

## My instance is stuck in Initializing or Starting services

A cold launch walks through **Launching → Initializing → Starting services → Running** and usually takes 5–10 minutes. The **Initializing** and **Starting services** steps cover first-boot setup and bringing the editors up. Give it a few minutes—we'll email you when it reaches **Running**. If it stays in one step far longer than that, it likely hit an error (below).

## My instance shows an Error

An **Error** status means the machine didn't come up cleanly. The status detail explains where it failed—for example:

* *The instance failed to boot.*
* *Started, but its services didn't come up in time.*
* *Initialization failed.*
* *Stopped after an error.*

Try **Restart…** from the instance menu. If it keeps erroring, contact <support@gofigr.io> with the instance name.

## My instance stopped on its own

That's [idle auto-stop](/managed-compute/lifecycle#idle-auto-stop) doing its job—instances stop after a period of inactivity (60 minutes by default) so you don't pay for an idle machine. Nothing is lost; just **Start** it again. To change how long it waits, adjust the idle window under **Auto-shutdown…**.

## I can't connect / the Jupyter button is disabled

The **Jupyter**, **code-server**, and **R Server** buttons only become active once the instance is **Running** and its services are up. If a button is disabled, check the status—if it's still **Initializing** or **Starting services**, wait for **Running**. Connect links are tied to you as the instance owner.

## The Restore action is disabled

You've reached the **3 self-service restore** limit for that instance. Email <support@gofigr.io> to restore it. See [Deleting & restoring](/managed-compute/delete-restore).

## Will I lose my files if I stop the instance?

No. Stopping preserves everything on your data volume—notebooks, files, and installed packages. Only **deleting** an instance removes its data. See [Lifecycle & persistence](/managed-compute/lifecycle#what-persists).

## How do I avoid surprise charges?

Compute bills per minute only while an instance is **Running**, so **Stop** it when you're done (or rely on idle auto-stop). Storage bills continuously on the data volume even while stopped. Watch your usage on the **Compute Usage** card—see [Compute & storage charges](/billing-and-plans/compute-usage).

## Getting help

Still stuck? Email <support@gofigr.io> with your instance name and what you were doing.


# Plans & Billing

Your **subscription** determines what your account can do—which features are available, how many workspaces you get, and your **included compute allowance**. GoFigr offers a free tier to get started and paid plans for teams and heavier workloads.

## What a plan governs

* **Features and limits** — workspaces, collaborators, and access to capabilities across the platform.
* **Compute** — which [machine types](/managed-compute/tiers) you can launch, your monthly **included compute allowance**, and whether usage beyond it is allowed. See [Compute & storage charges](/billing-and-plans/compute-usage) for the details.

## Managing your subscription

Billing is handled through Stripe. From the GoFigr web app you can:

* **Choose or change your plan** — upgrade, downgrade, or start a paid plan.
* **Open the billing portal** — update your payment method, view invoices, and manage the subscription through Stripe's secure customer portal.

## Compute billing

Managed Compute is metered separately from your plan's flat features—you're billed for the compute time and storage you actually use, against your plan's included allowance. That model is covered in full on its own page:

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Compute &#x26; storage charges</strong></td><td>How compute time and storage are metered, allowances, and reading your usage</td><td><a href="/pages/3SUuNQhRYMt39Q5VwM2F">/pages/3SUuNQhRYMt39Q5VwM2F</a></td></tr></tbody></table>

## Getting help

Questions about billing or plans? Email <support@gofigr.io>.


# Compute & Storage Charges

Managed Compute is **metered**—you pay for the compute time and storage you actually use, offset by your plan's included allowance. This page explains the model and how to read your usage in the app.

## What's billed

There are two separate meters:

* **Compute** — billed **per minute while an instance is Running**, at a rate that depends on the [machine type](/managed-compute/tiers). Larger tiers cost more per minute. A **Stopped** instance bills no compute.
* **Storage** — billed for the **data volume** (your persistent home directory), per GB over time. Storage bills **continuously, even while the instance is stopped**, because your files are still being kept. It pauses only while an instance is [pending deletion](/managed-compute/delete-restore).

{% hint style="info" %}
**Stopping saves compute, not storage.** Stop an instance to stop paying for compute. To stop paying for storage too, you have to delete the instance—which removes its data. There's no way to keep your files at zero cost.
{% endhint %}

## Your included allowance

Most plans include a monthly **compute allowance**—a credit that offsets your compute and storage usage each billing period. Usage is drawn down against the allowance first; you're only billed for what exceeds it.

* The allowance **refreshes each billing period** and **does not roll over**.
* What happens when you exceed it depends on your subscription:
  * **Overages enabled** — additional usage is billed at standard rates.
  * **Overages disabled** — the allowance caps **compute**: once it's used up, new instances won't launch and running ones are stopped, so you don't keep racking up compute charges.

{% hint style="warning" %}
**The overages cap applies to compute, not storage.** Stopping an instance ends its compute charges but **not** its storage charges—the data volume keeps billing while stopped. With overages off, storage on your existing volumes can still carry your usage past the included allowance and result in a charge. The only way to stop storage charges is to [delete](/managed-compute/delete-restore) the instance, which removes its data. Delete instances you no longer need to avoid ongoing storage costs.
{% endhint %}

### Low-allowance check at launch

If you're still within your allowance but don't have much runtime left at the selected tier, launching or starting an instance asks you to **confirm** before proceeding—so you're never surprised by an instance that stops shortly after it starts.

## Reading your usage

The **Compute Usage** card—available at the instance, workspace, and organization level—shows where you stand this period:

* **Compute usage** — what your running time has cost so far.
* **Storage usage** — what your data volumes have cost (shown in GB·hr / GB·mo).
* **Included credit** — your allowance, applied as a credit.
* **Billed** — the net amount you'll be charged after the credit.
* A **progress bar** against your allowance, with a status banner:
  * *Within the included compute allowance.*
  * *Approaching the included compute allowance.* (at 80%)
  * *You've used your included allowance. Additional usage is billed at standard rates…* (overages enabled)
  * *Over the included allowance by …* (overages disabled)

### Billable intervals

For a line-by-line breakdown, open **Billable usage…** from an instance's menu, or use the **Show billable intervals** expander on the usage card. You'll see:

* A **Compute** table — each running interval with its instance, start/end, tier, billed minutes, rate, and cost. An interval that's still open shows a **running** badge.
* A **Storage** table — each data volume's size, billed GB-hours, rate, and cost.

This is the authoritative record of what you're being charged and why.

## Getting help

Questions about a charge or your allowance? Email <support@gofigr.io>.


# Overview

Complete documentation of the GoFigr REST API.

**Base URL:** `https://api.gofigr.io`\
**Supported API Versions:** `v1`, `v1.1`, `v1.2`, `v1.3`, `v1.4`

All endpoints are versioned: `/api/{version}/resource/`

## Authentication

GoFigr supports two authentication methods:

| Method           | Header                                 | Use Case                       |
| ---------------- | -------------------------------------- | ------------------------------ |
| JWT Bearer Token | `Authorization: Bearer {access_token}` | Web app, short-lived sessions  |
| API Key          | `Authorization: Token {api_key}`       | Scripts, long-lived automation |

## Quick Links

* [Authentication](/api-reference/endpoints#authentication) — JWT tokens, API keys, password reset
* [Core Resources](/api-reference/endpoints#workspaces) — Workspaces, analyses, figures, revisions
* [Assets](/api-reference/endpoints#assets) — Data assets and asset revisions
* [Stories](/api-reference/endpoints#stories) — AI-powered presentations
* [Comments](/api-reference/endpoints#comments) — Collaboration features
* [AI & Deep Insight](/api-reference/endpoints#deep-insight-ai) — AI-powered analysis
* [Search](/api-reference/endpoints#search) — Text and image search
* [Users & Organizations](/api-reference/endpoints#users) — User management, organizations

## Response Format

All responses are JSON. Successful responses return `2xx` status codes.

### Error Responses

```json
{
  "error": "string",
  "detail": "string"  // optional
}
```

| Status | Meaning                                 |
| ------ | --------------------------------------- |
| 400    | Bad Request — Invalid request data      |
| 401    | Unauthorized — Authentication required  |
| 403    | Forbidden — Insufficient permissions    |
| 404    | Not Found — Resource doesn't exist      |
| 429    | Too Many Requests — Rate limit exceeded |
| 500    | Internal Server Error                   |

## Common Patterns

### Sharing

All shareable resources support user and link sharing:

```
POST /api/{version}/{resource}/{api_id}/share/user/
GET  /api/{version}/{resource}/{api_id}/share/user/
POST /api/{version}/{resource}/{api_id}/share/link/
GET  /api/{version}/{resource}/{api_id}/share/link/
```

### Thumbnails & Size

```
GET /api/{version}/{resource}/{api_id}/size/
GET /api/{version}/{resource}/{api_id}/thumbnail/
GET /api/{version}/{resource}/{api_id}/thumbnail/{size}/
```

### Query Parameters

| Parameter | Description                                           |
| --------- | ----------------------------------------------------- |
| `shallow` | Return lightweight representation without nested data |
| `silent`  | Suppress activity log generation for write operations |


# REST Endpoints

Complete documentation of all HTTP REST endpoints in the GoFigr server API.

**Base URL:** `https://api.gofigr.io`\
**Supported API Versions:** `v1`, `v1.1`, `v1.2`, `v1.3`, `v1.4`, `v1.4.1`

All endpoints are versioned: `/api/{version}/resource/`

**Authentication:**

* JWT Bearer token: `Authorization: Bearer {access_token}`
* API Key: `Authorization: Token {api_key}`

***

## Table of Contents

1. [Authentication](#authentication)
2. [Common Resource Behavior](#common-resource-behavior)
3. [Bootstrap](#bootstrap)
4. [API Info](#api-info)
5. [Site Settings](#site-settings)
6. [Organizations](#organizations)
7. [Workspaces](#workspaces)
8. [Analyses](#analyses)
9. [Figures](#figures)
10. [Figure Revisions](#figure-revisions)
11. [Assets](#assets)
12. [Asset Revisions](#asset-revisions)
13. [External Data](#external-data)
14. [Stories](#stories)
15. [Comments](#comments)
16. [Reactions](#reactions)
17. [Deep Insight (AI)](#deep-insight-ai)
18. [Search](#search)
19. [Managed Compute](#managed-compute)
20. [Tasks](#tasks)
21. [Users](#users)
22. [Plans](#plans)
23. [API Keys](#api-keys)
24. [SSH Keys](#ssh-keys)
25. [Git Repository](#git-repository)
26. [Data Upload](#data-upload)
27. [Invitations](#invitations)
28. [Billing](#billing)
29. [Auth0](#auth0)
30. [Short IDs](#short-ids)
31. [AI Usage](#ai-usage)
32. [Metadata Proxy](#metadata-proxy)
33. [API Version History](#api-version-history)

***

## Authentication

### Obtain JWT Token Pair

```
POST /api/token/
```

Authenticates user and returns access/refresh token pair.

**Request Body:**

```json
{
  "username": "string",
  "password": "string",
  "remember_me": true  // optional, extends token lifetime
}
```

**Response:** `200 OK`

```json
{
  "access": "string",
  "refresh": "string"
}
```

***

### Refresh Access Token

```
POST /api/token/refresh/
```

**Request Body:**

```json
{
  "refresh": "string"
}
```

**Response:** `200 OK`

```json
{
  "access": "string"
}
```

***

### Password Reset

```
POST /api/password_reset/
```

**Request Body:**

```json
{
  "email": "string"
}
```

***

## Common Resource Behavior

The core resources — **Organizations, Workspaces, Analyses, Figures, Figure Revisions, Assets, Asset Revisions, Stories, and External Data** — are all backed by the same viewset machinery and therefore share a common set of CRUD routes, sub-actions, and query parameters. Rather than repeat them under every resource, they are described once here. Resource-specific fields and extra actions are documented in each section below.

### Standard CRUD routes

For a resource mounted at `/{resource}/`:

```
GET    /api/{version}/{resource}/             # List (where supported)
POST   /api/{version}/{resource}/             # Create        → 201, 409 on duplicate client_id
GET    /api/{version}/{resource}/{api_id}/    # Retrieve
PUT    /api/{version}/{resource}/{api_id}/    # Full update
PATCH  /api/{version}/{resource}/{api_id}/    # Partial update
DELETE /api/{version}/{resource}/{api_id}/    # Delete         → 204
```

Objects are addressed by their `api_id` (a UUID). Permissions are enforced per action (`VIEW`, `CREATE`, `UPDATE`, `DELETE`, `MANAGE`, `SHARE`). Moving an object to a new parent (e.g. changing a figure's `analysis`) on update requires move permission and is rejected with `400` if the type is not movable.

### Common sub-actions

Available on every core resource (`{api_id}` detail routes):

```
GET  /api/{version}/{resource}/{api_id}/size/         # → { "size_bytes": int }
GET  /api/{version}/{resource}/{api_id}/children/     # → [ { "entity_type", "api_id" }, ... ] (recursive)
GET  /api/{version}/{resource}/{api_id}/thumbnail/    # → { "format", "thumbnail" } (base64)
GET  /api/{version}/{resource}/{api_id}/log/          # Activity log for the entity
GET  /api/{version}/{resource}/{api_id}/log/{log_id}/ # Single enhanced log item
GET  /api/{version}/{resource}/{api_id}/share/user/   # List users the object is shared with
POST /api/{version}/{resource}/{api_id}/share/user/   # { "username", "sharing_enabled" }
GET  /api/{version}/{resource}/{api_id}/share/link/   # Link-sharing status
POST /api/{version}/{resource}/{api_id}/share/link/   # { "enabled": bool }
```

* **`thumbnail/{size}/`** — an optional size segment scales the thumbnail. Pass `?dl=1` (or an `Accept: image/*` header) to receive raw PNG bytes instead of JSON.
* **`log/`** — supports `deep=true` (full activity items, paginated), `exclude_deleted=true`, `deduplicate=true`, `offset`, and `limit`.

### Common query parameters

| Parameter     | Applies to             | Effect                                                                                                |
| ------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- |
| `silent=true` | create, update, delete | Mark the resulting activity-log entry as silent (still recorded, but hidden from most activity views) |
| `dl=1`        | `thumbnail/`           | Return binary PNG instead of JSON                                                                     |

### Common response fields

All core resources include these read-only fields in their serialized form:

* `api_id`, `entity_type`, `size_bytes`
* `created_by`, `updated_by` (usernames), `created_on`, `updated_on`, `last_activity_on`
* `created_on_behalf`, `created_on_behalf_name`, `created_on_behalf_email` *(v1.3+)*

***

## Bootstrap

### Get Bootstrap Data

```
GET /api/{version}/bootstrap/
```

Returns all data needed for initial frontend load in a single request, eliminating multiple round trips.

**Response:** `200 OK`

```json
{
  "user": {...},
  "workspaces": [...],
  "settings": {...},
  "info": {
    "api_version": "string"
  }
}
```

***

## API Info

### Get API Info

```
GET /api/{version}/info/
```

Public endpoint (no authentication required) describing the running server.

**Response:** `200 OK`

```json
{
  "environment": "string",
  "server_version": "string",
  "started": "datetime",
  "api_versions": ["v1", "v1.1", "v1.2", "v1.3", "v1.4", "v1.4.1"],
  "your_ip": "string",
  "ai_enabled": true
}
```

When Auth0 is configured, the response also includes `auth0_domain`, `auth0_spa_client_id`, `auth0_cli_client_id`, and `auth0_audience`.

***

## Site Settings

### Get Site Settings

```
GET /api/{version}/settings/
```

Returns the singleton site-settings object (feature flags, AI configuration, limits). Includes a read-only `ai_supported_models` list of available AI models.

***

## Organizations

Organizations group workspaces and manage shared subscriptions, billing, and membership.

Supports the [standard CRUD routes and common sub-actions](#common-resource-behavior).

**Response fields:** `api_id`, `name`, `description`, `logo` (base64 PNG, nullable), `allow_link_sharing`, `allow_per_workspace_storage_settings`, `workspaces` (read-only, shallow), plus the common timestamp fields.

### Create Organization

```
POST /api/{version}/organization/
```

**Request Body:**

```json
{
  "name": "string",
  "description": "string"
}
```

### Members

```
GET  /api/{version}/organization/{api_id}/members/
POST /api/{version}/organization/{api_id}/members/add/
POST /api/{version}/organization/{api_id}/members/remove/
POST /api/{version}/organization/{api_id}/members/change/
```

Manage organization membership. `members/` returns `[{ "username", "membership_type" }]`. Requires `MANAGE`.

### Subscription

```
GET   /api/{version}/organization/{api_id}/subscription/
POST  /api/{version}/organization/{api_id}/subscription/
PATCH /api/{version}/organization/{api_id}/subscription/
```

Get or change the organization's plan, or toggle `compute_overages_enabled`. Requires `MANAGE`.

### Flexible Storage

```
GET  /api/{version}/organization/{api_id}/storage/
POST /api/{version}/organization/{api_id}/storage/
POST /api/{version}/organization/{api_id}/storage/test/
```

Configure a custom (BYO) storage backend. `storage/test/` validates vendor/credentials without saving.

### Invitations

```
GET /api/{version}/organization/{api_id}/invitations/
```

Lists valid pending invitations for the organization. Requires `MANAGE`. See [Invitations](#invitations) for create/accept.

### Compute Usage

```
GET /api/{version}/organization/{api_id}/compute_usage/?period=YYYY-MM
```

Billable managed-compute cost summary for the given month (defaults to month-to-date). Requires `MANAGE`.

***

## Workspaces

Supports the [standard CRUD routes and common sub-actions](#common-resource-behavior).

**Response fields:** `api_id`, `name`, `description`, `workspace_type`, `organization`, `size_bytes` (read-only), `compute_enabled` (read-only), and — in the full serializer — read-only nested `analyses`, `assets`, and `stories`. The list endpoint returns a lightweight representation in v1.3+.

### List Workspaces

```
GET /api/{version}/workspace/
```

Lists all workspaces the user has access to (directly or via an organization).

### Get Workspace

```
GET /api/{version}/workspace/{api_id}/
```

### Create Workspace

```
POST /api/{version}/workspace/
```

**Request Body:**

```json
{
  "name": "string",
  "description": "string",
  "workspace_type": "secondary"
}
```

Primary workspaces are assigned at user creation and cannot be created through the API.

### Get Workspace Overview

```
GET /api/{version}/workspace/{api_id}/overview/
```

Returns aggregated counts for the workspace.

**Response:** `200 OK`

```json
{
  "analysis_count": 10,
  "figure_count": 45,
  "asset_count": 5,
  "story_count": 3,
  "active_this_week": 2
}
```

### Get Workspace Dashboard

```
GET /api/{version}/workspace/{api_id}/dashboard/
```

Returns all data needed for the home view in a single request.

**Query Parameters:**

* `activity_limit` (integer): Number of activity items (default: 10, max: 50)
* `exclude_deleted` (boolean): Exclude deleted items from activity log
* `deduplicate` (boolean): Collapse activity to the newest entry per target

**Response:** `200 OK`

```json
{
  "workspace": {...},
  "overview": {...},
  "activity_log": {
    "items": [...],
    "has_more": true,
    "total_count": 100,
    "fetched_count": 10
  },
  "stories": [...]
}
```

### Get Recent Activity

```
GET /api/{version}/workspace/{api_id}/recent/?limit=20
```

Returns recently active assets, analyses, and figures (`limit` default 20, max 1000).

### Apply Promotion

```
POST /api/{version}/workspace/{api_id}/promotion/
```

**Request Body:**

```json
{ "promotion_code": "string" }
```

Applies a promotion code and returns the resulting plan. Requires `MANAGE`.

### Members

```
GET  /api/{version}/workspace/{api_id}/members/
POST /api/{version}/workspace/{api_id}/members/add/
POST /api/{version}/workspace/{api_id}/members/remove/
POST /api/{version}/workspace/{api_id}/members/change/
```

### Subscription & Storage

```
GET   /api/{version}/workspace/{api_id}/subscription/
POST  /api/{version}/workspace/{api_id}/subscription/
GET   /api/{version}/workspace/{api_id}/storage/
POST  /api/{version}/workspace/{api_id}/storage/
POST  /api/{version}/workspace/{api_id}/storage/test/
```

Manage the workspace plan and custom storage. Workspaces governed by an organization-level subscription return `409` with `managed_by_organization: true`.

### Compute Instances

```
GET  /api/{version}/workspace/{api_id}/compute_instances/
POST /api/{version}/workspace/{api_id}/compute_instances/
GET  /api/{version}/workspace/{api_id}/compute_instances/launch_options/
GET  /api/{version}/workspace/{api_id}/compute_allowance/
GET  /api/{version}/workspace/{api_id}/compute_usage/
```

List and launch managed-compute instances in the workspace. See [Managed Compute](#managed-compute) for the instance lifecycle and `POST` body. *(v1.4.1+)*

***

## Analyses

An analysis is a container for figures within a workspace.

Supports the [standard CRUD routes and common sub-actions](#common-resource-behavior).

**Response fields:** `api_id`, `name`, `description`, `workspace` (api\_id), `figures` (read-only), `assets` (read-only), `thumbnail` (read-only), `size_bytes`, `is_imported`, `import_source`, `import_source_asset`, plus common fields.

### Create Analysis

```
POST /api/{version}/analysis/
```

**Request Body:**

```json
{
  "name": "string",
  "description": "string",
  "workspace": "uuid"
}
```

***

## Figures

A figure is a named slot within an analysis; its content lives in [figure revisions](#figure-revisions).

Supports the [standard CRUD routes and common sub-actions](#common-resource-behavior).

**Response fields:** `api_id`, `name`, `description`, `analysis` (api\_id), `revisions` (read-only, shallow), `thumbnail` (read-only), `size_bytes`, `auto_assign_pending`, `is_imported`, `import_source`, `import_source_asset`, plus common fields.

### Create Figure

```
POST /api/{version}/figure/
```

**Request Body:**

```json
{
  "name": "string",
  "description": "string",
  "analysis": "uuid"
}
```

***

## Figure Revisions

A figure revision is an immutable snapshot of a figure's image, code, and data.

Supports the [standard CRUD routes and common sub-actions](#common-resource-behavior).

**Response fields:** `api_id`, `short_id`, `figure` (api\_id), `metadata` (JSON), `revision_index` (read-only), `data` (read-only; shallow in v1.2+), `assets` (read-only links), `thumbnail`, `figure_metadata` (read-only context object), `image_hash`, `size_bytes`, `is_processing`, `is_clean_room`, `description`, `description_timestamp`, `description_author`, `is_imported`, `import_source`, `import_source_asset`, plus common fields. `client_id` (UUID) is **write-only** and sets the revision's primary key (v1.4.1+).

### Create Figure Revision

```
POST /api/{version}/revision/
```

**Request Body:**

```json
{
  "figure": "uuid",
  "metadata": {...},
  "short_id": "string",        // optional; prefix must be reserved by the user
  "is_clean_room": false,
  "data": [                    // ExternalData objects (base64 data)
    { "name": "string", "type": "image", "metadata": {...}, "data": "base64" }
  ]
}
```

Data is processed asynchronously — poll the [`status`](#get-revision-status) action.

### Get Revision Status

```
GET /api/{version}/revision/{api_id}/status/
```

**Response:**

```json
{ "is_processing": false }
```

### Comment Count

```
GET /api/{version}/revision/{api_id}/comment_count/
```

Returns the number of top-level comments: `{ "count": int }`.

### Generate Description (AI)

```
POST /api/{version}/revision/{api_id}/generate_description/
```

Generates an AI description for the revision and stores it (author set to the `ai` system user). Requires AI to be enabled. Returns the updated revision.

### Derive Revision

```
POST /api/{version}/revision/{api_id}/derive/
```

Creates a new revision by cloning the source revision's data objects (same storage paths), returning the new revision immediately with its `api_id` so a watermark can be generated before uploading image data via [`append_data`](#append-data).

**Request Body (all optional):**

```json
{
  "figure": "uuid",       // target figure; defaults to the source's figure
  "metadata": {...}
}
```

### Append Data

```
POST /api/{version}/revision/{api_id}/append_data/
```

Appends data objects to an existing revision without deleting existing data (used after `derive` to upload watermarked images, code, and manifests).

**Request Body:**

```json
{ "data": [ /* ExternalData objects with base64 data */ ] }
```

### Auto-Assign Revision (AI)

```
POST /api/{version}/revision/auto-assign/
```

Creates a revision under a temporary figure and dispatches AI tasks to assign it to the most appropriate figure in the analysis.

**Request Body:**

```json
{
  "analysis": "uuid",
  "metadata": {...},
  "data": [ /* ExternalData objects */ ]
}
```

***

## Assets

Assets are reusable data objects (datasets, tables, files) that can be linked to figures.

Supports the [standard CRUD routes and common sub-actions](#common-resource-behavior).

**Response fields:** `api_id`, `name`, `description`, `workspace` (api\_id), `analysis` (api\_id, optional), `revisions` (read-only, shallow), `thumbnail`, `size_bytes`, plus common fields.

### Create Asset

```
POST /api/{version}/asset/
```

**Request Body:**

```json
{
  "name": "string",
  "description": "string",
  "workspace": "uuid",
  "analysis": "uuid"   // optional
}
```

### Find by Name

```
POST /api/{version}/asset/find_by_name/
```

Finds assets by exact name, optionally scoped to an analysis.

**Request Body:**

```json
{
  "name": "string",
  "analysis": "uuid"   // optional
}
```

**Response:** `200 OK` — JSON array of matching assets the user can view.

***

## Asset Revisions

An asset revision is an immutable snapshot of an asset's data.

Supports the [standard CRUD routes and common sub-actions](#common-resource-behavior).

**Response fields:** `api_id`, `asset` (api\_id), `metadata` (JSON), `data` (read-only, shallow), `revision_index`, `thumbnail`, `data_hash`, `figure_revisions` (read-only links), `size_bytes`, `is_processing`, plus common fields. `client_id` (UUID) is **write-only**.

### Create Asset Revision

```
POST /api/{version}/asset_revision/
```

**Request Body:**

```json
{
  "asset": "uuid",
  "metadata": {...},
  "data": [ /* ExternalData objects with base64 data */ ]
}
```

### Get Revision Status

```
GET /api/{version}/asset_revision/{api_id}/status/
```

**Response:** `{ "is_processing": false }`

### Find by Hash

```
POST /api/{version}/asset_revision/find_by_hash/
```

Finds asset revisions by content hash. Used for deduplication during sync.

**Request Body:**

```json
{
  "hash_type": "blake3",
  "digest": "string",
  "analysis": "uuid"  // Optional
}
```

* `hash_type` *(required)*: Hash algorithm. Currently only `blake3` is supported.
* `digest` *(required)*: The hex-encoded content hash.
* `analysis` *(optional)*: API ID of an analysis. When provided, only revisions whose parent asset belongs to that analysis are returned. When omitted, returns **all** matching revisions workspace-wide, including both scoped and unscoped assets. Note: omitting `analysis` does **not** filter to unscoped-only assets.

**Response:** `200 OK` — JSON array of matching asset revision objects, or an empty array if none found.

**Python Client:** `AssetRevision.find_by_hash(digest, hash_type="blake3", analysis=None)` **R Client:** `find_asset_revision_by_hash(gf, digest, hash_type="blake3")`

### Unlink Figure

```
POST /api/{version}/asset_revision/{api_id}/unlink_figure/
```

Unlinks a figure revision from an asset revision.

**Request Body:**

```json
{
  "figure_revision": "uuid",
  "anchor": "string",  // Optional
  "delete_figure_revision": false  // Optional
}
```

***

## External Data

Read-only access to the individual data objects (images, code, tables, manifests) attached to figure and asset revisions. Data objects are created and updated through their parent revision, not directly — `POST`, `PUT`, `PATCH`, and `DELETE` on this resource return `405 Method Not Allowed`.

```
GET /api/{version}/data/{api_id}/
```

**Response fields:** `api_id`, `name`, `type`, `metadata` (JSON), `data` (base64; null when shallow), `size_bytes`, `hash`, `is_clean_room`.

### Storage Info

```
GET /api/{version}/data/{api_id}/storage/
```

Returns the storage backend `vendor`, `hash`, and (for admins or third-party storage) the `path` of the underlying object. Requires `MANAGE` on the workspace.

***

## Stories

Stories are AI-generated presentations from figure collections.

### List Stories

```
GET /api/{version}/story/
```

***

### Get Story

```
GET /api/{version}/story/{api_id}/
```

**Response:**

```json
{
  "api_id": "uuid",
  "name": "string",
  "description": "string",
  "workspace": "uuid",
  "revisions": [...],
  "slides": [...],
  "created_on": "datetime",
  "updated_on": "datetime"
}
```

***

### Create Story

```
POST /api/{version}/story/
```

**Request Body:**

```json
{
  "name": "string",
  "description": "string",
  "workspace": "uuid",
  "revisions": ["uuid", "uuid", ...],  // Figure revision IDs
  "generate": false                     // Optional; if true, dispatch AI generation
}
```

When `generate` is `true`, the story is created and an asynchronous generation task is dispatched; the response story carries a `generation_status` of `generating` (poll via the [Tasks](#tasks) API).

***

### Update Story

```
PUT /api/{version}/story/{api_id}/
PATCH /api/{version}/story/{api_id}/
```

**Request Body:**

```json
{
  "name": "string",
  "description": "string",
  "revisions": ["uuid", ...],  // Replaces all revisions
  "slides": [  // Replaces all slides
    {
      "slide_id": "string",
      "slide_type": "title|introduction|figure|goals|data|conclusion|custom",
      "position": 0,
      "content": "string",
      "revision_id": "uuid",  // For figure slides
      "slide_data": {...}
    }
  ]
}
```

For figure slides, server-owned AI fields (`methods`, `data`, `availability`) are preserved if omitted from the client payload.

***

### Delete Story

```
DELETE /api/{version}/story/{api_id}/
```

***

## Comments

Comments support Markdown, @mentions, and threading.

### List Comments

```
GET /api/{version}/comment/?target_type={type}&target_id={uuid}
```

**Query Parameters (required):**

* `target_type`: `asset`, `asset_revision`, `figure`, or `figure_revision`
* `target_id`: API ID of the target object

**Response:** `200 OK`

```json
[
  {
    "id": "uuid",
    "user": {...},
    "content": "string",
    "parent_comment_id": "uuid|null",
    "is_edited": false,
    "created_on": "datetime",
    "updated_on": "datetime"
  }
]
```

***

### Create Comment

```
POST /api/{version}/comment/
```

**Request Body:**

```json
{
  "target_type": "figure_revision",
  "target_id": "uuid",
  "content": "Great analysis! @username what do you think?",
  "parent_comment_id": "uuid"  // Optional, for replies
}
```

Mentions using `@username` trigger email notifications.

***

### Update Comment

```
PUT /api/{version}/comment/{id}/
```

Only the comment author can edit. Sets `is_edited: true`.

**Request Body:**

```json
{
  "content": "Updated comment text"
}
```

***

### Delete Comment

```
DELETE /api/{version}/comment/{id}/
```

Only the comment author can delete.

***

### Create AI Response

```
POST /api/{version}/comment/{parent_id}/create_ai_response/
```

Creates an AI-authored reply to the specified comment. The AI generates the response asynchronously.

**Response:** `201 Created` - The empty AI comment (content populated async)

***

## Reactions

Emoji reactions on comments.

### List Reactions

```
GET /api/{version}/reaction/?comment_id={uuid}
```

***

### Create Reaction

```
POST /api/{version}/reaction/
```

**Request Body:**

```json
{
  "comment_id": "uuid",
  "emoji": "👍"
}
```

***

### Delete Reaction

```
DELETE /api/{version}/reaction/{id}/
```

***

## Deep Insight (AI)

AI-powered analysis of figures using Amazon Bedrock. All Deep Insight endpoints require AI to be enabled on the site (`503` otherwise) and are subject to plan-based rate limiting and token quotas (`429` when exhausted).

### Query Deep Insight

```
POST /api/{version}/deep_insight/
```

**Request Body:**

```json
{
  "figure_revision": "uuid",
  "prompt": "Explain this figure",
  "stream": false,
  "model": "us.amazon.nova-pro-v2:0"  // Optional
}
```

**Response (non-streaming):**

```json
{
  "response": "string"
}
```

**Response (streaming):** Server-Sent Events

***

### Compare Revisions

```
POST /api/{version}/deep_insight/compare/
```

**Request Body:**

```json
{
  "left_revision": "uuid",
  "right_revision": "uuid",
  "stream": true
}
```

***

### Extract Figure Code

```
POST /api/{version}/deep_insight/figure_code/
```

Extracts the code that generated a figure using AI.

**Request Body:**

```json
{
  "figure_revision": "uuid"
}
```

**Response:**

```json
{
  "code": "string"
}
```

***

### Edit Figure Code (AI)

```
POST /api/{version}/deep_insight/code_edit/
```

Agentic AI editing of a clean-room figure's code. Accepts a conversation and returns the assistant's reply along with revised, validated code.

**Request Body:**

```json
{
  "figure_revision": "uuid",
  "messages": [ {...} ],
  "current_code": "string",
  "figure_image": "data:image/png;base64,...",  // optional
  "available_packages": ["numpy", "pandas"],     // optional
  "model": "string"                              // optional
}
```

**Response:**

```json
{
  "role": "assistant",
  "content": "string",
  "code": "string",
  "valid": true,
  "error": null,
  "tool_calls": [...]
}
```

***

### Get Datasets

```
POST /api/{version}/deep_insight/datasets/
```

Returns data inputs/outputs for a figure revision.

**Response:**

```json
{
  "external_data": [...],
  "ai_inputs": [...],
  "ai_outputs": [...]
}
```

***

### Text-Only Query

```
POST /api/{version}/deep_insight/text_only/
```

AI query without a figure (text-only context).

**Request Body:**

```json
{
  "text": "string",
  "prompt": "string"
}
```

***

### Check Availability

```
POST /api/{version}/deep_insight/check/
```

Checks if AI can process a figure (permissions, rate limits).

**Response:**

```json
{
  "available": true,
  "messages": []
}
```

***

### Story Generation Endpoints

Optimized AI endpoints used to build and refine [stories](#stories).

```
POST /api/{version}/deep_insight/story/figure/
POST /api/{version}/deep_insight/story/overviews/
POST /api/{version}/deep_insight/story/refine/
```

Synchronous helpers that generate per-figure content, story overviews, and refinements.

#### Generate Single Slide

```
POST /api/{version}/deep_insight/story/slide/generate/
```

**Request Body:**

```json
{
  "story_id": "uuid",
  "slide_type": "introduction|goals|data|conclusion|figure|...",
  "revision_id": "uuid",   // required for figure slides
  "model": "string"        // optional
}
```

**Response (figure slide):** `{ "content", "methods", "inputs", "outputs" }` **Response (overview slide):** `{ "content", "figure_descriptions", "figure_methods" }`

#### Adjust Slide Detail

```
POST /api/{version}/deep_insight/story/slide/detail/
```

Rewrites a slide with more or less detail.

**Request Body:**

```json
{
  "story_id": "uuid",
  "slide_id": "string",
  "direction": "more",      // "more" | "less"
  "current_content": "string",
  "model": "string"          // optional
}
```

**Response:** `{ "content": "string" }`

#### Generate Full Story (Async)

```
POST /api/{version}/deep_insight/story/generate/
```

Submits asynchronous generation of an entire story. Cancels any in-flight generation and sets the story's `generation_status` to `generating`.

**Request Body:** `{ "story_id": "uuid" }` **Response:** `{ "task_id": "uuid" }` — poll via the [Tasks](#tasks) API.

#### Refine Full Story (Async)

```
POST /api/{version}/deep_insight/story/refine_async/
```

Submits asynchronous refinement of a story (`generation_status` becomes `refining`).

**Request Body:** `{ "story_id": "uuid" }` **Response:** `{ "task_id": "uuid" }`

***

## Search

### Search

```
POST /api/{version}/search/
```

Unified search across workspaces, analyses, figures, figure revisions, assets, and asset revisions. Results are filtered by per-object permissions.

**Request Body:**

```json
{
  "search_type": "keyword",   // "keyword" | "text" | "semantic" | "image"
  "query": "string",          // for keyword/text/semantic
  "keywords": ["string"],      // alternative to query (joined)
  "image": "base64",           // required for image search
  "k": 10,                     // optional max results (default 10)
  "workspace": "uuid"          // optional; restrict to one workspace
}
```

* **`keyword` / `text`** — full-text search across all indexed entity types, with highlight snippets.
* **`semantic`** — cross-modal (Nova/Bedrock) k-NN search over figure revisions; requires `query`.
* **`image`** — reverse-image search; requires a base64-encoded `image`.

**Response:** `200 OK` — array sorted by descending score:

```json
[
  {
    "score": 0.87,
    "object": {...},        // shallow-serialized matching entity
    "highlight": {...}       // per-field match snippets (keyword only)
  }
]
```

Returns `400` on a missing or unsupported `search_type`, or a missing query.

***

## Managed Compute

Cloud notebook/IDE instances managed by GoFigr. *(Available in v1.4.1+.)* Instances are **listed and launched** through their workspace, then **operated** through the `compute/instance/` routes.

### List / Launch Instances

```
GET  /api/{version}/workspace/{api_id}/compute_instances/
POST /api/{version}/workspace/{api_id}/compute_instances/
GET  /api/{version}/workspace/{api_id}/compute_instances/launch_options/
GET  /api/{version}/workspace/{api_id}/compute_allowance/
GET  /api/{version}/workspace/{api_id}/compute_usage/?period=YYYY-MM
```

**Launch Request Body:**

```json
{
  "name": "string",          // required
  "tier": "string",           // optional tier id
  "data_volume_gb": 50         // optional
}
```

`launch_options/` returns the available tiers and limits; `compute_allowance/` and `compute_usage/` report remaining allowance and billable usage.

### Instance Lifecycle

```
GET   /api/{version}/compute/instance/{api_id}/
PATCH /api/{version}/compute/instance/{api_id}/
POST  /api/{version}/compute/instance/{api_id}/start/
POST  /api/{version}/compute/instance/{api_id}/stop/
POST  /api/{version}/compute/instance/{api_id}/terminate/
POST  /api/{version}/compute/instance/{api_id}/change-tier/
GET   /api/{version}/compute/instance/{api_id}/connect/
GET   /api/{version}/compute/instance/{api_id}/heartbeat/
GET   /api/{version}/compute/instance/{api_id}/events/
GET   /api/{version}/compute/instance/{api_id}/usage/?period=YYYY-MM
```

* **`PATCH`** updates `name`, `idle_shutdown_enabled`, and `idle_shutdown_seconds`.
* **`start` / `stop` / `terminate`** drive the lifecycle (`202 Accepted`). `start` may return `402 allowance_exhausted` or `409 tier_unavailable`.
* **`change-tier`** — body `{ "tier": "string" }`; the instance must be stopped (`409` otherwise).
* **`connect`** — body-less `GET` (so a browser can navigate directly). Query `app` (default `jupyter`). Returns `{ "redirect_url", "expires_in_seconds" }` with a short-lived signed URL. Owner only.
* **`heartbeat`** (GET) — latest supervisor status: `{ "heartbeat_at", "heartbeat": { "jupyter_up", "active_kernel_count", "cpu_pct", "mem_pct", "disk_pct", ... } }`.

A `404` is returned for both nonexistent and inaccessible instances.

**Instance fields (selected):** `api_id`, `name`, `tier`, `workspace`, `owner`, `user_facing_status`, `lifecycle`, `health`, `status_detail`, `last_activity_at`, `running_since`, `idle_shutdown_enabled`, `idle_shutdown_seconds`, `idle_seconds`, `can_operate`, `can_connect`, `can_terminate`, `created_at`, `updated_at`.

### Tier Catalog

```
GET /api/{version}/compute/tiers/
```

Lists the public compute-tier catalog (label, vCPU, memory, description).

***

## Tasks

Background task tracking for user-initiated asynchronous work (story generation, imports, etc.). *(Available in v1.3+.)* Read-only; scoped to the authenticated user's own tasks.

### List Tasks

```
GET /api/{version}/tasks/
```

**Query Parameters:** `status__in`, `created_at__gte`, `created_at__lte`, `completed_at__gte`, `completed_at__lte`.

### Get Task

```
GET /api/{version}/tasks/{task_id}/
```

**Response fields:** `task_id`, `task_name`, `task_type`, `status`, `progress`, `status_message`, `task_detail`, `result`, `error`, `created_on`, `started_on`, `completed_on`. Cancelled tasks report status `REVOKED`.

### Task Logs

```
GET /api/{version}/tasks/{task_id}/logs/
```

**Response:** `{ "task_id": "uuid", "logs": [...] }`

### Cancel Task

```
POST /api/{version}/tasks/{task_id}/cancel/
```

Revokes the task in Celery and marks it cancelled. Returns `400` if the task already succeeded, failed, or was revoked.

***

## Users

Users are addressed by **username** (not UUID).

**Response fields:** `username`, `email`, `first_name`, `last_name`, `date_joined`, `is_active`, `is_staff`, `avatar`, `email_confirmed`, `using_auth0`, `user_profile`. Sensitive fields (`email`, `user_profile`, `email_confirmed`, `is_active`, `is_staff`) are stripped when viewing a user other than yourself.

### List / Search Users

```
GET /api/{version}/user/?q={query}
```

### Get User

```
GET /api/{version}/user/{username}/
```

### Create User

```
POST /api/{version}/user/
```

**Request Body:**

```json
{
  "username": "string",
  "email": "string",
  "password": "string",
  "first_name": "string",
  "last_name": "string"
}
```

**Response:** `201 Created` — the created user plus an `auth` object containing JWT tokens.

### Update User

```
PUT   /api/{version}/user/{username}/
PATCH /api/{version}/user/{username}/
```

Self only. Accepts `email`, `first_name`, `last_name`, `password`, `avatar`, and a nested `user_profile` payload.

### Contributions

```
GET /api/{version}/user/{username}/contributions/
```

Returns the authenticated user's activity counts per date over the last 365 days.

### Verify Email

```
POST /api/{version}/user/{username}/verify_email/
```

With a `token` in the body, confirms the user's email. Without a token, generates and emails a new verification token.

***

## Plans

### List Plans

```
GET /api/{version}/plan/
```

Lists available subscription plans (read-only).

**Response fields:** `api_id`, `name`, `description`, `monthly_cost`, `annual_cost`, `max_storage_bytes`, `max_users`, `allow_flexible_storage`, `monthly_token_quota`, `deep_insight_rate`, `compute_enabled`, `compute_max_running_instances`, `idle_shutdown_required`, `max_idle_seconds`, `default_idle_seconds`, `effective_max_data_volume_gb`.

***

## API Keys

Programmatic access tokens (used by the Python and R clients). For security, API keys can only be **created or deleted** with username/password (JWT) authentication — not while authenticated with an API key.

### List API Keys

```
GET /api/{version}/api_key/
```

**Response:** array of `{ "api_id", "name", "token": null, "expiry", "last_used", "created", "workspace" }`. The secret `token` is **never** returned on list/retrieve.

### Get API Key

```
GET /api/{version}/api_key/{api_id}/
```

### Create API Key

```
POST /api/{version}/api_key/
```

**Request Body:**

```json
{
  "name": "string",        // required, unique per user
  "workspace": "uuid",      // optional; scopes the key to one workspace
  "expiry": "datetime"      // optional; must be in the future
}
```

**Response:** `201 Created`

```json
{
  "api_id": "uuid",
  "name": "string",
  "token": "string",       // the secret — returned ONLY on create
  "expiry": "datetime",
  "last_used": null,
  "created": "datetime",
  "workspace": "uuid",
  "user": "string"
}
```

### Delete API Key

```
DELETE /api/{version}/api_key/{api_id}/
```

***

## SSH Keys

Manage SSH keys for Git repository imports.

### List SSH Keys

```
GET /api/{version}/ssh_key/
```

**Response:**

```json
[
  {
    "api_id": "uuid",
    "name": "string",
    "fingerprint": "SHA256:...",
    "is_default": true,
    "created_on": "datetime"
  }
]
```

***

### Add SSH Key

```
POST /api/{version}/ssh_key/
```

**Request Body:**

```json
{
  "name": "My Git Key",
  "private_key": "-----BEGIN OPENSSH PRIVATE KEY-----...",
  "is_default": false
}
```

Note: Private key is stored encrypted. Never returned in API responses.

***

### Update SSH Key

```
PATCH /api/{version}/ssh_key/{api_id}/
```

**Request Body:**

```json
{
  "name": "New Name",
  "is_default": true
}
```

***

### Delete SSH Key

```
DELETE /api/{version}/ssh_key/{api_id}/
```

***

## Git Repository

### Check Repository Access

```
POST /api/{version}/git/check/
```

Validates Git repository URL and authentication.

**Request Body:**

```json
{
  "url": "https://github.com/user/repo.git",
  "ssh_key_id": "uuid"  // Optional, for SSH URLs
}
```

***

## Data Upload

### Upload User Data

```
POST /api/{version}/user_data_upload/
```

Upload and process files (Git repos, PowerPoint, Word docs).

***

## Invitations

Invite users to a workspace or organization. The same routes exist for both targets — substitute `workspace` or `organization` for `{target}`.

### Create Invitation

```
POST /api/{version}/invitations/{target}/
```

Requires `MANAGE` on the target. An email is sent to the recipient.

**Request Body:**

```json
{
  "workspace": "uuid",         // or "organization": "uuid"
  "email": "string",
  "membership_type": "string",
  "expiry": "datetime"          // optional; capped to a maximum TTL
}
```

**Response:** `201 Created` — the serialized invitation. The `token` field is only populated in debug builds.

### Get Invitation

```
GET /api/{version}/invitations/{target}/{token}
```

Looks up an invitation by its token (any valid invite) or by api\_id (requires `MANAGE`). Returns `404` if expired or not found.

### Accept Invitation

```
POST /api/{version}/invitations/{target}/{token}/accept
```

Adds the requesting user to the target with the invitation's membership type and consumes the invite.

**Response:** `200 OK` — `{ "message": "Invitation accepted successfully" }`. Returns `400` if the collaborator limit has been reached.

### Delete Invitation

```
DELETE /api/{version}/invitations/{target}/{api_id}
```

Revokes a pending invitation (the path segment must be the invitation's api\_id). Requires `MANAGE`.

***

## Billing

Stripe-backed subscription management. Every request targets exactly one of `workspace_api_id` or `organization_api_id`; the caller needs `MANAGE` on the target. Organization-managed workspaces are rejected with `409` and `managed_by_organization: true`.

### Create Checkout Session

```
POST /api/{version}/billing/checkout/
```

Starts a Stripe Checkout session for a new paid subscription.

**Request Body:**

```json
{
  "plan_api_id": "uuid",
  "frequency": "monthly",          // or "annual"
  "workspace_api_id": "uuid"        // or "organization_api_id"
}
```

**Response:** `200 OK` — `{ "checkout_url", "session_id" }`. Returns `409` if an active subscription already exists (use change-plan).

### Open Billing Portal

```
POST /api/{version}/billing/portal/
```

Returns a Stripe Customer Portal URL.

**Request Body:**

```json
{ "workspace_api_id": "uuid" }     // or "organization_api_id"
```

**Response:** `200 OK` — `{ "portal_url" }`. Returns `409` if there is no Stripe customer yet.

### Change Plan

```
POST /api/{version}/billing/change-plan/
```

Switches an existing paid subscription to a different plan or billing frequency (with proration).

**Request Body:**

```json
{
  "plan_api_id": "uuid",
  "frequency": "annual",
  "workspace_api_id": "uuid"        // or "organization_api_id"
}
```

**Response:** `200 OK` — `{ "subscription_id", "plan_api_id", "frequency", "message" }`.

***

## Auth0

Endpoints for accounts managed via Auth0. All return `503` when Auth0 is not configured.

### Resend Verification Email

```
POST /api/{version}/auth0/resend-verification/
```

Resends the email-verification message for the bearer-token user. **Response:** `{ "detail": "Verification email sent" }`.

### Change Password

```
POST /api/{version}/auth0/change-password/
```

Returns a password-change ticket URL. Body (optional): `{ "return_url": "string" }`. **Response:** `{ "url": "string" }`.

### MFA Status

```
GET /api/{version}/auth0/mfa-status/
```

**Response:** `{ "enrolled": true, "methods": ["..."] }`.

### MFA Enroll

```
POST /api/{version}/auth0/mfa-enroll/
```

Returns a Guardian enrollment ticket URL. **Response:** `{ "url": "string" }`.

***

## Short IDs

Short IDs are compact, shareable identifiers for figure revisions. Clients reserve a unique prefix, then append a locally generated base62 index.

### Reserve Prefix

```
POST /api/{version}/short_id_prefix/reserve
```

Reserves a unique short-ID prefix for the authenticated user.

**Response:** `201 Created` — `{ "prefix": "string" }`.

### Resolve Short ID

```
GET /api/{version}/resolve/{short_id}
```

Resolves a short ID to a figure revision. No authentication is required, but the revision's `VIEW` permission is still enforced.

**Response:** `200 OK` — `{ "api_id": "uuid", "short_id": "string" }`. Returns `404` for both nonexistent and inaccessible revisions (to avoid leaking which short IDs are valid).

***

## AI Usage

### Get AI Usage

```
GET /api/{version}/ai/usage/
```

Returns AI usage statistics and quota information.

***

## Metadata Proxy

Short-lived secret tokens that let a running notebook/Jupyter host push figure metadata to GoFigr regardless of origin (open CORS, secured by a ≤60-second token). Used by real-time capture integrations.

### Create Token

```
POST /api/{version}/metadata/
```

Creates a token (requires authentication). Optional `expiry` (capped to a maximum TTL). **Response:** `201 Created` — the serialized token including its secret `token` value.

### Read Token

```
GET /api/{version}/metadata/{token}
```

Returns the token object. Only the user who created it may read it.

### Push Metadata

```
POST /api/{version}/metadata/{token}
```

Stores metadata against the token.

**Request Body:** `{ "metadata": {...} }`

***

## API Version History

| Version    | Key Changes                                                                                             |
| ---------- | ------------------------------------------------------------------------------------------------------- |
| **v1.4.1** | Managed compute API, client-provided revision IDs (`client_id`), shallow create responses for revisions |
| **v1.4**   | AI-generated titles and descriptions (`ai_title`, `ai_description` fields)                              |
| **v1.3**   | Tasks API, Comments, Reactions, `created_on_behalf` fields, optimized workspace list                    |
| **v1.2**   | Lazy-loaded revision data (data returned separately, not inline)                                        |
| **v1.1**   | Nested objects in responses (analyses/figures return full objects, not just IDs)                        |
| **v1**     | Original API                                                                                            |

### v1.4.1 (Latest)

* **Managed Compute:** Cloud notebook/IDE instances (`/compute/instance/`, workspace launch routes)
* **Client-provided IDs:** Revisions accept a write-only `client_id` to set their primary key
* **Shallow create responses:** Figure/asset revision creation returns a lightweight representation

### v1.4

* Added `ai_title` and `ai_description` fields to figures and revisions
* AI-generated descriptions for imported content

### v1.3

* **Tasks API:** Background task tracking (`/tasks/` endpoint)
* **Comments & Reactions:** Full collaboration features
* **Attribution:** `created_on_behalf`, `created_on_behalf_name`, `created_on_behalf_email` fields
* **Performance:** ShallowWorkspaceSerializer for list operations

### v1.2

* **Revision data:** Data objects returned via separate fetch, not inline with revision
* Improves performance for large revisions

### v1.1

* **Nested objects:** Workspace responses include full analysis objects (not just IDs)
* Analysis responses include full figure objects

### v1

* Original API with core CRUD operations

***

## Notes

1. **API Versioning:** Use `v1.4.1` for new integrations.
2. **Data Processing:** Revisions are processed asynchronously. Check the `status` endpoint.
3. **Shallow Representations:** List endpoints and v1.2+ revision data return lightweight objects; fetch full data separately.
4. **Silent Operations:** Use `?silent=true` to mark an operation's activity entry as silent — it is still recorded but hidden from most activity views.
5. **Base64 Encoding:** Binary data (images, files) is base64-encoded.
6. **Rate Limiting:** AI endpoints are rate-limited based on plan.

***

**Last Updated:** June 2026\
**API Versions Supported:** v1, v1.1, v1.2, v1.3, v1.4, v1.4.1


# Clean Room for Data Scientists

**Version 1.0 | March 2026**

***

## The Gap Between Exploration and Delivery

Data science moves fast. A typical analysis starts as a few lines in a notebook, grows into a sprawling series of cells with experiments, dead ends, and half-commented code blocks—each one a record of the thinking that got you to the answer. That's not messiness. That's the scientific process.

The problem isn't the exploration. The problem is what happens *after* you've found something worth sharing.

You've built something valuable: a model, a visualization, an analysis that decision-makers need to understand. Now you face a different kind of work—translating that discovery into something reproducible, explainable, and interactive. Usually that means:

* Cleaning up the notebook to make it presentable
* Answering endless "what if we changed X?" questions from stakeholders
* Rerunning the whole analysis every time someone wants a slightly different cut
* Hoping whoever runs it next has the same package versions you did

GoFigr Clean Room is built to close that gap—without disrupting how you actually work.

***

## What Clean Room Does

Clean Room transforms a Python function into a self-contained, browser-based interactive application. You write a function, decorate it with `@reproducible`, and that's it. GoFigr creates a clean boundary between this function and the rest of the notebook: it only has access to the variables you give it, the packages you declare, and nothing else.

Now, when you call this function and it produces a plot or a figure, the function and the figure get packaged together and published as an interactive web application. The whole process is seamless and automatic—GoFigr supports fully automatic figure capture in Jupyter.

***

## How It Works

### Setup: One Magic, One Decorator

In your notebook, load the GoFigr extension once. This enables automatic capture whenever a `@reproducible` function runs:

```python
# Cell 1 — imports and GoFigr setup
from typing import Literal
import seaborn as sns

%load_ext gofigr  # enables automatic figure capture and injects reproducible, SliderParam, DropdownParam, etc.
```

Then decorate your analysis function with `@reproducible(interactive=True)`:

```python
# Cell 2 — define the analysis as a reproducible function

@reproducible(interactive=True)
def flipper_length_distribution(
    data,                    # DataFrames are passed in from the notebook -- not embedded in source
    bins: int     = SliderParam(20, min=5, max=100, step=5),
    alpha: float  = SliderParam(0.7, min=0.1, max=1.0, step=0.05),
    show_kde: Literal["yes", "no", "auto"] = "yes",  # Literal types become dropdowns automatically
    species: str  = DropdownParam("Adelie", choices=["Adelie", "Chinstrap", "Gentoo"]),
    show_grid: bool = True,  # booleans become checkboxes
    title: str    = "Flipper Length Distribution"
):
    filtered = data[data['species'] == species]
    kde = True if show_kde == "yes" else (False if show_kde == "no" else None)

    ax = sns.histplot(
        data=filtered,
        x='flipper_length_mm',
        bins=bins,
        alpha=alpha,
        kde=kde,
    )
    ax.set_title(title)
    if show_grid:
        ax.grid(True, alpha=0.3)


# Cell 3 — call it like a normal function
# GoFigr captures the output and packages everything automatically
flipper_length_distribution(penguins_df)
```

That's the entire integration. There's no separate publish step, no export, no post-processing. Running the function in Jupyter is all it takes.

### What Gets Captured

When the function runs, GoFigr captures:

* **Source code** — the function body, extracted cleanly from the notebook cell
* **Parameters** — types, defaults, and widget configuration for every parameter
* **Data** — DataFrames passed as arguments, serialized and stored alongside the revision
* **Environment** — package names and versions, imports, Python version
* **Output** — the figures produced by the run

Each run creates a new revision. Every revision is immutable and traceable. You always know exactly what produced a given figure.

***

## Parameters and Widgets

GoFigr maps Python types to interactive controls automatically. You can use type annotations and `Literal` for the common cases, or use explicit parameter classes when you need more control:

| Type                               | Widget              | Example                                               |
| ---------------------------------- | ------------------- | ----------------------------------------------------- |
| `int` / `float` with `SliderParam` | Slider with bounds  | `bins: int = SliderParam(20, min=5, max=100, step=5)` |
| `str` with `Literal[...]`          | Dropdown (inferred) | `show_kde: Literal["yes", "no", "auto"] = "yes"`      |
| `str` with `DropdownParam`         | Dropdown (explicit) | `species = DropdownParam("Adelie", choices=[...])`    |
| `bool`                             | Checkbox            | `show_grid: bool = True`                              |
| Free-form `str`                    | Text input          | `title: str = "My Chart"`                             |
| `pd.DataFrame`                     | Static (read-only)  | Passed in at call time, available in studio           |

One thing worth noting: `data` is passed in at call time from your notebook, not hardcoded in the function. GoFigr serializes it automatically. The function stays general; the data is bound to the specific revision.

***

## The Provenance Model

Every time a Clean Room figure is re-run with new parameters and saved, a new revision is created that is:

* **Linked to the original** — the full revision history is preserved
* **Watermarked** — each output image contains a QR code linking back to the exact revision that produced it
* **Parameterized** — the parameter values used for that run are stored with the revision

This means you can answer "where did this chart come from?" with precision: the code, the data, the packages, the parameter values, the timestamp, and the user who ran it.

No more hunting through Slack to find which version of the notebook produced the slide in the board deck.

***

## The Workflow

1. **Explore** — work however you normally work in Jupyter: experiment freely, iterate fast.
2. **Distill** — pull the core logic into a `@reproducible` function. This is the moment of crystallization: you're extracting the essential analysis from the surrounding scaffolding.
3. **Run** — call the function as normal. GoFigr captures and packages everything automatically.
4. **Share** — enable link sharing and send the URL to whoever needs it.

From that point, stakeholders interact with the Clean Room studio directly. They adjust sliders, change dropdowns, re-run—and optionally save the result as a new revision. You get notified. You don't have to re-run anything yourself unless the underlying logic needs to change.

***

## The Studio Environment

When someone opens a Clean Room figure, they land in the studio:

* **Code editor** — the full function source, editable, with syntax highlighting
* **Parameter panel** — generated controls for every parameter
* **Figure output** — live rendering of whatever the function produces
* **Console** — stdout and stderr from the execution
* **Environment inspector** — browse live variables, preview DataFrames, inspect imports
* **AI assistant** — request code modifications in natural language

The runtime runs in the browser via WebAssembly Python. Packages are installed on-demand. No server-side execution, no infrastructure to manage.

***

## Supported Visualization Backends

Clean Room supports the output formats you're already using:

* **Matplotlib / Seaborn** — PNG, SVG, HTML
* **Plotly** — interactive HTML figures (coming soon)
* **Plotnine** — ggplot2-style static plots

***

## What You Don't Have to Do Anymore

Once a function is a Clean Room figure:

* You don't rerun the analysis every time someone wants a different filter
* You don't share notebooks with a page of setup instructions
* You don't maintain a separate "presentable" version of your notebook for stakeholders
* You don't try to reconstruct which parameters produced a specific output six months later

The exploration lives in your notebook. The deliverable lives in GoFigr.

***

## When to Use Clean Room

Clean Room is the right tool when:

* The analysis will be revisited with different parameters, by you or someone else
* Stakeholders need to explore "what if" scenarios without your involvement
* Reproducibility matters (regulatory, audit, or internal review)
* You want to deliver an interactive result, not a static slide

It's less appropriate for:

* Pure exploration that won't be revisited
* Analyses that depend on local databases or custom infrastructure not available in browser Python
* Code that requires packages not available in WebAssembly Python

***

## Summary

Clean Room lets you move from rapid, exploratory iteration to a shareable, reproducible, interactive asset without changing how you work in Jupyter. The `@reproducible` decorator captures your function's full context—code, data, parameters, environment—automatically when you run it. Every subsequent run produces a traceable revision. Stakeholders interact directly with the studio, adjusting parameters and re-running without your involvement.

Your exploration stays exploratory. Your deliverables become durable.

***

*GoFigr Clean Room —* [*gofigr.io*](https://gofigr.io)


# Clean Room for Data Science Leaders

**An Executive Briefing for VPs of Data Science, Chief Science Officers, and Data Leaders**

**Version 1.0 | March 2026**

***

## The Productivity Gap Nobody Talks About

Your data science team is capable. They produce good work. But somewhere between the analysis and the decision, there's friction.

A stakeholder asks a question. It goes to a data scientist. The data scientist re-runs the analysis, exports a new chart, drops it in a Slack message or email. The stakeholder asks a follow-up. The cycle repeats.

This isn't a people problem. It's a structural one. Every time someone outside the team needs a different cut of an analysis, it creates an interrupt. Those interrupts accumulate into a significant drag on both the data science team and the decision-makers waiting on them.

GoFigr Clean Room is designed to break this cycle.

***

## What Clean Room Is

When a data scientist produces a figure, GoFigr.io captures it automatically. Clean Room then packages everything needed to reproduce it: the analysis logic, the computational environment, and the underlying data. No extra steps, no separate documentation. It happens as a natural part of how the team already works.

The result is that a figure in a GoFigr workspace isn't a static image anymore. That ROC curve, that revenue breakdown, that cohort analysis—each one becomes a live, interactive scientific asset. Stakeholders can open it in their browser, download the underlying data, adjust parameters, and re-run the analysis themselves. No software to install. And no coding required.

***

## The Business Case

### Reducing Interrupt Load on Data Science Teams

The demand for data insights doesn't decrease as organizations mature - it grows. Without a scalable delivery mechanism, more demand means more interrupts for work that doesn't require data science expertise: "Can you re-run this for Q2?" "What does this look like for the EMEA region?" "Can you show me the same thing with a 90-day window instead of 30?"

Clean Room lets data scientists answer these questions once. Every figure they produce is already a Clean Room asset—they simply share the link. The stakeholder adjusts the parameters themselves. The data scientist is not in the loop unless the underlying logic needs to change.

At scale, this changes the economics of analytical delivery. A single Clean Room figure can serve dozens of stakeholders across multiple teams, time zones, and use cases—with no marginal cost to the data science team after the analysis is first produced.

### Stakeholders in the Analysis, Not in the Queue

Decision-makers who can directly explore an analysis make better decisions faster. Rather than waiting for a revised chart, they can immediately test their own hypotheses—adjusting a threshold, switching a time window, filtering by region—and see the result in seconds. They can download the underlying data directly from the figure and work with it however they need to.

This isn't about replacing data scientists. It's about reserving their time for work that actually requires it: methodology, model development, new analyses, and interpreting complex results. Stakeholders can perform smaller tweaks to existing results by themselves, without having to wait in the queue.

### A Reproducibility Record Built Into the Workflow

Regulatory environments, audit requirements, and internal governance frameworks increasingly require organizations to demonstrate that analytical outputs are reproducible and traceable. Answering "where did this number come from?" is often harder than it should be.

Because Clean Room packaging happens automatically as figures are produced, every figure carries a complete provenance record as a matter of course—no separate documentation process, no reliance on individual team members to remember what they did. Every output is watermarked with a QR code that links back to the exact analysis that produced it. The record is always complete and always accurate.

### Protecting Institutional Knowledge

Data science work is fragile institutional knowledge. When a data scientist leaves or moves teams, the analyses they built often become opaque: figures divorced from the logic that produced them, results that can't be reproduced, institutional memory that walks out the door.

Because Clean Room assets are self-contained—analysis, environment, and data stored together—any figure can be understood and re-run by someone who wasn't involved in creating it. A new team member, an auditor, or a manager reviewing historical work can open any Clean Room figure and immediately see what was done, with what data, and arrive at the same result.

***

## How It Works

As a data scientist produces figures through GoFigr, Clean Room automatically captures three things:

1. **The analysis logic** — the exact steps that produced the output
2. **The environment** — the versions of every tool and library in use at the time
3. **The data** — the underlying datasets the analysis depends on

These three components travel with the figure. When a stakeholder opens it, everything needed to re-run the analysis is already there—loaded directly in their browser, with no server-side computation and no data leaving the session unless they explicitly choose to publish a result.

Stakeholders interact through a clean studio interface: controls for adjusting parameters (time ranges, filters, thresholds, categories), a live preview that updates as they explore, and the ability to download the underlying data at any point. If they find a result worth preserving, one click publishes it as a new version, linked to the original and carrying the full chain of provenance forward.

***

## How Clean Room Fits the Data Science Workflow

Clean Room integrates with existing workflows rather than replacing them. Data scientists continue working in the tools they prefer. As they work, every figure they produce becomes a Clean Room asset automatically—no additional process, no extra work.

The moment a figure is produced is the handoff. Before that, the work is exploratory and internal. Once captured, the analysis becomes an organizational asset: versioned, shareable, and independently operable by anyone with access.

This creates a clean boundary between exploration and delivery that most data science organizations lack today. Exploratory work stays internal and fluid. Delivered analyses are stable, traceable, and accessible.

***

## Governance and Access Control

GoFigr provides granular control over who can access Clean Room figures:

* **Link sharing** — generate a URL that allows anyone with the link to view and interact with the figure, without authentication
* **User sharing** — share directly with specific users within the platform
* **Publish permissions** — only users with appropriate access can publish new versions; view-only stakeholders can explore and download but cannot modify the version history

Organizations can configure sharing policies to match their data governance requirements. Sensitive analyses can remain internal while broadly relevant figures are shared via link.

***

## Key Metrics for Evaluating Impact

| Metric                                   | What It Captures                                               |
| ---------------------------------------- | -------------------------------------------------------------- |
| Stakeholder-initiated re-runs per figure | Volume of self-service activity displacing data scientist work |
| Time from question to answer             | Reduction in turnaround for parameter variation requests       |
| Figure coverage with full provenance     | Analytical outputs that can be traced and reproduced on demand |
| Cross-team figure access                 | Reuse of analyses across organizational boundaries             |

***

## What Clean Room Is Not

Clean Room is not a business intelligence tool or a dashboard platform. It is not designed to replace Tableau, Power BI, or Looker for operational reporting.

Clean Room is designed for the analytical layer: bespoke analyses, model outputs, experimental results, and research findings that data scientists produce and that stakeholders need to explore interactively. It closes the gap between "we ran the analysis" and "the organization can act on it."

***

## Summary

| Challenge                                                        | Clean Room Response                                                                    |
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| Data scientists interrupted by parameter variation requests      | Stakeholders self-serve via browser-based studio                                       |
| Static figures disconnected from the analysis that produced them | Every figure automatically packages logic, environment, and data                       |
| Reproducibility gaps in audit or review scenarios                | Provenance captured automatically as figures are produced, watermarked on every output |
| Institutional knowledge lost when team members leave             | Self-contained analytical assets persisted in the platform                             |
| Stakeholders blocked from the underlying data                    | Download data directly from any Clean Room figure                                      |

Clean Room turns each analytical output from a one-time artifact into a durable organizational asset—one that stakeholders can operate independently, that auditors can trace completely, and that the data science team doesn't have to maintain manually.

***

*GoFigr — Reproducible data science at the speed of discovery* [*gofigr.io*](https://gofigr.io) *| <contact@gofigr.io>*


# What's New

Stay up to date with the latest GoFigr features and improvements.

**Current versions:** Server v3.5.0 • Web App v3.5.0 • Python v2.3.3 • R v2.0.2

## Recent Highlights

### [May–June 2026 — Managed Compute](/releases/2026-06)

GoFigr now runs your code, not just your figures:

* ☁️ **Managed Compute** — launch cloud machines and work in browser-based Jupyter, code-server, or R Server with GoFigr built in
* 🔌 **Full lifecycle** — tiers, idle auto-shutdown, restart, and soft-delete with restore
* 💳 **Self-service billing** — Stripe checkout, plan switching, compute allowances & overages, and per-period statements
* 📊 **Usage visibility** — compute and storage consumption with billable intervals, in-app
* 🔎 **Semantic Search** — find figures by meaning, not just keywords
* 🔔 **Notification inbox** — in-app alerts for compute and billing events

### [April 2026 — Auth0 & Clean Room R Support](/releases/2026-04)

Major updates to authentication and the Clean Room Studio experience:

* 🔐 **Auth0 Authentication** — Auth0 login on the web app, MFA enrollment, and `gfconfig` device-code flow
* 🧪 **Clean Room R Support** — In-browser WebR runtime and R language support in the AI code editor
* 🔄 **Manual Sync Analysis** — Explicit Sync Analysis button replaces background auto-tracking
* 🤖 **Smarter AI Code Editor** — Auto-retry on failure, `publish()` parameter awareness, richer context
* 🐍 **Python Client Polish** — Short ID watermarks, broader `@reproducible` container support

### [March 2026 — Clean Room & Studio](/releases/changelog)

Introducing Clean Room Studio, a major new capability for reproducible, interactive figures:

* 🧪 **Clean Room Studio** — In-browser Python execution with Pyodide, interactive parameter widgets, and one-click publishing
* 🤖 **AI Code Editing** — AI-powered chat panel for editing figure code in Studio
* 🐍 **Python `@reproducible` Decorator** — Capture source code, parameters, and dependencies with every figure
* 📦 **Pyodide Support** — Publish figures from browser-based Python environments
* ⚡ **Faster Jupyter Startup** — API v1.4 with shallow workspace serialization

### [January 2026](/releases/2026-01)

Major release introducing AI-powered features and enhanced collaboration:

* 🤖 **AI Story Mode** — Transform figures into presentations
* 💬 **Comments & Collaboration** — Team feedback with @mentions
* 📂 **Git Repository Import** — Import notebooks from GitHub/GitLab
* 📄 **Document Import** — Extract figures from PowerPoint & Word
* 🔍 **Enhanced Search** — Text and visual similarity search
* 🏢 **Workspace Improvements** — Better organization and navigation

***

## Full Changelog

For a complete version history, see the per-component changelogs:

* [Server Changelog](/releases/changelog/server)
* [Web App Changelog](/releases/changelog/webapp)
* [Python Client Changelog](/releases/changelog/python)
* [R Client Changelog](/releases/changelog/r)

| Version                                         | Date         | Highlights                                                                             |
| ----------------------------------------------- | ------------ | -------------------------------------------------------------------------------------- |
| Server v3.5.0 / Web App v3.5.0                  | Jun 26, 2026 | R Server on compute, soft-delete & restore, per-GB storage billing, notification inbox |
| Server v3.4.0 / Web App v3.4.0                  | Jun 13, 2026 | Semantic search                                                                        |
| Server v3.3.0 / Web App v3.3.0                  | Jun 11, 2026 | Compute allowances & overages, admins operate instances                                |
| Server v3.2.0 / Web App v3.2.0                  | Jun 4, 2026  | Compute usage views, 5-tier catalog, launch options                                    |
| Server v3.1.0 / Web App v3.1.0                  | May 29, 2026 | Self-service Stripe billing, plan switching                                            |
| Server v3.0.0 / Web App v3.0.0                  | May 20, 2026 | Managed Compute launch (browser Jupyter & code-server)                                 |
| Python v2.3.3                                   | Jun 1, 2026  | New notebooks auto-enable GoFigr                                                       |
| R v2.0.0                                        | Apr 7, 2026  | Clean Room, Auth0 device login, auto-assign                                            |
| Web App v2.4.0 / Python v2.3.1 / Server v2.12.0 | Apr 7, 2026  | Manual Sync Analysis, AI auto-retry, WebR publish() params, Clean Room polish          |
| Web App v2.3.0 / Server v2.11.4                 | Apr 3, 2026  | R language support in Clean Room code editor, Studio polish                            |
| Web App v2.2.7                                  | Apr 2, 2026  | WebR Clean Room runtime                                                                |
| Server v2.10.0 / Web App v2.2.5 / Python v2.2.0 | Mar 28, 2026 | Auth0 authentication, MFA, gfconfig device login                                       |
| Server v2.9.0 / Web App v2.2.4                  | Mar 23, 2026 | Auto-assign titles, AI chat UX, short IDs, caching fixes                               |
| Python v2.1.0 / Web App v2.2.2                  | Mar 19, 2026 | Simplified Jupyter install, NotebookResolver, MIME fix                                 |
| Server v2.7.0 / Web App v2.2.1                  | Mar 17, 2026 | Analysis-scoped assets, asset filter UX, sticky header                                 |
| Server v2.6.0 / Web App v2.2.0                  | Mar 10, 2026 | Story Mode tool calling, async generation, model updates                               |
| Python v2.0.2                                   | Mar 12, 2026 | auto\_configure switch, client-side UUIDs                                              |
| Server v2.5.2 / Web App v2.1.2                  | Mar 4, 2026  | AI code editing, Studio fixes                                                          |
| Server v2.5.0 / Web App v2.0.0                  | Mar 2, 2026  | Clean Room Studio, derive endpoints                                                    |
| Python v2.0.1                                   | Mar 5, 2026  | Graceful anywidget fallback, bare @reproducible                                        |
| Python v2.0.0                                   | Mar 5, 2026  | @reproducible decorator, Pyodide, API v1.4                                             |
| Server v2.4.6 / Web App v1.9.6                  | Jan 20, 2026 | Bug fixes, infinite scroll fix                                                         |
| Server v2.4.4 / Web App v1.9.5                  | Jan 18, 2026 | Performance optimizations                                                              |
| Server v2.4.2 / Web App v1.9.3                  | Jan 16, 2026 | AI Story Mode improvements, custom slides                                              |
| Server v2.4.0 / Web App v1.9.0                  | Dec 2025     | AI Story Mode, Comments, Git Import                                                    |

***

## Stay Updated

* Follow [@gofigr](https://twitter.com/gofigr) for announcements
* Subscribe to release notifications in your GoFigr settings


# Changelog

GoFigr consists of four components, each with its own release cycle:

| Component                    | Current Version | Changelog                                       |
| ---------------------------- | --------------- | ----------------------------------------------- |
| **Server** (API & Backend)   | v3.5.0          | [Server Changelog](/releases/changelog/server)  |
| **Web App** (Frontend)       | v3.5.0          | [Web App Changelog](/releases/changelog/webapp) |
| **Python Client** (`gofigr`) | v2.3.3          | [Python Changelog](/releases/changelog/python)  |
| **R Client** (`gofigR`)      | v2.0.2          | [R Changelog](/releases/changelog/r)            |

***

## Server

The GoFigr server provides the REST API, data storage, AI features, Managed Compute, billing, and background processing. Server releases include new API endpoints, performance improvements, and backend features.

[View Server Changelog →](/releases/changelog/server)

## Web App

The GoFigr web application is the browser-based frontend for viewing, sharing, and interacting with figures. Web App releases include UI improvements, new views, and client-side features like Clean Room Studio.

[View Web App Changelog →](/releases/changelog/webapp)

## Python Client

The `gofigr` Python package provides Jupyter integration, programmatic figure publishing, and the `@reproducible` decorator for Clean Room support.

[View Python Changelog →](/releases/changelog/python)

## R Client

The `gofigR` R package provides R and RStudio integration for publishing figures to GoFigr, with support for ggplot2, base R graphics, and Shiny.

[View R Changelog →](/releases/changelog/r)


# Server

All notable changes to the GoFigr Server (API & Backend).

**Current version:** v3.5.0

***

## June 2026

### v3.5.0 — June 26, 2026

* **R Server on Managed Compute** — R Server joins Jupyter and code-server as a third in-browser editor on compute instances
* **Soft delete with recovery** — deleted compute instances can be restored within a grace window; storage billing pauses while an instance is pending deletion
* **Per-GB storage billing** for compute data volumes, itemized in the per-instance usage view and on billing-period statements
* **Compute billing history** — customer-facing period statements
* **Notification inbox** — in-app notifications for compute events (idle, ready, error, reboot) and billing events (payment failed, subscription canceled)
* Auto-shutdown configurable at launch; plan-scoped tier allowlists and pricing in launch options; user-triggered reboot to apply staged OS security updates; optional confirmed-email gate before launch

### v3.4.4 — June 18, 2026

* Deep Insight now analyzes clean-room (re-published) figures by gathering their code, instead of returning empty

### v3.4.3 — June 18, 2026

* More accurate search on matplotlib figures — label text is reconstructed from the figure's vector glyphs instead of guessed via OCR

### v3.4.2 — June 18, 2026

* General improvements

### v3.4.1 — June 18, 2026

* Compute over-allowance warnings are softened when overages are enabled, so opted-in users aren't blocked

### v3.4.0 — June 13, 2026

* **Semantic search** — search figures by meaning, not just keywords (`search_type=semantic`), powered by multimodal embeddings
* Search now indexes figure code, AI descriptions, and figure names/descriptions; keyword results include highlighted match context
* Fixed short-ID watermark resolution

### v3.3.0 — June 11, 2026

* **Compute allowances & overages** — a monthly included compute allowance, enforced at launch/start, with opt-in overage billing and a clear upgrade path when it's exhausted
* Compute usage reconciled to the Stripe invoice and shown against the billing month
* Workspace and organization admins can operate and terminate compute instances they didn't launch

### v3.2.0 — June 4, 2026

* Expanded compute tier catalog (5 tiers) with live pricing surfaced before launch
* **Compute usage** read endpoints so you can see your consumption
* Per-plan data-volume size caps shown in launch options
* New "starting services" instance state during boot
* code-server instances ship with the GoFigr extension pre-installed and the Python interpreter pre-configured; new notebooks auto-enable GoFigr

### v3.1.1 — June 1, 2026

* Compute instances show an in-page idle-shutdown warning and a post-stop summary, and surface the last-started time

***

## May 2026

### v3.1.0 — May 29, 2026

* **Self-service billing** — Stripe Checkout, billing portal, and self-cancellation
* Plan switching for paying customers, with a graceful downgrade to Free
* Promo code support at checkout
* Larger upload limits on compute instances (up to \~500 MB)

### v3.0.1 — May 21, 2026

* Compute and idle-shutdown plan limits are now shown on plan details

### v3.0.0 — May 20, 2026

Major release introducing **Managed Compute**:

* **Managed Compute** — launch cloud machines and work in browser-based Jupyter or code-server, with GoFigr credentials provisioned automatically
* Instance lifecycle — start, stop, and terminate; named tiers (Standard, Pro) instead of raw instance types
* **Idle auto-shutdown** — running instances stop automatically when idle, with plan-driven limits
* Per-instance live status, activity tracking, and event log; session/expiry banners and reconnect handling

***

## April 2026

### v2.12.2 — April 7, 2026

* Backend performance improvements (faster short ID lookups, faster cold starts)

### v2.12.1 — April 7, 2026

* Backend stability improvements

### v2.12.0 — April 6, 2026

* **`publish()` parameter docs** — AI code editor is now aware of `width`, `height`, and `dpi` arguments in `publish()`
* **Improved AI chat context** — latest figure image is sent after conversation history for better grounding
* Backend stability and infrastructure improvements

### v2.11.4 — April 3, 2026

* **R language support in Clean Room code editor** — agentic code editing now supports R workspaces
* **Figure image and package context** added to the code edit agent
* Backend stability and infrastructure improvements

***

## March 2026

### v2.11.2 — March 31, 2026

* Fixed null `figure_id` in activity log after auto-assign moves a revision to another figure
* Backend stability and infrastructure improvements

### v2.10.0 — March 28, 2026

Major release introducing **Auth0 authentication**:

* **Auth0 JWT authentication** — dual-mode authentication (legacy + Auth0)
* **MFA enrollment and status endpoints** — self-service MFA from user settings
* **User settings Auth0 integration** — claim sync, password change, and email update flows
* **Auth0 client IDs exposed via `/info`** — separate SPA and CLI client IDs for frontends and command-line tools
* Figure title generation now uses figure code context for better suggestions

### v2.9.0 — March 23, 2026

* **Auto-assign revision endpoint** — New endpoint for AI-powered figure title assignment; revisions can be automatically moved to matching existing figures
* **Task progress reporting** — Story generation now reports detailed progress (completed slides, last content) through all generation phases
* Fixed processing flag not clearing after data task completion
* Fixed stale foreign key race condition in description saves
* Backend stability and infrastructure improvements

### v2.8.0 — March 20, 2026

* **Short ID support for revisions** — Figure revisions can now be resolved by short ID for compact sharing URLs
* Fixed short ID enumeration vulnerability by checking VIEW permission
* Backend stability and performance improvements

### v2.7.0 — March 16, 2026

* **Analysis-scoped asset lookups** — Asset and AssetRevision API endpoints (find\_by\_name, find\_by\_hash) now accept an analysis filter; analysis field added to Asset serializers
* Fixed activity aggregate timezone shift causing dates to display one day off in western timezones
* Backend stability and infrastructure improvements

### v2.6.0 — March 10, 2026

Major release improving Story Mode generation:

* **Improved story generation** — More reliable figure analysis, overview generation, and refinement
* **Updated Claude model registry** — Removed Claude 3, added Claude 4.6 and Haiku 4.5
* **Overview slide figure reordering** — LLM can now suggest improved narrative flow for figure slides
* **Async story generation** — Story generation and refinement now run in the background
* Fixed task cancellation not working during story generation
* Fixed model selection not being respected across story generation endpoints
* Fixed size double-counting in `append_data` endpoint

### v2.5.2 — March 3, 2026

* **AI code editing for Studio** — Agentic code editing for Clean Room figures
* Use Claude Sonnet 4.5 as default model for AI Figure Assistant
* Fixed derive endpoint cloning stale code into derived revisions

### v2.5.1 — March 2, 2026

* Backend stability improvements

### v2.5.0 — March 2, 2026

Major release supporting Clean Room Studio:

* **New `derive` and `append_data` endpoints** for studio figure publication
* **Clean Room support** — New field to distinguish Clean Room figures
* **Python 3.12 compatibility**
* Fixed derive endpoint excluding code/DataFrame objects
* Derived revision activity log entries are now visible

***

## February 2026

### v2.4.12 — February 6, 2026

* Security and stability improvements

### v2.4.11 — January 28, 2026

* Enabled anonymous access to site settings

***

## January 2026

### v2.4.10 — January 22, 2026

* Use first figure slide for story thumbnail

### v2.4.9 — January 22, 2026

* Added `og:article:author` property to OG tags
* Fixed: Use `X-Forwarded-Host` header for correct host behind proxies
* OG tag tweaks

### v2.4.8 — January 20, 2026

* Return analysis info in figure revision endpoint

### v2.4.7 — January 20, 2026

* Fixed: Deny Organization access for workspace-scoped API keys
* Fixed `generate_description` endpoint persisting errors
* Fixed description author/timestamp update on manual edit

### v2.4.6 — January 20, 2026

* Bug fixes and stability improvements

### v2.4.5 — January 19, 2026

* Bug fixes and stability improvements

### v2.4.4 — January 18, 2026

* **Performance optimizations** — Faster workspace and thumbnail loading, story thumbnail support

### v2.4.3 — January 16, 2026

* Enabled anonymous AI access for public stories
* Fixed thumbnail size handling

### v2.4.2 — January 16, 2026

* **AI metering** — Added anonymous user support for AI features
* Improved security for comment endpoints

### v2.4.1 — January 14, 2026

* Bug fixes and stability improvements

***

## December 2025

### v2.4.0 — December 2025

Major release introducing:

* **AI-Powered Story Mode** — Transform figures into presentations
* **Comments & Collaboration** — Team feedback with @mentions
* **Git Repository Import** — Import notebooks from GitHub/GitLab
* **Document Import** — Extract figures from PowerPoint & Word
* **Enhanced Search** — Text and visual similarity search

See [January 2026 Feature Summary](/releases/2026-01) for detailed descriptions.

### v2.3.0 — December 2025

* Initial Deep Insight integration
* Workspace management improvements
* Document assistant enhancements


# Web App

All notable changes to the GoFigr Web App (Frontend).

**Current version:** v3.5.0

***

## June 2026

### v3.5.0 — June 26, 2026

* **R Server** — connect to R Server in the browser, alongside Jupyter and code-server
* Richer launch flow — cost estimate and ETA in the launch modal, a cold-launch progress stepper, and auto-shutdown set at launch
* Instance controls — manual Restart, "restart needed" chips and banners with a user-triggered reboot, and deferred delete with a "Pending deletion" state plus Restore
* **Notification bell inbox** for in-app notifications
* Email-verification banner for new sign-ups; a compute billing-history card; storage usage in the billable-usage drill-down
* New **/upgrade** landing page for compute upsells and an open-source notices page at **/licenses**

### v3.4.1 – v3.4.3 — June 18, 2026

* The over-allowance compute banner is softened when overages are enabled, so it no longer warns users who have opted in

### v3.4.0 — June 13, 2026

* **Semantic Search** mode on the search page, with match-context snippets on result cards and code matches rendered in formatted blocks

### v3.3.0 — June 11, 2026

* Redesigned compute home — a Quick Actions card, instance cards that order running instances first, and your own instances shown by default with a "Show all" toggle
* Allowance UI — overages toggle, low-allowance and upsell banners, and one-click paths to plan and overage controls
* Billing card now shows all period statements, with billed-usage and credit/net breakdowns

### v3.2.0 — June 4, 2026

* **Compute Usage** cards on workspace and organization views, plus a per-instance "Billable usage" breakdown of usage intervals
* Smarter launch modal — default tier pre-selected, tiers and data-volume caps from the backend, micro/performance tiers labeled
* Clearer lifecycle — a distinct "starting services" state, connect buttons that activate only once an instance is reachable, and a confirmation prompt before stopping
* Hosted editor is consistently labeled "code-server"; inline Start/Stop actions
* In-page idle-shutdown warnings and a post-stop summary

***

## May 2026

### v3.1.1 — May 30, 2026

* Refreshing the browser now reliably shows up-to-date figures and data instead of stale cached content

### v3.1.0 — May 29, 2026

* New **billing** experience — paid checkout, the Stripe customer portal, and a plan picker, with a graceful downgrade to Free
* Subscription card on the organization view; site-wide banners for canceled and past-due/unpaid subscriptions

### v3.0.1 — May 21, 2026

* General improvements

### v3.0.0 — May 20, 2026

Major release introducing the **Managed Compute** UI:

* **Managed Compute** in the sidebar — launch instances from a modal with named tiers, then connect to Jupyter and code-server from the instance list
* Manage instances — stop/start/terminate, rename, and a "Live" column for reachability and disk usage; terminated instances hidden by default
* Per-instance event log and idle auto-shutdown controls, with captions explaining why an instance stopped
* Toast notifications replace blocking browser alerts across compute and task flows

***

## April 2026

### v2.4.2 — April 7, 2026

* Minor Clean Room fixes

### v2.4.1 — April 7, 2026

* Workspace dropdown is now always visible so users can create new workspaces from anywhere

### v2.4.0 — April 7, 2026

* **Manual Sync Analysis** — replaces auto-tracking with an explicit "Sync Analysis" button and modal; story config auto-saves before refine/recreate
* **Auto-retry AI code changes** — on execution failure or no output, the AI automatically retries with runtime and `publish()` details added to the feedback prompt
* **WebR `publish()` parameters** — `width`, `height`, and `dpi` are now supported in the WebR shim
* Removed short ID resolution layer — server now accepts short IDs directly
* Added refresh buttons to revision view, revision selector, and task overview panel
* Hide spurious scrollbar on AI chat input
* Updated empty-slide copy: "No content for this slide yet" / "Create with AI"
* Fixed unwanted page scroll when slides update or when clicking Create with AI / Edit manually
* Fixed `ReferenceError` in CleanRoomStudio caused by `availablePackages` ordering
* Fixed sync duplicates, stuck loading state, and various copy tweaks
* Suppress auto-scroll to new slides during sync; stop polling on unmount
* Fixed workspace selector race condition on navigation
* Fixed null revision crash in Sync Analysis modal

### v2.3.0 — April 3, 2026

* **Clean Room Studio polish** — richer AI context, base R graphics support, UX tweaks
* Fixed DPI mismatch between preview and publish in Clean Room Studio
* Updated bundled `gofigr` wheel to v2.2.0 (short\_id watermarks, consistent image sizing)

### v2.2.7 — April 2, 2026

* **WebR Clean Room runtime** — in-browser R execution with JS-side publishing and watermarking
* WebR polish: package preloading, cache invalidation, DataFrame preview, UI fixes
* Real-time capture wizard: hide API key when using `gfconfig`, upgrade pip command
* Switched Pyodide QR code library from `pyqrcodeng`+`pypng` to `qrcode`

***

## March 2026

### v2.2.6 — March 31, 2026

* **Workspace link in revision view** — workspace selector now syncs on navigation
* **Real-time capture wizard examples** — added data tracking and Clean Room examples
* Added raw JSON toggle and `formatValue` helper to the Clean Room manifest viewer
* Auto-save story config before refine and recreate
* Fixed Auth0 registration and invitation flows
* Fixed `jsToPythonLiteral` to handle arrays and objects with correct Python syntax
* Show logout button instead of retry on account conflict errors
* Handle Auth0 email verification and account conflict errors gracefully

### v2.2.5 — March 28, 2026

Frontend Auth0 migration:

* **Auth0 login** — full switch to Auth0 with dual-mode support; legacy auth UI removed
* **Live MFA enrollment status** on the user profile page
* **User settings refresh for Auth0** — password change, logout fix, and claim-based fields
* Real-time capture wizard improvements; activity deduplication moved server-side

### v2.2.4 — March 23, 2026

* **Auto-assign title polling** — Revision view shows a shimmer placeholder with countdown while AI assigns a figure title, then updates in place when complete
* **Friendlier AI assistant wording** — Interactive AI chat uses approachable language ("Ask a question or describe a change..." instead of "Describe a code change...")
* **Improved analysis breadcrumb** — Larger, darker text with "Analysis:" prefix for better visibility in revision view
* **Story generation progress** — Rolodex-style content scroller and task detail progress in story generation view
* **Clean Room badge** — Badge indicator and lazy descriptions for Clean Room figures
* Fixed unwanted page scroll when opening AI chat in Clean Room
* Fixed AI chat messages causing page overscroll
* Fixed stale descriptions in figure table after in-place refresh
* Fixed cache race condition causing duplicate API calls
* Fixed stale workspace data after cache invalidation
* Fixed navigation loop in create entity view
* Fixed back-navigation not refreshing analyses list and home dashboard

### v2.2.3 — March 20, 2026

* **Short ID resolution** — Shared revision URLs now support compact short IDs
* **Modernized analysis view** — Visual consistency with analyses list, card styling
* **Redesigned Data Files tab** — ModernFileListItem cards with skeleton loading
* **Inline image toolbar** — Replaced floating toolbar with inline toolbar above figure
* Restyled source line with left-accent bar and tinted background
* Fixed UI stuck in loading state after cancelling story generation

### v2.2.2 — March 18, 2026

* Fixed image MIME type for Chrome compatibility — replaced bare `data:image;base64` with `data:image/png;base64` for thumbnails and dynamic format from metadata for full images

### v2.2.1 — March 17, 2026

* **Asset list analysis filter** — Searchable autocomplete dropdown with analyses grouped by "With files" (showing count) and "No files", sorted by most recent
* **Analysis filtering on assets** — Filter assets by analysis; show analysis badge on asset cards/rows; "View analysis" link on asset detail
* **Sticky header** — Changed header from fixed to sticky positioning, eliminating top offset hacks
* Fixed analysis view tab margins and card background

### v2.2.0 — March 10, 2026

* **Async story generation** — Stories now generate in the background with phase-aware progress tracking (figure analysis, overview generation, refinement)
* **Code snippets in data slides** — Dataset slides now display inline code snippets when no URL is available
* **Figure reordering from overviews** — LLM-suggested narrative flow reorders figure slides automatically
* **Updated Claude model registry** — Removed Claude 3, added Claude 4.6 and Haiku 4.5
* Redirect presentation mode to editor when story generation is in progress
* Removed deprecated `promptData` system (content generation is now fully server-side)
* Fixed Pyodide runtime resetting when description updates
* Fixed model persistence and selection across story UI
* Fixed PowerPoint footer logo aspect ratio

### v2.1.2 — March 4, 2026

* Refresh JWT before publish in Pyodide Studio

### v2.1.1 — March 4, 2026

* Fixed null analysis crash when viewing link-shared figures as logged-in user

### v2.1.0 — March 3, 2026

* **AI chat panel** for agentic code editing in Studio
* Improved AI Figure Assistant empty state and placeholder contrast
* Moved AI chat to right sidebar in full Studio
* Fixed split height bug
* Pass current code to PyodidePublisher on publish

### v2.0.1 — March 3, 2026

* Added unified **UserDataTable** component for user-uploaded tabular data
* Show collapsible stack trace on error page
* Removed opacity on Clean Room fallback image

### v2.0.0 — March 2, 2026

Major release introducing **Clean Room Studio**:

* **Pyodide execution** — Run Python in the browser with interactive widgets
* **Studio IDE** — Mini IDE with code editor, environment tab, and memory display
* **Figure publication** — Debounced Pyodide publishing with countdown timer
* **Parameter panel** — Helper panel for parameter variables above code editor
* **Publish flow** — Error modals, stale content UX, and publish parameter management
* Stabilized figure size across publish/unpublish states
* Clean Room badge/link UI and fallback image
* SVG image format support for Pyodide

***

## February 2026

### v1.9.16 — February 7, 2026

* Release and infrastructure updates

### v1.9.15 — February 7, 2026

* Merge updates from develop

### v1.9.14 — February 6, 2026

* Handle paginated response from admin user analytics endpoint

### v1.9.13 — February 6, 2026

* Added anonymous user activity logging support

### v1.9.12 — January 28, 2026

* Enabled anonymous Deep Insight access
* Fixed asset revision links
* Added Dockerfile healthcheck to prevent premature routing
* Unified Jupyter notebook preview components

***

## January 2026

### v1.9.11 — January 23, 2026

* Enabled evidence in presentation mode
* Fixed modal sizing

### v1.9.10 — January 23, 2026

* Editable story titles
* Fixed spacing in edit mode
* Fixed slide re-ordering not updating positions

### v1.9.8 — January 22, 2026

* Handle `/story/{uuid}` without `/p` suffix for crawlers
* Fixed: Preserve `X-Forwarded-*` headers from upstream proxy (AWS ALB)
* Added nginx crawler detection for server-side OG tags
* Removed broken `/workspace/api_id` links

### v1.9.7 — January 20, 2026

* **Shareable revision presentation view** with Open Graph meta tags
* **Evidence finder** feature for Story Mode
* Added analysis navigation link below figure title
* Figure-level actions and section headers in actions menu
* Restored missing revision view features (actions menu, source info, alerts)
* Fixed mobile UI issues in revision view
* Fixed watermark toggle and description persistence in presentation view

### v1.9.6 — January 19, 2026

* Fixed infinite scroll bug in Recent Figures

### v1.9.5 — January 18, 2026

* **Performance optimizations**
  * Optimize home screen loading
  * Add story thumbnail support
* Added infinite scroll for Recent Figures section

### v1.9.4 — January 16, 2026

* Enable anonymous AI access for public stories
* Fixed thumbnail size handling

### v1.9.3 — January 16, 2026

* **AI Story Mode improvements**
  * Added custom slides support
  * Added Figure Quick View modal for presentations
  * Added generation progress indicator
  * Enable re-adding sections to stories
  * Fixed slide height issues

### v1.9.2 — January 14, 2026

* Fixed error handling in story orchestrator
* Bug fixes and stability improvements

***

## December 2025

### v1.9.0 — December 2025

Major release introducing:

* **AI-Powered Story Mode** — Transform figures into presentations
* **Comments & Collaboration** — Team feedback with @mentions
* **Git Repository Import** — Import notebooks from GitHub/GitLab
* **Document Import** — Extract figures from PowerPoint & Word
* **Enhanced Search** — Text and visual similarity search

See [January 2026 Feature Summary](/releases/2026-01) for detailed descriptions.

### v1.8.0 — December 2025

* Initial Deep Insight integration
* Workspace management improvements
* Document assistant enhancements


# Python Client

All notable changes to the GoFigr Python client (`gofigr`).

**Current version:** v2.3.3

***

## v2.3.3 — June 1, 2026

* **New notebooks auto-enable GoFigr** — a Jupyter contents manager turns on capture for newly created notebooks automatically (used on Managed Compute instances)

***

## v2.3.2 — May 13, 2026

* Added `GoFigr.request()`, a typed HTTP helper for calling the GoFigr API from your own code

***

## v2.3.1 — April 7, 2026

Patch release with Clean Room execution improvements.

### Improvements

* **Auto-injected `publish`** — `publish()` is now available in Clean Room code execution without an explicit import
* **`extra_globals` threading** — extra globals are now propagated through Clean Room execution

### Bug Fixes

* Fixed package install names for several Clean Room dependencies

***

## v2.3.0 — April 7, 2026

Minor release improving watermark sizing and `@reproducible` ergonomics.

### Improvements

* **Short ID watermarks** — watermark size calculation now uses short IDs, producing smaller and cleaner QR codes
* **QR library migration** — switched from `pyqrcodeng` to `python-qrcode`
* **`@reproducible` container support** — decorator now handles tuples, numpy arrays, and nested containers

### Bug Fixes

* Fixed `pad_for_watermark` widening images beyond the figure's intended width
* Fixed `PosixPath` serialization error in `FileData.read`

***

## v2.2.0 — March 29, 2026

Minor release introducing **Auth0 device login** in `gfconfig` and short ID watermark support.

### Highlights

* **Auth0 Device Authorization Flow** — `gfconfig` now supports Auth0 device-code login, automatically opening the browser
* **`gfconfig` UX improvements** — clearer API key prompt and auto-launch of the verification URL
* **Short ID support for watermarks** — produces compact QR codes
* **Auto-assign integration** — detects when AI is disabled and skips title auto-assignment
* **Matplotlib DPI capture** — clean room manifest now records matplotlib DPI for accurate Pyodide rendering

### Breaking Changes

* **Removed `--legacy` flag from `gfconfig`** — legacy auth path is no longer supported in the configuration tool

### Bug Fixes

* `configure()` now resets state so the extension doesn't appear ready from a prior autoconfig run

***

## v2.1.0 — March 19, 2026

Major release replacing the JupyterLab frontend extension with kernel-side notebook detection.

### Highlights

* **Simplified installation** — `pip install gofigr` no longer requires Node.js or JupyterLab build tools
* **`NotebookResolver`** — New unified detection chain (VSCode, Databricks, JPY\_SESSION\_NAME, JS Proxy) with resolution logging for debugging
* **JPY\_SESSION\_NAME detection** — Notebook name resolution now completes in under 1ms via the environment variable set by `jupyter_server` (JupyterLab 3+, Notebook 7+, JupyterHub)
* **Analysis-scoped asset sync** — File syncs (`gf.sync`) are now deferred when using `NotebookName()` until the analysis resolves, preventing unscoped assets
* **Organization logo** — New `logo` field on `gf_Organization` with `logo_image` PIL convenience property

### Bug Fixes

* Fixed widget logo images not rendering due to invalid MIME type (`data:image;base64` → `data:image/png;base64`)
* Fixed `inject_notebook_metadata` using raw proxy-format metadata instead of resolved format, causing silent `KeyError` in deferred sync processing
* Fixed manual `notebook_name`/`notebook_path` in `configure()` not feeding into the resolver
* Fixed stale display trap after reconfiguring with `auto_publish=False`
* Fixed duplicate resolution log entries from `resolve_analysis()`

### Breaking Changes

* **Removed `nest-asyncio` dependency** — code that transitively relied on GoFigr importing it will need to add it explicitly
* **Removed JupyterLab extension** — notebook detection is now handled entirely kernel-side

***

## v2.0.6 — March 12, 2026

* Fixed stale display trap causing `auto_publish=False` to be ignored on extension reload

***

## v2.0.5 — March 12, 2026

* Fixed pickle filename crash when `revision_index` is None (client-side UUID)

***

## v2.0.4 — March 12, 2026

* Fixed display crash on older IPython by falling back to `IPython.core.display`

***

## v2.0.3 — March 12, 2026

* Fixed `importlib.resources.files()` AttributeError on Python 3.8

***

## v2.0.2 — March 12, 2026

Patch release with bug fixes and new configuration options.

### Improvements

* **`auto_configure` switch** — New option to disable automatic configuration on Jupyter extension load
* **Client-side UUID generation** — Revision UUIDs are now generated client-side, reducing two API calls to one during figure publication

### Bug Fixes

* Fixed `sorted()` TypeError for list fields containing dicts
* Fixed null publisher crash when `auto_configure` is disabled

***

## v2.0.1 — March 5, 2026

Patch release improving widget resilience and `@reproducible` ergonomics.

### Improvements

* **Graceful anywidget fallback** — When `anywidget` is missing or its JupyterLab extension is not registered, interactive figures now fall back to non-interactive rendering with a warning banner instead of crashing
* **`check_anywidget_health()` utility** — New diagnostic function for step-by-step troubleshooting of widget issues (checks package installation, traitlets, Jupyter kernel, and provides frontend extension guidance)
* **Bare `@reproducible` support** — The decorator can now be used without parentheses (`@reproducible` is equivalent to `@reproducible()`)

### Files Changed

* `gofigr/reproducible.py` — +142 lines: `_ANYWIDGET_WARNING_HTML`, `check_anywidget_health()`, `_run_interactive_fallback()`, updated `reproducible()` signature

***

## v2.0.0 — March 5, 2026

Major release introducing **Clean Room** reproducibility and **Pyodide** support.

### Clean Room: Reproducible Figures

* **`@reproducible` decorator** — Capture source code, parameters, and package dependencies with every published figure
* **Interactive parameter widgets** — `SliderParam`, `DropdownParam`, `CheckboxParam`, `TextParam`, and `StaticParam` for in-browser figure re-rendering
* Parameter serialization supports JSON (primitives) and Parquet (DataFrames) with 100MB size limit
* Cross-language parameter model enables future R client support

### Pyodide Support

* **`PyodidePublisher`** — Publish figures from browser-based Python environments (e.g., JupyterLite)
* SVG added to default Pyodide image formats for vector export
* Guarded all optional imports (`IPython`, `blake3`, `git`, `py3Dmol`, etc.) for restricted environments

### Performance & Compatibility

* **API v1.4** — `Workspace.list()` uses shallow serialization, significantly speeding up `configure()` in Jupyter
* **Watermark stabilization** — Padding no longer causes figure size shifts on publish
* **Python 3.12 compatibility** — Replaced `pkg_resources` with `pathlib.Path` and updated `importlib.resources` APIs
* Switched from `pyqrcode` to `pyqrcodeng`
* Added `pyarrow` dependency for Parquet support

### Breaking Changes

* API version bumped from v1.3 to v1.4 — requires server v2.5.0+
* `Workspace.list()` no longer returns nested analyses/assets/stories; call `.fetch()` on individual workspaces for full details

***

## v1.3.2 and earlier

Previous releases focused on core figure publishing, Jupyter integration, and workspace management. See the [GitHub repository](https://github.com/GoFigr/gofigr-python) for the full commit history.


# R Client

All notable changes to the GoFigr R client (`gofigR`).

**Current version:** v2.0.2

***

## v2.0.2 — April 7, 2026

* Watermarks use the short redirect URL for compact QR codes (the `app.` prefix is kept elsewhere)

## v2.0.1 — April 7, 2026

* Fixed a missing `sync_file()` helper; added `get_app_url()`

## v2.0.0 — April 7, 2026

Major release bringing the R client in line with Clean Room and Auth0:

* **Clean Room support** — `reproducible()` for interactive, reproducible figures, with an interactive Shiny gadget for parameter exploration and a `viewer` control
* **Auth0 device-code login** — `gfconfig` migrates to the Auth0 Device Authorization Flow
* **Auto-assign** — AI-based figure titling and assignment
* **Short IDs and client-side UUIDs**, plus metadata annotators
* Analysis-scoped asset support
* Clean Room watermarks get a shield icon and smaller QR codes; plot dimensions stored in image metadata; `ggsave` warnings suppressed

***

## v1.1.3 — November 26, 2025

* Documentation improvements
* Environments for API objects (pass by reference semantics)
* Shiny and performance improvements
* Image dimension overrides
* Workspace name auto-creation in `enable()`
* Example data and compliance example
* Build cleanup

## v1.1.2 — July 31, 2025

* Removed global variables and global imports in tests
* Removed `dtt` dependency

## v1.1.1 — July 21, 2025

* Added reader functions
* Improved `sync_file` functionality
* Removed spurious imports
* Asset synced message

## v1.1.0 — July 17, 2025

* Asset tracking and sync
* rbuildignore fix
* Documentation updates
* Test infrastructure improvements

## v0.3.1 — April 9, 2025

* Interface simplification
* `ggplotify` refactoring
* Fixed `plot()` infinite recursion
* Workspace and analysis improvements

## v0.2.1 — January 29, 2025

* **Initial CRAN submission**
* Supports both automatic and manual publication of figures to GoFigr.io

***

For the full commit history, see the [GitHub repository](https://github.com/GoFigr/gofigR).


# Managed Compute (May–June 2026)

**Released:** May–June 2026

This is the **Managed Compute** era. GoFigr goes beyond tracking your figures to running the code that makes them: launch a cloud machine and work in browser-based Jupyter, code-server, or R Server with the GoFigr client already installed and signed in. The v3.x series builds that out with self-service billing, usage allowances, storage billing, and semantic search.

## ☁️ Managed Compute

Launch a cloud machine in a couple of clicks and start working — no setup, nothing to install.

**Key capabilities:**

* **Browser-based editors** — JupyterLab, code-server, and R Server, all on the same persistent files
* **Machine types (tiers)** — five named tiers from Micro to Performance XL, with live pricing shown before you launch
* **Lifecycle controls** — start, stop, restart, rename, and change machine type
* **Idle auto-shutdown** — instances stop themselves when idle so you don't pay for a machine you forgot about, with an in-page warning before they do
* **Soft delete with restore** — a deleted instance can be restored within a recovery window before its data volume is removed
* **Live status and event log** — per-instance status, activity, and a full event history

[Managed Compute documentation →](/managed-compute/compute)

***

## 💳 Self-Service Billing

GoFigr now has end-to-end billing you manage yourself.

**What's new:**

* **Stripe Checkout and customer portal** — subscribe, update payment details, and view invoices
* **Plan switching** — move between plans, with a graceful downgrade to Free and promo-code support at checkout
* **Subscription status** — an organization subscription card, plus banners for canceled and past-due subscriptions

[Plans & billing →](/billing-and-plans/billing)

***

## 📊 Compute Allowances, Overages & Usage

Compute and storage are metered against your plan's included allowance.

**What's new:**

* **Included allowance** — a monthly compute allowance enforced at launch and start
* **Overages** — opt in to bill usage beyond the allowance at standard rates, or keep it as a hard cap on compute
* **Per-GB storage billing** for compute data volumes
* **Usage visibility** — Compute Usage cards on workspace and organization views, a per-instance billable-usage breakdown of compute and storage intervals, and per-period billing statements

[Compute & storage charges →](/billing-and-plans/compute-usage)

***

## 🔎 Semantic Search

Search figures by **meaning**, not just keywords.

**What's new:**

* **Semantic Search mode** powered by multimodal embeddings
* Search indexes figure code, AI descriptions, and names; keyword results show highlighted match context
* More accurate matching on matplotlib figures, where label text is reconstructed from the figure's vector glyphs

[Enhanced Search →](/features/search)

***

## 🔔 Notifications

A new in-app **notification inbox** (the bell) surfaces:

* Compute events — instance ready, idle, error, and reboot
* Billing events — payment failed and subscription canceled

***

## Additional Improvements

* R Server added as a third compute editor in v3.5.0, alongside Jupyter and code-server
* Cost estimate and ETA in the launch modal, plus a cold-launch progress stepper
* User-triggered reboot to apply staged OS security updates
* Workspace and organization admins can operate and terminate instances they didn't launch
* Email-verification banner for new sign-ups
* Open-source notices page at `/licenses`
* Python client: new notebooks auto-enable GoFigr on compute instances (v2.3.3)


# April 2026

**Released:** April 2026

This release rolls out **Auth0 authentication** across the platform and expands **Clean Room Studio** with in-browser **R support**, manual analysis syncing, and a more capable AI code editor.

## 🔐 Auth0 Authentication

GoFigr now authenticates users through Auth0 on both the web app and the `gofigr` Python client, with the legacy auth path available in dual mode for a smooth transition.

**Key capabilities:**

* Auth0 login on the web app, with the legacy UI removed
* **MFA enrollment** and live status directly from user settings
* Password change, email update, and claim sync routed through Auth0
* Separate SPA and CLI client IDs exposed via the `/info` endpoint
* **`gfconfig` device-code login** — the Python client now supports Auth0 Device Authorization Flow, auto-opening the browser for verification
* Dual-mode server authentication so existing API keys and legacy credentials keep working

Auth0 integration covers: Server v2.10.0, Web App v2.2.5, Python v2.2.0.

***

## 🧪 Clean Room Studio: R Support

Clean Room Studio is no longer Python-only. Both the in-browser runtime and the AI code editor now understand R.

**New capabilities:**

* **WebR Clean Room runtime** — run R figures entirely in the browser with JS-side publishing and watermarking
* Package preloading, cache invalidation, and DataFrame preview for WebR
* Base R graphics support in Clean Room Studio
* **R language support in the AI code editor** — agentic code editing now works on R workspaces
* **WebR `publish()` parameters** — `width`, `height`, and `dpi` are honored in the WebR shim, matching the Python API

[Clean Room (Python) →](/features/clean-room-python) · [Clean Room (R) →](/features/clean-room-r)

***

## 🔄 Manual Sync Analysis

Story analysis tracking is now an explicit, user-controlled action instead of a background auto-sync.

**What's new:**

* **Sync Analysis button and modal** replace auto-tracking for story updates
* Story config auto-saves before refine and recreate operations
* Streamlined "Story Updated" modal copy
* Auto-scroll to new slides is suppressed during sync to avoid jumpy layouts

[AI Story Mode documentation →](/features/story-mode)

***

## 🤖 Smarter AI Code Editor

The AI code editor in Clean Room Studio gets more context and better recovery behavior.

**Improvements:**

* **Auto-retry on failure** — AI code changes are automatically retried on execution failure or no output, with runtime and `publish()` details added to the feedback prompt
* **`publish()` parameter awareness** — the editor knows about `width`, `height`, and `dpi` arguments
* **Figure image and package context** are now passed to the code edit agent for better grounding
* **Improved chat context** — the latest figure image is sent after conversation history, keeping responses grounded in the current state

***

## 🐍 Python Client: `@reproducible` & Watermarks

The `gofigr` Python client sharpens its watermarking and broadens `@reproducible` parameter support.

**Improvements:**

* **Short ID watermarks** — watermark size calculation now uses short IDs, producing smaller, cleaner QR codes
* **QR library migration** from `pyqrcodeng` to `python-qrcode`
* **`@reproducible` container support** — decorator now handles tuples, numpy arrays, and nested containers
* **Auto-injected `publish`** in Clean Room code execution
* **Matplotlib DPI capture** in the clean room manifest for accurate Pyodide rendering
* Fixed `pad_for_watermark` widening images beyond the figure's intended width
* Fixed `PosixPath` serialization error in `FileData.read`

***

## Additional Improvements

* Workspace dropdown is always visible so users can create new workspaces from anywhere
* Workspace link in the revision view, with the workspace selector syncing on navigation
* Refresh buttons added to the revision view, revision selector, and related panels
* Real-time capture wizard: data tracking and Clean Room examples, `gfconfig` integration, and API key handling improvements
* Clean Room manifest viewer: raw JSON toggle and `formatValue` helper
* Backend performance improvements (faster short ID lookups, faster cold starts)
* Numerous scroll, layout, and copy fixes across Clean Room Studio and Story Mode


# January 2026

**Released:** January 2026

This is a major release introducing AI-powered presentation generation, enhanced collaboration features, and powerful import capabilities.

## 🤖 AI Story Mode

Transform your figure collections into polished presentations, reports, or tutorials with a single click.

**Key capabilities:**

* AI-generated slide content and narratives
* Full context analysis (source code, metadata, not just images)
* Interactive Q\&A during presentations
* Export to PowerPoint, Word, or Markdown
* Customizable audience targeting

[Full documentation →](/features/story-mode)

***

## 💬 Comments & Collaboration

Add comments to any figure, document, or asset for team collaboration.

**Features:**

* Markdown formatting support
* @mentions with email notifications
* Threaded replies
* Emoji reactions
* AI-powered comment generation

[Full documentation →](/features/comments)

***

## 📂 Git Repository Import

Import Jupyter notebooks directly from your Git repositories.

**Supported platforms:**

* GitHub
* GitLab
* Bitbucket

**Features:**

* Extract figures from all commits
* Preserve version history
* Automatic attribution
* HTTPS and SSH support

[Full documentation →](/features/git-import)

***

## 📄 Document Import

Extract figures from PowerPoint and Word documents.

**Features:**

* AI-powered figure title generation
* Link figures to source documents
* OCR support for UUID extraction
* Maintain complete traceability

[Full documentation →](/features/document-import)

***

## 🔍 Enhanced Search

Find figures faster with text and visual search.

**New capabilities:**

* Full-text search across all content
* Image similarity search
* Results grouped by source
* Workspace filtering

[Full documentation →](/features/search)

***

## 🏢 Workspace Improvements

Better organization and navigation for teams.

**Updates:**

* Quick workspace switching
* Organization logos
* Improved navigation
* Flexible access controls

[Full documentation →](/features/workspaces)

***

## Additional Improvements

* In-browser Python playground for interactive exploration
* Performance optimizations across the platform
* UI/UX refinements based on user feedback


