Skip to main content
A round robin pool distributes inbound leads across an ordered list of participants using pointer rotation: each pool remembers whose turn is next, assigns the lead to that participant, then advances the pointer to the following participant (wrapping back to the first after the last). The rotation order is the order of the participantIds array. Connections route inbound leads into a pool by matching their origin — a generic webhook, a meta page/form/ad/campaign, an automation provider (Zapier, Make, n8n), or a workflow. When a lead arrives, FlowEstate picks the active pool whose connection matches the lead’s source and assigns it through that pool’s rotation. Every endpoint on this page operates inside the calling organization’s tenant — you cannot read or write round robin pools from another organization with the same key.

List pools

GET /round-robin
Scope: round-robin:read. Lists the organization’s round robin pools, newest first.

Query parameters

ParamTypeNotes
limitnumber1..100, default 50.
offsetnumberDefault 0.
querystringCase-insensitive match on the pool name.
statusenumall (default), active, or inactive.

Request

curl "https://panel.flowestate.app/api/v1/round-robin?status=active&limit=20" \
  -H "Authorization: Bearer fe_k_your_key_here"

Response 200

{
  "data": [
    {
      "id": "uuid",
      "organizationId": "uuid",
      "name": "Inbound — Meta leads",
      "isActive": true,
      "assignInboundLeads": true,
      "assignManualLeads": false,
      "emailNotificationsEnabled": false,
      "followerUserId": null,
      "goalTotalDeals": null,
      "goalWonDeals": null,
      "nextParticipantOrder": 2,
      "createdBy": "uuid",
      "createdByName": "Ada Lovelace",
      "followerName": null,
      "participantCount": 4,
      "assignmentCount": 128,
      "lastAssignmentAt": "2026-06-22T18:04:15.000Z",
      "createdAt": "2026-04-29T18:04:15.000Z",
      "updatedAt": "2026-06-22T18:04:15.000Z"
    }
  ],
  "pagination": { "total": 3, "limit": 20, "offset": 0, "hasMore": false }
}
nextParticipantOrder is the 1-based order value of the participant who will receive the next lead. participantCount counts only active participants.

Create a pool

POST /round-robin
Scope: round-robin:write

Request body

FieldTypeNotes
namestringRequired, 1..200 chars.
participantIdsstring[]Required, at least one UUID. Array order becomes the rotation order. Every id must be a member of the organization; no duplicates.
followerUserIdstringOptional UUID. A member who follows the pool’s activity. Must be an org member.
goalTotalDealsnumberOptional, integer ≥ 0.
goalWonDealsnumberOptional, integer ≥ 0.
isActivebooleanDefault true. Only active pools assign leads.
assignInboundLeadsbooleanDefault true. Assign leads arriving from connections (Meta, webhook, automation, workflow).
assignManualLeadsbooleanDefault false. Assign leads created manually (source: "manual").
emailNotificationsEnabledbooleanDefault false. Email the assigned participant on each assignment.
connectionsobject[]Default []. See connection object.

Connection object

Each entry in connections (and the body of POST .../connections) has this shape:
FieldTypeNotes
sourceenumDefault webhook. One of RoundRobinConnectionSource: webhook, meta, automation, workflow.
providerenumRoundRobinConnectionProvider: custom, make, n8n, zapier. Required when source is automation.
sourceKeystringOptional, 1..200 chars. Free-form key to match a custom source.
metaPageId, metaFormId, metaAdId, metaCampaignIdstringOptional, 1..200 chars each. Used to match meta leads.
utmCampaignstringOptional, 1..200 chars.
workflowIdstringOptional, 1..200 chars. Required when source is workflow.
Only one pool per organization may hold the generic webhook connection, and the organization’s universal lead webhook integration must be enabled. Duplicate connections (identical field-for-field) are rejected.

Request

curl -X POST https://panel.flowestate.app/api/v1/round-robin \
  -H "Authorization: Bearer fe_k_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Inbound — Meta leads",
    "participantIds": ["uuid-a", "uuid-b", "uuid-c"],
    "assignInboundLeads": true,
    "connections": [
      { "source": "meta", "metaFormId": "1234567890" }
    ]
  }'

Response 201

