Custom extractor schemas
Write JSON Schemas for Cryvis custom extractors — field types, formats, regex patterns, soft vs hard validation, and complete examples.
Custom extractors let you define your own document types with a JSON Schema. Cryvis converts the uploaded PDF or image to text, asks the model to fill every field, then validates the result against your schema.
Open the schema editor in Console →
Overview
- Create an extractor in the Console (name, document description, output schema).
- Cryvis assigns a short id. Call
POST /v1/custom-extractors/<id>with a multipartfile. - Credits are 1 per PDF page or 1 per image — see Credits.
The document description (for example utility bill or bill of lading) is injected into the model prompt. Field description values are also sent to the model — write them clearly.
Supported uploads: PDF, JPEG, PNG, WebP (same limits as built-in document APIs).
Schema shape
The root must be a JSON Schema object with a properties map:
{
"type": "object",
"properties": {
"field_name": {
"type": ["string", "null"],
"description": "What to extract",
"x-cryvis-validation": "soft"
}
},
"required": [],
"additionalProperties": false
}Rules
| Rule | Limit |
|---|---|
Root type | Must be "object" |
| Max schema size | ~50 KB |
| Max properties | 100 |
| Max nesting depth | 8 |
| Unsupported | $ref, oneOf, anyOf, allOf, if / then / else, not |
Prefer nullable unions (["string", "null"]) so missing values become null instead of failing the whole extraction.
Field types
| Type | Use for |
|---|---|
string | Text, IDs, dates-as-strings, emails |
number | Amounts, quantities (may include decimals) |
integer | Whole numbers only |
boolean | Yes/no flags |
object | Nested groups (address, party) |
array | Line items, pages, lists |
null | Allowed missing value (usually combined with another type) |
Example nullable string:
"vendor_name": {
"type": ["string", "null"],
"description": "Supplier or merchant name as printed on the document"
}Soft vs hard validation
Use Cryvis’s extension on any field:
"x-cryvis-validation": "soft" | "hard"| Mode | When a constraint fails |
|---|---|
| soft (default) | Keep the extracted value, add a warning in meta.validation, HTTP 200 |
| hard | Fail the request with HTTP 422 VALIDATION_ERROR |
required interacts with the field’s mode:
- Field in
required+ hard → missing/null→ 422 - Field in
required+ soft → missing/null→ warning, value set tonull - Not in
required→ missing is fine; response still includes the key asnull
Tip: Mark critical identifiers hard (invoice number, account id). Mark optional or OCR-noisy fields soft (email, dates, amounts).
Formats
Set "format" on string fields. Formats are validated after extraction (they are not enforced as strict model output constraints, so the model can still return a raw value).
| Format | Expected value |
|---|---|
date | YYYY-MM-DD |
time | HH:MM / HH:MM:SS (optional timezone) |
date-time | ISO-8601 datetime |
email | Email address |
uri / url | Absolute URL with scheme and host |
uuid | UUID string |
hostname | DNS hostname |
ipv4 / ipv6 | IP address |
phone | Phone number (E.164-friendly; spaces/()/- allowed) |
"customer_email": {
"type": ["string", "null"],
"format": "email",
"description": "Billing email on the document",
"x-cryvis-validation": "soft"
}Regex with pattern
pattern is an ECMA-262 regular expression. Invalid patterns are rejected when you save the schema.
"invoice_number": {
"type": ["string", "null"],
"pattern": "^[A-Z0-9-]+$",
"minLength": 1,
"description": "Vendor invoice number",
"x-cryvis-validation": "hard"
}Other string/number constraints you can use:
minLength/maxLengthminimum/maximum/exclusiveMinimum/exclusiveMaximumenum/const- For arrays:
minItems/maxItems, plusitemsschema
Nested objects and arrays
{
"type": "object",
"properties": {
"vendor": {
"type": ["object", "null"],
"description": "Supplier details",
"properties": {
"name": {
"type": ["string", "null"],
"description": "Vendor name"
},
"tax_id": {
"type": ["string", "null"],
"description": "VAT / GST / EIN if present"
}
},
"additionalProperties": false,
"x-cryvis-validation": "soft"
},
"line_items": {
"type": ["array", "null"],
"description": "Product or service lines",
"items": {
"type": "object",
"properties": {
"description": {
"type": ["string", "null"],
"description": "Line description"
},
"amount": {
"type": ["number", "null"],
"description": "Line total"
}
},
"additionalProperties": false
},
"x-cryvis-validation": "soft"
}
},
"additionalProperties": false
}Set "additionalProperties": false on objects when you want unexpected keys flagged (soft or hard depending on the object’s x-cryvis-validation).
API usage
List extractors for your account:
curl -s https://api.cryvis.com/v1/custom-extractors \
-H "Authorization: Bearer sk_live_..."Extract:
curl -X POST "https://api.cryvis.com/v1/custom-extractors/<id>" \
-H "Authorization: Bearer sk_live_..." \
-F "file=@document.pdf"Successful response shape:
{
"success": true,
"data": {
"invoice_number": "IN-123",
"customer_email": "billing@example.com",
"due_date": "2024-01-15",
"total_amount": 120.5
},
"meta": {
"document_type": "custom:<id>",
"request_id": "...",
"validation": {
"is_valid": true,
"warnings": [],
"confidence": 1
}
}
}Hard validation failures return 422 with error.code VALIDATION_ERROR and a message that includes field paths.
Full examples
Utility / vendor invoice
{
"type": "object",
"properties": {
"invoice_number": {
"type": ["string", "null"],
"description": "Vendor invoice number",
"pattern": "^[A-Z0-9-]+$",
"minLength": 1,
"x-cryvis-validation": "hard"
},
"customer_email": {
"type": ["string", "null"],
"format": "email",
"description": "Billing email",
"x-cryvis-validation": "soft"
},
"due_date": {
"type": ["string", "null"],
"format": "date",
"description": "Payment due date (YYYY-MM-DD)",
"x-cryvis-validation": "soft"
},
"total_amount": {
"type": ["number", "null"],
"description": "Total amount due",
"x-cryvis-validation": "soft"
}
},
"required": ["invoice_number"],
"additionalProperties": false
}Identity-style document
{
"type": "object",
"properties": {
"full_name": {
"type": ["string", "null"],
"description": "Full name of the holder",
"minLength": 1,
"x-cryvis-validation": "hard"
},
"document_number": {
"type": ["string", "null"],
"description": "Primary document or ID number",
"pattern": "^[A-Z0-9/-]+$",
"x-cryvis-validation": "hard"
},
"date_of_birth": {
"type": ["string", "null"],
"format": "date",
"description": "Date of birth YYYY-MM-DD",
"x-cryvis-validation": "soft"
},
"expiry_date": {
"type": ["string", "null"],
"format": "date",
"description": "Expiry date YYYY-MM-DD if present",
"x-cryvis-validation": "soft"
},
"nationality": {
"type": ["string", "null"],
"description": "Nationality or country of issue as printed",
"x-cryvis-validation": "soft"
}
},
"required": ["full_name", "document_number"],
"additionalProperties": false
}Enum-constrained field
"status": {
"type": ["string", "null"],
"enum": ["paid", "unpaid", "partial", null],
"description": "Payment status if explicitly stated",
"x-cryvis-validation": "soft"
}Editor reference
Use the Console schema editor for syntax highlighting, JSON linting, autocomplete (Ctrl+Space), and insert chips (Email, Phone, Regex, UUID, and more).
After you create an extractor, open it from Extractors to edit the schema and run a test upload.
Best practices
- Describe every field — the model uses
descriptiontext heavily. - Use nullable types —
["string", "null"]/["number", "null"]. - Hard only when necessary — overusing
hardcauses 422s on noisy OCR. - Put formats and patterns on the field — validated after extraction; keep descriptions human-readable.
- Set
additionalProperties: false on the root object for a closed response shape. - Test in Console — upload a real sample and inspect
meta.validation.warningsbefore going to production.