Government and public records

FOIA redaction workflow

Visual FOIA redaction workflow for PDFs: intake records, detect PII, review redactions, sanitize metadata, and publish an auditable link.

Execution status

The template snapshot is public and importable. The private run path depends on deployed runner support.

Status
Schema-only template today
Create
POST /v1/workflows with this definition returns object=workflow, execution_mode=step_definition, runnable=false, readiness.status=schema_only.
Run
POST /v1/runs against this workflow returns HTTP 409 with "Workflow definition is schema-only; step execution is not deployed yet."

This FOIA redaction workflow can be created, inspected, embedded, and visualized today. It does not execute end-to-end yet because the deployed finite step runner only supports the invoice human-review subset.

Example runs

These visual before/after fixtures live in the template snapshot so docs, embeds, and import screens do not depend on a separate sample PDF page.

exrun_foia_county_email_release

County email release package

A records team prepares a release package with staff emails, phone numbers, account numbers, and addresses that must be reviewed before publish.

schema only
Before

Source FOIA package PDF

Sensitive source
Open before PDF
After

Redacted derivative PDF

Publish candidate
Open after PDF

Input

{
  "pdf_url": "https://agency.example/records/foia-request-2026-019.pdf",
  "redaction_policy": {
    "pii": [
      "ssn",
      "email",
      "phone",
      "address",
      "account_number"
    ],
    "timing": "before_publish"
  },
  "reviewer": {
    "role": "records_officer",
    "approval_key": "foia-redaction"
  },
  "publish": {
    "visibility": "signed",
    "include_audit_manifest": true,
    "include_proof_cards": true
  }
}

Output

{
  "redacted_document_id": "doc_redacted_...",
  "published_view": "https://app.okrapdf.com/view/...",
  "redaction_decision_log": [
    {
      "page": 2,
      "entity": "email",
      "decision": "approved"
    }
  ],
  "audit_manifest": {
    "source_document_id": "doc_source_...",
    "derivative_document_id": "doc_redacted_...",
    "reviewer": "records_officer"
  }
}

Lifecycle

  1. create workflow definition
  2. start run after the redaction/publish runner supports this step set
  3. intake private source PDF
  4. detect redaction candidates
  5. wait for human.approval
  6. apply true redactions
  7. sanitize metadata, forms, and annotations
  8. publish redacted derivative
POST/v1/workflows

Create or import the workflow

Request

{
  "name": "FOIA redaction workflow",
  "definition": {
    "steps": "see template snapshot"
  }
}

Response

{
  "object": "workflow",
  "workflow_id": "wf_...",
  "execution_mode": "step_definition",
  "runnable": false,
  "readiness": {
    "status": "schema_only"
  }
}
POST/v1/runs

Attempt run today

Request

{
  "workflow_id": "wf_...",
  "inputs": {
    "pdf_url": "https://agency.example/records/foia-request-2026-019.pdf"
  }
}

Response

{
  "status": 409,
  "error": "Workflow definition is schema-only; step execution is not deployed yet"
}
  • This example run is attached to the FOIA workflow template for SEO, docs, and import screens.
  • It is not a standalone sample PDF and it is not a claim that the redaction/publish runner is live.
  • The same shape becomes executable when the deployed finite runner supports the redaction, approval, sanitize, and publish step set.

Comparable API standards

These examples make the template legible to developers who already understand async queues, prediction APIs, and public workflow galleries.

fal-style async queue

Relevant when redaction, OCR, sanitization, or review can take longer than a blocking API request should stay open.

Similar pattern: Submit a long-running job, keep a request id, poll status/results, or receive a webhook.

Okra pattern: Create or import a workflow, start a run with PDF inputs, wait on status_url or webhook events, then fetch the approved output.

Good fit for backend queues, agent workers, batch portals, and document automation that already treat AI work as jobs.

Replicate-style prediction

Relevant when product teams need a stable run record that can be shown to a reviewer or support person, not just a final file.

