## Authentication

Every Floqer API request is authenticated with a Bearer token. Your API key starts with `floq_` and grants full access to all endpoints.

### Get your API key

Generate an API key from the Floqer dashboard under **Settings**. Each key has full access to your organization's workflows, actions, and apps.

### Bearer tokens

Pass your API key in the `Authorization` header on every request.

request headerCOPY

```
Authorization: Bearer floq_YOUR_API_KEY
```

### Your first request

List all workflows in your organization. If the request succeeds, the response envelope contains a `data` array.

curlCOPY

```
curl "https://api.floqer.com/api/v1/workflows/" \
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

### Security reminders

- Never embed API keys in client-side code. Keys belong in server-side environment variables.
- Rotate keys if you suspect a leak. Old keys can be revoked from the dashboard.

## Rate Limits

The Floqer Public API allows 200 requests per minute and 10,000 per day per API key. Every response includes rate limit headers, and 429s tell you exactly how long to back off.

### Default limits

200

requests / minute

10,000

requests / day

Limits are per API key. Contact support for higher limits.

### If you hit the limit

The API returns a `429` with a `retryAfter` field — the number of seconds to wait before retrying.

429 responseCOPY

```
{
  "status": 429,
  "error": "Too Many Requests",
  "message": "Rate limit exceeded. Try again in 42 seconds.",
  "retryAfter": 42
}
```

## Errors

Every Floqer API response uses a consistent envelope. Success responses wrap data, error responses explain what went wrong.

### Response envelope

All 2xx responses wrap the payload in a `data` field:

success envelopeCOPY

```
{
  "status": 200,
  "data": { ... }
}
```

Single-issue errors (401, 403, 404, 429, 500) use a flat envelope:

single errorCOPY

```
{
  "status": 401,
  "error": "Unauthorized",
  "message": "API key is required"
}
```

Validation errors (400) return all field-level problems at once so you can fix them in a single retry:

validation errorCOPY

```
{
  "status": 400,
  "error": "Validation Error",
  "message": "2 validation errors",
  "errors": [\
    { "field": "name", "message": "required" },\
    { "field": "inputs[0].type", "message": "must be one of: string, url, email, number" }\
  ]
}
```

### Examples you will hit

`POST /workflows` with empty body

400COPY

```
{
  "status": 400,
  "error": "Validation Error",
  "message": "1 validation error",
  "errors": [\
    { "field": "name", "message": "required" }\
  ]
}
```

`PUT /workflows/:id/inputs` with an invalid type

400COPY

```
{
  "status": 400,
  "error": "Validation Error",
  "message": "1 validation error",
  "errors": [\
    { "field": "inputs[0].type", "message": "must be one of: string, url, email, number" }\
  ]
}
```

Any request without an `Authorization` header

401COPY

```
{
  "status": 401,
  "error": "Unauthorized",
  "message": "API key is required. Pass it as: Authorization: Bearer floq_..."
}
```

`GET /workflows/:id` with a non-existent ID

404COPY

```
{
  "status": 404,
  "error": "Not Found",
  "message": "Workflow not found"
}
```

Exceeding 200 requests/minute

429COPY

```
{
  "status": 429,
  "error": "Too Many Requests",
  "message": "Rate limit exceeded. Try again in 42 seconds.",
  "retryAfter": 42
}
```

## User

Caller identity and org context — the bootstrap call for an agent. `GET /api/v1/user` returns who the API key belongs to (email, name, org role) and the organization's member roster. Call this first to confirm identity before building or running workflows.

**Org knowledge.**`GET /api/v1/user/knowledge` reads, and `PUT /api/v1/user/knowledge` writes, a single free-form text/markdown file describing the org (who they are, what workflows they run, their goals) — shared context an agent can read before planning and update as it learns. The write is an idempotent full replacement.

## Get User

get`/api/v1/user/`

Returns the authenticated caller's bootstrap payload: identity and org membership. This is the natural first call for an agent — confirm who the API key belongs to (email, name, org role) and see the organization's member roster before building or running workflows. For accessible workflow metadata, use **List Workflows**.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Response

`status`integer

`data`object

Caller bootstrap payload. Identity and org membership are always present. Use List Workflows for accessible workflow metadata.

`email`email

Caller's email address (Floqer username).

`first_name`string

Caller's first name from their Floqer profile. `null` if unset.

`last_name`string

Caller's last name from their Floqer profile. `null` if unset.

`role`string

Organization role: `admin` (org lead) or `member`.

`org_id`string

UUID of the caller's organization.

`org_members`object

Members of the caller's organization. Emails are Floqer usernames.

`count`integer

Number of members in the organization.

`emails`array

Email address of each org member (Floqer username), sorted alphabetically.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/user/"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "email": "ada@acme.com",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "role": "admin",
    "org_id": "7c3e5b2a-9f10-4d23-8b71-1a2b3c4d5e6f",
    "org_members": {
      "count": 3,
      "emails": [\
        "ada@acme.com",\
        "grace@acme.com",\
        "linus@acme.com"\
      ]
    }
  }
}
```

Try it

## Get Org Knowledge

get`/api/v1/user/knowledge`

Returns the caller's organization knowledge file — a single free-form text/markdown blob describing the org (who they are, what workflows they run, their goals). It's shared context an agent can read before planning work. When no file has been written yet, this returns `{ content: null, exists: false, updated_at: null }` rather than a 404. Write it with **Set Org Knowledge**.

No scope required — any valid API key may read its own org's knowledge file.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Response

`status`integer

`data`object

The caller's organization knowledge file: a free-form text/markdown blob describing the org. `exists` is `false` and `content` is `null` when none has been written yet.

`content`string

The file's text content. `null` when no file has been written yet.

`exists`boolean

Whether a knowledge file currently exists for the org.

`updated_at`date-time

ISO 8601 timestamp of the last write, or `null` when unset.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/user/knowledge"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "content": "# Floqer\n\nGTM automation platform. Our team runs enrichment + outreach workflows: enrich inbound signups, find work emails via waterfall, and sync qualified leads to HubSpot. Goal: cut manual research time and keep the CRM enriched.",
    "exists": true,
    "updated_at": "2026-05-29T11:30:00Z"
  }
}
```

Try it

## Set Org Knowledge

put`/api/v1/user/knowledge`

Creates or replaces the caller's organization knowledge file — a single free-form text/markdown blob describing the org (who they are, what workflows they run, their goals). **Idempotent full replacement**: `content` becomes the entire file, so read the current value with **Get Org Knowledge** first if you intend to append. Send an empty string to clear it.

**Max size:** 256 KB (UTF-8).

No scope required — any valid API key may write its own org's knowledge file.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Request body

`content`stringrequired

The full text/markdown content of the knowledge file. Replaces any existing content. Send an empty string to clear it.

### Response

`status`integer

`data`object

`content`string

The file's text content after the write.

`exists`boolean

Whether a knowledge file currently exists for the org.

`updated_at`date-time

ISO 8601 timestamp of this write.

Requestcurl

```
curl -X PUT "https://api.floqer.com/api/v1/user/knowledge"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "content": "# Floqer\n\nGTM automation platform. Our team runs enrichment + outreach workflows: enrich inbound signups, find work emails via waterfall, and sync qualified leads to HubSpot. Goal: cut manual research time and keep the CRM enriched."
}'
```

Response200

Try it

## Build a Workflow

A workflow is a sheet. When you create a workflow, the first sheet is created for you. All endpoints operate on sheets — configure inputs, add actions, add data, run. If you need additional sheets (e.g. to expand an array of employees into individual rows), create them with Create Sheet. Each sheet gets its own ID and works with the same endpoints.

The typical build flow: create a workflow → define input columns → add actions → configure each action's inputs by wiring variable references from upstream outputs.

**Discovering available actions:** there is no API endpoint that lists or describes action templates. The static catalog files are the authoritative reference:

- `/docs/action-catalog.txt` — every action with needs, produces, category, credits
- `/docs/action-detail/{action_id}.txt` — per-action configuration guide (model selection, prompting patterns, when to use / when not to use)

Load these once into your context before constructing a workflow. Use `action_id` from the catalog in the Add Action endpoint.

### Inputs

3endpoints

Each input field has a type that helps agents match fields to action inputs. When configuring an action, reference an input with `{{input.name}}` — for example, `{{input.linkedin_url}}`.

| Type | Meaning | Example value |
| --- | --- | --- |
| string | General text | "Floqer Inc" |
| url | A URL — actions that need URLs match against this type | "https://linkedin.com/company/floqer" |
| email | An email address | "hello@floqer.com" |
| number | A numeric value | 42 |

## List Inputs

get`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/inputs`

Returns all input fields currently configured for a workflow. Use this to check existing inputs before adding or updating.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Response

`status`integer

`data`array

`name`string

`type`string

`description`string

`reference`string

Copy this into action configuration to reference this field

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/inputs"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "name": "linkedin_url",\
      "type": "url",\
      "description": "LinkedIn company profile URL",\
      "reference": "{{input.linkedin_url}}"\
    },\
    {\
      "name": "email",\
      "type": "email",\
      "description": "",\
      "reference": "{{input.email}}"\
    }\
  ]
}
```

Try it

## Add Inputs

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/inputs`

Adds input fields to the workflow. Existing inputs are not affected. Returns all inputs (existing + new). Reference input fields in actions with `{{input.field_name}}` syntax.

The request body is a JSON array — no wrapper object needed.

Valid types: `string`, `url`, `email`, `number`.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Request bodyarray of objects

`name`stringrequired

Field name — used as the column header and the key when adding data rows

`type`stringrequired

Data type. Helps agents match fields to action inputs (e.g. an action that needs a URL can search for type url). One of: string, url, email, number

`description`stringoptional

Human-readable description of what this field contains

### Response

`status`integer

`data`array

`name`string

`type`string

`description`string

`reference`string

Copy this into action configuration to reference this field

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/inputs"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '[\
  {\
    "name": "linkedin_url",\
    "type": "url",\
    "description": "LinkedIn company profile URL"\
  },\
  {\
    "name": "email",\
    "type": "email"\
  },\
  {\
    "name": "company_name",\
    "type": "string",\
    "description": "Target company name"\
  }\
]'
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "name": "linkedin_url",\
      "type": "url",\
      "description": "LinkedIn company profile URL",\
      "reference": "{{input.linkedin_url}}"\
    },\
    {\
      "name": "email",\
      "type": "email",\
      "description": "",\
      "reference": "{{input.email}}"\
    },\
    {\
      "name": "company_name",\
      "type": "string",\
      "description": "Target company name",\
      "reference": "{{input.company_name}}"\
    }\
  ]
}
```

Try it

## Delete Input

delete`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/inputs/{field_name}`

Removes a single input field from the workflow. **Any action references using `{{input.field_name}}` for this field will break.** Check your action configurations before deleting.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`field_name`required

The \`name\` of the input field (e.g. \`email\`). Use List Inputs to see all field names.

### Response

`status`integer

`data`array

`name`string

`type`string

`description`string

`reference`string

Copy this into action configuration to reference this field

Requestcurl

```
curl -X DELETE "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/inputs/:field_name"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "name": "email",\
      "type": "email",\
      "description": "",\
      "reference": "{{input.email}}"\
    }\
  ]
}
```

Try it

### Actions

11endpoints

Browse the action catalog to find the right action for your data. Each action lists what it needs and what it produces — match your available data types to find compatible actions.

[action-catalog.txt — full action catalog](/content/docs/action-catalog.txt)

## Add Action to Workflow

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/add`

Adds an action to the workflow. Returns the `action_instance_id`, input fields to configure, and output fields with reference strings.

Appends to the end of the chain by default. Use `after` to insert after a specific action. Pass `name` to set a custom display name on creation — otherwise the action template's default name is used. The display name can be changed later via **Rename Action**.

**Configure in the same request.** Every optional field **Configure Action** accepts — `inputs`, `run_if`, `continue_workflow_if_action_fails`, `note` — can be sent in this body too, with identical shapes and semantics. See **Configure Action** for those shapes. Doing so turns the usual add-then-configure pair into a single call, and any warnings come back on the 201 in the same `warnings[]` shape. If configuration fails validation outright, nothing is created.

Otherwise, add first and wire inputs afterwards with **Configure Action**.

ackDB actions support ingest, company/person/contact/member lookup, company/person/contact update/upsert, and company/contact relationships. The legacy `ackdb_merge_record` remains a company-merge alias. New `ackdb_ingest` actions expose boolean `accepted` first; company creation remains the separate `entity_created` output. Accepted includes retries, queued requests and retained unresolved content, and excludes dropped or failed items. It does not prove a new event was created or queued processing finished.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Request body

`action_id`stringrequired

Action template identifier from the action catalog (e.g. scrape\_company\_linkedin\_profile)

`after`stringoptional

Insert after this action\_instance\_id. Omit to append to the end of the chain.

`name`stringoptional

Optional display name for the new action instance. Omit to use the action template's default name. Whitespace-only values are ignored. Can also be changed later via Rename Action.

`inputs`objectoptional

Optional. Wire the action's input fields in this same request instead of following up with Configure Action. Same shapes and semantics as Configure Action's `inputs` — see that endpoint for the accepted value forms.

`run_if`object \| nulloptional

Optional. Same as Configure Action's `run_if` — see that endpoint for the condition-group shape.

`continue_workflow_if_action_fails`booleanoptional

Optional. Same as Configure Action's field of the same name: when true, downstream actions still run for a row even if this action fails.

`note`stringoptional

Optional free-text note to save on the action, identical to Save Action Note.

### Response

`status`integer

`data`object

`action_instance_id`string

Unique instance ID. Reference outputs with {{action\_instance\_id.field\_name}}

`action_id`string

Action template identifier

`display_name`string

Human-readable name

`inputs`array

Fields that need to be configured. Wire each one with a variable reference or static value using Configure Action.

`name`string

`type`string

`required`boolean

`description`string

`outputs`array

Fields this action will produce. Use the reference string in downstream action configuration.

`name`string

`type`string

One of: string, url, email, number, raw\_array, structured\_array

`reference`string

Copy this into downstream action configuration

`fields`array

