# Projects

## List workspace projects

`client.projects.list(ProjectListParamsquery, RequestOptionsoptions?): ProjectsCursorPage<ProjectListResponse>`

**get** `/projects`

Returns projects in the requested workspace that are accessible to the authenticated public API key, ordered by recent activity.

### Parameters

- `query: ProjectListParams`

  - `workspace_id: string`

    Workspace identifier

  - `cursor?: string`

    Opaque cursor for fetching the next page

  - `limit?: number`

    Maximum number of results to return

  - `query?: string`

    Search query

### Returns

- `ProjectListResponse`

  - `created_at: number`

  - `last_modified: number | null`

  - `name: string`

    Project name

  - `origin: string | null`

    Project origin

  - `project_id: string`

    Project identifier

  - `workspace_id: string`

    Workspace identifier

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

// Automatically fetches more pages as needed.
for await (const projectListResponse of client.projects.list({ workspace_id: 'ws_abc123' })) {
  console.log(projectListResponse.project_id);
}
```

#### Response

```json
{
  "meta": {
    "next_cursor": "eyJvZmZzZXQiOjIwfQ",
    "total_estimate": 0
  },
  "projects": [
    {
      "created_at": 0,
      "last_modified": 0,
      "name": "Spring Campaign",
      "origin": "api",
      "project_id": "prj_abc123",
      "workspace_id": "ws_abc123"
    }
  ]
}
```

## Create a project

`client.projects.create(ProjectCreateParamsbody, RequestOptionsoptions?): ProjectCreateResponse`

**post** `/projects`

Creates a new Flora project in the requested workspace. Mutating public API requests support an optional Idempotency-Key header for client retries; duplicate keys within two hours return idempotency_duplicate.

### Parameters

- `body: ProjectCreateParams`

  - `name: string`

    Project name

  - `workspace_id: string`

    Workspace identifier

### Returns

- `ProjectCreateResponse`

  - `created_at: number`

  - `last_modified: number | null`

  - `name: string`

    Project name

  - `origin: string | null`

    Project origin

  - `project_id: string`

    Project identifier

  - `workspace_id: string`

    Workspace identifier

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

const project = await client.projects.create({
  name: 'Spring Campaign',
  workspace_id: 'ws_abc123',
});

console.log(project.project_id);
```

#### Response

```json
{
  "created_at": 0,
  "last_modified": 0,
  "name": "Spring Campaign",
  "origin": "api",
  "project_id": "prj_abc123",
  "workspace_id": "ws_abc123"
}
```

## Get a project

`client.projects.retrieve(stringprojectID, RequestOptionsoptions?): ProjectRetrieveResponse`

**get** `/projects/{projectId}`

Returns metadata for a single project when it is accessible to the authenticated public API key. Missing and inaccessible projects both return 404.

### Parameters

- `projectID: string`

  Project identifier

### Returns

- `ProjectRetrieveResponse`

  - `created_at: number`

  - `last_modified: number | null`

  - `name: string`

    Project name

  - `origin: string | null`

    Project origin

  - `project_id: string`

    Project identifier

  - `workspace_id: string`

    Workspace identifier

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

const project = await client.projects.retrieve('prj_abc123');

