Skip to content

Custom Recipes

Custom Recipes

Custom recipes extend one of the 10 built-in presets and configure the exact operations that run for your sign and verify calls.

When to use a custom recipe

Use a custom recipe when:

  • You need assertions not included in any preset (e.g. legal_case_ref for your case management system)
  • You need a specific manifest retention period different from a preset’s default
  • You need BYOK KMS (kms_mode: byok)
  • You need a specific anchor batch interval (e.g. 5-minute fast anchor for legal evidence)
  • You want to disable soft-binding for a specific workflow

Anatomy of a recipe

# --- Identity ---
id: acme-legal-evidence-v1 # Required. Unique within tenant. Convention: <slug>-v<N>.
version: 1 # Required. Starts at 1.
extends: image-legal-evidence-v1@1 # Required for custom recipes. Base preset + version.
media_type: image # Required. image | audio | video | text.
description: ACME legal evidence chain # Required. Human-readable.
# --- Layer 1: C2PA ---
c2pa:
enabled: true # Required. Must be true for ai_generated content.
assertions:
- ai_generated
- generator
- model
- created_at
- legal_case_ref # Custom assertion key; must be in allowed assertion list.
require_ingredient_chain: false # If true: all edits must create new manifest nodes.
# --- Layer 2: Watermark ---
watermark:
enabled: true
engine: trustmark # trustmark | audioseal | videoseal | synthid_text
payload:
type: manifest_pointer
encoding: compact
# --- Layer 3: Soft-binding ---
soft_binding:
enabled: true
methods:
- exact_watermark # exact_watermark | exact_hash | perceptual_hash
- exact_hash
- perceptual_hash
# --- Anchoring ---
anchoring:
enabled: true
methods:
- opentimestamps # opentimestamps | arbitrum
- arbitrum
batch_interval_minutes: 5 # 5 for legal-evidence fast anchor; 60 for standard.
# --- KMS mode ---
kms_mode: kms # kms (default) | byok | dev_local
# --- Retention ---
retention:
manifest_days: 2555 # Days to retain the manifest. 2555 ~= 7 years.
original_asset_days: 0 # 0 = do not store original. >0 = store for N days.
derived_asset_days: 30 # 0 = do not store signed copy.
# --- Verification ---
verification:
minimum_confidence: 0.95 # Default 0.92. Higher = stricter pass threshold.
ocsp_mode: required # default | required (never degrades in prod or dev)
ambiguous_match_behavior: manual_review # manual_review | reject | accept_partial
# --- Billing ---
billing:
meter: image_sign # See Reference: Meter Kinds
unit: asset
# --- UI ---
ui:
public_label: "Legal evidence image with verified chain of custody"

Step kinds (closed enum)

The recipe controls which operations are active. It cannot add new step kinds. The valid values are:

Step kindControlled byDescription
c2pa_buildc2pa.enabledBuild the C2PA claim from assertions
watermark_embedwatermark.enabledEmbed watermark payload in the asset signal
soft_binding_writesoft_binding.enabledWrite fingerprint index (exact hash, pHash, watermark_id)
anchor_queueanchoring.enabledAdd manifest digest to the next anchor batch
c2pa_finalizeAlways (if c2pa enabled)Call signer, get signature, assemble JUMBF manifest

All active steps run in the above order. You cannot change the order.

Validation rules

The recipe schema enforces these constraints at POST /v1/recipes time:

  1. extends must reference an existing preset at the specified version.
  2. c2pa.enabled must be true when any assertion in c2pa.assertions is in the AI disclosure set (ai_generated, ai_assisted, deepfake_disclosure, voiceclone_disclosure).
  3. watermark.engine must be compatible with media_type (e.g. trustmark only for image).
  4. kms_mode: byok requires the tenant to have Enterprise BYOK plan enabled.
  5. anchoring.methods must be a subset of [opentimestamps, arbitrum].
  6. batch_interval_minutes cannot be less than 5.

Create a custom recipe

Terminal window
curl -X POST https://api.verbitas.io/v1/recipes \
-H "Authorization: Bearer $VERBITAS_API_KEY" \
-H "Content-Type: application/yaml" \
--data-binary @acme-legal-evidence-v1.yaml

Response:

{
"recipe_id": "acme-legal-evidence-v1",
"version": 1,
"created_at": "2026-05-09T10:00:00Z"
}

Versioning

Custom recipes are immutable after first use. To change a recipe:

  1. Increment the version field (e.g. version: 2).
  2. Change the id to match: acme-legal-evidence-v2.
  3. Create the new recipe via POST /v1/recipes.

Existing assets signed with v1 continue verifying against v1. The recipe version is embedded in the manifest.

Debugging recipe validation

If the recipe fails validation, the API returns a 400 with field-level errors:

{
"type": "https://docs.verbitas.io/api/errors#verbitas.recipes.invalid_schema",
"title": "Invalid recipe schema",
"status": 400,
"detail": "Validation failed: 2 error(s)",
"errors": [
{ "field": "watermark.engine", "message": "Required when watermark.enabled is true" },
{ "field": "billing.meter", "message": "Unknown meter kind 'image_sign_v2'; see docs/reference/meter-kinds" }
],
"request_id": "req_01j..."
}

Dry-run validation

Validate a recipe YAML locally against the schema without submitting it:

Terminal window
# Using the Python SDK
python -c "
import verbitas
from verbitas.recipes import validate_yaml
import sys
with open('acme-legal-evidence-v1.yaml') as f:
errors = validate_yaml(f.read())
if errors:
for e in errors:
print(e)
sys.exit(1)
print('Valid')
"

Common patterns

Fast anchor, maximum retention, required OCSP, ingredient chain:

anchoring:
enabled: true
methods: [opentimestamps, arbitrum]
batch_interval_minutes: 5
retention:
manifest_days: 2555
verification:
ocsp_mode: required
minimum_confidence: 0.97
c2pa:
require_ingredient_chain: true

Minimal recipe (C2PA only, no watermark)

For workflows where watermark quality cannot be guaranteed (extreme re-encoding expected):

watermark:
enabled: false
soft_binding:
enabled: false
# Note: without soft_binding, lookup by fingerprint is not available

Enterprise recipe with BYOK

kms_mode: byok
extends: enterprise-strict-v1@1

Requires Enterprise BYOK plan and a registered BYOK key ARN. See Guides: Bring Your Own KMS.