Developer Guide - Vault 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 Vault Platform CRM integration.
Integration Architecture
The Vault 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 Vault Dispatcher.
| Layer | Dynamics 365 Sales |
|---|---|
| UI trigger | Command Bar button |
| Template gallery | JavaScript dialog |
| Server-side processing | Dataverse Plugin (runs within Dataverse sandbox) |
| Template and field mapping storage | Dataverse custom tables |
| SharePoint upload | Microsoft Graph API |
| PDF generation | Vault Dispatcher |
| Document link storage | Dataverse vault_documentlink table |
| Workflow enforcement | Dataverse pre-operation plugin |
| Credential storage | Dataverse 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.
| Action | Endpoint | Method | Request | Response |
|---|---|---|---|---|
| Generate Document | /render | POST | { templatePath, payloadPath, options, templateSettings } | PDF buffer + headers: x-vaultpdf-blobpath, x-vaultpdf-correlationid, x-vaultpdf-workflowid |
| Open Document | /doc | GET | ?blobPath=... | { url: "<SAS_URL>" } |
| Workflow Status | /workflow/{id}/status | GET | {id} in path | { state, title, currentStepIndex, totalSteps } |
| Workflow Sealed PDF | /workflow/{id}/export | GET | {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.
| Action | What it does | When to use |
|---|---|---|
| Process Document | Reads the CRM record, uploads a data payload to SharePoint, calls the Dispatcher to render the PDF, and stores a document link on the record | First time generating a document for a record, or to regenerate |
| Open Document | Retrieves a short-lived download URL for the most recently generated PDF and opens it in a new tab | Viewing a document that has already been generated |
| Sync Workflow Status | Queries the Dispatcher for the current workflow state and displays it in a dialog; saves the outcome against the record on completion or rejection | Checking whether a workflow has been approved, rejected, or is still in progress |
| Get Signed Document | Retrieves the sealed, signed PDF from the Dispatcher after a workflow completes | Downloading 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 vault_.
vault_template
Stores template definitions - one row per template.
| Column | Type | Description |
|---|---|---|
vault_templateid | GUID (PK) | Unique template identifier |
vault_name | Text | Human-readable display name |
vault_entitylogicalname | Text | CRM entity, e.g. quote, opportunity |
vault_templatevariantid | Text | Template ID in the Vault template library |
vault_dispatchmode | Choice | Standard, PerRecord, SingleArray, GroupedArray |
vault_workflowenabled | Boolean | Activate workflow routing for this template |
vault_workflowenforcementmode | Choice | Block, Validate, or None |
vault_watermarkenabled | Boolean | Print a watermark on generated PDFs |
vault_watermarktext | Text | Watermark text, e.g. CONFIDENTIAL |
vault_isactive | Boolean | Only active templates appear in the gallery |
vault_templatefield
Stores field mappings for each template - one row per mapped field.
| Column | Type | Description |
|---|---|---|
vault_templatefieldid | GUID (PK) | Unique identifier |
vault_name | Text | Descriptive label for this mapping |
vault_templateid | Lookup → vault_template | Parent template |
vault_crmfieldname | Text | CRM field logical name, e.g. name, totalamount |
vault_placeholderkey | Text | Key in the Vault payload JSON, e.g. documentTitle |
vault_sectiontype | Choice (OptionSet) | Header (100000000), Line (100000001), or Footer (100000002) |
vault_jsongroup | Text | Optional nesting key, e.g. billing, shipping |
vault_sequencenumber | Integer | Field ordering within a section |
vault_templateassignment
Controls which templates appear on which entities and contexts.
| Column | Type | Description |
|---|---|---|
vault_templateid | Lookup → vault_template | The template to expose |
vault_entitylogicalname | Text | Target entity |
vault_context | Choice | form, view, or both |
vault_templatesigner
Stores e-signature signer configuration per template. Fields reference CRM field logical names resolved at generation time — they are not hardcoded email addresses.
| Column | Type | Description |
|---|---|---|
vault_templateid | Lookup → vault_template | Parent template |
vault_emailfieldname | Text | CRM field providing the signer's email, e.g. emailaddress1 |
vault_namefieldname | Text | CRM field providing the signer's display name, e.g. fullname |
vault_signerrole | Text | e.g. Signer, Witness, Approver |
vault_sequencenumber | Integer | Signing order for sequential mode |
vault_templaterecipient
Stores delivery and workflow recipient configuration per template. Recipients can be either dynamic (resolved from the CRM record) or static (hardcoded email address).
| Column | Type | Description |
|---|---|---|
vault_templateid | Lookup → vault_template | Parent template |
vault_recipienttype | Choice | Delivery or Workflow |
vault_emailfieldname | Text | CRM field logical name to resolve the recipient email from the record (mutually exclusive with vault_staticemailaddress) |
vault_staticemailaddress | Text | Hardcoded recipient email — used when vault_emailfieldname is not set |
vault_sequencenumber | Integer | Recipient ordering |
vault_documentlink
The runtime document link table. One row per generated document per record per link type.
| Column | Type | Description |
|---|---|---|
vault_documentlinkid | GUID (PK) | Unique document link |
vault_entityname | Text | Source CRM entity |
vault_recordid | Text | Source CRM record GUID |
vault_linktype | Choice | standard or workflow - see write rules below |
vault_correlationid | Text | Job reference from Dispatcher |
vault_blobpath | Text | SharePoint path to the generated PDF |
vault_workflowid | Text | Workflow instance ID (only for workflow linktype) |
vault_workflowstate | Text | complete or rejected - written only on terminal states |
vault_enforcementmode | Choice | Block, Validate, or None (only for workflow linktype) |
vault_generatedon | DateTime | Timestamp of generation |
vault_generatedbyuser | Text | CRM user who triggered the action |
Write rules:
| Action | Written columns |
|---|---|
| Process Document | blobPath, correlationId, linkType; also workflowId + enforcementMode when template is a workflow type |
| Sync Workflow Status | workflowState - only when state = complete or rejected |
| Open Document | Read only |
| Get Signed Document | Read only |
| Dispatcher | Never 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 API | Dispatcher Endpoint | Triggered by |
|---|---|---|
vault_ProcessDocument | POST /render | [Process Document] button |
vault_OpenDocument | GET /doc | [Open Document] button |
vault_SyncWorkflowStatus | GET /workflow/{id}/status | [Sync Workflow Status] button |
vault_GetSealedDocument | GET /workflow/{id}/export | [Get Signed Document] button |
Admin APIs
| Custom API | Purpose |
|---|---|
vault_BrowseSharePointFolder | Lists folders and .vpdf files in the templates SharePoint drive via Microsoft Graph. Used by the template browser dialog. |
vault_ImportTemplate | Downloads a .vpdf from SharePoint and creates or updates the matching vault_template record. |
vault_InitializeDefaults | Creates default templates and field mappings for all eight supported entities. Run once after installation. |
vault_ValidateSettings | Checks which of the twelve environment variables are configured and returns a missing-settings report. |
vault_InitializeDefaults parameters:
| Parameter | Type | Description |
|---|---|---|
overwriteExisting (input, optional) | Boolean | If true, regenerates field mappings even if templates already exist |
templatesCreated (output) | Integer | Number of new templates created |
fieldMappingsCreated (output) | Integer | Number of new field mapping records created |
assignmentsCreated (output) | Integer | Number of new template assignments created |
summary (output) | String | Human-readable summary |
vault_ValidateSettings outputs:
| Parameter | Type | Description |
|---|---|---|
isConfigured | Boolean | True if all required variables are set |
missingSettings | String | JSON array of missing variable names |
configuredCount | Integer | Number of variables that have values |
totalCount | Integer | Total number of expected variables |
summary | String | Human-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):
| Mode | Behaviour |
|---|---|
Block | CRM operation is prevented until the workflow reaches a terminal state (Complete or Rejected) |
Validate | A warning is shown to the user but the operation is allowed to proceed |
None | No enforcement - workflow state is tracked but does not affect CRM operations |
Suggested Enforcement Triggers by Entity
| Entity | Suggested blocked operations |
|---|---|
| Quote | Activate Quote, Close as Won/Lost, Convert to Order |
| Opportunity | Close as Won, Close as Lost |
| Account | Deactivate Account |
| Order | Fulfil Order, Cancel Order |
| Invoice | Mark as Paid, Cancel Invoice |
Enforcement mode (Block / Validate / None) is configured per template and stored in vault_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.
| Mode | Payload shape | Dispatcher 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:
| Mode | Description | Initial release |
|---|---|---|
Standard | One PDF for the current record | Yes |
Per Record | One PDF per selected record from a list view | Yes |
Single Array | All selected records combined into one PDF | Yes |
Grouped Array | Records grouped into arrays within one PDF | Future |
Batch Safety Thresholds
| Record count | Behaviour |
|---|---|
| ≤ 25 | Processes immediately |
| 26–100 | Confirmation dialog before processing |
| > 100 | Blocked with error message |
Thresholds are stored as Dataverse Environment Variables and can be adjusted per customer without a solution update.
Managed Solution Contents
VaultPlatform_D365Sales (Managed Solution)
├── Dataverse Tables (6)
│ ├── vault_template
│ ├── vault_templatefield
│ ├── vault_templateassignment
│ ├── vault_templatesigner
│ ├── vault_templaterecipient
│ └── vault_documentlink
├── Custom Dataverse APIs (8)
│ ├── vault_ProcessDocument - Command Bar action
│ ├── vault_OpenDocument - Command Bar action
│ ├── vault_SyncWorkflowStatus - Command Bar action
│ ├── vault_GetSealedDocument - Command Bar action
│ ├── vault_BrowseSharePointFolder - Admin: browse template library
│ ├── vault_ImportTemplate - Admin: import .vpdf from SharePoint
│ ├── vault_InitializeDefaults - Admin: seed default templates (8 entities)
│ └── vault_ValidateSettings - Admin: check environment variable config
├── Web Resources
│ ├── vault_/scripts/vault_actions.js - Command Bar button handlers
│ ├── vault_/scripts/vault_gallery.js - Template gallery + progress dialog
│ ├── vault_/scripts/vault_browser.js - Template browser + import dialog
│ ├── vault_/stylesheets/vault_gallery.css
│ ├── vault_/stylesheets/vault_browser.css
│ ├── vault_/pages/vault_gallery.html
│ └── vault_/pages/vault_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
│ └── Vault User
└── Environment Variables (12)
├── vault_graph_tenantid
├── vault_graph_clientid
├── vault_graph_clientsecret (secret)
├── vault_graph_driveid (output document library)
├── vault_graph_templatesdriveid (templates library)
├── vault_graph_templatessourcefolder (folder path within templates library)
├── vault_dispatcher_baseurl
├── vault_dispatcher_tenantid
├── vault_dispatcher_clientid
├── vault_dispatcher_clientsecret (secret)
├── vault_batchwarningthreshold (default: 25)
└── vault_batchmaxthreshold (default: 100)Deployment and ALM
| Stage | Method |
|---|---|
| Development | Unmanaged solution; plugins registered via Plugin Registration Tool |
| QA / UAT | Export managed solution → import to target environment; set environment variables per environment |
| Production | Import managed solution |
| Upgrade | Import new managed solution version - tables and data are preserved |
| Uninstall | Delete managed solution - full clean removal, no residual customisations left in the environment |
| Credential rotation | Update 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.
Vault Platform for Dynamics 365 Sales
Complete integration guide for Dynamics 365 Sales consultants and developers. Install, configure, and use VaultPDF with on-demand PDF generation, SharePoint storage, e-signature workflows, and audit trails - directly from CRM records.
Troubleshooting - Vault Platform for Dynamics 365 Sales
Diagnose and resolve common Vault Platform CRM issues. Security role errors, environment variable misconfigurations, plugin failures, SharePoint connectivity, workflow enforcement, and batch operation problems.