Generative Pages in model-driven Power Apps let you build rich custom UI with natural language or using AI code generation tools such as VS Code with Claude Code. This article shows you how to extend any generative page with document generation via DocumentsCorePack (DCP) — covering template selection, triggering generation, polling for the result, and surfacing the document for download or preview.
Note: A ready-to-use prompt for this integration is provided at the end of this article — copy it directly into your AI code generation tool to get started.
Prerequisites
- A model-driven app with a generative page targeting a specific entity
- DocumentsCorePack installed and configured in your environment
- At least one active DCP template configured for your target entity
How it works
The flow has five steps:
- Load available DCP templates for the current entity type
- User selects a template and clicks Create Document
- DCP is triggered by creating a working item with the template assigned
- Poll the working item until document generation completes
- Retrieve the document from an annotation and present it in your page
Step 1 — Load DCP templates
Query active templates for the table you need to create the document for.
i.e. Replace account with your entity’s logical name.
GET /api/data/v9.2/ptm_mscrmaddons_dcptemplateses ?$filter=statecode eq 0 and ptm_entitytype eq 'account' &$select=ptm_mscrmaddons_dcptemplatesid,ptm_name &$orderby=ptm_name asc
Bind the results to a searchable dropdown. Enable the Create Document button only when both a record ID and a template are selected.
Step 2 — Create the working item
POST /api/data/v9.2/ptm_automergeworkingitemses
Content-Type: application/json
{
"ptm_name": "Generate Document",
"ptm_automergeaction": 956670000,
"ptm_saveas": 956670008,
"ptm_primaryrecordurl": "<<YOUR_ENVIRONMENT_URL>>/main.aspx?etn=<<YOUR_ENTITY_LOGICAL_NAME>>&id=<recordid>&pagetype=entityrecord",
"ptm_entitytoattachid": "<same URL>"
}
Capture the new record ID from the OData-EntityId response header.
Step 3 — Assign the template (SOAP only)
Note: ptm_templatetoexecute is not a declared OData navigation property. Using @odata.bind or $ref will fail with an “undeclared property” error. You must use the SOAP 2011 endpoint immediately after the POST in Step 2.
POST /XRMServices/2011/Organization.svc/web
Content-Type: text/xml; charset=utf-8
SOAPAction: http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<request i:type="a:UpdateRequest"
xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
<a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<a:KeyValuePairOfstringanyType>
<b:key>Target</b:key>
<b:value i:type="a:Entity">
<a:Attributes>
<a:KeyValuePairOfstringanyType>
<b:key>ptm_automergeworkingitemsid</b:key>
<b:value i:type="c:guid"
xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/">
{WORKING_ITEM_ID}
</b:value>
</a:KeyValuePairOfstringanyType>
<a:KeyValuePairOfstringanyType>
<b:key>ptm_templatetoexecute</b:key>
<b:value i:type="a:EntityReference">
<a:Id>{TEMPLATE_ID}</a:Id>
<a:LogicalName>ptm_mscrmaddons_dcptemplates</a:LogicalName>
<a:Name i:nil="true"/>
</b:value>
</a:KeyValuePairOfstringanyType>
</a:Attributes>
<a:EntityState i:nil="true"/>
<a:FormattedValues/>
<a:Id>00000000-0000-0000-0000-000000000000</a:Id>
<a:LogicalName>ptm_automergeworkingitems</a:LogicalName>
<a:RelatedEntities/>
</b:value>
</a:KeyValuePairOfstringanyType>
</a:Parameters>
<a:RequestId i:nil="true"/>
<a:RequestName>Update</a:RequestName>
</request>
</Execute>
</s:Body>
</s:Envelope>
Step 4 — Poll for completion
Every e.g. 500ms, check the working item’s state:
GET /api/data/v9.2/ptm_automergeworkingitemses({workingItemId})
?$select=statecode,_ptm_createddocumentid_value
&Prefer: odata.include-annotations="Microsoft.Dynamics.CRM.lookuplogicalname"
Stop polling when statecode === 1 (Inactive)- this confirms the document is generated. Capture _ptm_createddocumentid_value — this is the ID of the User/Temp Settings record that holds a reference to the output.
Show elapsed time in seconds while polling so the user knows something is happening.
Step 5 — Retrieve the document
Note: The document is NOT directly on the working item. It is stored as an annotation (note) attached to the User/Temp Settings record identified by _ptm_createddocumentid_value from Step 4.
GET /api/data/v9.2/annotations
?$filter=_objectid_value eq {createdDocumentId}
&$select=annotationid,documentbody,mimetype,filename
&$orderby=createdon desc
&$top=10
Note: Do not add &$filter=isdocument eq true — it will exclude the relevant note.
Pick the first result with a non-null documentbody, then build a blob URL:
const bytes = Uint8Array.from(atob(documentbody), c => c.charCodeAt(0));
const blob = new Blob([bytes], { type: mimetype });
const url = URL.createObjectURL(blob);
Present the result as a Download link and optionally a Preview button opening the document in a dialog.
Ready-to-use prompt
Copy the block below into your generative page session in VS Code.
There are exactly two placeholders to replace, both marked with << >>:
- <<YOUR_ENTITY_LOGICAL_NAME>> — appears 3 times (template filter + both URL fields). Use the logical name of your entity, e.g. account, contact, opportunity.
- <<YOUR_ENVIRONMENT_URL>> — appears once. Your Dataverse environment base URL, e.g. https://yourorg.crm4.dynamics.com.
Everything else is fixed. The AI handles the UI — the two parts that cannot be inferred at runtime are the SOAP block in Step 3 and the indirect document path in Step 5.
Add a document generation feature to this page using the mscrm-addons
DocumentsCorePack (DCP) API.
ENTITY: <<YOUR_ENTITY_LOGICAL_NAME>> ← e.g. account, contact, opportunity
## UI
- Searchable dropdown listing available DCP templates
- "Create Document" button, enabled only when a record and a template are both selected
- While generating: show elapsed time in seconds
- When done: show a Download link and a Preview button (opens the document in a dialog)
## Templates
GET /api/data/v9.2/ptm_mscrmaddons_dcptemplateses
?$filter=statecode eq 0 and ptm_entitytype eq '<<YOUR_ENTITY_LOGICAL_NAME>>'
&$select=ptm_mscrmaddons_dcptemplatesid,ptm_name
&$orderby=ptm_name asc
## Create working item
POST /api/data/v9.2/ptm_automergeworkingitemses
{
"ptm_name": "Generate Document",
"ptm_automergeaction": 956670000,
"ptm_outputfileformat_chain": 956670001,
"ptm_saveas": 956670008,
"ptm_primaryrecordurl": "<<YOUR_ENVIRONMENT_URL>>/main.aspx?etn=<<YOUR_ENTITY_LOGICAL_NAME>>&id=<recordid>&pagetype=entityrecord",
"ptm_entitytoattachid": "<same URL>"
}
Capture the new record ID from the OData-EntityId response header.
## Assign template — SOAP ONLY
ptm_templatetoexecute is not a declared OData navigation property.
@odata.bind and $ref both fail. Use SOAP 2011:
POST /XRMServices/2011/Organization.svc/web
Content-Type: text/xml; charset=utf-8
SOAPAction: http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<request i:type="a:UpdateRequest"
xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
<a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<a:KeyValuePairOfstringanyType>
<b:key>Target</b:key>
<b:value i:type="a:Entity">
<a:Attributes>
<a:KeyValuePairOfstringanyType>
<b:key>ptm_automergeworkingitemsid</b:key>
<b:value i:type="c:guid"
xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/">
{WORKING_ITEM_ID}
</b:value>
</a:KeyValuePairOfstringanyType>
<a:KeyValuePairOfstringanyType>
<b:key>ptm_templatetoexecute</b:key>
<b:value i:type="a:EntityReference">
<a:Id>{TEMPLATE_ID}</a:Id>
<a:LogicalName>ptm_mscrmaddons_dcptemplates</a:LogicalName>
<a:Name i:nil="true"/>
</b:value>
</a:KeyValuePairOfstringanyType>
</a:Attributes>
<a:EntityState i:nil="true"/>
<a:FormattedValues/>
<a:Id>00000000-0000-0000-0000-000000000000</a:Id>
<a:LogicalName>ptm_automergeworkingitems</a:LogicalName>
<a:RelatedEntities/>
</b:value>
</a:KeyValuePairOfstringanyType>
</a:Parameters>
<a:RequestId i:nil="true"/>
<a:RequestName>Update</a:RequestName>
</request>
</Execute>
</s:Body>
</s:Envelope>
## Poll for completion
Every 500ms:
GET /api/data/v9.2/ptm_automergeworkingitemses({workingItemId})
?$select=statecode,_ptm_createddocumentid_value
&Prefer: odata.include-annotations="Microsoft.Dynamics.CRM.lookuplogicalname"
Stop when statecode === 1. Capture _ptm_createddocumentid_value.
## Retrieve the document
The document is NOT on the working item. It is an annotation attached to the
User/Temp Settings record identified by _ptm_createddocumentid_value.
GET /api/data/v9.2/annotations
?$filter=_objectid_value eq {createdDocumentId}
&$select=annotationid,documentbody,mimetype,filename
&$orderby=createdon desc&$top=10
Do NOT filter on isdocument — it excludes the relevant note.
Pick the first result with a non-null documentbody.
## Build blob URL
const bytes = Uint8Array.from(atob(documentbody), c => c.charCodeAt(0));
const blob = new Blob([bytes], { type: mimetype });
const url = URL.createObjectURL(blob);
Appendix — Option set reference values
The following tables document the relevant option set values used in the working item POST. Values not listed are either not applicable to this pattern or intended for advanced scenarios.
ptm_saveas
Controls the output file format. pdf is recommended for download/preview; use docx if the user needs an editable document.
| Label | Value | Notes |
| 956670008 | Recommended. Best for preview in an iframe and consistent rendering. | |
| docx | 956670006 | Use when the user needs an editable Word document. |
Further reading
The following Microsoft Learn articles provide the foundation for generative pages and the external tooling workflow used in this guide.
| Article | URL |
| Generate a page using natural language | learn.microsoft.com/…/generative-pages |
| Create and edit generative pages with AI code generation tools | learn.microsoft.com/…/generative-page-external-tools |
Note: The second article covers using AI code generation tools such as VS Code with Claude Code to build and iterate on generative pages — this is the workflow used to paste and run the DCP prompt described in this guide.