Skip to Content
Extraction and verificationDocument workflows

Workflows

Workflows are configurable processing pipelines. Define a sequence of steps such as capture, OCR, extraction, validation, checks, enrichment, risk, review, and output, then run them against uploaded documents or accumulated case data.

API credential mode

These resources currently require live API credentials. Test-mode API keys receive 403 on session, session-configuration and authenticated workflow routes, including reads. Separate sandbox sessions and workflow runs are not supported. Use an isolated development organization with live credentials for integration testing; its results must not authorize production actions.

Creating a Workflow

import { KoralaClient } from '@korala/api-client'; const korala = new KoralaClient({ apiKeyId: 'your-api-key-id', apiSecret: 'your-api-secret', }); const workflow = await korala.workflows.create({ name: 'ID Document Extraction', slug: 'id-document', steps: [ { type: 'capture', config: { acceptedTypes: ['application/pdf', 'image/png', 'image/jpeg'], maxSizeMb: 10 } }, { type: 'ocr', config: { provider: 'auto' } }, { type: 'extract', config: { prompt: 'inline', schema: 'inline', inlineSystemPrompt: '...', inlineOutputSchema: { ... } } }, { type: 'validate', config: { schema: 'inline', aiValidation: { prompt: '...', fields: ['document_number'], failOnInvalid: false } } }, { type: 'output', config: { format: 'json', includeMetadata: true } }, ], });

Output schema

A workflow’s outputSchema is a JSON Schema object describing the structured result each run returns. The extract step asks the model for exactly these fields, and validate checks the result against them. In the dashboard it is edited under Output fields; through the API you set it on create or update:

await korala.workflows.update('invoice-check', { outputSchema: { type: 'object', required: ['vendor_name', 'total'], properties: { vendor_name: { type: 'string', description: 'Who sent the invoice' }, total: { type: 'number' }, category: { type: 'string', enum: ['Food', 'Travel', 'Office supplies'] }, }, }, });
  • The steps read type (string, number, integer, boolean, object, array), enum, items, properties, required and description. Other keywords (format, minLength, $ref, oneOf…) are stored and returned unchanged; the dashboard shows such fields as Advanced and keeps them as they are.
  • null removes the schema. Without one, an extract step with an inline prompt returns whatever fields the model finds in the document, with no fixed set, and a validate step with schema: 'inline' has nothing to check against and fails.
  • Property keys are the keys in the run’s result, and the names other steps refer to (for example aiValidation.fields). Renaming a field through the API does not rename those references.
  • properties come back in the order you sent them, which is the order the dashboard shows the fields in. The exception is a key made only of digits ("2024"): JavaScript lists those first, whatever order they were sent in, so the dashboard refuses such a name for a new or renamed field.

Step Types

StepDescription
captureAccept document upload (PDF, images)
ocrExtract text from document via OCR
extractStructured data extraction with an LLM
validateAssertions over already-known data, extracted fields, or prior outputs
enrichTransforms or data augmentation that do not create first-class findings
checkProvider-backed or specialized verification that emits findings, scores, and review signals
riskAggregate scoring across evidence, history, and prior steps
reviewHuman-in-the-loop pause point
webhookFire-and-forget HTTP POST
outputFormat and return results

Use check when the step calls an external or specialized verification source and you want to persist first-class findings such as PEP, sanctions, adverse media, or business registry results. Keep validate for deterministic assertions over data you already have.

HTTP check responses

A check step using the http provider sends a JSON POST to its providerConfig.endpoint. A successful HTTP status alone does not clear the check: the endpoint must return a complete JSON object with an explicit result. The smallest completed response is:

{ "status": "clear" }

Completed results accept status values clear, match, review_required, or error. state may be omitted or set to completed. For example:

{ "state": "completed", "status": "review_required", "matches": [{ "label": "Possible name match", "category": "sanctions" }], "reasons": ["An analyst must verify whether this is the same person."], "reviewRecommended": true, "providerRef": "screening-example-123" }

Optional completed fields include numeric score, matches (each with a string label), string-array reasons, boolean reviewRecommended, nullable string providerRef, and object metadata / rawResultRedacted. Scores use the provider’s scale and must be within −2,147,483,648 to 2,147,483,647; they are rounded for persistence. If omitted, reviewRecommended defaults to true for review_required and error, and false for other statuses. The workflow’s blocking policy still determines how the finding affects the session.

For asynchronous work, return a pending response instead of a terminal status:

{ "state": "pending", "operationId": "screening-example-123", "retryAfterMs": 5000 }

operationId must be a nonblank string of at most 2,048 characters. Optional retryAfterMs must be an integer from 1 through 86,400,000 milliseconds; it defaults to 5,000 and intervals below 1,000 are raised to 1,000. Pending responses may include object metadata, but cannot include status. When polling, Korala POSTs to the same endpoint with the operation ID and metadata in pendingState. These interval limits do not impose an overall operation lifetime.

Both response forms must be UTF-8 JSON, at most 256 KiB and 64 object/array levels deep, received within the 10-second request deadline. Empty bodies, null, {}, invalid field types, non-success HTTP statuses, and unreadable, oversized or timed-out responses produce an error finding with review recommended, rather than a clear result. Strings and object keys must not contain NUL or unpaired Unicode surrogates.

The workflow configuration owns kind, provider, blockingMode, and reviewThreshold; response fields cannot override them. An HTTP check cannot be saved with blockingMode: "block": only the definite results listed in automatic decisions can decline a session. Unknown response fields are ignored. Keep private diagnostics out of evidence metadata and use rawResultRedacted only for redacted data.

Existing integrations: endpoints that previously returned an empty successful response must now explicitly return { "status": "clear" } when the check clears. Move any blocking policy supplied in the response into the workflow configuration.

Running a Workflow

// 1. Start a run and get upload URL const run = await korala.workflows.startRun('id-document', { inputData: { customField: 'value' }, }); // 2. Upload the document await fetch(run.uploadUrl, { method: 'PUT', body: documentFile, headers: { 'Content-Type': documentFile.type }, }); // 3. Confirm upload to start processing await korala.workflows.confirmUpload(run.runId); // 4. Poll for results const result = await korala.workflows.getRun(run.runId); // result.status: 'completed' | 'running' | 'failed' // result.outputData: extracted data

A run starts once. If a confirm’s answer is lost, confirming again is safe: a run that already started answers 409 Conflict, so treat a 409 as success and read the run. Any other 4xx means it did not start.

Workflow Webhooks

Subscribe to these events for real-time notifications:

EventDescription
workflow_run_completedWorkflow run finished
workflow_run_failedWorkflow run failed

Human Review

If a workflow includes a review step, it pauses for human review:

// Approve and continue await korala.workflows.submitReview(runId, { action: 'approve', editedData: { correctedField: 'new value' }, }); // Or reject await korala.workflows.submitReview(runId, { action: 'reject', reason: 'Document is unreadable', });
Last updated on