REFRACT Workflow

REFRACT Workflow

REFRACT Workflow is a governed multi-step document workflow engine. It orchestrates structured data collection, validation, approval routing, and final PDF sealing, all within your Azure tenant.

REFRACT Workflow provides structured, form-driven document generation with multi-step data collection, schema-defined validation rules, conditional approval routing, and sealing to a final PDF, with its SHA-256 hash recorded in the chained audit trail.

Enterprise Feature

REFRACT Workflow requires the vaultWorkflow feature gate on your licence. Contact your licence administrator if this feature is not enabled on your tenant.

Needs Azure Blob Storage

REFRACT Workflow keeps its records, its tamper-evident audit chain and its documents in your Azure Blob Storage account, and cannot run without it. The installer deploys that account on every install, whatever your storage routes say, so routing your documents to SharePoint does not turn REFRACT Workflow off. If a deployment is missing the account's settings, REFRACT Workflow refuses every request with BLOB_STORAGE_NOT_CONFIGURED, naming the missing settings, and the Dispatcher's health check reports blobStorage as missing. See Storage Providers.


What REFRACT Workflow Does

Unlike direct render requests (where a caller submits a complete payload), REFRACT Workflow builds the document payload incrementally across multiple participants and steps. When all steps are submitted and validation passes, the workflow routes to the designated approver(s), and on approval the processor seals the final PDF using the full REFRACT PDF render engine.

Not every workflow needs a signature. When acknowledgement or acceptance is enough, REFRACT Workflow's own validation, approval routing, and sealed audit trail are the complete answer - no REFRACT eSign or Guided Document step required. See Document Completion Options for how this compares to REFRACT eSign and Guided Documents.

Multi-Step Data Collection

Schema-defined steps guide participants through structured form fields. Each step is saved independently; participants can return and continue.

Schema Validation

YAML-defined validation rules evaluate collected data before submission. Error-severity rules block advancement; warning-severity rules surface to the approver.

Conditional Approval Routing

Routing rules evaluate collected data to determine the correct approver(s). Supports first-match, sequential, and parallel routing modes.

Review & Change Requests

Reviewers can return a submission with structured change requests. Participants address each item and resubmit without restarting the workflow.

Governed PDF Sealing

On approval, REFRACT Workflow enqueues a seal message. The processor renders the final PDF using the workflow template and the complete collected data snapshot.

Post-Seal Automation

REFRACT eSign can be triggered automatically after sealing, configured in the workflow session at creation time.


Workflow Lifecycle States

A workflow instance moves through the following states:

StateDescription
draftCreated but not yet started by the participant.
in-progressParticipant is actively filling steps.
validatingServer is evaluating schema validation rules (transient).
validation-failedOne or more error-severity rules failed. Participant must correct data.
pending-approvalAll steps submitted and validation passed. Awaiting approver action.
under-reviewApprover has opened the submission for review.
changes-requestedApprover returned the submission with structured change requests.
approvedApprover accepted the submission. Seal is being queued.
sealingSeal message enqueued; processor is rendering the final PDF.
completeFinal PDF sealed and stored in Blob Storage.
rejectedApprover rejected the submission. No PDF is produced.
escalatedSubmission was escalated to a secondary approver.
cancelledWorkflow was cancelled before completion.

Step Lifecycle States

Each step within a workflow tracks its own state independently:

StateDescription
pendingNot yet started by the participant.
in-progressParticipant has opened the step.
submittedParticipant completed and submitted the step.
approvedStep accepted during review (sub-flow).
rejectedStep returned during review (sub-flow).

Portal Access & OTP

When a workflow is created, the Dispatcher issues an invite token for the participant and registers it with the signing portal under a link reference. The emailed link (/w/<linkReference>) carries the reference, not the token: the portal resolves it and sends the token to the Dispatcher on the participant's behalf. The invite token's TTL is capped at 1 day regardless of any requested value. Once the participant is in the portal, it is reissued as a shorter-lived portal token (HMAC-signed, 4-hour TTL) on each valid access. If requireOtp is enabled in the session, the participant must verify a one-time passcode sent to their email before accessing any steps.

A link is bound to one browser

