API Reference

The Floqer Public API is organized around REST. All requests use Bearer token authentication and return JSON responses.

Base URL: https://api.floqer.com/api/v1

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

curl
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 response
{
  "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 envelope
{
  "status": 200,
  "data": { ... }
}

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

single error
{
  "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 error
{
  "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

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

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

400
{
  "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

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

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

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

Exceeding 200 requests/minute

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

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

3 endpoints

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

TypeMeaningExample value
stringGeneral text"Floqer Inc"
urlA URL — actions that need URLs match against this type"https://linkedin.com/company/floqer"
emailAn email address"hello@floqer.com"
numberA numeric value42

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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

statusinteger
dataarray
namestring
typestring
descriptionstring
referencestring

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}}"
    }
  ]
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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

namestringrequired

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

typestringrequired

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

descriptionstringoptional

Human-readable description of what this field contains

Response

statusinteger
dataarray
namestring
typestring
descriptionstring
referencestring

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}}"
    }
  ]
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_namerequired

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

Response

statusinteger
dataarray
namestring
typestring
descriptionstring
referencestring

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}}"
    }
  ]
}

Actions

10 endpoints

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.

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.

After adding, use Configure Action to wire input variables.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idstringrequired

Action template identifier from the action catalog (e.g. scrape_company_linkedin_profile)

afterstringoptional

Insert after this action_instance_id. Omit to append to the end of the chain.

namestringoptional

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.

Response

statusinteger
dataobject
action_instance_idstring

Unique instance ID. Reference outputs with {{action_instance_id.field_name}}

action_idstring

Action template identifier

display_namestring

Human-readable name

inputsarray

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

namestring
typestring
requiredboolean
descriptionstring
outputsarray

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

namestring
typestring

One of: string, url, email, number, raw_array, structured_array

referencestring

Copy this into downstream action configuration

fieldsarray

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"
}'
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"
      }
    ]
  }
}

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).
  • run_if — optional gate. The action runs for a row only when the resolved value of variable satisfies operator against values. 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.

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.

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.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

The action instance ID (e.g. scrape_company_linkedin_profile_1). Returned by Add Action to Workflow.

Request body

inputsobjectoptional

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

run_ifobjectnulloptional

Optional gate deciding whether this action runs for a given row. The action runs only when the resolved value of variable satisfies operator against values. Pass null to clear an existing condition. Omit to leave the existing condition unchanged. See the operator reference in the description above.

continue_workflow_on_failurebooleanoptional

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

statusinteger
warningsarray

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.

fieldstring

Field name the warning applies to

codestring

Machine-readable warning category

messagestring

Human-readable explanation, sometimes with suggestions

dataobject
action_instance_idstring

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": {
    "variable": "{{input.country}}",
    "operator": "is",
    "values": ["US", "CA"]
  }
}
Numeric and presence checks

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": {
    "variable": "{{scrape_company_linkedin_profile_1.employee_count}}",
    "operator": "greater than",
    "values": ["100"]
  }
}
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": {
    "variable": "{{input.country}}",
    "operator": "is",
    "values": [
      "US",
      "CA"
    ]
  },
  "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"
  }
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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

statusinteger
dataarray

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

action_instance_idstring

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_idstring

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

display_namestring

Human-readable action name.

inputsobject

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.

outputsarray

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

namestring
typestring
referencestring

Ready-to-paste variable reference for downstream actions.

continue_workflow_on_failureboolean

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_ifobject

Conditional gate that decides whether this action runs for a given row. Mirrors the value set via Configure Action's run_if. Absent when no condition is configured.

variablestring
operatorstring
valuesarray
nextarray

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": {
        "variable": "{{input.country}}",
        "operator": "is",
        "values": [
          "US",
          "CA"
        ]
      },
      "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": []
    }
  ]
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

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

statusinteger
warningsarray

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.

fieldstring

Field name the warning applies to.

codestring

Machine-readable warning category.

