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.
Authorization: Bearer floq_YOUR_API_KEYYour first request
List all workflows in your organization. If the request succeeds, the response envelope contains a data array.
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
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.
{
"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:
{
"status": 200,
"data": { ... }
}Single-issue errors (401, 403, 404, 429, 500) use a flat envelope:
{
"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:
{
"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
{
"status": 400,
"error": "Validation Error",
"message": "1 validation error",
"errors": [
{ "field": "name", "message": "required" }
]
}PUT /workflows/:id/inputs with an invalid type
{
"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
{
"status": 401,
"error": "Unauthorized",
"message": "API key is required. Pass it as: Authorization: Bearer floq_..."
}GET /workflows/:id with a non-existent ID
{
"status": 404,
"error": "Not Found",
"message": "Workflow not found"
}Exceeding 200 requests/minute
{
"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.
Get User
/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 ↑
Response
statusintegerdataobjectCaller bootstrap payload. Identity and org membership are always present. Use List Workflows for accessible workflow metadata.
emailemailCaller's email address (Floqer username).
first_namestringCaller's first name from their Floqer profile. null if unset.
last_namestringCaller's last name from their Floqer profile. null if unset.
rolestringOrganization role: admin (org lead), member, or user (sources, shortcut runs, connections only). A label only; gate on scopes, which is authoritative (an unrecognised stored role reads as member with no scopes). Renamed from operator in STAP-3793.
org_idstringUUID of the caller's organization.
org_membersobjectMembers of the caller's organization. Emails are Floqer usernames.
countintegerNumber of members in the organization.
emailsarrayEmail address of each org member (Floqer username), sorted alphabetically.
curl -X GET "https://api.floqer.com/api/v1/user/"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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"
]
}
}
}Organization
Your organization: who is in it, and what it knows.
Members. POST /api/v1/org/members invites a person — they get an email invitation and hold no access until they accept, so the roster from Get User is unchanged until then. DELETE /api/v1/org/members removes a member, revoking their API keys and withdrawing the credentials they shared with the org before dropping them from the roster; removing an address that was only invited revokes the invitation instead. Both need the members:write scope, which only an org admin's API key can carry — a key minted by a member or a user can never hold it, whatever scopes it was stamped with.
Seats. A pending invitation reserves a seat against the plan's member limit, so an org near its cap can be refused even though the roster itself has room.
Knowledge. 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. Set Knowledge is an idempotent full replacement. No scope required; a key reads and writes only its own org's file. These two keep their /api/v1/user/knowledge paths.
Not here. Changing an existing member's role, and listing pending invitations, are done in the Floqer app.
Add Member
/api/v1/org/membersInvites a person to your organization. They receive an email invitation and hold no access until they accept, so the roster returned by Get User is unchanged until then — a successful call has not yet given anyone access.
Check data.email_sent. The invitation is recorded whether or not the email goes out. When it is false the invitee was never told, the seat is still held, and re-inviting is refused with ALREADY_INVITED — revoke it with Remove Member and invite again.
A pending invitation still reserves a seat against your plan's member limit, so an org near its cap can be refused even though the roster itself has room.
Scope: members:write — carried only by an org admin's API key.
Requires API key. Authentication ↑
Request body
emailstringrequiredEmail address to invite. Matched case-insensitively against the existing roster.
rolestringrequiredRole the invitation carries. admin can manage members and billing, member can build and run workflows. Required — there is no default. A narrower third role exists and is reported by Get User, but is not invitable over the API yet; invite as member and change the role in the Floqer app.
Response
statusintegermessagestringdataobjectA pending invitation. The invitee holds no access until they accept.
emailstringThe invited email address, lowercased.
rolestringRole the invitee receives on acceptance.
statusstringAlways pending — acceptance happens in the Floqer app or at signup.
email_sentbooleanWhether the invitation email was accepted for delivery. false means the invitation is recorded and holds a seat but nobody was told — re-inviting returns ALREADY_INVITED, so revoke it with Remove Member and invite again.
curl -X POST "https://api.floqer.com/api/v1/org/members"
-H "Authorization: Bearer floq_YOUR_API_KEY"
-H "Content-Type: application/json"
-d '{
"email": "sam@acme.com",
"role": "member"
}'{
"status": 201,
"message": "Invitation sent",
"data": {
"email": "sam@acme.com",
"role": "member",
"status": "pending",
"email_sent": true
}
}Remove Member
/api/v1/org/membersRemoves a person from your organization. Their API keys are revoked and the credentials they shared with the org are withdrawn before the roster entry is dropped, so access dies first and nothing outlives the membership.
If the address has a pending invitation rather than a membership, the invitation is revoked instead — data.removed says which happened.
Check data.warnings. Removal is never blocked, so anything of theirs the org was relying on is affected: workflows using a connection they owned start failing, active sources they owned stop importing, and workflows they still own keep running — scheduled runs charge the owner, so the org keeps paying for them. Each warning names the affected resources so you can reassign them. An empty array means none of those checks found anything, not that the member was inconsequential.
Percent-encode the address. A literal + in a query string decodes to a space, so send sam+team@acme.com as sam%2Bteam%40acme.com.
You cannot remove yourself; another admin has to. An empty email is a 400, never a wildcard.
Scope: members:write — carried only by an org admin's API key.
Requires API key. Authentication ↑
Query parameters
emailstringrequiredEmail address of the member to remove, or of the pending invitation to revoke.
Response
statusintegermessagestringdataobjectWhat the call removed.
emailstringThe removed email address, lowercased.
removedstringmember when an active member was removed, invitation when a pending invitation was revoked.
warningsarrayWhat the removal broke or left behind. The removal still succeeded — these are consequences to act on, not failures. An empty array means none of the checks found anything, not that the member was inconsequential.
codestringWORKFLOWS_LOST_CONNECTION — workflows used a connection they owned and now fail. WORKFLOWS_LOST_OWNER — workflows they own keep running; scheduled runs charge the owner, so the org keeps paying for them. SOURCES_LOST_OWNER — active sources they owned stop importing. IMPACT_UNKNOWN — a lookup failed or was capped, so check their integrations by hand.
messagestringHuman-readable summary.
resourcesarrayWhat is affected. Absent on IMPACT_UNKNOWN.
idstringnamestringtypestringcurl -X DELETE "https://api.floqer.com/api/v1/org/members"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"message": "Member removed",
"data": {
"email": "sam@acme.com",
"removed": "member",
"warnings": [
{
"code": "WORKFLOWS_LOST_CONNECTION",
"message": "2 workflows used a connection this member owned. They will fail until someone reconnects the integration.",
"resources": [
{
"id": "7c3e5b2a",
"name": "Enrich inbound",
"type": "workflow"
},
{
"id": "9a1f4d8c",
"name": "CRM sync",
"type": "workflow"
}
]
},
{
"code": "SOURCES_LOST_OWNER",
"message": "1 active source was owned by this member and will stop importing. Recreate it under another member.",
"resources": [
{
"id": "b2d6e017",
"name": "HubSpot contacts",
"type": "source"
}
]
}
]
}
}Get Knowledge
/api/v1/user/knowledgeReturns 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 Knowledge.
No scope required — any valid API key may read its own org's knowledge file.
Requires API key. Authentication ↑
Response
statusintegerdataobjectThe 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.
contentstringThe file's text content. null when no file has been written yet.
existsbooleanWhether a knowledge file currently exists for the org.
updated_atdate-timeISO 8601 timestamp of the last write, or null when unset.
curl -X GET "https://api.floqer.com/api/v1/user/knowledge"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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"
}
}Set Knowledge
/api/v1/user/knowledgeCreates 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 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 ↑
Request body
contentstringrequiredThe full text/markdown content of the knowledge file. Replaces any existing content. Send an empty string to clear it.
Response
statusintegerdataobjectcontentstringThe file's text content after the write.
existsbooleanWhether a knowledge file currently exists for the org.
updated_atdate-timeISO 8601 timestamp of this write.
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."
}'{
"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"
}
}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 endpointsEach 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" |
| An email address | "hello@floqer.com" | |
| number | A numeric value | 42 |
List Inputs
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/inputsReturns 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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
statusintegerdataarraynamestringtypestringdescriptionstringreferencestringCopy this into action configuration to reference this field
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/inputs"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/inputsAdds 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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
namestringrequiredField name — used as the column header and the key when adding data rows
typestringrequiredData 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
descriptionstringoptionalHuman-readable description of what this field contains
Response
statusintegerdataarraynamestringtypestringdescriptionstringreferencestringCopy this into action configuration to reference this field
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"
}
]'{
"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
/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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_namerequiredThe `name` of the input field (e.g. `email`). Use List Inputs to see all field names.
Response
statusintegerdataarraynamestringtypestringdescriptionstringreferencestringCopy this into action configuration to reference this field
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"{
"status": 200,
"data": [
{
"name": "email",
"type": "email",
"description": "",
"reference": "{{input.email}}"
}
]
}Actions
11 endpointsBrowse 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
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/addAdds 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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idstringrequiredAction template identifier from the action catalog (e.g. scrape_company_linkedin_profile)
afterstringoptionalInsert after this action_instance_id. Omit to append to the end of the chain.
namestringoptionalOptional 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.
inputsobjectoptionalOptional. 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_ifobject | nulloptionalOptional. Same as Configure Action's run_if — see that endpoint for the condition-group shape.
continue_workflow_if_action_failsbooleanoptionalOptional. Same as Configure Action's field of the same name: when true, downstream actions still run for a row even if this action fails.
notestringoptionalOptional free-text note to save on the action, identical to Save Action Note.
Response
statusintegerdataobjectaction_instance_idstringUnique instance ID. Reference outputs with {{action_instance_id.field_name}}
action_idstringAction template identifier
display_namestringHuman-readable name
inputsarrayFields that need to be configured. Wire each one with a variable reference or static value using Configure Action.
namestringtypestringrequiredbooleandescriptionstringoutputsarrayFields this action will produce. Use the reference string in downstream action configuration.
namestringtypestringOne of: string, url, email, number, raw_array, structured_array
referencestringCopy this into downstream action configuration
fieldsarraySub-fields for structured_array types — these become input columns when expanded to a sheet
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"
}'{
"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
/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'spath_conditions(a single condition is one group with one leaf). Passnullto clear an existing condition.continue_workflow_on_failure— whentrue, 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 emptyignorevalues. - 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 bymoment(...). - 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 isworkflowscoped and has no value on this workflow. The reference is still saved, but rows error until a value is set for it. Passworkflow_idto List Environment Variables to see only the variables usable here.env_not_supported_in_conditions— an environment variable was used inrun_ifor a Filter action'spath_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.
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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe action instance ID (e.g. scrape_company_linkedin_profile_1). Returned by Add Action to Workflow.
Request body
inputsobjectoptionalMap of field name → value. Send only the fields you want to set.
run_ifobject | nulloptionalOptional 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_failurebooleanoptionalWhen 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
statusintegerwarningsarrayRead 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).
fieldstringField name the warning applies to
codestringMachine-readable warning category
messagestringHuman-readable explanation, sometimes with suggestions
dataobjectaction_instance_idstringExamples
Variable references, static text, or mixed.
{
"inputs": {
"linkedin_url": "{{input.linkedin_url}}",
"prompt": "Research {{input.company_name}} at {{scrape_company_linkedin_profile_1.website}}"
}
}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"]
}
}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"] }
]
}
]
}
}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" }
]
}
]
}
}Pass `null` to remove a previously set `run_if`. Omitting the field instead leaves the existing condition unchanged.
{
"run_if": null
}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
}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
}'{
"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
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/graphReturns 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 noinputsof its own — itsoutputslist every input field with ready-to-paste{{input.field_name}}references. If no inputs have been defined yet,outputsis[].data[1..]are the actions, in topological execution order from the entry action onward. If no actions have been added yet,datacontains only the input node withnext: [].- Walk the chain by following each node's
nextarray.next: []is terminal. - Multiple IDs in
nextrepresent 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-pastereferencestring. Absent on actions with no outputs (e.g. filter).continue_workflow_on_failure— present only when configured via Configure Action.truelets 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 resolvedvariablesatisfiesoperatoragainstvalues.
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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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
statusintegerdataarrayNodes in topological execution order. data[0] is the input node (action_id: "input"). data[1..] are actions in execution order.
action_instance_idstringUnique 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_idstringTemplate identifier from the action catalog (e.g. scrape_company_linkedin_profile). The reserved value "input" marks the input node.
display_namestringHuman-readable action name.
inputsobjectMap 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.
outputsarrayFields 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).
namestringtypestringreferencestringReady-to-paste variable reference for downstream actions.
continue_workflow_on_failurebooleanWhen 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_ifobjectConditional 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.
conditionsarrayAND/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.
conditionsarrayvariablestringoperatorstringvaluesarraycombinatorstringcombinatorstringcontinue_workflow_if_run_condition_not_metbooleanWhen 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.
nextarrayIDs 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.
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/actions/graph"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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": []
}
]
}Get Action
/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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe 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
statusintegerwarningsarrayValidation 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.
fieldstringField name the warning applies to.
codestringMachine-readable warning category.
messagestringHuman-readable explanation, sometimes with suggestions.
dataobjectThe action node. Same shape as nodes in the data array returned by Get Action Graph.
action_instance_idstringUnique instance ID for this action (e.g. scrape_company_linkedin_profile_1). Used in variable references and when calling Configure Action.
action_idstringTemplate identifier from the action catalog (e.g. scrape_company_linkedin_profile).
display_namestringHuman-readable action name.
inputsobjectMap 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.
outputsarrayFields this action produces, each with a ready-to-paste reference string for downstream actions. Absent on actions that produce no fields (e.g. filter).
namestringtypestringreferencestringReady-to-paste variable reference for downstream actions.
continue_workflow_on_failurebooleanWhen 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_ifobjectConditional 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.
conditionsarrayAND/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.
conditionsarrayvariablestringoperatorstringvaluesarraycombinatorstringcombinatorstringcontinue_workflow_if_run_condition_not_metbooleanWhen 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.
nextarrayIDs of immediate downstream actions. [] marks a terminal action. Multiple IDs represent a split path — today this will contain zero or one entry.
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"{
"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"
]
}
}Delete Action
/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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe 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
statusintegerdataobjectdeletedbooleanAlways true on a 200 response — the action was removed from the chain.
action_instance_idstringEchoes the action instance that was deleted.
dependent_actionsarrayActions 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_idstringThe dependent action's instance ID.
inputsobjectMap of <snake_field_name> → input value (string). Only fields that referenced the deleted action are included; unrelated fields on the same action are omitted.
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"{
"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
/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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe action instance ID whose field needs options. Returned by **Add Action**, **Get Action**, or **Get Action Graph**.
field_namerequiredSnake-cased input field name from the action's `inputs[]` (matches what **Add Action** returns and what **Configure Action** accepts).
Request body
contextobjectoptionalOptional 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
statusintegerdataobjectoptionsarrayAvailable option values for the requested field.
valuestringThe value to send back via Configure Action.
labelstringHuman-readable label for the option.
extrasobjectPer-integration metadata. Shape varies by integration — use it for richer rendering when needed.
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"
}
}'{
"status": 200,
"data": {
"options": [
{
"value": "Account",
"label": "Account"
},
{
"value": "Contact",
"label": "Contact"
},
{
"value": "Lead",
"label": "Lead"
}
]
}
}Get Action Output Schema
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/outputsReturns 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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe action instance whose output schema to return. Returned by **Add Action**, **Get Action**, or **Get Action Graph**.
Response
statusintegerdataobjectaction_instance_idstringEcho of the path parameter, for response-level self-description.
outputsarrayFields 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.
namestringOutput field identifier (snake_case).
typestringOutput 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.
referencestringVariable 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).
descriptionstringHuman-readable description of this output.
columnsarrayPresent 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).
namestringColumn identifier (snake_case).
typestringColumn data type.
structured_array_referencestring3-segment reference token for this column inside the parent structured_array. Shape: {{<action_instance_id>.<list>.<column>}}.
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"{
"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
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/nameSets 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe action instance ID (e.g. `enrich_company_linkedin_profile_1`). Returned by Add Action to Workflow.
Request body
namestringrequiredNew display name for the action instance. Trimmed; cannot be empty after trimming.
Response
statusintegerdataobjectaction_instance_idstringEcho of the path parameter — the action instance that was renamed. Unchanged by the rename; variable references ({{<action_instance_id>.<field>}}) keep working.
display_namestringThe stored display name.
Examples
Replace the auto-generated `enrich_company_linkedin_profile_1` with something readable for collaborators.
{
"name": "Enrich primary contact"
}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"
}'{
"status": 200,
"data": {
"action_instance_id": "enrich_company_linkedin_profile_1",
"display_name": "Enrich primary contact"
}
}Move Action
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/moveRe-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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe 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
afterstringoptionalMove 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
statusintegerdataobjectmovedbooleanAlways true on a 200 response — the action has been re-ordered.
action_instance_idstringEchoes the action instance that was moved.
afterstringThe 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
Place this action immediately after `scrape_company_linkedin_profile_1` in the chain.
{
"after": "scrape_company_linkedin_profile_1"
}Pass the literal `"input"` to position the action right after the synthetic input node.
{
"after": "input"
}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"
}'{
"status": 200,
"data": {
"moved": true,
"action_instance_id": "enrich_company_linkedin_profile_1",
"after": "scrape_person_linkedin_profile_1"
}
}Reorder Waterfall Providers
/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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe action instance whose waterfall field to reorder (e.g. `find_work_email_1`).
field_namerequiredSnake-cased waterfall input field name from the action's `inputs[]` (e.g. `work_email`).
Request body
providersarrayrequiredOrdered list of provider apiIds. Must match the set of providers currently configured on the field — reorder only, no adds or removes.
Response
statusintegerdataobjectreorderedbooleanaction_instance_idstringfield_namestringprovidersarrayExamples
Reorder a work-email waterfall so Hunter runs first.
{
"providers": ["hunter", "prospeo", "findymail"]
}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"
]
}'{
"status": 200,
"data": {
"reordered": true,
"action_instance_id": "find_work_email_1",
"field_name": "work_email",
"providers": [
"hunter",
"prospeo",
"findymail"
]
}
}Save Action Note
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/notesAttaches 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe 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
notestringrequiredFree-text note to attach to the action. Trimmed; cannot be empty after trimming.
Response
statusintegerdataobjectaction_instance_idstringEcho of the path parameter — the action the note is attached to.
sheet_idstringSheet the note is scoped to.
notestringThe saved note text.
Examples
Document why an action is configured but intentionally inactive.
{
"note": "Skip until Phase 2 — vendor still negotiating contract terms."
}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."
}'{
"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
4 endpointsA 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_sheetfans each item into its own row on a new sheet for independent enrichment. - Segmentation — categorize and filter rows, then use
send_to_sheetto 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_sheetto 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_sheetto 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.
Create Sheet
/api/v1/workflows/{workflow_id}/sheetsAdds 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_idrequiredWorkflow ID.
Request body
namestringrequiredSheet name. Not required to be unique within a workflow.
auto_runbooleanoptionalWhen 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_enabledbooleanoptionalWhen 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_sincedateoptionalEarliest 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
statusintegerdataobjectsheet_iduuidUUID 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_iduuidUUID of the workflow this sheet belongs to.
namestringSheet name as set at creation.
auto_runbooleanWhen true, the sheet's action chain runs automatically whenever new data rows arrive (via Add Data Rows, a synced source, or send_to_sheet from another sheet). When false, runs must be triggered explicitly with Run Rows. Change it later with Update Sheet.
cache_enabledbooleanWhen 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_sincedateEarliest 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.
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"
}'{
"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
}
}Update Sheet
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}Updates a sheet's settings. Send only the fields to change — at least one of auto_run, cache_enabled, cache_since; omitted fields are left untouched.
auto_run: true (the default on every sheet) runs the action chain on each row that lands — from Add Rows, a synced source (initial backfill and live rows alike), or send_to_sheet. auto_run: false loads rows without running them; run them later with Run Rows (row_ids or first_10).
Works on the main sheet: pass sheet_id === workflow_id. The main sheet is created with the workflow and never passes through Create Sheet, so this is the only way to switch it off. Turn it off before Sync Source to Workflow when you want to load a source's rows without running them.
Applies to rows that arrive after the change; rows already on the sheet are untouched.
cache_enabled / cache_since are the same settings Create Sheet accepts: whether identical-input rows reuse cached outputs, and the earliest date (YYYY-MM-DD, UTC) a cached run may be from to count as fresh. cache_since: null clears the cutoff.
Returns the full updated sheet so the values can be verified without a follow-up Get Workflow Overview.
Requires API key. Authentication ↑
Path parameters
workflow_idrequiredParent workflow ID.
sheet_idrequiredSheet ID to update. Pass the workflow's own ID for the main sheet.
Request body
auto_runbooleanoptionalRun-on-receive toggle. true runs the action chain on every row that lands on the sheet; false loads rows without running them.
cache_enabledbooleanoptionalWhen 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_sincedateoptionalEarliest date (YYYY-MM-DD, UTC) from which cached runs are considered fresh. Runs before this date are ignored even when inputs match. null clears the cutoff — all cached runs are considered fresh regardless of age.
Response
statusintegerdataobjectsheet_iduuidUUID of the sheet.
workflow_iduuidUUID of the parent workflow.
namestringSheet name.
is_main_sheetbooleantrue for the workflow's main sheet (sheet_id === workflow_id).
auto_runbooleanThe stored run-on-receive toggle, as just written.
cache_enabledbooleanWhether identical-input rows reuse cached outputs, as stored.
cache_sincedateEarliest date (YYYY-MM-DD, UTC) from which cached runs are considered fresh, as stored; null when unset.
continue_on_failbooleanWhether a row keeps moving to the next action after an action fails on it.
curl -X PATCH "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id"
-H "Authorization: Bearer floq_YOUR_API_KEY"
-H "Content-Type: application/json"
-d '{
"auto_run": false,
"cache_since": "2026-03-17"
}'{
"status": 200,
"data": {
"sheet_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"name": "Inbound leads",
"is_main_sheet": true,
"auto_run": false,
"cache_enabled": true,
"cache_since": "2026-03-17",
"continue_on_fail": false
}
}Rename Sheet
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/nameSets 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 ↑
Path parameters
workflow_idrequiredParent workflow ID.
sheet_idrequiredSheet ID to rename. Pass the workflow's own ID to rename the main sheet's tab (the workflow name is not changed).
Response
statusintegerdatacurl -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"
}'{
"status": 200,
"data": {
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"sheet_id": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
"name": "Qualified employees"
}
}Delete Sheet
/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 ↑
Path parameters
workflow_idrequiredParent workflow ID.
sheet_idrequiredChild sheet ID to delete. Must not equal `workflow_id` (the main sheet).
Response
statusintegerdataobjectworkflow_iduuidUUID of the parent workflow the sheet belonged to.
sheet_iduuidUUID of the child sheet that was deleted.
deletedbooleanAlways true on a 200 response. Included so agents can branch on a single field.
curl -X DELETE "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"data": {
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"sheet_id": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
"deleted": true
}
}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
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rowsAdds 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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
rowsarrayrequiredRows 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_addstringoptionalWhat 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
statusintegerwarningsarrayNon-blocking issues across the batch (e.g. duplicate detection, normalized values). Shape matches Configure Action's warnings.
fieldstringcodestringmessagestringdataobjectrow_countintegerNumber of rows accepted and written. Equals row_ids.length.
row_idsarrayFlat array of newly-assigned row UUIDs, in request order. Rejected rows are not included. Copy directly into Run Rows's row_ids to execute.
rejectedarrayRows 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.
rowobjectThe original row content as you sent it — echoed back exactly so you can fix and resend without having to track positions in your request.
errorsarrayfieldstringcodestringMachine-readable code indicating the failure reason (e.g. malformed_row, unknown_field).
messagestringrows_queued_for_runintegerHow many newly-added rows were queued for execution. 0 when run_after_add: "none". 10 when "first_10". Equal to row_count when "all".
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"
}'{
"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
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/runRuns a subset of the sheet's rows through the action chain. Specify the subset in one of three 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" (bycreated_atascending). Safe experimentation default.filters/status_filters/created_at— every row matching them, selected on the server (the same shapes List Rows takes). Use this to run a subset — e.g. rows whose cell on an action failed — instead of listing rows to collect their IDs.
Exactly one of these must be provided. None, or more than one, returns 400. An empty filters: [] or status_filters: [] also returns 400 — omit a filter you are not using.
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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idsarrayoptionalRow 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 and the filters.
first_10booleanoptionalShortcut 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.
filtersarrayoptionalValue filters on sheet input columns. Identical shape and operators to List Rows' filters — each entry is { variable, operator, values }, where variable is an {{input.<column>}} reference token. See List Rows for the operator list.
status_filtersarrayoptionalPer-action cell-status filters. Identical shape to List Rows' status_filters — each entry is { action_instance_id, is? , is_not? } with exactly one of is / is_not, using the public status vocabulary (queued | running | complete | failed | error | out_of_credits | condition_not_met).
created_atobjectoptionalFilter rows by creation timestamp. Identical shape to List Rows' created_at — { operator, values }, where values is [ISO timestamp] or [start, end] for is between.
Response
statusintegermessagestringdataobjectrows_queuedintegerNumber 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.
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"
]
}'{
"status": 200,
"message": "Rows queued for execution",
"data": {
"rows_queued": 3
}
}Run All Rows
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/run-allQueues 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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
statusintegermessagestringdataobjectrows_queuedintegerNumber of rows queued for execution. Equals the total row count on the sheet at the time of the call.
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/run-all"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"message": "All rows queued for execution",
"data": {
"rows_queued": 157
}
}Run Action
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/actions/{action_instance_id}/runRuns 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.filters/status_filters/created_at— withrow_ids: [], queue only the rows matching them, selected on the server (the same shapes List Rows takes). E.g. re-run only the rows whose cell on this action failed, without listing their IDs first. Combining them with a non-emptyrow_idsreturns 400, and so does an emptyfilters: []orstatus_filters: []— it never falls back to running every row.run_next_action—false(default) runs only the targeted action.trueruns 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredThe action instance to run. Returned by **Add Action**, **Get Action**, or **Get Action Graph**.
Request body
row_idsarrayoptionalRow 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_actionbooleanoptionalWhether 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.
filtersarrayoptionalWith row_ids: [], run only rows matching these value filters on sheet input columns. Identical shape and operators to List Rows' filters — each entry is { variable, operator, values }, where variable is an {{input.<column>}} reference token. See List Rows for the operator list.
status_filtersarrayoptionalWith row_ids: [], run only rows whose cells match these per-action statuses — e.g. this action's failed / error cells. Identical shape to List Rows' status_filters — each entry is { action_instance_id, is? , is_not? } with exactly one of is / is_not, using the public status vocabulary (queued | running | complete | failed | error | out_of_credits | condition_not_met).
created_atobjectoptionalWith row_ids: [], run only rows created in this window. Identical shape to List Rows' created_at — { operator, values }, where values is [ISO timestamp] or [start, end] for is between.
Response
statusintegermessagestringdataobjectrows_queuedintegerNumber of rows queued for execution against the targeted action. 0 when the sheet has no rows (and row_ids was empty / omitted).
Examples
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"
]
}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
}Pass an empty `row_ids` array to queue every row currently on the sheet against the targeted action.
{
"row_ids": []
}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,
"filters": [
{
"variable": "string",
"operator": "string",
"values": [
"string"
]
}
],
"status_filters": [
{
"action_instance_id": "string",
"is": [
"string"
],
"is_not": [
"string"
]
}
],
"created_at": {
"operator": "string",
"values": [
"string"
]
}
}'{
"status": 200,
"message": "Rows queued for execution",
"data": {
"rows_queued": 3
}
}Delete Rows
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows/deletePermanently 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idsarrayrequiredRow UUIDs to delete. From Add Rows (row_ids response) or List Rows. Max 200 per call.
Response
statusintegerdataobjectdeleted_countintegerNumber of rows actually deleted. Equals deleted_row_ids.length.
deleted_row_idsarrayUUIDs that were successfully deleted, in request order (skipping any rejected).
rejectedarrayRow UUIDs the server couldn't delete. Rows that WERE deleted in the same batch are still gone — this list contains only the failures.
row_idstringThe UUID you sent that couldn't be deleted.
errorstringHuman-readable reason (e.g. Row not found on this sheet).
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"
]
}'{
"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
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows/delete-allPermanently 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_idrequiredWorkflow ID.
sheet_idrequiredSheet 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
statusintegerdataobjectdeleted_countintegerNumber of rows deleted. 0 if the sheet was already empty.
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"{
"status": 200,
"data": {
"deleted_count": 157
}
}Data
4 endpointsList Rows
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows/listReturns 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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idsarrayoptionalFilter 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_nointegeroptionalPage number (1-indexed). Only meaningful when browsing (no row_ids) or when row_ids count exceeds page_size.
page_sizeintegeroptionalRows per page. Defaults to 20. Maximum 200.
filtersarrayoptionalBrowse-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_filtersarrayoptionalBrowse-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_atobjectoptionalBrowse-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
statusintegerdataobjectrowsarrayRows on this page, in insertion order.
row_iduuidUUID assigned by Add Rows.
row_statusstringRow-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-timeUTC timestamp (ISO 8601) when the row was added to the sheet via Add Rows.
inputsobjectInput column values as the row was created. Keys match the sheet's input field names.
cellsobjectOne 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_countintegerTotal rows on the sheet across all pages.
page_nointegerpage_sizeintegercurl -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
}'{
"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
}
}Get Cell Outputs
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/rows/{row_id}/cells/{action_instance_id}/outputsReturns 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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet 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_idrequiredRow ID, as returned by List Rows (`row_id`).
action_instance_idrequiredAction instance ID of the cell — the key it appears under in a row's `cells` map.
Response
statusintegerdataobjectoutputsobjectThe cell's outputs. Keys are the action's output field names.
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"{
"status": 200,
"data": {
"outputs": {
"full_name": "Ada Lovelace",
"headline": "Founder at Analytical Engines",
"experience": [
{
"company": "Analytical Engines",
"title": "Founder"
}
]
}
}
}Export Sheet
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/exportStarts a CSV export of the sheet's rows and returns immediately with an export_id. The file is built in the background — poll Get Export until status is completed, then download file_url.
Choosing what to export. Send no body (or {}) to export the whole sheet. Name rows explicitly with row_ids, or narrow the sheet with filters / status_filters / created_at — the same shapes List Rows accepts, with the same operators, so a filter that selects a row there selects it here too.
row_ids and the filters cannot be combined — doing so returns 400. An explicit row selection takes precedence in the export engine, so a request carrying both would quietly ignore what you filtered on and hand back a file that doesn't match what you asked for. exclude_row_ids subtracts from the filtered set and is ignored when row_ids is given.
The output is always CSV — UTF-8 with a byte-order mark, one column per input field and action output, one row per sheet row. Cells that have not produced a value (queued, running, failed) are exported blank.
One export at a time. While an export of this sheet is building for you, starting another hands back the in-flight job with created: false rather than queueing a second build — so a retry is safe. Exports are private to the API key's creator: a key created by a different member of your organization cannot read them.
Zero rows is a 400, not an empty file — an export that matches nothing is nearly always a mistake in the selection, and it is cheaper to hear about it here than to poll a job to find out.
Requires the workflows:read scope.
Requires API key. Authentication ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet ID. To export the workflow's main sheet, pass the workflow's own ID — the main sheet's ID equals the workflow ID.
Request body
row_idsarrayoptionalExport exactly these rows. Omit to export every row matching the filters, or the whole sheet when no filters are given. Mutually exclusive with filters / status_filters / created_at — combining them returns 400.
exclude_row_idsarrayoptionalRows to leave out. Applied after the filters. Ignored when row_ids is given, since that is already an explicit list.
filtersarrayoptionalValue filters on sheet input columns. Identical shape and operators to List Rows' filters — each entry is { variable, operator, values }, where variable is an {{input.<column>}} reference token. See List Rows for the operator list.
status_filtersarrayoptionalPer-action cell-status filters. Identical shape to List Rows' status_filters — each entry is { action_instance_id, is? , is_not? } with exactly one of is / is_not, using the public status vocabulary (queued | running | complete | failed | error | out_of_credits | condition_not_met).
created_atobjectoptionalFilter rows by creation timestamp. Identical shape to List Rows' created_at — { operator, values }, where values is [ISO timestamp] or [start, end] for is between.
file_namestringoptionalBase name for the downloaded file, without extension. Defaults to export. Non-alphanumeric characters are replaced and the name is capped at 100 characters.
curl -X POST "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/export"
-H "Authorization: Bearer floq_YOUR_API_KEY"
-H "Content-Type: application/json"
-d '{
"row_ids": [
"00000000-0000-0000-0000-000000000000"
],
"exclude_row_ids": [
"00000000-0000-0000-0000-000000000000"
],
"filters": [
{
"variable": "string",
"operator": "string",
"values": [
"string"
]
}
],
"status_filters": [
{
"action_instance_id": "string",
"is": [
"string"
],
"is_not": [
"string"
]
}
],
"created_at": {
"operator": "string",
"values": [
"string"
]
},
"file_name": "string"
}'Get Export
/api/v1/workflows/{workflow_id}/sheets/{sheet_id}/export/{export_id}Reports the progress of an export started by Export Sheet and, once it is finished, hands back the download link.
status is running (queued or building), completed, or failed. Poll every few seconds — there is no webhook. On failed, error says why.
file_url appears only when status is completed. It is a signed link valid for 15 minutes, minted fresh on every poll — if it expires, poll again for another. Treat it as a secret: it downloads the file without an API key for as long as it is valid, so do not log it or store it anywhere durable.
An export is readable for 24 hours, after which it is forgotten and this returns 404. Exports are private to the API key's creator, and an export_id belonging to a different sheet is not readable through that sheet's path — both answer 404.
Requires the workflows:read scope.
Requires API key. Authentication ↑
Path parameters
workflow_idrequiredWorkflow ID.
sheet_idrequiredSheet ID — the same one passed to Export Sheet.
export_idrequiredThe `export_id` returned by Export Sheet.
Response
statusintegerdataobjectexport_idstringID of this export.
statusstringrunning — queued or building. completed — file_url is ready. failed — see error.
progressintegerPercent complete, 0-100. Reaches 100 only when status is completed.
row_countintegerRows the export contains.
file_urlstringTime-limited download link, present only when status is completed. Valid for 15 minutes and re-minted on every poll. Signed — treat it as a secret.
expires_atdate-timeWhen file_url stops working (UTC, ISO 8601). Present only alongside file_url.
errorstringWhy the export failed. Present only when status is failed.
started_atdate-timeWhen the job was accepted (UTC, ISO 8601).
completed_atdate-timeWhen the job reached a terminal state. null while still running.
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id/sheets/:sheet_id/export/:export_id"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"data": {
"export_id": "8f2c1d90-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"status": "completed",
"progress": 100,
"row_count": 15230,
"file_url": "https://s3.amazonaws.com/floqer-workflow-export/exports/org_123/8f2c1d90/qualified-leads.csv?X-Amz-Signature=...",
"expires_at": "2026-09-14T14:05:00.000Z",
"started_at": "2026-09-14T13:50:00.000Z",
"completed_at": "2026-09-14T13:52:11.000Z"
}
}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
/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
namestringrequiredWorkflow name. Not required to be unique in the organization.
Response
statusintegerdataobjectworkflow_iduuidUUID of the newly created workflow. Use this for {workflow_id} (and {sheet_id} for main-sheet operations) across every other endpoint.
namestringName as set in the request.
created_atdate-timeUTC timestamp (ISO 8601).
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"
}'{
"status": 201,
"data": {
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"name": "Lead Enrichment Pipeline",
"created_at": "2026-04-20T10:30:00Z"
}
}List Workflows
/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 ↑
Response
statusintegerdataarrayworkflow_iduuidUUID of the workflow. Use this with every endpoint that takes {workflow_id}.
namestringWorkflow name as set at creation.
created_atdate-timeUTC timestamp (ISO 8601) when the workflow was created.
updated_atdate-timeUTC timestamp (ISO 8601) of the most recent configuration change (name, inputs, actions, sheet settings).
last_run_atdate-timeUTC 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.
curl -X GET "https://api.floqer.com/api/v1/workflows/"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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
}
]
}Get Workflow Overview
/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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
Response
statusintegerdataobjectworkflow_iduuidUUID of the workflow.
namestringWorkflow name as set at creation.
created_atdate-timeUTC timestamp (ISO 8601) when the workflow was created.
updated_atdate-timeUTC timestamp (ISO 8601) of the most recent configuration change.
last_run_atnull | stringUTC timestamp (ISO 8601) of the most recent run across every sheet in this workflow. null if no sheet has ever been run.
is_ownerbooleantrue when the caller is the workflow owner. shared_users is populated only when this is true.
shared_usersarrayEmails the workflow is shared with. Populated only when is_owner is true; otherwise [].
sheetsarrayEvery sheet in the workflow with settings and webhook URL. sheets[0] is the main sheet.
sheet_iduuidUUID of the sheet. Pass as {sheet_id} on sheet-scoped endpoints and as target_sheet_id in send_to_sheet.
namestringSheet name as set at creation.
is_main_sheetbooleantrue for the main sheet — auto-created with the workflow and has sheet_id === data.workflow_id.
auto_runbooleanWhen true, the sheet's action chain runs automatically whenever new rows arrive.
cache_enabledbooleanWhen true, identical input rows reuse prior outputs instead of re-running the chain.
cache_sincenull | stringEarliest date (YYYY-MM-DD, UTC) from which cached runs are considered fresh. null means all cached runs count.
webhook_urlnull | stringPOST URL for pushing rows into this sheet via webhook. null when no webhook input is configured.
curl -X GET "https://api.floqer.com/api/v1/workflows/:workflow_id"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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
}
]
}
}Rename Workflow
/api/v1/workflows/{workflow_id}/nameSets 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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
Response
statusintegerdatacurl -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"
}'{
"status": 200,
"data": {
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"name": "Inbound lead enrichment"
}
}Duplicate Workflow
/api/v1/workflows/{workflow_id}/duplicateDeep-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 ↑
Path parameters
workflow_idrequiredID of the workflow to duplicate.
Response
statusintegerdatacurl -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"
}'{
"status": 201,
"data": {
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"name": "Lead Enrichment Pipeline Copy"
}
}Delete Workflow
/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 ↑
Path parameters
workflow_idrequiredWorkflow ID.
Response
statusintegerdataobjectworkflow_iduuidUUID of the workflow that was deleted.
deletedbooleanAlways true on a 200 response. Included so agents can branch on a single field.
curl -X DELETE "https://api.floqer.com/api/v1/workflows/:workflow_id"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"data": {
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"deleted": true
}
}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
values 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 thesource_instance_idto 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
3 endpointsBuilding 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.
Preview Source
/api/v1/sources/{source_id}/previewReturns 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 — 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 ↑
Path parameters
source_idrequiredSource 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
limitintegeroptionalOptional cap on rows returned. Response-shaping only; metadata.total_results is unchanged and metadata.capped becomes true when rows are dropped.
Response
statusintegerdataobjectdataarrayPreview rows (max 100). Each row's keys depend on the source's payload.
metadataobjecttotal_resultsintegerTotal matching records upstream — may exceed the 100 rows returned in data.
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
}'{
"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
}
}
}Create Source
/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 ↑
Path parameters
source_idrequiredSource 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`. Not to be confused with the UUID returned in the response. Browse them in [`/docs/source-catalog.txt`](/docs/source-catalog.txt); see `/docs/source-detail/{source_id}.txt`.
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
statusintegerdataobjectsource_instance_iduuidUUID 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.
namestringDisplay name echoed from the request (trimmed).
created_atdate-timeUTC timestamp (ISO 8601) of source creation.
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 '{}'{
"status": 201,
"data": {
"source_instance_id": "c7e3a1b2-9f4d-4a8c-b1e6-2d5f6a7b8c90",
"name": "Customer accounts",
"created_at": "2026-05-25T09:12:00Z"
}
}Get Source Field Options
/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 values back in the body.
Which field_names 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 ↑
Path parameters
source_idrequiredSource 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_namerequiredSnake-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
contextobjectoptionalEarlier 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
statusintegerdataobjectoptionsarrayOption values the resolver returned.
valuestringThe value to send back via the source's Preview / Create payload.
labelstringHuman-readable label for the option.
extrasobjectPer-integration metadata, when present. Shape varies by integration — use it for richer rendering when needed.
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": {}
}'{
"status": 200,
"data": {
"options": [
{
"value": "123",
"label": "All accounts"
},
{
"value": "456",
"label": "Signups — last 30 days"
}
]
}
}Manage
4 endpointsOnce 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
/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 ↑
Response
statusintegerdataarrayThe caller's sources, newest first.
source_instance_idstringSource 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).
namestringDisplay name given at creation.
source_idstringPublic source-type slug (e.g. import_from_hubspot, find_companies) — the identifier used by Preview / Create / Get Source Field Options.
statusstringSource status: active (live, re-importing), completed (one-time import, won't refresh), paused, paused_out_of_credits, expired, or deleted.
lead_countintegerRows imported so far.
created_atdate-timeUTC creation timestamp (ISO 8601).
expiration_datestringWhen an active source stops firing. null for one-time sources.
curl -X GET "https://api.floqer.com/api/v1/sources/"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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
}
]
}Get Source Data
/api/v1/sources/{source_instance_id}/dataReturns 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 ↑
Path parameters
source_instance_idrequiredSource instance UUID — the `source_instance_id` returned by **Create Source**.
Query parameters
page_nointeger1-indexed page number. Defaults to 1.
page_sizeintegerRows per page. Defaults to 20; capped at 200.
Response
statusintegerdataobjectrowsarrayImported rows for this page. Field names depend on the source type.
total_countintegerTotal number of rows stored for this source.
page_nointeger1-indexed page number returned.
page_sizeintegerPage size used for this response.
curl -X GET "https://api.floqer.com/api/v1/sources/:source_instance_id/data"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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
}
}Sync Source to Workflow
/api/v1/sources/{source_instance_id}/syncConnects 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.
push_existing (default true) toggles the backfill — set it false to connect the source without importing its current rows.
Whether landed rows run is the sheet's auto_run toggle, not a sync option — it governs backfilled and future rows alike. To load without running, set auto_run: false with Update Sheet (PATCH /api/v1/workflows/{workflow_id}/sheets/{sheet_id}) before syncing, then run chosen rows with Run Rows. Leave it on to auto-run every row that arrives.
run is deprecated. It is accepted, ignored, and reported in warnings[]; it will be rejected in a future release.
Requires API key. Authentication ↑
Path parameters
source_instance_idrequiredSource instance UUID — the `source_instance_id` returned by **Create Source**.
Request body
workflow_iduuidrequiredUUID of the destination workflow.
field_mappingobjectrequiredMap 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_existingbooleanoptionalWhether to backfill the workflow with the source's current rows. Defaults to true; set false to connect without importing existing rows.
runstringoptionalDeprecated and ignored (reported in warnings[]). Whether landed rows execute is the sheet's auto_run toggle — set it with Update Sheet before syncing. Will be rejected in a future release.
Response
statusintegerwarningsarrayNon-blocking issues, e.g. a deprecated body field that was ignored (code: "deprecated_field").
fieldstringcodestringmessagestringdataobjectsource_instance_iduuidThe connected source's instance UUID.
workflow_iduuidThe destination workflow's UUID.
push_existingbooleanWhether existing rows were queued for backfill.
fields_mappedintegerNumber of workflow inputs mapped.
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
}'{
"status": 201,
"warnings": [],
"data": {
"source_instance_id": "c7e3a1b2-9f4d-4a8c-b1e6-2d5f6a7b8c90",
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"push_existing": true,
"fields_mapped": 3
}
}Pause or Resume Source
/api/v1/sources/{source_instance_id}/statusPauses 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 ↑
Path parameters
source_instance_idrequiredSource instance UUID — the `source_instance_id` returned by **Create Source**.
Request body
statusstringrequiredpaused stops recurring runs; active resumes them.
Response
statusintegerdataobjectsource_instance_iduuidThe source instance UUID.
statusstringThe source's new public status.
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"
}'{
"status": 200,
"data": {
"source_instance_id": "b7e2c1a0-4d8f-4a21-9c33-5e6f7a8b9c01",
"status": "paused"
}
}ackDB Syncs
A sync keeps an ackDB 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), 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
6 endpointsSync ackDB Segment to Floq
/api/v1/ackdb/segments/{segment_id}/syncCreate 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 ↑
Path parameters
segment_idrequiredackDB segment id. The `id` returned by `GET /api/v1/ackdb/observe/segments`.
Request body
workflow_iduuidrequiredUUID of the destination Floq workflow. The workflow must be published.
field_mappingobjectrequiredMap 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_existingbooleanoptionalDeliver the members already in the segment. Defaults to false, so only members that join after creation are delivered.
runstringoptionalWhether delivered rows execute the workflow (all) or are inserted without running (none, the default).
reentry_modestringoptionalWhat 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
statusintegerdataobjectsync_idstringconnection_idstringsegment_idstringworkflow_iduuidentity_kindstringstatestringreused_syncbooleancurl -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"
}'{
"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
}
}Get ackDB Sync
/api/v1/ackdb/segments/{segment_id}/syncRead 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 ↑
Path parameters
segment_idrequiredackDB segment id. The `id` returned by `GET /api/v1/ackdb/observe/segments`.
Response
statusintegerdatacurl -X GET "https://api.floqer.com/api/v1/ackdb/segments/:segment_id/sync"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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
}
}
]
}
}List ackDB Syncs
/api/v1/ackdb/syncsList 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 ↑
Response
statusintegerdataarraycurl -X GET "https://api.floqer.com/api/v1/ackdb/syncs"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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
}
}
]
}
]
}Confirm Held ackDB Sync Members
/api/v1/ackdb/segments/{segment_id}/sync/confirmResolve 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 ↑
Path parameters
segment_idrequiredackDB segment id. The `id` returned by `GET /api/v1/ackdb/observe/segments`.
Request body
actionstringrequiredpush releases the held members for delivery; skip records them as skipped and never delivers them.
Response
statusintegerdataobjectsync_idstringactionstringaffectedintegercurl -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"
}'{
"status": 200,
"data": {
"sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",
"action": "push",
"affected": 238
}
}Resume ackDB Sync
/api/v1/ackdb/segments/{segment_id}/sync/resumeResume 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 ↑
Path parameters
segment_idrequiredackDB segment id. The `id` returned by `GET /api/v1/ackdb/observe/segments`.
Response
statusintegerdataobjectsync_idstringstatestringcurl -X POST "https://api.floqer.com/api/v1/ackdb/segments/:segment_id/sync/resume"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"data": {
"sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",
"state": "active"
}
}Delete ackDB Sync
/api/v1/ackdb/segments/{segment_id}/syncStop 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 ↑
Path parameters
segment_idrequiredackDB segment id. The `id` returned by `GET /api/v1/ackdb/observe/segments`.
Response
statusintegerdataobjectsync_idstringconnections_archivedintegercurl -X DELETE "https://api.floqer.com/api/v1/ackdb/segments/:segment_id/sync"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"data": {
"sync_id": "8f3b6c21-4d55-4c2f-9a77-1b0e2d6c4a19",
"connections_archived": 2
}
}Destinations
3 endpointsUpdate ackDB Sync Destination
/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 ↑
Path parameters
connection_idrequiredSync destination id. The `connection_id` returned by **Sync ackDB Segment to Floq** or **Get ackDB Sync**.
Request body
statusstringoptionalPause this destination. Resuming uses Resume ackDB Sync Destination, because it needs a backlog choice.
reentry_modestringoptionalChange what happens when a member re-enters the segment.
field_mappingobjectoptionalComplete 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
statusintegerdataobjectconnection_idstringstatusnull | stringreentry_modenull | stringremappedbooleancurl -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"
}
}'{
"status": 200,
"data": {
"connection_id": "4821",
"status": "paused",
"reentry_mode": null,
"remapped": true
}
}Resume ackDB Sync Destination
/api/v1/ackdb/sync/connections/{connection_id}/resumeResume 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 ↑
Path parameters
connection_idrequiredSync destination id. The `connection_id` returned by **Sync ackDB Segment to Floq** or **Get ackDB Sync**.
Request body
backlogstringrequiredWhat to do with members that accumulated while the destination was paused: deliver queues them, skip records them as skipped.
Response
statusintegerdataobjectconnection_idstringbacklogstringaffectedintegercurl -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"
}'{
"status": 200,
"data": {
"connection_id": "4821",
"backlog": "deliver",
"affected": 54
}
}Delete ackDB Sync Destination
/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 ↑
Path parameters
connection_idrequiredSync destination id. The `connection_id` returned by **Sync ackDB Segment to Floq** or **Get ackDB Sync**.
Response
statusintegerdataobjectconnection_idstringcurl -X DELETE "https://api.floqer.com/api/v1/ackdb/sync/connections/:connection_id"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"data": {
"connection_id": "4821"
}
}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. Create one from a workflow with Create Shortcut, or in the Floqer UI; the other 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.
Packaging a workflow. One call: Create Shortcut takes outputs (the fields a run returns, each pointed at an upstream value with the same {{...}} syntax Configure Action uses) and reads input_schema from the input columns of the workflow. It writes the output_shortcut action onto the workflow itself, so there is no action to add first. A shortcut is frozen at publish: editing the workflow afterwards changes nothing until Update Shortcut publishes a new version. Deleting the workflow archives its shortcut.
Scopes: shortcuts:write to create shortcuts; shortcuts:read to discover them and read runs; shortcuts:run to start them.
Create Shortcut
/api/v1/shortcuts/Packages a workflow as a shortcut and publishes its first version, in one call. You say what it returns; what it takes is read from the workflow:
- input_schema — the workflow's input columns (List Inputs). Every column becomes an input; its
referenceis the snake_cased column name. Nothing to send. - output_schema — the
outputsyou send, in order. Each item names one field a run returns and points it at a value: a{{input.<field>}}or{{<action_instance_id>.<field>}}reference, a literal, or a mix — the same syntax Configure Action takes. Create Shortcut writes them onto the workflow'soutput_shortcutaction itself, adding one to the end of the chain when there is none, so do not add or configure that action first.
A value that references a field nothing produces is kept and reported in warnings on the 201 — it will never resolve at run time.
Requirements (a 400 with code SHORTCUT_WORKFLOW_INELIGIBLE lists every unmet one in reasons): the workflow has at least one named input column. You must be able to edit the workflow (403 otherwise). A workflow that already has one output_shortcut action per branch cannot be packaged here (400); use the Floqer app. A refused call changes nothing on the workflow.
One shortcut per workflow: a second call returns 409 SHORTCUT_ALREADY_EXISTS. The created shortcut is published and runnable immediately — pass its id to Run Shortcut.
Requires the shortcuts:write scope.
Requires API key. Authentication ↑
Request body
workflow_iduuidrequiredUUID of the workflow to package. It must belong to your organization and have at least one named input column.
namestringrequiredShortcut display name.
descriptionstringoptionalWhat the shortcut does, shown in the Gallery and returned by Get Shortcut.
icon_emojistringoptionalEmoji used as the shortcut icon. Defaults to 🚀.
outputsarrayrequiredThe fields a run returns, in order. Each item names one output field and points it at a value. Written onto the workflow's output_shortcut action (added to the end of the chain when there is none) and returned as output_schema.
Response
statusintegermessagestringwarningsarrayNon-fatal issues with outputs — an unresolved {{...}} reference or an item with neither a name nor a value. Same codes as Configure Action. Absent when every item applied cleanly.
fieldstringcodestringmessagestringdataobjectThe new 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.
iduuidUnique shortcut identifier (UUID).
workflow_iduuidUUID of the workflow this shortcut packages.
parent_app_idstringAlways null for a shortcut you created.
namestringShortcut display name.
descriptionstringHuman-readable description of what the shortcut does.
creator_emailemailEmail of the API key's user.
org_idstringOrganization ID the shortcut belongs to.
input_schemaarrayDerived from the workflow's input columns. Each field's reference is the key to use in input_data when running the shortcut.
referencestringSnake_case identifier for this input field (derived from name) — use it as the key in input_data.
namestringHuman-readable field name.
descriptionstringHelp text describing what value to provide for this field.
typestringField type: text, number, email, url, etc.
requiredbooleanWhether this field must be provided when running the shortcut.
defaultValueDefault value used when the field is not provided.
output_schemaarrayThe outputs you sent, in order. Each field's name is the key under which its value appears in a run's output_data.
namestringHuman-readable name for this output field — the key for its value in a run's output_data.
descriptionstringDescription of what this output contains.
display_columnstringColumn header label shown in results tables.
is_masterbooleanAlways true for a shortcut you created.
is_publishedbooleanAlways true — the shortcut is runnable immediately.
is_archivedbooleanWhether the shortcut has been archived.
cover_imagestringURL of the shortcut's cover image.
icon_emojistringEmoji used as the shortcut icon.
created_atdate-timeWhen the shortcut was created.
updated_atdate-timeWhen the shortcut was last updated.
curl -X POST "https://api.floqer.com/api/v1/shortcuts/"
-H "Authorization: Bearer floq_YOUR_API_KEY"
-H "Content-Type: application/json"
-d '{
"workflow_id": "531952c2-3d07-4be2-8c4b-733acba3187b",
"name": "Company Enrichment",
"description": "Enriches company data from a LinkedIn URL",
"icon_emoji": "🏢",
"outputs": [
{
"name": "Company Website",
"value": "{{enrich_company_linkedin_profile_1.company_website}}"
},
{
"name": "Employee Count",
"value": "{{enrich_company_linkedin_profile_1.employee_count}}"
}
]
}'{
"status": 201,
"message": "Shortcut created successfully",
"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": "",
"type": "text",
"required": false
}
],
"output_schema": [
{
"name": "Company Website",
"description": "",
"display_column": "Company Website"
},
{
"name": "Employee Count",
"description": "",
"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-10T09:00:00Z"
}
}List Shortcuts
/api/v1/shortcuts/Returns every shortcut the caller can access — the same set the Floqer app shows: shortcuts they created (master), and shortcuts shared with them or published to the whole organization (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.
Use search to find a shortcut by name. Shortcuts with callable: true can also be called from inside a workflow: add the call_shortcut action and set its shortcut input to the shortcut's id.
Requires the shortcuts:read scope.
Requires API key. Authentication ↑
Query parameters
filterstringFilter by ownership: `master` (shortcuts you created), `shared` (shortcuts shared with you or published to your whole organization), or `all` (both). Defaults to `all`.
searchstringCase-insensitive text to match anywhere in the shortcut name, e.g. `enrich`.
Response
statusintegerdataarrayShortcuts accessible to the caller. Each shortcut wraps a workflow with typed input/output schemas.
iduuidUnique shortcut identifier (UUID).
workflow_iduuidUUID of the underlying workflow that powers this shortcut.
parent_app_idstringIf this is a shared copy, the ID of the original master shortcut. null for master shortcuts.
namestringShortcut display name.
descriptionstringHuman-readable description of what the shortcut does.
creator_emailemailEmail of the user who created the shortcut.
org_idstringOrganization ID the shortcut belongs to.
input_schemaarrayOrdered list of input fields the shortcut takes. Each field's reference is the key to use in input_data when running the shortcut.
referencestringSnake_case identifier for this input field (derived from name) — use it as the key in input_data when running the shortcut.
namestringHuman-readable field name displayed in the shortcut UI.
descriptionstringHelp text describing what value to provide for this field.
typestringField type: text, number, email, url, etc.
requiredbooleanWhether this field must be provided when running the shortcut.
defaultValueDefault value used when the field is not provided.
output_schemaarrayOrdered 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.
namestringHuman-readable name for this output field — the key for its value in a run's output_data.
descriptionstringDescription of what this output contains.
display_columnstringColumn header label shown in results tables.
is_masterbooleantrue if this is the original shortcut (not a shared copy).
is_publishedbooleanWhether the shortcut is published and available for runs. Only published shortcuts can be run.
is_archivedbooleanWhether the shortcut has been archived.
callablebooleanWhether a workflow can call this shortcut through the call_shortcut action. False until the shortcut has a published version; republish it in the Floqer app.
cover_imagestringURL of the shortcut's cover image.
icon_emojistringEmoji used as the shortcut icon.
created_atdate-timeWhen the shortcut was created.
updated_atdate-timeWhen the shortcut was last updated.
curl -X GET "https://api.floqer.com/api/v1/shortcuts/"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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,
"callable": true,
"cover_image": null,
"icon_emoji": "🏢",
"created_at": "2025-01-10T09:00:00Z",
"updated_at": "2025-01-14T16:20:00Z"
}
]
}Get Shortcut
/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'sreference(snake_case, e.g.linkedin_url) is the key to use ininput_datawhen calling Run Shortcut. CheckrequiredanddefaultValueto know what you must send.output_schema— the fields the shortcut produces. Each field'sname(e.g.Company Website) is the key under which its value appears in a run'soutput_data.is_published— only published shortcuts can be run; running an unpublished shortcut returns 403.
Requires the shortcuts:read scope.
Requires API key. Authentication ↑
Path parameters
idrequiredShortcut ID — from **List Shortcuts**.
Response
statusintegerdataobjectThe 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.
iduuidUnique shortcut identifier (UUID).
workflow_iduuidUUID of the underlying workflow that powers this shortcut.
parent_app_idstringIf this is a shared copy, the ID of the original master shortcut. null for master shortcuts.
namestringShortcut display name.
descriptionstringHuman-readable description of what the shortcut does.
creator_emailemailEmail of the user who created the shortcut.
org_idstringOrganization ID the shortcut belongs to.
input_schemaarrayOrdered list of input fields the shortcut takes. Each field's reference is the key to use in input_data when running the shortcut.
referencestringSnake_case identifier for this input field (derived from name) — use it as the key in input_data when running the shortcut.
namestringHuman-readable field name displayed in the shortcut UI.
descriptionstringHelp text describing what value to provide for this field.
typestringField type: text, number, email, url, etc.
requiredbooleanWhether this field must be provided when running the shortcut.
defaultValueDefault value used when the field is not provided.
output_schemaarrayOrdered 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.
namestringHuman-readable name for this output field — the key for its value in a run's output_data.
descriptionstringDescription of what this output contains.
display_columnstringColumn header label shown in results tables.
is_masterbooleantrue if this is the original shortcut (not a shared copy).
is_publishedbooleanWhether the shortcut is published and available for runs. Only published shortcuts can be run.
is_archivedbooleanWhether the shortcut has been archived.
callablebooleanWhether a workflow can call this shortcut through the call_shortcut action. False until the shortcut has a published version; republish it in the Floqer app.
cover_imagestringURL of the shortcut's cover image.
icon_emojistringEmoji used as the shortcut icon.
created_atdate-timeWhen the shortcut was created.
updated_atdate-timeWhen the shortcut was last updated.
curl -X GET "https://api.floqer.com/api/v1/shortcuts/:id"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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,
"callable": true,
"cover_image": null,
"icon_emoji": "🏢",
"created_at": "2025-01-10T09:00:00Z",
"updated_at": "2025-01-14T16:20:00Z"
}
}Update Shortcut
/api/v1/shortcuts/{id}Changes a shortcut you created. Send only the fields you want to change.
name,description,icon_emoji— metadata, applied immediately.outputs— the full list of fields a run returns, in the same shape Create Shortcut takes. It replaces the previous outputs, is written onto the workflow'soutput_shortcutaction, and publishes a new version of the shortcut. The version is re-derived from the workflow, so input columns added since the last publish are picked up at the same time; every existing input keeps its type, required flag and default. An output whosenameis unchanged keeps its identity. To pick up new inputs without changing outputs, send the current outputs again.
A refused call changes nothing — not the workflow, not the shortcut. Unknown body keys are rejected with 400.
A shortcut is frozen at publish. Runs execute the latest version's frozen graph and Get Shortcut / Get Shortcut Run read the stored schemas, so editing the workflow on its own (Configure Action, Add Inputs) never changes what the shortcut takes or returns. This is the call that does.
Removing an output that another workflow's Call Shortcut cell reads is refused with 409 SHORTCUT_CONTRACT_BREAK and the list of callers; send force: true to publish anyway and mark those call sites broken.
Runs already in flight finish on the version they started on.
Requires the shortcuts:write scope.
Requires API key. Authentication ↑
Path parameters
idrequiredShortcut ID — from **Create Shortcut** or **List Shortcuts** (`filter=master`).
Request body
namestringoptionalNew display name.
descriptionstringoptionalNew description.
icon_emojistringoptionalNew icon emoji.
outputsarrayoptionalThe full replacement list of fields a run returns, in order. Written onto the workflow's output_shortcut action and published as a new version.
forcebooleanoptionalPublish even when the new outputs remove a field another workflow's Call Shortcut cell reads; those call sites are marked broken. Defaults to false, which returns 409 SHORTCUT_CONTRACT_BREAK with the callers instead.
Response
statusintegermessagestringwarningsarrayNon-fatal issues with outputs — an unresolved {{...}} reference or an item with neither a name nor a value. Absent when every item applied cleanly.
fieldstringcodestringmessagestringdataobjectThe updated shortcut — same shape as Get Shortcut.
curl -X PATCH "https://api.floqer.com/api/v1/shortcuts/:id"
-H "Authorization: Bearer floq_YOUR_API_KEY"
-H "Content-Type: application/json"
-d '{
"name": "Company Enrichment v2",
"description": "string",
"icon_emoji": "🏢",
"outputs": [
{
"name": "Company Website",
"value": "{{enrich_company_linkedin_profile_1.company_website}}"
}
],
"force": false
}'{
"status": 200,
"message": "Shortcut updated and republished",
"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": "",
"type": "url",
"required": false
}
],
"output_schema": [
{
"name": "Company Website",
"description": "",
"display_column": "Company Website"
},
{
"name": "Employee Count",
"description": "",
"display_column": "Employee Count"
},
{
"name": "Industry",
"description": "",
"display_column": "Industry"
}
],
"is_master": true,
"is_published": true,
"is_archived": false,
"cover_image": null,
"icon_emoji": "🏢",
"created_at": "2025-01-10T09:00:00Z",
"updated_at": "2025-02-02T11:30:00Z"
}
}Run Shortcut
/api/v1/shortcuts/{id}/runStarts 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 defaultValues, then to "".
The shortcut must be published — running an unpublished shortcut returns 403.
Requires the shortcuts:run scope.
Requires API key. Authentication ↑
Path parameters
idrequiredShortcut ID — from **List Shortcuts**.
Request body
input_dataobjectrequiredInput 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
statusintegermessagestringdataobjectiduuidThe run ID — poll Get Shortcut Run (GET .../runs/{run_id}) with it.
data_iduuidID of the data row created in the underlying workflow for this run.
statusstringInitial run status — starts as pending. Poll Get Shortcut Run until a terminal status (completed, failed, error, outOfCredits).
created_atdate-timeWhen the run was created.
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"
}
}'{
"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"
}
}Run Shortcut Batch
/api/v1/shortcuts/{id}/run/batchStarts 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 ↑
Path parameters
idrequiredShortcut ID — from **List Shortcuts**.
Request body
rowsarrayrequiredInput 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
statusintegermessagestringdataobjectbatch_iduuidID grouping every run created by this request — filter List Shortcut Runs by it.
run_countintegerNumber of runs created (one per row).
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"
}
]
}'{
"status": 201,
"message": "Shortcut batch run started",
"data": {
"batch_id": "3f2a1b6c-9d4e-4f7a-8b0c-5d6e7f8a9b0c",
"run_count": 25
}
}List Shortcut Runs
/api/v1/shortcuts/{id}/runsReturns 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 ↑
Path parameters
idrequiredShortcut ID — from **List Shortcuts**.
Query parameters
limitstringNumber of runs to return per page. Default 50, max 100.
offsetstringOffset for pagination. Default 0.
modestringFilter by run mode: `single` (runs from **Run Shortcut**) or `batch` (runs from **Run Shortcut Batch**).
batch_idstringFilter to the runs created by one **Run Shortcut Batch** call — the `batch_id` it returned.
Response
statusintegerdataobjectrunsarrayRun records, newest first. Same shape as Get Shortcut Run's data.
iduuidUnique run identifier (UUID).
app_idstringID of the shortcut this run belongs to.
workflow_idstringID of the underlying workflow that was executed.
data_idstringID of the data row created in the workflow for this run.
user_emailstringEmail of the user who triggered the run.
org_idstringOrganization ID.
input_dataobjectInput values provided when the run was started, keyed by each field's reference from the shortcut's input_schema.
output_dataobjectOutput values keyed by each output field's name from the shortcut's output_schema. Each entry is { value, status }. null until the run completes.
statusstringCurrent execution status. Terminal: completed, failed, error, outOfCredits. In-progress values: pending, inProgress, payloadFilled, readyToBeReviewed, checkingNextSource.
error_messagestringError details if the run failed. null on success.
created_atdate-timeWhen the run was created.
completed_atdate-timeWhen the run finished (completed or failed). null while in progress.
paginationobjectlimitintegeroffsetintegertotalintegerTotal 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.
curl -X GET "https://api.floqer.com/api/v1/shortcuts/:id/runs"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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
}
}
}Get Shortcut Run
/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 ↑
Path parameters
idrequiredShortcut ID — from **List Shortcuts**.
runIdrequiredRun ID — the `id` returned by **Run Shortcut**, or from **List Shortcut Runs**.
Response
statusintegerdataobjectiduuidUnique run identifier (UUID).
app_idstringID of the shortcut this run belongs to.
workflow_idstringID of the underlying workflow that was executed.
data_idstringID of the data row created in the workflow for this run.
user_emailstringEmail of the user who triggered the run.
org_idstringOrganization ID.
input_dataobjectInput values provided when the run was started, keyed by each field's reference from the shortcut's input_schema.
output_dataobjectOutput 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.
statusstringCurrent execution status. Terminal: completed, failed, error, outOfCredits. In-progress values: pending, inProgress, payloadFilled, readyToBeReviewed, checkingNextSource. Recomputed live from the workflow engine for in-progress runs.
error_messagestringError details if the run failed. null on success.
created_atdate-timeWhen the run was created.
completed_atdate-timeWhen the run finished (completed or failed). null while in progress.
curl -X GET "https://api.floqer.com/api/v1/shortcuts/:id/runs/:runId"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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"
}
}Export Shortcut Runs
/api/v1/shortcuts/{id}/runs/exportStarts a CSV export of one batch's runs and returns immediately with an export_id. The file is built in the background — poll Get Shortcut Runs Export until status is completed, then download file_url.
batch_id is the id Run Shortcut Batch and Run Shortcut from CSV return, and the same one List Shortcut Runs filters by. You can only export a batch you ran — runs are private to the API key that created them.
Columns are one per input field, then one per output field, then status — the same public field names the run endpoints return. They are fixed when the export starts, so a batch whose later runs failed still produces a rectangular file, and editing the shortcut afterwards cannot reshape an export already under way. Each output cell holds the value from that field's {value, status} envelope, flattened; the per-field status is not a column, only the run's own status is.
Always CSV — there is no format option.
One export at a time. While an export of this shortcut is building for you, starting another returns the in-flight job with created: false rather than queueing a second build, so a retry is safe.
A batch with no runs is a 400, not an empty file.
Requires the shortcuts:read scope.
Requires API key. Authentication ↑
Path parameters
idrequiredShortcut ID.
Request body
batch_idstringrequiredThe batch to export, as returned by Run Shortcut Batch or Run Shortcut from CSV. The same id List Shortcut Runs filters by.
file_namestringoptionalBase name for the downloaded file, without extension. Defaults to shortcut-runs. Non-alphanumeric characters are replaced and the name is capped at 100 characters.
curl -X POST "https://api.floqer.com/api/v1/shortcuts/:id/runs/export"
-H "Authorization: Bearer floq_YOUR_API_KEY"
-H "Content-Type: application/json"
-d '{
"batch_id": "string",
"file_name": "string"
}'Get Shortcut Runs Export
/api/v1/shortcuts/{id}/runs/export/{export_id}Reports the progress of an export started by Export Shortcut Runs and, once it is finished, hands back the download link.
status is running (queued or building), completed, or failed. Poll every few seconds — there is no webhook. On failed, error says why.
file_url appears only when status is completed. It is a signed link valid for 15 minutes, minted fresh on each poll — re-poll to get another. Treat it as a secret: it downloads the file without an API key for as long as it is valid, so do not log it or store it anywhere durable.
An export is readable for 24 hours, after which this returns 404. An export_id belonging to a different shortcut, or to another member's key, also returns 404.
Requires the shortcuts:read scope.
Requires API key. Authentication ↑
Path parameters
idrequiredShortcut ID.
export_idrequiredThe `export_id` returned by Export Shortcut Runs.
Response
statusintegerdataobjectexport_idstringID of this export.
statusstringrunning — queued or building. completed — file_url is ready. failed — see error.
progressintegerPercent complete, 0-100. Reaches 100 only when status is completed.
row_countintegerRuns the export contains.
file_urlstringTime-limited download link, present only when status is completed. Valid for 15 minutes and re-minted on every poll. Signed — treat it as a secret.
expires_atdate-timeWhen file_url stops working (UTC, ISO 8601). Present only alongside file_url.
errorstringWhy the export failed. Present only when status is failed.
started_atdate-timeWhen the job was accepted (UTC, ISO 8601).
completed_atdate-timeWhen the job reached a terminal state. null while still running.
curl -X GET "https://api.floqer.com/api/v1/shortcuts/:id/runs/export/:export_id"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"data": {
"export_id": "2d7f4a10-9b3c-4e5d-8f01-223344556677",
"status": "completed",
"progress": 100,
"row_count": 240,
"file_url": "https://s3.amazonaws.com/floqer-workflow-export/exports/org_123/2d7f4a10/enriched-leads.csv?X-Amz-Signature=...",
"expires_at": "2026-09-15T09:27:00.000Z",
"started_at": "2026-09-15T09:10:00.000Z",
"completed_at": "2026-09-15T09:12:04.000Z"
}
}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
/api/v1/environment-variablesReturns 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 ↑
Query parameters
workflow_idstringRestrict 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
statusintegerdataarraykeystringCanonical identifier, unique within the organization and immutable once created. This is the segment used in the reference token.
namestringDisplay name shown in the Floqer app. Editable, so reference a variable by key, never by name.
descriptionstringFree-text description set by the org. null when unset.
scopestringWhich 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.
referencestringReady-to-paste reference token. Use it as, or inside, any string value in Configure Action's inputs.
value_countintegerHow 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_bystringEmail of the member who created the variable. null on older rows.
updated_atdate-timeUTC timestamp (ISO 8601) the definition was last changed. Setting a value does not change it.
curl -X GET "https://api.floqer.com/api/v1/environment-variables"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"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"
}
]
}Usage
What your organization has to spend, and what consumed it. Get Credit Balance returns the balance; Get Usage Overview returns spend for a window with the daily trend and the top consumers; List Workflow Usage and List Source Usage attribute that spend per workflow sheet and per source instance, and Get Workflow Usage drills into a single sheet.
Windows. Every breakdown takes from and to (ISO 8601 — from inclusive, to exclusive) and defaults to the current calendar month in UTC.
Runs and cache. One run is one action call against one row. A run served from cache counts toward runs and cache_hits but spends no credits — cache_savings reports what it would have cost, separately from credits_consumed.
Sheets, not workflows. A workflow's sheets are separate rows sharing a workflow_id; sum them for the workflow total. Ids follow the Sheet schema throughout — sheet_id is the row, workflow_id is the workflow it belongs to — so a usage row joins straight onto the workflow and sheet endpoints. Deleted workflows and sources still appear, with deleted: true — their runs really did spend credits in the window.
Freshness. The balance is read from the credit ledger itself; the breakdowns come from an analytics replica of it that lags by seconds, so a run that has just completed can move the balance a moment before it shows up in a breakdown.
What a key sees. Reads need billing:read, which every role holds. A key that also holds org:read (admin only) reports the whole organization; any other key reports only the usage of the member who created it. The credit balance is organization-wide either way.
Get Credit Balance
/api/v1/usage/creditsReturns your organization's spendable credit balance — what is left to spend right now, not what has been spent. For spend, use Get Usage Overview.
The balance is organization-wide and is the same for every key in the organization, including a key narrowed to its creator's own usage.
It is the organization's balance, not this caller's allowance. A member can also carry a personal credit quota, enforced when a run spends credits rather than here, so a run can still be refused while this reports credits available. Treat it as the ceiling.
It is read from the credit ledger itself, while every breakdown endpoint here reads an analytics replica of that ledger. The replica lags by seconds, so a run that has just completed can move this balance a moment before it appears in a breakdown.
Scope: billing:read — held by every role.
Requires API key. Authentication ↑
Response
statusintegerdatacurl -X GET "https://api.floqer.com/api/v1/usage/credits"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 200,
"data": {
"credits": 16134.5
}
}Get Usage Overview
/api/v1/usage/overviewReturns usage for the requested window: totals, a per-day trend, and the top workflows, actions and members by credits spent. Defaults to the current calendar month (UTC).
What a run is. One run is one action call against one row. A run served from cache is counted in total_runs and cache_hits but costs no credits; cache_savings reports what those runs would have cost, and is not part of credits_consumed.
top_workflows lists workflow sheets only. Source spend counts toward summary — the whole organization's usage — and is attributed by List Source Usage.
What the key can see. A key holding org:read (admin only) reports the whole organization and populates top_users and summary.active_users. Any other key reports only the usage of the member who created it, leaves top_users empty, and omits active_users.
Scope: billing:read — held by every role.
Requires API key. Authentication ↑
Query parameters
fromstringInclusive start of the window, ISO 8601. Defaults to the first day of the current UTC month.
tostringExclusive end of the window, ISO 8601. Defaults to now.
Response
statusintegerdatacurl -X GET "https://api.floqer.com/api/v1/usage/overview"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 0,
"data": {
"summary": {
"total_runs": 0,
"credits_consumed": 0,
"success_rate": 0,
"cache_savings": 0,
"avg_cost_per_run": 0,
"active_users": 0
},
"daily": [
{
"date": "string",
"runs": 0,
"credits": 0,
"cache_hits": 0
}
],
"top_workflows": [
{
"sheet_id": "string",
"name": "string",
"deleted": false,
"runs": 0,
"credits": 0
}
],
"top_actions": [
{
"action_name": "string",
"runs": 0,
"credits": 0,
"cache_hits": 0
}
],
"top_users": [
{
"email": "string",
"runs": 0,
"credits": 0
}
],
"top_error": null
}
}List Workflow Usage
/api/v1/usage/workflowsReturns credits spent per workflow sheet in the requested window, ranked by credits. Defaults to the current calendar month (UTC).
One row is one sheet, not one workflow. A workflow with three sheets produces three rows sharing a workflow_id; sum them for the workflow total. The ids match the Sheet schema — sheet_id is the row, workflow_id is the workflow it belongs to — so either goes straight to the workflow and sheet endpoints. is_main_sheet marks the workflow's own main sheet, whose sheet_id equals its workflow_id.
Capped at the 100 highest-spending sheets, and not paginated — narrow the window to see further down.
Source runs are reported by List Source Usage, not here, so the two never double-count. Deleted workflows still appear with deleted: true: their runs really did spend credits in the window, so dropping them would under-report the total.
What the key can see. A key holding org:read (admin only) reports the whole organization. Any other key reports only the usage of the member who created it, so a sheet a colleague ran is absent.
Scope: billing:read — held by every role.
Requires API key. Authentication ↑
Query parameters
fromstringInclusive start of the window, ISO 8601. Defaults to the first day of the current UTC month.
tostringExclusive end of the window, ISO 8601. Defaults to now.
Response
statusintegerdataarrayOne row per sheet with activity in the window, highest spend first.
curl -X GET "https://api.floqer.com/api/v1/usage/workflows"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 0,
"data": [
{
"sheet_id": "string",
"name": "string",
"deleted": false,
"workflow_id": "string",
"workflow_name": "string",
"sheet_name": "string",
"is_main_sheet": false,
"runs": 0,
"credits": 0,
"success_count": 0,
"cache_hits": 0,
"cache_hit_rate": 0,
"top_actions": [
{
"action_name": null,
"runs": null,
"credits": null,
"cache_hits": null
}
]
}
]
}Get Workflow Usage
/api/v1/usage/workflows/{sheet_id}Returns one sheet's usage in the requested window: totals, a per-day trend, the actions that spent the credits, the most frequent errors, the 20 most recent runs, and per-member spend. Defaults to the current calendar month (UTC).
sheet_id is one sheet's id, as returned by List Workflow Usage — the same id every sheet-scoped workflow endpoint takes. Passing a workflow's main-sheet id reports that sheet alone, not the workflow's other sheets; for a workflow total, sum its rows in List Workflow Usage. An id that is not a UUID is a 400, so a 404 always means the window holds no usage for that sheet.
A key restricted by allowed_workflow_ids may only drill into sheets of the workflows on its list, and is refused with a 403 otherwise. The list endpoints are not filtered that way — a filtered list would not add up to its own totals.
A 404 means "no usage", not "no such sheet". The answer comes from usage history, which holds nothing about a sheet that never ran.
What the key can see. A key holding org:read (admin only) reports every member's runs and populates per_user. Any other key reports only the runs of the member who created it and leaves per_user empty — so a sheet only a colleague ran returns 404 for that key, even though the sheet exists.
Scope: billing:read — held by every role.
Requires API key. Authentication ↑
Path parameters
sheet_idrequiredSheet ID, as returned by List Workflow Usage.
Query parameters
fromstringInclusive start of the window, ISO 8601. Defaults to the first day of the current UTC month.
tostringExclusive end of the window, ISO 8601. Defaults to now.
Response
statusintegerdatacurl -X GET "https://api.floqer.com/api/v1/usage/workflows/:sheet_id"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 0,
"data": {
"summary": {
"sheet_id": "string",
"name": "string",
"deleted": false,
"total_runs": 0,
"credits_consumed": 0,
"success_rate": 0,
"cache_hit_rate": 0
},
"daily": [
{
"date": "string",
"runs": 0,
"credits": 0,
"cache_hits": 0
}
],
"top_actions": [
{
"action_name": "string",
"runs": 0,
"credits": 0,
"cache_hits": 0
}
],
"top_errors": [
{
"message": "string",
"count": 0,
"last_seen": "string"
}
],
"recent_runs": [
{
"run_id": "string",
"created_at": "string",
"action_name": null,
"email": "string",
"credits": 0,
"status": null,
"fulfillment_source": "string"
}
],
"per_user": [
{
"email": "string",
"runs": 0,
"credits": 0
}
]
}
}List Source Usage
/api/v1/usage/sourcesReturns credits spent per source instance in the requested window, ranked by credits. Defaults to the current calendar month (UTC).
source_instance_id is the same id List Sources returns under that name — the source instance, not the source_id source-type slug. Pass it to Get Source Data to see what the spend produced.
Source runs are reported here and excluded from List Workflow Usage, so the two never double-count. Deleted sources still appear with deleted: true.
The 100-row cap is shared with List Workflow Usage. The query takes the 100 highest spenders across sheets and sources together and splits them afterwards, so an organization whose biggest spenders are all sheets can see fewer than 100 sources here — or an empty list with a 200. Narrow the window to bring sources into the top 100.
What the key can see. A key holding org:read (admin only) reports the whole organization. Any other key reports only the usage of the member who created it.
Scope: billing:read — held by every role.
Requires API key. Authentication ↑
Query parameters
fromstringInclusive start of the window, ISO 8601. Defaults to the first day of the current UTC month.
tostringExclusive end of the window, ISO 8601. Defaults to now.
Response
statusintegerdataarrayOne row per source with activity in the window, highest spend first.
curl -X GET "https://api.floqer.com/api/v1/usage/sources"
-H "Authorization: Bearer floq_YOUR_API_KEY"{
"status": 0,
"data": [
{
"source_instance_id": "string",
"name": "string",
"deleted": false,
"runs": 0,
"credits": 0,
"success_count": 0,
"cache_hits": 0,
"cache_hit_rate": 0,
"top_actions": [
{
"action_name": null,
"runs": null,
"credits": null,
"cache_hits": null
}
]
}
]
}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
/api/v1/agent/runRuns 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 ↑
Request body
promptstringrequiredNatural-language instruction for the web agent.
output_schemaobjectoptionalOptional 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
statusintegerdataobjectStructured output keyed by your output_schema fields, or { result } when no schema given.
citationsarrayWeb sources the agent consulted.
request_idstringUnique id for this request (e.g. fl_…). Quote it when contacting support.
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": {}
}'{
"status": 0,
"data": {},
"citations": [
{}
],
"request_id": "string"
}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_sincetimestamp — entries older thancache_sinceare 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_sinceinvalidates 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.