Similar pattern: Create a prediction with input, get an id plus status URL, optionally include a webhook, then inspect the web view.

Okra pattern: A workflow run has a workflow_id, run_id, status_url, approval gate, and visualization endpoint for the live run graph.

Good fit if you want prediction-like lifecycle semantics for PDF operations with human review before publish.

n8n community workflow

Relevant when the workflow itself is the artifact: sales pages, docs, internal enablement, and customer-specific variants.

Similar pattern: Public template page for discovery, isolated visual embed for sharing, JSON definition for import or automation.

Okra pattern: workflows.okrapdf.com hosts crawlable metadata and JSON; embed.okrapdf.com renders only the isolated workflow visual.

Good fit for SEO pages, integration docs, and template galleries where private runs must stay separated from public content.

API examples

The snapshot includes these examples so generated docs, customer demos, and import screens can explain the workflow without scraping the page.

POST/v1/workflows

Create a dynamic workflow definition

Use the resource-style workflow API to store a configurable workflow source. This mirrors a model or function registration step.

Expected response shape
{
  "object": "workflow",
  "id": "wf_...",
  "workflow_id": "wf_...",
  "version_id": "wv_...",
  "execution_mode": "agent_workflow_script",
  "runnable": true,
  "run": null
}
const source = [
  "phase('Detect');",
  "const plan = await agent('Detect FOIA redactions in the supplied PDF.', { label: 'redactions' });",
  "phase('Review');",
  "const decision = await human.approval({ approval_key: 'foia-redaction', prompt: 'Approve redactions before publish', event_type: 'redaction-approval', timeout: '24 hours', on_timeout: 'reject' });",
  "return { plan, decision };"
].join("\n");

const workflow = await fetch("https://api.okrapdf.com/v1/workflows", {
  method: "POST",
  headers: {
    "Authorization": "Bearer $OKRAPDF_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    name: "FOIA redaction review",
    definition: { code: source }
  })
}).then((res) => res.json());
POST/v1/runs

Run it like an async job

This is the run shape for executable workflows. For this FOIA step-definition template today, the API returns 409 until the redaction/publish runner lands.

Expected response shape
{
  "object": "run",
  "id": "dynrun_...",
  "run_id": "dynrun_...",
  "workflow_id": "wf_...",
  "status": "queued",
  "status_url": "/v1/runs/dynrun_..."
}
const run = await fetch("https://api.okrapdf.com/v1/runs", {
  method: "POST",
  headers: {
    "Authorization": "Bearer $OKRAPDF_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    workflowId: workflow.id,
    inputs: {
      pdf_url: "https://example.gov/request.pdf",
      webhook_url: "https://example.com/webhooks/okra"
    }
  })
}).then((res) => res.json());
GET/v1/runs/{run_id}/visualization

Show status and the live run graph

Fetch the status endpoint for lifecycle state, and fetch the visualization endpoint when you want a UI to show the current node and review gate.

Expected response shape
{
  "object": "workflow_run_visualization_projection",
  "version": "okra.workflow.run.visualization.v1",
  "run_id": "dynrun_...",
  "nodes": [
    "intake",
    "detect",
    "review",
    "publish"
  ],
  "current_step": "review"
}
const status = await fetch(run.status_url, {
  headers: { "Authorization": "Bearer $OKRAPDF_API_KEY" }
}).then((res) => res.json());

const visual = await fetch(`https://api.okrapdf.com/v1/runs/${run.run_id}/visualization`, {
  headers: { "Authorization": "Bearer $OKRAPDF_API_KEY" }
}).then((res) => res.json());

Embeddable iframe

<iframe src="https://embed.okrapdf.com/workflows/foia-redaction-workflow" title="FOIA redaction workflow" loading="lazy" sandbox="allow-scripts allow-same-origin"></iframe>

Template snapshot

