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_reffor 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 kind | Controlled by | Description |
|---|---|---|
c2pa_build | c2pa.enabled | Build the C2PA claim from assertions |
watermark_embed | watermark.enabled | Embed watermark payload in the asset signal |
soft_binding_write | soft_binding.enabled | Write fingerprint index (exact hash, pHash, watermark_id) |
anchor_queue | anchoring.enabled | Add manifest digest to the next anchor batch |
c2pa_finalize | Always (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:
extendsmust reference an existing preset at the specified version.c2pa.enabledmust betruewhen any assertion inc2pa.assertionsis in the AI disclosure set (ai_generated,ai_assisted,deepfake_disclosure,voiceclone_disclosure).watermark.enginemust be compatible withmedia_type(e.g.trustmarkonly forimage).kms_mode: byokrequires the tenant to have Enterprise BYOK plan enabled.anchoring.methodsmust be a subset of[opentimestamps, arbitrum].batch_interval_minutescannot be less than 5.
Create a custom recipe
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.yamlResponse:
{ "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:
- Increment the
versionfield (e.g.version: 2). - Change the
idto match:acme-legal-evidence-v2. - 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:
# Using the Python SDKpython -c "import verbitasfrom verbitas.recipes import validate_yamlimport 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
Legal evidence recipe
Fast anchor, maximum retention, required OCSP, ingredient chain:
anchoring: enabled: true methods: [opentimestamps, arbitrum] batch_interval_minutes: 5retention: manifest_days: 2555verification: ocsp_mode: required minimum_confidence: 0.97c2pa: require_ingredient_chain: trueMinimal recipe (C2PA only, no watermark)
For workflows where watermark quality cannot be guaranteed (extreme re-encoding expected):
watermark: enabled: falsesoft_binding: enabled: false # Note: without soft_binding, lookup by fingerprint is not availableEnterprise recipe with BYOK
kms_mode: byokextends: enterprise-strict-v1@1Requires Enterprise BYOK plan and a registered BYOK key ARN. See Guides: Bring Your Own KMS.