Matcher
The matcher takes two datasets and finds records that correspond to each other. It outputs three arrays: matched pairs, unmatched records from the left side, and unmatched records from the right side.
For three or four datasets that belong to one group, such as a purchase order, its receipts, and its invoices, use the N-Way Match step instead of chaining two-way matches.
Basic Usage
{
"type": "matcher",
"properties": {
"left": "@input.invoices",
"right": "@input.payments",
"matchOn": ["invoice_id"],
"outputMatched": "matched",
"outputUnmatchedLeft": "unmatched_invoices",
"outputUnmatchedRight": "unmatched_payments"
}
}
This matches invoices to payments by exact invoice_id. Records with matching IDs land in @matched. Invoices with no payment go to @unmatched_invoices. Payments with no invoice go to @unmatched_payments.
Properties Reference
| Property | Type | Required | Description |
|---|---|---|---|
left |
array / @path / doc: | Yes | First dataset as an inline array, @path reference, or doc: uploaded document |
right |
array / @path / doc: | Yes | Second dataset as an inline array, @path reference, or doc: uploaded document |
matchOn |
string[] | Yes | Fields that must match exactly |
tolerance |
number | No | Absolute numeric tolerance applied to the amount field. A tolerance of 0.02 means amounts differing by more than $0.02 won't match |
dateWindowDays |
number | No | Date tolerance in days, applied to the date field |
fuzzyThreshold |
number | No | Text similarity threshold from 0 to 100, applied to descriptionKey |
descriptionKey |
string | No | Field name for fuzzy text matching |
rules |
array | No | Custom matching rules |
outputMatched |
string | No | Context key for matched pairs (default: "matched") |
outputUnmatchedLeft |
string | No | Context key for unmatched left records (default: "unmatchedLeft") |
outputUnmatchedRight |
string | No | Context key for unmatched right records (default: "unmatchedRight") |
Matching Criteria
Exact Key Matching (matchOn)
Fields listed in matchOn must match exactly. Records are compared only when their matchOn fields align.
{
"matchOn": ["invoice_id"]
}
Multiple keys create a composite match. Every key must match:
{
"matchOn": ["vendor_id", "invoice_number"]
}
Numeric Tolerance (tolerance)
Allow the amount field to differ by an absolute value. A tolerance of 50 means amounts within $50 of each other are still considered a match.
{
"matchOn": ["invoice_id"],
"tolerance": 50
}
With this configuration, an invoice for $1,000.00 would match a payment from $950.00 to $1,050.00. Smaller tolerances such as 0.02 are valid when you mean two cents.
Date Window (dateWindowDays)
Allow date fields to differ by up to N days:
{
"matchOn": ["invoice_id"],
"dateWindowDays": 3
}
An invoice dated January 10 would match a payment dated January 7 through January 13.
Fuzzy Text Matching (fuzzyThreshold + descriptionKey)
Compare text fields using fuzzy string similarity. The threshold runs from 0 to 100, where 100 is an exact match:
{
"matchOn": ["vendor_id"],
"fuzzyThreshold": 85,
"descriptionKey": "description"
}
This matches records where vendor_id is identical and the description fields are at least 85% similar. Useful for matching line-item descriptions that may be worded differently across systems.
Custom Rules (rules)
Add a compact comparison rule when two records must agree on another field:
{
"matchOn": ["invoice_id"],
"rules": [
{
"field": "left.currency",
"operator": "eq",
"value": "@right.currency"
}
]
}
Each rule uses field, operator, and value. The supported operators are eq, neq, >, >=, <, <=, and contains. Use left.<field> for the left record and @right.<field> for a value from the right record. Use tolerance for an allowed difference on amount.
Output Format
Matched Pairs
Each matched record contains both the left and right record:
[
{
"left": { "invoice_id": "INV-001", "amount": 1000, "vendor": "Acme" },
"right": { "invoice_id": "INV-001", "amount": 1000, "vendor": "Acme Corp" }
}
]
The matcher returns the original records under left and right. Counts and the applied policy are available in the normalized matcher result.
Unmatched Records
Unmatched arrays contain the original records with no modifications:
[
{ "invoice_id": "INV-099", "amount": 5000, "vendor": "NewVendor" }
]
Worked Example
Input:
{
"invoices": [
{ "invoice_id": "INV-001", "amount": 1000.00, "date": "2025-01-10", "description": "Monthly service fee" },
{ "invoice_id": "INV-002", "amount": 2500.00, "date": "2025-01-15", "description": "Equipment rental" },
{ "invoice_id": "INV-003", "amount": 750.00, "date": "2025-01-20", "description": "Consulting hours" }
],
"payments": [
{ "invoice_id": "INV-001", "amount": 1000.00, "date": "2025-01-12", "description": "Monthly service" },
{ "invoice_id": "INV-002", "amount": 2475.00, "date": "2025-01-15", "description": "Equip rental Jan" }
]
}
Matcher configuration:
{
"type": "matcher",
"properties": {
"left": "@input.invoices",
"right": "@input.payments",
"matchOn": ["invoice_id"],
"tolerance": 50,
"dateWindowDays": 3,
"fuzzyThreshold": 80,
"descriptionKey": "description",
"outputMatched": "reconciled",
"outputUnmatchedLeft": "exceptions"
}
}
Results:
@reconciled: INV-001 (exact match), INV-002 (amount difference $25 within tolerance, descriptions 80%+ similar)@exceptions: INV-003 (no matching payment found)
Using Uploaded Documents
Instead of embedding datasets in the execution payload, upload files to Document Storage and reference them with the doc: prefix:
{
"type": "matcher",
"properties": {
"left": "doc:doc_a1b2c3d4e5f6",
"right": "doc:doc_x7y8z9w0v1u2",
"matchOn": ["invoice_id"],
"tolerance": 50,
"outputMatched": "matched",
"outputUnmatchedLeft": "exceptions"
}
}
CSV files resolve to Array<Object> with header rows as keys. JSON files resolve as-is. Pin a specific version with doc:doc_xxx@2 for audit reproducibility.
Working with larger datasets
The matcher keeps the same input and output contract across supported dataset sizes. For large files, upload CSV or JSON data and pass doc: references instead of placing every row in the workflow execution request.
Matcher as a front-end step. Many operational workflows put a matcher near the start. Matched records continue through deterministic processing. Exceptions can move to an agent or a person for review.