{
  "id": "uuid",
  "organizationId": "uuid",
  "name": "Inbound — Meta leads",
  "isActive": true,
  "assignInboundLeads": true,
  "assignManualLeads": false,
  "emailNotificationsEnabled": false,
  "followerUserId": null,
  "goalTotalDeals": null,
  "goalWonDeals": null,
  "nextParticipantOrder": 1,
  "createdBy": null,
  "createdAt": "2026-06-23T18:04:15.000Z",
  "updatedAt": "2026-06-23T18:04:15.000Z"
}

Common errors

  • 400 VALIDATION_ERROR — invalid body (empty participantIds, automation connection missing provider, workflow connection missing workflowId, etc.).
  • 400 BAD_REQUEST — a participant or follower is not an organization member, duplicate participants, or duplicate connections.
  • 400 BAD_REQUESTwebhook connection requested but the universal lead webhook integration is not enabled.
  • 409 CONFLICT — the universal webhook connection is already assigned to another pool.

Get a pool

GET /round-robin/{roundRobinId}
Scope: round-robin:read. Returns the pool with its ordered participants, connections, a settings summary, and assignment stats. 404 NOT_FOUND if the pool doesn’t exist or belongs to another org.

Response 200

{
  "id": "uuid",
  "organizationId": "uuid",
  "name": "Inbound — Meta leads",
  "isActive": true,
  "assignInboundLeads": true,
  "assignManualLeads": false,
  "emailNotificationsEnabled": false,
  "followerUserId": null,
  "goalTotalDeals": null,
  "goalWonDeals": null,
  "nextParticipantOrder": 2,
  "createdBy": "uuid",
  "createdAt": "2026-04-29T18:04:15.000Z",
  "updatedAt": "2026-06-22T18:04:15.000Z",
  "participants": [
    {
      "id": "uuid",
      "roundRobinId": "uuid",
      "organizationId": "uuid",
      "userId": "uuid",
      "order": 1,
      "isActive": true,
      "createdAt": "2026-04-29T18:04:15.000Z",
      "updatedAt": "2026-04-29T18:04:15.000Z",
      "user": {
        "id": "uuid",
        "name": "Ada Lovelace",
        "email": "ada@example.com",
        "image": null
      }
    }
  ],
  "connections": [
    {
      "id": "uuid",
      "roundRobinId": "uuid",
      "organizationId": "uuid",
      "source": "meta",
      "metaPageId": null,
      "metaFormId": "1234567890",
      "metaAdId": null,
      "metaCampaignId": null,
      "provider": null,
      "sourceKey": null,
      "utmCampaign": null,
      "workflowId": null,
      "isActive": true,
      "createdAt": "2026-04-29T18:04:15.000Z",
      "updatedAt": "2026-04-29T18:04:15.000Z"
    }
  ],
  "settings": {
    "id": "uuid",
    "name": "Inbound — Meta leads",
    "isActive": true,
    "assignInboundLeads": true,
    "assignManualLeads": false,
    "emailNotificationsEnabled": false,
    "followerUserId": null,
    "goalTotalDeals": null,
    "goalWonDeals": null
  },
  "stats": {
    "totalAssignments": 128,
    "lastAssignmentAt": "2026-06-22T18:04:15.000Z",
    "participantAssignments": [
      { "assignedUserId": "uuid", "assignmentCount": 32, "lastAssignedAt": "2026-06-22T18:04:15.000Z" }
    ]
  }
}
Participants are returned in rotation order (order ascending).

Update a pool

PATCH /round-robin/{roundRobinId}
Scope: round-robin:write. Body is the create body minus name’s required flag — every field is optional; send only what you want to change. 404 NOT_FOUND if the pool doesn’t exist or belongs to another org.
FieldTypeNotes
namestring1..200 chars.
isActivebooleanActivate or deactivate the pool. Deactivated pools stop receiving leads.
participantIdsstring[]At least one UUID. Replaces the full participant list; the array order becomes the new rotation order and resets the pointer to the first participant.
followerUserIdstring | nullUUID or null to clear.
goalTotalDealsnumber | nullInteger ≥ 0 or null.
goalWonDealsnumber | nullInteger ≥ 0 or null.
assignInboundLeadsboolean
assignManualLeadsboolean
emailNotificationsEnabledboolean
connectionsobject[]Replaces the full connection list. See connection object.

Request

Reorder the rotation and deactivate the pool:
curl -X PATCH https://panel.flowestate.app/api/v1/round-robin/uuid \
  -H "Authorization: Bearer fe_k_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "isActive": false,
    "participantIds": ["uuid-c", "uuid-a", "uuid-b"]
  }'

Response 200

{ "success": true }