Sub-fields for structured\_array types — these become input columns when expanded to a sheet

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/add"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "action_id": "scrape_company_linkedin_profile",
  "after": "scrape_company_linkedin_profile_1",
  "name": "Enrich primary contact",
  "inputs": {
    "linkedin_url": "{{input.linkedin_url}}"
  },
  "run_if": null,
  "continue_workflow_if_action_fails": false,
  "note": "string"
}'
```

Response201

```
{
  "status": 201,
  "data": {
    "action_instance_id": "scrape_person_linkedin_profile_1",
    "action_id": "scrape_person_linkedin_profile",
    "display_name": "Scrape Person LinkedIn Profile",
    "inputs": [\
      {\
        "name": "linkedin_url",\
        "type": "url",\
        "required": true,\
        "description": "LinkedIn profile URL of the person"\
      }\
    ],
    "outputs": [\
      {\
        "name": "full_name",\
        "type": "string",\
        "reference": "{{scrape_person_linkedin_profile_1.full_name}}"\
      },\
      {\
        "name": "headline",\
        "type": "string",\
        "reference": "{{scrape_person_linkedin_profile_1.headline}}"\
      },\
      {\
        "name": "current_company",\
        "type": "string",\
        "reference": "{{scrape_person_linkedin_profile_1.current_company}}"\
      },\
      {\
        "name": "current_company_domain",\
        "type": "url",\
        "reference": "{{scrape_person_linkedin_profile_1.current_company_domain}}"\
      },\
      {\
        "name": "experiences",\
        "type": "raw_array",\
        "reference": "{{scrape_person_linkedin_profile_1.experiences}}",\
        "description": "Work history as JSON. Each item: title, company, start_date, end_date, summary, url, company_domain, company_identifier"\
      },\
      {\
        "name": "education",\
        "type": "raw_array",\
        "reference": "{{scrape_person_linkedin_profile_1.education}}",\
        "description": "Education history as JSON. Each item: school, degree, field, start_date, end_date"\
      }\
    ]
  }
}
```

Try it

## Configure Action

patch`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}`

Configures an action instance. Send only the fields you want to set — unset fields keep their current value.

**Body fields:**

- `inputs` — map of field name → value. String values can be literals, public reference tokens (`{{input.<name>}}` / `{{<action_instance_id>.<name>}}`), or a mix. String-array values are used for waterfall-provider fields (ordered provider IDs). Environment variables are referenced the same way, as `{{env.<key>}}` — see **List Environment Variables** for the available keys.
- `run_if` — optional gate. AND/OR condition groups: `{conditions: [{conditions: [{variable, operator, values?, combinator?}], combinator?}]}` — the same shape as the filter action's `path_conditions` (a single condition is one group with one leaf). Pass `null` to clear an existing condition.
- `continue_workflow_on_failure` — when `true`, downstream actions still run for a row even if this action fails.

For action-specific configuration guidance (prompts, waterfall provider IDs, model selection), see the action detail file at `/docs/action-detail/{action_id}.txt`.

**Supported `operator` values depend on the variable's stored type** — the type is derived from the sheet's chain, so you don't pass it.

- **Universal** (any type): `is`, `is not`, `is empty`, `is not empty`. `is empty` / `is not empty` ignore `values`.
- **String / email / url / imageUrl**: `contains`, `does not contain`, `starts with`, `does not start with`, `ends with`, `does not end with`. Matching is case-insensitive.
- **Number / currency**: `greater than`, `less than`, `greater than or equal to`, `less than or equal to`, `is between` (pass `[min, max]`).
- **Date**: `is after`, `is before`, `is between` (pass `[start, end]`). Values are parsed by `moment(...)`.
- **Array / sectionList / json**: `contains`, `does not contain`.

Pass `values` as an array of strings — number / date matchers coerce as needed.

**Environment variables.** Reference an org environment variable in any string value as `{{env.<key>}}`; Floqer substitutes its value for every row at run time. Get the available keys from **List Environment Variables**. Two cases come back as warnings on a 200:

- `env_variable_unavailable` — the variable is `workflow` scoped and has no value on this workflow. The reference is still saved, but rows error until a value is set for it. Pass `workflow_id` to List Environment Variables to see only the variables usable here.
- `env_not_supported_in_conditions` — an environment variable was used in `run_if` or a Filter action's `path_conditions`. Environment variables are substituted _after_ conditions are evaluated, so one there can never resolve; that condition is skipped rather than saved.

**Cache invalidation.** Any change to this action's configuration — including whitespace-only edits, a new `run_if` predicate, or flipping `continue_workflow_on_failure` — invalidates the cache for every row already processed through it. That action, and every action downstream, re-runs and re-bills on the next execution. See **[Caching](/content/docs/reference#caching/index.html)**.

**Sheet ID convention:** to operate on the workflow's main sheet, pass the workflow's own ID as `sheet_id` — the main sheet's ID equals the workflow ID. For additional sheets under the workflow, pass the child sheet's ID.

**ackDB:** discover Source, Events, settable fields and enum choices through Get Action Field Options. Lookup type spells company `entity`; Update Scope spells it `company`. Configure field lists as `[{name, value}]`, preserving JSON false/0. Person-only ingest ownership comes from the registered event definition, not a request toggle.

Ordinary configure/publish reconciles a missing `ackdb_ingest.accepted` output into live action metadata, preserving existing output UUIDs, names, order and references. A still-old live schema can also acquire it when a new worker completes with a real Accepted boolean. Read-only Get Action/Get Outputs do not upgrade ingest schemas or historical rows. An old worker lacking Accepted omits that output. Immutable shortcut versions remain unchanged: republish a new version for the expanded schema. Conflicting preexisting Accepted UUIDs fail the save instead of silently rewriting references.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance ID (e.g. scrape\_company\_linkedin\_profile\_1). Returned by Add Action to Workflow.

### Request body

`inputs`objectoptional

Map of field name → value. Send only the fields you want to set.

`run_if`object \| nulloptional

Optional gate deciding whether this action runs for a given row. `conditions` is an array of AND/OR condition **groups** — the same shape as the filter action's `path_conditions`. The outer array is groups, joined by each group's `combinator` (AND/OR); each group's `conditions` are leaf conditions, joined by each leaf's `combinator`. The first group's and first leaf's `combinator` is ignored. A single condition is just one group with one leaf. Pass `null` to clear an existing condition; omit to leave it unchanged. See the operator reference in the description above.

`continue_workflow_on_failure`booleanoptional

When `true`, downstream actions still run for a row even if this action fails. When `false` (default), the row stops at this action on failure and downstream cells stay queued.

### Response

`status`integer

`warnings`array

Read this first. Issues that won't block saving but may cause problems at runtime. Each warning has a `field`, a machine-readable `code`, and a human-readable `message`. Common codes: `unknown_field`, `unresolved_reference`, `downstream_reference`, `required_field_missing`, `deprecated_format` (the request used a deprecated-but-still-accepted shape, e.g. the legacy flat `run_if` form — migrate to the documented shape).

`field`string

Field name the warning applies to

`code`string

Machine-readable warning category

`message`string

Human-readable explanation, sometimes with suggestions

`data`object

`action_instance_id`string

### Examples

Wire variables to fields

Variable references, static text, or mixed.

```
{
  "inputs": {
    "linkedin_url": "{{input.linkedin_url}}",
    "prompt": "Research {{input.company_name}} at {{scrape_company_linkedin_profile_1.website}}"
  }
}
```

Waterfall fields

Array of provider IDs in priority order. The first provider is tried first, then the next as fallback. Omit a provider to skip it.

```
{
  "inputs": {
    "waterfall_providers": ["provider_a", "provider_b"]
  }
}
```

Run only when conditions match

Skip this action for rows that don't match the predicate. The action's outputs resolve to empty (\`""\`) for downstream references. Default behavior on skip: the row stops at this action. To keep downstream running on skip or failure, set \`continue\_workflow\_on\_failure: true\`.

```
{
  "inputs": {
    "linkedin_url": "{{input.linkedin_url}}"
  },
  "run_if": {
    "conditions": [\
      {\
        "conditions": [\
          { "variable": "{{input.country}}", "operator": "is", "values": ["US", "CA"] }\
        ]\
      }\
    ]
  }
}
```

Combine conditions with AND / OR

Each group's leaf \`conditions\` are joined by each leaf's \`combinator\`; multiple groups are joined by each group's \`combinator\`. The first group's and first leaf's \`combinator\` is ignored. Operator strings depend on the variable's stored type — numeric uses \`greater than\`, \`less than\`, \`is between\`; universal operators \`is empty\` / \`is not empty\` ignore the \`values\` array.

```
{
  "inputs": { "prompt": "Summarize {{scrape_company_linkedin_profile_1.about}}" },
  "run_if": {
    "conditions": [\
      {\
        "conditions": [\
          { "variable": "{{scrape_company_linkedin_profile_1.employee_count}}", "operator": "greater than", "values": ["100"] },\
          { "variable": "{{input.country}}", "operator": "is", "values": ["US"], "combinator": "AND" }\
        ]\
      }\
    ]
  }
}
```

Clear an existing condition

Pass \`null\` to remove a previously set \`run\_if\`. Omitting the field instead leaves the existing condition unchanged.

```
{
  "run_if": null
}
```

Non-blocking action — continue on failure

For enrichment that's nice-to-have but not required. The row keeps moving through the chain even if this action errors.

```
{
  "inputs": { "linkedin_url": "{{input.linkedin_url}}" },
  "continue_workflow_on_failure": true
}
```

Requestcurl

```
curl -X PATCH "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "inputs": {
    "prompt": "Write a personalized cold email to {{scrape_person_linkedin_profile_1.full_name}} who works at {{scrape_company_linkedin_profile_1.company_name}} as {{scrape_person_linkedin_profile_1.headline}}. Keep it under 100 words.",
    "model": "claude_4_sonnet"
  },
  "run_if": {
    "conditions": [\
      {\
        "conditions": [\
          {\
            "variable": "{{input.country}}",\
            "operator": "is",\
            "values": [\
              "US",\
              "CA"\
            ]\
          }\
        ]\
      }\
    ],
    "continue_workflow_if_run_condition_not_met": false
  },
  "continue_workflow_on_failure": false
}'
```

Response200

```
{
  "status": 200,
  "warnings": [\
    {\
      "field": "model",\
      "code": "unknown_field",\
      "message": "Field 'model' is not recognized by this action and was ignored."\
    },\
    {\
      "field": "prompt",\
      "code": "unresolved_reference",\
      "message": "Reference {{input.linkdin_url}} does not match any input field. Did you mean {{input.linkedin_url}}?"\
    }\
  ],
  "data": {
    "action_instance_id": "ai_generate_content_1"
  }
}
```

Try it

## Get Action Graph

get`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/graph`

Returns every node in a workflow as a flat array, ordered topologically from the input node onward.

**Reading the response:**

- `data[0]` is always the **input node** (`action_id: "input"`). It represents the workflow's input columns and has no `inputs` of its own — its `outputs` list every input field with ready-to-paste `{{input.field_name}}` references. If no inputs have been defined yet, `outputs` is `[]`.
- `data[1..]` are the actions, in topological execution order from the entry action onward. If no actions have been added yet, `data` contains only the input node with `next: []`.
- Walk the chain by following each node's `next` array. `next: []` is terminal.
- Multiple IDs in `next` represent a split path — today only one downstream ID is populated, but the shape accommodates future branching actions without changing.

**Resolving variable references:**`{{X.field}}` → find the node where `action_instance_id === "X"` → look in its `outputs`. This rule works uniformly for `{{input.linkedin_url}}` and for action outputs like `{{scrape_company_linkedin_profile_1.company_name}}`.

**What each action node carries:**

- `inputs` — the variable wiring you set via Configure Action. Unconfigured fields are simply absent from the map.
- `outputs` — the fields this action produces, each with a ready-to-paste `reference` string. Absent on actions with no outputs (e.g. filter).
- `continue_workflow_on_failure` — present only when configured via Configure Action. `true` lets the chain skip past this action's failure for a row.
- `run_if` — present only when a condition is configured via Configure Action. The action runs for a row only when the resolved `variable` satisfies `operator` against `values`.

Use this endpoint to inspect the full graph of a workflow before modifying individual actions. For a single action in isolation, use Get Action.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Response

`status`integer

`data`array

Nodes in topological execution order. `data[0]` is the input node (`action_id: "input"`). `data[1..]` are actions in execution order.

`action_instance_id`string

Unique instance ID for this node. `"input"` for the input node; for actions, the template name suffixed with `_1`, `_2`, etc. (e.g. `scrape_company_linkedin_profile_1`). Used in variable references and when calling Configure Action.

`action_id`string

Template identifier from the action catalog (e.g. `scrape_company_linkedin_profile`). The reserved value `"input"` marks the input node.

`display_name`string

Human-readable action name.

`inputs`object

Map of field name → configured value. Values are variable references (e.g. `{{input.linkedin_url}}`), static strings, or mixed. Unconfigured fields are absent. Empty map `{}` means the action has not been configured yet. **Absent entirely on the input node** — the input node consumes nothing upstream.

`outputs`array

Fields this node produces. For the input node, this lists every workflow input column. For actions, the fields the action produces when it runs. Absent on actions that produce no fields (e.g. filter).

`name`string

`type`string

`reference`string

Ready-to-paste variable reference for downstream actions.

`continue_workflow_on_failure`boolean

When `true`, downstream actions still run for a row even if this action fails. Mirrors the value set via Configure Action's `continue_workflow_on_failure`. Absent when the action has never had it configured.

`run_if`object

Conditional gate that decides whether this action runs for a given row. Always returned in the nested `{conditions: [groups]}` form — the same shape Configure Action accepts (a single condition is one group with one leaf). Absent when no condition is configured.

`conditions`array

AND/OR condition groups. Outer array = groups (joined by each group's `combinator`); each group's `conditions` = leaf conditions (joined by each leaf's `combinator`). The first group's and first leaf's `combinator` is ignored.

`conditions`array

`variable`string

`operator`string

`values`array

`combinator`string

`combinator`string

`continue_workflow_if_run_condition_not_met`boolean

When `true`, downstream actions still run for a row even when this action's condition isn't met (the action itself is skipped). When `false` (default), the row stops at this action when the condition isn't met.

`next`array

IDs of immediate downstream actions. `[]` marks a terminal action. Multiple IDs represent a split path — the shape is ready for future branching actions; today this will contain zero or one entry.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/graph"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "action_instance_id": "input",\
      "action_id": "input",\
      "display_name": "Inputs",\
      "outputs": [\
        {\
          "name": "linkedin_url",\
          "type": "url",\
          "reference": "{{input.linkedin_url}}"\
        },\
        {\
          "name": "company_name",\
          "type": "string",\
          "reference": "{{input.company_name}}"\
        }\
      ],\
      "next": [\
        "scrape_company_linkedin_profile_1"\
      ]\
    },\
    {\
      "action_instance_id": "scrape_company_linkedin_profile_1",\
      "action_id": "scrape_company_linkedin_profile",\
      "display_name": "Scrape Company LinkedIn Profile",\
      "inputs": {\
        "linkedin_url": "{{input.linkedin_url}}"\
      },\
      "outputs": [\
        {\
          "name": "company_name",\
          "type": "string",\
          "reference": "{{scrape_company_linkedin_profile_1.company_name}}"\
        },\
        {\
          "name": "industry",\
          "type": "string",\
          "reference": "{{scrape_company_linkedin_profile_1.industry}}"\
        },\
        {\
          "name": "employee_count",\
          "type": "number",\
          "reference": "{{scrape_company_linkedin_profile_1.employee_count}}"\
        }\
      ],\
      "run_if": {\
        "conditions": [\
          {\
            "conditions": [\
              {\
                "variable": "{{input.country}}",\
                "operator": "is",\
                "values": [\
                  "US",\
                  "CA"\
                ]\
              }\
            ]\
          }\
        ],\
        "continue_workflow_if_run_condition_not_met": false\
      },\
      "continue_workflow_on_failure": false,\
      "next": [\
        "filter_1"\
      ]\
    },\
    {\
      "action_instance_id": "filter_1",\
      "action_id": "filter",\
      "display_name": "Filter",\
      "inputs": {\
        "condition": "{{scrape_company_linkedin_profile_1.employee_count}} > 100"\
      },\
      "next": [\
        "ai_generate_content_1"\
      ]\
    },\
    {\
      "action_instance_id": "ai_generate_content_1",\
      "action_id": "ai_generate_content",\
      "display_name": "AI Generate Content",\
      "inputs": {\
        "prompt": "Write a cold email to {{scrape_company_linkedin_profile_1.company_name}} about {{scrape_company_linkedin_profile_1.industry}}.",\
        "model": "claude_4_sonnet"\
      },\
      "outputs": [\
        {\
          "name": "generated_content",\
          "type": "string",\
          "reference": "{{ai_generate_content_1.generated_content}}"\
        }\
      ],\
      "next": []\
    }\
  ]
}
```

Try it

## Get Action

get`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}`

Returns a single action from a workflow. The response `data` is the same per-node shape emitted by Get Action Graph — use this endpoint when you only need one action and don't want to fetch the entire graph.

**Round-tripping with Configure Action:** the `inputs` map in this response matches the body Configure Action accepts. GET the current action, modify `inputs`, then `PATCH` the same URL (Configure Action) to save.

**Validation warnings on read.** The response includes a `warnings` array whenever the action's current configuration has issues (unresolved references, downstream references, required fields missing). Agents inspecting a workflow they didn't build — or revisiting one after changes upstream — can spot broken config without having to re-PATCH. Same warning shape as Configure Action's response, so one parsing path covers both.

**Reserved `action_instance_id` values:**`"input"` (the input pseudo-node — use List Inputs or `data[0]` of Get Action Graph) and `"graph"` (the graph endpoint path — use Get Action Graph). Both return 404 here.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance ID (e.g. \`scrape\_company\_linkedin\_profile\_1\`). Returned by Add Action to Workflow, or found in the Get Action Graph response. Reserved values: \`"input"\` (input pseudo-node) and \`"graph"\` (the graph endpoint path) — both return 404 here.

### Response

`status`integer

`warnings`array

Validation issues with the action's current configuration, detected at read time. Absent or empty when there are no issues. Same shape and `code` values as the `warnings` returned by Configure Action — common codes: `unresolved_reference`, `downstream_reference`, `required_field_missing`, `unknown_field`.

`field`string

Field name the warning applies to.

`code`string

Machine-readable warning category.

`message`string

Human-readable explanation, sometimes with suggestions.

`data`object

The action node. Same shape as nodes in the `data` array returned by Get Action Graph.

`action_instance_id`string

Unique instance ID for this action (e.g. `scrape_company_linkedin_profile_1`). Used in variable references and when calling Configure Action.

`action_id`string

Template identifier from the action catalog (e.g. `scrape_company_linkedin_profile`).

`display_name`string

Human-readable action name.

`inputs`object

Map of field name → configured value. Values are variable references (e.g. `{{input.linkedin_url}}`), static strings, or mixed. Unconfigured fields are absent. Empty map `{}` means the action has not been configured yet.

`outputs`array

Fields this action produces, each with a ready-to-paste `reference` string for downstream actions. Absent on actions that produce no fields (e.g. filter).

`name`string

`type`string

`reference`string

Ready-to-paste variable reference for downstream actions.

`continue_workflow_on_failure`boolean

`run_if`object

`conditions`array

`conditions`array

`variable`string

`operator`string

`values`array

`combinator`string

`combinator`string

`continue_workflow_if_run_condition_not_met`boolean

`next`array

IDs of immediate downstream actions. `[]` marks a terminal action. Multiple IDs represent a split path — today this will contain zero or one entry.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "warnings": [\
    {\
      "field": "linkedin_url",\
      "code": "unresolved_reference",\
      "message": "Reference {{input.linkdin_url}} does not match any input field. Did you mean {{input.linkedin_url}}?"\
    }\
  ],
  "data": {
    "action_instance_id": "scrape_company_linkedin_profile_1",
    "action_id": "scrape_company_linkedin_profile",
    "display_name": "Scrape Company LinkedIn Profile",
    "inputs": {
      "linkedin_url": "{{input.linkdin_url}}"
    },
    "outputs": [\
      {\
        "name": "company_name",\
        "type": "string",\
        "reference": "{{scrape_company_linkedin_profile_1.company_name}}"\
      },\
      {\
        "name": "industry",\
        "type": "string",\
        "reference": "{{scrape_company_linkedin_profile_1.industry}}"\
      },\
      {\
        "name": "employee_count",\
        "type": "number",\
        "reference": "{{scrape_company_linkedin_profile_1.employee_count}}"\
      }\
    ],
    "run_if": {
      "conditions": [\
        {\
          "conditions": [\
            {\
              "variable": "{{input.country}}",\
              "operator": "is",\
              "values": [\
                "US",\
                "CA"\
              ]\
            }\
          ]\
        }\
      ],
      "continue_workflow_if_run_condition_not_met": false
    },
    "continue_workflow_on_failure": false,
    "next": [\
      "filter_1"\
    ]
  }
}
```

Try it

## Delete Action

delete`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}`

Removes an action from the sheet's chain by relinking its previous and next neighbours so the chain skips it.

**Returns the affected inputs.** Any action whose configuration references one of the deleted action's outputs is listed in `dependent_actions[]`, with `inputs` carrying only the fields that actually referenced the deleted action. Values are translated to public reference form so the broken refs are easy to spot. Unrelated fields on the same dependent action are not included. The caller is expected to fix the listed fields via Configure Action.

Cannot delete the input action — pass any other `action_instance_id`. Attempting to delete the input action returns 400. The reserved string IDs `"input"` and `"graph"` (the graph endpoint path) return 404.

**Cache invalidation.** Removing an action invalidates the cache for every row already processed through that action. The remaining chain — including all downstream actions — re-runs and re-bills on the next execution. See **[Caching](/content/docs/reference#caching/index.html)**.

**Not idempotent.** Deleting an `action_instance_id` that does not exist returns 404. Use Get Action Graph beforehand if you need to confirm presence first.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance ID to delete (e.g. \`scrape\_company\_linkedin\_profile\_1\`). Returned by Add Action to Workflow, or found in the Get Action Graph response.

### Response

`status`integer

`data`object

`deleted`boolean

Always `true` on a 200 response — the action was removed from the chain.

`action_instance_id`string

Echoes the action instance that was deleted.

`dependent_actions`array

Actions whose configuration referenced one of the deleted action's outputs. Empty when nothing depended on it. Each entry carries only the affected fields — keys are the dependent action's input field names (snake-cased), values are the stored `inputString` translated to public reference form so the broken refs are obvious. The caller is expected to re-wire these via Configure Action.

`action_instance_id`string

The dependent action's instance ID.

`inputs`object

Map of `<snake_field_name>` → input value (string). Only fields that referenced the deleted action are included; unrelated fields on the same action are omitted.

Requestcurl

```
curl -X DELETE "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "deleted": true,
    "action_instance_id": "scrape_company_linkedin_profile_1",
    "dependent_actions": [\
      {\
        "action_instance_id": "ai_generate_content_1",\
        "inputs": {\
          "prompt": "Write a cold email to {{scrape_company_linkedin_profile_1.company_name}} about {{scrape_company_linkedin_profile_1.industry}}."\
        }\
      }\
    ]
  }
}
```

Try it

## Get Action Field Options

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/options/{field_name}`

Fetches the dropdown / dynamic option values for one of an action's input fields. Use it when configuring an action whose field expects a value from a connected integration (Salesforce objects, Instantly campaigns, HubSpot properties, etc.).

Pass the action's `action_instance_id` and the snake-cased `field_name` (both come back in **Add Action**'s response) — the server dispatches to the matching resolver internally.

