Unit of Work
Correlate related interactions across distributed transactions.
A Unit of Work (UOW) is a UUID that links related interactions together — even when they happen in different services at different times.
How it works
Pass the x-uowid header on every interaction that belongs to the same logical operation:
# Service A: Create order
curl -X POST https://provenance.onrender.com/api/interactions \
-H "x-api-key: your-api-key" \
-H "x-uowid: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"resourceId": "order-789",
"resourceType": "ORDER",
"action": "CREATE",
"origin": "WEBAPP",
"interaction": { "total": 99.99 }
}'
# Service B: Process payment
curl -X POST https://provenance.onrender.com/api/interactions \
-H "x-api-key: your-api-key" \
-H "x-uowid: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"resourceId": "payment-456",
"resourceType": "PAYMENT",
"action": "PROCESS",
"origin": "PAYMENT_SVC",
"interaction": { "method": "card", "last4": "4242" }
}'
# Service C: Update inventory
curl -X POST https://provenance.onrender.com/api/interactions \
-H "x-api-key: your-api-key" \
-H "x-uowid: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"resourceId": "sku-001",
"resourceType": "INVENTORY",
"action": "UPDATE",
"origin": "INVENTORY_SVC",
"interaction": { "quantity": -1 }
}'All three interactions are now linked by the same UOW ID.
Auto-generation
If you don't provide x-uowid, the API generates one automatically. This means every interaction always has a UOW ID — you just need to provide your own when you want to correlate multiple interactions.
Querying by UOW
Find all interactions in a unit of work:
GET /api/interactions?uowId=550e8400-e29b-41d4-a716-446655440000Or via the activity endpoint:
POST /api/activity
{
"resources": [
{ "resourceId": "550e8400-e29b-41d4-a716-446655440000", "resourceType": "ORDER" }
]
}Using the CLI
# Generate a UOW ID
UOWID=$(provenance uow new)
# Use it across multiple track commands
provenance track -r order-789 -t ORDER -a CREATE -u $UOWID \
-d '{"total":99.99}'
provenance track -r payment-456 -t PAYMENT -a PROCESS -u $UOWID \
-d '{"method":"card"}'
provenance track -r sku-001 -t INVENTORY -a UPDATE -u $UOWID \
-d '{"quantity":-1}'Using the SDK
Queue ingestion
const { v4: uuidv4 } = require('uuid');
const uowId = uuidv4();
await provenance.log({
resourceType: 'order', resourceId: 'order-789',
action: 'create', uowId,
interaction: { total: 99.99 }
});
await provenance.log({
resourceType: 'payment', resourceId: 'payment-456',
action: 'process', uowId,
interaction: { method: 'card' }
});REST API client
const api = await provenance.api();
// Find all interactions in a unit of work
const results = await api.activity.search({
filters: { uowId: '550e8400-e29b-41d4-a716-446655440000' },
});When to use UOW
- Multi-step transactions — order → payment → fulfillment → notification
- CI/CD pipelines — build → test → deploy → verify
- Data pipelines — extract → transform → load → validate
- User journeys — signup → verify email → complete profile → first login
- Any operation that spans multiple services or resources
Span hierarchy
While UOW IDs group interactions into a flat set, span IDs add tree structure within a trace. Every interaction has a spanId (auto-generated UUID) and an optional parentSpanId that points to the spanId of the interaction that caused it.
Think of it this way:
uowId= "these interactions belong to the same logical operation" (flat grouping)spanId/parentSpanId= "this interaction was triggered by that interaction" (tree structure)
How it works
Every call to log() (SDK), track (CLI), or POST /api/interactions automatically generates a spanId. To build a tree, pass the parent's spanId as parentSpanId on the child interaction.
# API: use x-span-id and x-parent-span-id headers
curl -X POST https://provenance.onrender.com/api/interactions \
-H "x-api-key: your-api-key" \
-H "x-uowid: 550e8400-e29b-41d4-a716-446655440000" \
-H "x-parent-span-id: <parent-span-id>" \
-H "Content-Type: application/json" \
-d '{
"resourceId": "payment-456",
"resourceType": "PAYMENT",
"action": "PROCESS",
"origin": "PAYMENT_SVC",
"interaction": { "method": "card" }
}'SDK examples
log() returns the generated spanId, which you can pass as parentSpanId to child interactions:
const { v4: uuidv4 } = require('uuid');
const uowId = uuidv4();
// Parent interaction — log() returns the spanId
const parentSpanId = await provenance.log({
resourceType: 'order', resourceId: 'order-789',
action: 'create', uowId,
interaction: { total: 99.99 },
});
// Child interaction — linked via parentSpanId
await provenance.log({
resourceType: 'payment', resourceId: 'payment-456',
action: 'process', uowId,
parentSpanId,
interaction: { method: 'card' },
});Context layering with parentSpanId
Use addContext() to propagate parentSpanId to all subsequent calls from a scoped client:
const parentSpanId = await provenance.log({ ... });
const child = provenance.addContext({ parentSpanId });
// All log() calls on `child` will include this parentSpanId
await child.log({ resourceId: 'item-1', action: 'reserve', ... });
await child.log({ resourceId: 'item-2', action: 'reserve', ... });CLI examples
# Track with a parent span
PARENT=$(provenance track -r order-789 -t ORDER -a CREATE -u $UOWID -d '{"total":99.99}')
provenance track -r payment-456 -t PAYMENT -a PROCESS -u $UOWID \
--parent-span $PARENT -d '{"method":"card"}'
# View trace as a tree
provenance trace $UOWID --tree
# Include span IDs in output
provenance trace $UOWID --spansNotification auto-linking
When a notification subscriber is triggered by an interaction, the resulting QUEUED/notified/notify-err interactions automatically have their parentSpanId set to the triggering interaction's spanId. This means notification chains appear as children in the span tree — no manual linking needed.
Inbound webhooks
Inbound webhook interactions also participate in the span hierarchy. A spanId is auto-generated for every inbound interaction, just like SDK and CLI interactions. If the external system includes a correlation ID in its payload, you can map it via metadataPaths:
{
"metadataPaths": {
"uowId": "$.data.object.payment_intent",
"spanId": "$.data.object.metadata.traceId",
"parentSpanId": "$.data.object.metadata.parentTraceId"
}
}If spanId is not mapped or not found in the payload, one is generated automatically. This means any notifications triggered by an inbound interaction will correctly appear as children in the trace tree. See Inbound Mappings — Metadata paths for details.
Visualizing the tree
The Trace Viewer renders span hierarchies as an expandable tree timeline and an interactive graph view. Root interactions (those without a parentSpanId) appear at the top level, with children nested below.