REFRACT Platform for Dynamics 365 Sales

Developer Guide - REFRACT Platform for Dynamics 365 Sales

Technical architecture, detailed action flows, Dataverse table schemas, Custom API and plugin reference, batch dispatch modes, workflow enforcement, and deployment/ALM for the REFRACT Platform CRM integration.

Integration Architecture

The REFRACT Platform for Dynamics 365 Sales uses a native server-side processing layer within Dataverse. Each Command Bar action routes through a Custom Dataverse API to server-side logic that builds the document payload, uploads it to SharePoint, and calls the REFRACT Dispatcher.

LayerDynamics 365 Sales
UI triggerCommand Bar button
Template galleryJavaScript dialog
Server-side processingDataverse Plugin (runs within Dataverse sandbox)
Template and field mapping storageDataverse custom tables
SharePoint uploadMicrosoft Graph API
PDF generationREFRACT Dispatcher
Document link storageDataverse refract_documentlink table
Workflow enforcementDataverse pre-operation plugin
Credential storageDataverse Environment Variables

No Azure Function or Power Automate relay

There is no intermediate Azure Function or Power Automate flow in the processing path. The server-side plugin runs directly on the Dataverse platform. JavaScript handles UI only - it reads context, calls the Custom API, and displays results.


Dispatcher Contract

The Dispatcher is source-agnostic. It is a pure request-response service with four operations, and it only reads and writes SharePoint.

ActionEndpointMethodRequestResponse
Generate Document/renderPOST{ templatePath, payloadPath, options, templateSettings }PDF buffer + headers: X-REFRACTPDF-blobpath, X-REFRACTPDF-correlationid, X-REFRACTPDF-workflowid
Open Document/docGET?blobPath=...{ url: "<SAS_URL>" }
Workflow Status/workflow/{id}/statusGET{id} in path{ state, title, currentStepIndex, totalSteps }
Workflow Sealed PDF/workflow/{id}/exportGET{id} in path, ?format=pdf{ downloadUrl: "<SAS_URL>" }

The Dispatcher never calls back to Dynamics 365. There is no push, no webhook, and no write to Dataverse. All state updates in CRM happen when a CRM user explicitly triggers an action - pull model throughout.


Action Summary

Each Command Bar button routes to a dedicated Custom Dataverse API. The table below describes what each action does and when to use it.

ActionWhat it doesWhen to use
Process DocumentReads the CRM record, uploads a data payload to SharePoint, calls the Dispatcher to render the PDF, and stores a document link on the recordFirst time generating a document for a record, or to regenerate
Open DocumentRetrieves a short-lived download URL for the most recently generated PDF and opens it in a new tabViewing a document that has already been generated
Sync Workflow StatusQueries the Dispatcher for the current workflow state and displays it in a dialog; saves the outcome against the record on completion or rejectionChecking whether a workflow has been approved, rejected, or is still in progress
Get Signed DocumentRetrieves the sealed, signed PDF from the Dispatcher after a workflow completesDownloading the final signed document

Pull Model - No Background Sync

Workflow status is never updated automatically. It is only refreshed when a user clicks Sync Workflow Status. There is no Dispatcher callback or background sync into Dynamics 365.


Dataverse Custom Tables

The managed solution installs six custom tables. All table names are prefixed with refract_.

refract_template

Stores template definitions - one row per template.

ColumnTypeDescription
refract_templateidGUID (PK)Unique template identifier
refract_nameTextHuman-readable display name
refract_entitylogicalnameTextCRM entity, e.g. quote, opportunity
refract_templatevariantidTextTemplate ID in the REFRACT template library
refract_dispatchmodeChoiceStandard, PerRecord, SingleArray, GroupedArray
refract_workflowenabledBooleanActivate workflow routing for this template
refract_workflowenforcementmodeChoiceBlock, Validate, or None
refract_watermarkenabledBooleanPrint a watermark on generated PDFs
refract_watermarktextTextWatermark text, e.g. CONFIDENTIAL
refract_isactiveBooleanOnly active templates appear in the gallery

refract_templatefield

Stores field mappings for each template - one row per mapped field.

ColumnTypeDescription
refract_templatefieldidGUID (PK)Unique identifier
refract_nameTextDescriptive label for this mapping
refract_templateidLookup → refract_templateParent template
refract_crmfieldnameTextCRM field logical name, e.g. name, totalamount
refract_placeholderkeyTextKey in the REFRACT payload JSON, e.g. documentTitle
refract_sectiontypeChoice (OptionSet)Header (100000000), Line (100000001), or Footer (100000002)
refract_jsongroupTextOptional nesting key, e.g. billing, shipping
refract_sequencenumberIntegerField ordering within a section

refract_templateassignment

Controls which templates appear on which entities and contexts.

