openapi: 3.1.0
info:
title: Loops OpenAPI Spec Workflows API
description: This is the OpenAPI Spec for the [Loops API](https://loops.so/docs/api).
version: 1.21.6
servers:
- url: https://app.loops.so/api/v1
tags:
- name: Workflows
description: View and mutate workflow graphs
paths:
/workflows:
get:
tags:
- Workflows
summary: List workflows
operationId: listWorkflows
description: Retrieve a paginated list of workflows.
x-mint:
href: /api-reference/list-workflows
parameters:
- name: perPage
in: query
required: false
description: How many results to return in each request. Must be between 10 and 50. Default is 20.
schema:
type: string
- name: cursor
in: query
required: false
description: A cursor to return a specific page of results. Cursors can be found from the `pagination.nextCursor` value in each response.
schema:
type: string
responses:
'200':
description: Successful.
content:
application/json:
schema:
$ref: '#/components/schemas/ListWorkflowsResponse'
'400':
description: Invalid `perPage` or `cursor` value.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
'401':
description: Invalid API key.
'405':
description: Wrong HTTP request method.
security:
- apiKey: []
post:
tags:
- Workflows
summary: Create a workflow
operationId: createWorkflow
description: Create a draft workflow with a blank trigger and exit node.
x-mint:
href: /api-reference/create-workflow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateWorkflowRequest'
responses:
'200':
description: Workflow created.
content:
application/json:
schema:
$ref: '#/components/schemas/SimplifiedWorkflow'
'400':
description: Invalid request body.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
'401':
description: Invalid API key.
'405':
description: Wrong HTTP request method.
'500':
description: Workflow creation is unavailable for this team.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
security:
- apiKey: []
/workflows/{workflowId}:
parameters:
- name: workflowId
in: path
required: true
description: The ID of the workflow.
schema:
type: string
examples:
- clw1a3b5c7d9e1f3g5h7i9j1
get:
tags:
- Workflows
summary: Get a workflow
operationId: getWorkflow
description: Retrieve a workflow graph with node type names, connections, and selected display fields.
x-mint:
content: Returns a simplified view of the workflow graph. Each node includes its `typeName`, outgoing connections in `nextNodeIds`, and selected display fields. Use [Get a workflow node](/api-reference/get-workflow-node) to retrieve full details for a single node.
href: /api-reference/get-workflow
responses:
'200':
description: Successful.
content:
application/json:
schema:
$ref: '#/components/schemas/SimplifiedWorkflow'
'400':
description: Invalid `workflowId`.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
'401':
description: Invalid API key.
'404':
description: Workflow not found.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
'405':
description: Wrong HTTP request method.
security:
- apiKey: []
post:
tags:
- Workflows
summary: Update a workflow
operationId: updateWorkflowProperties
description: Update a workflow's display properties. At least one property must be provided. To change the workflow's mailing list, use the `POST /v1/workflows/{workflowId}/mailing-list` endpoint instead.
x-mint:
href: /api-reference/update-workflow
content: <Info>To change a workflow's mailing list, use the [Change workflow mailing list](/api-reference/change-workflow-mailing-list) endpoint instead. This is because changing mailing lists may remove queued contacts from the workflow.</Info>
metadata:
description: Update a workflow's display properties.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateWorkflowPropertiesRequest'
responses:
'200':
description: Workflow properties updated.
content:
application/json:
schema:
$ref: '#/components/schemas/SimplifiedWorkflow'
'400':
description: Invalid `workflowId` or request body.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
'401':
description: Invalid API key.
'404':
description: Workflow not found.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
'405':
description: Wrong HTTP request method.
'409':
description: '`expectedRevisionId` is stale.'
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
security:
- apiKey: []
/workflows/{workflowId}/mailing-list:
parameters:
- name: workflowId
in: path
required: true
description: The ID of the workflow.
schema:
type: string
post:
tags:
- Workflows
summary: Change workflow mailing list
operationId: changeWorkflowMailingList
description: 'Dry run or apply a workflow mailing list change. If queued contacts would be removed from the workflow due to the change of mailing list, Loops returns with `"status": "queuedContactsFound"` instead of applying the change. Retry with `queuedContactPolicy: "discard"` to apply the change and discard those contacts. Confirmed update responses include the simplified workflow after the mailing list changes.'
x-mint:
href: /api-reference/change-workflow-mailing-list
content: 'Use this endpoint to change a workflow''s mailing list.
Make a dry run request using `dryRun: true`.
If queued contacts would be removed from the workflow due to the change of mailing list, Loops returns with `"status": "queuedContactsFound"` instead of applying the change. Retry with `queuedContactPolicy: "discard"` to apply the change and discard those contacts.
Confirmed update responses include the simplified workflow after the mailing list changes.'
metadata:
sidebarTitle: Change mailing list
description: Change a workflow's mailing list.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChangeWorkflowMailingListRequest'
responses:
'200':
description: Mailing-list dry run, queued-contact warning, or update result. Confirmed update responses include the simplified workflow after the mailing list changes.
content:
application/json:
schema:
$ref: '#/components/schemas/ChangeWorkflowMailingListResponse'
'400':
description: Invalid `workflowId`, request body, mailing list, or workflow state.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
'401':
description: Invalid API key.
'404':
description: Workflow not found.
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
'405':
description: Wrong HTTP request method.
'409':
description: '`expectedRevisionId` is stale.'
content:
application/json:
schema:
$ref: '#/components/schemas/WorkflowFailureResponse'
security:
- apiKey: []
components:
schemas:
ChangeWorkflowMailingListRequest:
type: object
properties:
expectedRevisionId:
$ref: '#/components/schemas/WorkflowExpectedRevisionId'
mailingListId:
type:
- string
- 'null'
description: 'The mailing list to use for the workflow. When assigning a mailing list, queued contacts excluded by the new list can return `queuedContactsFound`; retry with `queuedContactPolicy: "discard"` to apply the change and discard those contacts. Use `null` to clear the workflow mailing list; clearing does not discard queued contacts.'
dryRun:
type: boolean
description: If `true`, the request will be validated but the workflow will not be modified.
queuedContactPolicy:
$ref: '#/components/schemas/WorkflowQueuedContactPolicy'
required:
- expectedRevisionId
- mailingListId
additionalProperties: false
ChangeWorkflowMailingListResponse:
oneOf:
- $ref: '#/components/schemas/WorkflowMailingListPreview'
- $ref: '#/components/schemas/WorkflowMailingListUpdatedResponse'
CreateWorkflowRequest:
type: object
properties:
name:
type: string
description: The name of the workflow.
description:
type: string
description: The description of the workflow.
mailingListId:
type:
- string
- 'null'
description: The ID of a mailing list the workflow sends to. After creation, the mailing list can be changed with the `/v1/workflows/{workflowId}/mailing-list` endpoint.
required:
- name
additionalProperties: false
ListWorkflowsResponse:
type: object
properties:
pagination:
$ref: '#/components/schemas/Pagination'
data:
type: array
items:
$ref: '#/components/schemas/WorkflowSummary'
required:
- pagination
- data
examples:
- pagination:
totalResults: 45
returnedResults: 20
perPage: 20
totalPages: 3
nextCursor: clp9k2m4n6p8q0r2s4t6u8v0
nextPage: https://app.loops.so/api/v1/campaign-groups?cursor=clp9k2m4n6p8q0r2s4t6u8v0&perPage=20
data:
- id: clw1a3b5c7d9e1f3g5h7i9j1
name: Welcome series
createdAt: 2025-06-29 07:47:39.370000+00:00
updatedAt: 2025-06-29 07:47:39.370000+00:00
NullableWorkflowRevisionId:
type:
- string
- 'null'
description: The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation. Will be `null` for workflows without a revision token yet.
Pagination:
type: object
properties:
totalResults:
type: number
examples:
- 2
returnedResults:
type: number
examples:
- 2
perPage:
type: number
examples:
- 20
totalPages:
type: number
examples:
- 1
nextCursor:
type:
- string
- 'null'
examples:
- null
nextPage:
type:
- string
- 'null'
examples:
- null
required:
- totalResults
- returnedResults
- perPage
- totalPages
- nextCursor
- nextPage
examples:
- totalResults: 2
returnedResults: 2
perPage: 20
totalPages: 1
nextCursor: null
nextPage: null
SimplifiedAddToListTriggerWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- AddToListTrigger
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
mailingListId:
type:
- string
- 'null'
description: The ID of the mailing list that triggers the workflow.
reEligible:
$ref: '#/components/schemas/WorkflowReEligible'
required:
- typeName
- nextNodeIds
- mailingListId
- reEligible
additionalProperties: false
SimplifiedAudienceFilterWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- AudienceFilter
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
required:
- typeName
- nextNodeIds
additionalProperties: false
SimplifiedBlankTriggerWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- BlankTrigger
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
required:
- typeName
- nextNodeIds
additionalProperties: false
SimplifiedBranchWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- BranchNode
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
required:
- typeName
- nextNodeIds
additionalProperties: false
SimplifiedContactPropertyTriggerWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- ContactPropertyTrigger
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
contactPropertyQuery:
oneOf:
- $ref: '#/components/schemas/WorkflowContactPropertyQuery'
- type: 'null'
reEligible:
$ref: '#/components/schemas/WorkflowReEligible'
required:
- typeName
- nextNodeIds
- contactPropertyQuery
- reEligible
additionalProperties: false
SimplifiedEventTriggerWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- EventTrigger
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
eventName:
type:
- string
- 'null'
description: The name of the event that triggers the workflow.
reEligible:
$ref: '#/components/schemas/WorkflowReEligible'
required:
- typeName
- nextNodeIds
- eventName
- reEligible
additionalProperties: false
examples:
- typeName: EventTrigger
nextNodeIds:
- cln1a3b5c7d9e1f3g5h7i9j1
eventName: signup
reEligible: false
SimplifiedExitActionWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- ExitAction
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
required:
- typeName
- nextNodeIds
additionalProperties: false
SimplifiedExperimentBranchWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- ExperimentBranchNode
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
samplingRate:
type: number
description: The percentage of contacts that will be sent to variant branches, between `0` and `100`. The remaining percentage will be sent to the control branch.
required:
- typeName
- nextNodeIds
- samplingRate
additionalProperties: false
SimplifiedSendEmailActionWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- SendEmailAction
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
emailMessageId:
type:
- string
- 'null'
description: The ID of the email message to send. To edit this email, use the `POST /v1/email-messages/{emailMessageId}` endpoint.
subject:
type: string
description: The subject of the email message (reference only).
required:
- typeName
- nextNodeIds
- emailMessageId
- subject
additionalProperties: false
examples:
- typeName: SendEmailAction
nextNodeIds: []
emailMessageId: cle5f7g9h1i3j5k7l9m1n3p5
subject: Welcome to Acme!
SimplifiedSignupTriggerWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- SignupTrigger
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
required:
- typeName
- nextNodeIds
additionalProperties: false
SimplifiedTimerActionWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- TimerAction
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
amount:
$ref: '#/components/schemas/WorkflowTimerAmount'
unit:
$ref: '#/components/schemas/WorkflowTimerUnit'
required:
- typeName
- nextNodeIds
- amount
- unit
additionalProperties: false
examples:
- typeName: TimerAction
nextNodeIds:
- cln1a3b5c7d9e1f3g5h7i9j1
amount: 2
unit: d
SimplifiedVariantWorkflowNode:
type: object
properties:
typeName:
type: string
enum:
- VariantNode
nextNodeIds:
$ref: '#/components/schemas/WorkflowNextNodeIds'
isControl:
type: boolean
description: Whether this is the control variant of an experiment.
required:
- typeName
- nextNodeIds
- isControl
additionalProperties: false
SimplifiedWorkflow:
type: object
properties:
id:
type: string
description: The ID of the workflow.
workflowRevisionId:
$ref: '#/components/schemas/NullableWorkflowRevisionId'
status:
type: string
enum:
- Draft
- Sending
- Paused
- PausedAndQueueing
name:
type: string
description: The name of the workflow.
description:
type: string
description: The description of the workflow.
mailingListId:
type:
- string
- 'null'
description: The ID of the mailing list the workflow sends to.
rootNodeId:
type: string
description: The ID of the root node in the workflow graph.
nodes:
type: object
description: A map of node IDs to simplified node objects. Each node includes `typeName` and `nextNodeIds`, plus type-specific fields when present. To get the full node object, use the `GET /v1/workflows/{workflowId}/nodes/{nodeId}` endpoint.
additionalProperties:
$ref: '#/components/schemas/SimplifiedWorkflowNode'
example:
cf16k73gq014h3mmj5b6jdi9r:
typeName: SignupTrigger
nextNodeIds:
- cf16k73gq014h3mmj5b4jdifg
- cf16k73gq014h3mmj5b4jdifh
required:
- id
- workflowRevisionId
- status
- mailingListId
- rootNodeId
- nodes
examples:
- id: clw1a3b5c7d9e1f3g5h7i9j1
status: Draft
name: Welcome series
description: Onboarding emails for new signups
mailingListId: clm2k8j4h6g0f8d6s4a2b0z8
rootNodeId: cln8p0q2r4s6t8u0v2w4x6z8
nodes:
cln8p0q2r4s6t8u0v2w4x6z8:
typeName: SignupTrigger
nextNodeIds:
- cln1a3b5c7d9e1f3g5h7i9j1
cln1a3b5c7d9e1f3g5h7i9j1:
typeName: SendEmailAction
nextNodeIds: []
emailMessageId: cle5f7g9h1i3j5k7l9m1n3p5
subject: Welcome to Acme!
SimplifiedWorkflowNode:
oneOf:
- $ref: '#/components/schemas/SimplifiedSignupTriggerWorkflowNode'
- $ref: '#/components/schemas/SimplifiedEventTriggerWorkflowNode'
- $ref: '#/components/schemas/SimplifiedContactPropertyTriggerWorkflowNode'
- $ref: '#/components/schemas/SimplifiedAddToListTriggerWorkflowNode'
- $ref: '#/components/schemas/SimplifiedBlankTriggerWorkflowNode'
- $ref: '#/components/schemas/SimplifiedAudienceFilterWorkflowNode'
- $ref: '#/components/schemas/SimplifiedTimerActionWorkflowNode'
- $ref: '#/components/schemas/SimplifiedSendEmailActionWorkflowNode'
- $ref: '#/components/schemas/SimplifiedExitActionWorkflowNode'
- $ref: '#/components/schemas/SimplifiedBranchWorkflowNode'
- $ref: '#/components/schemas/SimplifiedExperimentBranchWorkflowNode'
- $ref: '#/components/schemas/SimplifiedVariantWorkflowNode'
discriminator:
propertyName: typeName
UpdateWorkflowPropertiesRequest:
type: object
description: At least one property must be provided.
properties:
expectedRevisionId:
$ref: '#/components/schemas/WorkflowExpectedRevisionId'
name:
type: string
description: The updated workflow name.
description:
type: string
description: The updated workflow description.
required:
- expectedRevisionId
anyOf:
- required:
- name
- required:
- description
additionalProperties: false
WorkflowContactPropertyComparison:
type: object
description: For Contact Updated triggers, the API validates `operator` against the selected contact property's type and the side of the comparison. The `was` comparison can use any operator supported by the selected property type. The `is` comparison uses the same operators, except number and boolean properties cannot use `empty`. String properties support `any`, `equal`, `not_equal`, `contains`, `not_contains`, `empty`, and `not_empty`. Number properties support `any`, `greater_than`, `less_than`, `numeric_equal`, `numeric_not_equal`, `empty`, and `not_empty`. Boolean properties support `any`, `true`, `false`, `empty`, and `not_empty`. Date properties support `any`, `empty`, `not_empty`, `after`, `before`, and `between`.
properties:
value:
oneOf:
- type: string
- type: number
- type: boolean
operator:
type: string
description: The comparison operator. It must be valid for the selected contact property's type and for the `is` or `was` side of the comparison. Number and boolean properties allow `empty` on `was`, but not on `is`.
enum:
- any
- contains
- not_contains
- empty
- not_empty
- equal
- not_equal
- greater_than
- less_than
- 'true'
- 'false'
- numeric_equal
- numeric_not_equal
- after
- before
- between
required:
- value
- operator
WorkflowContactPropertyQuery:
type: object
description: Define the contact property change that triggers the workflow. In update requests, `key` must resolve to an existing contact property that is available for Contact Updated triggers. Hidden or unsupported fields, such as `createdAt`, `notes`, and computed contact properties, are rejected.
properties:
key:
type: string
description: The camel-cased `key` of the contact property to query. The property must exist for the team and must be available for Contact Updated triggers.
examples:
- firstName
- email
- planName
is:
description: Comparison for the new contact property value after the update.
$ref: '#/components/schemas/WorkflowContactPropertyComparison'
was:
description: Comparison for the previous contact property value before the update.
$ref: '#/components/schemas/WorkflowContactPropertyComparison'
required:
- key
- is
- was
WorkflowExpectedRevisionId:
type:
- string
- 'null'
description: The workflow revision token returned by the latest workflow read or mutation. Older workflows may return `null` before their first revision-aware mutation; pass `null` back as `expectedRevisionId` in that case. If the token is stale, the API returns a `409 Conflict` error.
WorkflowFailureResponse:
type: object
properties:
message:
type: string
required:
- message
examples:
- message: Workflow not found.
WorkflowMailingListPreview:
type: object
properties:
status:
type: string
enum:
- dryRun
- queuedContactsFound
mailingListId:
type:
- string
- 'null'
queuedContactCount:
type: number
description: The number of queued contacts that would be removed from the workflow due to the mailing list changing.
required:
- status
- mailingListId
- queuedContactCount
WorkflowMailingListUpdatedResponse:
type: object
properties:
status:
type: string
enum:
- updated
mailingListId:
type:
- string
- 'null'
workflowRevisionId:
$ref: '#/components/schemas/WorkflowRevisionId'
queuedContactCount:
type: number
description: The number of queued contacts that were removed from the workflow due to the mailing list changing.
workflow:
$ref: '#/components/schemas/SimplifiedWorkflow'
required:
- status
- mailingListId
- workflowRevisionId
- queuedContactCount
- workflow
WorkflowNextNodeIds:
type: array
items:
type: string
description: The IDs of the nodes that are downstream of this node.
WorkflowQueuedContactPolicy:
type: string
enum:
- fail
- discard
default: fail
description: '`fail` returns queued-contact impact instead of mutating. `discard` confirms that matching queued contacts should be discarded. Defaults to `fail` when omitted.'
WorkflowReEligible:
type: boolean
description: If `true`, the contacts will be able to enter this workflow every time the trigger is matched. If `false`, contacts will only ever enter this workflow once. Matches the "Trigger frequency" option in the UI.
WorkflowRevisionId:
type:
- string
description: The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
WorkflowSummary:
type: object
properties:
id:
type: string
description: The ID of the workflow.
examples:
- clw1a3b5c7d9e1f3g5h7i9j1
name:
type: string
description: The name of the workflow.
examples:
- Welcome series
createdAt:
type: string
format: date-time
examples:
- 2025-06-29 07:47:39.370000+00:00
description: ISO 8601 timestamp for when the workflow was created.
updatedAt:
type: string
format: date-time
examples:
- 2025-06-29 07:47:39.370000+00:00
description: ISO 8601 timestamp for when the workflow was last updated.
required:
- id
- name
- createdAt
- updatedAt
examples:
- id: clw1a3b5c7d9e1f3g5h7i9j1
name: Welcome series
createdAt: 2025-06-29 07:47:39.370000+00:00
updatedAt: 2025-06-29 07:47:39.370000+00:00
WorkflowTimerAmount:
type: number
description: The amount of time to wait before triggering the next node. Set to `0` to move to the next node immediately.
WorkflowTimerUnit:
type: string
enum:
- m
- h
- d
description: The unit of time for the timer action node. m = minutes, h = hours, d = days.
securitySchemes:
apiKey:
type: http
scheme: bearer