Portal and invite tokens are scoped to a specific workflow instance and cannot grant access to other workflow instances or other dispatcher endpoints. The emailed link (/w/<linkReference>) is bound to one browser by the participant's first code verification, submit, or decision; a mail scanner opening the link binds nothing. After that the portal serves that browser, and refuses another with 409 LINK_IN_USE_ELSEWHERE (or LINK_MOVED_ELSEWHERE for a browser the link was bound to before). On a workflow that requires the one-time code, another browser can take the link over by verifying a code of its own. When the workflow has a consent step, the new browser is then shown the consent policy again, and the participant accepts it there before the workflow's content opens or a save, submit, or decision is accepted (until then, 409 CONSENT_NOT_ACKNOWLEDGED). The audit chain keeps both acceptances, each with the code verification it was made under. This applies to workflows created while the consent step is in force; a workflow created before this rule keeps the participant's earlier acceptance. Observer links are not asked again. Without the code, the participant asks for a new link, which goes to the address on record. When the code is accepted but the portal cannot confirm the browser for the link, the portal says "Your code was accepted, but this browser could not be confirmed for the link. Please request a new code and enter it here." (503 OTP_VERIFICATION_NOT_BOUND): the code is used up, and nothing is bound.

Each emailed link is registered with the signing portal before the email goes out, and the email carries the link reference, not a token. When the portal cannot be reached, times out, or answers with a 5xx:

  • Creating a workflow (POST /api/workflow, or a render that creates one) is refused with 503 WORKFLOW_LINK_UNAVAILABLE and Retry-After: 60. Nothing is created, written, or sent, and a render stores no intake receipt. Send the same request again after the wait. A workflow created in relay mode has no caller to try again, so it is created and its invitations are held.
  • Other workflow emails, such as an approval request, a Resend, a reminder, a correction, an escalation, a renewed link, or the completion notice, are held. The Dispatcher tries to register the link again about 1, 5, 15, 30, and 60 minutes after the hold, then every hour, and sends the email once the link registers. It stops trying when a quarter of the link's life is left, or two hours before the link expires, whichever comes sooner. A held email is dropped when it is no longer needed, for example when the workflow is cancelled or a newer link was sent to the same person.
  • A Resend that is held answers 202 with { "ok": true, "held": true }. A correction or an escalation is recorded, and answers participantNotified: "held" or escalationNotified: "held".

A portal that refuses the registration (a 4xx) is not retried: a create is refused with 502 WORKFLOW_LINK_NOT_REGISTERED, and another email is recorded as not sent.

The Lifecycle timeline shows each held email:

EntryWhat it means
Waiting to SendThe email is held: "the portal has not registered its link yet", with the reason. "It is tried again automatically and sent once the link registers."
Notification Queued After a HoldThe link registered and the email was queued for sending, with how long it waited and how many registration attempts it took.
Notification Not SentThe portal did not register the link before the email stopped being tried, or refused it.
Held Notification No Longer NeededThe held email was dropped, with why, for example "a newer link was sent to the same person".

Links sent before this change. An older email's link in the token form (/workflow/{workflowId}?pt=...) no longer opens the workflow. The portal shows "This workflow could not be opened. Please re-open the link from your most recent email, and contact the sender if the problem continues.", and the Dispatcher refuses a token in the URL with 401 LINK_TOKEN_IN_URL.

The intake receipt. A render that creates a workflow returns an intake receipt that lists the participants and says each receives their own link by email. The receipt carries no link; the participant's link is in the X-Refract-WorkflowPortalUrl response header.

When a Session Ends

A participant's session in the portal ends at the earliest of three bounds:

BoundEndsApplies to
IdleFour hours after the participant's last request. Each request moves it on.Every session.
VerifiedTwelve hours after the participant verified the one-time code.A workflow that requires the code (requireOtp). Approver and observer links carry no code, so their sessions have the idle and link bounds.
LinkFour hours after the link itself expires, and never later than two days after the link was sent.Every session.

Before the end. The portal header counts down to the earliest bound ("Session ends in"). Fifteen minutes before it, the page says "Your session ends at" the time, and what to do next:

BoundThe page adds
Idle"Save this step before then to keep your session open: anything not saved will be lost."
Verified"Save this step before then: anything not saved will be lost, and you can verify your email again to continue."
Link"Save this step before then: anything not saved will be lost, and you can request a new link to continue."

A step saved before the end is kept. What was typed and not saved is not.

After the end.

  • Verified. The link still opens the workflow, but its content is withheld and the code screen says "Your session has ended. Verify your email again to continue." The Dispatcher refuses a save or submit with 403 and code OTP_REQUIRED. A new verification starts another twelve hours on the same link.
  • Link. The page says "Your session reached its time limit. For your security, request a new link to continue."
  • Idle. The page says "This link has expired. Please request a new link."

