from retab import Retab
client = Retab()
# Manual inputs: hand-write the handle inputs the block should run with.
test = client.workflows.tests.create(
workflow_id="wf_abc123xyz",
target={"type": "block", "block_id": "block_extract_invoice"},
source={
"type": "manual",
"handle_inputs": {
"input-document-0": {
"type": "file",
"file_id": "file_invoice_q1",
},
},
},
assertion={
"target": {"output_handle_id": "output-json-0", "path": "total"},
"condition": {"kind": "equals", "expected": 1234.56},
},
name="Q1 invoice total",
)
# Run-step inputs: replay the inputs the block ACTUALLY received in a run.
test = client.workflows.tests.create(
workflow_id="wf_abc123xyz",
target={"type": "block", "block_id": "block_extract_invoice"},
source={
"type": "run_step",
"run_id": "wfrun_def456",
"step_id": "block_extract_invoice",
},
assertion={
"target": {"output_handle_id": "output-json-0", "path": "vendor.name"},
"condition": {"kind": "contains", "expected": "Acme"},
},
)
print(f"Test created: {test.id}")
import { Retab } from "@retab/node";
const client = new Retab({ apiKey: process.env.RETAB_API_KEY });
const test = await client.workflows.tests.create("wf_abc123xyz", { type: "block", blockId: "block_extract_invoice" }, {
type: "manual",
handleInputs: {
"input-document-0": {
type: "file",
document: {
id: "file_invoice_q1",
filename: "invoice.pdf",
mimeType: "application/pdf",
},
},
},
}, {
target: { outputHandleId: "output-json-0", path: "total" },
condition: { kind: "equals", expected: 1234.56 },
}, "Q1 invoice total");
console.log(`Test created: ${test.id}`);
package main
import (
"context"
"fmt"
"log"
retab "github.com/retab-dev/retab/clients/go"
)
func ptr[T any](v T) *T { return &v }
func main() {
ctx := context.Background()
client, err := retab.NewClient("")
if err != nil {
log.Fatal(err)
}
// Manual inputs: hand-write the handle inputs the block should run with.
test, err := client.Workflows.Tests.Create(ctx, &retab.WorkflowTestsCreateParams{
WorkflowID: "wf_abc123xyz",
Target: retab.WorkflowTestBlockTarget{
Type: ptr("block"),
BlockID: "block_extract_invoice",
},
Source: retab.WorkflowTestSourceFromManualWorkflowTestSource(retab.ManualWorkflowTestSource{
Type: ptr("manual"),
}),
Assertion: retab.AssertionSpec{
Target: retab.OutputTarget{OutputHandleID: "output-json-0", Path: ptr("total")},
Condition: retab.ConditionFromExistCondition(retab.ExistCondition{Kind: ptr("exists")}),
},
Name: ptr("Q1 invoice total"),
})
if err != nil {
log.Fatal(err)
}
// Run-step inputs: replay the inputs the block actually received in a run.
runStepTest, err := client.Workflows.Tests.Create(ctx, &retab.WorkflowTestsCreateParams{
WorkflowID: "wf_abc123xyz",
Target: retab.WorkflowTestBlockTarget{
Type: ptr("block"),
BlockID: "block_extract_invoice",
},
Source: retab.WorkflowTestSourceFromManualWorkflowTestSource(retab.ManualWorkflowTestSource{
Type: ptr("manual"),
}),
Assertion: retab.AssertionSpec{
Target: retab.OutputTarget{OutputHandleID: "output-json-0", Path: ptr("vendor.name")},
Condition: retab.ConditionFromExistCondition(retab.ExistCondition{Kind: ptr("exists")}),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Test created: %s\n", test.ID)
_ = runStepTest
}
require 'retab'
client = Retab::Client.new(api_key: ENV['RETAB_API_KEY'])
# Manual inputs: hand-write the handle inputs the block should run with.
test = client.workflow_tests.create(
workflow_id: 'wf_abc123xyz',
target: { type: 'block', block_id: 'block_extract_invoice' },
source: {
type: 'manual',
handle_inputs: {
'input-document-0' => { type: 'file', file_id: 'file_invoice_q1' },
},
},
assertion: {
target: { output_handle_id: 'output-json-0', path: 'total' },
condition: { kind: 'equals', expected: 1234.56 },
},
name: 'Q1 invoice total',
)
# Run-step inputs: replay the inputs the block ACTUALLY received in a run.
test = client.workflow_tests.create(
workflow_id: 'wf_abc123xyz',
target: { type: 'block', block_id: 'block_extract_invoice' },
source: {
type: 'run_step',
run_id: 'wfrun_def456',
step_id: 'block_extract_invoice',
},
assertion: {
target: { output_handle_id: 'output-json-0', path: 'vendor.name' },
condition: { kind: 'contains', expected: 'Acme' },
},
)
puts "Test created: #{test.id}"
use retab::models::{
AssertionSpec, ContainCondition, CreateWorkflowTestRequest, OutputTarget,
RunStepWorkflowTestSource, WorkflowTestBlockTarget,
};
use retab::resources::workflow_tests::CreateParams;
use retab::Retab;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Retab::new(std::env::var("RETAB_API_KEY")?);
let mut source = RunStepWorkflowTestSource::new("wfrun_def456");
source.step_id = Some("block_extract_invoice".into());
let mut target = OutputTarget::new("output-json-0");
target.path = Some("vendor.name".into());
let condition = ContainCondition {
kind: None,
expected: serde_json::json!("Acme"),
};
let body = CreateWorkflowTestRequest::new(
"wf_abc123xyz",
WorkflowTestBlockTarget::new("block_extract_invoice"),
source.into(),
AssertionSpec::new(target, condition.into()),
);
let test = client
.workflows()
.tests()
.create(CreateParams::new(body))
.await?;
println!("Test created: {}", test.id);
Ok(())
}
<?php
require 'vendor/autoload.php';
use Retab\Client;
use Retab\Resource\AssertionSpec;
use Retab\Resource\ExistCondition;
use Retab\Resource\JsonHandleInput;
use Retab\Resource\ManualWorkflowTestSource;
use Retab\Resource\OutputTarget;
use Retab\Resource\WorkflowTestBlockTarget;
$client = new Client(apiKey: getenv('RETAB_API_KEY'));
$result = $client->workflowTests()->create(
workflowId: 'wf_abc123',
target: new WorkflowTestBlockTarget(blockId: 'block_extract_invoice'),
source: new ManualWorkflowTestSource(handleInputs: [
'input-json-0' => new JsonHandleInput(data: ['invoice_number' => 'INV-001']),
]),
assertion: new AssertionSpec(
target: new OutputTarget(outputHandleId: 'output-json-0', path: 'invoice_number'),
condition: new ExistCondition(),
),
name: 'Invoice number exists',
);
print_r($result);
using Retab;
using RetabClient = Retab.Retab;
var apiKey = Environment.GetEnvironmentVariable("RETAB_API_KEY")!;
var client = new RetabClient(apiKey);
var result = await client.Workflows.Tests.CreateAsync(new WorkflowTestsCreateOptions());
Console.WriteLine(result);
import com.retab.RetabClient;
public final class Example {
public static void main(String[] args) throws Exception {
RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));
var result = client.workflows().tests().create("wf_abc123", null, null, "Invoice Processing", null);
System.out.println(result);
}
}
curl -X 'POST' \
'https://api.retab.com/v1/workflows/tests' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <your-api-key>' \
-d '{
"workflow_id": "wf_abc123xyz",
"target": { "type": "block", "block_id": "block_extract_invoice" },
"source": {
"type": "manual",
"handle_inputs": {
"input-document-0": { "type": "file", "file_id": "file_invoice_q1" }
}
},
"assertion": {
"target": { "output_handle_id": "output-json-0", "path": "total" },
"condition": { "kind": "equals", "expected": 1234.56 }
},
"name": "Q1 invoice total"
}'
{
"id": "wfnodetest_hsLEQiM61ez9Piv147MWk",
"workflow_id": "wf_abc123xyz",
"target": {
"type": "block",
"block_id": "block_extract_invoice"
},
"source": {
"type": "manual",
"handle_inputs": {
"input-document-0": {
"type": "file",
"file_id": "file_invoice_q1"
}
}
},
"name": "Q1 invoice total",
"assertion": {
"id": "assert_xyz",
"target": { "output_handle_id": "output-json-0", "path": "total" },
"condition": { "kind": "equals", "expected": 1234.56 },
"label": null
},
"assertion_schema_dep": {
"schema_path": "total",
"subtree_hash": "7d79dd764ab6548d",
"depends_on_root": false
},
"assertion_drift_status": null,
"schema_drift": "unknown",
"schema_drift_detail": null,
"validation_status": "valid",
"validation_issues": [],
"latest_run_summary": null,
"latest_passing_run_summary": null,
"latest_failing_run_summary": null,
"created_at": "2026-05-01T14:30:00Z",
"updated_at": "2026-05-01T14:30:00Z"
}
{
"detail": "assertion is required for workflow tests."
}
{
"detail": "Workflow test not found: <block_id>"
}
Create Workflow Test
Create a workflow test.
Pins an expected outcome for one block in a workflow. Provide the
workflow_id, the target block, an assertion describing the expected
output, and a source of test inputs (explicit handle inputs or a capture
from a prior run/step). Returns the created test with status 201.
from retab import Retab
client = Retab()
# Manual inputs: hand-write the handle inputs the block should run with.
test = client.workflows.tests.create(
workflow_id="wf_abc123xyz",
target={"type": "block", "block_id": "block_extract_invoice"},
source={
"type": "manual",
"handle_inputs": {
"input-document-0": {
"type": "file",
"file_id": "file_invoice_q1",
},
},
},
assertion={
"target": {"output_handle_id": "output-json-0", "path": "total"},
"condition": {"kind": "equals", "expected": 1234.56},
},
name="Q1 invoice total",
)
# Run-step inputs: replay the inputs the block ACTUALLY received in a run.
test = client.workflows.tests.create(
workflow_id="wf_abc123xyz",
target={"type": "block", "block_id": "block_extract_invoice"},
source={
"type": "run_step",
"run_id": "wfrun_def456",
"step_id": "block_extract_invoice",
},
assertion={
"target": {"output_handle_id": "output-json-0", "path": "vendor.name"},
"condition": {"kind": "contains", "expected": "Acme"},
},
)
print(f"Test created: {test.id}")
import { Retab } from "@retab/node";
const client = new Retab({ apiKey: process.env.RETAB_API_KEY });
const test = await client.workflows.tests.create("wf_abc123xyz", { type: "block", blockId: "block_extract_invoice" }, {
type: "manual",
handleInputs: {
"input-document-0": {
type: "file",
document: {
id: "file_invoice_q1",
filename: "invoice.pdf",
mimeType: "application/pdf",
},
},
},
}, {
target: { outputHandleId: "output-json-0", path: "total" },
condition: { kind: "equals", expected: 1234.56 },
}, "Q1 invoice total");
console.log(`Test created: ${test.id}`);
package main
import (
"context"
"fmt"
"log"
retab "github.com/retab-dev/retab/clients/go"
)
func ptr[T any](v T) *T { return &v }
func main() {
ctx := context.Background()
client, err := retab.NewClient("")
if err != nil {
log.Fatal(err)
}
// Manual inputs: hand-write the handle inputs the block should run with.
test, err := client.Workflows.Tests.Create(ctx, &retab.WorkflowTestsCreateParams{
WorkflowID: "wf_abc123xyz",
Target: retab.WorkflowTestBlockTarget{
Type: ptr("block"),
BlockID: "block_extract_invoice",
},
Source: retab.WorkflowTestSourceFromManualWorkflowTestSource(retab.ManualWorkflowTestSource{
Type: ptr("manual"),
}),
Assertion: retab.AssertionSpec{
Target: retab.OutputTarget{OutputHandleID: "output-json-0", Path: ptr("total")},
Condition: retab.ConditionFromExistCondition(retab.ExistCondition{Kind: ptr("exists")}),
},
Name: ptr("Q1 invoice total"),
})
if err != nil {
log.Fatal(err)
}
// Run-step inputs: replay the inputs the block actually received in a run.
runStepTest, err := client.Workflows.Tests.Create(ctx, &retab.WorkflowTestsCreateParams{
WorkflowID: "wf_abc123xyz",
Target: retab.WorkflowTestBlockTarget{
Type: ptr("block"),
BlockID: "block_extract_invoice",
},
Source: retab.WorkflowTestSourceFromManualWorkflowTestSource(retab.ManualWorkflowTestSource{
Type: ptr("manual"),
}),
Assertion: retab.AssertionSpec{
Target: retab.OutputTarget{OutputHandleID: "output-json-0", Path: ptr("vendor.name")},
Condition: retab.ConditionFromExistCondition(retab.ExistCondition{Kind: ptr("exists")}),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Test created: %s\n", test.ID)
_ = runStepTest
}
require 'retab'
client = Retab::Client.new(api_key: ENV['RETAB_API_KEY'])
# Manual inputs: hand-write the handle inputs the block should run with.
test = client.workflow_tests.create(
workflow_id: 'wf_abc123xyz',
target: { type: 'block', block_id: 'block_extract_invoice' },
source: {
type: 'manual',
handle_inputs: {
'input-document-0' => { type: 'file', file_id: 'file_invoice_q1' },
},
},
assertion: {
target: { output_handle_id: 'output-json-0', path: 'total' },
condition: { kind: 'equals', expected: 1234.56 },
},
name: 'Q1 invoice total',
)
# Run-step inputs: replay the inputs the block ACTUALLY received in a run.
test = client.workflow_tests.create(
workflow_id: 'wf_abc123xyz',
target: { type: 'block', block_id: 'block_extract_invoice' },
source: {
type: 'run_step',
run_id: 'wfrun_def456',
step_id: 'block_extract_invoice',
},
assertion: {
target: { output_handle_id: 'output-json-0', path: 'vendor.name' },
condition: { kind: 'contains', expected: 'Acme' },
},
)
puts "Test created: #{test.id}"
use retab::models::{
AssertionSpec, ContainCondition, CreateWorkflowTestRequest, OutputTarget,
RunStepWorkflowTestSource, WorkflowTestBlockTarget,
};
use retab::resources::workflow_tests::CreateParams;
use retab::Retab;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Retab::new(std::env::var("RETAB_API_KEY")?);
let mut source = RunStepWorkflowTestSource::new("wfrun_def456");
source.step_id = Some("block_extract_invoice".into());
let mut target = OutputTarget::new("output-json-0");
target.path = Some("vendor.name".into());
let condition = ContainCondition {
kind: None,
expected: serde_json::json!("Acme"),
};
let body = CreateWorkflowTestRequest::new(
"wf_abc123xyz",
WorkflowTestBlockTarget::new("block_extract_invoice"),
source.into(),
AssertionSpec::new(target, condition.into()),
);
let test = client
.workflows()
.tests()
.create(CreateParams::new(body))
.await?;
println!("Test created: {}", test.id);
Ok(())
}
<?php
require 'vendor/autoload.php';
use Retab\Client;
use Retab\Resource\AssertionSpec;
use Retab\Resource\ExistCondition;
use Retab\Resource\JsonHandleInput;
use Retab\Resource\ManualWorkflowTestSource;
use Retab\Resource\OutputTarget;
use Retab\Resource\WorkflowTestBlockTarget;
$client = new Client(apiKey: getenv('RETAB_API_KEY'));
$result = $client->workflowTests()->create(
workflowId: 'wf_abc123',
target: new WorkflowTestBlockTarget(blockId: 'block_extract_invoice'),
source: new ManualWorkflowTestSource(handleInputs: [
'input-json-0' => new JsonHandleInput(data: ['invoice_number' => 'INV-001']),
]),
assertion: new AssertionSpec(
target: new OutputTarget(outputHandleId: 'output-json-0', path: 'invoice_number'),
condition: new ExistCondition(),
),
name: 'Invoice number exists',
);
print_r($result);
using Retab;
using RetabClient = Retab.Retab;
var apiKey = Environment.GetEnvironmentVariable("RETAB_API_KEY")!;
var client = new RetabClient(apiKey);
var result = await client.Workflows.Tests.CreateAsync(new WorkflowTestsCreateOptions());
Console.WriteLine(result);
import com.retab.RetabClient;
public final class Example {
public static void main(String[] args) throws Exception {
RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));
var result = client.workflows().tests().create("wf_abc123", null, null, "Invoice Processing", null);
System.out.println(result);
}
}
curl -X 'POST' \
'https://api.retab.com/v1/workflows/tests' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <your-api-key>' \
-d '{
"workflow_id": "wf_abc123xyz",
"target": { "type": "block", "block_id": "block_extract_invoice" },
"source": {
"type": "manual",
"handle_inputs": {
"input-document-0": { "type": "file", "file_id": "file_invoice_q1" }
}
},
"assertion": {
"target": { "output_handle_id": "output-json-0", "path": "total" },
"condition": { "kind": "equals", "expected": 1234.56 }
},
"name": "Q1 invoice total"
}'
{
"id": "wfnodetest_hsLEQiM61ez9Piv147MWk",
"workflow_id": "wf_abc123xyz",
"target": {
"type": "block",
"block_id": "block_extract_invoice"
},
"source": {
"type": "manual",
"handle_inputs": {
"input-document-0": {
"type": "file",
"file_id": "file_invoice_q1"
}
}
},
"name": "Q1 invoice total",
"assertion": {
"id": "assert_xyz",
"target": { "output_handle_id": "output-json-0", "path": "total" },
"condition": { "kind": "equals", "expected": 1234.56 },
"label": null
},
"assertion_schema_dep": {
"schema_path": "total",
"subtree_hash": "7d79dd764ab6548d",
"depends_on_root": false
},
"assertion_drift_status": null,
"schema_drift": "unknown",
"schema_drift_detail": null,
"validation_status": "valid",
"validation_issues": [],
"latest_run_summary": null,
"latest_passing_run_summary": null,
"latest_failing_run_summary": null,
"created_at": "2026-05-01T14:30:00Z",
"updated_at": "2026-05-01T14:30:00Z"
}
{
"detail": "assertion is required for workflow tests."
}
{
"detail": "Workflow test not found: <block_id>"
}
workflow_id in the request body.
The request body has three parts:
target— the block the test runs against.source— where the inputs come from.manualcarries an explicithandle_inputsmap;run_stepreferences a previous workflow run plus the optional step inside it whose inputs to capture.assertion— required. One assertion per test against one declared output handle (see Workflow Tests for the assertion shape and the available operators).
from retab import Retab
client = Retab()
# Manual inputs: hand-write the handle inputs the block should run with.
test = client.workflows.tests.create(
workflow_id="wf_abc123xyz",
target={"type": "block", "block_id": "block_extract_invoice"},
source={
"type": "manual",
"handle_inputs": {
"input-document-0": {
"type": "file",
"file_id": "file_invoice_q1",
},
},
},
assertion={
"target": {"output_handle_id": "output-json-0", "path": "total"},
"condition": {"kind": "equals", "expected": 1234.56},
},
name="Q1 invoice total",
)
# Run-step inputs: replay the inputs the block ACTUALLY received in a run.
test = client.workflows.tests.create(
workflow_id="wf_abc123xyz",
target={"type": "block", "block_id": "block_extract_invoice"},
source={
"type": "run_step",
"run_id": "wfrun_def456",
"step_id": "block_extract_invoice",
},
assertion={
"target": {"output_handle_id": "output-json-0", "path": "vendor.name"},
"condition": {"kind": "contains", "expected": "Acme"},
},
)
print(f"Test created: {test.id}")
import { Retab } from "@retab/node";
const client = new Retab({ apiKey: process.env.RETAB_API_KEY });
const test = await client.workflows.tests.create("wf_abc123xyz", { type: "block", blockId: "block_extract_invoice" }, {
type: "manual",
handleInputs: {
"input-document-0": {
type: "file",
document: {
id: "file_invoice_q1",
filename: "invoice.pdf",
mimeType: "application/pdf",
},
},
},
}, {
target: { outputHandleId: "output-json-0", path: "total" },
condition: { kind: "equals", expected: 1234.56 },
}, "Q1 invoice total");
console.log(`Test created: ${test.id}`);
package main
import (
"context"
"fmt"
"log"
retab "github.com/retab-dev/retab/clients/go"
)
func ptr[T any](v T) *T { return &v }
func main() {
ctx := context.Background()
client, err := retab.NewClient("")
if err != nil {
log.Fatal(err)
}
// Manual inputs: hand-write the handle inputs the block should run with.
test, err := client.Workflows.Tests.Create(ctx, &retab.WorkflowTestsCreateParams{
WorkflowID: "wf_abc123xyz",
Target: retab.WorkflowTestBlockTarget{
Type: ptr("block"),
BlockID: "block_extract_invoice",
},
Source: retab.WorkflowTestSourceFromManualWorkflowTestSource(retab.ManualWorkflowTestSource{
Type: ptr("manual"),
}),
Assertion: retab.AssertionSpec{
Target: retab.OutputTarget{OutputHandleID: "output-json-0", Path: ptr("total")},
Condition: retab.ConditionFromExistCondition(retab.ExistCondition{Kind: ptr("exists")}),
},
Name: ptr("Q1 invoice total"),
})
if err != nil {
log.Fatal(err)
}
// Run-step inputs: replay the inputs the block actually received in a run.
runStepTest, err := client.Workflows.Tests.Create(ctx, &retab.WorkflowTestsCreateParams{
WorkflowID: "wf_abc123xyz",
Target: retab.WorkflowTestBlockTarget{
Type: ptr("block"),
BlockID: "block_extract_invoice",
},
Source: retab.WorkflowTestSourceFromManualWorkflowTestSource(retab.ManualWorkflowTestSource{
Type: ptr("manual"),
}),
Assertion: retab.AssertionSpec{
Target: retab.OutputTarget{OutputHandleID: "output-json-0", Path: ptr("vendor.name")},
Condition: retab.ConditionFromExistCondition(retab.ExistCondition{Kind: ptr("exists")}),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Test created: %s\n", test.ID)
_ = runStepTest
}
require 'retab'
client = Retab::Client.new(api_key: ENV['RETAB_API_KEY'])
# Manual inputs: hand-write the handle inputs the block should run with.
test = client.workflow_tests.create(
workflow_id: 'wf_abc123xyz',
target: { type: 'block', block_id: 'block_extract_invoice' },
source: {
type: 'manual',
handle_inputs: {
'input-document-0' => { type: 'file', file_id: 'file_invoice_q1' },
},
},
assertion: {
target: { output_handle_id: 'output-json-0', path: 'total' },
condition: { kind: 'equals', expected: 1234.56 },
},
name: 'Q1 invoice total',
)
# Run-step inputs: replay the inputs the block ACTUALLY received in a run.
test = client.workflow_tests.create(
workflow_id: 'wf_abc123xyz',
target: { type: 'block', block_id: 'block_extract_invoice' },
source: {
type: 'run_step',
run_id: 'wfrun_def456',
step_id: 'block_extract_invoice',
},
assertion: {
target: { output_handle_id: 'output-json-0', path: 'vendor.name' },
condition: { kind: 'contains', expected: 'Acme' },
},
)
puts "Test created: #{test.id}"
use retab::models::{
AssertionSpec, ContainCondition, CreateWorkflowTestRequest, OutputTarget,
RunStepWorkflowTestSource, WorkflowTestBlockTarget,
};
use retab::resources::workflow_tests::CreateParams;
use retab::Retab;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Retab::new(std::env::var("RETAB_API_KEY")?);
let mut source = RunStepWorkflowTestSource::new("wfrun_def456");
source.step_id = Some("block_extract_invoice".into());
let mut target = OutputTarget::new("output-json-0");
target.path = Some("vendor.name".into());
let condition = ContainCondition {
kind: None,
expected: serde_json::json!("Acme"),
};
let body = CreateWorkflowTestRequest::new(
"wf_abc123xyz",
WorkflowTestBlockTarget::new("block_extract_invoice"),
source.into(),
AssertionSpec::new(target, condition.into()),
);
let test = client
.workflows()
.tests()
.create(CreateParams::new(body))
.await?;
println!("Test created: {}", test.id);
Ok(())
}
<?php
require 'vendor/autoload.php';
use Retab\Client;
use Retab\Resource\AssertionSpec;
use Retab\Resource\ExistCondition;
use Retab\Resource\JsonHandleInput;
use Retab\Resource\ManualWorkflowTestSource;
use Retab\Resource\OutputTarget;
use Retab\Resource\WorkflowTestBlockTarget;
$client = new Client(apiKey: getenv('RETAB_API_KEY'));
$result = $client->workflowTests()->create(
workflowId: 'wf_abc123',
target: new WorkflowTestBlockTarget(blockId: 'block_extract_invoice'),
source: new ManualWorkflowTestSource(handleInputs: [
'input-json-0' => new JsonHandleInput(data: ['invoice_number' => 'INV-001']),
]),
assertion: new AssertionSpec(
target: new OutputTarget(outputHandleId: 'output-json-0', path: 'invoice_number'),
condition: new ExistCondition(),
),
name: 'Invoice number exists',
);
print_r($result);
using Retab;
using RetabClient = Retab.Retab;
var apiKey = Environment.GetEnvironmentVariable("RETAB_API_KEY")!;
var client = new RetabClient(apiKey);
var result = await client.Workflows.Tests.CreateAsync(new WorkflowTestsCreateOptions());
Console.WriteLine(result);
import com.retab.RetabClient;
public final class Example {
public static void main(String[] args) throws Exception {
RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));
var result = client.workflows().tests().create("wf_abc123", null, null, "Invoice Processing", null);
System.out.println(result);
}
}
curl -X 'POST' \
'https://api.retab.com/v1/workflows/tests' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <your-api-key>' \
-d '{
"workflow_id": "wf_abc123xyz",
"target": { "type": "block", "block_id": "block_extract_invoice" },
"source": {
"type": "manual",
"handle_inputs": {
"input-document-0": { "type": "file", "file_id": "file_invoice_q1" }
}
},
"assertion": {
"target": { "output_handle_id": "output-json-0", "path": "total" },
"condition": { "kind": "equals", "expected": 1234.56 }
},
"name": "Q1 invoice total"
}'
{
"id": "wfnodetest_hsLEQiM61ez9Piv147MWk",
"workflow_id": "wf_abc123xyz",
"target": {
"type": "block",
"block_id": "block_extract_invoice"
},
"source": {
"type": "manual",
"handle_inputs": {
"input-document-0": {
"type": "file",
"file_id": "file_invoice_q1"
}
}
},
"name": "Q1 invoice total",
"assertion": {
"id": "assert_xyz",
"target": { "output_handle_id": "output-json-0", "path": "total" },
"condition": { "kind": "equals", "expected": 1234.56 },
"label": null
},
"assertion_schema_dep": {
"schema_path": "total",
"subtree_hash": "7d79dd764ab6548d",
"depends_on_root": false
},
"assertion_drift_status": null,
"schema_drift": "unknown",
"schema_drift_detail": null,
"validation_status": "valid",
"validation_issues": [],
"latest_run_summary": null,
"latest_passing_run_summary": null,
"latest_failing_run_summary": null,
"created_at": "2026-05-01T14:30:00Z",
"updated_at": "2026-05-01T14:30:00Z"
}
{
"detail": "assertion is required for workflow tests."
}
{
"detail": "Workflow test not found: <block_id>"
}
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
Body to create a workflow test: the target block, an input source, and an assertion to evaluate its output.
Public workflow-test target.
The storage layer remains block-scoped today, but the API shape names the tested entity explicitly so workflow-level targets can be added later.
Show child attributes
Show child attributes
- ManualWorkflowTestSource
- RunStepWorkflowTestSource
Show child attributes
Show child attributes
Block-test assertion against one declared output handle.
target is the only supported shape: an output handle id and an
optional relative path inside that handle's payload.
Show child attributes
Show child attributes
Response
Successful Response
A saved workflow test: a target block, an input source, and the assertion evaluated against its output.
Public workflow-test target.
The storage layer remains block-scoped today, but the API shape names the tested entity explicitly so workflow-level targets can be added later.
Show child attributes
Show child attributes
- ManualWorkflowTestSource
- RunStepWorkflowTestSource
Show child attributes
Show child attributes
Block-test assertion against one declared output handle.
target is the only supported shape: an output handle id and an
optional relative path inside that handle's payload.
Show child attributes
Show child attributes
Single-rule schema dependency for Level 2 drift detection.
Show child attributes
Show child attributes
valid, drifted, broken none, partial, drifted, unknown Show child attributes
Show child attributes
Show child attributes
Show child attributes
Summary of the most recent block-test run.
Execution status and verdict outcome are exposed as separate fields.
The summary is written on terminal-state transitions, so in practice
status is one of completed | error | cancelled and outcome is
populated when status == "completed".
Show child attributes
Show child attributes
Summary of the most recent block-test run.
Execution status and verdict outcome are exposed as separate fields.
The summary is written on terminal-state transitions, so in practice
status is one of completed | error | cancelled and outcome is
populated when status == "completed".
Show child attributes
Show child attributes
Summary of the most recent block-test run.
Execution status and verdict outcome are exposed as separate fields.
The summary is written on terminal-state transitions, so in practice
status is one of completed | error | cancelled and outcome is
populated when status == "completed".
Show child attributes
Show child attributes
When the workflow test was created
When the workflow test was last updated