Architecture & Data Flow
Full data-flow diagrams, trust boundaries, and key management overview for REFRACT PDF. The canonical architecture reference for security architects, procurement teams, and AppSource reviewers.
Hosting Model
REFRACT PDF uses a customer-owned infrastructure model. Every component that processes or stores your documents deploys into your own Azure subscription using the supplied Bicep templates. REFRACT PDF does not operate a shared multi-tenant processing environment.
A small set of Refract Logic-operated shared services handle licensing, signing/delivery hand-off, and outbound notifications:
| REFRACT-shared service | Role | Data it receives |
|---|---|---|
| Licensing API | Validates your license key and returns feature entitlements | Tenant identifier, license key, identity token, deployment address; usage reporting sends counts only |
| eSign / Delivery / Workflow Portal | Signer and recipient UI, OTP verification, cross-device transfer | Recipient/signer email and name, OTP codes; signature images and field values pass through in memory during the signed callback on their way to your Dispatcher or, for REFRACT Delivery, your SharePoint site, but are not written to the portal's own storage. Keeps signer contact details, OTP hashes, and session state in its own database. |
| REFRACT Notification service | Sends signing- and delivery-related notification emails on your behalf | Recipient email, portal link, document name, message body, one-time codes |
| Shared Azure Communication Services (ACS) | Outbound email transport used by REFRACT Notification when you have not configured Bring-Your-Own email | Recipient address + message body |
Documents Stored in Your Environment
Document content, rendered PDFs, and audit records are generated and stored in your Microsoft 365 tenant and your Azure subscription. Every document download, for REFRACT eSign, Delivery, and Workflow alike, redirects the browser to your own Dispatcher or SharePoint site, which serves the bytes directly; the portal never receives them. Signature images and field values, and REFRACT Workflow attachment uploads, are the two exceptions: they pass through the portal in memory on their way to your Dispatcher and are never written to its storage - see Security & Verification for the full breakdown by product.
Architecture Diagram
For the canonical architecture diagram and interactive Mermaid source, see the System Architecture page: System Architecture.
Trust Boundaries
For a canonical diagram and Mermaid source view, see the System Architecture page: System Architecture.
| Boundary | Description |
|---|---|
| Customer Microsoft 365 Tenant | SharePoint libraries, Activity lists, and Entra ID identities. Governed by your Microsoft 365 admin. |
| Customer Azure Subscription | All processing infrastructure. You own the resource group, the keys, and the data. Refract Logic has no standing access. |
| REFRACT PDF - Licensing API | Receives: license key (string), tenant ID (string). Returns: feature flags (JSON). No document data crosses this boundary. Calls are HTTPS/TLS 1.2+. |
| REFRACT PDF - eSign/Delivery/Workflow Portal | Receives: signer/recipient email and name, OTP codes, and session identifiers. Signature images and field values transit in memory during the signed callback to your Dispatcher. Keeps signer contact details, OTP hashes, and session state in its own database. |
| Licensing Sync | Periodic (nightly, 2:00 AM UTC) outbound call from The Dispatcher to REFRACT PDF Licensing API. Transmits licenseKey and tenantId only. Returns a JWS ES256-signed entitlement object written to SharePoint license.json. Usage deltas (counts only) are also pushed. No document content crosses this boundary. A license check waits on the Licensing API only when the instance's Tier 1 cache is empty and Tier 2 cannot answer it; see Licensing Architecture for when that happens. |
Licensing Architecture (Tiered Cache Model)
REFRACT PDF uses a 4-tier license validation cascade to minimise runtime dependency on the REFRACT PDF Licensing API.
Nightly Sync
sequenceDiagram participant Timer as Nightly Timer participant Dispatcher as The Dispatcher participant LicAPI as REFRACT PDF Licensing API participant SP as SharePoint license.json Timer->>Dispatcher: Trigger scheduled sync Dispatcher->>LicAPI: Validate licenseKey and tenantId LicAPI-->>Dispatcher: Return signed entitlement object Dispatcher->>SP: Write signed license.json Dispatcher->>LicAPI: Push usage counts only
Runtime Tier Cascade
flowchart TD
R["Runtime license check"] --> T1{"Tier 1\nIn-memory cache\n(24 h TTL)"}
T1 -->|"Valid"| Allow1["✅ Allowed\n(no API call)"]
T1 -->|"Expired / empty"| T2{"Tier 2\nSharePoint license.json\n(JWS ES256 signed)"}
T2 -->|"Valid - non-trial"| Allow2["✅ Allowed\n(no API call)"]
T2 -->|"Missing / expired / trial"| T3{"Tier 3\nREFRACT PDF Licensing API"}
T3 -->|"Valid"| Allow3["✅ Allowed\n(cache refreshed)"]
T3 -->|"Invalid (revoked / expired)"| Deny["❌ Denied"]
T3 -->|"Unreachable"| T4{"Tier 4\n48 h grace\n(API unavailable only)"}
T4 -->|"Within 48 h of this instance's last valid answer"| Allow4["✅ Allowed\n(last known good)"]
T4 -->|"Grace expired, or no valid answer yet in this instance"| Deny2["⏳ Refused for now (503)\nretry after Retry-After"]
Failure Isolation
| Scenario | Behaviour |
|---|---|
| Licensing API temporarily unreachable, instance already licensed | An instance with a valid Tier 1 cache, or with a valid license.json on a plan other than trial, keeps serving without the API. An instance that had a valid license answer in the last 48 hours serves under the grace period. |
Licensing API unreachable, new instance on a trial plan or without license.json | Requests are refused for now. The instance has no grace period. It tries the API once more, then refuses the request with 503 and code LICENSE_UNAVAILABLE, "retryable": true, and Retry-After: 30. Nothing was done, and the caller sends the request again. See New instances have no grace period below. |
| Licensing API revokes or expires license | Nightly sync clears in-memory cache, deletes SharePoint license.json. Next runtime check reaches Tier 3 and is refused with 403 and code LICENSE_INVALID. Grace period does not apply to genuine revocations. |
| Grace period expires (API still unreachable) | Requests are refused with 503 and code LICENSE_UNAVAILABLE, as on a new instance. System resumes automatically when the API is reachable again. |
New instances have no grace period
The Tier 1 cache and the grace period both live in the memory of one Dispatcher instance. A new instance starts with neither. A new instance starts after every cold start, scale-out, deployment, and platform move. One always-ready instance, the default for new installations, makes new instances less frequent, but deployments and platform moves still start them.
A new instance that cannot use Tier 2 must reach the Licensing API before it serves a request that checks the license. Tier 2 is not available to it on a trial plan, which always skips Tier 2, or when there is no valid license.json in SharePoint: SharePoint is not configured, the file has not been written yet, or it has expired. If the API cannot be reached, the instance waits one second and tries once more; both tries finish within 15 seconds. If the second try cannot reach it either, the request is refused with 503 and code LICENSE_UNAVAILABLE, with "retryable": true and Retry-After: 30. Nothing was done, and the same request succeeds once the API answers. The instance does not keep the refusal: the next request checks again. Requests that arrive while a check is running wait for that check rather than calling the API themselves. When to retry describes how a caller handles the refusal.
Every request that checks the license reads license.json, including starting an eSign, Delivery, or workflow session as a request of its own, and an eSign capture. On a plan other than trial with a valid license.json, none of them waits on the API on a new instance.
The grace period runs for 48 hours from the instance's last valid answer, not from the start of an outage. It applies only when the API cannot be reached: a network error, no answer within 8 seconds, an answer with a 5xx status, or an answer that carries no verdict (not JSON, or JSON with no valid field). An answer below 500 that says the license is not valid is a denial with no grace period, refused with 403 and code LICENSE_INVALID.
Runtime Principle
The Licensing API is not called on every request. With a populated Tier 1 cache, API calls occur at most once per 24 hours per instance. With a valid SharePoint license.json on a plan other than trial, a render does not wait on the API when the in-memory cache is empty, typically after a function cold start; the Dispatcher still calls the API in the background.
Availability of REFRACT-Shared Services
The notification service, the portal, and shared email are not on the critical path for document generation. The Licensing API is, for a Dispatcher instance that has no valid license answer of its own yet; see the first row below.
| If this is unavailable... | What happens | Customer impact |
|---|---|---|
| Licensing API | An instance that is already licensed serves from its Tier 1 in-memory cache (24 h), from the JWS-signed license.json in SharePoint (Tier 2) on a plan other than trial, or under the 48-hour grace period from its last valid answer. A new instance on a trial plan, or without a valid license.json, has no grace period and refuses each request that checks the license with 503 and code LICENSE_UNAVAILABLE, marked retryable, until the API answers. Independently, the REFRACT Notification rate-limit cache is fail-open: it keeps serving the last-known limits and never blocks a send when the API is unreachable. | None for an instance that is already licensed, for up to 48 h from its last valid answer. Requests refused for now, to be sent again after Retry-After, on a new instance on a trial plan or without license.json. None for notifications. |
| REFRACT Notification service | Notification requests are held on a durable Azure Service Bus queue and processed when the service recovers. Document generation, rendering, and signing are unaffected because notifications are a downstream, asynchronous step. | Emails may be delayed, not dropped. Core document and signing flows continue normally. |
| eSign/Delivery/Workflow Portal | The portal renders the signer/recipient UI itself and redirects the browser to your own Dispatcher for every document download; it never holds document bytes. | New sessions pause until it recovers; documents already accessible remain so. |
| Shared ACS (email) | Configure Bring-Your-Own email to route all outbound mail through your own Azure Communication Services resource. | Optional: removes the dependency on REFRACT PDF shared email entirely. |
A Licensing API outage can refuse renders
A Dispatcher instance that is already licensed keeps generating documents through a Licensing API outage of up to 48 hours from its last valid answer. A new instance has no grace period. On a trial plan, or without a valid license.json in SharePoint, it refuses renders with 503 and code LICENSE_UNAVAILABLE until the Licensing API answers, and a new instance starts after every cold start, scale-out, deployment, and platform move. The refusal is marked retryable: a caller sends the request again after Retry-After. Notifications are not affected. Every instance resumes automatically once the API is reachable again.
Sequence Diagram - Render Request (Data Residency View)
This sequence diagram shows the full render request path step by step. For AppSource and ISV review: note that document content never crosses into Refract Logic-operated infrastructure.
sequenceDiagram
actor User as 👤 User (SharePoint)
participant SPFx as SPFx App<br/>(Microsoft 365 tenant)
participant SPO as SharePoint Online<br/>(Microsoft 365 tenant)
participant Dispatcher as The Dispatcher<br/>func-dispatcher<br/>(your Azure subscription)
participant LicAPI as REFRACT PDF Licensing API<br/>(Refract Logic-operated)
participant SB as Azure Service Bus queues<br/>(your Azure subscription)
participant KV as Azure Key Vault<br/>(your Azure subscription)
participant Processor as The REFRACT Engine<br/>func-processor<br/>(your Azure subscription)
participant Storage as Azure Blob Storage<br/>(your Azure subscription)
User->>SPFx: Click "Generate PDF" (SPFx path)
Note over User,Dispatcher: Source systems may also POST /api/render directly to the Dispatcher, skipping SPFx
SPFx->>Dispatcher: POST /api/render (Entra ID Bearer JWT · HTTPS)
Dispatcher->>SPO: Read template + payload (Graph API · MSI)
SPO-->>Dispatcher: template + payload data
Dispatcher->>LicAPI: Validate license
Note over Dispatcher,LicAPI: Body: { licenseKey, tenantId }<br/>❌ No document content crosses this boundary
LicAPI-->>Dispatcher: { features: { ... } }
Dispatcher->>KV: Get encryption keys (MSI)
KV-->>Dispatcher: Keys
Dispatcher->>SB: Enqueue render message (MSI)
SB->>Processor: Dequeue render message (MSI trigger)
Processor->>SPO: Read template (Graph API · MSI)
SPO-->>Processor: Template data
Processor->>KV: Get keys (MSI)
KV-->>Processor: Keys
Processor->>Processor: Render PDF in memory (Ephemeral Processing Unit)
Processor->>SPO: Upload PDF to output library (Graph API · MSI)
Processor->>Storage: Write audit JSONL event (MSI)
Processor->>SPO: Update Activity list (Graph API · MSI)
SPFx-->>User: Document ready notification
Trust Boundaries - Summary View
A simplified view of who controls what, for procurement and legal review.
graph LR
subgraph CustomerEnv["🔒 Customer Environment"]
SP["SharePoint Libraries<br/>Document data · Activity lists"]
EID["Entra ID<br/>Authentication · MSI trust"]
Disp["The Dispatcher<br/>func-dispatcher"]
Proc["The REFRACT Engine<br/>func-processor"]
SB2["Azure Service Bus queues"]
KV2["Azure Key Vault<br/>All encryption keys"]
Stor["Azure Blob Storage<br/>Outputs · Audit JSONL"]
AI2["App Insights<br/>(no document content)"]
end
subgraph SharedServicesBoundary["🏢 REFRACT Platform Shared Services"]
LicAPI2["Licensing API<br/>Receives: license key + tenant ID only"]
NOTIFY2["REFRACT Notification API<br/>Receives: notification metadata only"]
ACS2["Azure Communication Services<br/>Email · SMS (Refract Logic or BYO)"]
eSign2["eSign/Delivery/Workflow Portal<br/>Receives: signer/recipient contact details, OTP codes, session state"]
NOTIFY2 --> ACS2
end
Disp -->|"license key + tenant ID - no document content"| LicAPI2
Disp -.->|"HMAC token redirect - no document bytes"| eSign2
eSign2 -.->|"token verify"| Disp
Proc -.-> NOTIFY2
NOTIFY2 -.-> ProcTrust boundary notes
- REFRACT Notification API: handles notification metadata only (recipient email, portal links, document IDs). No document bytes are transmitted.
- Azure Communication Services (ACS): used by Refract Logic for outbound email/SMS. Customers can opt for BYO ACS to route outbound email and SMS through their own Azure Communication Services resource instead of the Refract Logic-operated one.
- Processor Function Apps: interact with REFRACT Notification for sending delivery and signing-related notifications; interactions are metadata-only.
Data Flow - eSign, Delivery and Workflow Requests
Approver clicks eSign link in email
→ Browser loads the REFRACT eSign Portal, which renders the signing UI itself
→ Portal verifies the session, applies OTP verification where configured
→ Approver previews the document: the portal redirects the browser to your own
Dispatcher for the PDF bytes; the portal never receives them
→ Approver draws their signature and completes fields, then submits
→ Portal posts an HMAC-signed callback to your Dispatcher, carrying the
signature image and field values in memory only
→ The Dispatcher verifies the callback, records approval, enqueues the seal message
→ The REFRACT Engine seals the document, writes it and the certificate
to your Azure Blob Storage
→ Portal redirects the approver's browser to your Dispatcher for the signed
document and certificate; document bytes never transit the portalThe eSign Portal renders the signing UI itself and keeps signer contact details, OTP hashes, and session state in its own database - it is not a stateless relay. It does not store document bytes: the document preview, the signed copy, and the certificate are all served by redirecting the browser to your own Dispatcher, and the signature image and field values a signer submits pass through the portal only in memory, on their way to your Dispatcher.
REFRACT Delivery and REFRACT Workflow downloads follow the same redirect pattern: the portal resolves a per-download credential and 302s the browser to your own deployment, which serves the bytes directly. For REFRACT Workflow that deployment is always your Dispatcher; for REFRACT Delivery it is your Dispatcher, or, when the document is SharePoint-stored, your SharePoint site directly. The one remaining exception is a REFRACT Workflow attachment upload - the file passes through the portal's own process in memory on its way to your Dispatcher, and is never written to REFRACT storage.
Key Management
All encryption keys are stored in your Azure Key Vault. REFRACT PDF uses:
| Key | Purpose | Storage |
|---|---|---|
WORKFLOW_ENCRYPTION_KEY (falls back to ESIGN_ENCRYPTION_KEY) | HKDF root key; derives kEnc and kMac for portal tokens | Your Azure Key Vault |
| Approver-email AES-256-GCM key | Encrypts approver PII in workflow state | Your Azure Key Vault |
AUDIT_REPORT_HMAC_KEY | HMAC-SHA256 signs verification reports | Your Azure App Settings |
| ACS email key (optional) | Customer BYO email via Azure Communication Services | Your Azure Key Vault |
Refract Logic staff have no access to your Azure Key Vault. Key rotation is an operator action performed by your team using az keyvault secret set or the Azure Key Vault portal.
Network Controls
All four Azure Storage accounts (stDisp, stProc, stData, stOut) have networkAcls.defaultAction: Deny with bypass: AzureServices. External internet access to storage is blocked at the network ACL layer. Function App MSI access traverses the Azure backbone.
REFRACT PDF's outbound calls to the Licensing API and eSign Portal use HTTPS/TLS 1.2+. No inbound firewall rules need to be opened for these calls.
VNet Isolation
For customers requiring strict VNet-level isolation (e.g., FedRAMP High or air-gapped deployments): deploy Isolated Azure Functions on a Premium App Service Plan with VNet integration and private endpoints. The Bicep templates support this with parameter changes.
Security Questions?
Contact our security team for architecture questionnaires, pen-test scoping, and enterprise procurement support.
Trust Center
Enterprise security, compliance, and data-handling reference for REFRACT PDF. Share this section with your security team, procurement department, or any customer requesting a security review.
Data Processing Guide
What data REFRACT PDF processes, where it flows, how long it is retained, and how it relates to GDPR and data-residency requirements.