After the link or idle end, the page offers Email me a new link. The participant is told "If this address is on record, a new link is on its way." The new link goes to the address the original link was sent to, and starts a new session.


Notifications

REFRACT Workflow sends participant portal links and approver notifications through REFRACT Notification - the shared notification layer for the REFRACT Platform. The default mode (vault-shared) requires no setup. For deployments where notification metadata must stay in your Azure subscription, set EMAIL_PROVIDER=customer-acs on both your Dispatcher and Processor.

See the REFRACT Notification reference for architecture, environment variables, and mode comparison.

Approver and Observer Emails

The link in an approver's or an observer's email is view only. It opens the request for reading; approving or rejecting it is done in the Lifecycle panel in SharePoint, with the approver's own sign-in.

EmailSent toThe callout says
"Your approval is needed: title" and "Reminder: your approval is still needed: title"The approver"This link lets you read the request. Approving or rejecting it is done in the REFRACT Workflow panel in SharePoint. If you do not have access to the panel, contact organization."
"Escalated to you: title" and "Escalated: title is waiting for a decision"The person the workflow was escalated to (an observer)"This link lets you read the request. It does not let you approve or reject it; the assigned approver, or the tenant's workflow administrator where one is configured, can still decide in the SharePoint panel."

Each of these emails carries a View only: decisions are made in SharePoint callout above its button, and the button says View the request (read only). A new link sent to an approver or an observer, after their link expired, carries the same callout and button, including one that was held and sent later. The page the link opens says "This is a read-only view of the workflow submission."


Reminders

REFRACT Workflow reminds the person it is waiting on automatically. Reminders are on by default for every customer.

Who is reminded and when.

  • The participant, while the workflow is in-progress or changes-requested. The schedule is counted from creation, or from the request for changes.
  • The assigned approver, while a stage awaits a decision (pending-approval or under-review). The schedule is counted from when that stage became pending. In a parallel approval stage, every approver still pending is reminded on one schedule.

The first reminder goes out after firstAfterDays, then one every repeatEveryDays, up to maxReminders. The sweep runs hourly, so a reminder goes out at the first sweep after it is due; there are no quiet hours.

Each reminder carries a fresh link. The new link replaces the one the person holds, and the email says so. Links last one day. Workflows have no expiry warning.

What the reminder says. The participant receives "Reminder: <title>" and "<org> is still waiting for you to complete <title>", with a Continue your request button. The approver receives "Reminder: your approval is still needed: <title>", with a View the request button.

What stops them. Reminders stop when the participant submits, or when the workflow is approved, rejected, cancelled, sealing or complete.

Settings. Set them in templateSettings.workflow.reminders, on the template or on the POST /api/workflow request. There is no dialog for workflows.

SettingTypeDefaultRangeDescription
enabledbooleantrueSet to false to turn reminders off.
firstAfterDaysnumber21 to 30Days before the first reminder.
repeatEveryDaysnumber21 to 30Days between reminders after the first.
maxRemindersnumber31 to 10Most reminders sent to one person.

All values are whole numbers; anything else is refused with HTTP 400 and a sentence naming the setting (WORKFLOW_REMINDERS_INVALID). The narrowest layer wins field by field: the built-in default, then the template's reminders block, then the request's. The resolved schedule is stored on the workflow when it is created, so a workflow created before reminders were introduced has none and is never reminded. A workflow's post-seal delivery uses its own delivery block's reminders; see REFRACT Delivery.

{
  "templateSettings": {
    "workflow": {
      "reminders": { "firstAfterDays": 3, "repeatEveryDays": 2, "maxReminders": 4 }
    }
  }
}

Reminders and escalation

Approver reminders come before escalation and never postpone it.

  • The first approver reminder falls at the earlier of firstAfterDays and half of the stage's escalateAfterDays. No approver reminder is sent at or after the escalation moment.
  • Escalation is measured from when the stage became pending, not from the workflow's last update. Sending a Resend or a reminder does not move that clock.
  • Once a stage is escalated, the assigned approver is no longer reminded, because the escalation email takes over.
  • The person an escalation went to receives a read-only link. They are never reminded and are never sent an approver link.

Resend

The Resend button on the Lifecycle panel re-sends the invitation to a single party: the participant or the approver. The panel sends the party of the row the button is on, and offers the approver row's Resend only while an approver is awaited. In an escalated workflow, a Resend goes to the assigned approver, never to the escalation target. See Resend Invitation for the request.