console.log(project.project_id);
```

#### Response

```json
{
  "created_at": 0,
  "last_modified": 0,
  "name": "Spring Campaign",
  "origin": "api",
  "project_id": "prj_abc123",
  "workspace_id": "ws_abc123"
}
```

## List project canvas nodes

`client.projects.listNodes(stringprojectID, ProjectListNodesParamsquery?, RequestOptionsoptions?): CanvasNodesCursorPage<ProjectListNodesResponse>`

**get** `/projects/{projectId}/nodes`

Returns sanitized visible media nodes on a project canvas. The response omits raw graph documents, Liveblocks internals, raw Convex IDs, and unbounded node data blobs.

### Parameters

- `projectID: string`

  Project identifier

- `query: ProjectListNodesParams`

  - `cursor?: string`

    Opaque cursor for fetching the next page

  - `limit?: number`

    Maximum number of results to return

### Returns

- `ProjectListNodesResponse`

  - `node_id: string`

    Canvas node identifier

  - `type: "image" | "video" | "audio" | "text"`

    Canvas node media type

    - `"image"`

    - `"video"`

    - `"audio"`

    - `"text"`

  - `asset_id?: string | null`

    Asset identifier

  - `height?: number | null`

  - `label?: string | null`

    Canvas node label

  - `url?: string | null`

    Canvas node output URL or text content

  - `width?: number | null`

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

// Automatically fetches more pages as needed.
for await (const projectListNodesResponse of client.projects.listNodes('prj_abc123')) {
  console.log(projectListNodesResponse.node_id);
}
```

#### Response

```json
{
  "canvas_url": "https://example.com",
  "meta": {
    "next_cursor": "eyJvZmZzZXQiOjIwfQ",
    "total_estimate": 0
  },
  "nodes": [
    {
      "node_id": "node_abc123",
      "type": "image",
      "asset_id": "asset_abc123",
      "height": 0,
      "label": "Logo",
      "url": "https://media.flora.ai/output.png",
      "width": 0
    }
  ],
  "project_id": "prj_abc123"
}
```

## Domain Types

### Project List Response

- `ProjectListResponse`

  - `created_at: number`

  - `last_modified: number | null`

  - `name: string`

    Project name

  - `origin: string | null`

    Project origin

  - `project_id: string`

    Project identifier

  - `workspace_id: string`

    Workspace identifier

### Project Create Response

- `ProjectCreateResponse`

  - `created_at: number`

  - `last_modified: number | null`

  - `name: string`

    Project name

  - `origin: string | null`

    Project origin

  - `project_id: string`

    Project identifier

  - `workspace_id: string`

    Workspace identifier

### Project Retrieve Response

- `ProjectRetrieveResponse`

  - `created_at: number`

  - `last_modified: number | null`

  - `name: string`

    Project name

  - `origin: string | null`

    Project origin

  - `project_id: string`

    Project identifier

  - `workspace_id: string`

    Workspace identifier

### Project List Nodes Response

- `ProjectListNodesResponse`

  - `node_id: string`

    Canvas node identifier

  - `type: "image" | "video" | "audio" | "text"`

    Canvas node media type

    - `"image"`

    - `"video"`

    - `"audio"`

    - `"text"`

  - `asset_id?: string | null`

    Asset identifier

  - `height?: number | null`

  - `label?: string | null`

    Canvas node label

  - `url?: string | null`

    Canvas node output URL or text content

  - `width?: number | null`

# Assets

## Attach an asset to a canvas

`client.projects.assets.attachAsset(stringassetID, AssetAttachAssetParamsparams, RequestOptionsoptions?): AssetAttachAssetResponse`

**post** `/projects/{projectId}/assets/{assetId}/attach`

Attaches an existing ready asset to a project canvas as a static media node. Mutating public API requests support an optional Idempotency-Key header for client retries; duplicate keys within two hours return idempotency_duplicate.

### Parameters

- `assetID: string`

  Asset identifier

- `params: AssetAttachAssetParams`

  - `projectId: string`

    Project identifier

### Returns

- `AssetAttachAssetResponse`

  - `asset_id: string`

    Asset identifier

  - `canvas_url: string`

    Project canvas URL

  - `node_id: string`

    Canvas node identifier

  - `project_id: string`

    Project identifier

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

const response = await client.projects.assets.attachAsset('asset_abc123', {
  projectId: 'prj_abc123',
});