Common errors

  • 400 VALIDATION_ERROR / 400 BAD_REQUEST — same validation rules as create (membership, duplicates, connection requirements).
  • 409 CONFLICT — the universal webhook connection is already held by another pool.

Delete a pool

DELETE /round-robin/{roundRobinId}
Scope: round-robin:write. Removes the pool and its participants, connections, and assignment history. 404 NOT_FOUND if the pool doesn’t exist or belongs to another org.

Response 200

{ "deleted": true, "id": "uuid" }

Add a connection

POST /round-robin/{roundRobinId}/connections
Scope: round-robin:write. Adds a single routing connection to an existing pool. Body is the connection object. 404 NOT_FOUND if the pool doesn’t exist or belongs to another org.

Request

curl -X POST https://panel.flowestate.app/api/v1/round-robin/uuid/connections \
  -H "Authorization: Bearer fe_k_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "connection": {
      "source": "automation",
      "provider": "zapier",
      "sourceKey": "q3-launch"
    }
  }'

Response 201

{
  "id": "uuid",
  "roundRobinId": "uuid",
  "organizationId": "uuid",
  "source": "automation",
  "metaPageId": null,
  "metaFormId": null,
  "metaAdId": null,
  "metaCampaignId": null,
  "provider": "zapier",
  "sourceKey": "q3-launch",
  "utmCampaign": null,
  "workflowId": null,
  "isActive": true,
  "createdAt": "2026-06-23T18:04:15.000Z",
  "updatedAt": "2026-06-23T18:04:15.000Z"
}

Common errors

  • 400 VALIDATION_ERROR — automation connection missing provider, or workflow connection missing workflowId.
  • 400 BAD_REQUESTwebhook connection requested but the universal lead webhook integration is not enabled.
  • 409 CONFLICT — an identical connection already exists on this pool, or the universal webhook connection is held by another pool.

Remove a connection

DELETE /round-robin/{roundRobinId}/connections/{connectionId}
Scope: round-robin:write. 404 NOT_FOUND if the connection doesn’t exist on that pool or belongs to another org.

Response 200

{ "deleted": true, "id": "uuid" }

List assignments

GET /round-robin/{roundRobinId}/assignments
Scope: round-robin:read. Lists the pool’s assignment history, newest first.

Query parameters

ParamTypeNotes
limitnumber1..100, default 50.
offsetnumberDefault 0.
sourceenumOptional. Filter by RoundRobinAssignmentSource: manual, meta, webhook, whatsapp, system.

Response 200

{
  "data": [
    {
      "id": "uuid",
      "leadId": "uuid",
      "assignedUserId": "uuid",
      "source": "meta",
      "turnOrder": 2,
      "metadata": "{\"match\":{\"fallbackType\":\"meta_form\",\"connectionId\":\"uuid\"}}",
      "createdAt": "2026-06-22T18:04:15.000Z",
      "userName": "Ada Lovelace",
      "userEmail": "ada@example.com"
    }
  ],
  "pagination": { "total": 128, "limit": 50, "offset": 0, "hasMore": true }
}
turnOrder is the participant’s order value at the time of assignment. metadata is a JSON-encoded string (or null) describing how the lead matched a connection.

Assign a lead

POST /round-robin/assign
Scope: round-robin:write. Runs a lead through the matching active pool’s pointer rotation, assigns it to the next eligible participant, and advances the pointer.

Request body

FieldTypeNotes
leadIdstringRequired UUID. The lead must belong to your organization.
sourceenumDefault system. One of RoundRobinAssignmentSource: manual, meta, webhook, whatsapp, system. A manual source routes through pools with assignManualLeads enabled; all other sources route through pools with assignInboundLeads enabled.

Request

curl -X POST https://panel.flowestate.app/api/v1/round-robin/assign \
  -H "Authorization: Bearer fe_k_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "leadId": "uuid", "source": "system" }'

Response 200

When a participant is assigned:
{
  "assigned": true,
  "roundRobinId": "uuid",
  "assignedToId": "uuid",
  "turnOrder": 2
}
When no assignment happens — no active pool matches the source, the matched pool has no eligible participant, or the lead already has an assignee — the response is:
{ "assigned": false }
The lead is only assigned if it currently has no assignee. A lead that already has an owner returns { "assigned": false } and is left untouched.

Common errors

  • 400 VALIDATION_ERRORleadId is not a UUID, or source is not a known value.
  • 404 NOT_FOUND — the lead does not exist or belongs to another organization.