**Cascading resolvers** (e.g. Salesforce: pick object → pick external ID field) take their parent selection via `context`. Each cascading resolver declares its required context keys; calling without them returns a 400 with the list of missing keys so callers can fetch the parent first.

Returns options as `{ value, label, extras? }`. `value` is what to send back via **Configure Action**; `extras` carries per-integration metadata when relevant.

**ackDB:**`ackdb_ingest:source` lists enabled source tokens. `ackdb_entity_lookup:events` uses `context.lookup_type` (`entity` for company, `person`, `contact`, or `member`), falling back to the saved literal Lookup type only when context omits it. Missing, unresolved, or invalid lookup types return 400; they never silently select company. Person options include person-only events; company options exclude them. Event coverage is data-gated, so a newly registered type may not appear until data exists. Values are `source:event_type`. Contact/member intentionally return no event options.

`ackdb_update_record` exposes `company_fields_to_set`, `person_fields_to_set`, and `contact_fields_to_set`, including registered attributes, core fields, supported identity keys, and company/contact owners. Optional `context.scope` must match the selected field catalog. Lookup type, Scope and boolean switches also expose static options; boolean option strings `true`/`false` can be configured as JSON booleans. `ackdb_company_relationship` exposes `relationship_target`, `operation`, and `relationship_type`; explicit context overrides its saved target. Relationship targets remain case-insensitive; legacy missing/empty target defaults to company. Company operations are create/end/retract/merge; contact operations are contact\_create/contact\_end/contact\_retract. The legacy `ackdb_merge_record` action has no dropdown fields.

ackDB catalogs use the workflow connection's stored baseUrl/apiKey, like action execution. A missing connection returns 424; upstream failures return 502 instead of silently substituting another grain or a partial field catalog.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance ID whose field needs options. Returned by \*\*Add Action\*\*, \*\*Get Action\*\*, or \*\*Get Action Graph\*\*.

`field_name`required

Snake-cased input field name from the action's \`inputs\[\]\` (matches what \*\*Add Action\*\* returns and what \*\*Configure Action\*\* accepts).

### Request body

`context`objectoptional

Optional context for cascading resolvers. Keys are snake-cased and match the parent field's public name. Calling without a required key returns 400 listing the accepted spellings.

### Response

`status`integer

`data`object

`options`array

Available option values for the requested field.

`value`string

The value to send back via Configure Action.

`label`string

Human-readable label for the option.

`extras`object

Per-integration metadata. Shape varies by integration — use it for richer rendering when needed.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id/options/:field_name"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "context": {
    "salesforce_object": "Account"
  }
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "options": [\
      {\
        "value": "Account",\
        "label": "Account"\
      },\
      {\
        "value": "Contact",\
        "label": "Contact"\
      },\
      {\
        "value": "Lead",\
        "label": "Lead"\
      }\
    ]
  }
}
```

Try it

## Get Action Output Schema

get`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/outputs`

Returns the **output schema** of an action — the names, types, and reference tokens of the fields it produces, for wiring into downstream actions. **This does not return the values the action has produced** — for that, use **List Rows**.

**Static vs. dynamic actions.** Static-output actions (Salesforce, HubSpot, enrichment providers, etc.) have a fixed output schema — this endpoint returns it immediately, no run required. Dynamic-output actions — `http_api_call`, `raw_to_structured_array`, AI generations with custom output shapes — only know their schema after they run, and **the schema returned here reflects only the latest run**. There's no historical view.

**Wiring implication.** To reference a dynamic action's outputs from a downstream action, run it against at least one row first. The schema then populates and downstream `Configure Action` calls can wire `{{<action_instance_id>.<field>}}` into their inputs.

**`structured_array` outputs** carry per-column schemas under `columns[]`, each with its own `structured_array_reference` token shaped `{{<action_instance_id>.<list>.<column>}}`.

For `ackdb_ingest`, this GET reports the stored schema without reconciling old 19-output configurations. Configure/publish or a future boolean-bearing live completion adds Accepted while preserving old UUIDs. Existing row/cell outputs are historical stored values: they are never backfilled, even when the current schema includes Accepted. The frontend may derive a legacy display from recorded Results; the public API does not invent that field. Frozen shortcut versions require republishing a new version.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance whose output schema to return. Returned by \*\*Add Action\*\*, \*\*Get Action\*\*, or \*\*Get Action Graph\*\*.

### Response

`status`integer

`data`object

`action_instance_id`string

Echo of the path parameter, for response-level self-description.

`outputs`array

Fields this action produces, each with a ready-to-paste `reference` token for downstream wiring. Empty array for actions that produce no fields (e.g. `filter`), or for dynamic-output actions that haven't run yet.

`name`string

Output field identifier (snake\_case).

`type`string

Output data type (e.g. `string`, `url`, `number`, `raw_array`, `json`, `structured_array`). `structured_array` is a list of structured rows; per-row schema is exposed via `columns`.

`reference`string

Variable reference token. Drop verbatim into any downstream action's input via **Configure Action** to pull this output's value at run time. Shape: `{{<action_instance_id>.<name>}}`. For an individual column of a `structured_array`, use the 3-segment form `{{<action_instance_id>.<list>.<column>}}` (also exposed on each entry of `columns[].structured_array_reference`).

`description`string

Human-readable description of this output.

`columns`array

Present only on `structured_array` outputs. One entry per column, each with its own `structured_array_reference` token. Empty / absent for outputs whose columns aren't yet configured (e.g. a freshly added `raw_to_structured_array` action that hasn't run).

`name`string

Column identifier (snake\_case).

`type`string

Column data type.

`structured_array_reference`string

3-segment reference token for this column inside the parent `structured_array`. Shape: `{{<action_instance_id>.<list>.<column>}}`.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id/outputs"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 0,
  "data": {
    "action_instance_id": "string",
    "outputs": [\
      {\
        "name": "full_name",\
        "type": "string",\
        "reference": "{{enrich_person_linkedin_profile_1.full_name}}",\
        "description": "string",\
        "columns": [\
          {\
            "name": "string",\
            "type": "string",\
            "structured_array_reference": "{{raw_to_structured_array_1.list.first_name}}"\
          }\
        ]\
      }\
    ]
  }
}
```

Try it

## Rename Action

patch`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/name`

Sets the display name of an action instance. Single-field PATCH — the only body field is `name`.

**Why rename.** The default name is the action's template name (e.g. `Enrich Company LinkedIn Profile`). That's fine for a one-off, but a workflow that uses the same action multiple times — or several similar actions side by side — ends up with ambiguous duplicates. Rename each instance to reflect the specific role it plays in the chain so collaborators reviewing the workflow, and you debugging it later, can tell the nodes apart at a glance.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance ID (e.g. \`enrich\_company\_linkedin\_profile\_1\`). Returned by Add Action to Workflow.

### Request body

`name`stringrequired

New display name for the action instance. Trimmed; cannot be empty after trimming.

### Response

`status`integer

`data`object

`action_instance_id`string

Echo of the path parameter — the action instance that was renamed. Unchanged by the rename; variable references (`{{<action_instance_id>.<field>}}`) keep working.

`display_name`string

The stored display name.

### Examples

Rename to a friendly label

Replace the auto-generated \`enrich\_company\_linkedin\_profile\_1\` with something readable for collaborators.

```
{
  "name": "Enrich primary contact"
}
```

Requestcurl

```
curl -X PATCH "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id/name"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "name": "Enrich primary contact"
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "action_instance_id": "enrich_company_linkedin_profile_1",
    "display_name": "Enrich primary contact"
  }
}
```

Try it

## Move Action

patch`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/move`

Re-orders an action within the sheet's chain. Updates chain pointers on the moved action, its old neighbours, and its new neighbours; configured inputs and outputs are untouched.

**Positioning.** Omit `after` to move to the end of the chain. Pass `"input"` to move to the start (immediately after the synthetic input node). Otherwise pass an existing `action_instance_id` to move directly after that instance. `after` cannot equal the action being moved, and the input action itself cannot be moved.

**Reference safety.** Move does NOT rewrite the action's configured inputs. If the new position pushes the action ahead of an upstream reference — or pushes a downstream consumer ahead of this action — those references will fail at runtime. Fix broken refs afterwards via Configure Action.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance ID to move (e.g. \`enrich\_company\_linkedin\_profile\_1\`). Returned by Add Action to Workflow, or found in the Get Action Graph response.

### Request body

`after`stringoptional

Move the action immediately after this `action_instance_id`. Omit to move to the end of the chain. Pass `"input"` to move to the start of the chain (right after the synthetic input node). NOTE: this is an **instance ID** (e.g. `scrape_company_linkedin_profile_1`), not an `action_id`. Cannot equal the action being moved.

### Response

`status`integer

`data`object

`moved`boolean

Always `true` on a 200 response — the action has been re-ordered.

`action_instance_id`string

Echoes the action instance that was moved.

`after`string

The `action_instance_id` the moved action now sits immediately after. `"input"` indicates the moved action now sits at the start of the chain (right after the synthetic input node).

### Examples

Move after a specific action

Place this action immediately after \`scrape\_company\_linkedin\_profile\_1\` in the chain.

```
{
  "after": "scrape_company_linkedin_profile_1"
}
```

Move to the start of the chain

Pass the literal \`"input"\` to position the action right after the synthetic input node.

```
{
  "after": "input"
}
```

Requestcurl

```
curl -X PATCH "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id/move"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "after": "scrape_company_linkedin_profile_1"
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "moved": true,
    "action_instance_id": "enrich_company_linkedin_profile_1",
    "after": "scrape_person_linkedin_profile_1"
  }
}
```

Try it

## Reorder Waterfall Providers

patch`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/waterfall/{field_name}`

Re-orders the provider list on a waterfall (`stepDownSearch`) input field. Pass the snake-cased `field_name` from **Add Action to Workflow**'s `inputs[]` and a `providers` array listing every currently configured provider `apiId` exactly once, in the desired execution order.

This endpoint only reorders — it does not add or remove providers. To change which providers are enabled, use **Configure Action**.

**Cache.** Same as **Configure Action** — reordering invalidates the row-level cache for this action and every downstream action on the sheet.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance whose waterfall field to reorder (e.g. \`find\_work\_email\_1\`).

`field_name`required

Snake-cased waterfall input field name from the action's \`inputs\[\]\` (e.g. \`work\_email\`).

### Request body

`providers`arrayrequired

Ordered list of provider `apiId`s. Must match the set of providers currently configured on the field — reorder only, no adds or removes.

### Response

`status`integer

`data`object

`reordered`boolean

`action_instance_id`string

`field_name`string

`providers`array

### Examples

Try Hunter before Prospeo

Reorder a work-email waterfall so Hunter runs first.

```
{
  "providers": ["hunter", "prospeo", "findymail"]
}
```

Requestcurl

```
curl -X PATCH "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id/waterfall/:field_name"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "providers": [\
    "hunter",\
    "prospeo",\
    "findymail"\
  ]
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "reordered": true,
    "action_instance_id": "find_work_email_1",
    "field_name": "work_email",
    "providers": [\
      "hunter",\
      "prospeo",\
      "findymail"\
    ]
  }
}
```

Try it

## Save Action Note

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/notes`

Attaches a free-text note to an action instance. One note per action — calling again on the same action overwrites whatever was there. Notes come back on the action via **Get Action** and on each node of **Get Action Graph** as a `note` field (absent when none has been saved).

Use this to flag work-in-progress configuration, document why an action is parked, or leave context for collaborators reviewing the workflow. The note is workflow content, not a system field — it does NOT change what the action does and does NOT invalidate the row-level cache. The workflow's `updated_at` timestamp is bumped on save.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance the note attaches to (e.g. \`enrich\_company\_linkedin\_profile\_1\`). Returned by \*\*Add Action\*\*, \*\*Get Action\*\*, or \*\*Get Action Graph\*\*. Reserved values \`"input"\` and \`"graph"\` return 404.

### Request body

`note`stringrequired

Free-text note to attach to the action. Trimmed; cannot be empty after trimming.

### Response

`status`integer

`data`object

`action_instance_id`string

Echo of the path parameter — the action the note is attached to.

`sheet_id`string

Sheet the note is scoped to.

`note`string

The saved note text.

### Examples

Park an action with rationale

Document why an action is configured but intentionally inactive.

```
{
  "note": "Skip until Phase 2 — vendor still negotiating contract terms."
}
```

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id/notes"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "note": "Skip until Phase 2 — vendor still negotiating contract terms."
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "action_instance_id": "enrich_company_linkedin_profile_1",
    "sheet_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "note": "Skip until Phase 2 — vendor still negotiating contract terms."
  }
}
```

Try it

### Sheets

3endpoints

A workflow has one or more sheets — like tabs in a spreadsheet, except each tab has its own automation pipeline. Each sheet is an independent data table with its own inputs, actions, data rows, run history, and settings (`auto_run`,`cache_enabled`, `cache_since`). The main sheet is created automatically and the main sheet ID is the same as the workflow ID — additional sheets can be added and removed, but the main sheet is permanent.

Sheets connect to each other through the`send_to_sheet` action. Any sheet can send rows to any other sheet in the same workflow — this is how data flows between sheets.

**Common use cases:**

- **Array expansion**— an action returns a`structured_array` (e.g. employees at a company). `send_to_sheet` fans each item into its own row on a new sheet for independent enrichment.
- **Segmentation**— categorize and filter rows, then use`send_to_sheet` to route subsets to different sheets (e.g. Tier A to one sheet, Tier B+C to another) for clean data separation.
- **Enrichment staging**— keep raw source data on the main sheet, then use `send_to_sheet` to send enriched and cleaned records to a separate output sheet.
- **Multi-source consolidation**— import different data sources (CSVs, webhooks, CRM exports) into separate sheets, then use `send_to_sheet` to feed processed records from each source into a common destination sheet.

Every sheet-scoped operation (inputs, actions, data, runs) lives under`/workflows/{workflow_id}/sheets/{sheet_id}/...`. To operate on the main sheet, pass the workflow's ID as both`{workflow_id}` and`{sheet_id}` — the main sheet's ID equals the workflow ID.

abc123(workflow ID = main sheet ID)

├── ghi789— “Employees” (array expansion)

├── jkl012— “Tier A Companies” (segmentation)

└── mno345— “Enriched Leads” (enrichment staging)

## Create Sheet

post`/api/v1/workflows/{workflow_id}/sheets`

Adds a new sheet to the workflow identified by `workflow_id`. Returns the full sheet object. Pass the returned `sheet_id` as the `{sheet_id}` path parameter on every sheet-scoped endpoint (Add Inputs, Add Action, Add Data, Run Sheet, etc.).

**Routing data into the new sheet.** Sheets receive rows via the `send_to_sheet` action on another sheet in the same workflow. On the source sheet, add `send_to_sheet`, set `target_sheet_id` to the `sheet_id` returned here, and map source fields to the new sheet's input columns. To expand a `structured_array` (one row per array item), set `expand_from` on the `send_to_sheet` action. Full configuration: [`/docs/action-detail/send_to_sheet.txt`](/content/docs/action-detail/send_to_sheet.txt).

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

### Request body

`name`stringrequired

Sheet name. Not required to be unique within a workflow.

`auto_run`booleanoptional

When `true`, the sheet's action chain runs automatically whenever new data rows arrive — via Add Data Rows or via a `send_to_sheet` action on another sheet. When `false` (default), runs must be triggered explicitly with Run Rows.

`cache_enabled`booleanoptional

When `true`, a row with input column values identical to a previously-run row reuses the previous row's outputs instead of re-running the action chain. Matching compares every input column value on the row as a string, after variable references are resolved. Default: `false`.

`cache_since`dateoptional

Earliest date (YYYY-MM-DD, UTC) from which cached runs are considered fresh. Runs performed on or after this date are reusable; runs performed before are ignored even when inputs match. Ignored when `cache_enabled` is `false`. Default: `null` — all cached runs are considered fresh regardless of age.

### Response

`status`integer

`data`object

`sheet_id`uuid

UUID of the sheet. Pass as `{sheet_id}` on every sheet-scoped endpoint. Also used as `target_sheet_id` when another sheet routes data here via `send_to_sheet`.

`workflow_id`uuid

UUID of the workflow this sheet belongs to.

`name`string

Sheet name as set at creation.

`auto_run`boolean

When `true`, the sheet's action chain runs automatically whenever new data rows arrive (via Add Data Rows or via `send_to_sheet` from another sheet). When `false`, runs must be triggered explicitly with Run Rows.

`cache_enabled`boolean

When `true`, a row with input column values identical to a previously-run row reuses the previous row's outputs instead of re-running the action chain. When `false`, every row triggers a fresh run.

`cache_since`date

Earliest date (YYYY-MM-DD, UTC) from which cached runs are considered fresh. Runs before this date are ignored even when inputs match. `null` when unset — all cached runs are considered fresh regardless of age.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "name": "Employees",
  "auto_run": false,
  "cache_enabled": false,
  "cache_since": "2026-03-17"
}'
```

