REFRACT Dispatcher API

Connect a source system

Which Microsoft Entra ID app registrations a REFRACT deployment creates, which ones you create, and how to connect Business Central, Dynamics 365 Sales, Dynamics GP, Power Platform, or your own scripts to the Dispatcher with a connector app per system.

A source system (Business Central, Dynamics 365 Sales, Dynamics GP, a Power Automate flow, or your own script) calls the Dispatcher with an access token from your Microsoft Entra ID tenant. This page explains which app registrations are involved, which ones you create, and what to enter in each system.

In short: every source system gets its own connector app. The connector app is who is calling. "REFRACT Dispatcher API" is what the call is for. Nobody signs in as "REFRACT Dispatcher API", and no source system holds its credentials.


What our deployment creates for you

Created byWhatDetails
infra/deploy.ps1The "REFRACT Dispatcher API" app registrationSingle tenant. Application ID URI api://<appId>. One delegated scope, user_impersonation, which needs admin consent. Two app roles: Render.Access (applications only) and Lifecycle.Reader (users, groups, and applications). A service principal (the "REFRACT Dispatcher API" entry in Enterprise applications). The Dispatcher's JWT_AUDIENCE setting is set to this app's ID, so it accepts only tokens issued for this app.
infra/Grant-SpAccess.ps1Microsoft Graph application permissions for the Dispatcher's managed identity: Sites.ReadWrite.All and GroupMember.Read.AllYou run it once after the Azure deployment (Step 8 of the deployment guide). The Dispatcher uses GroupMember.Read.All for group-membership checks. It uses the managed identity for SharePoint only when sp-secrets holds no client ID and secret. deploy.ps1 checks for GroupMember.Read.All and prints the command to run if it is missing.

Running deploy.ps1 creates an app registration and grants admin consent for its user_impersonation scope, which needs the Application Administrator or Cloud Application Administrator role in Microsoft Entra ID, as well as the Azure roles the deployment itself needs. Running Grant-SpAccess.ps1 assigns Microsoft Graph application permissions, which needs Privileged Role Administrator.

At the end of each run, deploy.ps1 prints what each source system needs:

  • Dispatcher app: its App ID, labelled as the API every source system calls and never the caller, the scope api://<appId>/.default, and the IDs of its Render.Access and Lifecycle.Reader roles in your tenant.
  • One short block per source system (Business Central, Dynamics 365, the Power Platform connector, and Dynamics GP) with the roles it needs and the settings to enter.
  • The commands to grant each role to a connector app by a direct app role assignment, and a link to this page.

What the deployment does not do:

  • It creates no client secret for "REFRACT Dispatcher API" and no redirect URIs. Nothing needs to sign in as this app.
  • It grants Render.Access and Lifecycle.Reader to no app, and assigns Lifecycle.Reader to no person. At the end of each run it warns if no one holds Lifecycle.Reader.
  • It creates no connector app for any source system, and does not create "REFRACT SharePoint Access".
  • It does not approve the SharePoint Framework (SPFx) API access request.

What you create

You createWhyHow
"REFRACT SharePoint Access" app registrationThe Dispatcher reads and writes your SharePoint libraries as this app. Its credentials are stored in your Azure Key Vault as sp-secrets.One Microsoft Graph application permission: Sites.ReadWrite.All, with admin consent. Create it before the deployment (Step 4 of the deployment guide).
One connector app per source systemThe source system's identity when it calls the DispatcherCreate a connector app below
SPFx API access approvalLets REFRACT Lifecycle call the Dispatcher as the signed-in personAfter the refract-spfx.sppkg package is deployed, open the SharePoint admin center -> Advanced -> API access and approve the pending REFRACT Dispatcher API / user_impersonation request. An Application Administrator can approve it. Until it is approved, every REFRACT Lifecycle call to the Dispatcher fails.
Lifecycle.Reader assignment for peopleLets people read document records in REFRACT LifecycleAssign the role to a group. See Give people access to document records.

The model

Shared apps (one each per deployment)

