Limai Docs
API ReferenceWebhooks

Document Validated Event

Webhook event fired when all validations for a document settle.

The DOCUMENT_VALIDATED event fires when the custom validations configured for a document settle -- that is, when a validation write completes and nothing is still pending or running. It reports the aggregate validation outcome for the file plus a per-script summary.

When It Triggers

The event fires each time the file's validation state settles: a validation write completes and no validation for that file is PENDING or RUNNING.

Validations can run on two independent paths -- the automatic (static) batch that runs during processing, and validations an agent submits afterward. Because these paths are independent and whether an agent will submit is not knowable in advance, the event can fire more than once for the same file:

  • The static batch settles and the event fires with the current outcome.
  • If an agent later submits validations, the file settles again and the event fires a second time.

When it fires more than once, the event with the later timestamp supersedes the earlier one. Treat the payload as "the current validation state of this file", keyed by fileId. Delivery is queued, retried and processed concurrently, so receipt order is not state order -- compare timestamp before overwriting what you already stored, or re-read the document with GET /v1/deployments/{deploymentId}/documents/{fileId} instead of applying the event blind. This also holds across the lifecycle events: a later DOCUMENT_VALIDATED supersedes an earlier DOCUMENT_VALIDATION_FAILED.

Unlike the lifecycle events, this one is document-scoped, not attempt-scoped. data.validationAttemptId is the newest attempt that contributed to the document, while each entry in data.results carries the attempt it came from -- after a partial revalidation those differ.

This is a document event, routed to subscriptions by deployment route.

Payload Structure

Response200
{
  "eventId": "evt_validated_654",
  "eventType": "DOCUMENT_VALIDATED",
  "timestamp": "2024-01-15T10:40:00Z",
  "organizationId": "org_xyz789",
  "projectId": "proj_abc123",
  "deploymentId": "dep_123abc",
  "extractionSchemaId": "schema_456def",
  "fileId": "file_789ghi",
  "data": {
    "status": "failed",
    "failCount": 2,
    "validationAttemptId": "att_2b",
    "results": [
      {
        "scriptKey": "totals-check",
        "source": "STATIC",
        "status": "PASSED",
        "validationAttemptId": "att_1a"
      },
      {
        "scriptKey": "agent-validator",
        "source": "AGENT",
        "status": "FAILED",
        "validationAttemptId": "att_2b"
      }
    ]
  }
}

Payload Fields

FieldTypeDescription
eventIdstringUnique event identifier. Use for idempotency.
eventTypestringAlways "DOCUMENT_VALIDATED"
timestampISO 8601When the validation state settled (UTC)
organizationIdstringYour organization identifier
projectIdstringThe project the document belongs to
deploymentIdstringThe model or deployment that processed this document
extractionSchemaIdstringThe extraction schema ID of that model or deployment
fileIdstringThe validated file identifier
data.status"passed" | "failed" | "error"Aggregate outcome. failed takes precedence over error, which takes precedence over passed.
data.failCountnumberThe file's validation fail count after recompute
data.validationAttemptIdstring | nullThe newest attempt id on the file. Correlates with the DOCUMENT_VALIDATION_STARTED and DOCUMENT_VALIDATION_FAILED events of that attempt. null for attempts started before attempt ids existed.
data.resultsarrayPer-script summary (see below)
data.results[].scriptKeystringIdentifier of the validation script
data.results[].source"STATIC" | "AGENT"Whether the validation came from the automatic batch or an agent submission
data.results[].statusstringPer-script status, e.g. "PASSED", "FAILED", "RESEND_PASSED", "RESEND_FAILED", "ERROR"
data.results[].validationAttemptIdstring | nullThe attempt this individual result belongs to. null for results written before attempt ids existed.

Attempt Ids Can Differ Within One Payload

data.validationAttemptId is the newest attempt id on the file, not a guarantee that every result belongs to it. A partial re-validation -- {"include": ["AGENT"]} or {"include": ["STATIC"]} -- starts a new attempt for one lane only and leaves the other lane's rows on their previous attempt. The settled payload then mixes attempts: the re-run lane carries the new id, the untouched lane carries the older one.

Compare data.results[].validationAttemptId against data.validationAttemptId when you need to know which results are fresh. Results whose attempt id is older were carried over from an earlier attempt and are still the current state of that script -- they are not stale in the sense of being wrong, only in the sense of not having been re-run.

Full Findings Are Not in the Payload

The payload carries a summary per script, not the full validation findings. Detailed findings are intentionally excluded for payload size, retry, and signing reasons. To get the complete findings, call the get-file-data endpoint using the fileId from the payload.

Handling the Event

function handleDocumentValidated(event) {
const { eventId, fileId, data } = event

console.log(`Document ${fileId} validation settled: ${data.status} (failCount: ${data.failCount})`)

if (data.status === "passed") {
  fetchFileData(fileId).then(exportToDownstreamSystems)
} else {
  const failed = data.results.filter(r => r.status.includes("FAILED"))
  routeToReview(fileId, failed)
}
}

On this page