← Files Zuora Coding AgentARCHIVED FILE

references/workflow-task-catalog.md

11.9 KB · Oct 2, 2026 · 00:30 UTC

↓ Download file

# Zuora Workflow Task Catalog

Canonical list of supported Zuora Workflow `action_type` values. This is a narrative reference; the machine-readable source of truth for composing tasks is `workflow-task-templates.json`, which pairs every entry below with a ready-to-use JSON template, parameter rules, enums, and `required_at_import` columns.

Distilled from `Task.action_type` in `~/Workspace/workflow/rails/app/models/task.rb:636-681`.

When picking an `action_type`, follow this order:

1. Scan the relevant category below.
2. Read the matching entry in `workflow-task-templates.json` for the exact JSON shape.
3. Read the matching entry in `workflow-triggers-and-linkages.md` for the allowed `linkage_type` hooks.
4. When in doubt, prefer the most common Tier 1 task; do not invent an `action_type`.

### Data-read selection guide

| Situation | Correct choice | Notes |
| --- | --- | --- |
| Fetch ≤ 2000 rows into `Data.<object>` for downstream Liquid/tasks | `Query` | Synchronous; result lives in `Data.*`. Default choice. |
| Fetch > 2000 rows or need a CSV/ZIP for downstream `Iterate` | `Export` | SOAP bulk export; produces a file in `Data.Files.*`. |
| Large async ZOQL export (stateful/stateless AQuA job) | `Data::Aqua` | Same ZOQL language as `Query`/`Export`; async; always produces a file — adds a download step before data is usable. |
| Joined/nested read that fits in GraphQL | `GraphQuery` | Result lives in `Data.<baseObject>`. |
| Run SQL-style row queries for workflow logic | `Data::Link` / Data Query | Supported SQL-style query surface; result rows can feed downstream `Iterate` / `Callout` logic. |

**Default to `Query`.** Only reach for `Data::Aqua` when the dataset is too large for `Export` or you need AQuA's stateful/incremental extraction. `Data::Aqua` uses the same ZOQL query language as `Query`/`Export` — it is **not** SQL and does not support JOINs. Use `Data::Link` / Data Query when the workflow needs SQL-style row results.

### Transform/compute selection guide (avoid overusing JavaScript)

| Situation | Correct choice | Wrong choice |
| --- | --- | --- |
| Compute a value, reshape `Data.*`, conditional logic | `Logic::Liquid` | `Script::JavaScript` |
| Transform JSON structure or reshape a response payload | `Logic::JSONTransform` | `Script::JavaScript` |
| Parse/transform XML | `Logic::XMLTransform` | `Script::JavaScript` |
| Require Node.js libraries, complex business logic, or computation not possible in Liquid | `Script::JavaScript` | — |

**`Script::JavaScript` is a last resort**, not the default. It is OPAQUE (downstream `Data.*` references require `_expected_response_schema` or `_opaque_trusted`), has a hard 20 s default timeout (5 min max), and produces output that the linter cannot statically verify. Always ask: can `Logic::Liquid` or `Logic::JSONTransform` do this? If yes, use those instead.

## Tier 1 — the 80% cases

These thirteen action types cover the vast majority of production workflows. Know them cold.

| Action type   | Purpose                                                                        |
| ------------- | ------------------------------------------------------------------------------ |
| `Query`       | SOAP Query a Zuora object (up to 2000 rows) into `Data.<object>`.              |
| `Export`      | SOAP bulk export (> 2000 rows) emitting a CSV file.                            |
| `Iterate`     | Fan out over a collection with a `For Each` hook.                              |
| `If`          | Binary branch on a Liquid boolean expression (`True` / `False` hooks).         |
| `Logic::Case` | Multi-way branch keyed by a Liquid clause (`Case_1`, `Case_2`, …, `Case_Else`).|
| `Logic::Liquid` | Transform or compute values into `Data.Liquid` or `Data.<placement>`.        |
| `Logic::Merge`| Collapse parallel branches back into a single path after fan-out.              |
| `Callout`     | Sync HTTP callout to any URL. The workhorse for external integrations.         |
| `Email`       | Liquid-templated email via the workflow notification service.                  |
| `Update`      | SOAP update a Zuora object by `object_id`.                                     |
| `Create`      | SOAP create a Zuora object.                                                    |
| `Delete`      | SOAP delete a Zuora object by `object_id`.                                     |
| `Delay`       | Sleep for N seconds, or until a specific absolute time.                        |

