DoculaDevelopersDezvoltatori
API 0.8.0 · BuildVersiune produs 0.33.0
Documentation navigationNavigare documentație

Guide 5 of 7Ghidul 5 din 7 · API 0.8.0

Execution trace and artifactsTrasa execuției și artefactele

Submitting work against a release, following the persisted step trace live and downloading verified DOCX and PDF artifacts.Trimiterea lucrului către un release, urmărirea live a trasei de pași persistate și descărcarea artefactelor DOCX și PDF verificate.

Derived from:Derivat din: contracts/openapi/openapi.yamldocs/ARCHITECTURE.md

Submit an executionTrimite o execuție

submitExecution takes release_id and the input document, with a request limit of 1 MiB, and requires an Idempotency-Key. The API answers 202 with the queued execution and idempotent_replay: false; sending the same key and body again returns 200 with the same execution and idempotent_replay: true, while the same key with a different body answers 409. Administrator, engineer and operator sessions may submit.submitExecution primește release_id și documentul input, cu o limită de cerere de 1 MiB, și cere un Idempotency-Key. API-ul răspunde 202 cu execuția pusă în coadă și idempotent_replay: false; trimiterea aceleiași chei și a aceluiași body întoarce 200 cu aceeași execuție și idempotent_replay: true, în timp ce aceeași cheie cu un body diferit răspunde 409. Sesiunile de administrator, inginer și operator pot trimite.

Business submissions use createSubmission instead. The body carries metadata with model_slug, object_reference, object_effective_at, source_system and source_actor, plus the original payload. Ingestion resolves the applicable families at object_effective_at, persists an immutable processing plan with every node's pinned transform and release, and either triggers processing or records a queryable rejection.Solicitările de business folosesc în schimb createSubmission. Body-ul poartă metadata cu model_slug, object_reference, object_effective_at, source_system și source_actor, plus payload-ul original. Ingestia rezolvă familiile aplicabile la object_effective_at, persistă un plan de procesare imuabil cu transformarea și release-ul fixate pentru fiecare nod și fie declanșează procesarea, fie înregistrează o respingere interogabilă.

execution-trace/submit-execution.request.json · submitExecution body: the release to run and the raw input.Corpul submitExecution: publicarea de rulat și intrarea brută.
{
  "release_id": "66666666-6666-4666-8666-000000000001",
  "input": {
    "first_name": "Iulia",
    "last_name": "Popescu",
    "email": "[email protected]"
  }
}

The persisted step traceTrasa de pași persistată

An execution has up to four ordered steps: validate, transform.declarative, compose and render. Each step records status (one of not_started, queued, running, succeeded, failed), attempt bounded to 5, state_version, started_at, finished_at and duration_ms, plus bounded measurements such as byte counts and peak RSS when the isolated worker can attribute them honestly. Canonical timestamps are the only source of elapsed time; a missing measurement is null, never zero.O execuție are până la patru pași ordonați: validate, transform.declarative, compose și render. Fiecare pas înregistrează status (unul dintre not_started, queued, running, succeeded, failed), attempt limitat la 5, state_version, started_at, finished_at și duration_ms, plus măsurători limitate precum numărul de octeți și RSS-ul maxim, atunci când workerul izolat le poate atribui onest. Timestamp-urile canonice sunt singura sursă pentru timpul scurs; o măsurătoare lipsă este null, niciodată zero.

A transformation release stops after the transform step and exposes output and output_hash; a document release continues through compose and render and exposes artifacts. A failed step carries a safe failure code. Planned descendants of a failed node are derived as blocked with blocked_by, never mislabeled as failed attempts.Un release de transformare se oprește după pasul de transformare și expune output și output_hash; un release de documente continuă prin compunere și randare și expune artefacte. Un pas eșuat poartă un cod de eșec sigur. Descendenții planificați ai unui nod eșuat sunt derivați ca blocked cu blocked_by, niciodată etichetați greșit ca încercări eșuate.

Follow it live with SSEUrmărește live prin SSE