AppWhat it isWho signs in as it
REFRACT Dispatcher APIThe resource every token is for. Its app roles (Render.Access, Lifecycle.Reader) and its user_impersonation scope are the permissions you grant to callers.Nobody. It has no secret and is never a caller.
REFRACT SharePoint AccessThe Dispatcher's access to SharePoint through Microsoft GraphThe Dispatcher, with the credentials in sp-secrets. Business Central, Dynamics 365 Sales, and Dynamics GP also use it for their own SharePoint uploads (see Settings for each system).

Connector apps (one per source system)

Source systemRender.AccessLifecycle.Reader
Business CentralYes, every callYes: Open Document, Sync Workflow Status, and the sealed workflow PDF
Dynamics 365 SalesYes, every callYes: Open Document, Sync Workflow Status, and the sealed workflow PDF
Dynamics GPYes, every callNo: GP only generates documents
Power Platform connectorYes, every actionYes, for Get Document URL, Check Workflow Status, and Download Sealed PDF
Your own scripts and API testingYes, every callOnly if the script reads document records

Both are application permissions on "REFRACT Dispatcher API". No connector app can approve, reject, request changes, escalate, or decide a batch, whatever it holds: decisions are made by signed-in people.

With Lifecycle.Reader, an app reads a workflow's sealed PDF and attachments as it reads any other stored document: an app is never a workflow participant. The document's restriction policy, when it has one, still applies.

Worked examples

You useApp registrations
Business Central only3: REFRACT Dispatcher API, REFRACT SharePoint Access, and a Business Central connector
Business Central and Dynamics 365 Sales4: the two shared apps, plus a Business Central connector and a Dynamics 365 Sales connector
Each further system (GP, Power Platform, a script)+1 connector each

Why one connector per system:

  • Revoke one system, not all of them. If one system's secret leaks or the system is retired, delete that connector's secret. The other systems keep working.
  • Sign-in logs show the caller. Each connector has its own name in Microsoft Entra ID's service principal sign-in logs, so you can tell which system made a call.
  • No system holds the Dispatcher's own credentials. "REFRACT Dispatcher API" never gets a secret, so there is nothing to copy into a source system.

Client ID vs scope

Every token request carries a client ID, a client secret, and a scope. They answer different questions:

ValueAnswersWhat to enterSame for every caller?
Tenant IDWhich directory issues the tokenYour Microsoft Entra tenant IDYes
Client IDWho is callingThe connector app's Application (client) IDNo, one per system
Client secretProof that the caller is that connectorThe connector app's secret valueNo, one per system
ScopeWhat the token is forapi://<dispatcher-app-client-id>/.defaultYes

<dispatcher-app-client-id> is the Application (client) ID of "REFRACT Dispatcher API" (App registrations -> REFRACT Dispatcher API -> Overview). It goes only in the scope, never in a Client ID field. deploy.ps1's closing output prints it as the Dispatcher app App ID, together with the full scope. /.default asks for every permission the caller has been granted on that app. The Dispatcher accepts a token whose audience is the app's ID with or without the api:// prefix, but enter the api:// form everywhere so the value is the same in every system.

Postman as an app or as a person

You can test with Postman either way, using a connector app created for testing (for example "REFRACT API testing"):

As an appAs a person
Postman grant typeClient CredentialsAuthorization Code (With PKCE)
Client ID and secretThe testing connector app'sThe testing connector app's
Scopeapi://<dispatcher-app-client-id>/.defaultapi://<dispatcher-app-client-id>/.default
Permissions the testing app needsApplication permission Render.Access, plus Lifecycle.Reader to read recordsDelegated permission user_impersonation, with admin consent
What the person needsNothing (no one signs in)Lifecycle.Reader, assigned to them, to read records
Token carriesroles from the testing appscp: user_impersonation, and the person's own roles

See Testing with Postman for the Postman settings.


Create a connector app

Repeat these steps once for each source system.

Register the app

In the Microsoft Entra admin center, open App registrations -> New registration.

FieldValue
NameA name that says which system it is, for example REFRACT Connector - Business Central
Supported account typesAccounts in this organizational directory only (single tenant)
Redirect URILeave empty. A connector signs in with its secret, not in a browser.

Select Register. From Overview, copy the Application (client) ID and the Directory (tenant) ID.

Create a client secret

Open Certificates & secrets -> Client secrets -> New client secret, give it a description and an expiry, and select Add.

