Workflows Explained
Understanding workflows as the core orchestration layer in bem
Workflows are the core abstraction that enables bem to power sophisticated user interfaces and business processes at scale. A workflow orchestrates multiple functions into a unified processing pipeline with a single entry point, allowing you to build complex data transformations declaratively.
What is a Workflow?
A workflow is a versioned, reusable orchestration layer that wraps one or more functions into a cohesive processing unit. Think of it as a directed graph where:
- Nodes are functions (Transform, Route, Split, Join, Enrich, etc.)
- Edges are relationships that define how data flows between functions
Workflow
+--------------------------------------------------------+
| |
| +-----------+ +-----------+ |
| | Transform | ----> | Enrich | |
| | (main) | | | |
| +-----------+ +-----------+ |
| |
+--------------------------------------------------------+
^
|
Input (PDF, email, JSON, etc.)When you call a workflow, bem automatically executes all functions in the correct order, managing state and data flow between them.
Why Use Workflows?
Single Entry Point
Instead of managing multiple function calls and tracking their dependencies yourself, workflows provide a single API endpoint. Call the workflow once, and bem handles the orchestration:
curl -X POST https://api.bem.ai/v2/calls \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"calls": [{
"workflowName": "invoice-processing",
"input": { "singleFile": { "inputType": "pdf", "inputContent": "..." } }
}]
}'Versioned Configuration
Workflows are versioned independently from the functions they contain. Each time you update a workflow's structure (changing functions or relationships), a new version is created. This means:
- Existing integrations continue working on their version
- You can test new workflow configurations without disrupting production
- Rollback is straightforward—just point to a previous version
Unified Monitoring
Track the entire processing pipeline through a single workflow call. The response includes all constituent function calls, their statuses, and outputs:
{
"call": {
"callID": "wc_abc123",
"callType": "workflow",
"status": "completed",
"workflowName": "invoice-processing",
"functionCalls": [
{ "functionName": "invoice-extractor", "status": "completed", "transformedContent": {...} },
{ "functionName": "sku-matcher", "status": "completed", "transformedContent": {...} }
]
}
}Workflow Structure
Every workflow has two key components:
1. Main Function
The main function is the entry point—the first function that receives input when the workflow is called. It can be any function type:
{
"name": "invoice-processing",
"mainFunction": {
"name": "invoice-extractor"
}
}2. Relationships
Relationships define how functions connect. Each relationship specifies a source function and a destination function, creating the processing graph:
{
"relationships": [
{
"sourceFunction": { "name": "invoice-extractor" },
"destinationFunction": { "name": "sku-matcher" }
}
]
}The output of the source function becomes the input for the destination function.
Common Workflow Patterns
Sequential Pipeline
Chain functions in sequence for multi-step processing:
Input --> Transform --> Enrich --> Payload ShapingExample: Extract invoice data, match to product catalog, then format for ERP system.
{
"name": "invoice-to-erp",
"mainFunction": { "name": "invoice-extractor" },
"relationships": [
{
"sourceFunction": { "name": "invoice-extractor" },
"destinationFunction": { "name": "product-matcher" }
},
{
"sourceFunction": { "name": "product-matcher" },
"destinationFunction": { "name": "erp-formatter" }
}
]
}Branching with Route
Use Route functions to direct data down different paths based on classification:
+--> Invoice Transform
|
Input --> Route ----+--> Receipt Transform
|
+--> PO TransformExample: Classify incoming documents and process each type differently.
{
"name": "document-processor",
"mainFunction": { "name": "document-classifier" },
"relationships": [
{
"sourceFunction": { "name": "document-classifier" },
"destinationName": "invoice",
"destinationFunction": { "name": "invoice-extractor" }
},
{
"sourceFunction": { "name": "document-classifier" },
"destinationName": "receipt",
"destinationFunction": { "name": "receipt-extractor" }
},
{
"sourceFunction": { "name": "document-classifier" },
"destinationName": "purchase_order",
"destinationFunction": { "name": "po-extractor" }
}
]
}The destinationName field maps to the route names defined in your Route function configuration.
Split and Process
Handle multi-document files by splitting and processing each piece:
+--> Doc 1 --> Transform A
|
PDF --> Split-+--> Doc 2 --> Transform B
|
+--> Doc 3 --> Transform CExample: A PDF containing multiple shipment documents, each needing extraction.
{
"name": "shipment-bundle-processor",
"mainFunction": { "name": "shipment-splitter" },
"relationships": [
{
"sourceFunction": { "name": "shipment-splitter" },
"destinationName": "bill_of_lading",
"destinationFunction": { "name": "bol-extractor" }
},
{
"sourceFunction": { "name": "shipment-splitter" },
"destinationName": "commercial_invoice",
"destinationFunction": { "name": "commercial-invoice-extractor" }
},
{
"sourceFunction": { "name": "shipment-splitter" },
"destinationName": "packing_list",
"destinationFunction": { "name": "packing-list-extractor" }
}
]
}Aggregation with Join
Combine outputs from multiple sources into a unified result:
Source A --+
|
Source B --+--> Join --> Unified Transform Output
|
Source C --+Example: Merge data from multiple related documents into a comprehensive record.
Function Reference Options
When specifying functions in workflows, you have flexibility in how you reference them:
| Reference Style | Example | Description |
|---|---|---|
| By name (latest version) | { "name": "invoice-extractor" } | Uses the current version |
| By ID (latest version) | { "id": "f_abc123" } | Uses the current version |
| By name with version | { "name": "invoice-extractor", "versionNum": 2 } | Pins to specific version |
| By ID with version | { "id": "f_abc123", "versionNum": 2 } | Pins to specific version |
Pinning to specific versions is useful when you need deterministic behavior and want to control when updates propagate.
Creating a Workflow
curl -X POST https://api.bem.ai/v2/workflows \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"name": "invoice-processing",
"displayName": "Invoice Processing Pipeline",
"tags": ["finance", "invoices"],
"mainFunction": {
"name": "invoice-extractor"
},
"relationships": [
{
"sourceFunction": { "name": "invoice-extractor" },
"destinationFunction": { "name": "sku-matcher" }
}
]
}'Configuration Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique identifier (alphanumeric, hyphens, underscores) |
displayName | string | No | Human-readable name for the UI |
tags | string[] | No | Tags for organizing workflows |
mainFunction | object | Yes | The entry point function |
relationships | array | No | Function-to-function connections |
Updating a Workflow
Updates create a new version, preserving the previous configuration:
curl -X PATCH https://api.bem.ai/v2/workflows/invoice-processing \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"mainFunction": { "name": "invoice-extractor" },
"relationships": [
{
"sourceFunction": { "name": "invoice-extractor" },
"destinationFunction": { "name": "sku-matcher" }
},
{
"sourceFunction": { "name": "sku-matcher" },
"destinationFunction": { "name": "erp-formatter" }
}
]
}'When updating structure, you must provide both mainFunction and relationships together.
Workflow Versions
List all versions of a workflow:
curl https://api.bem.ai/v2/workflows/invoice-processing/versions \
-H "x-api-key: YOUR_API_KEY"Get a specific version:
curl https://api.bem.ai/v2/workflows/invoice-processing/versions/2 \
-H "x-api-key: YOUR_API_KEY"Executing Workflows
Call a workflow using the unified Calls API:
curl -X POST https://api.bem.ai/v2/calls \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"calls": [
{
"workflowName": "invoice-processing",
"callReferenceID": "my-tracking-id-001",
"input": {
"singleFile": {
"inputType": "pdf",
"inputContent": "JVBERi0xLjQK..."
}
}
}
]
}'The callReferenceID is your custom identifier for tracking this execution in your systems.
Workflows vs. Ad-hoc Function Calls
You can call functions directly without a workflow using ad-hoc calls:
curl -X POST https://api.bem.ai/v2/calls \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"calls": [
{
"functionName": "invoice-extractor",
"input": { ... }
}
]
}'When to use workflows:
- Multiple functions need to execute in sequence
- You need branching logic (Route functions)
- You want unified monitoring across a pipeline
- You're building production integrations that may evolve
When to use ad-hoc calls:
- Single-function processing
- Testing individual functions
- Simple, one-off transformations
Best Practices
Start Simple, Extend Incrementally
Begin with a single-function workflow. As requirements grow, add functions and relationships:
- Create workflow with just a Transform function
- Add Enrich to augment data with your catalogs
- Add Payload Shaping to format for downstream systems
Use Meaningful Names
Workflow and function names should describe their purpose:
invoice-processing(workflow)invoice-extractor(transform function)product-catalog-matcher(enrich function)
Leverage Tags for Organization
Use tags to categorize workflows by domain, team, or use case:
{
"tags": ["finance", "ap-automation", "production"]
}Pin Versions for Stability
In production, consider pinning function versions to prevent unexpected changes:
{
"mainFunction": { "name": "invoice-extractor", "versionNum": 3 }
}