Response201

```
{
  "status": 201,
  "data": {
    "sheet_id": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "name": "Employees",
    "auto_run": false,
    "cache_enabled": false,
    "cache_since": null
  }
}
```

Try it

## Rename Sheet

patch`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/name`

Sets a sheet's name. Single-field PATCH — the only body field is `name` (required, trimmed, non-empty).

**Renaming the main sheet does NOT rename the workflow.** Pass `sheet_id === workflow_id` to rename the main sheet's tab; the workflow's own name is left untouched, and Get Workflow Overview keeps returning it as `data.name` while `data.sheets[0].name` changes to the new label. To rename the workflow itself, use `PATCH /api/v1/workflows/{workflow_id}/name` (Rename Workflow).

**Cache.** Renaming does not affect row data, cached action runs, or the action chain — only the sheet's name. The workflow's `updated_at` timestamp is bumped.

**Sheet ID convention:** sheet IDs come from Get Workflow Overview (`data.sheets[].sheet_id`) or from Create Sheet. The main sheet's ID equals the workflow ID.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Parent workflow ID.

`sheet_id`required

Sheet ID to rename. Pass the workflow's own ID to rename the main sheet's tab (the workflow name is not changed).

### Response

`status`integer

`data`

Requestcurl

```
curl -X PATCH "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/name"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "name": "Qualified employees"
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "sheet_id": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
    "name": "Qualified employees"
  }
}
```

Try it

## Delete Sheet

delete`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}`

Permanently deletes a child sheet from the workflow. Archives the sheet and its action chain; row data and run history on the sheet are no longer reachable. **Cannot be undone.**

Only the sheet owner can delete a child sheet. Shared users and org members with workflow access cannot delete sheets they do not own.

The workflow's main sheet cannot be deleted (`sheet_id` must not equal `workflow_id`). To remove the entire workflow including its main sheet, use **Delete Workflow**.

**Sheet ID convention:** pass the child sheet's UUID as `sheet_id` — not the parent workflow ID.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Parent workflow ID.

`sheet_id`required

Child sheet ID to delete. Must not equal \`workflow\_id\` (the main sheet).

### Response

`status`integer

`data`object

`workflow_id`uuid

UUID of the parent workflow the sheet belonged to.

`sheet_id`uuid

UUID of the child sheet that was deleted.

`deleted`boolean

Always `true` on a 200 response. Included so agents can branch on a single field.

Requestcurl

```
curl -X DELETE "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "sheet_id": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
    "deleted": true
  }
}
```

Try it

## Run a Workflow

Process data through a configured workflow. The typical loop while building a workflow is: add a small number of rows (10–50), run, analyze the output, adjust the next action's configuration, run again. Expand to larger batches (100 → 1,000 → all) only once you are confident in the output.

Each **cell** — one action's execution on one row — is an independent execution unit with a lifecycle: `queued → running → complete | failed`. A row's run is a set of cells (one per action in the chain) that execute and bill independently. Runs are async — submit rows and poll **List Rows** for per-cell status and outputs.

**Cost safety — read this carefully.** Every action call consumes credits. A workflow with 5 actions × 10,000 rows is 50,000 action calls. **Never run all rows without first running 10–50 and verifying output.** Even when satisfied with the 10–50, still expand in batches (100 → 1,000 → all) before running the full dataset. Burning credits on a misconfigured workflow is the single biggest risk in this section.

**Caching.** When `cache_enabled` is on, a cell re-runs free if its resolved inputs exactly match a prior run. See **[Caching](/content/docs/reference#caching/index.html)**.

**Sheets:** to run a non-main sheet, use the sheet's ID in place of the workflow ID. The same endpoints work.

## Add Rows

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows`

Adds rows to a sheet. Each row's keys are input field names (from **Add Inputs**); values are written as-is — there is no type checking at the API layer, so any type mismatches surface later when downstream actions try to consume the data. Missing fields are stored as null; unknown fields come back in `rejected`.

**Partial success — never atomic.** Accepted rows are written and their UUIDs returned in `row_ids` — pipe directly into **Run Rows**'s `row_ids`. Any rows the server rejects are echoed back in `rejected` with their original content and field-level error codes so you can fix and resend just those. A batch of 10,000 with 5 bad rows writes 9,995 and returns the 5 failures in `rejected`.

**Max 1,000 rows per call.**

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Request body

`rows`arrayrequired

Rows to add. Each row is a JSON object whose keys are input field names (as defined by Add Inputs) and whose values match the field's declared type. Max 1,000 per call.

`run_after_add`stringoptional

What to run after rows are written. **Scoped to the rows in this call only** — pre-existing rows on the sheet are never touched. `none` (default) — add rows, don't run. `first_10` — queue 10 of the newly-added rows for execution; recommended build-loop default. `all` — queue every newly-added row (⚠️ full credit cost = N rows × actions in chain; avoid until output is verified on `first_10`).

### Response

`status`integer

`warnings`array

Non-blocking issues across the batch (e.g. duplicate detection, normalized values). Shape matches Configure Action's warnings.

`field`string

`code`string

`message`string

`data`object

`row_count`integer

Number of rows accepted and written. Equals `row_ids.length`.

`row_ids`array

Flat array of newly-assigned row UUIDs, in request order. Rejected rows are not included. Copy directly into **Run Rows**'s `row_ids` to execute.

`rejected`array

Rows the server couldn't accept (e.g. malformed shape, unknown fields). Valid rows in the same batch were still written — this list contains only the failures. Each entry echoes the original row content alongside field-level error codes so you can fix and resend just those rows.

`row`object

The original row content as you sent it — echoed back exactly so you can fix and resend without having to track positions in your request.

`errors`array

`field`string

`code`string

Machine-readable code indicating the failure reason (e.g. `malformed_row`, `unknown_field`).

`message`string

`rows_queued_for_run`integer

How many newly-added rows were queued for execution. `0` when `run_after_add: "none"`. `10` when `"first_10"`. Equal to `row_count` when `"all"`.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/rows"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "rows": [\
    {\
      "linkedin_url": "https://linkedin.com/company/floqer",\
      "email": "hello@floqer.com",\
      "company_name": "Floqer"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/acme",\
      "email": "contact@acme.com",\
      "company_name": "Acme"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/globex",\
      "email": "ops@globex.com",\
      "company_name": "Globex"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/hooli",\
      "email": "hi@hooli.com",\
      "company_name": "Hooli"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/initech",\
      "email": "info@initech.com",\
      "company_name": "Initech"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/pied-piper",\
      "email": "team@piedpiper.com",\
      "company_name": "Pied Piper"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/soylent",\
      "email": "info@soylent.com",\
      "company_name": "Soylent"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/dundermifflin",\
      "email": "sales@dundermifflin.com",\
      "company_name": "Dunder Mifflin"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/stark",\
      "email": "hi@stark.com",\
      "company_name": "Stark Industries"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/wayne",\
      "email": "info@wayne.com",\
      "company_name": "Wayne Enterprises"\
    },\
    {\
      "linkedin_url": "https://linkedin.com/company/umbrella",\
      "email": "contact@umbrella.com",\
      "company_name": "Umbrella"\
    }\
  ],
  "run_after_add": "first_10"
}'
```

Response201

```
{
  "status": 201,
  "warnings": [],
  "data": {
    "row_count": 11,
    "row_ids": [\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567001",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567002",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567003",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567004",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567005",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567006",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567007",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567008",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567009",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567010",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567011"\
    ],
    "rejected": [],
    "rows_queued_for_run": 10
  }
}
```

Try it

## Run Rows

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/run`

Runs a subset of the sheet's rows through the action chain. Specify the subset in one of two ways:

- **`row_ids`** — array of specific row UUIDs (from **Add Rows** or **List Rows**). Rows not listed are untouched.
- **`first_10: true`** — shortcut for "run the first 10 rows on the sheet" (by `created_at` ascending). Safe experimentation default.

**Exactly one** of `row_ids` or `first_10` must be provided. Both or neither returns 400.

**Asynchronous.** The endpoint returns immediately after queueing. Poll **List Rows** for per-cell status and outputs — each cell (one action × one row) queues, runs, and bills independently.

**To run every row on the sheet**, use **Run All Rows** instead — a separate endpoint (no request body), so a misread filter can never run the whole sheet.

**Prerequisites:** the sheet has at least one action configured, and the rows in `row_ids` (if provided) already exist on the sheet.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Request body

`row_ids`arrayoptional

Row UUIDs to queue through the action chain. Get these from **Add Rows** (`row_ids` response field) or **List Rows**. Rows on the sheet not included here are not re-run. Mutually exclusive with `first_10`.

`first_10`booleanoptional

Shortcut for "run the first 10 rows on the sheet" (by `created_at` ascending). Useful for experimentation and sampling on existing sheets. Mutually exclusive with `row_ids`. Pass `true` to enable; omit or pass `false` to use `row_ids` instead.

### Response

`status`integer

`message`string

`data`object

`rows_queued`integer

Number of rows queued for execution. Equals `row_ids.length` when `row_ids` was provided, or `10` (or fewer if the sheet has fewer rows) when `first_10: true` was provided.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/run"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "row_ids": [\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567001",\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567002",\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567003"\
  ]
}'
```

Response200

```
{
  "status": 200,
  "message": "Rows queued for execution",
  "data": {
    "rows_queued": 3
  }
}
```

Try it

## Run All Rows

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/run-all`

Queues **every row** on the sheet for execution through the action chain. No request body required — the path is the contract.

Same async semantics as **Run Rows** — the endpoint returns immediately after queueing; poll **List Rows** for per-cell status and outputs.

**⚠️ Credit cost.** Scales with (rows on sheet) × (actions in chain). A sheet with 10,000 rows and 5 actions costs 50,000 cell executions. **Do not call this without first running a sample (e.g. `run_after_add: "first_10"` on Add Rows, or Run Rows with `first_10: true`) and verifying outputs.** See the **Run a Workflow** tag for the full build-loop guidance.

**Separate from Run Rows** (which takes `row_ids` or `first_10`) so an agent can't accidentally run the whole sheet by misinterpreting a filter. Cache-enabled cells with unchanged resolved inputs still hit cache and don't re-bill.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Response

`status`integer

`message`string

`data`object

`rows_queued`integer

Number of rows queued for execution. Equals the total row count on the sheet at the time of the call.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/run-all"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "message": "All rows queued for execution",
  "data": {
    "rows_queued": 157
  }
}
```

Try it

## Run Action

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/run`

Runs a single action against a set of rows — useful for refreshing one action's outputs after a **Configure Action** edit, without re-running everything upstream of it.

**By default, only the targeted action runs.** Nothing downstream of it in the chain executes. To also run the rest of the chain after this action finishes, set `run_next_action: true` in the body.

**Body:**

- `row_ids` — array of row UUIDs (from **Add Rows** or **List Rows**). Omit or pass an empty array to queue every row on the sheet. Capped at 1000 IDs per request.
- `run_next_action` — `false` (default) runs only the targeted action. `true` runs the targeted action then continues through the rest of the chain, re-running every action that comes after it. Useful after **Configure Action** when you want to refresh a single action _and_ everything downstream of it in one call.

**Asynchronous.** Returns immediately after queueing. Poll **List Rows** for per-cell status and outputs — each cell (one action × one row) queues, runs, and bills independently.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`action_instance_id`required

The action instance to run. Returned by \*\*Add Action\*\*, \*\*Get Action\*\*, or \*\*Get Action Graph\*\*.

### Request body

`row_ids`arrayoptional

Row UUIDs to queue against the targeted action. From **Add Rows** (`row_ids` response field) or **List Rows**. Omit or pass an empty array to run every row on the sheet.

`run_next_action`booleanoptional

Whether to continue execution down the chain after the targeted action finishes. `false` (default) runs ONLY the targeted action — all actions downstream of it are skipped. `true` runs the targeted action then continues through the rest of the chain, re-running every action that comes after it.

### Response

`status`integer

`message`string

`data`object

`rows_queued`integer

Number of rows queued for execution against the targeted action. `0` when the sheet has no rows (and `row_ids` was empty / omitted).

### Examples

Run only this action for selected rows

Default behavior. Useful for retrying a few rows that failed at this action, or spot-checking changes after a \*\*Configure Action\*\* edit.

```
{
  "row_ids": [\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567001",\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567002"\
  ]
}
```

Refresh the chain after editing this action

After \*\*Configure Action\*\* changes this action's inputs, set \`run\_next\_action: true\` to re-run this action AND everything downstream of it in the chain in one call.

```
{
  "row_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567001"],
  "run_next_action": true
}
```

Run every row on the sheet

Pass an empty \`row\_ids\` array to queue every row currently on the sheet against the targeted action.

```
{
  "row_ids": []
}
```

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/:action_instance_id/run"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "row_ids": [\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567001",\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567002"\
  ],
  "run_next_action": false
}'
```

Response200

```
{
  "status": 200,
  "message": "Rows queued for execution",
  "data": {
    "rows_queued": 3
  }
}
```

Try it

## List Rows

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows/list`

Returns rows on a sheet with their inputs and per-cell action status/outputs. Use this to poll for results after **Run Rows**, or to inspect the current state of a sheet.

**POST, not GET** — passing up to 200 row UUIDs as a filter doesn't fit reliably in a query string, so the request body carries the filter and pagination.

Pass optional `row_ids` to filter — the typical flow after **Add Rows** or **Run Rows** when you want only the rows you just touched. Omit `row_ids` to browse all rows on the sheet (paginated).

**Browse-mode filtering** — when you omit `row_ids`, you can narrow the result set with `filters` (sheet input-column value filters), `status_filters` (per-action cell-status filters), and `created_at` (row creation time filter). These three cannot combine with `row_ids` — picking explicit row IDs and filtering at the same time returns 400.

Each returned row includes the inputs you provided and a `cells` object keyed by `action_instance_id` (one entry per action in the chain). For rows that haven't been run yet, `cells` is an empty object `{}`.

Each cell has a `status` that progresses `queued` → `running` → `complete` \| `failed`. Complete cells carry their outputs inline as `outputs` when small, or as a pointer under `outputs_ref` when the payload is large (e.g. LinkedIn profile scrapes, enrichment blobs). Failed cells carry an `error` message. `queued` and `running` cells carry only `status`.

**Large payloads** — `outputs_ref.url` is an absolute **Get Cell Outputs** URL. GET it with your API key to read the payload; it returns the same `outputs` shape the cell would have carried inline, so one parser handles both cases. Those fetches draw on their own 600/minute quota rather than the general API rate limit, so following every pointer on a page leaves your normal request budget intact.

**Paginated** — default page size 20, max 200.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Request body

`row_ids`arrayoptional

Filter to specific rows. Omit to return all rows on the sheet (paginated). Max 200 IDs per call — if you have more than 200 to poll, chunk your IDs across multiple calls. Mutually exclusive with `filters` / `status_filters` / `created_at` — combining them returns 400.

`page_no`integeroptional

Page number (1-indexed). Only meaningful when browsing (no `row_ids`) or when `row_ids` count exceeds `page_size`.

`page_size`integeroptional

Rows per page. Defaults to 20. Maximum 200.

`filters`arrayoptional

Browse-mode only (omit `row_ids`). Value filters on **sheet input columns** — action-output filtering is not exposed yet. Each entry has `variable` (an `{{input.<column>}}` reference token), `operator`, and `values`. Operators: `is equal to`, `not equal to`, `is empty`, `not empty`, `contains`, `does not contain`, `contains any of`, `not contains any of`, `greater than`, `less than`. Most operators take exactly one value; `contains any of` / `not contains any of` accept one or more (exact match each); `is empty` / `not empty` ignore `values`; `greater than` / `less than` are numeric. Multiple entries are AND-ed together.

`status_filters`arrayoptional

Browse-mode only (omit `row_ids`). Match rows by per-action cell status. Each entry pairs an `action_instance_id` with exactly one of `is` (status matches one of) or `is_not` (status matches none of) — passing both, or neither, returns 400. Status values use the public vocabulary (`queued | running | complete | failed | error | condition_not_met`); the server expands them to the engine's internal statuses (e.g. `queued` covers both `payloadFilled` and `checkingNextSource`). Multiple entries are AND-ed together.

`created_at`objectoptional

Browse-mode only (omit `row_ids`). Single time filter on row creation. `operator` is one of `equals to`, `greater than`, `greater than or equals to`, `less than`, `less than or equals to`, `is between`. `values` is `[ISO timestamp]` for the single-value operators or `[start, end]` for `is between` (uses its own dialect, separate from `filters[]`).

### Response

`status`integer

`data`object

`rows`array

Rows on this page, in insertion order.

`row_id`uuid

UUID assigned by **Add Rows**.

`row_status`string

Row-level execution summary — a quick check without iterating every cell. `pending`: the row has never been run (`cells` is `{}`). `running`: at least one cell is `queued` or `running`. `complete`: all cells reached `complete`. `has_failures`: terminal state — no cell is still running and at least one cell has `status: "failed"`.

`created_at`date-time

UTC timestamp (ISO 8601) when the row was added to the sheet via **Add Rows**.

`inputs`object

Input column values as the row was created. Keys match the sheet's input field names.

`cells`object

One entry per action in the sheet's chain, keyed by `action_instance_id` (e.g. `scrape_company_linkedin_profile_1`). **Empty `{}` for rows that have never been run** (added with `run_after_add: "none"` and not subsequently executed via Run Rows).

`total_count`integer

Total rows on the sheet across all pages.

`page_no`integer

`page_size`integer

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/rows/list"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "filters": [\
    {\
      "variable": "{{input.email}}",\
      "operator": "contains",\
      "values": [\
        ".com"\
      ]\
    }\
  ],
  "status_filters": [\
    {\
      "action_instance_id": "enrich_company_linkedin_profile_1",\
      "is_not": [\
        "failed",\
        "error"\
      ]\
    }\
  ],
  "created_at": {
    "operator": "greater than",
    "values": [\
      "2026-05-07T22:14:00Z"\
    ]
  },
  "page_size": 20
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "rows": [\
      {\
        "row_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567001",\
        "row_status": "complete",\
        "created_at": "2026-04-20T10:30:00Z",\
        "inputs": {\
          "linkedin_url": "https://linkedin.com/company/floqer",\
          "email": "hello@floqer.com",\
          "company_name": "Floqer"\
        },\
        "cells": {\
          "scrape_company_linkedin_profile_1": {\
            "status": "complete",\
            "outputs_ref": {\
              "url": "https://api.floqer.com/api/v1/workflows/9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/sheets/9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/rows/a1b2c3d4-e5f6-7890-abcd-ef1234567001/cells/scrape_company_linkedin_profile_1/outputs",\
              "size_bytes": 204800\
            }\
          },\
          "ai_generate_content_1": {\
            "status": "complete",\
            "outputs": {\
              "generated_content": "Hi Floqer team — noticed you raised a Series A..."\
            }\
          }\
        }\
      },\
      {\
        "row_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567002",\
        "row_status": "running",\
        "created_at": "2026-04-20T10:30:02Z",\
        "inputs": {\
          "linkedin_url": "https://linkedin.com/company/acme",\
          "email": "contact@acme.com",\
          "company_name": "Acme"\
        },\
        "cells": {\
          "scrape_company_linkedin_profile_1": {\
            "status": "running"\
          },\
          "ai_generate_content_1": {\
            "status": "queued"\
          }\
        }\
      },\
      {\
        "row_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567003",\
        "row_status": "has_failures",\
        "created_at": "2026-04-20T10:30:05Z",\
        "inputs": {\
          "linkedin_url": "not-a-real-url",\
          "email": "ops@globex.com",\
          "company_name": "Globex"\
        },\
        "cells": {\
          "scrape_company_linkedin_profile_1": {\
            "status": "failed",\
            "error": "Provider returned 404 for the given URL."\
          },\
          "ai_generate_content_1": {\
            "status": "failed",\
            "error": "Skipped: upstream action scrape_company_linkedin_profile_1 failed."\
          }\
        }\
      }\
    ],
    "total_count": 11,
    "page_no": 1,
    "page_size": 20
  }
}
```