Copy the Value column straight away. It is shown only once; the Secret ID is not the secret. Record the expiry date and plan the renewal: when the secret expires, every token request from that system fails until you enter a new one.

Set the API permissions

Open API permissions.

  1. A new registration lists the Microsoft Graph delegated permission User.Read. A connector does not sign anyone in, so remove it: select the ... next to it -> Remove permission -> Yes, remove.
  2. Select Add a permission -> APIs my organization uses, and search for REFRACT Dispatcher API (or the name your deployment gave it).
  3. Choose Application permissions, select Render.Access and, if this system reads document records, Lifecycle.Reader (see Connector apps). Select Add permissions.

Select Grant admin consent for (your tenant) and confirm. The Status column shows Granted for (your tenant) for every permission.

Admin consent approves everything on the list

Grant admin consent approves every permission listed on the page, not only the ones you just added. Check the list first and remove anything the connector does not need.

Instead of admin consent, you can grant each role by a direct app role assignment, with the commands deploy.ps1 prints at the end of its run. A direct assignment grants exactly that role and approves nothing else on the connector's list, so the portal's default User.Read cannot slip through. The connector's API permissions page then shows the role under Other permissions granted for (your tenant).

To grant consent, or to make a direct assignment of a "REFRACT Dispatcher API" role, you need one of these Microsoft Entra roles:

  • Application Administrator or Cloud Application Administrator, for permissions on "REFRACT Dispatcher API".
  • Privileged Role Administrator, when the list includes a Microsoft Graph application permission, such as Sites.ReadWrite.All for REFRACT SharePoint Access.

Apps are not assigned in Enterprise applications

Enterprise applications -> REFRACT Dispatcher API -> Users and groups lists people and groups only, and you cannot add an app there. A connector gets its roles from admin consent on its API permissions page, or from a direct app role assignment. That page is where you give people access.


Settings for each system

Business Central

Enter the connector's values in REFRACT System Settings:

KeyValue
REFRACTPDF_DISPATCHER_BASE_URLhttps://<your-dispatcher>.azurewebsites.net/api
REFRACTPDF_DISPATCHER_TENANT_IDYour tenant ID
REFRACTPDF_DISPATCHER_CLIENT_IDThe Business Central connector's client ID
REFRACTPDF_DISPATCHER_CLIENT_SECRETThe Business Central connector's secret
REFRACTPDF_DISPATCHER_SCOPEapi://<dispatcher-app-client-id>/.default

Business Central's own SharePoint uploads use a separate app: REFRACTPDF_TENANT_ID, REFRACTPDF_CLIENT_ID, and REFRACTPDF_CLIENT_SECRET take the REFRACT SharePoint Access app's values. The installer leaves REFRACTPDF_DISPATCHER_CLIENT_ID and REFRACTPDF_DISPATCHER_CLIENT_SECRET empty in the settings file it generates, and warns that the client ID still has to be filled in. Enter the Business Central connector's values there. See Business Central setup.

Dynamics GP

GP reads its settings from the REFRACT_SystemSettings table in each GP company database where the add-in is used. REFRACT_DATABASE_SETUP.sql creates the keys; set their values with UPDATE REFRACT_SystemSettings.

GP follows the same pattern as Business Central and Dynamics 365 Sales: REFRACT SharePoint Access for its SharePoint uploads, and a GP connector app for its calls to the Dispatcher.

KeyValue
REFRACT_TENANT_IDYour tenant ID, used for both apps
REFRACT_CLIENT_IDREFRACT SharePoint Access client ID, for GP's SharePoint (Graph) uploads
REFRACT_CLIENT_SECRETREFRACT SharePoint Access secret
REFRACT_DISPATCHER_CLIENT_IDThe GP connector's client ID
REFRACT_DISPATCHER_CLIENT_SECRETThe GP connector's secret
REFRACT_DISPATCHER_BASE_URLhttps://<your-dispatcher>.azurewebsites.net/api
REFRACT_DISPATCHER_SCOPEapi://<dispatcher-app-client-id>/.default
REFRACT_GRAPH_SITE_ID, REFRACT_GRAPH_DRIVE_ID, REFRACT_GRAPH_TEMPLATES_DRIVE_IDYour SharePoint site and library IDs

The GP connector needs Render.Access only, and no Microsoft Graph permission.