{
  "object": "okrapdf.workflow_template_snapshot",
  "version": "okra.workflow_template_snapshot.v1",
  "slug": "foia-redaction-workflow",
  "template_id": "tpl_foia_redaction_workflow",
  "name": "FOIA redaction workflow",
  "vertical": "Government and public records",
  "description": "Visual FOIA redaction workflow for PDFs: intake records, detect PII, review redactions, sanitize metadata, and publish an auditable link.",
  "urls": {
    "content": "https://workflows.okrapdf.com/workflows/foia-redaction-workflow",
    "embed": "https://embed.okrapdf.com/workflows/foia-redaction-workflow",
    "registry": "https://workflows.okrapdf.com/templates/foia-redaction-workflow",
    "json": "https://workflows.okrapdf.com/templates/foia-redaction-workflow.json"
  },
  "execution_status": {
    "label": "Schema-only template today",
    "status": "schema_only",
    "summary": "This FOIA redaction workflow can be created, inspected, embedded, and visualized today. It does not execute end-to-end yet because the deployed finite step runner only supports the invoice human-review subset.",
    "createResult": "POST /v1/workflows with this definition returns object=workflow, execution_mode=step_definition, runnable=false, readiness.status=schema_only.",
    "runResult": "POST /v1/runs against this workflow returns HTTP 409 with \"Workflow definition is schema-only; step execution is not deployed yet.\""
  },
  "example_runs": [
    {
      "id": "exrun_foia_county_email_release",
      "title": "County email release package",
      "summary": "A records team prepares a release package with staff emails, phone numbers, account numbers, and addresses that must be reviewed before publish.",
      "status": "schema_only",
      "visualArtifacts": [
        {
          "role": "before",
          "label": "Before",
          "title": "Source FOIA package PDF",
          "badge": "Sensitive source",
          "fileName": "county-email-release-source.pdf",
          "pageLabel": "Page 2 of 6",
          "pdfUrl": "https://workflows.okrapdf.com/workflow-assets/foia-redaction-workflow/examples/county-email-release/source.pdf",
          "lines": [
            {
              "text": "County Public Works - Email Release",
              "emphasis": true
            },
            {
              "text": "From: Alex Chen <alex.chen@county.example>"
            },
            {
              "text": "Phone: (555) 014-2198"
            },
            {
              "text": "Resident account: ACCT-8842-19"
            },
            {
              "text": "Address: 412 Oak Bend Road, Unit 7"
            },
            {
              "text": "Decision: release after records-officer review"
            }
          ]
        },
        {
          "role": "after",
          "label": "After",
          "title": "Redacted derivative PDF",
          "badge": "Publish candidate",
          "fileName": "county-email-release-redacted.pdf",
          "pageLabel": "Page 2 of 6",
          "pdfUrl": "https://workflows.okrapdf.com/workflow-assets/foia-redaction-workflow/examples/county-email-release/redacted.pdf",
          "lines": [
            {
              "text": "County Public Works - Email Release",
              "emphasis": true
            },
            {
              "text": "From: redacted staff email",
              "redacted": true
            },
            {
              "text": "Phone number redacted",
              "redacted": true
            },
            {
              "text": "Resident account redacted",
              "redacted": true
            },
            {
              "text": "Street address redacted",
              "redacted": true
            },
            {
              "text": "Decision: release after records-officer review"
            }
          ]
        }
      ],
      "input": {
        "pdf_url": "https://agency.example/records/foia-request-2026-019.pdf",
        "redaction_policy": {
          "pii": [
            "ssn",
            "email",
            "phone",
            "address",
            "account_number"
          ],
          "timing": "before_publish"
        },
        "reviewer": {
          "role": "records_officer",
          "approval_key": "foia-redaction"
        },
        "publish": {
          "visibility": "signed",
          "include_audit_manifest": true,
          "include_proof_cards": true
        }
      },
      "lifecycle": [
        "create workflow definition",
        "start run after the redaction/publish runner supports this step set",
        "intake private source PDF",
        "detect redaction candidates",
        "wait for human.approval",
        "apply true redactions",
        "sanitize metadata, forms, and annotations",
        "publish redacted derivative"
      ],
      "output": {
        "redacted_document_id": "doc_redacted_...",
        "published_view": "https://app.okrapdf.com/view/...",
        "redaction_decision_log": [
          {
            "page": 2,
            "entity": "email",
            "decision": "approved"
          }
        ],
        "audit_manifest": {
          "source_document_id": "doc_source_...",
          "derivative_document_id": "doc_redacted_...",
          "reviewer": "records_officer"
        }
      },
      "apiCalls": [
        {
          "label": "Create or import the workflow",
          "method": "POST",
          "path": "/v1/workflows",
          "request": {
            "name": "FOIA redaction workflow",
            "definition": {
              "steps": "see template snapshot"
            }
          },
          "response": {
            "object": "workflow",
            "workflow_id": "wf_...",
            "execution_mode": "step_definition",
            "runnable": false,
            "readiness": {
              "status": "schema_only"
            }
          }
        },
        {
          "label": "Attempt run today",
          "method": "POST",
          "path": "/v1/runs",
          "request": {
            "workflow_id": "wf_...",
            "inputs": {
              "pdf_url": "https://agency.example/records/foia-request-2026-019.pdf"
            }
          },
          "response": {
            "status": 409,
            "error": "Workflow definition is schema-only; step execution is not deployed yet"
          }
        }
      ],
      "notes": [
        "This example run is attached to the FOIA workflow template for SEO, docs, and import screens.",
        "It is not a standalone sample PDF and it is not a claim that the redaction/publish runner is live.",
        "The same shape becomes executable when the deployed finite runner supports the redaction, approval, sanitize, and publish step set."
      ]
    }
  ],
  "definition": {
    "object": "workflow_definition",
    "version": "okra.workflow.definition.v1",
    "steps": [
      {
        "id": "intake",
        "type": "document.upload",
        "name": "Intake private records",
        "with": {
          "file": "${inputs.pdf_file}",
          "url": "${inputs.pdf_url}",
          "visibility": "private"
        }
      },
      {
        "id": "detect",
        "type": "document.redact",
        "name": "Detect sensitive material",
        "with": {
          "document_id": "${steps.intake.document_id}",
          "policy": {
            "pii": [
              "ssn",
              "email",
              "phone",
              "address",
              "account_number"
            ],
            "timing": "before_publish"
          },
          "output": "redaction_plan"
        },
        "depends_on": [
          "intake"
        ]
      },
      {
        "id": "review",
        "type": "human.approval",
        "name": "Human review gate",
        "with": {
          "approval_key": "foia-redaction",
          "prompt": "Approve redactions before publishing this public-records PDF.",
          "event_type": "redaction-approval",
          "timeout": "24 hours",
          "on_timeout": "reject",
          "context": {
            "document_id": "${steps.intake.document_id}",
            "redaction_plan": "${steps.detect.redaction_plan_id}"
          }
        },
        "depends_on": [
          "detect"
        ]
      },
      {
        "id": "apply",
        "type": "document.redact.apply",
        "name": "Apply true redactions",
        "with": {
          "document_id": "${steps.intake.document_id}",
          "redaction_plan": "${steps.detect.redaction_plan_id}",
          "approval": "${steps.review.decision}",
          "mode": "true_removal",
          "output": "redacted_document"
        },
        "depends_on": [
          "review"
        ]
      },
      {
        "id": "sanitize",
        "type": "document.sanitize",
        "name": "Sanitize the derivative",
        "with": {
          "document_id": "${steps.apply.document_id}",
          "remove_metadata": true,
          "flatten_annotations": true,
          "flatten_forms": true,
          "output": "sanitized_document"
        },
        "depends_on": [
          "apply"
        ]
      },
      {
        "id": "publish",
        "type": "document.publish.view",
        "name": "Publish a safe view",
        "with": {
          "document_id": "${steps.sanitize.document_id}",
          "source_document_id": "${steps.intake.document_id}",
          "visibility": "signed",
          "view_name": "FOIA redacted public view",
          "include_audit_manifest": true,
          "include_proof_cards": true
        },
        "depends_on": [
          "sanitize"
        ]
      }
    ]
  },
  "visualization": {
    "object": "workflow_visualization",
    "version": "okra.workflow.visualization.v1",
    "projection": "data_pipeline_sequence_v1",
    "source_ast_version": "okra.workflow.ast.v1",
    "nodes": [
      {
        "id": "visual:start",
        "ast_node_id": "start",
        "kind": "start",
        "title": "Start",
        "rank": 0
      },
      {
        "id": "visual:step:intake",
        "ast_node_id": "step:intake",
        "kind": "step",
        "title": "Intake private records",
        "subtitle": "document.upload",
        "status": "defined",
        "rank": 1,
        "metadata": {
          "category": "file_io",
          "node_kind": "document.upload",
          "output_contract": "private_source_document",
          "output": "private_source_document",
          "summary": "Upload the original PDF, URL, or folder batch as a private source. The original stays private and becomes the provenance root.",
          "depends_on": [],
          "input_keys": [
            "file",
            "url",
            "visibility"
          ]
        }
      },
      {
        "id": "visual:step:detect",
        "ast_node_id": "step:detect",
        "kind": "step",
        "title": "Detect sensitive material",
        "subtitle": "document.redact",
        "status": "defined",
        "rank": 2,
        "metadata": {
          "category": "transformation",
          "node_kind": "document.redact",
          "output_contract": "redaction_plan",
          "output": "redaction_plan",
          "summary": "Scan text, OCR layers, form fields, annotations, and page images for PII, account numbers, addresses, and agency-specific policy targets.",
          "depends_on": [
            "intake"
          ],
          "input_keys": [
            "document_id",
            "output",
            "policy"
          ]
        }
      },
      {
        "id": "visual:step:review",
        "ast_node_id": "step:review",
        "kind": "wait",
        "title": "Human review gate",
        "subtitle": "human.approval",
        "status": "defined",
        "rank": 3,
        "metadata": {
          "category": "human_gate",
          "node_kind": "human.approval",
          "output_contract": "approval_decision",
          "output": "approval_decision",
          "summary": "Review every proposed redaction on a page overlay before export. Low-confidence detections remain unresolved until a reviewer decides.",
          "depends_on": [
            "detect"
          ],
          "input_keys": [
            "approval_key",
            "context",
            "event_type",
            "on_timeout",
            "prompt",
            "timeout"
          ],
          "event_type": "redaction-approval",
          "timeout": "24 hours"
        }
      },
      {
        "id": "visual:step:apply",
        "ast_node_id": "step:apply",
        "kind": "step",
        "title": "Apply true redactions",
        "subtitle": "document.redact.apply",
        "status": "defined",
        "rank": 4,
        "metadata": {
          "category": "transformation",
          "node_kind": "document.redact.apply",
          "output_contract": "redacted_document",
          "output": "redacted_document",
          "summary": "Create a derivative with approved content removed rather than hidden by an overlay. Rejecting the gate blocks export.",
          "depends_on": [
            "review"
          ],
          "input_keys": [
            "approval",
            "document_id",
            "mode",
            "output",
            "redaction_plan"
          ]
        }
      },
      {
        "id": "visual:step:sanitize",
        "ast_node_id": "step:sanitize",
        "kind": "step",
        "title": "Sanitize the derivative",
        "subtitle": "document.sanitize",
        "status": "defined",
        "rank": 5,
        "metadata": {
          "category": "transformation",
          "node_kind": "document.sanitize",
          "output_contract": "sanitized_document",
          "output": "sanitized_document",
          "summary": "Remove metadata, flatten annotations and form fields, and prepare the redacted derivative for parsing and public delivery.",
          "depends_on": [
            "apply"
          ],
          "input_keys": [
            "document_id",
            "flatten_annotations",
            "flatten_forms",
            "output",
            "remove_metadata"
          ]
        }
      },
      {
        "id": "visual:step:publish",
        "ast_node_id": "step:publish",
        "kind": "step",
        "title": "Publish a safe view",
        "subtitle": "document.publish.view",
        "status": "defined",
        "rank": 6,
        "metadata": {
          "category": "output",
          "node_kind": "document.publish.view",
          "output_contract": "published_view",
          "output": "published_view",
          "summary": "Publish a signed or public link for the redacted derivative, with the original source id retained only in the audit trail.",
          "depends_on": [
            "sanitize"
          ],
          "input_keys": [
            "document_id",
            "include_audit_manifest",
            "include_proof_cards",
            "source_document_id",
            "view_name",
            "visibility"
          ]
        }
      },
      {
        "id": "visual:end",
        "ast_node_id": "end",
        "kind": "end",
        "title": "End",
        "rank": 7
      }
    ],
    "edges": [
      {
        "id": "visual:edge:start->step:intake",
        "ast_edge_id": "edge:start->step:intake",
        "source": "visual:start",
        "target": "visual:step:intake",
        "kind": "sequence"
      },
      {
        "id": "visual:edge:step:intake->step:detect",
        "ast_edge_id": "edge:step:intake->step:detect",
        "source": "visual:step:intake",
        "target": "visual:step:detect",
        "kind": "dependency"
      },
      {
        "id": "visual:edge:step:detect->step:review",
        "ast_edge_id": "edge:step:detect->step:review",
        "source": "visual:step:detect",
        "target": "visual:step:review",
        "kind": "dependency"
      },
      {
        "id": "visual:edge:step:review->step:apply",
        "ast_edge_id": "edge:step:review->step:apply",
        "source": "visual:step:review",
        "target": "visual:step:apply",
        "kind": "dependency"
      },
      {
        "id": "visual:edge:step:apply->step:sanitize",
        "ast_edge_id": "edge:step:apply->step:sanitize",
        "source": "visual:step:apply",
        "target": "visual:step:sanitize",
        "kind": "dependency"
      },
      {
        "id": "visual:edge:step:sanitize->step:publish",
        "ast_edge_id": "edge:step:sanitize->step:publish",
        "source": "visual:step:sanitize",
        "target": "visual:step:publish",
        "kind": "dependency"
      },
      {
        "id": "visual:edge:step:publish->end",
        "ast_edge_id": "edge:step:publish->end",
        "source": "visual:step:publish",
        "target": "visual:end",
        "kind": "sequence"
      }
    ]
  },
  "relevance_examples": [
    {
      "label": "fal-style async queue",
      "compareTo": "Submit a long-running job, keep a request id, poll status/results, or receive a webhook.",
      "signal": "Relevant when redaction, OCR, sanitization, or review can take longer than a blocking API request should stay open.",
      "okraPattern": "Create or import a workflow, start a run with PDF inputs, wait on status_url or webhook events, then fetch the approved output.",
      "fit": "Good fit for backend queues, agent workers, batch portals, and document automation that already treat AI work as jobs."
    },
    {
      "label": "Replicate-style prediction",
      "compareTo": "Create a prediction with input, get an id plus status URL, optionally include a webhook, then inspect the web view.",
      "signal": "Relevant when product teams need a stable run record that can be shown to a reviewer or support person, not just a final file.",
      "okraPattern": "A workflow run has a workflow_id, run_id, status_url, approval gate, and visualization endpoint for the live run graph.",
      "fit": "Good fit if you want prediction-like lifecycle semantics for PDF operations with human review before publish."
    },
    {
      "label": "n8n community workflow",
      "compareTo": "Public template page for discovery, isolated visual embed for sharing, JSON definition for import or automation.",
      "signal": "Relevant when the workflow itself is the artifact: sales pages, docs, internal enablement, and customer-specific variants.",
      "okraPattern": "workflows.okrapdf.com hosts crawlable metadata and JSON; embed.okrapdf.com renders only the isolated workflow visual.",
      "fit": "Good fit for SEO pages, integration docs, and template galleries where private runs must stay separated from public content."
    }
  ],
  "api_examples": [
    {
      "title": "Create a dynamic workflow definition",
      "description": "Use the resource-style workflow API to store a configurable workflow source. This mirrors a model or function registration step.",
      "method": "POST",
      "path": "/v1/workflows",
      "code": "const source = [\n  \"phase('Detect');\",\n  \"const plan = await agent('Detect FOIA redactions in the supplied PDF.', { label: 'redactions' });\",\n  \"phase('Review');\",\n  \"const decision = await human.approval({ approval_key: 'foia-redaction', prompt: 'Approve redactions before publish', event_type: 'redaction-approval', timeout: '24 hours', on_timeout: 'reject' });\",\n  \"return { plan, decision };\"\n].join(\"\\n\");\n\nconst workflow = await fetch(\"https://api.okrapdf.com/v1/workflows\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer $OKRAPDF_API_KEY\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    name: \"FOIA redaction review\",\n    definition: { code: source }\n  })\n}).then((res) => res.json());",
      "responseShape": {
        "object": "workflow",
        "id": "wf_...",
        "workflow_id": "wf_...",
        "version_id": "wv_...",
        "execution_mode": "agent_workflow_script",
        "runnable": true,
        "run": null
      }
    },
    {
      "title": "Run it like an async job",
      "description": "This is the run shape for executable workflows. For this FOIA step-definition template today, the API returns 409 until the redaction/publish runner lands.",
      "method": "POST",
      "path": "/v1/runs",
      "code": "const run = await fetch(\"https://api.okrapdf.com/v1/runs\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": \"Bearer $OKRAPDF_API_KEY\",\n    \"Content-Type\": \"application/json\"\n  },\n  body: JSON.stringify({\n    workflowId: workflow.id,\n    inputs: {\n      pdf_url: \"https://example.gov/request.pdf\",\n      webhook_url: \"https://example.com/webhooks/okra\"\n    }\n  })\n}).then((res) => res.json());",
      "responseShape": {
        "object": "run",
        "id": "dynrun_...",
        "run_id": "dynrun_...",
        "workflow_id": "wf_...",
        "status": "queued",
        "status_url": "/v1/runs/dynrun_..."
      }
    },
    {
      "title": "Show status and the live run graph",
      "description": "Fetch the status endpoint for lifecycle state, and fetch the visualization endpoint when you want a UI to show the current node and review gate.",
      "method": "GET",
      "path": "/v1/runs/{run_id}/visualization",
      "code": "const status = await fetch(run.status_url, {\n  headers: { \"Authorization\": \"Bearer $OKRAPDF_API_KEY\" }\n}).then((res) => res.json());\n\nconst visual = await fetch(`https://api.okrapdf.com/v1/runs/${run.run_id}/visualization`, {\n  headers: { \"Authorization\": \"Bearer $OKRAPDF_API_KEY\" }\n}).then((res) => res.json());",
      "responseShape": {
        "object": "workflow_run_visualization_projection",
        "version": "okra.workflow.run.visualization.v1",
        "run_id": "dynrun_...",
        "nodes": [
          "intake",
          "detect",
          "review",
          "publish"
        ],
        "current_step": "review"
      }
    }
  ],
  "review_checklist": [
    "Every redaction has a page, bbox, entity class, and reviewer decision.",
    "Rejected or unresolved detections block publish instead of silently passing through.",
    "Metadata and hidden text are removed after redaction, not before review.",
    "The public URL points to the redacted derivative, never the source PDF."
  ],
  "outputs": [
    "Redacted PDF derivative",
    "Signed or public review link",
    "Redaction decision log",
    "Audit manifest with source and derivative ids"
  ]
}