ColumnTypeDescription
refract_templateidLookup → refract_templateThe template to expose
refract_entitylogicalnameTextTarget entity
refract_contextChoiceform, view, or both

refract_templatesigner

Stores e-signature signer configuration per template. Fields reference CRM field logical names resolved at generation time - they are not hardcoded email addresses.

ColumnTypeDescription
refract_templateidLookup → refract_templateParent template
refract_emailfieldnameTextCRM field providing the signer's email, e.g. emailaddress1
refract_namefieldnameTextCRM field providing the signer's display name, e.g. fullname
refract_signerroleTexte.g. Signer, Witness, Approver
refract_sequencenumberIntegerSigning order for sequential mode

refract_templaterecipient

Stores delivery and workflow recipient configuration per template. Recipients can be either dynamic (resolved from the CRM record) or static (hardcoded email address).

ColumnTypeDescription
refract_templateidLookup → refract_templateParent template
refract_recipienttypeChoiceDelivery or Workflow
refract_emailfieldnameTextCRM field logical name to resolve the recipient email from the record (mutually exclusive with refract_staticemailaddress)
refract_staticemailaddressTextHardcoded recipient email - used when refract_emailfieldname is not set
refract_sequencenumberIntegerRecipient ordering

The runtime document link table. One row per generated document per record per link type.

ColumnTypeDescription
refract_documentlinkidGUID (PK)Unique document link
refract_entitynameTextSource CRM entity
refract_recordidTextSource CRM record GUID
refract_linktypeChoicestandard or workflow - see write rules below
refract_correlationidTextJob reference from Dispatcher
refract_blobpathTextSharePoint path to the generated PDF
refract_workflowidTextWorkflow instance ID (only for workflow linktype)
refract_workflowstateTextcomplete or rejected - written only on terminal states
refract_enforcementmodeChoiceBlock, Validate, or None (only for workflow linktype)
refract_generatedonDateTimeTimestamp of generation
refract_generatedbyuserTextCRM user who triggered the action

Write rules:

ActionWritten columns
Process DocumentblobPath, correlationId, linkType; also workflowId + enforcementMode when template is a workflow type
Sync Workflow StatusworkflowState - only when state = complete or rejected
Open DocumentRead only
Get Signed DocumentRead only
DispatcherNever writes to this table

Link type policy: A record can hold one standard link and one workflow link simultaneously. A new standard document never deletes a workflow link, and vice versa. The latest document of each type overwrites the previous.


Custom APIs

The solution includes eight Custom Dataverse APIs. The four command actions are invoked by Command Bar buttons. The four admin APIs are intended for one-time or periodic administrative use.

Command Actions

Custom APIDispatcher EndpointTriggered by
refract_ProcessDocumentPOST /render[Process Document] button
refract_OpenDocumentGET /doc[Open Document] button
refract_SyncWorkflowStatusGET /workflow/{id}/status[Sync Workflow Status] button
refract_GetSealedDocumentGET /workflow/{id}/export[Get Signed Document] button

Admin APIs

Custom APIPurpose
refract_BrowseSharePointFolderLists folders and .vpdf files in the templates SharePoint drive via Microsoft Graph. Used by the template browser dialog.
refract_ImportTemplateDownloads a .vpdf from SharePoint and creates or updates the matching refract_template record.
refract_InitializeDefaultsCreates default templates and field mappings for all eight supported entities. Run once after installation.
refract_ValidateSettingsChecks which of the twelve environment variables are configured and returns a missing-settings report.

refract_InitializeDefaults parameters:

ParameterTypeDescription
overwriteExisting (input, optional)BooleanIf true, regenerates field mappings even if templates already exist
templatesCreated (output)IntegerNumber of new templates created
fieldMappingsCreated (output)IntegerNumber of new field mapping records created
assignmentsCreated (output)IntegerNumber of new template assignments created
summary (output)StringHuman-readable summary

refract_ValidateSettings outputs:

ParameterTypeDescription
isConfiguredBooleanTrue if all required variables are set
missingSettingsStringJSON array of missing variable names
configuredCountIntegerNumber of variables that have values
totalCountIntegerTotal number of expected variables
summaryStringHuman-readable summary

Workflow Enforcement

A pre-operation plugin checks whether a pending workflow should block or warn before certain CRM operations complete. It reads the last-known workflow state saved on the record - it never calls the Dispatcher directly. If a user has not recently synced the workflow status, enforcement acts on the most recently saved state.

Enforcement modes (configured per template):

ModeBehaviour
BlockCRM operation is prevented until the workflow reaches a terminal state (Complete or Rejected)
ValidateA warning is shown to the user but the operation is allowed to proceed
NoneNo enforcement - workflow state is tracked but does not affect CRM operations

