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 by | What | Details |
|---|---|---|
infra/deploy.ps1 | The "REFRACT Dispatcher API" app registration | Single 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.ps1 | Microsoft Graph application permissions for the Dispatcher's managed identity: Sites.ReadWrite.All and GroupMember.Read.All | You 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 itsRender.AccessandLifecycle.Readerroles 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.AccessandLifecycle.Readerto no app, and assignsLifecycle.Readerto no person. At the end of each run it warns if no one holdsLifecycle.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 create | Why | How |
|---|---|---|
| "REFRACT SharePoint Access" app registration | The 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 system | The source system's identity when it calls the Dispatcher | Create a connector app below |
| SPFx API access approval | Lets REFRACT Lifecycle call the Dispatcher as the signed-in person | After 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 people | Lets people read document records in REFRACT Lifecycle | Assign the role to a group. See Give people access to document records. |
The model
Shared apps (one each per deployment)
| App | What it is | Who signs in as it |
|---|---|---|
| REFRACT Dispatcher API | The 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 Access | The Dispatcher's access to SharePoint through Microsoft Graph | The 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 system | Render.Access | Lifecycle.Reader |
|---|---|---|
| Business Central | Yes, every call | Yes: Open Document, Sync Workflow Status, and the sealed workflow PDF |
| Dynamics 365 Sales | Yes, every call | Yes: Open Document, Sync Workflow Status, and the sealed workflow PDF |
| Dynamics GP | Yes, every call | No: GP only generates documents |
| Power Platform connector | Yes, every action | Yes, for Get Document URL, Check Workflow Status, and Download Sealed PDF |
| Your own scripts and API testing | Yes, every call | Only 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 use | App registrations |
|---|---|
| Business Central only | 3: REFRACT Dispatcher API, REFRACT SharePoint Access, and a Business Central connector |
| Business Central and Dynamics 365 Sales | 4: 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:
| Value | Answers | What to enter | Same for every caller? |
|---|---|---|---|
| Tenant ID | Which directory issues the token | Your Microsoft Entra tenant ID | Yes |
| Client ID | Who is calling | The connector app's Application (client) ID | No, one per system |
| Client secret | Proof that the caller is that connector | The connector app's secret value | No, one per system |
| Scope | What the token is for | api://<dispatcher-app-client-id>/.default | Yes |
<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 app | As a person | |
|---|---|---|
| Postman grant type | Client Credentials | Authorization Code (With PKCE) |
| Client ID and secret | The testing connector app's | The testing connector app's |
| Scope | api://<dispatcher-app-client-id>/.default | api://<dispatcher-app-client-id>/.default |
| Permissions the testing app needs | Application permission Render.Access, plus Lifecycle.Reader to read records | Delegated permission user_impersonation, with admin consent |
| What the person needs | Nothing (no one signs in) | Lifecycle.Reader, assigned to them, to read records |
| Token carries | roles from the testing app | scp: 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.
| Field | Value |
|---|---|
| Name | A name that says which system it is, for example REFRACT Connector - Business Central |
| Supported account types | Accounts in this organizational directory only (single tenant) |
| Redirect URI | Leave 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.
- 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. - Select Add a permission -> APIs my organization uses, and search for REFRACT Dispatcher API (or the name your deployment gave it).
- Choose Application permissions, select Render.Access and, if this system reads document records, Lifecycle.Reader (see Connector apps). Select Add permissions.
Grant admin consent
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.Allfor 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:
| Key | Value |
|---|---|
REFRACTPDF_DISPATCHER_BASE_URL | https://<your-dispatcher>.azurewebsites.net/api |
REFRACTPDF_DISPATCHER_TENANT_ID | Your tenant ID |
REFRACTPDF_DISPATCHER_CLIENT_ID | The Business Central connector's client ID |
REFRACTPDF_DISPATCHER_CLIENT_SECRET | The Business Central connector's secret |
REFRACTPDF_DISPATCHER_SCOPE | api://<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.
| Key | Value |
|---|---|
REFRACT_TENANT_ID | Your tenant ID, used for both apps |
REFRACT_CLIENT_ID | REFRACT SharePoint Access client ID, for GP's SharePoint (Graph) uploads |
REFRACT_CLIENT_SECRET | REFRACT SharePoint Access secret |
REFRACT_DISPATCHER_CLIENT_ID | The GP connector's client ID |
REFRACT_DISPATCHER_CLIENT_SECRET | The GP connector's secret |
REFRACT_DISPATCHER_BASE_URL | https://<your-dispatcher>.azurewebsites.net/api |
REFRACT_DISPATCHER_SCOPE | api://<dispatcher-app-client-id>/.default |
REFRACT_GRAPH_SITE_ID, REFRACT_GRAPH_DRIVE_ID, REFRACT_GRAPH_TEMPLATES_DRIVE_ID | Your 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 leavesSettingValueempty. 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 grantsEXECUTEon it toDYNGRP.
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".
- Deploy the add-in first, on every GP workstation and Remote Desktop host. An older add-in reads
SettingValuedirectly, so it finds the secrets empty once the setup script has encrypted them. - Run
REFRACT_DATABASE_SETUP.sqlin 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. - Check the database role. The script grants
EXECUTEondbo.REFRACT_GetSystemSettingstoDYNGRP. If your GP users are in another role, grant that roleEXECUTEon the procedure, or GP reportsThis GP user may not read the REFRACT settings. - 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. - 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 variable | Value |
|---|---|
refract_dispatcher_baseurl | https://<your-dispatcher>.azurewebsites.net/api |
refract_dispatcher_tenantid | Your tenant ID |
refract_dispatcher_clientid | The Dynamics 365 Sales connector's client ID |
refract_dispatcher_clientsecret | The Dynamics 365 Sales connector's secret |
refract_dispatcher_scope | api://<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 field | Value |
|---|---|
| Dispatcher URL | https://<your-dispatcher>.azurewebsites.net, with no /api |
| Azure AD Tenant ID | Your tenant ID |
| Client ID | The Power Platform connector app's client ID |
| Client Secret | The Power Platform connector app's secret |
| OAuth Scope | api://<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_IDset to the client ID of REFRACT SharePoint Access or of "REFRACT Dispatcher API". Earlier versions of the installer and ofdeploy.ps1put those IDs there. - Dynamics 365 Sales with
refract_dispatcher_clientidset to the client ID of "REFRACT Dispatcher API". - Dynamics GP with
REFRACT_DISPATCHER_CLIENT_IDempty, so it calls as theREFRACT_CLIENT_IDapp (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)
| Code | Meaning | Fix |
|---|---|---|
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.Reader | An 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_MISSING | A signed-in person's token without the user_impersonation scope | Grant 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_DECIDE | An app tried to approve, reject, request changes, escalate, make a governance decision, or decide a batch | No 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.
Authentication
How callers get tokens for the Dispatcher - connector apps with client credentials, and people through REFRACT Lifecycle or a testing app - and which callers the Dispatcher accepts.
Error responses and codes
The error body, the error headers, when to retry, and every error code the REFRACT Dispatcher returns to a caller, grouped by area.