console.log(response.asset_id);
```

#### Response

```json
{
  "asset_id": "asset_abc123",
  "canvas_url": "https://example.com",
  "node_id": "node_abc123",
  "project_id": "prj_abc123"
}
```

## Domain Types

### Asset Attach Asset Response

- `AssetAttachAssetResponse`

  - `asset_id: string`

    Asset identifier

  - `canvas_url: string`

    Project canvas URL

  - `node_id: string`

    Canvas node identifier

  - `project_id: string`

    Project identifier

# Canvas

## Get project canvas

`client.projects.canvas.retrieve(stringprojectID, RequestOptionsoptions?): CanvasRetrieveResponse`

**get** `/projects/{projectId}/canvas`

Returns the current project canvas topology as a Mermaid flowchart using the same serializer as the Fauna agent.

### Parameters

- `projectID: string`

  Project identifier

### Returns

- `CanvasRetrieveResponse`

  - `canvas_url: string`

    Project canvas URL

  - `diagram: string`

    Mermaid flowchart diagram

  - `project_id: string`

    Project identifier

  - `summary: Summary`

    - `edge_count: number`

    - `group_count: number`

    - `isolated_node_count: number`

    - `node_count: number`

    - `workflow_count: number`

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

const canvas = await client.projects.canvas.retrieve('prj_abc123');

console.log(canvas.project_id);
```

#### Response

```json
{
  "canvas_url": "https://example.com",
  "diagram": "graph LR\n  n1[\"Prompt (text)\"]\n  n2[\"Result (image)\"]\n  n1 -->|text| n2",
  "project_id": "prj_abc123",
  "summary": {
    "edge_count": 0,
    "group_count": 0,
    "isolated_node_count": 0,
    "node_count": 0,
    "workflow_count": 0
  }
}
```

## Patch project canvas

`client.projects.canvas.update(stringprojectID, CanvasUpdateParamsbody, RequestOptionsoptions?): CanvasUpdateResponse`

**patch** `/projects/{projectId}/canvas`

Applies a Mermaid flowchart patch to the project canvas using the same create_workflow path as the Fauna agent. The diagram may add nodes, connect nodes, and reference existing canvas nodes by their Mermaid short IDs in edges (e.g. `n1 --> out`). This endpoint is add-only: re-declaring an existing node id with a label (e.g. `n3["..."]`) creates a NEW node instead of updating the existing one, and returns a warning. To attach to an existing node, reference its id in an edge without re-declaring its label. Subgraph grouping is not applied (nodes inside a `subgraph` are added ungrouped) and returns a warning. To place an existing image/video/audio as a static node, set `node_params` — which is keyed by Mermaid node id, e.g. `{ "img1": { "content_url": "https://…" } }`, NOT a bare `{ content_url }` object. `prompt` and `content_url` are mutually exclusive for a node: use `prompt` (or a label that doubles as the prompt) for generation, or `content_url` for existing media. When using `content_url`, give the node a content-free type-only label such as `img1["(Image)"]` so no prompt is inferred from the label.

### Parameters

- `projectID: string`

  Project identifier

- `body: CanvasUpdateParams`

  - `diagram: string`

    Mermaid flowchart diagram to apply

  - `node_params?: Record<string, NodeParams>`

    Optional per-node parameters, keyed by Mermaid node id (a Record<nodeId, NodeParams>), e.g. { "img1": { "content_url": "https://…" } }. Pass a map keyed by node id, NOT a bare { content_url } object.

    - `aspect_ratio?: string | null`

    - `content_url?: string | null`

      HTTPS URL of existing media to place as a static node. Mutually exclusive with prompt: give the node a content-free type-only label such as `(Image)` so no prompt is inferred from the label. Only supported for Image, Video, and Audio nodes.

    - `model?: string | null`

    - `model_parameters?: Record<string, unknown> | null`

    - `prompt?: string | null`

      Generation prompt for this node. Mutually exclusive with content_url. If omitted, the node's Mermaid label is used as the prompt.

    - `resolution?: string | null`

### Returns

- `CanvasUpdateResponse`

  - `canvas_url: string`

    Project canvas URL

  - `created_edge_count: number`

  - `created_node_count: number`

  - `diagram: string`

    Applied Mermaid flowchart diagram

  - `project_id: string`

    Project identifier

  - `warnings?: Array<string>`

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