streamExecutionEvents replays every persisted event for the execution and then tails new ones as text/event-stream. Each frame carries a per-execution sequence in id, an event name such as state and JSON data. Reconnect with Last-Event-ID set to the last id you processed: the server backfills missed rows from history and continues, and clients deduplicate by id. A 422 on resume means the cursor is not a valid persisted identity; restart without a cursor.streamExecutionEvents reia fiecare eveniment persistat al execuției, apoi urmărește evenimentele noi ca text/event-stream. Fiecare cadru poartă o secvență per execuție în id, un nume event precum state și data JSON. Reconectează-te cu Last-Event-ID setat la ultimul id procesat: serverul completează rândurile ratate din istoric și continuă, iar clienții deduplică după id. Un 422 la reluare înseamnă că cursorul nu este o identitate persistată validă; repornește fără cursor.

When the stream reports a terminal state, reconcile with getExecution rather than trusting the last frame alone. streamSubmissionEvents offers the same contract per submission, with phase, event, status and lineage_node_id in the data.Când fluxul raportează o stare terminală, reconciliază cu getExecution în loc să te încrezi doar în ultimul cadru. streamSubmissionEvents oferă același contract per solicitare, cu phase, event, status și lineage_node_id în date.

execution-trace/events.sse · text/event-stream transcript of a completed document-generation run.Transcrierea text/event-stream a unei rulări de generare de documente finalizate.
retry: 1000

id: 1
event: execution
data: {"id":1,"execution_id":"55555555-5555-4555-8555-000000000001","type":"execution.submitted","correlation_id":"docs-example-correlation-id","payload":{"input_hash":"6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700","raw_input_hash":"889007605af33b472a3f0b3e0c85826ca70cc3484d668e2cae165ebef41a04d8","release_hash":"a4d451ec23463726f72c43d64c710968f6b602cd653b4de8adee1b556240a829"},"occurred_at":"2026-08-30T09:15:00Z"}