Try it

### Data

1endpoints

## Get Cell Outputs

get`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows/{row_id}/cells/{action_instance_id}/outputs`

Returns the outputs of one `complete` cell. This is what `outputs_ref.url` on a List Rows cell points at — call it with your API key exactly like any other endpoint.

List Rows inlines a cell's `outputs` when the payload is small and hands back an `outputs_ref` pointing here when it is large (LinkedIn profile scrapes, enrichment blobs). The response shape is the same either way: keys are the action's output field names.

The URL never expires and carries no credential of its own — it is authorized by your API key on every call, so revoking the key revokes access.

Payload fetches are metered in their own quota (600/minute per key) rather than against the general API rate limit, so following many `outputs_ref` pointers from one page of rows does not exhaust your request budget.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

`row_id`required

Row ID, as returned by List Rows (\`row\_id\`).

`action_instance_id`required

Action instance ID of the cell — the key it appears under in a row's \`cells\` map.

### Response

`status`integer

`data`object

`outputs`object

The cell's outputs. Keys are the action's output field names.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/rows/:row_id/cells/:action_instance_id/outputs"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "outputs": {
      "full_name": "Ada Lovelace",
      "headline": "Founder at Analytical Engines",
      "experience": [\
        {\
          "company": "Analytical Engines",\
          "title": "Founder"\
        }\
      ]
    }
  }
}
```

Try it

## Delete Rows

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows/delete`

Permanently deletes rows from a sheet. For each deleted row, the input values and all per-cell outputs across every action in the chain are removed. **Cannot be undone.**

**Partial success — never atomic.** Row UUIDs that exist on the sheet are deleted and returned in `deleted_row_ids`. Any UUIDs the server can't delete (not found on this sheet, malformed) come back in `rejected` with a reason. A batch of 10 with 2 bad UUIDs deletes 8 and reports 2.

**Max 200 row UUIDs per call.**

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Request body

`row_ids`arrayrequired

Row UUIDs to delete. From **Add Rows** (`row_ids` response) or **List Rows**. Max 200 per call.

### Response

`status`integer

`data`object

`deleted_count`integer

Number of rows actually deleted. Equals `deleted_row_ids.length`.

`deleted_row_ids`array

UUIDs that were successfully deleted, in request order (skipping any rejected).

`rejected`array

Row UUIDs the server couldn't delete. Rows that WERE deleted in the same batch are still gone — this list contains only the failures.

`row_id`string

The UUID you sent that couldn't be deleted.

`error`string

Human-readable reason (e.g. `Row not found on this sheet`).

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/rows/delete"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "row_ids": [\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567001",\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567002",\
    "a1b2c3d4-e5f6-7890-abcd-ef1234567003"\
  ]
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "deleted_count": 2,
    "deleted_row_ids": [\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567001",\
      "a1b2c3d4-e5f6-7890-abcd-ef1234567002"\
    ],
    "rejected": [\
      {\
        "row_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567003",\
        "error": "Row not found on this sheet"\
      }\
    ]
  }
}
```

Try it

## Delete All Rows

post`/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows/delete-all`

Permanently deletes **every row** on the sheet, including all input values and per-cell outputs across every action in the chain. **Cannot be undone. Use with extreme caution.** No request body required — the path is the contract.

**When to use.** Iterating on a workflow build: add 10 test rows → run → verify → delete all → add real 1,000 rows → run. Separate from **Delete Rows** (which targets specific row UUIDs) so an agent can't accidentally wipe a sheet by misinterpreting a filter.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

`sheet_id`required

Sheet ID. To operate on the workflow's main sheet, pass the workflow's ID as \`sheet\_id\` — the main sheet's ID equals the workflow ID.

### Response

`status`integer

`data`object

`deleted_count`integer

Number of rows deleted. `0` if the sheet was already empty.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/rows/delete-all"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "deleted_count": 157
  }
}
```

Try it

## Manage Workflows

Admin-only CRUD on workflow records — create, list, inspect (Get Workflow Overview), rename, duplicate, and delete. No configuration happens here; everything about building a workflow (inputs, actions, sheets, data, runs) lives in **Build a Workflow** and **Run a Workflow**.

## Create Workflow

post`/api/v1/workflows/`

Creates a new empty workflow with a main sheet. Returns the `workflow_id` — this is also the main sheet's `sheet_id` (the two IDs are equal for main sheets).

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Request body

`name`stringrequired

Workflow name. Not required to be unique in the organization.

### Response

`status`integer

`data`object

`workflow_id`uuid

UUID of the newly created workflow. Use this for `{workflow_id}` (and `{sheet_id}` for main-sheet operations) across every other endpoint.

`name`string

Name as set in the request.

`created_at`date-time

UTC timestamp (ISO 8601).

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "name": "Lead Enrichment Pipeline"
}'
```

Response201

```
{
  "status": 201,
  "data": {
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "name": "Lead Enrichment Pipeline",
    "created_at": "2026-04-20T10:30:00Z"
  }
}
```

Try it

## List Workflows

get`/api/v1/workflows/`

Returns all workflows the caller can access, sorted by most recently updated first. Each entry carries the ID, name, and timestamps — no deeper state. Use **Get Workflow Overview** for sheet roster, settings, webhook URLs, and sharing on a single workflow.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Response

`status`integer

`data`array

`workflow_id`uuid

UUID of the workflow. Use this with every endpoint that takes `{workflow_id}`.

`name`string

Workflow name as set at creation.

`created_at`date-time

UTC timestamp (ISO 8601) when the workflow was created.

`updated_at`date-time

UTC timestamp (ISO 8601) of the most recent configuration change (name, inputs, actions, sheet settings).

`last_run_at`date-time

UTC timestamp (ISO 8601) of the most recent run on any sheet in this workflow. `null` if it has never been run. Runs started outside this API — on a schedule, or by a webhook trigger — do not update it.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/workflows/"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",\
      "name": "Lead Enrichment Pipeline",\
      "created_at": "2026-04-18T09:12:00Z",\
      "updated_at": "2026-04-20T10:30:05Z",\
      "last_run_at": "2026-04-20T10:32:00Z"\
    },\
    {\
      "workflow_id": "9a2fc018-2d4a-4e41-b0e7-112233445566",\
      "name": "Inbound Demo Scoring",\
      "created_at": "2026-04-10T14:05:00Z",\
      "updated_at": "2026-04-19T22:14:00Z",\
      "last_run_at": null\
    }\
  ]
}
```

Try it

## Get Workflow Overview

get`/api/v1/workflows/{workflow_id}`

Returns operational info for a single workflow — the bootstrap call after List Workflows.

Includes workflow metadata (`name`, timestamps), a per-sheet roster with settings (`auto_run`, cache), webhook URLs for pushing rows into each sheet, and sharing metadata.

`data.sheets[0]` is always the main sheet; child sheets follow `sheets_config.sheetsOrder` when set, otherwise creation order.

`is_owner` is `true` when the caller owns the workflow; `shared_users` is populated only for owners (other callers get `[]`).

**When to use.** After List Workflows to enumerate `sheet_id` values, inspect cache settings, and webhook URLs before configuring `send_to_sheet`, `lookup_another_floqer_workflow_row`, or other sheet-scoped build/run endpoints.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

### Response

`status`integer

`data`object

`workflow_id`uuid

UUID of the workflow.

`name`string

Workflow name as set at creation.

`created_at`date-time

UTC timestamp (ISO 8601) when the workflow was created.

`updated_at`date-time

UTC timestamp (ISO 8601) of the most recent configuration change.

`last_run_at`null \| string

UTC timestamp (ISO 8601) of the most recent run across every sheet in this workflow. `null` if no sheet has ever been run.

`is_owner`boolean

`true` when the caller is the workflow owner. `shared_users` is populated only when this is `true`.

`shared_users`array

Emails the workflow is shared with. Populated only when `is_owner` is `true`; otherwise `[]`.

`sheets`array

Every sheet in the workflow with settings and webhook URL. `sheets[0]` is the main sheet.

`sheet_id`uuid

UUID of the sheet. Pass as `{sheet_id}` on sheet-scoped endpoints and as `target_sheet_id` in `send_to_sheet`.

`name`string

Sheet name as set at creation.

`is_main_sheet`boolean

`true` for the main sheet — auto-created with the workflow and has `sheet_id === data.workflow_id`.

`auto_run`boolean

When `true`, the sheet's action chain runs automatically whenever new rows arrive.

`cache_enabled`boolean

When `true`, identical input rows reuse prior outputs instead of re-running the chain.

`cache_since`null \| string

Earliest date (YYYY-MM-DD, UTC) from which cached runs are considered fresh. `null` means all cached runs count.

`webhook_url`null \| string

POST URL for pushing rows into this sheet via webhook. `null` when no webhook input is configured.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "name": "Lead Enrichment Pipeline",
    "created_at": "2026-04-20T10:30:00Z",
    "updated_at": "2026-04-20T10:30:00Z",
    "last_run_at": null,
    "is_owner": true,
    "shared_users": [\
      "colleague@example.com"\
    ],
    "sheets": [\
      {\
        "sheet_id": "531952c2-3d07-4be2-8c4b-733acba3187b",\
        "name": "Lead Enrichment Pipeline",\
        "is_main_sheet": true,\
        "auto_run": false,\
        "cache_enabled": false,\
        "cache_since": null,\
        "webhook_url": "https://workers.floqer.com/v2/trigger/webhook?id=eyJ..."\
      },\
      {\
        "sheet_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",\
        "name": "Employees",\
        "is_main_sheet": false,\
        "auto_run": true,\
        "cache_enabled": true,\
        "cache_since": "2026-03-17",\
        "webhook_url": null\
      }\
    ]
  }
}
```

Try it

## Rename Workflow

patch`/api/v1/workflows/{workflow_id}/name`

Sets the workflow's name. Single-field PATCH — the only body field is `name` (required, trimmed, non-empty).

**This does not rename the main sheet's tab.** The workflow name and the main sheet's label are stored separately. To rename a sheet — main or child — use `PATCH /api/v1/workflows/{workflow_id}/sheets/{sheet_id}/name` (Rename Sheet). Get Workflow Overview reports this name as `data.name`; `data.sheets[0].name` follows it only until the main sheet is given its own label.

**Cache.** Renaming does not affect sheets, rows, cached action runs, or the action chain — only the name.

Any caller with access to the workflow may rename it; unlike Delete Workflow this is not restricted to the owner.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

### Response

`status`integer

`data`

Requestcurl

```
curl -X PATCH "https://api.floqer.com/api/v1/workflows/:workflow_id/name"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "name": "Inbound lead enrichment"
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "name": "Inbound lead enrichment"
  }
}
```

Try it

## Duplicate Workflow

post`/api/v1/workflows/{workflow_id}/duplicate`

Deep-copies a workflow's configuration — its input columns, action chains, and child sheets — into a **new** workflow owned by the caller in their active organization. Use it to branch a proven workflow before changing it, or to hand a team a ready-made starting point.

**What comes across:** the structure. Input definitions, every action with its configuration and `{{ref}}` wiring, and all child sheets.

**What does not:** data rows, run history, and sharing permissions. The copy starts empty — add rows and run it like any new workflow. Because no cells are copied, nothing is billed by this call.

**Ownership and placement.** The copy is owned by the caller (not the source's owner) and lands in the root folder — the public API does not expose folders.

**Child sheets.** If `workflow_id` identifies a child sheet rather than a top-level workflow, the copy stays attached to the same parent workflow rather than becoming a new top-level one.

`name` is required and becomes the copy's name. It is trimmed and does not need to be unique.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

ID of the workflow to duplicate.

### Response

`status`integer

`data`

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/duplicate"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "name": "Lead Enrichment Pipeline Copy"
}'
```

Response201

```
{
  "status": 201,
  "data": {
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "name": "Lead Enrichment Pipeline Copy"
  }
}
```

Try it

## Delete Workflow

delete`/api/v1/workflows/{workflow_id}`

Permanently deletes a workflow and everything in it — all sheets, inputs, actions, rows, and run history. **Cannot be undone.**

Only the workflow owner can delete a workflow. Shared users and org members with workflow access cannot delete workflows they do not own.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`workflow_id`required

Workflow ID.

### Response

`status`integer

`data`object

`workflow_id`uuid

UUID of the workflow that was deleted.

`deleted`boolean

Always `true` on a 200 response. Included so agents can branch on a single field.

Requestcurl

```
curl -X DELETE "https://api.floqer.com/api/v1/workflows/:workflow_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "deleted": true
  }
}
```

Try it

## Sources

Sources pull external data **into** Floqer, where it lands as rows your workflows can enrich, score, and act on. This is the inbound counterpart to a workflow's actions: an action pushes data _out_ to an integration; a source brings data _in_. Each source is addressed by a **source slug** under `/api/v1/sources/{source_id}`.

The operations fall into two stages:

**Build** — prepare and persist a source:

- **Preview Source** — see the records that _would_ be imported for a given payload, without persisting anything. Use it to validate your selection before you commit.
- **Create Source** — persist the source and start importing. Whether it imports once or keeps importing over time is source-specific and set in the body.
- **Get Source Field Options** — resolve the dynamic values a payload field expects. When a field's value comes from a connected system, fetch its options here first, then reference the returned `value`s in your payload.

**Manage** — operate on sources you've already created:

- **List Sources** — list the source instances you've created (newest first), each with its instance `source_instance_id`, its type slug (`source_id`), status, and row count. Use it to find the `source_instance_id` to sync.
- **Get Source Data** — page through the rows imported into a created source. Use it to poll while a backfill runs or to inspect the dataset before syncing.
- **Sync Source to Workflow** — connect a created source to a workflow and backfill it with the source's existing rows, mapping source fields onto the workflow's inputs.
- **Pause or Resume Source** — pause or resume an ongoing source's recurring imports.

**Discovering a source's payload.** There is no endpoint that describes a source's body shape. The route schema accepts any JSON object; the real per-source body, field semantics, supported filters, and any prerequisites (such as a connected integration) are validated inside the handler and documented in full at `/docs/source-detail/{source_id}.txt`. Load that file once before constructing a payload — it is the authoritative reference, the same way the action catalog is for actions.

**Scopes:** Preview, Get Source Field Options, List Sources, and Get Source Data need `sources:read`; Create, Sync, and Pause/Resume need `sources:write`.

### Build

3endpoints

Building a source is a short loop: resolve any dynamic field values with **Get Source Field Options**, **Preview** the records your payload would import, then **Create** the source to persist it and start importing.

The request body is specific to each source type — the route only checks that it's a JSON object. Each source's payload fields, filters, and any prerequisites (such as a connected integration) are documented in its detail file. Load it before constructing a payload.

[source-detail — per-source payload reference](/content/docs/source-detail/index.html)

## Preview Source

post`/api/v1/sources/{source_id}/preview`