Suggested Enforcement Triggers by Entity

EntitySuggested blocked operations
QuoteActivate Quote, Close as Won/Lost, Convert to Order
OpportunityClose as Won, Close as Lost
AccountDeactivate Account
OrderFulfil Order, Cancel Order
InvoiceMark as Paid, Cancel Invoice

Enforcement mode (Block / Validate / None) is configured per template and stored in refract_documentlink.enforcementMode at the time of document generation.


Batch Dispatch Modes

The Dispatcher detects the operation mode from the payload schema. The plugin code is identical for single and batch operations - both call POST /render and receive a PDF buffer in response.

ModePayload shapeDispatcher response
Single{ mode: "single", sourceRecordId: "...", metadata: {...} }Rendered PDF buffer
Batch{ mode: "batch", references: [{...}, {...}, ...] }Intake Receipt PDF buffer

For batch operations, the Dispatcher accepts the job, immediately generates and returns an Intake Receipt PDF as acknowledgment, then processes the batch asynchronously and writes results to SharePoint. The CRM user receives the Intake Receipt immediately - the same hand-in-hand experience as a single record operation.

Initial release scope:

ModeDescriptionInitial release
StandardOne PDF for the current recordYes
Per RecordOne PDF per selected record from a list viewYes
Single ArrayAll selected records combined into one PDFYes
Grouped ArrayRecords grouped into arrays within one PDFFuture

Batch Safety Thresholds

Record countBehaviour
≤ 25Processes immediately
26 - 100Confirmation dialog before processing
> 100Blocked with error message

Thresholds are stored as Dataverse Environment Variables and can be adjusted per customer without a solution update.


Managed Solution Contents

RefractPlatform_D365Sales (Managed Solution)
├── Dataverse Tables (6)
│   ├── refract_template
│   ├── refract_templatefield
│   ├── refract_templateassignment
│   ├── refract_templatesigner
│   ├── refract_templaterecipient
│   └── refract_documentlink
├── Custom Dataverse APIs (8)
│   ├── refract_ProcessDocument         - Command Bar action
│   ├── refract_OpenDocument            - Command Bar action
│   ├── refract_SyncWorkflowStatus      - Command Bar action
│   ├── refract_GetSealedDocument       - Command Bar action
│   ├── refract_BrowseSharePointFolder  - Admin: browse template library
│   ├── refract_ImportTemplate          - Admin: import .vpdf from SharePoint
│   ├── refract_InitializeDefaults      - Admin: seed default templates (8 entities)
│   └── refract_ValidateSettings        - Admin: check environment variable config
├── Web Resources
│   ├── refract_/scripts/refract_actions.js    - Command Bar button handlers
│   ├── refract_/scripts/refract_gallery.js    - Template gallery + progress dialog
│   ├── refract_/scripts/refract_browser.js    - Template browser + import dialog
│   ├── refract_/stylesheets/refract_gallery.css
│   ├── refract_/stylesheets/refract_browser.css
│   ├── refract_/pages/refract_gallery.html
│   └── refract_/pages/refract_browser.html
├── Command Bar Customisations (8 entities)
│   ├── quote, opportunity, account, contact
│   ├── salesorder, invoice, incident, lead
│   └── Buttons: [Process Document] [Open Document] [Sync Workflow Status] [Get Signed Document]
├── Security Roles
│   └── REFRACT User
└── Environment Variables (12)
    ├── refract_graph_tenantid
    ├── refract_graph_clientid
    ├── refract_graph_clientsecret           (secret)
    ├── refract_graph_driveid                (output document library)
    ├── refract_graph_templatesdriveid       (templates library)
    ├── refract_graph_templatessourcefolder  (folder path within templates library)
    ├── refract_dispatcher_baseurl
    ├── refract_dispatcher_tenantid
    ├── refract_dispatcher_clientid
    ├── refract_dispatcher_clientsecret      (secret)
    ├── refract_batchwarningthreshold        (default: 25)
    └── refract_batchmaxthreshold            (default: 100)

Deployment and ALM

StageMethod
DevelopmentUnmanaged solution; plugins registered via Plugin Registration Tool
QA / UATExport managed solution → import to target environment; set environment variables per environment
ProductionImport managed solution
UpgradeImport new managed solution version - tables and data are preserved
UninstallDelete managed solution - full clean removal, no residual customisations left in the environment
Credential rotationUpdate the relevant Environment Variable secret values only - no code change or solution reimport required

Entity Extension Pattern

Command Bar customisations are the only entity-specific artefacts in the solution. Adding support for a new CRM entity requires only a new Command Bar entry in a solution update - no plugin code changes. The plugin reads the entity name from the Custom API request parameters, so it works generically for any entity with an assigned template.

On this page