Storage Providers
How to configure REFRACT PDF's storage system. Covers SharePoint setup, Azure Blob Storage setup, output routing via storage-config.json, required environment variables, and the response headers returned on each render.
Overview
REFRACT PDF uses two storage providers. You configure which one handles each output library using storage-config.json.
| Provider | Role |
|---|---|
| SharePoint | Always active. REFRACT PDF reads all templates, payloads, configurations, and assets from your SharePoint site. SharePoint can also receive generated PDFs, reports, and archive files. |
| Azure Blob Storage | Always deployed. REFRACT eSign, REFRACT Workflow and REFRACT Delivery keep their records, audit chains and documents here. You can also route any output library (generated documents, reports, archive, audit logs) here instead of SharePoint. |
SharePoint is always required, and so is Azure Blob Storage. The installer deploys the storage account on every install, whatever your storage-config.json routes say. REFRACT eSign, REFRACT Workflow and REFRACT Delivery cannot run without it: they keep their session and workflow records, their tamper-evident audit chains, and the documents they sign, seal or deliver there. Your routes decide only where generated documents, reports, archive files and audit logs are stored, so you can still keep every document in SharePoint.
If a deployment is missing the storage account's settings anyway (an app setting removed by hand, for example), eSign, Workflow and Delivery refuse every request with BLOB_STORAGE_NOT_CONFIGURED, naming the missing settings, rather than failing part-way through, and GET /api/healthz on the Dispatcher reports blobStorage as missing. Run the installer again to restore it.
Step 1 -- Configure SharePoint
sp-config.json
REFRACT PDF needs the SharePoint site and library identifiers for your tenant. Provide these in sp-config.json, which is deployed alongside the REFRACT PDF service.
{
"siteId": "contoso.sharepoint.com,<site-guid>,<web-guid>",
"driveIds": {
"Templates": "<drive-id>",
"System Templates": "<drive-id>",
"Configurations": "<drive-id>",
"Payloads": "<drive-id>",
"Generated Documents": "<drive-id>",
"Reports": "<drive-id>",
"Archive": "<drive-id>",
"Audit Logs": "<drive-id>"
},
"lists": {
"REFRACT_Activity": "<list-id>"
},
"features": {
"generatedDocuments": true,
"reports": true,
"archive": true,
"auditLogs": true
}
}How to find your IDs:
| Value | How to retrieve |
|---|---|
siteId | Call GET https://graph.microsoft.com/v1.0/sites/{hostname}:{path} and copy the id field |
driveIds | Call GET https://graph.microsoft.com/v1.0/sites/{siteId}/drives and match each library by name |
REFRACT_Activity list ID | Call GET https://graph.microsoft.com/v1.0/sites/{siteId}/lists and match by displayName |
Feature flags
The features object controls which output libraries are active. Set a flag to false to silently skip writes to that library.
| Flag | Effect when false |
|---|---|
features.generatedDocuments | Generated PDFs are not uploaded to SharePoint |
features.reports | Error and scheduled reports are not uploaded |
features.archive | Archive writes are skipped |
features.auditLogs | Audit JSONL writes to SharePoint are skipped |
When a library is routed to Azure Blob in storage-config.json, REFRACT PDF automatically treats its feature flag as enabled -- you do not need to set it to true in sp-config.json.
SP_SECRETS environment variable
The SharePoint credentials are provided as a JSON string in the SP_SECRETS environment variable. Set this in your production app settings, backed by a Key Vault reference.
{
"tenantId": "<azure-ad-tenant-id>",
"clientId": "<app-registration-client-id>",
"clientSecret": "<secret>"
}These are the credentials of the REFRACT SharePoint Access app registration, which you create before the deployment. It needs one Microsoft Graph application permission, Sites.ReadWrite.All, with admin consent. The deployment stores the credentials in your Azure Key Vault as sp-secrets. If SP_SECRETS holds no client ID and secret, the Function Apps use their managed identity instead. See Connect a source system.
Step 2 -- Choose Output Storage
By default, all generated documents are written back to SharePoint. If you want some or all output libraries routed to Azure Blob Storage instead, follow the steps below. Either way, the storage account itself is deployed (see Overview).
All documents in SharePoint (default)
No additional configuration needed. The Azure Storage account is still deployed and used by eSign, Workflow and Delivery; only the documents go to SharePoint. Omit STORAGE_CONFIG_PATH from your app settings, or create a storage-config.json with all routes set to sharepoint:
{
"libraryRoutes": {
"generatedDocuments": "sharepoint",
"reports": "sharepoint",
"archive": "sharepoint",
"auditLogs": "basic"
},
"accountName": "",
"blobContainers": {}
}Documents in Azure Blob Storage
The installer has already created the storage account, a container for each output library, and the Managed Identity's roles on them. To route one or more output libraries there:
- Set
STORAGE_CONFIG_PATHin your app settings to the path of yourstorage-config.jsonfile. - Configure
storage-config.jsonas described in the next section.
Blob naming convention
Files written to Azure Blob follow this path pattern:
{DocType}/{YYYY}/{MM}/{correlationId}_{fileName}.pdfExamples:
Invoice/2026/03/BC-INV-001_Invoice_2026-03-31.pdf
Reports/RenderErrors/2026/03/abc-123_error_render.pdf
document-archive/2026/01/abc-100_SO-00400-Invoice.pdfReferencing an uploaded blob
Files written to Azure Blob are referenced by their blob path, returned in the X-Refract-BlobPath response header, and stored on the Activity list item. A time-limited SAS-URL header (X-Refract-BlobSasUrl) is defined in the codebase but is not currently populated by any route - do not build an integration that depends on it being present.
storage-config.json
storage-config.json controls which output libraries go to SharePoint and which go to Azure Blob. Deploy this file alongside the REFRACT PDF service and set STORAGE_CONFIG_PATH to its path.
Full schema
{
"libraryRoutes": {
"generatedDocuments": "sharepoint | blob",
"reports": "sharepoint | blob",
"archive": "sharepoint | blob",
"auditLogs": "basic | blob"
},
"accountName": "your-storage-account-name",
"blobContainers": {
"generatedDocuments": "generated-documents",
"reports": "reports",
"archive": "document-archive",
"auditLogs": "audit-logs"
},
"sasUrlExpiryDays": 30
}Field reference
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
libraryRoutes | object | Yes | -- | Per-library routing declarations |
accountName | string | When any route is blob | -- | Azure Storage account name |
blobContainers | object | When any route is blob | -- | Library to container name map |
sasUrlExpiryDays | number | No | 30 | Days until generated SAS URLs expire. Max recommended: 180. |
libraryRoutes valid values
| Library | Valid values | Default |
|---|---|---|
generatedDocuments | 'sharepoint' | 'blob' | 'sharepoint' |
reports | 'sharepoint' | 'blob' | 'sharepoint' |
archive | 'sharepoint' | 'blob' | 'sharepoint' |
auditLogs | 'basic' | 'blob' | 'basic' |
'enterprise' is withdrawn and must not be used; see Audit Logs.
Libraries omitted from libraryRoutes fall back to their defaults. If STORAGE_CONFIG_PATH is not set at all, every library defaults to SharePoint.
Example: Reports on SharePoint, everything else on Blob
A common production configuration -- reports stay in SharePoint so users can browse them, generated documents and archive go to Blob for cost efficiency:
{
"libraryRoutes": {
"generatedDocuments": "blob",
"reports": "sharepoint",
"archive": "blob",
"auditLogs": "blob"
},
"accountName": "contosorefractstorage",
"blobContainers": {
"generatedDocuments": "generated-documents",
"archive": "document-archive",
"auditLogs": "audit-logs"
},
"sasUrlExpiryDays": 90
}Example: all documents in SharePoint
{
"libraryRoutes": {
"generatedDocuments": "sharepoint",
"reports": "sharepoint",
"archive": "sharepoint",
"auditLogs": "basic"
},
"accountName": "",
"blobContainers": {}
}Environment Variables
Set these in your production app settings. Use Key Vault references for all secrets.
| Variable | Required | Description |
|---|---|---|
SP_CONFIG_PATH | Yes | Absolute path to sp-config.json on the deployment filesystem |
SP_SECRETS | Yes | JSON string with tenantId, clientId, clientSecret for SharePoint access |
STORAGE_CONFIG_PATH | No | Absolute path to storage-config.json. When absent, all output routes default to SharePoint. |
Response Headers
REFRACT PDF sets the following headers on every successful render response. Which headers are present depends on your libraryRoutes configuration.
| Header | Present when | Value |
|---|---|---|
X-Refract-UploadStatus | The document was stored, or storing it was skipped | ok when stored, skipped when not |
X-Refract-UploadError | Storing was skipped because no storage is configured or generatedDocuments is not enabled | The reason |
X-Refract-WebUrl | generatedDocuments: sharepoint | SharePoint item web URL |
X-Refract-DriveItemId | generatedDocuments: sharepoint | SharePoint DriveItem ID |
X-Refract-BlobPath | The document was stored | The blob path of the stored PDF, or its SharePoint URL when it was stored in SharePoint |
X-Refract-AuditStatus | Audit write skipped or failed | skipped:<reason> or failed:<reason> |
X-Refract-BlobPath names the stored document whichever storage was used; a SharePoint upload adds X-Refract-WebUrl and X-Refract-DriveItemId. X-Refract-BlobUrl and X-Refract-BlobSasUrl are defined in the codebase but are not currently populated by any route. The Dispatcher sends these X-Refract- names and none of the earlier ones (see Earlier Header Names).
Related Pages
- Audit Logs -- choose and configure an audit tier (basic or blob)
- System Architecture -- how REFRACT PDF fits into your tenant
Relay Mode
How to trigger asynchronous, queue-based rendering for a single document via options.features.relay, how the request is routed through Service Bus, and how to confirm after the fact whether a render was queued or returned inline.
Audit Logs
How to configure REFRACT PDF's audit system. Covers the basic (SharePoint JSONL) and blob (Azure Blob AppendBlob) audit tiers, the AuditEvent schema, file naming, and setup steps for each tier. The enterprise tier is withdrawn.