Returns the records that **would** be imported for the given payload, without persisting anything. Use it to validate your selection — filters, list memberships, object types — before committing with **Create Source**.

The request body shape depends on `source_id`. The route only enforces that the body is a JSON object; per-source validation runs inside the handler and surfaces as a 400 with a specific message. The authoritative body reference for each source is [`/docs/source-detail/{source_id}.txt`](/content/docs/source-detail/find_companies.txt) — load it before building the payload.

Results are capped at 100 rows; `metadata.total_results` is the full upstream count. Each row's keys depend on the source's payload (e.g. which fields you asked for). Preview never creates a source and never consumes credits.

Requires the `sources:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`source_id`required

Source slug identifying the source TYPE. Currently supported: \`extract\_from\_website\`, \`find\_companies\`, \`find\_companies\_by\_buying\_intent\`, \`find\_companies\_by\_tech\_stack\`, \`find\_companies\_from\_sales\_navigator\`, \`find\_companies\_hiring\`, \`find\_job\_postings\`, \`find\_linkedin\_post\_reactors\`, \`find\_people\`, \`find\_people\_from\_sales\_navigator\`, \`import\_from\_fireflies\`, \`import\_from\_google\_sheets\`, \`import\_from\_hubspot\`, \`import\_from\_pipedrive\`, \`import\_from\_salesforce\`, \`import\_from\_slack\`, \`import\_from\_snowflake\`, \`import\_from\_stripe\`, \`import\_from\_typeform\`, \`search\_local\_businesses\`, \`search\_x\_tweets\`, \`track\_job\_postings\`, \`track\_linkedin\_posts\`, \`track\_personal\_website\_visitors\`, \`track\_website\_visitors\`, \`track\_x\_posts\`. Browse them in \[\`/docs/source-catalog.txt\`\](/docs/source-catalog.txt); see \`/docs/source-detail/{source\_id}.txt\` for each source's payload.

### Request body

`limit`integeroptional

Optional cap on rows returned. Response-shaping only; `metadata.total_results` is unchanged and `metadata.capped` becomes true when rows are dropped.

### Response

`status`integer

`data`object

`data`array

Preview rows (max 100). Each row's keys depend on the source's payload.

`metadata`object

`total_results`integer

Total matching records upstream — may exceed the 100 rows returned in `data`.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/sources/:source_id/preview"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "limit": 3
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "data": [\
      {\
        "email": "ada@example.com",\
        "first_name": "Ada",\
        "last_name": "Lovelace"\
      },\
      {\
        "email": "alan@example.com",\
        "first_name": "Alan",\
        "last_name": "Turing"\
      }\
    ],
    "metadata": {
      "total_results": 1240
    }
  }
}
```

Try it

## Create Source

post`/api/v1/sources/{source_id}`

Persists a new source and starts importing. Behavior is source-specific and driven by the body — a source may import once or keep importing over time; consult `/docs/source-detail/{source_id}.txt` for the exact fields.

The request body shape depends on `source_id`. The route only enforces that the body is a JSON object; per-source validation runs inside the handler and surfaces as a 400 with a specific message. **Preview the payload first** — Create accepts the same body plus the create-only fields, so a payload that previews cleanly is ready to create.

Returns `source_instance_id` — the new source's **UUID**, distinct from the `source_id` slug in the URL, which names the source TYPE. The initial import runs asynchronously: the call returns as soon as the source is persisted and the import is queued, not when records have finished arriving.

Requires the `sources:write` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`source_id`required

### Request body

The request body shape depends on `source_id` — typically the source's preview payload plus its create-only fields (e.g. a display name). The route only checks that the body is a JSON object; the accepted fields are documented per source in `/docs/source-detail/{source_id}.txt` and validated inside the handler, where unknown or malformed fields return 400.

### Response

`status`integer

`data`object

`source_instance_id`uuid

UUID of the newly created source instance. Distinct from the `source_id` slug in the request URL — pass it to Sync / Get Source Data / Pause-Resume.

`name`string

Display name echoed from the request (trimmed).

`created_at`date-time

UTC timestamp (ISO 8601) of source creation.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/sources/:source_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{}'
```

Response201

```
{
  "status": 201,
  "data": {
    "source_instance_id": "c7e3a1b2-9f4d-4a8c-b1e6-2d5f6a7b8c90",
    "name": "Customer accounts",
    "created_at": "2026-05-25T09:12:00Z"
  }
}
```

Try it

## Get Source Field Options

post`/api/v1/sources/{source_id}/options/{field_name}`

Resolves the dynamic option values for one of a source's payload fields — the values a field expects when they come from a connected system rather than free text. Call it while building a Preview / Create payload, then reference the returned `value`s back in the body.

Which `field_name`s a source supports, and whether a field's resolver needs context, is documented per source in `/docs/source-detail/{source_id}.txt`. **Cascading** resolvers (where one selection narrows the next) take the parent selection via `context` in the body; calling one without its required key returns a 400 listing the accepted key spellings. Resolvers without cascades accept an empty body.

Returns options as `{ value, label, extras? }`. `value` is what to send back in the source's payload; `label` is the human-readable text; `extras` carries per-integration metadata when present.

Requires the `sources:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`source_id`required

Source slug — the same slug used on the source's Preview / Create endpoint (e.g. \`import\_from\_hubspot\`, \`find\_people\_from\_sales\_navigator\`). See \`/docs/source-detail/{source\_id}.txt\`. Not every source exposes dynamic options — \`find\_companies\`, \`find\_people\`, and \`track\_linkedin\_posts\` have none today; Sales Navigator sources expose region autocomplete via \`location\` and/or \`company\_headquarters\`.

`field_name`required

Snake-cased payload field to fetch options for. Matches a key on the source's Preview / Create body. The supported field names are listed in the source's detail doc.

### Request body

`context`objectoptional

Earlier cascading selections this resolver depends on, keyed by the parent field's name on the source's payload. Omit (or send an empty body) for resolvers that don't cascade. Calling a cascading resolver without its required key returns 400 listing the accepted spellings.

### Response

`status`integer

`data`object

`options`array

Option values the resolver returned.

`value`string

The value to send back via the source's Preview / Create payload.

`label`string

Human-readable label for the option.

`extras`object

Per-integration metadata, when present. Shape varies by integration — use it for richer rendering when needed.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/sources/:source_id/options/:field_name"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "context": {}
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "options": [\
      {\
        "value": "123",\
        "label": "All accounts"\
      },\
      {\
        "value": "456",\
        "label": "Signups — last 30 days"\
      }\
    ]
  }
}
```

Try it

### Manage

4endpoints

Once a source exists, manage it by its`source_id` UUID — the value returned by **Create Source**. **Sync Source to Workflow** connects it to a workflow — mapping the source's fields onto the workflow's inputs with`field_mapping` and backfilling the workflow with the source's existing rows.

## List Sources

get`/api/v1/sources/`

Lists the sources you've created, newest first. Only sources whose type is supported by this API are returned (internal data feeds not exposed by this API are omitted).

Each entry's `source_instance_id` is the source **INSTANCE UUID** — pass it to **Sync Source to Workflow** (`POST /api/v1/sources/{source_instance_id}/sync`) to connect it to a workflow. `source_id` is the source-type slug used by the Preview / Create / Get Source Field Options endpoints.

Requires the `sources:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Response

`status`integer

`data`array

The caller's sources, newest first.

`source_instance_id`string

Source instance UUID — pass to **Sync Source to Workflow** (`POST /api/v1/sources/{source_instance_id}/sync`) or **Get Source Data** (`GET /api/v1/sources/{source_instance_id}/data`).

`name`string

Display name given at creation.

`source_id`string

Public source-type slug (e.g. `import_from_hubspot`, `find_companies`) — the identifier used by Preview / Create / Get Source Field Options.

`status`string

Source status: `active` (live, re-importing), `completed` (one-time import, won't refresh), `paused`, `paused_out_of_credits`, `expired`, or `deleted`.

`lead_count`integer

Rows imported so far.

`created_at`date-time

UTC creation timestamp (ISO 8601).

`expiration_date`string

When an active source stops firing. `null` for one-time sources.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/sources/"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "source_instance_id": "b7e2c1a0-4d8f-4a21-9c33-5e6f7a8b9c01",\
      "name": "AI Posts from CEOs",\
      "source_id": "track_linkedin_posts",\
      "status": "active",\
      "lead_count": 128,\
      "created_at": "2026-05-27T12:00:00Z",\
      "expiration_date": "2026-06-26"\
    },\
    {\
      "source_instance_id": "a1d4f9c3-2b6e-4f70-8a12-3c4d5e6f7a8b",\
      "name": "US Mid-Market SaaS",\
      "source_id": "find_companies",\
      "status": "completed",\
      "lead_count": 1000,\
      "created_at": "2026-05-26T09:30:00Z",\
      "expiration_date": null\
    }\
  ]
}
```

Try it

## Get Source Data

get`/api/v1/sources/{source_instance_id}/data`

Returns paginated rows that have been imported into a created source. `source_instance_id` is the source **INSTANCE UUID** returned by **Create Source**.

Use this to inspect or poll a source after Create while its backfill runs, or to page through the full dataset before **Sync Source to Workflow**. Row field names match **Preview Source** for that source type.

Query params `page_no` (1-indexed, default 1) and `page_size` (default 20, max 200) control pagination. When `page_no * page_size > total_count`, `rows` is `[]`. `total_count` is the total number of rows stored for the source.

Requires the `sources:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`source_instance_id`required

Source instance UUID — the \`source\_instance\_id\` returned by \*\*Create Source\*\*.

### Query parameters

`page_no`integer

1-indexed page number. Defaults to 1.

`page_size`integer

Rows per page. Defaults to 20; capped at 200.

### Response

`status`integer

`data`object

`rows`array

Imported rows for this page. Field names depend on the source type.

`total_count`integer

Total number of rows stored for this source.

`page_no`integer

1-indexed page number returned.

`page_size`integer

Page size used for this response.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/sources/:source_instance_id/data"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "rows": [\
      {\
        "email": "ada@example.com",\
        "firstname": "Ada",\
        "lastname": "Lovelace"\
      },\
      {\
        "email": "grace@example.com",\
        "firstname": "Grace",\
        "lastname": "Hopper"\
      }\
    ],
    "total_count": 128,
    "page_no": 1,
    "page_size": 20
  }
}
```

Try it

## Sync Source to Workflow

post`/api/v1/sources/{source_instance_id}/sync`

Connects a created source to a workflow and, by default, backfills the workflow with the source's existing rows. This is the step that wires inbound source data into a pipeline — once synced, the source's records flow in as workflow rows.

**`source_instance_id` here is the source's UUID** — the value returned by **Create Source**.

**`field_mapping`** maps the workflow's inputs to the source's fields. Each key is a public workflow input reference (`input.<field>`); each value is the source field NAME to pull — the row's top-level key exactly as it appears in the **Preview Source** response (e.g. `Name`, `email`, `business_id`), NEVER a nested path like `Name.value`. The import resolves each field's value automatically, including for sources whose preview cells render as `{label, value}`. Keys are **case-sensitive** — use the exact lowercase snake\_case `reference` from **List Inputs** (minus the `{{ }}`); variants like `Input.Email` or `input.firstName` return 400, though surrounding `{{ }}` is tolerated.

**`run`** controls whether backfilled rows execute the workflow: `all` (default) runs every row, `first_10` runs the first 10 and just loads the rest, `none` loads rows without running. **`push_existing`** (default `true`) toggles the backfill entirely — set it `false` to connect the source without importing its current rows.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`source_instance_id`required

Source instance UUID — the \`source\_instance\_id\` returned by \*\*Create Source\*\*.

### Request body

`workflow_id`uuidrequired

UUID of the destination workflow.

`field_mapping`objectrequired

Map of workflow input reference → source field. Keys are `input.<field>` references from the workflow's inputs (case-sensitive); each value is the source field NAME — the row's top-level key exactly as it appears in the Preview response (e.g. `Name`, `email`), never a nested path like `Name.value`. A key that doesn't match a workflow input returns 400.

`push_existing`booleanoptional

Whether to backfill the workflow with the source's current rows. Defaults to `true`; set `false` to connect without importing existing rows.

`run`stringoptional

Controls whether backfilled rows execute the workflow: `all` (default) runs every row, `first_10` runs the first 10 and loads the rest, `none` loads rows without running.

### Response

`status`integer

`data`object

`source_instance_id`uuid

The connected source's instance UUID.

`workflow_id`uuid

The destination workflow's UUID.

`push_existing`boolean

Whether existing rows were queued for backfill.

`run`string

The run mode applied to backfilled rows.

`fields_mapped`integer

Number of workflow inputs mapped.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/sources/:source_instance_id/sync"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
  "field_mapping": {
    "input.email": "email",
    "input.first_name": "firstname",
    "input.last_name": "lastname"
  },
  "push_existing": true,
  "run": "first_10"
}'
```

Response201

```
{
  "status": 201,
  "data": {
    "source_instance_id": "c7e3a1b2-9f4d-4a8c-b1e6-2d5f6a7b8c90",
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "push_existing": true,
    "run": "first_10",
    "fields_mapped": 3
  }
}
```

Try it

## Pause or Resume Source

patch`/api/v1/sources/{source_instance_id}/status`

Pauses or resumes a **created** source. `source_instance_id` is the source **INSTANCE UUID** returned by **Create Source**.

Set `status` to `paused` to stop an ongoing source's recurring runs, or `active` to resume it. Pausing stops the schedule (when present), disables provider webhooks where applicable (e.g. Stripe, TheirStack), and prevents further imports; resuming restarts them. Static / one-time sources have nothing recurring to pause, but the status still updates.

Requires the `sources:write` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`source_instance_id`required

Source instance UUID — the \`source\_instance\_id\` returned by \*\*Create Source\*\*.

### Request body

`status`stringrequired

`paused` stops recurring runs; `active` resumes them.

### Response

`status`integer

`data`object

`source_instance_id`uuid

The source instance UUID.

`status`string

The source's new public status.

Requestcurl

```
curl -X PATCH "https://api.floqer.com/api/v1/sources/:source_instance_id/status"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "status": "active"
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "source_instance_id": "b7e2c1a0-4d8f-4a21-9c33-5e6f7a8b9c01",
    "status": "paused"
  }
}
```

Try it

## ackDB Syncs

A **sync** keeps an [ackDB](/content/docs/ackdb/index.html) segment wired into a Floq workflow. Every five minutes the watcher polls the segment and delivers its new members into the workflow as rows, so an audience you maintain in ackDB drives a pipeline in Floqer without any polling of your own.

The shape: one segment has one **sync**, and a sync fans out to one or more **destinations**, one per workflow. Creating a sync for a segment that already has one adds a destination rather than replacing it, and the response says so with `reused_sync: true`.

**The flow.** Find the segment with `GET /api/v1/ackdb/observe/segments` (see [Tables and segments](/content/docs/ackdb/tables-and-segments/index.html)), read the destination workflow's input references with **List Inputs**, then call **Sync ackDB Segment to Floq** with a `field_mapping` from each `input.<field>` reference to a segment field. Poll **Get ackDB Sync** for lifecycle state and per-destination counters.

**Defaults are conservative.**`push_existing: false` delivers only members who join after creation, `run: none` inserts rows without executing the workflow, and `reentry_mode: suppress` delivers a member once ever even if it leaves the segment and comes back. Set them explicitly when you want a backfill, an immediate run, or repeated delivery.

**When a sync stops on its own.** An import error pauses the watcher and surfaces `last_import_error`: fix the cause, then call **Resume ackDB Sync**. A suspicious change instead trips a breaker, leaving the sync `pending_confirmation` with a held count and a reason (`filter_changed` or `flood`): call **Confirm Held ackDB Sync Members** with `push` to release them or `skip` to drop them. Pausing one destination is separate, and resuming it makes the same deliver-or-skip choice for that destination's backlog.

**Scope:** every operation here needs the `ackdb` scope. A key restricted by `allowed_workflow_ids` sees only the destinations it is permitted, and a watcher-wide call (confirm, resume, delete) requires the key to cover every destination on the sync.

### Syncs

6endpoints

## Sync ackDB Segment to Floq

post`/api/v1/ackdb/segments/{segment_id}/sync`

Create a durable sync from an ackDB segment into a published Floq workflow. New segment members are delivered every five minutes. `field_mapping` keys are public workflow input references from List Inputs; values are ackDB segment field names. Defaults are conservative: existing members are not pushed, delivered rows are inserted without running the workflow, and re-entries are suppressed.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`segment_id`required

ackDB segment id. The \`id\` returned by \`GET /api/v1/ackdb/observe/segments\`.

### Request body

`workflow_id`uuidrequired

UUID of the destination Floq workflow. The workflow must be published.

`field_mapping`objectrequired

Map of workflow input reference → ackDB segment field. Keys are the public `input.<field>` references from **List Inputs** (case-sensitive, without the surrounding `{{ }}`); each value is a segment field name. A key that doesn't match a workflow input returns 400.

`push_existing`booleanoptional

Deliver the members already in the segment. Defaults to `false`, so only members that join after creation are delivered.

`run`stringoptional

Whether delivered rows execute the workflow (`all`) or are inserted without running (`none`, the default).

`reentry_mode`stringoptional

What happens when a member leaves the segment and re-enters: `suppress` (the default) delivers it once ever, `repush` delivers it again on each re-entry.

### Response

`status`integer

`data`object

`sync_id`string

`connection_id`string

`segment_id`string

`workflow_id`uuid

`entity_kind`string

`state`string

`reused_sync`boolean

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/ackdb/segments/:segment_id/sync"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
  "field_mapping": {
    "input.company_domain": "domain",
    "input.company_name": "name"
  },
  "push_existing": false,
  "run": "none",
  "reentry_mode": "suppress"
}'
```

Response201

```
{
  "status": 201,
  "data": {
    "sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",
    "connection_id": "4821",
    "segment_id": "seg_9c1f4d2a",
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "entity_kind": "company",
    "state": "seeding",
    "reused_sync": false
  }
}
```

