Core Concepts

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.

ProviderRole
SharePointAlways 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 StorageAlways 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:

ValueHow to retrieve
siteIdCall GET https://graph.microsoft.com/v1.0/sites/{hostname}:{path} and copy the id field
driveIdsCall GET https://graph.microsoft.com/v1.0/sites/{siteId}/drives and match each library by name
REFRACT_Activity list IDCall 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.

FlagEffect when false
features.generatedDocumentsGenerated PDFs are not uploaded to SharePoint
features.reportsError and scheduled reports are not uploaded
features.archiveArchive writes are skipped
features.auditLogsAudit 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:

  1. Set STORAGE_CONFIG_PATH in your app settings to the path of your storage-config.json file.
  2. Configure storage-config.json as described in the next section.

Blob naming convention

Files written to Azure Blob follow this path pattern:

{DocType}/{YYYY}/{MM}/{correlationId}_{fileName}.pdf

Examples:

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.pdf

Referencing 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

FieldTypeRequiredDefaultDescription
libraryRoutesobjectYes--Per-library routing declarations
accountNamestringWhen any route is blob--Azure Storage account name
blobContainersobjectWhen any route is blob--Library to container name map
sasUrlExpiryDaysnumberNo30Days until generated SAS URLs expire. Max recommended: 180.

libraryRoutes valid values

LibraryValid valuesDefault
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.

VariableRequiredDescription
SP_CONFIG_PATHYesAbsolute path to sp-config.json on the deployment filesystem
SP_SECRETSYesJSON string with tenantId, clientId, clientSecret for SharePoint access
STORAGE_CONFIG_PATHNoAbsolute 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.

HeaderPresent whenValue
X-Refract-UploadStatusThe document was stored, or storing it was skippedok when stored, skipped when not
X-Refract-UploadErrorStoring was skipped because no storage is configured or generatedDocuments is not enabledThe reason
X-Refract-WebUrlgeneratedDocuments: sharepointSharePoint item web URL
X-Refract-DriveItemIdgeneratedDocuments: sharepointSharePoint DriveItem ID
X-Refract-BlobPathThe document was storedThe blob path of the stored PDF, or its SharePoint URL when it was stored in SharePoint
X-Refract-AuditStatusAudit write skipped or failedskipped:<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).


On this page