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

  1. Create an extractor in the Console (name, document description, output schema).
  2. Cryvis assigns a short id. Call POST /v1/custom-extractors/<id> with a multipart file.
  3. 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

RuleLimit
Root typeMust be "object"
Max schema size~50 KB
Max properties100
Max nesting depth8
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

TypeUse for
stringText, IDs, dates-as-strings, emails
numberAmounts, quantities (may include decimals)
integerWhole numbers only
booleanYes/no flags
objectNested groups (address, party)
arrayLine items, pages, lists
nullAllowed 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"
ModeWhen a constraint fails
soft (default)Keep the extracted value, add a warning in meta.validation, HTTP 200
hardFail the request with HTTP 422 VALIDATION_ERROR

required interacts with the field’s mode:

  • Field in required + hard → missing/null422
  • Field in required + soft → missing/null → warning, value set to null
  • Not in required → missing is fine; response still includes the key as null

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).

FormatExpected value
dateYYYY-MM-DD
timeHH:MM / HH:MM:SS (optional timezone)
date-timeISO-8601 datetime
emailEmail address
uri / urlAbsolute URL with scheme and host
uuidUUID string
hostnameDNS hostname
ipv4 / ipv6IP address
phonePhone 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 / maxLength
  • minimum / maximum / exclusiveMinimum / exclusiveMaximum
  • enum / const
  • For arrays: minItems / maxItems, plus items schema

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).

Open schema editor →

After you create an extractor, open it from Extractors to edit the schema and run a test upload.

Best practices

  1. Describe every field — the model uses description text heavily.
  2. Use nullable types["string", "null"] / ["number", "null"].
  3. Hard only when necessary — overusing hard causes 422s on noisy OCR.
  4. Put formats and patterns on the field — validated after extraction; keep descriptions human-readable.
  5. Set additionalProperties: false on the root object for a closed response shape.
  6. Test in Console — upload a real sample and inspect meta.validation.warnings before going to production.