## Tier 2 — specialist tasks by category

### Retrieve (data in)

- `GraphQuery` — GraphQL queries with variables (best for joined or nested reads).
- `Data::Aqua` — Async Queuing API (AQuA) stateful/stateless extracts.
- `Data::BillingPreviewRun` — run a billing preview against accounts.
- `Data::Link` — Data Query / SQL-style row query whose results can feed downstream Workflow tasks.

### Logic / Transform

- `Logic::Lambda` — invoke AWS Lambda synchronously.
- `Logic::CSVTranslator` — filter/merge/sort CSV, or convert to XML / JSON.
- `Logic::XMLTransform` — XSLT template transforms (CSV / XML / TXT / HTML).
- `Logic::JSONTransform` — JSONata or CSV-liquid transforms.
- `Logic::ResponseFormatter` — format SYNC-mode UI responses (UI / UIACTION / REALTIME / SYNC_UI_ACTION / DATASTREAM workflows only).
- `Script::JavaScript` — arbitrary Node.js code blocks.

### CRUD

- `Create`, `Update`, `Delete` — standard SOAP object CRUD (Tier 1).

### Custom Objects

- `CustomObject::Create`, `CustomObject::Update`, `CustomObject::Delete`, `CustomObject::Query`.

### Amendments

- `NewProduct`, `RemoveProduct`, `Suspend`, `Resume`, `Cancel` — legacy SOAP subscription amendments. Each requires `object_id` = subscription id.
- For subscription cancellation on the new API stack, prefer a Zuora-authorized `Callout` to Orders API (`{{ Credentials.zuora.rest_endpoint }}orders`) with an `orderActions[]` entry whose `type` is `"CancelSubscription"`. Use `Cancel` only for explicitly legacy amendment workflows.

### Billing

- `Billing::BillRun` — create / batch / replicate a bill run. Use a custom Zuora `Callout` to `{{ Credentials.zuora.rest_endpoint }}bill-runs` when the requirement needs bill-run filters this OOTB task does not support (for example account-number, batch-number, or APM/PRPC filters). Do not use the legacy object CRUD endpoint `/object/bill-run` for Create a bill run.
- `InvoiceGenerate` — SOAP InvoiceGenerate for a single account.
- `WriteOff` — settlement or amendment-based invoice write-off.
- `Billing::ReverseInvoice` — reverse an invoice.
- `Billing::CurrencyConversion` — pull FX rates and rewrite currency fields.
- `Billing::CustomBillingDocument` — render a custom billing document from a PDF template.

### Payment

- `Payment::PaymentRun` — create/update/get/delete a payment run.
- `Payment::GatewayReconciliation` — settle/reverse/reject gateway records.

### Usage

- `Usage::ImportUsage` — bulk-import usage data from a file label.

### Notifications / Messaging

- `Email` — primary (Tier 1).
- `Callout` — primary (Tier 1).
- `AsynchronousCallout` — fire-and-poll for long-running endpoints.
- `Notifications::SMS` — send SMS via the notification service.
- `Notifications::Kafka` — publish to an internal Kafka topic (internal access only).

### File / Attachments

- `Attachment` — attach a file to a Zuora object (`object` + `object_id` required).
- `File::CustomPDF::CustomDocument` — render a PDF from a template.
- `File::DownloadFile` — download file from Zuora or an external URL.
- `File::ZuoraImport` — legacy bulk-import API.
- `File::FileOperations` — Zip / Unzip / Encrypt / Decrypt / SplitCSV / PDF / ZipFiles.
- `File::FileStreamingUpload` — stream file to S3.
- `File::BulkDataLoader` — bulk-data-loader job (insert / update / upsert / delete).

### Reporting

- `Reporting::RunReport` — execute a Zuora Report (`object_id` = report id).
- `Reporting::OracleFusionReport` — run an Oracle Fusion report.

### Upload / Download

- `Upload::FTP`, `Upload::SFTP`, `Upload::S3` — upload file collections to the matching transport.
- `Download::SFTP`, `Download::S3` — download remote files.