messagestring

Human-readable explanation, sometimes with suggestions.

dataobject

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

action_instance_idstring

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

action_idstring

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

display_namestring

Human-readable action name.

inputsobject

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.

outputsarray

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

namestring
typestring
referencestring

Ready-to-paste variable reference for downstream actions.

continue_workflow_on_failureboolean

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_ifobject

Conditional gate that decides whether this action runs for a given row. Mirrors the value set via Configure Action's run_if. Absent when no condition is configured.

variablestring
operatorstring
valuesarray
nextarray

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": {
      "variable": "{{input.country}}",
      "operator": "is",
      "values": [
        "US",
        "CA"
      ]
    },
    "continue_workflow_on_failure": false,
    "next": [
      "filter_1"
    ]
  }
}

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.

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.

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.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

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

statusinteger
dataobject
deletedboolean

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

action_instance_idstring

Echoes the action instance that was deleted.

dependent_actionsarray

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_idstring

The dependent action's instance ID.

inputsobject

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}}."
        }
      }
    ]
  }
}

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.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

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

field_namerequired

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

Request body

contextobjectoptional

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

statusinteger
dataobject
optionsarray

Available option values for the requested field.

valuestring

The value to send back via Configure Action.

labelstring

Human-readable label for the option.

extrasobject

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"
      }
    ]
  }
}

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

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

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

Response

statusinteger
dataobject
action_instance_idstring

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

outputsarray

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.

namestring

Output field identifier (snake_case).

typestring

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.

referencestring

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

descriptionstring

Human-readable description of this output.

columnsarray

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

namestring

Column identifier (snake_case).

typestring

Column data type.

structured_array_referencestring

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}}"
          }
        ]
      }
    ]
  }
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

The action instance ID (e.g. `enrich_company_linkedin_profile_1`). Returned by Add Action to Workflow.

Request body

namestringrequired

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

Response

statusinteger
dataobject
action_instance_idstring

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

display_namestring

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"
  }
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

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

afterstringoptional

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

statusinteger
dataobject
movedboolean

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

action_instance_idstring

Echoes the action instance that was moved.

afterstring

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"
  }
}

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.

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.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

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

notestringrequired

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

Response

statusinteger
dataobject
action_instance_idstring

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

sheet_idstring

Sheet the note is scoped to.

notestring

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."
  }
}

Sheets

2 endpoints

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.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

Request body

namestringrequired

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

auto_runbooleanoptional

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_enabledbooleanoptional

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_sincedateoptional

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

statusinteger
dataobject
sheet_iduuid

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_iduuid

UUID of the workflow this sheet belongs to.

namestring

Sheet name as set at creation.

auto_runboolean

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_enabledboolean

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_sincedate

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
  }
}

List Sheets

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

Returns every sheet in a workflow, including the main sheet. Each entry is a summary — for the full state of a sheet, call the sheet-scoped endpoints with the returned sheet_id (List Inputs for input columns, Get Action Graph for the action chain, List Rows for row data and per-cell status).

data[0] is always the main sheet — its sheet_id equals the workflow_id you passed in the path and is_main_sheet is true. Additional sheets follow in creation order.

When to use. Before configuring send_to_sheet to find a target_sheet_id, or to enumerate auto_run/cache_enabled/cache_since across every sheet in a workflow.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

Response

statusinteger
dataarray
sheet_iduuid

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_iduuid

UUID of the workflow this sheet belongs to.

namestring

Sheet name as set at creation.

is_main_sheetboolean

true for the workflow's main sheet — auto-created with the workflow, always present, cannot be deleted separately, and has sheet_id === workflow_id. false for additional sheets added via Create Sheet.

auto_runboolean

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_enabledboolean

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_sincedate

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 GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets"
  -H "Authorization: Bearer floq_YOUR_API_KEY"