When REFRACT_DISPATCHER_CLIENT_ID is empty, GP signs in to the Dispatcher with REFRACT_CLIENT_ID and REFRACT_CLIENT_SECRET instead, which is how setups made before the two Dispatcher keys existed keep working. The client ID and its secret always come from the same app: a REFRACT_DISPATCHER_CLIENT_SECRET without REFRACT_DISPATCHER_CLIENT_ID is ignored, and a REFRACT_DISPATCHER_CLIENT_ID without its secret stops GP with REFRACT_DISPATCHER_CLIENT_SECRET is not configured.

REFRACT_DISPATCHER_SCOPE is required. When it is empty, GP stops with REFRACT_DISPATCHER_SCOPE is not configured in REFRACT_SystemSettings.

How GP stores its secrets

REFRACT_CLIENT_SECRET, REFRACT_DISPATCHER_CLIENT_SECRET, and any other key that dbo.REFRACT_IsSecretSetting treats as a secret (names containing SECRET, and the Dispatcher function keys of older versions) are stored AES-256 encrypted. The key is kept in the company database: the symmetric key REFRACT_SettingsKey is protected by the certificate REFRACT_SettingsCert, which is protected by the database master key, which SQL Server's service master key opens.

  • Setting a secret: use the same plain UPDATE REFRACT_SystemSettings SET SettingValue = '<secret>' WHERE SettingKey = '<key>' as for any other key. A trigger encrypts the value as it is written and leaves SettingValue empty. Setting a secret to '' clears it. A secret is limited to 8000 bytes.
  • Reading: the add-in reads every setting through the stored procedure dbo.REFRACT_GetSystemSettings, which returns the secrets decrypted. The setup script grants EXECUTE on it to DYNGRP.

What this protects: a direct SELECT on REFRACT_SystemSettings, database backups, and copied database files show only ciphertext. A backup restored on another SQL Server cannot be decrypted there until the database master key is moved too.

What it does not protect: any GP user who can run the add-in can obtain both client secrets, including the REFRACT SharePoint Access secret, by running dbo.REFRACT_GetSystemSettings, because the add-in itself needs the secrets to sign in. Database owners and SQL Server administrators can decrypt them as well.

The add-in's Debug output names the settings it reads and whether each was found. For each Dispatcher token it shows the client ID and scope it requested, and the token's aud, appid, and roles claims. It never writes a secret or a token.

Installing or upgrading GP

Do these in order. The GP add-in's DEPLOYMENT_CHECKLIST.md has the full commands, under "Database setup", "Secret settings", and "Upgrading an existing installation".

  1. Deploy the add-in first, on every GP workstation and Remote Desktop host. An older add-in reads SettingValue directly, so it finds the secrets empty once the setup script has encrypted them.
  2. Run REFRACT_DATABASE_SETUP.sql in each company database that uses the add-in. It is safe to run again, and on an existing database it encrypts any secret still stored as plain text.
  3. Check the database role. The script grants EXECUTE on dbo.REFRACT_GetSystemSettings to DYNGRP. If your GP users are in another role, grant that role EXECUTE on the procedure, or GP reports This GP user may not read the REFRACT settings.
  4. After moving from plain text, rotate both client secrets. Older backups and log backups still hold the plain text. Create new secrets in Microsoft Entra ID, set them with UPDATE, and delete the old ones in Entra.
  5. Before moving a company database to another SQL Server, back up its database master key (BACKUP MASTER KEY) and restore it on the new server. Without it, GP reports ... is encrypted in REFRACT_SystemSettings but cannot be decrypted on this SQL Server, and you must enter the secrets again. Restoring a company into another company on the same SQL Server needs nothing.

Dynamics 365 Sales

Set the Dataverse environment variables in Solutions -> REFRACT Platform for D365 Sales -> Environment variables:

Environment variableValue
refract_dispatcher_baseurlhttps://<your-dispatcher>.azurewebsites.net/api
refract_dispatcher_tenantidYour tenant ID
refract_dispatcher_clientidThe Dynamics 365 Sales connector's client ID
refract_dispatcher_clientsecretThe Dynamics 365 Sales connector's secret
refract_dispatcher_scopeapi://<dispatcher-app-client-id>/.default