const canvas = await client.projects.canvas.update('prj_abc123', {
  diagram:
    'graph LR\n  source["Product photo (Image)"]\n  output["Editorial campaign image (Image)"]\n  source --> output',
});

console.log(canvas.project_id);
```

#### Response

```json
{
  "canvas_url": "https://example.com",
  "created_edge_count": 0,
  "created_node_count": 0,
  "diagram": "graph LR\n  n1[\"Prompt (text)\"]\n  n2[\"Result (image)\"]\n  n1 --> n2",
  "project_id": "prj_abc123",
  "warnings": [
    "string"
  ]
}
```

## Domain Types

### Canvas Retrieve Response

- `CanvasRetrieveResponse`

  - `canvas_url: string`

    Project canvas URL

  - `diagram: string`

    Mermaid flowchart diagram

  - `project_id: string`

    Project identifier

  - `summary: Summary`

    - `edge_count: number`

    - `group_count: number`

    - `isolated_node_count: number`

    - `node_count: number`

    - `workflow_count: number`

### Canvas Update Response

- `CanvasUpdateResponse`

  - `canvas_url: string`

    Project canvas URL

  - `created_edge_count: number`

  - `created_node_count: number`

  - `diagram: string`

    Applied Mermaid flowchart diagram

  - `project_id: string`

    Project identifier

  - `warnings?: Array<string>`

# Actions

## Create a canvas action

`client.projects.actions.create(stringprojectID, ActionCreateParamsbody, RequestOptionsoptions?): ActionCreateResponse`

**post** `/projects/{projectId}/actions`

Creates a prebuilt action node on a project canvas using a raw action slug.

### Parameters

- `projectID: string`

  Project identifier

- `body: ActionCreateParams`

  - `action_id: "split-text" | "find-and-replace-text" | "concat-text" | 34 more`

    Action identifier

    - `"split-text"`

    - `"find-and-replace-text"`

    - `"concat-text"`

    - `"ken-burns-video"`

    - `"color-grade-image"`

    - `"change-image-ar"`

    - `"rotate-image"`

    - `"flip-image"`

    - `"color-filter-image"`

    - `"color-tint-image"`

    - `"filter-color-image"`

    - `"blur-image"`

    - `"duplicate-image"`

    - `"side-by-side-composite"`

    - `"add-shape-to-image"`

    - `"generate-shape-image"`

    - `"add-text-to-image"`

    - `"generate-text-image"`

    - `"qr-code-generator"`

    - `"stitch-videos"`

    - `"split-video"`

    - `"extract-video-frames"`

    - `"color-grade-video"`

    - `"video-to-frame-grid"`

    - `"boomerang-video"`

    - `"reverse-video"`

    - `"video-to-long-exposure"`

    - `"video-effect"`

    - `"color-filter-video"`

    - `"speed-up-video"`

    - `"slow-down-video"`

    - `"duplicate-video"`

    - `"greenscreen-video"`

    - `"resize-video"`

    - `"change-video-ar"`

    - `"split-audio-from-video"`

    - `"merge-audio-into-video"`

  - `params?: Record<string, unknown>`

    Action parameters

### Returns

- `ActionCreateResponse`

  - `action_id: "split-text" | "find-and-replace-text" | "concat-text" | 34 more`

    Action identifier

    - `"split-text"`

    - `"find-and-replace-text"`

    - `"concat-text"`

    - `"ken-burns-video"`

    - `"color-grade-image"`

    - `"change-image-ar"`

    - `"rotate-image"`

    - `"flip-image"`

    - `"color-filter-image"`

    - `"color-tint-image"`

    - `"filter-color-image"`

    - `"blur-image"`

    - `"duplicate-image"`

    - `"side-by-side-composite"`

    - `"add-shape-to-image"`

    - `"generate-shape-image"`

    - `"add-text-to-image"`

    - `"generate-text-image"`

    - `"qr-code-generator"`

    - `"stitch-videos"`

    - `"split-video"`

    - `"extract-video-frames"`

    - `"color-grade-video"`

    - `"video-to-frame-grid"`

    - `"boomerang-video"`

    - `"reverse-video"`

    - `"video-to-long-exposure"`

    - `"video-effect"`

    - `"color-filter-video"`

    - `"speed-up-video"`

    - `"slow-down-video"`

    - `"duplicate-video"`

    - `"greenscreen-video"`

    - `"resize-video"`

    - `"change-video-ar"`

    - `"split-audio-from-video"`

    - `"merge-audio-into-video"`

  - `canvas_url: string`

    Project canvas URL

  - `node_id: string`

    Canvas action node identifier

  - `project_id: string`

    Project identifier

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

const action = await client.projects.actions.create('prj_abc123', { action_id: 'split-text' });

console.log(action.action_id);
```