Timing rules, audit trail and kill switch

  • A reminder is not sent within 2 hours of expiry, and waits while the person was emailed in the last 12 hours, a manual Resend included.
  • After downtime only the latest due reminder is sent. Reminders are email only, and do not count as a new session for licensing or metering.
  • Reminders go through the same notification queue and Processor as every other workflow email, so EMAIL_PROVIDER decides how they are sent, customer-acs included.
  • The audit chain records WORKFLOW_REMINDER_SENT (party, number, outcome, and a hash of the recipient, never an address) beside the usual WORKFLOW_NOTIFICATION_QUEUED or WORKFLOW_NOTIFICATION_FAILED record with purpose reminder. The Lifecycle timeline shows a Reminder Sent sub-entry, for example "Reminder 1 sent to the participant".
  • REFRACT_REMINDERS_DISABLED=true on the Dispatcher Function App stops every reminder across the deployment.

Post-Seal Actions

Set templateSettings.esign on the workflow session to trigger a REFRACT eSign signing session automatically after the PDF is sealed. This is captured into the session's internal postSealActions at creation time - it isn't a top-level request field itself:

{
  "templateSettings": {
    "esign": {
      "enabled": true,
      "signers": [
        { "slotIndex": 0, "role": "manager", "email": "manager@contoso.com" }
      ],
      "signingMode": "sequential",
      "expiresInMinutes": 4320
    }
  }
}

If you want the workflow initiator to hear back once the signature session runs (per-signer notices, a documents-ready email, or a decline/revoke/seal-failure notice), add requestedByEmail inside templateSettings.esign. Without it, templateSettings.esign still triggers the signing session but the initiator receives no notifications about it. See Requester Notifications for the full field reference.

Delivering the sealed PDF

Triggering REFRACT Delivery directly from a workflow's post-seal settings does not currently send the recipient a notification email. To deliver a sealed workflow PDF, call POST /api/delivery yourself once the workflow reaches complete, using the sealed PDF's blob path.

Guided Documents use a similar pattern - see below.


Guided Documents

Beyond sealing your own REFRACT PDF document, a workflow can also fill an official third-party PDF - an IRS form, a bank-mandated form, a government compliance form - using the same collected data. No separate data entry, and no compromise on submitting the exact form the recipient requires.

To get both your sealed REFRACT PDF and the official form from the same submission, set templateSettings.guidedDocuments (an array) alongside your normal workflow settings - each entry is filled automatically right after the seal, as an additional artifact:

{
  "templateSettings": {
    "workflow": { "enabled": true, "recipients": [{ "email": "vendor@example.com" }] },
    "guidedDocuments": [
      {
        "enabled": true,
        "formPath": "OfficialForms/w9.pdf",
        "mappingPath": "mappings/w9-mapping.yaml",
        "outputName": "w9"
      }
    ]
  }
}

officialForm (singular) replaces the render instead

There's a second, single-object shape - templateSettings.officialForm - used when a render request's only output should be the filled official form. Setting it alongside your normal workflow settings will skip your sealed REFRACT PDF entirely, not add to it. See Guided Documents for when to use which.

The generated document is uploaded as its own file, separate from the sealed PDF - it shows up as its own row under Related Files in the Lifecycle Dialog once the seal completes. See Guided Documents for field-mapping configuration, overflow handling, accessibility notes, and retrieval details, or Document Completion Options for how to choose between this, REFRACT eSign, and plain workflow submission.


Scenarios

Scenario 1 - Customer Contract (Standard, Multi-Step)

Illustrative Example

The following scenario is fictional and intended to demonstrate how lifecycle events and evidence trails may appear in a production deployment.

Business context: Sales team creates a vendor contract workflow from Dynamics 365.
Vendor fills three steps over the portal; Finance Lead approves; sealed PDF
auto-triggers REFRACT eSign for counter-signature.

Step 1: Vendor Contact Details (name, email, company, ABN/EIN)
Step 2: Contract Terms (value, start date, payment terms)
Step 3: Compliance Declaration (sanctions check, insurance confirmation, T&C acceptance)

Create session:
POST /api/workflow
{
  "templatePath": "templates/vendor-contract.rpkg",
  "correlationId": "vc-2026-0601-acme",
  "templateSettings": {
    "workflow": {
      "recipients": [{ "email": "vendor@acme.com", "displayName": "ACME Contact" }],
      "organizationName": "Contoso Ltd",
      "message": "Please complete and submit the vendor contract form."
    },
    "esign": {
      "enabled": true,
      "signers": [{ "slotIndex": 0, "role": "vendor", "email": "vendor@acme.com" }],
      "signingMode": "single"
    }
  },
  "options": { "documentId": "VC-2026-0601", "docType": "vendor-contract" }
}