refract_dispatcher_scope is optional in the solution. When it is empty, the plugin requests <refract_dispatcher_clientid>/.default, which is right only when refract_dispatcher_clientid is "REFRACT Dispatcher API" itself. Installs made before the variable existed are set up that way and keep working unchanged. With a connector app of its own, set all three: its client ID, its secret, and refract_dispatcher_scope.

The connector needs Render.Access for every call and Lifecycle.Reader for Open Document and Sync Workflow Status.

After any change, run the REFRACT: Validate Settings Custom API (refract_ValidateSettings). It signs in with a fresh token and reads Render.Access and Lifecycle.Reader from the token's roles claim, then calls the Dispatcher's GET /lifecycle-access as the live check that the Dispatcher accepts the token. It fails if either role is missing, even though document generation may still work with Render.Access alone. It reports the scope it used and the token's aud, appid, and roles, never the token itself.

The plugin's SharePoint access uses the refract_graph_* variables, which take the REFRACT SharePoint Access app's values. See Dynamics 365 Sales setup.

Power Platform connector

Each connection asks for five values:

Connection fieldValue
Dispatcher URLhttps://<your-dispatcher>.azurewebsites.net, with no /api
Azure AD Tenant IDYour tenant ID
Client IDThe Power Platform connector app's client ID
Client SecretThe Power Platform connector app's secret
OAuth Scopeapi://<dispatcher-app-client-id>/.default

See Power Platform Connector Setup.

Your own scripts, and Postman

Request a token from https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token with grant_type=client_credentials, your connector's client ID and secret, and scope=api://<dispatcher-app-client-id>/.default, then send it as Authorization: Bearer <token>. There is no function key. For Postman, see Postman as an app or as a person.


Moving an existing deployment

Before you update the Dispatcher to a version that requires Render.Access for app tokens, check which app each source system calls as. Earlier instructions led to these setups, which stop working after the update because the app they call as does not hold Render.Access:

  • Business Central with REFRACTPDF_DISPATCHER_CLIENT_ID set to the client ID of REFRACT SharePoint Access or of "REFRACT Dispatcher API". Earlier versions of the installer and of deploy.ps1 put those IDs there.
  • Dynamics 365 Sales with refract_dispatcher_clientid set to the client ID of "REFRACT Dispatcher API".
  • Dynamics GP with REFRACT_DISPATCHER_CLIENT_ID empty, so it calls as the REFRACT_CLIENT_ID app (REFRACT SharePoint Access, or a single app used for both).

For each one, create a connector app, grant it the roles above, and enter its client ID, secret, and the api:// scope before you update. Granting Render.Access to REFRACT SharePoint Access or to "REFRACT Dispatcher API" instead would keep the old setup working, but it would let a SharePoint credential, or the resource itself, call the Dispatcher.

To see which apps call the Dispatcher today, open Microsoft Entra admin center -> Sign-in logs -> Service principal sign-ins, and filter on the resource "REFRACT Dispatcher API".


When the Dispatcher refuses a call (403)

CodeMeaningFix
ROLE_MISSING, "role": "Render.Access"An app token without Render.Access. Every call needs it.On that system's connector app, add the Render.Access application permission and grant admin consent.
ROLE_MISSING, "role": "Lifecycle.Reader"The caller read a document record without Lifecycle.ReaderAn app: add the Lifecycle.Reader application permission to its connector app and grant admin consent. A person: assign them the role; see Give people access to document records.
SCOPE_MISSINGA signed-in person's token without the user_impersonation scopeGrant the app they signed in with the delegated permission user_impersonation on "REFRACT Dispatcher API", with admin consent. For REFRACT Lifecycle, approve the SPFx API access request. Then sign out and sign in again.
APP_CANNOT_DECIDEAn app tried to approve, reject, request changes, escalate, make a governance decision, or decide a batchNo permission changes this. A signed-in person makes the decision, in REFRACT Lifecycle or from their emailed link.

A role or scope you add reaches a caller in its next token. A token the system already holds keeps its old permissions until it expires.

A 401 is a different problem: the Dispatcher rejected the token itself. Check that the scope is api://<dispatcher-app-client-id>/.default, that the token comes from your own tenant, and that it has not expired. See Authentication for the full responses.

On this page