#### Response

```json
{
  "action_id": "split-text",
  "canvas_url": "https://example.com",
  "node_id": "node_abc123",
  "project_id": "prj_abc123"
}
```

## Run a canvas action

`client.projects.actions.run(stringnodeID, ActionRunParamsparams, RequestOptionsoptions?): ActionRunResponse`

**post** `/projects/{projectId}/actions/{nodeId}/run`

Runs an existing canvas action node through the action execution workflow.

### Parameters

- `nodeID: string`

  Canvas action node identifier

- `params: ActionRunParams`

  - `projectId: string`

    Project identifier

### Returns

- `ActionRunResponse`

  - `charged_cost: number`

    Cost charged in USD

  - `estimated_seconds: number | null`

  - `run_id: string`

    Run identifier

  - `type: "generation" | "technique" | "action"`

    Run type

    - `"generation"`

    - `"technique"`

    - `"action"`

  - `action?: Action | null`

    - `action_id: "split-text" | "find-and-replace-text" | "concat-text" | 34 more`

      Action identifier

      - `"split-text"`

      - `"find-and-replace-text"`

      - `"concat-text"`

      - `"ken-burns-video"`

      - `"color-grade-image"`

      - `"change-image-ar"`

      - `"rotate-image"`

      - `"flip-image"`

      - `"color-filter-image"`

      - `"color-tint-image"`

      - `"filter-color-image"`

      - `"blur-image"`

      - `"duplicate-image"`

      - `"side-by-side-composite"`

      - `"add-shape-to-image"`

      - `"generate-shape-image"`

      - `"add-text-to-image"`

      - `"generate-text-image"`

      - `"qr-code-generator"`

      - `"stitch-videos"`

      - `"split-video"`

      - `"extract-video-frames"`

      - `"color-grade-video"`

      - `"video-to-frame-grid"`

      - `"boomerang-video"`

      - `"reverse-video"`

      - `"video-to-long-exposure"`

      - `"video-effect"`

      - `"color-filter-video"`

      - `"speed-up-video"`

      - `"slow-down-video"`

      - `"duplicate-video"`

      - `"greenscreen-video"`

      - `"resize-video"`

      - `"change-video-ar"`

      - `"split-audio-from-video"`

      - `"merge-audio-into-video"`

  - `model?: Model | null`

    - `model_id: string`

      Model identifier

  - `poll_url?: string | null`

    URL to poll pending/running runs or fetch completed/failed run details.

  - `project_id?: string | null`

    Project identifier

  - `technique?: Technique | null`

    - `name: string`

      Technique name

    - `technique_id: string`

      Technique identifier

### Example

```typescript
import FLORA from '@flora-ai/flora';

const client = new FLORA({
  apiKey: process.env['FLORA_API_KEY'], // This is the default and can be omitted
});

const response = await client.projects.actions.run('nodeId', { projectId: 'prj_abc123' });

console.log(response.run_id);
```

#### Response

