Simple Smooth Safe / DocGen / docs / plugin-registration ← All docs

SSS DocGen — Plugin Registration (sss_GenerateDocument)

How to register SssDocGen.Plugins.dll and bind it to the sss_GenerateDocument custom API. The plugin is a thin HTTP proxy: it acquires an Entra client-credentials token and forwards the request to the DocGen API's POST /render endpoint. All business logic lives in the API.

Prerequisites

  • Built, strong-name-signed assembly: src/SssDocGen.Plugins/bin/Release/net462/SssDocGen.Plugins.dll (dotnet build -c Release src/SssDocGen.Plugins)
  • Power Platform CLI (pac) authenticated against the target environment (pac auth create --environment <env-url>), or the classic Plugin Registration Tool (PRT)
  • The sss_GenerateDocument custom API already exists in the environment (created by the solution)
  • The five environment variables below exist and have values

1. Register the plugin assembly (sandbox isolation)

With pac:

pac plugin push --pluginFile src/SssDocGen.Plugins/bin/Release/net462/SssDocGen.Plugins.dll

Or with PRT: Register > Register New Assembly, select SssDocGen.Plugins.dll, isolation mode Sandbox, location Database. Both are mandatory for online environments.

Either route creates a plugintype record for SssDocGen.Plugins.GenerateDocumentPlugin.

2. Bind the plugin type to the custom API

Custom APIs are not bound with a plugin step. Instead, set the Plugin Type (plugintypeid) on the customapi record for sss_GenerateDocument:

  • make.powerapps.com: open the solution > Custom API sss_GenerateDocument > edit > set Plugin Type to SssDocGen.Plugins.GenerateDocumentPlugin > save.
  • PRT: locate the custom API under the message browser is not supported; use the maker portal or Web API instead:
# Web API alternative (ids retrieved via GET on customapis / plugintypes)
PATCH {env-url}/api/data/v9.2/customapis(<customapi-id>)
{ "PluginTypeId@odata.bind": "/plugintypes(<plugintype-id>)" }

No SdkMessageProcessingStep registration is needed; Dataverse invokes the bound plugin type on the main operation of the custom API message.

3. Required environment variables

All five are read at runtime by the plugin via the RetrieveEnvironmentVariableValue request (current value overrides default).

The secret must be a Text variable, not a Key Vault-backed one. RetrieveEnvironmentVariableValue does not resolve Key Vault-backed secret variables — that needs RetrieveEnvironmentVariableSecretValue, which the plugin does not call — so a secret-typed variable comes back empty. The env-var type is immutable once created; if yours is Secret, delete and recreate it as Text under the same schema name. See the deployment runbook; the security & compliance review (available on request) covers the risk this carries and how to mitigate it.

Schema name Type Purpose Example
sss_DocGenApiBaseUrl Text Base URL of the DocGen API (ACA app), no trailing slash needed https://sss-docgen.<region>.azurecontainerapps.io
sss_DocGenClientSecret Text (see note above) Client secret of the app registration used for the client-credentials flow the secret value itself, retrieved from Key Vault
sss_DocGenTenantId Text Entra tenant (directory) ID 2f4a...-....
sss_DocGenClientId Text Application (client) ID of the app registration 9b1c...-....
sss_DocGenScope Text OAuth scope requested for the token api://<app-id>/.default

Solution note (orchestrator): sss_DocGenTenantId, sss_DocGenClientId and sss_DocGenScope must be added to the solution alongside the existing sss_DocGenApiBaseUrl and sss_DocGenClientSecret definitions. This document does not modify solution/; the orchestrator owns that change.

App registration note: the same Entra app registration serves three roles — (1) the token audience configured on the DocGen API (ACA) for JWT validation, (2) the identity in the client-credentials flow (sss_DocGenClientId + secret), and (3) the Dataverse application user the API uses to call Dataverse. Keep them in sync.

4. Custom API contract (for reference)

Message: sss_GenerateDocument (bound plugin: SssDocGen.Plugins.GenerateDocumentPlugin)

Direction Parameter Type Notes
In Code String Template/document code; required, non-empty
In EntityId String Target record id (GUID as string); required, non-empty
In Attach Boolean Optional, default false. Forwarded as attach; when true the API stores the PDF as an annotation on the record
In Output String Optional, default pdf. Forwarded as outputpdf, html or data. Omitted from the request body when null, so existing callers are unaffected
Out FileBase64 String Generated file content, base64. Null unless Output is pdf
Out FileName String Suggested file name
Out AnnotationId String Annotation id if the API stored the file; null unless Attach was true
Out Content String Rendered HTML (Output=html) or the enriched data tree as JSON (Output=data); null when Output is pdf
Out Warnings String Non-fatal problems, newline-joined; null when there were none. The API returns these as a JSON array, which the plugin joins because a custom API response property is a String

The full semantics of Attach, Output and Warnings — including which combinations return 400 — are in Usage & Customization §2. This table is the plugin's view of the same contract.

Errors from the API (problem+json title/detail), timeouts and network failures surface as InvalidPluginExecutionException messages to the caller. Warnings is the exception: it is non-fatal by definition — the document was still produced — so it never throws. Plugin trace logs record URL, HTTP status and duration — never the secret or the access token.

5. Smoke test

POST {env-url}/api/data/v9.2/sss_GenerateDocument
{ "Code": "<template-code>", "EntityId": "<record-guid>" }

Expect FileBase64, FileName and (with the defaults above) a null AnnotationId, Content and Warnings in the response. A populated Warnings means the document was produced but may be wrong — do not treat the 200 as proof of correctness. To exercise note creation, repeat with "Attach": true and expect an AnnotationId; that is the only path needing the prvAppendNote / prvAppendTo<target> privilege pair. On failure, check the Plugin Trace Log (enable via Settings > Administration > System Settings > Customization).