id: 2
event: execution
data: {"id":2,"execution_id":"55555555-5555-4555-8555-000000000001","type":"step.succeeded","step":"validate","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"contract_hash":"cc8321d6375c494d043fdd0260f21bc0ec51dacc9f6abb7f909cdcd3041b78bf","contract_version_id":"11111111-1111-4111-8111-000000000001","input_hash":"6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700"},"occurred_at":"2026-08-30T09:15:01Z"}

id: 3
event: execution
data: {"id":3,"execution_id":"55555555-5555-4555-8555-000000000001","type":"step.succeeded","step":"transform.declarative","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"input_hash":"6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700","output_hash":"e0ee8bb50685e05fa0f47ed04203ae953fdfd055f5bd2892ea186504254f8c3a","transform_hash":"aa214ea38326805d95661c3ad1643cc07f88e2bae0438ac0448a66d93335ca6e","transform_version_id":"22222222-2222-4222-8222-000000000001"},"occurred_at":"2026-08-30T09:15:02Z"}

id: 4
event: execution
data: {"id":4,"execution_id":"55555555-5555-4555-8555-000000000001","type":"step.succeeded","step":"compose","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"artifact_id":"77777777-7777-4777-8777-000000000001","kind":"docx","media_type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document","byte_size":18432,"content_hash":"584cb925e6ad45273e46037369c5ec3a5d7cfdd409ce13a69e7087f8accd1c79"},"occurred_at":"2026-08-30T09:15:04Z"}

id: 5
event: execution
data: {"id":5,"execution_id":"55555555-5555-4555-8555-000000000001","type":"step.succeeded","step":"render","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"artifact_id":"77777777-7777-4777-8777-000000000002","kind":"pdf","media_type":"application/pdf","byte_size":24576,"content_hash":"c35b21d6ca39aa7cc3b79a705d989f1a6e88b99ab43988d74048799e3db926a3"},"occurred_at":"2026-08-30T09:15:07Z"}

id: 6
event: execution
data: {"id":6,"execution_id":"55555555-5555-4555-8555-000000000001","type":"execution.completed","step":"render","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"artifact_id":"77777777-7777-4777-8777-000000000002","kind":"pdf","media_type":"application/pdf","byte_size":24576,"content_hash":"c35b21d6ca39aa7cc3b79a705d989f1a6e88b99ab43988d74048799e3db926a3"},"occurred_at":"2026-08-30T09:15:07Z"}

Verify and download artifactsVerifică și descarcă artefactele

Completed document executions list artifacts with id, kind (docx or pdf), media_type, byte_size of at most 25 MiB and a SHA-256 content_hash. downloadExecutionArtifact is a POST with Content-Type: application/json, Origin, X-CSRF-Token and an empty JSON object as body; it authorizes the exact artifact identity, verifies the stored bytes, writes an audit event and streams the file as an attachment. Compare the SHA-256 of the downloaded bytes with content_hash.Execuțiile de documente finalizate listează artefacte cu id, kind (docx sau pdf), media_type, byte_size de cel mult 25 MiB și un content_hash SHA-256. downloadExecutionArtifact este un POST cu Content-Type: application/json, Origin, X-CSRF-Token și un obiect JSON gol ca body; autorizează identitatea exactă a artefactului, verifică octeții stocați, scrie un eveniment de audit și transmite fișierul ca atașament. Compară SHA-256 al octeților descărcați cu content_hash.

Object-store keys are never exposed; the authenticated download endpoint is the only delivery path. Administrator, engineer, operator and business_viewer sessions may download.Cheile din object storage nu sunt expuse niciodată; endpoint-ul autentificat de descărcare este singura cale de livrare. Sesiunile de administrator, inginer, operator și business_viewer pot descărca.

execution-trace/curl.sh · Submit, stream the trace, read the execution, download and verify the artifact.Trimite, urmărește trasarea, citește execuția, descarcă și verifică artefactul.
#!/bin/sh
# Docula example: execution trace and artifacts.
# login -> submitExecution -> streamExecutionEvents (SSE) until completion -> getExecution -> downloadExecutionArtifact + hash check.
# Requires curl, jq and sha256sum. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME,
# DOCULA_PASSWORD, DOCULA_RELEASE_ID (a published document-generation release whose contract accepts the person input).
set -eu
API=${DOCULA_API:?set DOCULA_API to the API origin, for example http://127.0.0.1:18081}
ORIGIN=${DOCULA_ORIGIN:-$API}
HERE=$(cd "$(dirname "$0")" && pwd)
JAR=$(mktemp)
trap 'rm -f "$JAR" "$JAR.body" "$JAR.events"' EXIT

CSRF=$(jq -n --arg u "${DOCULA_USERNAME:?}" --arg p "${DOCULA_PASSWORD:?}" '{username: $u, password: $p}' |
  curl --fail --silent --show-error -c "$JAR" -H "Origin: $ORIGIN" -H 'Content-Type: application/json' \
    --data-binary @- "$API/api/v1/auth/login" | jq -r '.csrf_token')

# 1. Submit (submitExecution). Idempotency-Key is required: replaying the same key and body returns the same execution (200),
#    a different body under the same key is a 409.
jq --arg r "${DOCULA_RELEASE_ID:?}" '.release_id = $r' "$HERE/submit-execution.request.json" > "$JAR.body"
SUBMITTED=$(curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
  -H "Idempotency-Key: docs-example-$(date +%Y%m%d%H%M%S)" -H 'Content-Type: application/json' \
  --data-binary @"$JAR.body" "$API/api/v1/executions")
EXECUTION_ID=$(printf '%s' "$SUBMITTED" | jq -r '.execution.id')

# 2. Follow the trace (streamExecutionEvents). The stream replays persisted events, then continues live and closes itself
#    after execution.completed or step.failed. Send Last-Event-ID to resume after a disconnect.
curl --fail --silent --show-error --no-buffer --max-time 120 -b "$JAR" -H 'Accept: text/event-stream' -H 'Last-Event-ID: 0' \
  "$API/api/v1/executions/$EXECUTION_ID/events" | tee "$JAR.events"
grep -q '"type":"execution.completed"' "$JAR.events" || { echo "execution did not complete" >&2; exit 1; }

# 3. Read the final state (getExecution): four steps and the retained artifacts with their SHA-256 hashes.
EXECUTION=$(curl --fail --silent --show-error -b "$JAR" "$API/api/v1/executions/$EXECUTION_ID")
printf '%s' "$EXECUTION" | jq '{status, steps: [.steps[] | {name, status, duration_ms}], artifacts}'

# 4. Download one artifact (downloadExecutionArtifact is a POST with an empty JSON body because it is audited)
#    and verify the bytes against the X-Content-SHA256 header and the recorded content_hash.
ARTIFACT_ID=$(printf '%s' "$EXECUTION" | jq -r '.artifacts[] | select(.kind == "pdf") | .id')
EXPECTED=$(printf '%s' "$EXECUTION" | jq -r '.artifacts[] | select(.kind == "pdf") | .content_hash')
curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
  -H 'Content-Type: application/json' --data-binary @"$HERE/download-artifact.request.json" \
  -D "$JAR.headers" -o document.pdf "$API/api/v1/executions/$EXECUTION_ID/artifacts/$ARTIFACT_ID/download"
HEADER_HASH=$(tr -d '\r' < "$JAR.headers" | awk 'tolower($1) == "x-content-sha256:" { print $2 }')
ACTUAL=$(sha256sum document.pdf | cut -d ' ' -f 1)
rm -f "$JAR.headers"
[ "$ACTUAL" = "$EXPECTED" ] && [ "$ACTUAL" = "$HEADER_HASH" ] || { echo "artifact hash mismatch" >&2; exit 1; }
printf 'document.pdf sha256=%s\n' "$ACTUAL"

Submission graph and evidenceGraful solicitării și dovezile

getSubmissionGraph returns the submission, the ingestion decision with its selection snapshot, every processing node with pinned transform and release identities and status, and the storage rows. getSubmissionElement reads one element only when it belongs to that submission, and getSubmissionElementContent streams the canonical JSON evidence of a node. listExecutions pages with an opaque cursor and a limit of at most 100.getSubmissionGraph întoarce solicitarea, decizia de ingestie cu instantaneul ei de selecție, fiecare nod de procesare cu identitățile fixate ale transformării și release-ului și cu statusul, plus rândurile de stocare. getSubmissionElement citește un element doar când aparține acelei solicitări, iar getSubmissionElementContent transmite dovada JSON canonică a unui nod. listExecutions paginează cu un cursor opac și un limit de cel mult 100.

curl sequence sketchSchiță de secvență curl

  1. Step 1 · copy onlyPasul 1 · doar pentru copiere
    curl --request POST "$DOCULA_URL/api/v1/executions" --cookie "$COOKIE_JAR" \
      --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" \
      --header "X-CSRF-Token: $CSRF_TOKEN" --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
      --data-binary @execution.json
  2. Step 2 · copy onlyPasul 2 · doar pentru copiere
    curl --request GET "$DOCULA_URL/api/v1/executions/$EXECUTION_ID/events" --cookie "$COOKIE_JAR" \
      --header "Accept: text/event-stream" --header "Last-Event-ID: $LAST_EVENT_ID" --no-buffer
  3. Step 3 · copy onlyPasul 3 · doar pentru copiere
    curl --request GET "$DOCULA_URL/api/v1/executions/$EXECUTION_ID" --cookie "$COOKIE_JAR"
  4. Step 4 · copy onlyPasul 4 · doar pentru copiere
    curl --request POST "$DOCULA_URL/api/v1/executions/$EXECUTION_ID/artifacts/$ARTIFACT_ID/download" --cookie "$COOKIE_JAR" \
      --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \
      --data-binary '{}' --output document.pdf
  5. Step 5 · copy onlyPasul 5 · doar pentru copiere
    sha256sum document.pdf   # must equal the artifact content_hash