Provenance
Inbound Webhooks

Sources

Configure external services that send webhooks to Provenance.

An inbound source represents an external service that sends webhook events to Provenance. Each source defines how to verify incoming requests and optionally validate their payload structure.

Creating a source

POST /api/inbound-sources
{
  "title": "Stripe Webhooks",
  "mnemonic": "STRIPE",
  "description": "Receives payment events from Stripe",
  "originId": "uuid",
  "verification": {
    "algorithm": "hmac-sha256",
    "signatureHeader": "stripe-signature",
    "encoding": "hex",
    "signaturePrefix": "",
    "timestampField": "timestamp",
    "replayTolerance": 300
  },
  "verifySecret": "{{secret.stripeWebhookSecret}}",
  "payloadSchema": {
    "type": "object",
    "required": ["id", "type", "data"],
    "properties": {
      "id": { "type": "string" },
      "type": { "type": "string" },
      "data": { "type": "object" }
    }
  },
  "isActive": true
}

Fields

FieldRequiredDescription
titleYesDisplay name for the source
mnemonicYesUnique short code (e.g. STRIPE, GITHUB)
descriptionNoWhat this source receives
originIdYesOrigin UUID — interactions from this source are tagged with this origin
verificationNoSignature verification configuration (see below)
verifySecretNoSecret used for signature verification — supports template variables like {{secret.name}}
payloadSchemaNoAJV JSON schema to validate incoming payloads
isActiveYesWhether the source accepts incoming webhooks

Signature verification

Provenance verifies webhook signatures to ensure requests are authentic. The verification object supports:

FieldDescription
algorithmhmac-sha256, hmac-sha1, or plain (direct string comparison)
signatureHeaderHTTP header containing the signature (e.g. x-hub-signature-256 for GitHub)
encodinghex or base64
signaturePrefixPrefix to strip from the header value before comparison (e.g. sha256= for GitHub)
timestampFieldHeader or payload field containing the event timestamp (for replay protection)
replayToleranceMaximum age in seconds before rejecting the event (e.g. 300 for 5 minutes)

Examples by provider

Stripe

{
  "algorithm": "hmac-sha256",
  "signatureHeader": "stripe-signature",
  "encoding": "hex"
}

GitHub

{
  "algorithm": "hmac-sha256",
  "signatureHeader": "x-hub-signature-256",
  "encoding": "hex",
  "signaturePrefix": "sha256="
}

Plain token comparison

{
  "algorithm": "plain",
  "signatureHeader": "x-webhook-token"
}

Payload schema

Optionally validate the structure of every incoming payload at the source level. This catches malformed requests before they reach any mapping.

The schema uses AJV JSON Schema format:

{
  "type": "object",
  "required": ["id", "type", "data"],
  "properties": {
    "id": { "type": "string", "pattern": "^evt_" },
    "object": { "type": "string", "const": "event" },
    "type": { "type": "string" },
    "data": {
      "type": "object",
      "required": ["object"],
      "properties": {
        "object": { "type": "object" }
      }
    }
  }
}

Generating schemas from samples

In the Dashboard UI, you can paste a sample webhook payload and auto-generate a schema. The generator infers types, required fields, nested objects, arrays, and string patterns (like ^evt_ for IDs starting with evt_).

Template variables in secrets

The verifySecret field supports template variables, so you never need to hardcode secrets:

{
  "verifySecret": "{{secret.stripeWebhookSecret}}"
}

The secret is resolved at runtime from your configured secrets store.

Webhook URLs

Once created, each source gets two webhook URLs:

POST /api/inbound/{tenantSlug}/{sourceId}
POST /api/inbound/{tenantSlug}/by-mnemonic/{mnemonic}

Configure either URL in your external service's webhook settings. The mnemonic-based URL is easier to read and doesn't change if you recreate the source.