Try it

## Get ackDB Sync

get`/api/v1/ackdb/segments/{segment_id}/sync`

Read the durable sync for one ackDB segment, including destination connections, delivery counters, current lifecycle state, breaker/error details, and a best-effort live segment size. An unsynced or inaccessible segment returns `synced: false`.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`segment_id`required

ackDB segment id. The \`id\` returned by \`GET /api/v1/ackdb/observe/segments\`.

### Response

`status`integer

`data`

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/ackdb/segments/:segment_id/sync"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "synced": true,
    "sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",
    "name": "Enterprise ICP → Enrichment",
    "segment_id": "seg_9c1f4d2a",
    "entity_kind": "company",
    "state": "active",
    "segment_size": 1284,
    "segment_size_is_lower_bound": false,
    "last_import_error": null,
    "pending_confirmation": null,
    "created_by": "ops@acme.com",
    "created_at": "2026-08-14T09:12:44.000Z",
    "connection_count": 1,
    "connections": [\
      {\
        "connection_id": "4821",\
        "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",\
        "status": "active",\
        "reentry_mode": "suppress",\
        "push_existing": false,\
        "run": "none",\
        "field_mapping": {\
          "input.company_domain": "domain",\
          "input.company_name": "name"\
        },\
        "mapping_warnings": [],\
        "created_at": "2026-08-14T09:12:44.000Z",\
        "counters": {\
          "delivered": 412,\
          "exited_since_delivery": 17,\
          "suppressed_reentries": 6,\
          "held": 0,\
          "pending": 3\
        }\
      }\
    ]
  }
}
```

Try it

## List ackDB Syncs

get`/api/v1/ackdb/syncs`

List the organization's durable ackDB segment-to-Floq syncs, newest first. This stored-state list does not call ackDB for a live segment count; use Get Sync for that detail. A key restricted by `allowed_workflow_ids` sees only permitted destinations.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Response

`status`integer

`data`array

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/ackdb/syncs"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "synced": true,\
      "sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",\
      "name": "Enterprise ICP → Enrichment",\
      "segment_id": "seg_9c1f4d2a",\
      "entity_kind": "company",\
      "state": "active",\
      "segment_size": 1284,\
      "segment_size_is_lower_bound": true,\
      "last_import_error": null,\
      "pending_confirmation": null,\
      "created_by": "ops@acme.com",\
      "created_at": "2026-08-14T09:12:44.000Z",\
      "connection_count": 1,\
      "connections": [\
        {\
          "connection_id": "4821",\
          "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",\
          "status": "active",\
          "reentry_mode": "suppress",\
          "push_existing": false,\
          "run": "none",\
          "field_mapping": {\
            "input.company_domain": "domain",\
            "input.company_name": "name"\
          },\
          "mapping_warnings": [],\
          "created_at": "2026-08-14T09:12:44.000Z",\
          "counters": {\
            "delivered": 412,\
            "exited_since_delivery": 17,\
            "suppressed_reentries": 6,\
            "held": 0,\
            "pending": 3\
          }\
        }\
      ]\
    }\
  ]
}
```

Try it

## Confirm Held ackDB Sync Members

post`/api/v1/ackdb/segments/{segment_id}/sync/confirm`

Resolve a sync breaker in `pending_confirmation`: `push` releases held members for delivery, while `skip` records them as skipped. A workflow-restricted key must cover every destination on the sync.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`segment_id`required

ackDB segment id. The \`id\` returned by \`GET /api/v1/ackdb/observe/segments\`.

### Request body

`action`stringrequired

`push` releases the held members for delivery; `skip` records them as skipped and never delivers them.

### Response

`status`integer

`data`object

`sync_id`string

`action`string

`affected`integer

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/ackdb/segments/:segment_id/sync/confirm"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "action": "push"
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",
    "action": "push",
    "affected": 238
  }
}
```

Try it

## Resume ackDB Sync

post`/api/v1/ackdb/segments/{segment_id}/sync/resume`

Resume a sync watcher that auto-paused after an import error. This clears the surfaced error and restarts its scheduler. Breaker-held syncs use Confirm instead. A workflow-restricted key must cover every destination.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`segment_id`required

ackDB segment id. The \`id\` returned by \`GET /api/v1/ackdb/observe/segments\`.

### Response

`status`integer

`data`object

`sync_id`string

`state`string

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/ackdb/segments/:segment_id/sync/resume"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",
    "state": "active"
  }
}
```

Try it

## Delete ackDB Sync

delete`/api/v1/ackdb/segments/{segment_id}/sync`

Stop the segment sync, archive every destination connection, and remove its scheduler. Rows already delivered to workflows remain. A workflow-restricted key must cover every destination.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`segment_id`required

ackDB segment id. The \`id\` returned by \`GET /api/v1/ackdb/observe/segments\`.

### Response

`status`integer

`data`object

`sync_id`string

`connections_archived`integer

Requestcurl

```
curl -X DELETE "https://api.floqer.com/api/v1/ackdb/segments/:segment_id/sync"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",
    "connections_archived": 2
  }
}
```

Try it

### Destinations

3endpoints

## Update ackDB Sync Destination

patch`/api/v1/ackdb/sync/connections/{connection_id}`

Update one sync destination. `field_mapping` replaces the mapping for future deliveries; existing workflow rows are not re-pushed. `status: paused` holds new members until Resume chooses whether to deliver or skip the backlog.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`connection_id`required

Sync destination id. The \`connection\_id\` returned by \*\*Sync ackDB Segment to Floq\*\* or \*\*Get ackDB Sync\*\*.

### Request body

`status`stringoptional

Pause this destination. Resuming uses **Resume ackDB Sync Destination**, because it needs a backlog choice.

`reentry_mode`stringoptional

Change what happens when a member re-enters the segment.

`field_mapping`objectoptional

Complete replacement mapping, in the same shape as **Sync ackDB Segment to Floq**. It applies to future deliveries only: rows already in the workflow are not re-pushed.

### Response

`status`integer

`data`object

`connection_id`string

`status`null \| string

`reentry_mode`null \| string

`remapped`boolean

Requestcurl

```
curl -X PATCH "https://api.floqer.com/api/v1/ackdb/sync/connections/:connection_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "status": "paused",
  "reentry_mode": "suppress",
  "field_mapping": {
    "input.company_domain": "domain",
    "input.company_name": "name"
  }
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "connection_id": "4821",
    "status": "paused",
    "reentry_mode": null,
    "remapped": true
  }
}
```

Try it

## Resume ackDB Sync Destination

post`/api/v1/ackdb/sync/connections/{connection_id}/resume`

Resume a paused destination and choose what happens to members accumulated while paused: `deliver` queues them, while `skip` records them as skipped.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`connection_id`required

Sync destination id. The \`connection\_id\` returned by \*\*Sync ackDB Segment to Floq\*\* or \*\*Get ackDB Sync\*\*.

### Request body

`backlog`stringrequired

What to do with members that accumulated while the destination was paused: `deliver` queues them, `skip` records them as skipped.

### Response

`status`integer

`data`object

`connection_id`string

`backlog`string

`affected`integer

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/ackdb/sync/connections/:connection_id/resume"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "backlog": "deliver"
}'
```

Response200

```
{
  "status": 200,
  "data": {
    "connection_id": "4821",
    "backlog": "deliver",
    "affected": 54
  }
}
```

Try it

## Delete ackDB Sync Destination

delete`/api/v1/ackdb/sync/connections/{connection_id}`

Archive one destination connection. Other workflows on the same segment sync continue receiving members, and rows already delivered to this workflow remain.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`connection_id`required