```json
{
  "charged_cost": 0,
  "estimated_seconds": 0,
  "run_id": "run_abc123",
  "type": "generation",
  "action": {
    "action_id": "split-text"
  },
  "model": {
    "model_id": "t2i-flux-2-pro"
  },
  "poll_url": "https://example.com",
  "project_id": "prj_abc123",
  "technique": {
    "name": "name",
    "technique_id": "tech_abcd1234"
  }
}
```

## Domain Types

### Action Create Response

- `ActionCreateResponse`

  - `action_id: "split-text" | "find-and-replace-text" | "concat-text" | 34 more`

    Action identifier

    - `"split-text"`

    - `"find-and-replace-text"`

    - `"concat-text"`

    - `"ken-burns-video"`

    - `"color-grade-image"`

    - `"change-image-ar"`

    - `"rotate-image"`

    - `"flip-image"`

    - `"color-filter-image"`

    - `"color-tint-image"`

    - `"filter-color-image"`

    - `"blur-image"`

    - `"duplicate-image"`

    - `"side-by-side-composite"`

    - `"add-shape-to-image"`

    - `"generate-shape-image"`

    - `"add-text-to-image"`

    - `"generate-text-image"`

    - `"qr-code-generator"`

    - `"stitch-videos"`

    - `"split-video"`

    - `"extract-video-frames"`

    - `"color-grade-video"`

    - `"video-to-frame-grid"`

    - `"boomerang-video"`

    - `"reverse-video"`

    - `"video-to-long-exposure"`

    - `"video-effect"`

    - `"color-filter-video"`

    - `"speed-up-video"`

    - `"slow-down-video"`

    - `"duplicate-video"`

    - `"greenscreen-video"`

    - `"resize-video"`

    - `"change-video-ar"`

    - `"split-audio-from-video"`

    - `"merge-audio-into-video"`

  - `canvas_url: string`

    Project canvas URL

  - `node_id: string`

    Canvas action node identifier

  - `project_id: string`

    Project identifier

### Action Run Response

- `ActionRunResponse`

  - `charged_cost: number`

    Cost charged in USD

  - `estimated_seconds: number | null`

  - `run_id: string`

    Run identifier

  - `type: "generation" | "technique" | "action"`

    Run type

    - `"generation"`

    - `"technique"`

    - `"action"`

  - `action?: Action | null`

    - `action_id: "split-text" | "find-and-replace-text" | "concat-text" | 34 more`

      Action identifier

      - `"split-text"`

      - `"find-and-replace-text"`

      - `"concat-text"`

      - `"ken-burns-video"`

      - `"color-grade-image"`

      - `"change-image-ar"`

      - `"rotate-image"`

      - `"flip-image"`

      - `"color-filter-image"`

      - `"color-tint-image"`

      - `"filter-color-image"`

      - `"blur-image"`

      - `"duplicate-image"`

      - `"side-by-side-composite"`

      - `"add-shape-to-image"`

      - `"generate-shape-image"`

      - `"add-text-to-image"`

      - `"generate-text-image"`

      - `"qr-code-generator"`

      - `"stitch-videos"`

      - `"split-video"`

      - `"extract-video-frames"`

      - `"color-grade-video"`

      - `"video-to-frame-grid"`

      - `"boomerang-video"`

      - `"reverse-video"`

      - `"video-to-long-exposure"`

      - `"video-effect"`

      - `"color-filter-video"`

      - `"speed-up-video"`

      - `"slow-down-video"`

      - `"duplicate-video"`

      - `"greenscreen-video"`

      - `"resize-video"`

      - `"change-video-ar"`

      - `"split-audio-from-video"`

      - `"merge-audio-into-video"`

  - `model?: Model | null`

    - `model_id: string`

      Model identifier

  - `poll_url?: string | null`

    URL to poll pending/running runs or fetch completed/failed run details.

  - `project_id?: string | null`

    Project identifier

  - `technique?: Technique | null`

    - `name: string`

      Technique name

    - `technique_id: string`

      Technique identifier
