Mappings
Define how webhook payloads are transformed into provenance interactions.
An inbound mapping defines how a specific type of webhook payload from a source becomes a provenance interaction. A single source can have multiple mappings — each matching different event types.
Creating a mapping
POST /api/inbound-mappings
{
"sourceId": "uuid",
"title": "Stripe Checkout Completed",
"description": "Maps completed checkout sessions to ORDER CREATE interactions",
"resourceTypeId": "uuid",
"actionId": "uuid",
"resourceIdPath": "$.data.object.id",
"idempotencyPath": "$.id",
"priority": 10,
"filter": {
"conditions": [
{ "field": "$.type", "operator": "equals", "value": "checkout.session.completed" }
]
},
"fieldMappings": {
"customerEmail": "$.data.object.customer_email",
"amount": "$.data.object.amount_total",
"currency": "$.data.object.currency"
},
"metadataPaths": {
"userId": "$.data.object.customer",
"sessionId": "$.data.object.id"
},
"payloadSchema": { ... },
"schemaRootPath": "$.data.object",
"isActive": true
}Fields
| Field | Required | Description |
|---|---|---|
sourceId | Yes | Which inbound source this mapping belongs to |
title | Yes | Display name |
description | No | What this mapping does |
resourceTypeId | Yes | Resource type for the created interaction |
actionId | Yes | Action for the created interaction |
resourceIdPath | Yes | JSONPath to extract the resource ID from the payload |
idempotencyPath | No | JSONPath for deduplication — if an interaction with this value already exists, the webhook is skipped |
priority | No | Higher priority mappings are matched first (default: 0) |
filter | No | Conditions to match specific payloads |
fieldMappings | No | Map payload paths to interaction metadata fields. If omitted, the entire payload is used as the interaction object |
metadataPaths | No | Map payload paths to interaction root-level fields (userId, sessionId, uowId, etc.) |
payloadSchema | No | AJV schema for mapping-level validation |
schemaRootPath | No | JSONPath to a sub-object for schema validation (e.g. $.data.object) |
isActive | Yes | Whether this mapping is active |
Filter conditions
Filters determine which payloads match this mapping. All conditions must match (AND logic).
{
"conditions": [
{ "field": "$.type", "operator": "equals", "value": "checkout.session.completed" },
{ "field": "$.data.object.status", "operator": "equals", "value": "complete" }
]
}Supported operators
| Operator | Description |
|---|---|
equals | Exact match |
not_equals | Not equal |
contains | String contains |
starts_with | String starts with |
ends_with | String ends with |
gt | Greater than |
gte | Greater than or equal |
lt | Less than |
lte | Less than or equal |
in | Value is in array |
exists | Field exists (non-null) |
Field mappings
Field mappings extract values from the webhook payload into the interaction's metadata object. Keys are the target field names, values are JSONPath expressions.
{
"customerEmail": "$.data.object.customer_email",
"amount": "$.data.object.amount_total",
"currency": "$.data.object.currency",
"lineItems": "$.data.object.line_items"
}When no field mappings are defined, the entire webhook payload is stored as the interaction object.
Metadata paths
Metadata paths map payload fields to interaction root-level fields. These are stored with a _ prefix in the interaction JSONB.
{
"userId": "$.data.object.customer",
"sessionId": "$.data.object.id",
"uowId": "$.data.object.payment_intent",
"interactionTimestamp": "$.created",
"externalReference": "$.id"
}| Key | Description |
|---|---|
userId | User who triggered the event |
sessionId | Session identifier |
uowId | Unit of Work ID for correlation — auto-generates a UUID if not mapped |
interactionTimestamp | Original event timestamp |
externalReference | External system reference ID |
Schema validation
Mappings can define their own AJV schema, independent of the source-level schema. This is useful when different event types within the same source have different structures.
Schema root path
Use schemaRootPath to validate a nested sub-object instead of the full payload:
{
"payloadSchema": {
"type": "object",
"required": ["id", "customer_email"],
"properties": {
"id": { "type": "string" },
"customer_email": { "type": "string" }
}
},
"schemaRootPath": "$.data.object"
}This validates payload.data.object against the schema, not the entire payload. Useful for services like Stripe where the interesting data is nested inside a wrapper.
Priority and matching
When multiple mappings match a payload, the one with the highest priority value wins. Use this to create specific mappings that override general ones:
| Mapping | Filter | Priority |
|---|---|---|
| Stripe Checkout | $.type equals checkout.session.completed | 10 |
| Stripe Payment | $.type starts_with payment_intent | 5 |
| Stripe Catch-All | (no filter) | 0 |
Testing mappings
Test a mapping against a sample payload without creating an actual interaction:
POST /api/inbound-mappings/:id/test
{
"payload": {
"id": "evt_123",
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_123",
"customer_email": "user@example.com",
"amount_total": 2999
}
}
}
}The response shows:
- Whether the source schema passed
- Whether the mapping schema passed (with error details if not)
- Whether the filter conditions matched
- The interaction that would be created (resource ID, metadata, field values)
Example: Stripe checkout
Source
{
"title": "Stripe",
"mnemonic": "STRIPE",
"originId": "...",
"verification": {
"algorithm": "hmac-sha256",
"signatureHeader": "stripe-signature",
"encoding": "hex"
},
"verifySecret": "{{secret.stripeWebhookSecret}}",
"payloadSchema": {
"type": "object",
"required": ["id", "type", "data"],
"properties": {
"id": { "type": "string", "pattern": "^evt_" },
"type": { "type": "string" },
"data": { "type": "object" }
}
}
}Mapping
{
"title": "Checkout Completed",
"resourceTypeId": "...",
"actionId": "...",
"resourceIdPath": "$.data.object.id",
"idempotencyPath": "$.id",
"priority": 10,
"filter": {
"conditions": [
{ "field": "$.type", "operator": "equals", "value": "checkout.session.completed" }
]
},
"fieldMappings": {
"email": "$.data.object.customer_email",
"amount": "$.data.object.amount_total",
"currency": "$.data.object.currency"
},
"metadataPaths": {
"userId": "$.data.object.customer",
"externalReference": "$.id"
},
"schemaRootPath": "$.data.object",
"payloadSchema": {
"type": "object",
"required": ["id", "customer_email"],
"properties": {
"id": { "type": "string", "pattern": "^cs_" },
"customer_email": { "type": "string" }
}
}
}