Flow:
  Vendor receives portal link → fills 3 steps → submits
  ├─ Form field data (text, selects, dates): browser → your Dispatcher (portal token auth)
  │   → stored in your Azure Table Storage; portal server not in data path
  ├─ File attachments (e.g. business registration, insurance certificate):
  │   browser → portal proxy → POST /api/workflow/{workflowId}/attachment on your Dispatcher
  │   → the Dispatcher receives the bytes directly and writes them to your Blob Storage
  │   → 25 MB size cap, content-type allowlist enforced
  Validation passes → Finance Lead receives approval notification
  Finance Lead approves → Processor seals PDF
  Post-seal: REFRACT eSign auto-triggers → vendor receives signing invitation
  Vendor signs → evidence certificate appended → complete

Scenario 2 - AP 3-Way Match (Governance, Auto-Validate)

Illustrative Example

The following scenario is fictional and intended to demonstrate how lifecycle events and evidence trails may appear in a production deployment.

Business context: D365 Finance triggers a 3-way match check on receipt of an invoice.
All data is pre-populated; no portal interaction required from the submitter.
Validation runs immediately; AP Manager (or Finance Director for high-value) approves.

POST /api/workflow
{
  "templatePath": "templates/ap-3way-match.rpkg",
  "correlationId": "inv-2026-0601-8821",
  "initialData": {
    "po_number": "PO-2026-0412",
    "po_amount": 42500.00,
    "po_quantity": 100,
    "po_vendor_id": "VEND-0098",
    "gr_number": "GRN-2026-0187",
    "gr_quantity": 100,
    "invoice_number": "INV-2026-8821",
    "invoice_total": 43200.00,
    "invoice_quantity": 100,
    "invoice_vendor_id": "VEND-0098",
    "lines_all_price_match": true,
    "lines_all_qty_match": true
  },
  "options": { "documentId": "INV-2026-8821", "docType": "ap-invoice" }
}

Flow:
  Session created → validation runs immediately (autoValidateOnCreate: true in schema)
  invoice_total ($43,200) within 5% of po_amount ($42,500) → tolerance warning surfaced
  All error rules pass → AP Manager receives approval notification with variance panel
  AP Manager approves → Processor seals PDF with Governance Validation page
  Sealed PDF stored → WORKFLOW_SEALED audit event emitted

Scenario 3 - Employee Expense Claim (Standard, Changes-Requested Flow)

Illustrative Example

The following scenario is fictional and intended to demonstrate how lifecycle events and evidence trails may appear in a production deployment.

Business context: Employee submits an expense claim via the portal.
Approver requests a correction (receipt amount mismatch); employee corrects and resubmits.

Create session:
POST /api/workflow
{
  "templatePath": "templates/expense-claim.rpkg",
  "correlationId": "exp-2026-0601-jsmith",
  "templateSettings": {
    "workflow": {
      "recipients": [{ "email": "j.smith@contoso.com", "displayName": "J. Smith" }],
      "organizationName": "Contoso Ltd"
    }
  },
  "options": { "documentId": "EXP-2026-0601", "docType": "expense-claim" }
}

Flow:
  Employee receives portal link → fills expense details, uploads receipts
  ├─ Expense receipt attachments: uploaded to POST /api/workflow/{workflowId}/attachment
  │   → the Dispatcher receives the bytes directly and writes them to your Blob Storage
  │   → 25 MB size cap, content-type allowlist enforced on both the declared type and the file's own bytes
  ├─ Form field data (amounts, dates, categories): browser → your Dispatcher
  │   → stored in your Azure Table Storage
  Employee submits → validation passes → Line Manager notified
  Line Manager reviews → requests change: "Receipt for lunch on Jun 3 is $85.50, not $95.50"
  Employee corrects field → resubmits
  Line Manager approves → Processor seals PDF
  AP calls POST /api/delivery with the sealed PDF's blob path to send it to the employee's inbox

Ready to Configure a Workflow?

See the Workflow Schema reference for step definitions, validation rules, and approval routing configuration.

Dispatcher Deployment and Link Lifecycle

What happens to outstanding workflow portal links when you redeploy or move your Dispatcher, and how to plan around it.

On this page