Sync destination id. The \`connection\_id\` returned by \*\*Sync ackDB Segment to Floq\*\* or \*\*Get ackDB Sync\*\*.

### Response

`status`integer

`data`object

`connection_id`string

Requestcurl

```
curl -X DELETE "https://api.floqer.com/api/v1/ackdb/sync/connections/:connection_id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "connection_id": "4821"
  }
}
```

Try it

## Shortcuts

Run a published workflow as a **typed function**. A shortcut wraps a workflow behind a typed input/output schema so it can be run as a single call — no workflow building, no sheets, no rows. Shortcuts are created and published in the Floqer UI; these endpoints **consume** them.

Where a workflow run is "add rows to a sheet, run, list rows", a shortcut run is "call a function": you pass one input object, one run executes, and you read one output object.

The flow: **List Shortcuts** → **Get Shortcut** (read `input_schema` for the `reference` keys to send, `output_schema` for the `name` keys to expect back) → **Run Shortcut** (or **Run Shortcut Batch** for up to 1,000 rows) → poll **Get Shortcut Run** (or **List Shortcut Runs** with `batch_id`) until a terminal status → read `output_data`.

**Shortcuts vs workflows — when to use which.** Use a shortcut when the workflow already exists and you just need its result for one record (or a batch): one POST, one poll, typed in/out. Use the workflow endpoints when you need to _build_ or modify a pipeline, run large ongoing datasets on sheets, or inspect per-action cells.

**Legacy `apps` URLs.** Shortcuts were previously published as **Apps**, under `/api/v1/apps/*` with the `apps:read` / `apps:run` scopes. Both keep working permanently — the old prefix is folded onto `/api/v1/shortcuts/*` before routing, and the old scope names stay valid aliases — so an existing integration needs no change.

**Scopes:**`shortcuts:read` to discover shortcuts and read runs; `shortcuts:run` to start them.

## List Shortcuts

get`/api/v1/shortcuts/`

Returns every shortcut the caller can access — shortcuts they created ( **master**) and shortcuts shared with them ( **shared**). Each entry carries the shortcut's full `input_schema` and `output_schema`, so a single List Shortcuts call is enough to discover what's runnable and what each shortcut takes and returns.

Only **published** shortcuts (`is_published: true`) can be run — see **Run Shortcut**.

Requires the `shortcuts:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Query parameters

`filter`string

Filter by ownership: \`master\` (shortcuts you created), \`shared\` (shortcuts shared with you), or \`all\` (both). Defaults to \`all\`.

### Response

`status`integer

`data`array

Shortcuts accessible to the caller. Each shortcut wraps a workflow with typed input/output schemas.

`id`uuid

Unique shortcut identifier (UUID).

`workflow_id`uuid

UUID of the underlying workflow that powers this shortcut.

`parent_app_id`string

If this is a shared copy, the ID of the original master shortcut. `null` for master shortcuts.

`name`string

Shortcut display name.

`description`string

Human-readable description of what the shortcut does.

`creator_email`email

Email of the user who created the shortcut.

`org_id`string

Organization ID the shortcut belongs to.

`input_schema`array

Ordered list of input fields the shortcut takes. Each field's `reference` is the key to use in `input_data` when running the shortcut.

`reference`string

Snake\_case identifier for this input field (derived from `name`) — use it as the key in `input_data` when running the shortcut.

`name`string

Human-readable field name displayed in the shortcut UI.

`description`string

Help text describing what value to provide for this field.

`type`string

Field type: text, number, email, url, etc.

`required`boolean

Whether this field must be provided when running the shortcut.

`defaultValue`

Default value used when the field is not provided.

`output_schema`array

Ordered list of output fields the shortcut produces. Each field's `name` is the key under which its value appears in a run's `output_data`.

`name`string

Human-readable name for this output field — the key for its value in a run's `output_data`.

`description`string

Description of what this output contains.

`display_column`string

Column header label shown in results tables.

`is_master`boolean

`true` if this is the original shortcut (not a shared copy).

`is_published`boolean

Whether the shortcut is published and available for runs. Only published shortcuts can be run.

`is_archived`boolean

Whether the shortcut has been archived.

`cover_image`string

URL of the shortcut's cover image.

`icon_emoji`string

Emoji used as the shortcut icon.

`created_at`date-time

When the shortcut was created.

`updated_at`date-time

When the shortcut was last updated.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/shortcuts/"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",\
      "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",\
      "parent_app_id": null,\
      "name": "Company Enrichment",\
      "description": "Enriches company data from a LinkedIn URL",\
      "creator_email": "ada@acme.com",\
      "org_id": "7c3e5b2a-9f10-4d23-8b71-1a2b3c4d5e6f",\
      "input_schema": [\
        {\
          "reference": "linkedin_url",\
          "name": "LinkedIn URL",\
          "description": "Full LinkedIn company profile URL",\
          "type": "text",\
          "required": true\
        }\
      ],\
      "output_schema": [\
        {\
          "name": "Company Website",\
          "description": "Main website URL of the company",\
          "display_column": "Company Website"\
        },\
        {\
          "name": "Employee Count",\
          "description": "Current number of employees",\
          "display_column": "Employee Count"\
        }\
      ],\
      "is_master": true,\
      "is_published": true,\
      "is_archived": false,\
      "cover_image": null,\
      "icon_emoji": "🏢",\
      "created_at": "2025-01-10T09:00:00Z",\
      "updated_at": "2025-01-14T16:20:00Z"\
    }\
  ]
}
```

Try it

## Get Shortcut

get`/api/v1/shortcuts/{id}`

Returns full details for a single shortcut. This is the bootstrap call before running one:

- `input_schema` — the fields the shortcut takes. Each field's `reference` (snake\_case, e.g. `linkedin_url`) is the key to use in `input_data` when calling **Run Shortcut**. Check `required` and `defaultValue` to know what you must send.
- `output_schema` — the fields the shortcut produces. Each field's `name` (e.g. `Company Website`) is the key under which its value appears in a run's `output_data`.
- `is_published` — only published shortcuts can be run; running an unpublished shortcut returns 403.

Requires the `shortcuts:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`id`required

Shortcut ID — from \*\*List Shortcuts\*\*.

### Response

`status`integer

`data`object

The shortcut. `input_schema` lists the fields to pass as `input_data` to **Run Shortcut**; `output_schema` lists the fields returned in a run's `output_data`.

`id`uuid

Unique shortcut identifier (UUID).

`workflow_id`uuid

UUID of the underlying workflow that powers this shortcut.

`parent_app_id`string

If this is a shared copy, the ID of the original master shortcut. `null` for master shortcuts.

`name`string

Shortcut display name.

`description`string

Human-readable description of what the shortcut does.

`creator_email`email

Email of the user who created the shortcut.

`org_id`string

Organization ID the shortcut belongs to.

`input_schema`array

Ordered list of input fields the shortcut takes. Each field's `reference` is the key to use in `input_data` when running the shortcut.

`reference`string

Snake\_case identifier for this input field (derived from `name`) — use it as the key in `input_data` when running the shortcut.

`name`string

Human-readable field name displayed in the shortcut UI.

`description`string

Help text describing what value to provide for this field.

`type`string

Field type: text, number, email, url, etc.

`required`boolean

Whether this field must be provided when running the shortcut.

`defaultValue`

Default value used when the field is not provided.

`output_schema`array

Ordered list of output fields the shortcut produces. Each field's `name` is the key under which its value appears in a run's `output_data`.

`name`string

Human-readable name for this output field — the key for its value in a run's `output_data`.

`description`string

Description of what this output contains.

`display_column`string

Column header label shown in results tables.

`is_master`boolean

`true` if this is the original shortcut (not a shared copy).

`is_published`boolean

Whether the shortcut is published and available for runs. Only published shortcuts can be run.

`is_archived`boolean

Whether the shortcut has been archived.

`cover_image`string

URL of the shortcut's cover image.

`icon_emoji`string

Emoji used as the shortcut icon.

`created_at`date-time

When the shortcut was created.

`updated_at`date-time

When the shortcut was last updated.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/shortcuts/:id"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "parent_app_id": null,
    "name": "Company Enrichment",
    "description": "Enriches company data from a LinkedIn URL",
    "creator_email": "ada@acme.com",
    "org_id": "7c3e5b2a-9f10-4d23-8b71-1a2b3c4d5e6f",
    "input_schema": [\
      {\
        "reference": "linkedin_url",\
        "name": "LinkedIn URL",\
        "description": "Full LinkedIn company profile URL",\
        "type": "text",\
        "required": true\
      }\
    ],
    "output_schema": [\
      {\
        "name": "Company Website",\
        "description": "Main website URL of the company",\
        "display_column": "Company Website"\
      },\
      {\
        "name": "Employee Count",\
        "description": "Current number of employees",\
        "display_column": "Employee Count"\
      }\
    ],
    "is_master": true,
    "is_published": true,
    "is_archived": false,
    "cover_image": null,
    "icon_emoji": "🏢",
    "created_at": "2025-01-10T09:00:00Z",
    "updated_at": "2025-01-14T16:20:00Z"
  }
}
```

Try it

## Run Shortcut

post`/api/v1/shortcuts/{id}/run`

Starts one run of a published shortcut with the provided input. **Execution is asynchronous** — the call returns `201` immediately with a run `id`; poll **Get Shortcut Run** until the status is terminal, then read `output_data`.

**Body:**`input_data` is an object keyed by each field's `reference` from the shortcut's `input_schema` (see **Get Shortcut**). Unknown keys are rejected with a 400 that lists the valid references. Missing fields fall back to the shortcut's input `defaultValue`s, then to `""`.

The shortcut must be published — running an unpublished shortcut returns 403.

Requires the `shortcuts:run` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`id`required

Shortcut ID — from \*\*List Shortcuts\*\*.

### Request body

`input_data`objectrequired

Input field values keyed by each field's `reference` from the shortcut's `input_schema` (see **Get Shortcut**). Unknown keys → 400 listing the valid references.

### Response

`status`integer

`message`string

`data`object

`id`uuid

The run ID — poll **Get Shortcut Run** (`GET .../runs/{run_id}`) with it.

`data_id`uuid

ID of the data row created in the underlying workflow for this run.

`status`string

Initial run status — starts as `pending`. Poll **Get Shortcut Run** until a terminal status (`completed`, `failed`, `error`, `outOfCredits`).

`created_at`date-time

When the run was created.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/shortcuts/:id/run"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "input_data": {
    "linkedin_url": "https://www.linkedin.com/company/floqer"
  }
}'
```

Response201

```
{
  "status": 201,
  "message": "Shortcut run started",
  "data": {
    "id": "f1a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c",
    "data_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "pending",
    "created_at": "2025-01-15T10:30:00Z"
  }
}
```

Try it

## Run Shortcut Batch

post`/api/v1/shortcuts/{id}/run/batch`

Starts one run per input row, grouped under a single `batch_id`. Accepts up to **1,000 rows** per request.

**Validation is all-or-nothing:** one unknown key in any row rejects the entire batch — the 400 names the offending row index. No partial batches are created.

Each row is an object keyed by field `reference` values from the shortcut's `input_schema` (see **Get Shortcut**). Runs execute asynchronously — follow the batch via **List Shortcut Runs** with `?batch_id=...` until every run reaches a terminal status.

The shortcut must be published — running an unpublished shortcut returns 403.

Requires the `shortcuts:run` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`id`required

Shortcut ID — from \*\*List Shortcuts\*\*.

### Request body

`rows`arrayrequired

Input rows — one shortcut run is created per row. Each row is keyed by field `reference` values from the shortcut's `input_schema`. Max 1,000 rows per request.

### Response

`status`integer

`message`string

`data`object

`batch_id`uuid

ID grouping every run created by this request — filter **List Shortcut Runs** by it.

`run_count`integer

Number of runs created (one per row).

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/shortcuts/:id/run/batch"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "rows": [\
    {\
      "linkedin_url": "https://www.linkedin.com/company/floqer"\
    },\
    {\
      "linkedin_url": "https://www.linkedin.com/company/anthropic"\
    }\
  ]
}'
```

Response201

```
{
  "status": 201,
  "message": "Shortcut batch run started",
  "data": {
    "batch_id": "3f2a1b6c-9d4e-4f7a-8b0c-5d6e7f8a9b0c",
    "run_count": 25
  }
}
```

Try it

## List Shortcut Runs

get`/api/v1/shortcuts/{id}/runs`

Returns a shortcut's runs, newest first, with limit/offset pagination (default 50 per page, max 100).

**Filters:**`batch_id` narrows to the runs created by one **Run Shortcut Batch** call; `mode` narrows to `single` (runs from **Run Shortcut**) or `batch`.

⚠ For filtered queries (`batch_id` or `mode`), `pagination.total` reflects the returned page's row count, not the full filtered count — paginate until a page returns fewer rows than `limit`.

Requires the `shortcuts:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`id`required

Shortcut ID — from \*\*List Shortcuts\*\*.

### Query parameters

`limit`string

Number of runs to return per page. Default 50, max 100.

`offset`string

Offset for pagination. Default 0.

`mode`string

Filter by run mode: \`single\` (runs from \*\*Run Shortcut\*\*) or \`batch\` (runs from \*\*Run Shortcut Batch\*\*).

`batch_id`string

Filter to the runs created by one \*\*Run Shortcut Batch\*\* call — the \`batch\_id\` it returned.

### Response

`status`integer

`data`object

`runs`array

Run records, newest first. Same shape as **Get Shortcut Run**'s `data`.

`id`uuid

Unique run identifier (UUID).

`app_id`string

ID of the shortcut this run belongs to.

`workflow_id`string

ID of the underlying workflow that was executed.

`data_id`string

ID of the data row created in the workflow for this run.

`user_email`string

Email of the user who triggered the run.

`org_id`string

Organization ID.

`input_data`object

Input values provided when the run was started, keyed by each field's `reference` from the shortcut's `input_schema`.

`output_data`object

Output values keyed by each output field's `name` from the shortcut's `output_schema`. Each entry is `{ value, status }`. `null` until the run completes.

`status`string

Current execution status. Terminal: `completed`, `failed`, `error`, `outOfCredits`. In-progress values include `pending` and `inProgress`.

`error_message`string

Error details if the run failed. `null` on success.

`created_at`date-time

When the run was created.

`completed_at`date-time

When the run finished (completed or failed). `null` while in progress.

`pagination`object

`limit`integer

`offset`integer

`total`integer

Total run count for unfiltered queries. For filtered queries (`batch_id` or `mode`), the returned page's row count — paginate until a page returns fewer rows than `limit`.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/shortcuts/:id/runs"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "runs": [\
      {\
        "id": "f1a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c",\
        "app_id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",\
        "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",\
        "data_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",\
        "user_email": "ada@acme.com",\
        "org_id": "7c3e5b2a-9f10-4d23-8b71-1a2b3c4d5e6f",\
        "input_data": {\
          "linkedin_url": "https://www.linkedin.com/company/floqer"\
        },\
        "output_data": {\
          "Company Website": {\
            "value": "https://floqer.com",\
            "status": "completed"\
          },\
          "Employee Count": {\
            "value": "11-50",\
            "status": "completed"\
          }\
        },\
        "status": "completed",\
        "error_message": null,\
        "created_at": "2025-01-15T10:30:00Z",\
        "completed_at": "2025-01-15T10:31:42Z"\
      }\
    ],
    "pagination": {
      "limit": 50,
      "offset": 0,
      "total": 1
    }
  }
}
```

Try it

## Get Shortcut Run

get`/api/v1/shortcuts/{id}/runs/{runId}`

The polling target after **Run Shortcut**. Returns a single run with its `input_data`, `output_data`, and current status. For in-progress runs the status is recomputed live from the workflow engine, so the response reflects real-time progress.

**Poll until `status` is terminal:**`completed`, `failed`, `error`, or `outOfCredits`. In-progress values include `pending` and `inProgress`.

**Reading results:**`output_data` is `null` until the run finishes, then an object keyed by each output field's `name` from the shortcut's `output_schema`. Each entry carries its own per-field `status` — a run can complete with some fields failed (e.g. "No data found"); check per-field status before trusting a value.

Requires the `shortcuts:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Path parameters

`id`required

Shortcut ID — from \*\*List Shortcuts\*\*.

`runId`required

Run ID — the \`id\` returned by \*\*Run Shortcut\*\*, or from \*\*List Shortcut Runs\*\*.

### Response

`status`integer

`data`object

`id`uuid

Unique run identifier (UUID).

`app_id`string

ID of the shortcut this run belongs to.

`workflow_id`string

ID of the underlying workflow that was executed.

`data_id`string

ID of the data row created in the workflow for this run.

`user_email`string

Email of the user who triggered the run.

`org_id`string

Organization ID.

`input_data`object

Input values provided when the run was started, keyed by each field's `reference` from the shortcut's `input_schema`.

`output_data`object

Output values keyed by each output field's `name` from the shortcut's `output_schema`. Each entry is `{ value, status }` — per-field status, so a run can complete with some fields failed. `null` until the run completes.

`status`string

Current execution status. Terminal: `completed`, `failed`, `error`, `outOfCredits`. In-progress values include `pending` and `inProgress`. Recomputed live from the workflow engine for in-progress runs.

`error_message`string

Error details if the run failed. `null` on success.

`created_at`date-time

When the run was created.

`completed_at`date-time

When the run finished (completed or failed). `null` while in progress.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/shortcuts/:id/runs/:runId"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": {
    "id": "f1a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c",
    "app_id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a",
    "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
    "data_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "user_email": "ada@acme.com",
    "org_id": "7c3e5b2a-9f10-4d23-8b71-1a2b3c4d5e6f",
    "input_data": {
      "linkedin_url": "https://www.linkedin.com/company/floqer"
    },
    "output_data": {
      "Company Website": {
        "value": "https://floqer.com",
        "status": "completed"
      },
      "Employee Count": {
        "value": "11-50",
        "status": "completed"
      }
    },
    "status": "completed",
    "error_message": null,
    "created_at": "2025-01-15T10:30:00Z",
    "completed_at": "2025-01-15T10:31:42Z"
  }
}
```

Try it

## Environment Variables

Organization-level values you reference from action fields as `{{env.<key>}}` — an API base URL, a shared vendor key, a per-rep email signature. Floqer substitutes the value for every row when the workflow runs, so the reference lives on the action and the value stays in one place.

**Read-only here.** Variables are created, edited, and given values in the Floqer app, where the org-lead permissions live. `GET /api/v1/environment-variables` exists so you can discover what exists and how to reference it.

**Values are never returned by this API.** Each entry carries the definition plus `value_count` — how many scope points have a value — so you can tell a configured variable from an empty one.

**Scopes.**`org` — one value for the whole organization, usable in any workflow. `user` — one value per member, resolved against the workflow's **owner**, not whoever triggers the run; usable in any workflow. `workflow` — one value per workflow, so it resolves **only** in workflows that have a value set for it. Pass `workflow_id` to list just what is usable in a given workflow.

**Not usable in conditions.** Environment variables are substituted after `run_if` and the Filter action's `path_conditions` are evaluated, so a reference there never resolves. Configure Action returns an `env_not_supported_in_conditions` warning and skips that condition.

**Scope:**`workflows:read`.

## List Environment Variables

get`/api/v1/environment-variables`

Returns the organization's environment variables, sorted by `key`. Reference one from any string value in **Configure Action**'s `inputs` using its `reference` token (e.g. `{{env.api_base}}`) — on its own, or mixed with literal text and other references. The value is substituted for every row when the workflow runs.

**Values are never returned by this API.** Each entry carries the definition plus `value_count`, the number of scope points that currently have a value, so you can tell a configured variable from an empty one. Values are set in the Floqer app.

**Scopes.**`org` — one value for the whole organization, usable in any workflow. `user` — one value per member, resolved against the workflow's owner (not whoever triggers the run), usable in any workflow. `workflow` — one value per workflow, so it resolves only in workflows that have a value set for it.

**Pass `workflow_id`** to get the list usable in that workflow: workflow-scoped variables with no value there are omitted. Without it you get the org-wide inventory, which can include workflow-scoped variables that resolve in no workflow you are currently building.

Referencing a workflow-scoped variable that has no value on the workflow you are configuring is allowed — Configure Action stores it and returns an `env_variable_unavailable` warning — but rows error until a value is set.

Environment variables cannot be used in conditions (`run_if`, or the Filter action's `path_conditions`); they are substituted after conditions are evaluated.

Requires the `workflows:read` scope.

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Query parameters

`workflow_id`string

Restrict the list to variables usable in this workflow — drops workflow-scoped variables that have no value set for it. A sheet ID also works; it resolves to its parent workflow.

### Response

`status`integer

`data`array

`key`string

Canonical identifier, unique within the organization and immutable once created. This is the segment used in the reference token.

`name`string

Display name shown in the Floqer app. Editable, so reference a variable by `key`, never by name.

`description`string

Free-text description set by the org. `null` when unset.

`scope`string

Which axis the value varies along. `org` — one value for the whole organization. `workflow` — one value per workflow. `user` — one value per member, resolved against the workflow's owner.

`reference`string

Ready-to-paste reference token. Use it as, or inside, any string value in Configure Action's `inputs`.

`value_count`integer

How many scope points currently have a value: 1 or 0 for `org` scope, and for `workflow` / `user` scope the number of workflows / members with one set. `0` means the variable resolves nowhere yet.

`created_by`string

Email of the member who created the variable. `null` on older rows.

`updated_at`date-time

UTC timestamp (ISO 8601) the definition was last changed. Setting a value does not change it.

Requestcurl

```
curl -X GET "https://api.floqer.com/api/v1/environment-variables"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
```

Response200

```
{
  "status": 200,
  "data": [\
    {\
      "key": "api_base",\
      "name": "API Base",\
      "description": "Base URL for our internal API.",\
      "scope": "org",\
      "reference": "{{env.api_base}}",\
      "value_count": 1,\
      "created_by": "admin@acme.com",\
      "updated_at": "2026-07-30T15:41:40.634Z"\
    },\
    {\
      "key": "prospeo_key",\
      "name": "Prospeo-Key",\
      "description": null,\
      "scope": "workflow",\
      "reference": "{{env.prospeo_key}}",\
      "value_count": 3,\
      "created_by": "ops@acme.com",\
      "updated_at": "2026-09-02T22:43:26.490Z"\
    },\
    {\
      "key": "email_signature",\
      "name": "Email Signature",\
      "description": "Appended to every outbound email.",\
      "scope": "user",\
      "reference": "{{env.email_signature}}",\
      "value_count": 4,\
      "created_by": "admin@acme.com",\
      "updated_at": "2026-08-14T18:50:44.743Z"\
    }\
  ]
}
```

Try it

## Agent

Run **Floqer Nova**, the web agent, as a single synchronous call. You give it a natural-language `prompt` and, optionally, an `output_schema` describing the JSON fields you want back; it browses the web and returns the answer in the same response.

Unlike every other run endpoint here, this one is **not** asynchronous — there is nothing to poll. The request stays open for the 10–60 seconds the agent typically needs. The server gives up after 150 seconds and answers `504 AGENT_TIMEOUT`, so set your client timeout above that.

**Billing:** one Floqer Nova charge per request. Any 5xx is refunded automatically — a 5xx means you were not charged. Every response, success or failure, carries a `request_id` (`fl_…`); quote it in support requests.

**Scope:**`agent:run`.

## Run Agent

post`/api/v1/agent/run`

Runs a single Floqer Nova web-agent request synchronously and returns the result. Provide a natural-language `prompt` and, optionally, an `output_schema` describing the JSON fields you want back. Requests typically take 10-60 seconds; the request stays open until the agent finishes. Billing: one Floqer Nova charge per request. Any 5xx response is automatically refunded — a 5xx means you were not charged. Every response (success or error) includes a `request_id` (e.g. `fl_…`) — quote it when contacting support. Error codes: INVALID\_REQUEST, INVALID\_OUTPUT\_SCHEMA (400); UNAUTHORIZED (401); INSUFFICIENT\_CREDITS (402); INSUFFICIENT\_SCOPE (403); RATE\_LIMITED (429); AGENT\_INTERNAL\_ERROR, AGENT\_EXECUTION\_FAILED (500); AGENT\_BUSY (503); AGENT\_TIMEOUT (504).

Requires API key. [Authentication ↑](/content/docs/reference#guide-authentication/index.html)

### Request body

`prompt`stringrequired

Natural-language instruction for the web agent.

`output_schema`objectoptional

Optional JSON-schema-style object describing the structured output. Shape: `{ "properties": { "<field>": { "type": "string", "description": "..." } } }`. Supported field types: string, number, integer, boolean, array, object. Max 20 fields. When omitted, the full answer is returned under a single `result` field.

### Response

`status`integer

`data`object

Structured output keyed by your output\_schema fields, or `{ result }` when no schema given.

`citations`array

Web sources the agent consulted.

`request_id`string

Unique id for this request (e.g. fl\_…). Quote it when contacting support.

Requestcurl

```
curl -X POST "https://api.floqer.com/api/v1/agent/run"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
  -H "Content-Type: application/json"
  -d '{
  "prompt": "string",
  "output_schema": {}
}'
```

Response200

```
{
  "status": 0,
  "data": {},
  "citations": [\
    {}\
  ],
  "request_id": "string"
}
```

Try it

### Caching

Workflow execution is cached per **cell** — one action's execution on one row. When `cache_enabled` is on for a sheet (default: on), re-running a cell whose resolved inputs match a prior run pulls the output from the cache — the cell is not re-executed and not re-billed. This is what makes the iterative build loop affordable.

**The simple rule.** If a cell's resolved inputs exactly match a prior run within the cache window, you don't pay for that cell. Any difference — including a single whitespace character — is a cache miss and the cell runs fresh.

### Cache key

A cache hit requires an exact match on two things, within the cache window:

- **The cell's resolved inputs**— the final values after substituting references like`{{input.linkedin_url}}` or`{{scrape_company_linkedin_profile_1.company_name}}`, plus any static parameters (prompt text, model, provider). A whitespace change in a prompt template changes the resolved prompt, so it counts.
- **A cache entry newer than the sheet's `cache_since`timestamp**— entries older than `cache_since` are ignored.

### What invalidates the cache

- **Action configuration changes**— any edit: a different prompt, a changed model, a swapped provider. **Even a single whitespace change counts**, because the hash is computed over the exact string. Re-running after the edit re-executes and re-bills every cell for that action.
- **Resolved input changes**— when an upstream cell produces a different output on a re-run, the downstream cell's resolved inputs change, its hash changes, and it re-runs. Where the upstream cell's output is identical, the downstream cell still hits cache.
- **Cache window advancing**— advancing `cache_since` invalidates older entries in bulk (see below).
- **New cells** — cells that have never been executed have no cache entry to hit, so the first run always bills in full.

### Cost implication of iteration

In the “run 10, tweak action 3's prompt, run 10 again” loop (10 rows of data):

- Action 1 and action 2 cells hit cache for every row (config and inputs unchanged) — no re-bill.
- Action 3 re-runs for all 10 of its cells (config hash changed) — bills for all 10.
- Downstream cells — action 4, action 5, and so on — re-run **only for rows where action 3's output changed**. For rows where action 3 produces the same output after your edit, the downstream cells for those rows see identical resolved inputs and still hit cache. For rows where action 3's output differs, the downstream cell's input hash changes and it re-runs.

How far the cascade travels depends on how deterministic each action is. Stable lookups and scrapers of unchanging data often produce identical output across re-runs, leaving downstream cells intact. Generative actions (AI content with non-zero temperature, waterfall providers) are more likely to produce different output, so downstream cells cascade further.

**When budgeting a re-run, assume the worst case — every downstream cell re-bills.** Treat downstream cache hits as a bonus, not a guarantee.

### Disabling caching

Set `cache_enabled: false` on a sheet via Update Workflow. Every run then executes every action fresh, regardless of prior runs. Useful for non-deterministic actions you want to re-sample (e.g. generative AI with high temperature) or for debugging cache behavior. With caching off, every re-run is billed in full.

### cache\_since

Sheets also carry a `cache_since` timestamp. Only cache entries produced after this timestamp are valid — advancing it invalidates older entries in bulk without having to disable caching entirely.
