Paragraph workflows (code)
When to use
Use for workflow definitions in a Paragraph repo: files under src/integrations/<integration>/workflows/, triggers, steps, branching, and wiring to the Paragon dashboard. For integration config only (config.ts / inputs.ts), use the project skill paragon-paragraph-custom-integrations.
Official references
Create and place files
- From the Paragraph project root:
para new workflow --integration <integration> (e.g. hubspot, linear, or custom.myapp).
- New workflow files live in
src/integrations/<integration>/workflows/.
- Keep
readonly id as generated; Paragon maintains it.
- List workflow classes in that integration’s
config.ts in workflowDisplayOrder so they appear in the Connect Portal and dashboard.
Workflow class shape (typical native integration)
Match the generated pattern in this repo:
- Extend
Workflow<IIntegration, IPersona<typeof personaMeta>, InputResultMap> from @useparagon/core.
- Import
IContext, IConnectUser, IPermissionContext, persona types, and InputResultMap / I…Integration from @useparagon/integrations/<name>.
- Import
personaMeta from ../../../persona.meta (adjust depth if the workflow folder depth differs).
define(integration, context, connectUser)
- Declare steps (any order).
- Wire control flow with
.nextStep(...) (this repo’s @useparagon/core surface).
- Return
this.register({ … }) with an object whose keys are stable (they map to the workflow graph); every step used in the graph should appear here.
Class fields (common)
| Member |
Role |
name, description |
Dashboard and Connect Portal copy |
inputs |
Workflow-level user settings via createInputs({ … }) from the integration package |
defaultEnabled, hidden |
Connect Portal behavior (see Paragon connect-portal docs) |
definePermissions |
Optional ConditionalInput for workflow visibility |
id |
Do not edit |
Where steps come from
| Kind |
Source |
Triggers: CronStep, EndpointStep, EventStep, IntegrationEnabledStep |
@useparagon/core |
Core steps: RequestStep, ResponseStep, ConditionalStep, FunctionStep, FanOutStep, DelayStep, IntegrationRequestStep, … |
@useparagon/core |
| Integration-specific actions and triggers |
integration argument in define, e.g. integration.triggers.recordCreated({ … }), integration.actions.searchRecords({ … }, { … }) |
Optional step options (second arg on many integration actions, or step config): autoRetry, continueWorkflowOnError, description, etc., per defining workflows.
Referencing data
- Prior step output:
someStep.output… (template strings can interpolate like the Workflow Editor’s {{1.output…}}).
- Environment secrets:
context.getEnvironmentSecret("KEY").
- User settings:
context.getInput(…) — integration-level inputs from that integration’s inputs.ts / exports; workflow-level from this.inputs (see defining workflows).
- Connected user:
connectUser.userId, connectUser.meta… (metadata typed from persona.meta.ts).
Orchestration (control flow)
- Linear chain:
triggerStep.nextStep(stepA).nextStep(stepB).
- Conditional:
conditionalStep.whenTrue(stepIfTrue).whenFalse(stepIfFalse) then continue with .nextStep(...) on the conditional as needed (see conditional branches).
- Fan out:
fanOutStep.branch(innerStep.nextStep(…)) — .branch is not chainable; then connect the fan-out’s continuation per docs (fan out branches).
Conditional logic
- Import operators:
import * as Operators from '@useparagon/core/operator';
- Pass
ConditionalInput to ConditionalStep (and similar APIs such as pagination stopCondition).
- Paragon expects conditions in disjunctive normal form: OR of ANDs (conditional logic).
Triggers (quick reference)
| Trigger |
Use case |
EndpointStep |
HTTP request trigger; set allowArbitraryPayload or validations; output: output.request (headers, body, params, file). |
CronStep |
Scheduler; init params include cron and optional timezone (IANA string; types live in @useparagon/core). Some older docs may show different casing—prefer the installed package. |
EventStep |
App Event from src/events; import the event module into the workflow file. |
IntegrationEnabledStep |
Runs when user enables the integration. |
Details and examples: @useparagon/core glossary — Triggers.
Requests
RequestStep: full URL; you supply auth (e.g. Bearer) via authorization or headers; output output.response (statusCode, body, headers).
IntegrationRequestStep: path relative to integration apiBaseUrl (or full URL); user’s integration auth applied automatically; same output.response shape.
Pagination: optional pagination callback returning outputPath, pageToken, stopCondition (Operators). See request pagination and the glossary example.
ResponseStep
Only for workflows driven by an HTTP request trigger: set responseType, statusCode, and body (JSON or file). See glossary ResponseStep.
FunctionStep
Runs sandboxed code: must be self-contained; pass data via parameters. Signatures and allowed libraries are in Paragon’s JavaScript libraries doc. Output: functionStep.output.result. Follow the pattern already used in the repo (string module-style code vs inline code) for consistency with neighboring workflows.
Reusing steps across workflows
Put shared helpers under a top-level src/<something>/ folder not named integrations (e.g. src/common/), export step factories, import from workflows. See Reusing steps. For secrets inside shared code, patterns using Execution appear in the same doc page.
Checklist before finishing a workflow change
- Trigger defined and is the start of the
.nextStep chain (or branches/fan-out as designed).
- Every step in the graph is passed to
this.register({ … }) with unchanged keys if the workflow already exists in Paragon.
- Types: integration imports align with the folder’s integration (
@useparagon/integrations/...).
- Conditions use Operators in DNF where required.
- Integration
config.ts workflowDisplayOrder includes this workflow class if it should show in the portal.
For step parameter details not repeated here, prefer the live glossary: @useparagon/core.
1---2name: paragraph-workflow-builder3description: Builds and edits Paragon Paragraph workflows as TypeScript (triggers, core steps, integration actions, orchestration with nextStep, Operators, register/context/connectUser). Use when authoring workflows under src/integrations/*/workflows/, para new workflow, RequestStep, IntegrationRequestStep, EndpointStep, or when the user mentions Paragraph workflow code or @useparagon/core workflow APIs.4---56# Paragraph workflows (code)78## When to use910Use for **workflow definitions** in a Paragraph repo: files under `src/integrations/<integration>/workflows/`, triggers, steps, branching, and wiring to the Paragon dashboard. For **integration config** only (`config.ts` / `inputs.ts`), use the project skill **paragon-paragraph-custom-integrations**.1112**Official references**1314- [Defining workflows](https://docs.useparagon.com/paragraph/defining-workflows) — structure, orchestration, conditions, fan-out, reusing steps.15- [@useparagon/core glossary](https://docs.useparagon.com/paragraph/reference/useparagon-core) — triggers and step constructors, inputs/outputs.16- Doc index for deeper pages: [llms.txt](https://docs.useparagon.com/llms.txt).1718---1920## Create and place files21221. From the Paragraph project root: `para new workflow --integration <integration>` (e.g. `hubspot`, `linear`, or `custom.myapp`).232. New workflow files live in **`src/integrations/<integration>/workflows/`**.243. Keep **`readonly id`** as generated; Paragon maintains it.254. List workflow classes in that integration’s **`config.ts`** in **`workflowDisplayOrder`** so they appear in the Connect Portal and dashboard.2627---2829## Workflow class shape (typical native integration)3031Match the generated pattern in this repo:3233- Extend **`Workflow<IIntegration, IPersona<typeof personaMeta>, InputResultMap>`** from `@useparagon/core`.34- Import **`IContext`**, **`IConnectUser`**, **`IPermissionContext`**, persona types, and **`InputResultMap`** / **`I…Integration`** from `@useparagon/integrations/<name>`.35- Import **`personaMeta`** from `../../../persona.meta` (adjust depth if the workflow folder depth differs).3637**`define(integration, context, connectUser)`**3839- Declare steps (any order).40- Wire control flow with **`.nextStep(...)`** (this repo’s `@useparagon/core` surface).41- Return **`this.register({ … })`** with an object whose **keys are stable** (they map to the workflow graph); every step used in the graph should appear here.4243**Class fields (common)**4445| Member | Role |46|--------|------|47| `name`, `description` | Dashboard and Connect Portal copy |48| `inputs` | Workflow-level user settings via `createInputs({ … })` from the integration package |49| `defaultEnabled`, `hidden` | Connect Portal behavior (see Paragon connect-portal docs) |50| `definePermissions` | Optional `ConditionalInput` for workflow visibility |51| `id` | Do not edit |5253---5455## Where steps come from5657| Kind | Source |58|------|--------|59| Triggers: `CronStep`, `EndpointStep`, `EventStep`, `IntegrationEnabledStep` | `@useparagon/core` |60| Core steps: `RequestStep`, `ResponseStep`, `ConditionalStep`, `FunctionStep`, `FanOutStep`, `DelayStep`, `IntegrationRequestStep`, … | `@useparagon/core` |61| Integration-specific **actions** and **triggers** | `integration` argument in `define`, e.g. `integration.triggers.recordCreated({ … })`, `integration.actions.searchRecords({ … }, { … })` |6263Optional step options (second arg on many integration actions, or step config): `autoRetry`, `continueWorkflowOnError`, `description`, etc., per [defining workflows](https://docs.useparagon.com/paragraph/defining-workflows#defining-steps).6465---6667## Referencing data6869- **Prior step output**: `someStep.output…` (template strings can interpolate like the Workflow Editor’s `{{1.output…}}`).70- **Environment secrets**: `context.getEnvironmentSecret("KEY")`.71- **User settings**: `context.getInput(…)` — integration-level inputs from that integration’s `inputs.ts` / exports; workflow-level from **`this.inputs`** (see [defining workflows](https://docs.useparagon.com/paragraph/defining-workflows#referencing-user-settings-and-environment-secrets)).72- **Connected user**: `connectUser.userId`, `connectUser.meta…` (metadata typed from `persona.meta.ts`).7374---7576## Orchestration (control flow)7778- **Linear chain**: `triggerStep.nextStep(stepA).nextStep(stepB)`.79- **Conditional**: `conditionalStep.whenTrue(stepIfTrue).whenFalse(stepIfFalse)` then continue with **`.nextStep(...)`** on the conditional as needed (see [conditional branches](https://docs.useparagon.com/paragraph/defining-workflows#conditional-branches)).80- **Fan out**: `fanOutStep.branch(innerStep.nextStep(…))` — **`.branch` is not chainable**; then connect the fan-out’s continuation per docs ([fan out branches](https://docs.useparagon.com/paragraph/defining-workflows#fan-out-branches)).8182---8384## Conditional logic85861. Import operators: `import * as Operators from '@useparagon/core/operator';`872. Pass **`ConditionalInput`** to `ConditionalStep` (and similar APIs such as pagination **stopCondition**).883. Paragon expects conditions in **disjunctive normal form**: **OR of ANDs** ([conditional logic](https://docs.useparagon.com/paragraph/defining-workflows#conditional-logic)).8990---9192## Triggers (quick reference)9394| Trigger | Use case |95|---------|-----------|96| `EndpointStep` | HTTP request trigger; set `allowArbitraryPayload` or validations; output: **`output.request`** (`headers`, `body`, `params`, `file`). |97| `CronStep` | Scheduler; init params include **`cron`** and optional **`timezone`** (IANA string; types live in `@useparagon/core`). Some older docs may show different casing—prefer the installed package. |98| `EventStep` | App Event from **`src/events`**; import the event module into the workflow file. |99| `IntegrationEnabledStep` | Runs when user enables the integration. |100101Details and examples: [@useparagon/core glossary — Triggers](https://docs.useparagon.com/paragraph/reference/useparagon-core).102103---104105## Requests106107- **`RequestStep`**: full URL; you supply auth (e.g. Bearer) via `authorization` or headers; output **`output.response`** (`statusCode`, `body`, `headers`).108- **`IntegrationRequestStep`**: path relative to integration **apiBaseUrl** (or full URL); user’s integration auth applied automatically; same **`output.response`** shape.109110Pagination: optional `pagination` callback returning `outputPath`, `pageToken`, `stopCondition` (Operators). See [request pagination](https://docs.useparagon.com/workflows/requests/request-pagination) and the glossary example.111112---113114## `ResponseStep`115116Only for workflows driven by an HTTP request trigger: set `responseType`, `statusCode`, and `body` (JSON or file). See glossary **ResponseStep**.117118---119120## `FunctionStep`121122Runs sandboxed code: must be **self-contained**; pass data via **`parameters`**. Signatures and allowed **`libraries`** are in Paragon’s [JavaScript libraries](https://docs.useparagon.com/resources/javascript-libraries) doc. Output: **`functionStep.output.result`**. Follow the pattern already used in the repo (string module-style `code` vs inline `code`) for consistency with neighboring workflows.123124---125126## Reusing steps across workflows127128Put shared helpers under a top-level **`src/<something>/`** folder **not** named `integrations` (e.g. `src/common/`), export step factories, import from workflows. See [Reusing steps](https://docs.useparagon.com/paragraph/defining-workflows#reusing-steps). For secrets inside shared code, patterns using `Execution` appear in the same doc page.129130---131132## Checklist before finishing a workflow change1331341. **Trigger** defined and is the start of the **`.nextStep`** chain (or branches/fan-out as designed).1352. Every step in the graph is passed to **`this.register({ … })`** with **unchanged keys** if the workflow already exists in Paragon.1363. **Types**: integration imports align with the folder’s integration (`@useparagon/integrations/...`).1374. **Conditions** use Operators in DNF where required.1385. **Integration** `config.ts` **`workflowDisplayOrder`** includes this workflow class if it should show in the portal.139140For step parameter details not repeated here, prefer the live glossary: [@useparagon/core](https://docs.useparagon.com/paragraph/reference/useparagon-core).