@mohsinonxrm/dataverse-sdk-actions
v1.0.0
Published
Typed Web API actions for Dataverse (POST operations with side effects)
Maintainers
Readme
@mohsinonxrm/dataverse-sdk-actions
Typed Web API actions for Dataverse. Actions are operations with side effects that execute via HTTP POST.
Features
- ✅ 1,356 typed actions - Complete CSDL-generated coverage of all Dataverse Web API actions
- ✅ Business operations - ExecuteWorkflow, WinOpportunity, Merge, and hundreds more
- ✅ Bulk operations - CreateMultiple, UpdateMultiple, UpsertMultiple, DeleteMultiple
- ✅ File operations - Chunked upload/download with InitializeFileBlocksUpload, UploadBlock, CommitFileBlocksUpload
- ✅ Solution operations - Import, export, publish with ExportSolution, ImportSolution, PublishXml
- ✅ Service-specific operations - Sales, Customer Service, Field Service, Marketing modules
- ✅ Type-safe - Full TypeScript typing for request and response
Installation
npm install @mohsinonxrm/dataverse-sdk-actions @mohsinonxrm/dataverse-sdk-coreQuick Start
import { DataverseClient } from "@mohsinonxrm/dataverse-sdk-core";
import { CreateMultipleAction } from "@mohsinonxrm/dataverse-sdk-actions";
const client = new DataverseClient({
baseUrl: "https://your-org.crm.dynamics.com",
// ... auth configuration
});
// Create 100 accounts in one request
const accounts = Array.from({ length: 100 }, (_, i) => ({
"@odata.type": "Microsoft.Dynamics.CRM.account",
name: `Account ${i + 1}`,
creditlimit: 50000,
}));
const action = new CreateMultipleAction("accounts", accounts);
const response = await client.execute(action);
console.log("Created IDs:", response.Ids); // Array of 100 GUIDsBusiness Actions
ExecuteWorkflow
Starts a workflow for a specific entity record.
Maps to: POST /workflows({id})/Microsoft.Dynamics.CRM.ExecuteWorkflow (bound action)
Example:
import { ExecuteWorkflowAction } from "@mohsinonxrm/dataverse-sdk-actions";
const action = new ExecuteWorkflowAction(
"workflow-guid", // WorkflowId
"account-guid" // EntityId (record to execute workflow on)
);
const response = await client.execute(action);
console.log("Async Operation ID:", response.Id);WinOpportunity
Marks an opportunity as won.
Maps to: POST /WinOpportunity (unbound action)
Example:
import { WinOpportunityAction } from "@mohsinonxrm/dataverse-sdk-actions";
const opportunityClose = {
"@odata.type": "Microsoft.Dynamics.CRM.opportunityclose",
subject: "Won - Contoso Deal",
"[email protected]": "/opportunities(opportunity-guid)",
actualrevenue: 100000,
};
const action = new WinOpportunityAction(opportunityClose, 3); // Status: 3 (Won)
await client.execute(action);Action Categories
The SDK includes 1,356 typed actions organized into the following categories:
Platform Actions (Core Operations)
Entity Operations:
CreateMultipleAction- Bulk create recordsUpdateMultipleAction- Bulk update recordsUpsertMultipleAction- Bulk upsert recordsDeleteMultipleAction- Bulk delete records (elastic tables only)
Business Logic:
ExecuteWorkflowAction- Execute workflow on a recordMergeAction- Merge two records (account, contact, lead, incident)AssignAction- Assign record to user/teamSetStateAction- Set state and status
Solution Management:
ExportSolutionAction- Export solutionImportSolutionAction- Import solutionPublishXmlAction- Publish metadata changesPublishAllXmlAction- Publish all metadata changes
Sales Module Actions (CRM)
Opportunity Management:
WinOpportunityAction- Close opportunity as wonLoseOpportunityAction- Close opportunity as lostGenerateQuoteFromOpportunityAction- Create quote from opportunityGenerateSalesOrderFromOpportunityAction- Create sales order from opportunityGenerateInvoiceFromOpportunityAction- Create invoice from opportunity
Quote & Order Processing:
ConvertQuoteToSalesOrderAction- Convert quote to sales orderConvertSalesOrderToInvoiceAction- Convert sales order to invoiceCalculatePriceAction- Calculate product pricing
Customer Service Module Actions
Case Management:
CloseIncidentAction- Close caseApplyRoutingRuleAction- Apply routing rules
Knowledge Base:
FullTextSearchKnowledgeArticleAction- Search knowledge articlesCreateKnowledgeArticleVersionAction- Create article version
Field Service Module Actions (msdyn_*)
Resource Scheduling:
msdyn_BookRequirementGroupAction- Book requirement groupmsdyn_SearchResourceAvailabilityAction- Find available resourcesmsdyn_CancelBookingsAction- Cancel bookings
Work Order Management:
msdyn_CreateWorkOrderAction- Create work ordermsdyn_CascadeCrewBookingsAction- Cascade crew bookings
Marketing Module Actions (msdynmkt_*)
Campaign & Consent:
msdynmkt_checkconsentAction- Check consent statusmsdynmkt_consentsubmissionAction- Submit consent
File & Image Operations
Chunked Upload:
InitializeFileBlocksUploadAction- Start upload sessionUploadBlockAction- Upload chunkCommitFileBlocksUploadAction- Finalize upload
Chunked Download:
InitializeFileBlocksDownloadAction- Start download sessionDownloadBlockAction- Download chunk
Complete Action Reference
For a complete list of all 1,356 actions with descriptions and examples, see docs/mapping/supported-actions.md.
Merge
Merges two entity records (subordinate into target).
Maps to: POST /Merge (unbound action)
Supported entities: account, contact, lead, incident
Example:
import { MergeAction } from "@mohsinonxrm/dataverse-sdk-actions";
const action = new MergeAction(
{ logicalName: "account", id: "target-account-guid" }, // Keep this record
"subordinate-account-guid", // Merge & deactivate this
{ name: "Merged Company Name" }, // Update target with these values
false // PerformParentingChecks
);
await client.execute(action);Bulk Operations
Bulk operations provide a simpler alternative to $batch for common scenarios.
When to Use Bulk Operations vs $batch
Use Bulk Operations when:
- Single entity type, single operation type (all creates OR all updates)
- 100-1000 records (standard tables) or up to 100 (elastic tables)
- Want simple API with minimal setup
- Need immediate response
Use $batch when:
- Mixed operation types (creates + updates + deletes)
- Cross-entity operations with references (create account → create contacts)
- Need transactional semantics across different entities
- Want to bundle unrelated operations to reduce HTTP requests
- Need Content-ID references
See @mohsinonxrm/dataverse-sdk-batch for $batch support.
CreateMultiple
Creates multiple records in a single request.
Maps to: POST /CreateMultiple (unbound action)
Example:
import { CreateMultipleAction } from "@mohsinonxrm/dataverse-sdk-actions";
const accounts = [
{ "@odata.type": "Microsoft.Dynamics.CRM.account", name: "Contoso", creditlimit: 50000 },
{ "@odata.type": "Microsoft.Dynamics.CRM.account", name: "Fabrikam", creditlimit: 75000 },
];
const action = new CreateMultipleAction("accounts", accounts);
const response = await client.execute(action);
console.log("Created IDs:", response.Ids); // ['id1', 'id2']With Optimistic Concurrency:
const action = new CreateMultipleAction("accounts", accounts, "IfRowVersionMatches");UpdateMultiple
Updates multiple records in a single request.
Maps to: POST /UpdateMultiple (unbound action)
Example:
import { UpdateMultipleAction } from "@mohsinonxrm/dataverse-sdk-actions";
const updates = [
{ "@odata.type": "Microsoft.Dynamics.CRM.account", accountid: "id1", creditlimit: 100000 },
{ "@odata.type": "Microsoft.Dynamics.CRM.account", accountid: "id2", creditlimit: 125000 },
];
const action = new UpdateMultipleAction("accounts", updates);
await client.execute(action); // 204 No ContentUpsertMultiple
Creates or updates multiple records in a single request.
Maps to: POST /UpsertMultiple (unbound action)
Example:
import { UpsertMultipleAction } from "@mohsinonxrm/dataverse-sdk-actions";
const records = [
{ "@odata.type": "Microsoft.Dynamics.CRM.account", accountid: "id1", name: "Updated" },
{ "@odata.type": "Microsoft.Dynamics.CRM.account", name: "New Account" }, // Will be created
];
const action = new UpsertMultipleAction("accounts", records);
await client.execute(action); // 204 No ContentDeleteMultiple
Deletes multiple records in a single request.
⚠️ IMPORTANT: Only supported for elastic tables, not standard tables.
Maps to: POST /DeleteMultiple (unbound action)
Example:
import { DeleteMultipleAction } from "@mohsinonxrm/dataverse-sdk-actions";
const targets = [
{ "@odata.type": "Microsoft.Dynamics.CRM.account", accountid: "id1" },
{ "@odata.type": "Microsoft.Dynamics.CRM.account", accountid: "id2" },
];
const action = new DeleteMultipleAction("accounts", targets);
await client.execute(action); // Only works for elastic tables@odata.type Requirement
All bulk operations require @odata.type annotation on all targets:
// ✅ Correct
const account = {
"@odata.type": "Microsoft.Dynamics.CRM.account",
name: "Contoso",
};
// ❌ Will throw error
const account = {
name: "Contoso", // Missing @odata.type
};File Operations
Actions for chunked file/image column upload. See @mohsinonxrm/dataverse-sdk-files for higher-level FileColumnClient.
InitializeFileBlocksUpload
Initializes a chunked file upload session.
Maps to: POST /{entitySetName}({id})/Microsoft.Dynamics.CRM.InitializeFileBlocksUpload (bound action)
Example:
import { InitializeFileBlocksUploadAction } from "@mohsinonxrm/dataverse-sdk-actions";
const action = new InitializeFileBlocksUploadAction(
"accounts", // entitySetName
"account-guid", // recordId
"new_logo", // fileAttributeName
"logo.png" // fileName
);
const response = await client.execute(action);
const uploadToken = response.FileContinuationToken;UploadBlock
Uploads a single block (chunk) of a file.
Maps to: POST /UploadBlock (unbound action)
Example:
import { UploadBlockAction } from "@mohsinonxrm/dataverse-sdk-actions";
const blockId = btoa("0"); // Base64-encoded block ID
const blockData = btoa(chunkData); // Base64-encoded chunk data
const action = new UploadBlockAction(uploadToken, blockId, blockData);
await client.execute(action);CommitFileBlocksUpload
Finalizes a chunked file upload.
Maps to: POST /{entitySetName}({id})/Microsoft.Dynamics.CRM.CommitFileBlocksUpload (bound action)
Example:
import { CommitFileBlocksUploadAction } from "@mohsinonxrm/dataverse-sdk-actions";
const action = new CommitFileBlocksUploadAction(
"accounts",
"account-guid",
"new_logo",
["AAAAAA==", "AQAAAA==", "AgAAAA=="], // blockList in order
uploadToken,
"logo.png",
"image/png" // Optional MIME type
);
await client.execute(action);Architecture
Actions vs Functions
Actions (this package):
- HTTP method:
POST - Side effects: Yes - actions can modify data
- Parameters: Passed in request body
- Use for: Business operations, bulk operations, file uploads
- HTTP method:
Functions (
@mohsinonxrm/dataverse-sdk-functions):- HTTP method:
GET - Side effects: No - functions should be read-only
- Parameters: Passed as query string with parameter aliases
- Use for: Queries, calculations, retrieving information
- HTTP method:
Action Execution Flow
// 1. Create action instance
const action = new CreateMultipleAction("accounts", accounts);
// 2. Action converts to RequestInformation
const requestInfo = action.toRequestInformation(baseUrl);
// => { method: 'POST', url: '.../ CreateMultiple', body: {...} }
// 3. Client executes the request
const response = await client.execute(action);
// 4. Action parses the response
// Returns typed response (e.g., CreateMultipleResponse)Extending with Custom Actions
import type { Executable, RequestInformation } from "@mohsinonxrm/dataverse-sdk-core";
interface CustomActionResponse {
Result: string;
}
class CustomAction implements Executable<CustomActionResponse> {
constructor(public readonly param: string) {}
toRequestInformation(baseUrl: string): RequestInformation {
return {
method: "POST",
url: `${baseUrl}/CustomAction`,
headers: { "Content-Type": "application/json" },
body: { Param: this.param },
};
}
async parseResponse(response: Response): Promise<CustomActionResponse> {
const data = await response.json();
return { Result: data.Result };
}
}License
This project is licensed under the GNU AGPL v3.0 License - see LICENSE file for details.