### UI (only inside UI / UIACTION / SYNC_UI_ACTION workflows)

- `UI::Page` — render a named page; each route becomes a `Page:<route>` hook.
- `UI::Stop` — halt an interactive workflow.
- `UI::WebShare` — web-share action; exposes `Webshare:<route>`, `Upload`, `Timeout`, `Success`, `Failure`.

### Execute / Manual / Time

- `Execute::WorkflowTask` — invoke another workflow as a subroutine.
- `Approval` — human approval step with `Approve`, `Reject`, `Failure` hooks.
- `Delay` — sleep (Tier 1).

### Usage Mediation (lowercase hooks!)

- `UsageMediation::Source`
- `UsageMediation::Watermark`
- `UsageMediation::Filter`
- `UsageMediation::Map`
- `UsageMediation::Group`
- `UsageMediation::Sink`
- `Mediation::SendEvents` — downstream dispatch.

All mediation tasks use the hooks `next` and `error` (lowercase) instead of `Success` / `Failure`. The linter understands this exception.

## Format pitfalls

The Rails backend silently accepts many near-valid JSONs but fails at runtime. The linter (see `scripts/lint-workflow-json.js`) catches these explicitly.

1. **Booleans as strings.** Every `strict_variables`, `disable_validation`, `preview_only`, `encrypt`, etc. parameter is parsed with `to_bool`. Always emit the string `"true"` or `"false"`, never JSON `true` / `false`.

2. **`parameters: {}` is required on every task.** `Task.import` calls `parameters.merge!` — a missing or null `parameters` raises `NoMethodError` on import.

3. **`required_at_import` columns are validated on import.** These are top-level task attributes (not inside `parameters`) backed by ActiveRecord `validates :column, presence: true`. Examples: `Update` needs `object` and `object_id`; `Query` needs `object`. Consult `workflow-task-templates.json` → `required_at_import` for the exact list per `action_type`.

4. **`Logic::Case` keys must be `Case_1`, `Case_2`, …, `Case_Else`.** Rails renumbers non-canonical keys in `before_save` and destroys any linkage whose `linkage_type` doesn't survive the rename. The composer pre-normalizes these keys so the rename is a no-op.

5. **`Iterate` hook name is `For Each` (with the space).** Not `Iterate`, not `ForEach`, not `for_each`. Same goes for `Complete` on the completion hook.

6. **Usage mediation hooks are lowercase (`next`, `error`).**

7. **`linkage_type` is not server-validated against source-task hooks.** The backend does not check that a `Success` linkage actually emanates from a task that publishes `Success`. The linter is the only line of defense.

8. **For-Each / Merge rule.** No `For Each` linkage may sit on any path from the workflow start to a `Logic::Merge` task. The server DFS (`Linkage#avoid_for_each_linkage_before_merge_task`) catches it at save time but is slow to reproduce; the linter does a cheap path-substring check.

9. **`workflow.type` must be the literal string `"Workflow::Setup"`.** The server overrides this anyway, but writing it correctly keeps the file diff-stable and matches `Workflow::Setup#export` output.

10. **Status-code arrays on callouts are arrays of strings.** `"status_codes": ["200"]`, not `"status_codes": [200]`.

11. **Non-empty `tasks` and `linkages`.** Import rejects payloads whose `tasks` or `linkages` arrays are empty, even if the workflow is meant to be a placeholder.

12. **Start linkage.** Every workflow needs exactly one linkage where `linkage_type = "Start"`, `source_workflow_id = workflow.id`, `source_task_id = null`, and `target_task_id` points at the entry task.

## Cross-references

- Linkages, triggers, call types: `workflow-triggers-and-linkages.md`
- Liquid scopes and filters: `workflow-liquid.md`
- Three end-to-end annotated examples: `workflow-examples.md`
- Composition algorithm and composer contract: `workflow-patterns.md`
- Machine-readable templates: `workflow-task-templates.json`
- Machine-readable enums: `workflow-enums.json`
- Canonical empty envelope: `workflow-skeleton.json`

SHA-256: 8ec971f33c2fe023a8d7059159bd5e41cfea1909a310324feb0bd5abc5d3dff6