Documentation navigationNavigare documentație
Guide 6 of 7Ghidul 6 din 7 · API 0.8.0
Operational tasks via APISarcini operaționale prin API
The workbench pages that were not ported to the React application are operated through these API sequences. Every call is same-origin, session-authenticated and CSRF-protected.Paginile de workbench care nu au fost portate în aplicația React sunt operate prin aceste secvențe API. Fiecare apel este pe aceeași origine, autentificat prin sesiune și protejat CSRF.
Derived from:Derivat din: docs/OPERATIONAL-UI-SCOPE.mddocs/MODULE-MAP.mdcontracts/openapi/openapi.yaml
Session, CSRF and the curl variablesSesiune, CSRF și variabilele curl
Log in once with login and keep the cookie jar; then read getCurrentSession to obtain csrf_token and check that must_change_password is false. Every mutation below sends Origin: $DOCULA_URL, X-CSRF-Token: $CSRF_TOKEN and Content-Type: application/json; idempotent operations also send Idempotency-Key, and revisioned drafts send If-Match. The sequences use $DOCULA_URL for the exact origin and $COOKIE_JAR for the host-only session cookie, and login.json holds the username and password fields.Autentifică-te o dată cu login și păstrează cookie jar-ul; apoi citește getCurrentSession pentru a obține csrf_token și pentru a verifica că must_change_password este false. Fiecare mutație de mai jos trimite Origin: $DOCULA_URL, X-CSRF-Token: $CSRF_TOKEN și Content-Type: application/json; operațiile idempotente trimit și Idempotency-Key, iar ciornele cu revizie trimit If-Match. Secvențele folosesc $DOCULA_URL pentru originea exactă și $COOKIE_JAR pentru cookie-ul de sesiune legat de host, iar login.json conține câmpurile username și password.
curl sequence sketchSchiță de secvență curl
curl --request POST "$DOCULA_URL/api/v1/auth/login" --cookie-jar "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --data-binary @login.jsoncurl --request GET "$DOCULA_URL/api/v1/auth/session" --cookie "$COOKIE_JAR" # read csrf_token into $CSRF_TOKEN
Readiness, formerly /app/adminReadiness, fosta pagină /app/admin
getReadiness is public and returns only safe dependency names with aggregate status for PostgreSQL, NATS and S3. getDetailedReadiness requires an administrator session and adds the probe operation per dependency plus outbox health: backlog_size, oldest_unpublished_age_seconds, consumer_last_success_at and publish_failures. NATS or S3 failures appear inside a 200 response; a 503 means PostgreSQL prevented session or authorization evaluation.getReadiness este publică și întoarce doar nume sigure de dependențe cu statusul agregat pentru PostgreSQL, NATS și S3. getDetailedReadiness cere o sesiune de administrator și adaugă operația de probă pentru fiecare dependență plus sănătatea outbox-ului: backlog_size, oldest_unpublished_age_seconds, consumer_last_success_at și publish_failures. Eșecurile NATS sau S3 apar într-un răspuns 200; un 503 înseamnă că PostgreSQL a împiedicat evaluarea sesiunii sau a autorizării.
operational-tasks-api/admin.curl.sh · getReadiness and getDetailedReadiness.getReadiness și getDetailedReadiness.#!/bin/sh
# Docula example: readiness through the API (the /app/admin page is API-only).
# getReadiness is public and coarse; getDetailedReadiness needs an administrator session and names each dependency.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD.
set -eu
API=${DOCULA_API:?set DOCULA_API to the API origin, for example http://127.0.0.1:18081}
ORIGIN=${DOCULA_ORIGIN:-$API}
JAR=$(mktemp)
trap 'rm -f "$JAR"' EXIT
# 1. Public readiness (getReadiness): 200 when every dependency is ready, 503 with a Problem body otherwise.
curl --silent --show-error -o /dev/null -w 'health/ready %{http_code}\n' "$API/health/ready"
# 2. Detailed readiness (getDetailedReadiness) after login: PostgreSQL, NATS, S3 and the outbox backlog with durations.
curl --fail --silent --show-error -c "$JAR" -H "Origin: $ORIGIN" -H 'Content-Type: application/json' \
--data-binary "$(jq -n --arg u "${DOCULA_USERNAME:?}" --arg p "${DOCULA_PASSWORD:?}" '{username: $u, password: $p}')" \
"$API/api/v1/auth/login" > /dev/null
curl --fail --silent --show-error -b "$JAR" "$API/api/v1/admin/readiness" |
jq '{status, dependencies: [.dependencies[] | {name, operation, status, duration_ms}]}'
curl sequence sketchSchiță de secvență curl
curl --request GET "$DOCULA_URL/health/ready"curl --request GET "$DOCULA_URL/api/v1/admin/readiness" --cookie "$COOKIE_JAR"
Contracts, formerly /app/contractsContracte, fosta pagină /app/contracts
Create a draft, validate it, update it with expected_revision, freeze a version, publish it, and deprecate superseded versions. Lists and reads are available to auditors and operators; mutations require administrator or engineer. The contracts guide explains the revision and hash semantics.Creezi o ciornă, o validezi, o actualizezi cu expected_revision, îngheți o versiune, o publici și retragi versiunile înlocuite. Listările și citirile sunt disponibile auditorilor și operatorilor; mutațiile cer administrator sau inginer. Ghidul despre contracte explică semantica reviziilor și a hash-urilor.
contract-versioning/curl.sh · The full contract workbench sequence from the contracts example.Secvența completă a bancului de lucru pentru contracte din exemplul de contracte.#!/bin/sh
# Docula example: contract versioning.
# login -> create contract -> version 1 -> publish v1 -> validate v2 draft -> update draft -> version 2 -> publish v2 -> deprecate v1.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD.
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"' 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')
api() {
if [ "$#" -ge 3 ]; then
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
-H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
else
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
fi
}
# with_revision FILE REVISION: the API rejects stale expected_revision values with 412, so always read the live one.
with_revision() { jq --argjson rev "$2" '.expected_revision = $rev' "$1" > "$JAR.body"; printf '%s' "$JAR.body"; }
# 1. Create the draft (createContract) and freeze + publish version 1.
CONTRACT=$(api POST /api/v1/contracts "$HERE/create-contract.request.json")
CONTRACT_ID=$(printf '%s' "$CONTRACT" | jq -r '.contract.id')
REVISION=$(printf '%s' "$CONTRACT" | jq -r '.contract.draft_revision')
V1=$(api POST "/api/v1/contracts/$CONTRACT_ID/versions" "$(with_revision "$HERE/create-version.request.json" "$REVISION")")
V1_NUMBER=$(printf '%s' "$V1" | jq -r '.version.number')
api POST "/api/v1/contracts/$CONTRACT_ID/versions/$V1_NUMBER/publish" "$HERE/publish.request.json" > /dev/null
# 2. Check the version 2 schema before touching the draft (validateContractDraft is side-effect free).
api POST "/api/v1/contracts/$CONTRACT_ID/validate" "$HERE/validate-draft.request.json" | jq -e '.valid == true' > /dev/null
# 3. Replace the draft (updateContractDraft) with the current draft revision, then freeze + publish version 2.
REVISION=$(api GET "/api/v1/contracts/$CONTRACT_ID" | jq -r '.draft_revision')
UPDATED=$(api PUT "/api/v1/contracts/$CONTRACT_ID" "$(with_revision "$HERE/update-draft.request.json" "$REVISION")")
REVISION=$(printf '%s' "$UPDATED" | jq -r '.contract.draft_revision')
V2=$(api POST "/api/v1/contracts/$CONTRACT_ID/versions" "$(with_revision "$HERE/create-version.request.json" "$REVISION")")
V2_NUMBER=$(printf '%s' "$V2" | jq -r '.version.number')
api POST "/api/v1/contracts/$CONTRACT_ID/versions/$V2_NUMBER/publish" "$HERE/publish.request.json" > /dev/null
# 4. Deprecate version 1 (deprecateContractVersion). Published versions are immutable: deprecation only closes them to new releases.
api POST "/api/v1/contracts/$CONTRACT_ID/versions/$V1_NUMBER/deprecate" "$HERE/deprecate.request.json" | jq -e '.version.status == "deprecated"' > /dev/null
api GET "/api/v1/contracts/$CONTRACT_ID" | jq '{id, draft_revision, versions: [.versions[] | {number, status, content_hash}]}'
curl sequence sketchSchiță de secvență curl
curl --request GET "$DOCULA_URL/api/v1/contracts" --cookie "$COOKIE_JAR"curl --request POST "$DOCULA_URL/api/v1/contracts" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary @contract.jsoncurl --request PUT "$DOCULA_URL/api/v1/contracts/$CONTRACT_ID" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary @contract-update.json # includes expected_revisioncurl --request POST "$DOCULA_URL/api/v1/contracts/$CONTRACT_ID/versions" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary '{"expected_revision": 2}'curl --request POST "$DOCULA_URL/api/v1/contracts/$CONTRACT_ID/versions/$VERSION_NUMBER/publish" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" --data-binary '{}'curl --request POST "$DOCULA_URL/api/v1/contracts/$CONTRACT_ID/versions/$OLD_VERSION_NUMBER/deprecate" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" --data-binary '{}'
OperationsOperații
- GET listContracts
/api/v1/contracts - GET getContract
/api/v1/contracts/{contractId} - POST createContract
/api/v1/contracts - PUT updateContractDraft
/api/v1/contracts/{contractId} - POST validateContractDraft
/api/v1/contracts/{contractId}/validate - POST createContractVersion
/api/v1/contracts/{contractId}/versions - POST publishContractVersion
/api/v1/contracts/{contractId}/versions/{versionNumber}/publish - POST deprecateContractVersion
/api/v1/contracts/{contractId}/versions/{versionNumber}/deprecate
Cases, formerly /app/casesCazuri, fosta pagină /app/cases
Generate a deterministic case from a published contract version, seed and kind, or import a fixture payload idempotently, then read it back with its hashes. importFixtureCase had no page at all; it is the way to turn a real, redacted edge case into a reproducible fixture.Generezi un caz determinist dintr-o versiune publicată de contract, un seed și un tip, sau imporți idempotent un payload de fixture, apoi îl citești înapoi cu hash-urile lui. importFixtureCase nu a avut niciodată o pagină; este modul de a transforma un caz-limită real, anonimizat, într-un fixture reproductibil.
operational-tasks-api/generate-case.request.json · generateCase body: contract version, kind and deterministic seed.Corpul generateCase: versiunea contractului, tipul și seed-ul determinist.{
"contract_version_id": "11111111-1111-4111-8111-000000000001",
"kind": "valid",
"seed": 42
}
operational-tasks-api/import-case.request.json · importFixtureCase body: a hand-written valid payload.Corpul importFixtureCase: un payload valid scris manual.{
"contract_version_id": "11111111-1111-4111-8111-000000000001",
"kind": "valid",
"payload": {
"first_name": "Iulia",
"last_name": "Popescu",
"email": "[email protected]"
}
}
operational-tasks-api/cases.curl.sh · listCases, generateCase, getCase, importFixtureCase.listCases, generateCase, getCase, importFixtureCase.#!/bin/sh
# Docula example: cases through the API (the /app/cases workbench is API-only).
# login -> listCases -> generateCase (deterministic, seeded) -> getCase -> importFixtureCase.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD,
# DOCULA_CONTRACT_VERSION_ID (a published contract version).
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"' 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')
api() {
if [ "$#" -ge 3 ]; then
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
-H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
else
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
fi
}
# 1. List existing cases (listCases).
api GET /api/v1/cases | jq '.cases | length'
# 2. Generate a synthetic valid case for the contract version (generateCase). Same seed + same contract hash = same case.
jq --arg c "${DOCULA_CONTRACT_VERSION_ID:?}" '.contract_version_id = $c' "$HERE/generate-case.request.json" > "$JAR.body"
CASE=$(api POST /api/v1/cases/generate "$JAR.body")
CASE_ID=$(printf '%s' "$CASE" | jq -r '.id')
printf '%s' "$CASE" | jq '{id, kind, seed, provider, content_hash}'
# 3. Read it back (getCase).
api GET "/api/v1/cases/$CASE_ID" | jq '.payload'
# 4. Import a hand-written payload as a fixture case (importFixtureCase). Only valid and boundary kinds can be imported.
jq --arg c "${DOCULA_CONTRACT_VERSION_ID:?}" '.contract_version_id = $c' "$HERE/import-case.request.json" > "$JAR.body"
api POST /api/v1/cases/import "$JAR.body" | jq '{id, kind, provider, generator_version}'
curl sequence sketchSchiță de secvență curl
curl --request POST "$DOCULA_URL/api/v1/cases/generate" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary @generate-case.json # contract_version_id, kind, seedcurl --request POST "$DOCULA_URL/api/v1/cases/import" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary @import-case.json # contract_version_id, kind, payloadcurl --request GET "$DOCULA_URL/api/v1/cases/$CASE_ID" --cookie "$COOKIE_JAR"
Legacy transforms, formerly /app/transformsTransformări în format vechi, fosta pagină /app/transforms
The legacy transform family keeps the declarative-v1 language with expected_revision concurrency and no calendar. Validate unsaved source, preview it against a stored case to obtain the deterministic output and output_hash, freeze a version and publish it. New work should prefer the canonical transformer family described in the transforms guide.Familia veche de transformări păstrează limbajul declarative-v1 cu concurență prin expected_revision și fără calendar. Validezi sursa nesalvată, o previzualizezi față de un caz stocat pentru a obține output-ul determinist și output_hash, îngheți o versiune și o publici. Lucrările noi ar trebui să prefere familia canonică de transformatori descrisă în ghidul despre transformări.
operational-tasks-api/validate-transform.request.json · validateTransformDraft body; source embeds the declarative-v1 program.Corpul validateTransformDraft; source încorporează programul declarative-v1.{
"source": "{\n \"language\": \"declarative-v1\",\n \"output\": {\n \"first_name\": { \"$copy\": \"/first_name\" },\n \"middle_name\": { \"$copy_optional\": \"/middle_name\" },\n \"display_name\": {\n \"$concat\": [\n { \"$copy\": \"/first_name\" },\n { \"$literal\": \" \" },\n { \"$copy\": \"/last_name\" }\n ]\n },\n \"preferred_contact\": {\n \"$coalesce\": [\n { \"$copy_optional\": \"/phone\" },\n { \"$copy_optional\": \"/email\" },\n { \"$literal\": \"none\" }\n ]\n },\n \"source\": { \"$literal\": \"docs-example\" }\n }\n}\n",
"format": "json"
}
operational-tasks-api/preview-transform.request.json · previewTransform body: the same source plus the case to run it against.Corpul previewTransform: aceeași sursă plus cazul pe care rulează.{
"source": "{\n \"language\": \"declarative-v1\",\n \"output\": {\n \"first_name\": { \"$copy\": \"/first_name\" },\n \"middle_name\": { \"$copy_optional\": \"/middle_name\" },\n \"display_name\": {\n \"$concat\": [\n { \"$copy\": \"/first_name\" },\n { \"$literal\": \" \" },\n { \"$copy\": \"/last_name\" }\n ]\n },\n \"preferred_contact\": {\n \"$coalesce\": [\n { \"$copy_optional\": \"/phone\" },\n { \"$copy_optional\": \"/email\" },\n { \"$literal\": \"none\" }\n ]\n },\n \"source\": { \"$literal\": \"docs-example\" }\n }\n}\n",
"format": "json",
"case_id": "44444444-4444-4444-8444-000000000001"
}
operational-tasks-api/update-transform-draft.request.json · updateTransformDraft body with the expected draft revision.Corpul updateTransformDraft cu revizia de ciornă așteptată.{
"name": "Person welcome projection",
"description": "Docs example: draft revised through the API-only transform workbench.",
"source": "{\n \"language\": \"declarative-v1\",\n \"output\": {\n \"first_name\": { \"$copy\": \"/first_name\" },\n \"middle_name\": { \"$copy_optional\": \"/middle_name\" },\n \"display_name\": {\n \"$concat\": [\n { \"$copy\": \"/first_name\" },\n { \"$literal\": \" \" },\n { \"$copy\": \"/last_name\" }\n ]\n },\n \"preferred_contact\": {\n \"$coalesce\": [\n { \"$copy_optional\": \"/phone\" },\n { \"$copy_optional\": \"/email\" },\n { \"$literal\": \"none\" }\n ]\n },\n \"source\": { \"$literal\": \"docs-example\" }\n }\n}\n",
"format": "json",
"expected_revision": 1
}
operational-tasks-api/transforms.curl.sh · listTransforms, validateTransformDraft, previewTransform, updateTransformDraft, getTransform.listTransforms, validateTransformDraft, previewTransform, updateTransformDraft, getTransform.#!/bin/sh
# Docula example: transforms through the API (the /app/transforms workbench is API-only).
# login -> listTransforms -> validateTransformDraft -> previewTransform against a case -> updateTransformDraft -> getTransform.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD,
# DOCULA_TRANSFORM_ID (an existing transform) and DOCULA_CASE_ID (a valid or boundary case to preview against).
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"' 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')
api() {
if [ "$#" -ge 3 ]; then
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
-H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
else
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
fi
}
# 1. Page through the registry (listTransforms).
api GET '/api/v1/transforms?limit=20' | jq '.transforms[] | {id, name, draft_revision}'
# 2. Validate a program without saving it (validateTransformDraft): syntax, operators and limits.
api POST "/api/v1/transforms/${DOCULA_TRANSFORM_ID:?}/validate" "$HERE/validate-transform.request.json" | jq '.valid'
# 3. Preview the program against a stored case (previewTransform): returns the canonical output and its hash.
jq --arg k "${DOCULA_CASE_ID:?}" '.case_id = $k' "$HERE/preview-transform.request.json" > "$JAR.body"
api POST "/api/v1/transforms/$DOCULA_TRANSFORM_ID/preview" "$JAR.body" | jq '{output, output_hash}'
# 4. Save the draft (updateTransformDraft) with the live draft revision; a stale revision is rejected with 412.
REVISION=$(api GET "/api/v1/transforms/$DOCULA_TRANSFORM_ID" | jq -r '.draft_revision')
jq --argjson rev "$REVISION" '.expected_revision = $rev' "$HERE/update-transform-draft.request.json" > "$JAR.body"
api PUT "/api/v1/transforms/$DOCULA_TRANSFORM_ID" "$JAR.body" | jq '{id: .transform.id, draft_revision: .transform.draft_revision}'
# 5. Freeze and publish as in the declarative-transform example (createTransformVersion, publishTransformVersion).
api GET "/api/v1/transforms/$DOCULA_TRANSFORM_ID" | jq '{draft_revision, versions: [.versions[] | {number, status}]}'
curl sequence sketchSchiță de secvență curl
curl --request POST "$DOCULA_URL/api/v1/transforms" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary @transform.json # name, description, source, formatcurl --request POST "$DOCULA_URL/api/v1/transforms/$TRANSFORM_ID/preview" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary @transform-preview.json # source, format, case_idcurl --request POST "$DOCULA_URL/api/v1/transforms/$TRANSFORM_ID/versions" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary '{"expected_revision": 1}'curl --request POST "$DOCULA_URL/api/v1/transforms/$TRANSFORM_ID/versions/$VERSION_NUMBER/publish" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" --data-binary '{}'
OperationsOperații
- GET listTransforms
/api/v1/transforms - GET getTransform
/api/v1/transforms/{transformId} - POST createTransform
/api/v1/transforms - PUT updateTransformDraft
/api/v1/transforms/{transformId} - POST validateTransformDraft
/api/v1/transforms/{transformId}/validate - POST previewTransform
/api/v1/transforms/{transformId}/preview - POST createTransformVersion
/api/v1/transforms/{transformId}/versions - POST publishTransformVersion
/api/v1/transforms/{transformId}/versions/{versionNumber}/publish
Templates, formerly /app/templatesȘabloane, fosta pagină /app/templates
The legacy page uploaded a DOCX as an immediate multipart version and previewed it against a case and transform version. The same operations accept either the JSON canonical body or the legacy multipart upload; the templates guide lists the validator limits that apply to both.Pagina veche încărca un DOCX ca versiune multipart imediată și îl previzualiza față de un caz și o versiune de transformare. Aceleași operații acceptă fie body-ul canonic JSON, fie încărcarea multipart veche; ghidul despre șabloane listează limitele validatorului care se aplică ambelor.
operational-tasks-api/create-template.request.json · createTemplate JSON body for a canonical template family.Corpul JSON createTemplate pentru o familie de șabloane canonice.{
"slug": "person-welcome-letter",
"name": "Person welcome letter",
"description": "Docs example: canonical template family created through the API-only template workbench."
}
operational-tasks-api/preview-template.request.json · previewTemplateVersion body: transform version and case used to resolve every binding.Corpul previewTemplateVersion: versiunea transformării și cazul folosite pentru fiecare binding.{
"transform_version_id": "22222222-2222-4222-8222-000000000001",
"case_id": "44444444-4444-4444-8444-000000000001"
}
operational-tasks-api/templates.curl.sh · listTemplates, createTemplate (multipart), getTemplate, createTemplateVersion, previewTemplateVersion.listTemplates, createTemplate (multipart), getTemplate, createTemplateVersion, previewTemplateVersion.#!/bin/sh
# Docula example: templates through the API (the /app/templates workbench is API-only).
# login -> listTemplates -> createTemplate (multipart upload of a .docx) -> getTemplate -> createTemplateVersion -> previewTemplateVersion.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD,
# DOCULA_TRANSFORM_VERSION_ID (published) and DOCULA_CASE_ID (valid or boundary case) for the preview.
# The .docx comes from the docx-template example next to this directory.
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"' 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')
api() {
if [ "$#" -ge 3 ]; then
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
-H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
else
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
fi
}
DOCX=${DOCULA_TEMPLATE_DOCX:-$(dirname "$HERE")/docx-template/template.docx}
# 1. Page through the registry (listTemplates).
api GET '/api/v1/templates?limit=20' | jq '.templates[] | {id, slug, name}'
# 2. Upload a template (createTemplate, multipart/form-data). The API validates the archive, extracts {{/pointer}} bindings
# and stores version 1 immediately. Alternatively POST create-template.request.json as JSON to create an empty canonical family.
TEMPLATE=$(curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
-F 'name=Person welcome letter' -F 'description=Docs example: uploaded through the API-only template workbench.' \
-F "file=@$DOCX;type=application/vnd.openxmlformats-officedocument.wordprocessingml.document" "$API/api/v1/templates")
TEMPLATE_ID=$(printf '%s' "$TEMPLATE" | jq -r '.template.id')
printf '%s' "$TEMPLATE" | jq '.template.versions[] | {number, byte_size, content_hash, bindings}'
# 3. Read the family (getTemplate).
api GET "/api/v1/templates/$TEMPLATE_ID" | jq '{id, name, versions: (.versions | length)}'
# 4. Add a version from a revised file (createTemplateVersion, multipart/form-data). Identical bytes are rejected as a duplicate.
VERSION=$(curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
-F "file=@${DOCULA_TEMPLATE_DOCX_V2:-$DOCX};type=application/vnd.openxmlformats-officedocument.wordprocessingml.document" \
"$API/api/v1/templates/$TEMPLATE_ID/versions" || true)
VERSION_NUMBER=$(printf '%s' "$VERSION" | jq -r '.version.number // 1')
# 5. Resolve every binding against a transform version and a case (previewTemplateVersion). Missing or null bindings fail here,
# before any release is published.
jq --arg t "${DOCULA_TRANSFORM_VERSION_ID:?}" --arg k "${DOCULA_CASE_ID:?}" '.transform_version_id = $t | .case_id = $k' \
"$HERE/preview-template.request.json" > "$JAR.body"
api POST "/api/v1/templates/$TEMPLATE_ID/versions/$VERSION_NUMBER/preview" "$JAR.body" | jq '{bindings, bindings_hash, preview_hash}'
curl sequence sketchSchiță de secvență curl
curl --request POST "$DOCULA_URL/api/v1/templates" --cookie "$COOKIE_JAR" \ --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --form "name=Customer notice" --form "[email protected]"curl --request POST "$DOCULA_URL/api/v1/templates/$TEMPLATE_ID/versions" --cookie "$COOKIE_JAR" \ --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --form "[email protected]"curl --request POST "$DOCULA_URL/api/v1/templates/$TEMPLATE_ID/versions/$VERSION_NUMBER/preview" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" --header "X-CSRF-Token: $CSRF_TOKEN" \ --data-binary @template-preview.json # transform_version_id, case_id
Intake test lab, formerly /app/intake-test-labLaboratorul de ingestie, fosta pagină /app/intake-test-lab
The lab runs bounded synthetic scenarios through a shared release and keeps a fixed fixture cohort. Read the catalog and the readiness of the shared release, submit one scenario with an Idempotency-Key and an empty JSON body, then read the history and the measured-only statistics. The React demo at /app/demo covers the interactive journey; these operations remain for scripted checks.Laboratorul rulează scenarii sintetice limitate printr-un release partajat și păstrează o cohortă fixă de fixture-uri. Citești catalogul și disponibilitatea release-ului partajat, trimiți un scenariu cu un Idempotency-Key și un body JSON gol, apoi citești istoricul și statisticile bazate doar pe măsurători. Demonstrația React de la /app/demo acoperă parcursul interactiv; aceste operații rămân pentru verificări scriptate.
operational-tasks-api/run-intake-lab.request.json · runIntakeLabScenario body: an empty JSON object.Corpul runIntakeLabScenario: un obiect JSON gol.{}
operational-tasks-api/intake-lab.curl.sh · getIntakeLabCatalog, runIntakeLabScenario, getIntakeLabHistory, getIntakeLabStatistics.getIntakeLabCatalog, runIntakeLabScenario, getIntakeLabHistory, getIntakeLabStatistics.#!/bin/sh
# Docula example: intake test lab through the API (the /app/intake-test-lab page is API-only; /app/demo covers the demo journey).
# login -> getIntakeLabCatalog -> runIntakeLabScenario -> getIntakeLabHistory -> getIntakeLabStatistics.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD.
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"' 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')
api() {
if [ "$#" -ge 3 ]; then
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
-H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
else
curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
fi
}
# 1. The fixed scenario catalog (getIntakeLabCatalog): monthly, semiannual, annual and invalid.
api GET /api/v1/intake-test-lab/scenarios | jq '.scenarios[] | {id, expected_outcome, schedule_interval_months}'
# 2. Run one scenario through the shared release (runIntakeLabScenario). The body is an empty JSON object; Idempotency-Key is required.
RUN=$(curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
-H "Idempotency-Key: docs-intake-lab-$(date +%Y%m%d%H%M%S)" -H 'Content-Type: application/json' \
--data-binary @"$HERE/run-intake-lab.request.json" "$API/api/v1/intake-test-lab/runs/monthly")
printf '%s' "$RUN" | jq '{execution_id: .execution.id, status: .execution.status, idempotent_replay}'
# 3. Recent runs and aggregate statistics (getIntakeLabHistory, getIntakeLabStatistics).
api GET '/api/v1/intake-test-lab/history?limit=10' | jq '.runs | length'
api GET /api/v1/intake-test-lab/statistics | jq '.'
curl sequence sketchSchiță de secvență curl
curl --request GET "$DOCULA_URL/api/v1/intake-test-lab/scenarios" --cookie "$COOKIE_JAR"curl --request POST "$DOCULA_URL/api/v1/intake-test-lab/runs/$SCENARIO_ID" --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 '{}'curl --request GET "$DOCULA_URL/api/v1/intake-test-lab/history" --get --data-urlencode "limit=50" --cookie "$COOKIE_JAR"curl --request GET "$DOCULA_URL/api/v1/intake-test-lab/statistics" --cookie "$COOKIE_JAR"
Canonical configuration with no page at allConfigurare canonică fără nicio pagină
Ingestion models, canonical transformers, the canonical template extensions, release validation and lookup, configuration deployments and submission element reads never had a business page. They share the canonical protocol: Idempotency-Key on creation and publication, a strong ETag returned by every draft read and echoed in If-Match, atomic publish with effective_from, cancellation of unpinned future activations, and versions:resolve with an at timestamp. The contracts and releases guides walk through each family; the reference pages below carry the exact schemas.Modelele de ingestie, transformatorii canonici, extensiile canonice ale șabloanelor, validarea și căutarea release-urilor, deployment-urile de configurare și citirile elementelor unei solicitări nu au avut niciodată o pagină de business. Ele împart protocolul canonic: Idempotency-Key la creare și publicare, un ETag ferm întors de fiecare citire a ciornei și reflectat în If-Match, publicare atomică cu effective_from, anularea activărilor viitoare nefixate și versions:resolve cu un timestamp at. Ghidurile despre contracte și release-uri parcurg fiecare familie; paginile de referință de mai jos poartă schemele exacte.
curl sequence sketchSchiță de secvență curl
curl --request POST "$DOCULA_URL/api/v1/ingestion-models" --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 @ingestion-model.jsoncurl --request GET "$DOCULA_URL/api/v1/ingestion-models/$INGESTION_MODEL_ID/draft" --cookie "$COOKIE_JAR" --include # read the ETagcurl --request POST "$DOCULA_URL/api/v1/ingestion-models/$INGESTION_MODEL_ID/versions" --cookie "$COOKIE_JAR" \ --header "Content-Type: application/json" --header "Origin: $DOCULA_URL" \ --header "X-CSRF-Token: $CSRF_TOKEN" --header "Idempotency-Key: $IDEMPOTENCY_KEY" --header "If-Match: $ETAG" \ --data-binary '{}'curl --request POST "$DOCULA_URL/api/v1/ingestion-models/$INGESTION_MODEL_ID/versions/$VERSION_NUMBER/publish" --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 @effective-from.jsoncurl --request GET "$DOCULA_URL/api/v1/releases/lookup" --get --cookie "$COOKIE_JAR" \ --data-urlencode "name=$RELEASE_NAME" --data-urlencode "description=$RELEASE_DESCRIPTION" \ --data-urlencode "contract_version_id=$CONTRACT_VERSION_ID" --data-urlencode "transform_version_id=$TRANSFORM_VERSION_ID" \ --data-urlencode "template_version_id=$TEMPLATE_VERSION_ID" --data-urlencode "case_id=$CASE_ID"curl --request GET "$DOCULA_URL/api/v1/submissions/$SUBMISSION_ID/elements/$ELEMENT_ID" --cookie "$COOKIE_JAR"
OperationsOperații
- GET listIngestionModels
/api/v1/ingestion-models - POST createIngestionModel
/api/v1/ingestion-models - GET getIngestionModelBySlug
/api/v1/ingestion-models/by-slug/{ingestionModelSlug} - GET getIngestionModel
/api/v1/ingestion-models/{ingestionModelId} - GET getIngestionModelDraft
/api/v1/ingestion-models/{ingestionModelId}/draft - PATCH updateIngestionModelDraft
/api/v1/ingestion-models/{ingestionModelId}/draft - GET listIngestionModelVersions
/api/v1/ingestion-models/{ingestionModelId}/versions - POST versionIngestionModel
/api/v1/ingestion-models/{ingestionModelId}/versions - GET resolveIngestionModelVersion
/api/v1/ingestion-models/{ingestionModelId}/versions:resolve - GET listIngestionModelActivations
/api/v1/ingestion-models/{ingestionModelId}/activations - POST validateIngestionModel
/api/v1/ingestion-models/{ingestionModelId}/validate - POST testIngestionModel
/api/v1/ingestion-models/{ingestionModelId}/test - POST publishIngestionModelVersionCalendar
/api/v1/ingestion-models/{ingestionModelId}/versions/{versionNumber}/publish - POST cancelIngestionModelActivation
/api/v1/ingestion-models/{ingestionModelId}/activations/{activationId}/cancel - GET listCanonicalTransformers
/api/v1/transformers - POST createCanonicalTransformer
/api/v1/transformers - GET getCanonicalTransformer
/api/v1/transformers/{transformerId} - GET getCanonicalTransformerDraft
/api/v1/transformers/{transformerId}/draft - PATCH updateCanonicalTransformerDraft
/api/v1/transformers/{transformerId}/draft - GET listCanonicalTransformerVersions
/api/v1/transformers/{transformerId}/versions - POST versionCanonicalTransformer
/api/v1/transformers/{transformerId}/versions - GET resolveCanonicalTransformerVersion
/api/v1/transformers/{transformerId}/versions:resolve - GET listCanonicalTransformerActivations
/api/v1/transformers/{transformerId}/activations - POST validateCanonicalTransformer
/api/v1/transformers/{transformerId}/validate - POST testCanonicalTransformer
/api/v1/transformers/{transformerId}/test - POST publishCanonicalTransformerVersion
/api/v1/transformers/{transformerId}/versions/{versionNumber}/publish - POST cancelCanonicalTransformerActivation
/api/v1/transformers/{transformerId}/activations/{activationId}/cancel - GET getCanonicalTemplateDraft
/api/v1/templates/{templateId}/draft - POST updateCanonicalTemplateDraft
/api/v1/templates/{templateId}/draft-content - GET listCanonicalTemplateVersions
/api/v1/templates/{templateId}/versions - GET resolveCanonicalTemplateVersion
/api/v1/templates/{templateId}/versions:resolve - GET listCanonicalTemplateActivations
/api/v1/templates/{templateId}/activations - POST validateCanonicalTemplateDraft
/api/v1/templates/{templateId}/validate - POST testCanonicalTemplateDraft
/api/v1/templates/{templateId}/test - POST publishCanonicalTemplateVersion
/api/v1/templates/{templateId}/versions/{versionNumber}/publish - POST cancelCanonicalTemplateActivation
/api/v1/templates/{templateId}/activations/{activationId}/cancel - POST validateCanonicalRelease
/api/v1/releases/validate - GET findPublishedRelease
/api/v1/releases/lookup - POST validateConfigurationDeployment
/api/v1/configuration-deployments/validate - GET listConfigurationDeployments
/api/v1/configuration-deployments - POST applyConfigurationDeployment
/api/v1/configuration-deployments - GET getConfigurationDeployment
/api/v1/configuration-deployments/{deploymentId} - GET getSubmissionElement
/api/v1/submissions/{submissionId}/elements/{elementId}
