REFRACT Delivery
REFRACT Delivery provides secure document dispatch and delivery for REFRACT PDF-generated documents. Send documents to external recipients via time-limited access links, with access auditing and proof of delivery.
REFRACT Delivery handles the final stage of the document lifecycle, getting a rendered PDF securely to an external recipient, with a delivery audit trail.
Enterprise Feature
REFRACT Delivery requires the vaultDelivery feature gate on your licence.
Needs Azure Blob Storage
REFRACT Delivery 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 Delivery off. If a deployment is missing the account's settings, REFRACT Delivery 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.
Documents stored in your Azure subscription
Documents are generated and stored in your Azure subscription. When a recipient opens a REFRACT Delivery link, the portal redirects the browser to your own Dispatcher or SharePoint site, which serves the document directly - the portal never receives the bytes.
Invitation and one-time-code emails are sent through a REFRACT shared notification service by default, which receives the recipient's email address and name, the document name, the rendered message body, any custom message text, one-time codes, links, and correlation identifiers. No document content is included. For an invitation sent from a REFRACT Dispatch batch, it also receives the batch's identifier, the record number, and a keyed hash of the recipient's address, and it holds the email's result (sent or failed, with a stage and error code, but no address) until your Processor collects it, or for 30 days if it is never collected.
For customers who want notification metadata to stay in their own tenant, customer-acs mode sends notifications directly from your Azure Communication Services instance instead of the shared service. It does not change how the portal redirects for the document itself.
What REFRACT Delivery Does
Secure Email Delivery
Recipients receive a notification email with a time-limited access link. The document is never attached directly to the email; it is retrieved through the secure portal.
Token-Gated Access
Each delivery session issues a unique, HMAC-signed access token. Access itself expires when the session's own expiry is reached, and is revocable by the initiator at any time.
OTP Verification
Optional one-time passcode challenge before a recipient can access the document, providing an additional layer of identity assurance.
Access Auditing
Session creation and each document access are recorded in the audit trail, providing a record of when a recipient accessed the document.
Configurable Expiry
Access links expire after a configurable window (default 7 days). The document becomes inaccessible via the delivery portal once the session expires.
Delivery Modes
| Mode | Description |
|---|---|
secure-email | Sends a notification email to each recipient with a unique, time-limited access link to the delivery portal. The document is retrieved on demand. |
Consent Step
A template can ask each recipient to accept a consent policy before the document opens. Set it in the template's templateSettings.delivery.consent block:
{
"templateSettings": {
"delivery": { "mode": "SECURE_LINK", "consent": { "mode": "required" } }
}
}- Default. A template with no
consentblock has no consent step (none). - Policy. Without
policyId, the recipient is shown the REFRACT system policyrefract-standard-consent-delivery. Consent Policies lists its versions and describes how to publish your own. - Delivery mode. The step is shown on the recipient's secure link, so it needs delivery mode
SECURE_LINK(secure-email). A template that requires consent withSHAREPOINTorBOTHis refused withconsent_requires_portal_link. - Which template. A manual send (
POST /api/delivery) uses the consent setting of the template the document was rendered with; a document with no render record is sent without a consent step. A render's delivery, a REFRACT Dispatch batch, and a workflow's post-seal delivery use their own template's setting. - The signing portal. The portal shows the step when its health check lists the
deliveryConsentcapability. When it does not, a manual send is refused with503consent_layer_unavailableandRetry-After: 30, and nothing is sent. A queued delivery sends nothing and recordsDELIVERY_SKIPPED, which REFRACT Lifecycle shows as Delivery Not Sent: Consent Step.
What the Recipient Sees
When the recipient opens their link, after the one-time code when requireOtp is set, the portal shows the policy. The recipient ticks each of its statements and accepts it (the policy's button, "Agree and continue" on the system policy), or chooses Decline. Until the policy is accepted, the Dispatcher refuses to record an access or serve the document, with 409 CONSENT_NOT_ACKNOWLEDGED, and the portal shows the document's name and sender, not the document.
The audit chain records the step as DELIVERY_CONSENT_POLICY_PRESENTED and DELIVERY_CONSENT_POLICY_ACKNOWLEDGED, with the policy's id, version, and SHA-256.
Declining
The portal asks once more before a decline: "Decline this document?" and "The sender will be told that you declined, and this link will stop working. You cannot undo this." After the decline:
- The delivery. Its state becomes
declined, recorded asDELIVERY_DECLINEDwith the policy the recipient declined. The link stops working and shows "You declined this document". Other recipients of the same send are not affected. - The sender. A notice, "Delivery declined: document", goes to the person who sent the delivery: the signed-in user of a manual send, or the person whose approval sealed the workflow for a post-seal delivery. A delivery with no sender on record, such as a render's delivery or a batch, sends the notice to the template's contact:
consent.contacts.withdrawalwhen it is an email address, elsetemplateSettings.delivery.contactEmail. With neither, no notice is sent. - REFRACT Lifecycle. The recipient's row on the Deliveries tab says Declined, and the timeline shows Delivery Declined. Under it, the notice is listed as queued for sending, or as "The sender was not emailed about the decline" with the reason.
A recipient who has accepted the policy can no longer decline: 409 DELIVERY_NOT_DECLINABLE. When the decline cannot be recorded, it is not made, and the portal is answered 503 DELIVERY_DECLINE_NOT_RECORDED with Retry-After: 5, so the recipient can try again.
Email Notifications
REFRACT Delivery sends recipient invitations and OTP codes through REFRACT Notification - the shared notification layer that also serves REFRACT Workflow. Two modes are supported: vault-shared (default, no setup required) and customer-acs (routes all notification emails through your own Azure Communication Services - REFRACT Platform infrastructure not involved).
One setting for Delivery, Workflow, and eSign
The EMAIL_PROVIDER environment variable applies to REFRACT Delivery, REFRACT Workflow, and REFRACT eSign together - configure it once on both your Dispatcher and Processor. REFRACT eSign emails are composed by the eSign Portal and follow the setting for every envelope your Dispatcher creates while it is set. See REFRACT Notification for the full breakdown.
See the REFRACT Notification reference for architecture diagrams, environment variable configuration, and mode comparison.
Reminders
REFRACT Delivery reminds a recipient who has not downloaded the document. Reminders are on by default for every customer.
One reminder per recipient, no repeats. The reminder is sent beforeExpiryDays (default 2) before the recipient's secure link expires, and only if the recipient has not downloaded the document. The sweep runs hourly.
What the recipient receives. The invitation, worded as a reminder: "Reminder: a secure document is waiting for you: <document>" and "This is a reminder. <org> sent you a secure document that has not been downloaded yet", with the same Download Document button and the recipient's same secure link. It is sent through the same queue and Processor as the invitation, so EMAIL_PROVIDER (customer-acs included) decides how it is sent.
When no reminder is sent. A reminder is not sent:
- for a link that lives less than
beforeExpiryDaysplus 2 days; - to a recipient who has downloaded the document;
- to a recipient whose link was revoked or has expired;
- to a recipient whose secure-link email never went out;
- to a recipient who was already reminded.
"Downloaded" means the recipient's download was served from the secure link, which is recorded as firstDownloadedAt. Opening the page is not a download.
Settings.
| Setting | Type | Default | Range | Description |
|---|---|---|---|---|
enabled | boolean | true | Set to false to turn reminders off. | |
beforeExpiryDays | number | 2 | 1 to 14 | Days before the secure link expires to send the one reminder. |
Set them as templateSettings.delivery.reminders, or as a reminders object in the POST /api/delivery body (see the API reference). The narrowest layer wins field by field: the built-in default, then the template's reminders block, then the request's. Values must be whole numbers; anything else is refused with HTTP 400 and a sentence naming the setting (DELIVERY_REMINDERS_INVALID; on a render, before the render runs). A workflow's post-seal delivery uses its delivery block's reminders. The resolved schedule is stored on the session when it is created, so a session created before reminders were introduced has none and is never reminded.
{
"templateSettings": {
"delivery": {
"reminders": { "beforeExpiryDays": 3 }
}
}
}Timing rules, audit trail and kill switch
- A reminder is not sent within 2 hours of expiry, and waits while the recipient was emailed in the last 12 hours.
- After downtime only the latest due reminder is sent. There are no quiet hours: a reminder goes out at the first sweep after it is due. Reminders are email only, and do not count as a new session for licensing or metering.
- The audit chain records
DELIVERY_REMINDER_SENTwith its outcome (sent or failed). The Lifecycle timeline shows a Reminder Sent sub-entry. REFRACT_REMINDERS_DISABLED=trueon the Dispatcher Function App stops every reminder across the deployment.
When a Link Cannot Be Registered Yet
Each recipient's link is registered with the signing portal before the email goes out, and the email carries the link reference (/d/<linkReference>), not a token. When the portal cannot be reached, times out, or answers with a 5xx, the session is created and its invitation is held: a manual send answers that session with held: true and no portalUrl (see the API reference). 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 invitation 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 reminder works the same way. A held email is dropped when it is no longer needed, for example when the delivery was revoked, declined, or has expired, or the document was already downloaded.
The Lifecycle timeline shows Waiting to Send (or Reminder Waiting to Send) while the email is held, Invitation Queued After a Hold (or Reminder Queued After a Hold) once it goes out, Invitation Not Sent (or Reminder Not Sent) when the link did not register in time, and Held Invitation No Longer Needed when it was dropped.
An older email's link in the token form (/deliver/{deliveryId}?t=...) no longer opens the document. The portal shows Link unavailable: "This link could not be opened. Please re-open the link from your most recent email, and contact the sender if the problem continues."
Delivery Session States
| State | Description |
|---|---|
pending | Session created; notification email not yet enqueued. |
sent | Notification email successfully enqueued (set immediately after comms queue write). |
opened | Recipient opened the delivery portal or accessed the document. |
expired | Session TTL elapsed before the recipient accessed the document. |
revoked | Session manually revoked by the initiator. |
declined | The recipient declined the consent policy (see Declining). Their link no longer works. |
There is no separate downloaded state. A recipient's first download is recorded separately, as firstDownloadedAt, when the download is served from the secure link. Opening the portal page is not a download. The reminder below looks at firstDownloadedAt, not at the session state.
Proof of Delivery
Two events are recorded to the audit trail for each delivery session, and a third when a reminder is sent:
| Event | Description |
|---|---|
DELIVERY_SESSION_CREATED | Session created; access link issued. |
DELIVERY_DOCUMENT_ACCESSED | Recipient accessed the document. Recorded with the session's running view count. |
DELIVERY_REMINDER_SENT | The automatic reminder was sent to the recipient. Records the outcome (sent or failed). |
Revoking a session updates its state but does not write an audit event.
Batch Dispatch
REFRACT Delivery is used as the delivery layer for REFRACT Dispatch governed batch operations. Batch dispatch is initiated via POST /api/render with a template that has distribution.mode configured. The Dispatcher validates and queues the batch; after approval (or immediately if requireApproval: false), the Processor picks up the queue message and processes each record independently - rendering a PDF per record and issuing a DeliveryJobMessage to the delivery Service Bus queue for each recipient.
Batch Size
A batch holds at most 1,000 records, or fewer where your license sets a lower limit. A larger batch is refused before anything is stored: the response is an error-report PDF with status 400 and code BATCH_TOO_LARGE, stating the limit and the number of records received, and no batch, receipt or Activity item is created and nothing is sent. To send more documents, split the records into several smaller batches and submit each one. Each batch dispatch processes records sequentially within the processor queue message. For large batches (over a few hundred records), the payload is written to Blob Storage (vault-dispatch-payloads container) at intake time and loaded by the Processor from there - the queue message carries only the batchId reference.
Scenarios
Scenario 1 - Invoice Delivery to External Vendor
Business context: After AP approves a purchase order, the signed invoice PDF is
delivered securely to the vendor via email link. The vendor can download it for
7 days; access is logged for audit.
POST /api/delivery
{
"correlationId": "inv-2026-0601-acme",
"documentName": "Invoice INV-2026-0601.pdf",
"pdfBlobPath": "generated-documents/invoice/2026/06/inv-2026-0601-acme.pdf",
"mode": "secure-email",
"recipients": [
{ "email": "accounts@acme.com", "name": "ACME Accounts", "role": "vendor" }
],
"requesterOrg": "Contoso Ltd",
"message": "Please find your invoice attached. This link expires in 7 days.",
"expiresInHours": 168
}
Flow:
Session created → notification email queued → vendor receives email with portal link
├─ Portal link: opens portal.refractplatform.com UI (REFRACT Shared Services)
│ → portal redirects the browser to your Dispatcher or SharePoint site, which serves the document directly
Vendor opens link → DELIVERY_DOCUMENT_ACCESSED event recorded, session state moves to opened
Audit trail updated: proof of access for the AP record
Session expires after 7 days → further access refusedScenario 2 - Confidential HR Document with OTP Verification
Business context: HR sends an employment contract to a new hire. The document
contains personal data; OTP verification adds a second layer of identity assurance
before the recipient can download.
POST /api/delivery
{
"correlationId": "hr-contract-2026-jsmith",
"documentName": "Employment Contract - J. Smith.pdf",
"pdfBlobPath": "generated-documents/hr-contract/2026/06/hr-contract-2026-jsmith.pdf",
"mode": "secure-email",
"recipients": [
{ "email": "j.smith@personal.com", "name": "J. Smith", "role": "employee" }
],
"requesterOrg": "Contoso Ltd",
"expiresInHours": 72,
"requireOtp": true
}
Flow:
Session created → invitation email sent
Recipient opens portal link → document metadata shown (name, expiry, requireOtp flag)
Recipient requests OTP → 6-digit code sent to j.smith@personal.com
Recipient enters OTP → identity verified
Document accessed → DELIVERY_DOCUMENT_ACCESSED event recorded, session state moves to opened
Personal data (email) stored AES-256-GCM encrypted in your Table StorageScenario 3 - Batch Contract Distribution After Approval
Business context: After a REFRACT Dispatch batch is approved and processed by the
Processor, each record in the batch receives its rendered PDF via a delivery session.
This is triggered automatically by the Processor's DeliveryJobMessage queue handler
- no manual API call is required per recipient.
Processor (automatic, triggered from the delivery queue):
For each record in the batch:
Generates deliveryId + HMAC-signed access token
Writes DeliverySessionEntity to Table Storage
Enqueues CommJobMessage → notification email sent to recipientEmail field
What crosses to REFRACT Shared Services (vault-shared mode):
Recipient email address - for ACS delivery
Portal access link - a link reference (/d/<linkReference>), no token
Document name - shown in email and portal
What stays in your subscription:
Document bytes - the portal redirects the browser to your Dispatcher or SharePoint site, which serves them directly; the portal never receives them
All delivery session state and audit eventsAPI Reference
Full reference for creating delivery sessions, querying session state, revoking access, and recording access events.
Dispatcher Deployment and Link Lifecycle
What happens to outstanding delivery links when you redeploy or move your Dispatcher, and how to plan around it.
Cross-Device Signing
Start a REFRACT eSign signing session on one device and continue securely on another. A one-time QR transfer binds the new device through the same OTP identity verification used for the original session.
REFRACT Delivery API Reference
HTTP endpoint reference for creating delivery sessions, querying session state, OTP verification, and revoking access.