Response200
{
  "status": 200,
  "data": [
    {
      "sheet_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
      "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
      "name": "Lead Enrichment Pipeline",
      "is_main_sheet": true,
      "auto_run": false,
      "cache_enabled": false,
      "cache_since": null
    },
    {
      "sheet_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
      "name": "Employees",
      "is_main_sheet": false,
      "auto_run": true,
      "cache_enabled": true,
      "cache_since": "2026-03-17"
    }
  ]
}

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.

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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

rowsarrayrequired

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_addstringoptional

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

statusinteger
warningsarray

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

fieldstring
codestring
messagestring
dataobject
row_countinteger

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

row_idsarray

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.

rejectedarray

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.

rowobject

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.

errorsarray
fieldstring
codestring

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

messagestring
rows_queued_for_runinteger

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
  }
}

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 (requires confirm_sheet_id for safety).

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idsarrayoptional

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_10booleanoptional

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

statusinteger
messagestring
dataobject
rows_queuedinteger

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
  }
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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

statusinteger
messagestring
dataobject
rows_queuedinteger

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
  }
}

Actions

1 endpoints

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.

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_actionfalse (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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idrequired

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

Request body

row_idsarrayoptional

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_actionbooleanoptional

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

statusinteger
messagestring
dataobject
rows_queuedinteger

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
  }
}

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

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 queuedrunningcomplete | failed. Complete cells carry their outputs inline as outputs when small, or as a URL under outputs_ref when the payload is large (e.g. LinkedIn profile scrapes, enrichment blobs — fetch the URL with your API key to read the data). Failed cells carry an error message. queued and running cells carry only status.

Paginated — default page size 20, max 200.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idsarrayoptional

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.

page_nointegeroptional

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

page_sizeintegeroptional

Rows per page. Defaults to 20. Maximum 200.

Response

statusinteger
dataobject
rowsarray

Rows on this page, in insertion order.

row_iduuid

UUID assigned by Add Rows.

row_statusstring

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_atdate-time

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

inputsobject

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

cellsobject

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_countinteger

Total rows on the sheet across all pages.

page_nointeger
page_sizeinteger
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 '{
  "row_ids": [
    "a1b2c3d4-e5f6-7890-abcd-ef1234567001",
    "a1b2c3d4-e5f6-7890-abcd-ef1234567002",
    "a1b2c3d4-e5f6-7890-abcd-ef1234567003"
  ],
  "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://storage.floqer.com/orgs/acme/cells/a1b2c3d4-scrape-1.json",
              "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
  }
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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_idsarrayrequired

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

Response

statusinteger
dataobject
deleted_countinteger

Number of rows actually deleted. Equals deleted_row_ids.length.

deleted_row_idsarray

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

rejectedarray

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_idstring

The UUID you sent that couldn't be deleted.

errorstring

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"
      }
    ]
  }
}

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 ↑

Path parameters

workflow_idrequired

Workflow ID.

sheet_idrequired

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

statusinteger
dataobject
deleted_countinteger

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
  }
}

Manage Workflows

Admin-only CRUD on workflow records — create, list, 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 ↑

Request body

namestringrequired

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

Response

statusinteger
dataobject
workflow_iduuid

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

namestring

Name as set in the request.

created_atdate-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"
  }
}

List Workflows

get/api/v1/workflows/

Returns all workflows in the caller's organization, sorted by most recently updated first. Each entry carries the ID, name, and timestamps — no deeper state.

Requires API key. Authentication ↑

Response

statusinteger
dataarray
workflow_iduuid

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

namestring

Workflow name as set at creation.

created_atdate-time

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

updated_atdate-time

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

last_run_atdate-time

UTC timestamp (ISO 8601) of the most recent run queued on any sheet in this workflow. null if the workflow has never been run.

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
    }
  ]
}

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.

Requires API key. Authentication ↑

Path parameters

workflow_idrequired

Workflow ID.

Response

statusinteger
dataobject
workflow_iduuid

UUID of the workflow that was deleted.

deletedboolean

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
  }
}

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.