Package Exports
- @brunsforge/dataverse-custom-api
- @brunsforge/dataverse-custom-api/cli
Readme
Dataverse Custom API CLI
A Node.js/TypeScript CLI for managing Dataverse Custom APIs. It enables reading, exporting, editing, validating, diffing, planning, and synchronizing Custom API definitions between local JSON files and Dataverse environments.
The GitHub repository contains the TypeScript src/ source code. The compiled dist/ output is generated during build and included together with src/ in the published npm package.
Installation
npm install -g @brunsforge/dataverse-custom-apiOr from source:
git clone https://github.com/brunsforge/dataverse-custom-api.git
cd dataverse-custom-api
npm install
npm run build
npm linkFeatures
- Connect to Dataverse environments: multiple authentication methods (Device Code, Client Secret, Interactive Browser)
- Environment management: save, list, switch, and remove environments
- Custom API management:
- list available Custom APIs
- export Custom APIs as local JSON artifacts
- edit Custom API definitions locally
- validate local definitions before syncing — catches type errors, missing fields, and invalid combinations with structured diagnostics
- compare local definitions with Dataverse metadata
- create sync plans (validation runs automatically; plan is blocked if errors are found)
- execute single sync operations
- execute full sync plans
- create, update, or delete Custom API metadata in Dataverse
- validate privileges — check which Dataverse privileges the configured App User holds and which features are available or restricted
- list publishers — show all registered publishers with their customization prefixes
- Graceful privilege fallback: if the App User lacks
prvAppendToPluginType,createCustomApiautomatically retries without the PluginType binding and records awarningin the sync result instead of failing - Simulation mode: preview operations with
--simulatebefore applying changes - JSON and human-readable output: every command supports
--json - Structured diagnostics:
api validate --jsonreturns aCcdvCommandResultenvelope for VS Code extension integration
Warning: Commands that execute without
--simulatemay create, update, or delete Custom API metadata in the connected Dataverse environment.
Authentication and connection
Authentication methods
The CLI supports three authentication methods:
- Device Code Flow (recommended for interactive use)
- Client Secret (for automation / app-only access)
- Interactive Browser (for browser-based login)
Configuration
Create an auth.json file in the repository root based on one of the sample files:
auth.devicecode.example.json→ Device Code authauth.clientsecret.example.json→ Client Secret authauth.interactivebrowser.example.json→ Interactive Browser auth
Device Code auth
{
"tenantId": "your-tenant-id",
"clientId": "your-client-id",
"authMode": "deviceCode"
}App registration setup:
- Create an Azure AD / Microsoft Entra ID App Registration
- Add API permissions: Dynamics CRM > user_impersonation
- Add a redirect URI:
https://login.microsoftonline.com/common/oauth2/nativeclient - No client secret is required
Client Secret auth
{
"tenantId": "your-tenant-id",
"clientId": "your-app-only-client-id",
"clientSecret": "your-client-secret",
"authMode": "clientSecret"
}App registration setup:
- Create an Azure AD / Microsoft Entra ID App Registration
- Create a client secret
- Add API permissions: Dynamics CRM > user_impersonation (or Dataverse application permissions for app-only access)
- No redirect URI is required
Interactive Browser auth
{
"tenantId": "your-tenant-id",
"clientId": "your-client-id",
"authMode": "interactiveBrowser"
}Connect to an environment
dvc connect -u "https://your-org.crm.dynamics.com"Output:
Connected and cached: https://your-org.crm.dynamics.com
Auth mode: deviceCode
Cache file: /path/to/cache/environment.jsonEnvironment management
List environments
dvc env listOutput:
* env-001 (Production) -> https://prod.crm.dynamics.com
- env-002 (Development) -> https://dev.crm.dynamics.comShow current environment
dvc env currentOutput:
Active environment: env-001
Display name: Production
URL: https://prod.crm.dynamics.com
Auth mode: deviceCodeSwitch environment
dvc env use -i env-002Remove environment
dvc env remove -i env-001Custom API management
List Custom APIs
dvc api listOutput:
* ccsm_MyCustomApi
- ccsm_AnotherApi
- sample_CustomFunctionShow active Custom API
dvc api currentOutput:
Active API: ccsm_MyCustomApi
Cache file: /path/to/cache/active-api.jsonSet active Custom API
dvc api use -n ccsm_MyCustomApiExport Custom API
Exports the current Dataverse definition to a local JSON catalog file.
dvc api export
# or with explicit name:
dvc api export -n ccsm_MyCustomApiOutput:
Exported: /path/to/output/ccsm_MyCustomApi.jsonValidate local definition
Validates the local JSON file against Dataverse field rules. Returns structured diagnostics with error codes, field paths, and suggested fixes. No network connection is required.
dvc api validate
# or with explicit name:
dvc api validate -n ccsm_MyCustomApiOutput — no issues:
Validation for: ccsm_MyCustomApi
Status: succeeded
No issues found.Output — with diagnostics:
Validation for: ccsm_MyCustomApi
Status: validationFailed
ERROR [CCDV_PARAM_LOGICAL_ENTITY_NAME_NOT_ALLOWED] [ccsm_MyCustomApi > NewParameter].logicalEntityName
Request parameter 'NewParameter' has type 'EntityCollection' which does not support logicalEntityName. Only Entity and EntityReference are allowed.
Fix: Remove logicalEntityName or change the parameter type to Entity or EntityReference.
WARN [CCDV_PARAM_NAME_RECOMMENDED_FORMAT] [ccsm_MyCustomApi > InputParam].name
Request parameter 'InputParam' name 'InputParam' does not follow the recommended format 'ccsm_MyCustomApi.InputParam'.
Fix: Set name to 'ccsm_MyCustomApi.InputParam'.
Summary: 1 error(s), 1 warning(s), 0 info(s)JSON output returns a full CcdvCommandResult envelope:
dvc api validate --json{
"schemaVersion": "1.0.0",
"status": "validationFailed",
"command": "api validate",
"startedAtUtc": "2025-01-15T10:00:00.000Z",
"finishedAtUtc": "2025-01-15T10:00:00.001Z",
"durationMs": 1,
"diagnostics": [
{
"id": "diag-0001",
"code": "CCDV_PARAM_LOGICAL_ENTITY_NAME_NOT_ALLOWED",
"severity": "error",
"category": "validation",
"message": "Request parameter 'NewParameter' has type 'EntityCollection' which does not support logicalEntityName.",
"entityKind": "requestParameter",
"parentUniqueName": "ccsm_MyCustomApi",
"uniqueName": "NewParameter",
"field": "logicalEntityName",
"jsonPath": "$.requestParameters[?(@.uniqueName=='NewParameter')].logicalEntityName",
"blocking": true,
"suggestedFix": {
"kind": "removeField",
"field": "logicalEntityName",
"message": "Remove logicalEntityName or change the parameter type to Entity or EntityReference."
}
}
]
}Diagnostic severity levels:
| Severity | Meaning |
|---|---|
error |
Blocking — Dataverse will reject the payload or the value is structurally invalid. api plan will not proceed. |
warning |
Non-blocking — the definition is accepted but deviates from recommended practice (e.g. name format). |
info |
Informational — e.g. an open Entity type without logicalEntityName, which is valid but may be unintentional. |
Validated rules include:
uniqueNamerequired, max 128 characters, must contain publisher prefix (e.g.ccsm_), only letters/digits/underscoresnameanddisplayNamerequired, max 100 charactersdescriptionmax 300 charactersbindingTypemust beGlobal,Entity, orEntityCollectionboundEntityLogicalNamerequired whenbindingTypeisEntityorEntityCollection; must be absent forGlobalallowedCustomProcessingStepTypemust beNone,AsyncOnly, orSyncAndAsync- Functions (
isFunction: true) must have at least one response property - Functions must not use open Entity/EntityCollection request parameters (without
logicalEntityName) logicalEntityNameonly allowed on parameters/properties with typeEntityorEntityReference; automatically flagged for all other typesnameformat recommendation:{customApiUniqueName}.{childUniqueName}
Note:
api planruns the same validation internally. If blocking errors are found, the plan is not created and the error message referencesapi validatefor the full report. Always runapi validatefirst to see all diagnostics before planning.
Compare local definition with Dataverse
Compares the local JSON file against the current Dataverse metadata and shows field-level differences.
dvc api diff
# or with explicit name:
dvc api diff -n ccsm_MyCustomApiOutput:
Diff for: ccsm_MyCustomApi
Differences found: yes
Custom API: update
Request parameter summary:
none=1, create=1, update=0, delete=0, recreate=0
- create: NewParameter
Response property summary:
none=2, create=0, update=0, delete=0, recreate=0Use --json for the full machine-readable diff output.
Create sync plan
Compares local vs. Dataverse, generates an ordered operation list, and writes plan and state files. Validation runs automatically — if blocking errors exist, the plan is not created.
dvc api plan
# or with explicit name:
dvc api plan -n ccsm_MyCustomApiOutput:
Plan created: /path/to/output/ccsm_MyCustomApi.syncplan.json
State file: /path/to/output/ccsm_MyCustomApi.syncstate.json
Operations: 2
Destructive changes required: no
- [10] createRequestParameter NewParameter (new)
- [20] updateCustomApi ccsm_MyCustomApi (changed)If validation fails:
Error: Sync plan blocked by 1 validation error(s). Run 'api validate' for the full report.
First error: [CCDV_PARAM_LOGICAL_ENTITY_NAME_NOT_ALLOWED] Request parameter 'NewParameter' has type 'EntityCollection' which does not support logicalEntityName.Execute a single operation
# dry run:
dvc api exec-op -o "op-0010-createRequestParameter-NewParameter-<uuid>" --simulate
# live:
dvc api exec-op -o "op-0010-createRequestParameter-NewParameter-<uuid>"Output:
Operation: op-0010-createRequestParameter-NewParameter-<uuid>
Status: succeeded
Message: createRequestParameter for NewParameter completed successfully.
Simulated: no
State file: /path/to/output/ccsm_MyCustomApi.syncstate.jsonWarning: Without
--simulate, the operation is executed against the connected Dataverse environment.
Execute full plan
# dry run:
dvc api exec-plan --simulate
# live:
dvc api exec-planOutput:
Plan executed for: ccsm_MyCustomApi
Status: succeeded
State file: /path/to/output/ccsm_MyCustomApi.syncstate.json
- [10] createRequestParameter NewParameter: succeeded
- [20] updateCustomApi ccsm_MyCustomApi: succeededWarning: Without
--simulate, all operations in the sync plan are executed in sequence against the connected Dataverse environment.
Check metadata consistency
Compares the local definition with current Dataverse metadata and reports field-level mismatches. Requires a network connection.
dvc api check-metadataOutput:
Metadata check for: ccsm_MyCustomApi
Status: ok
No metadata mismatches found.Validate Dataverse privileges
Checks which relevant Dataverse privileges the configured App User holds and reports which features are available or missing. Requires a network connection.
dvc api validate-privileges
# show all discovered privilege IDs:
dvc api validate-privileges --verbose
# machine-readable output:
dvc api validate-privileges --jsonOutput:
Privilege validation for: orgf707a816.crm16.dynamics.com
App user: DVC Custom API CLI - AppOnly (6dcbb78d-...)
FEATURE PRIVILEGE STATUS
──────────────────────────────────────────────────────────────────────────────
Read Custom API prvReadCustomAPI ✓ Available
Create Custom API prvCreateCustomAPI ✓ Available
Update Custom API prvWriteCustomAPI ✓ Available
Delete Custom API prvDeleteCustomAPI ✓ Available
PluginType Binding prvAppendToPluginType ✗ Missing
Create Plugin Steps prvCreateSdkMessageProcessingStep ✗ Missing
HINTS:
• Custom APIs will be created without a plugin type link.
Grant 'prvAppendToPluginType' (AppendTo, plugintype, organisation scope) to the app user's security role.
• Plugin step registrations cannot be created automatically.
Grant 'prvCreateSdkMessageProcessingStep' (Create, sdkmessageprocessingstep, organisation scope) to the app user's security role.Checked privileges:
| Feature | Privilege | Known ID |
|---|---|---|
| Read Custom APIs | prvReadCustomAPI |
resolved via API |
| Create Custom APIs | prvCreateCustomAPI |
resolved via API |
| Update Custom APIs | prvWriteCustomAPI |
resolved via API |
| Delete Custom APIs | prvDeleteCustomAPI |
resolved via API |
| PluginType binding on create | prvAppendToPluginType |
574c053e-6488-4bfb-832a-cbc47aff8b32 |
| Create Plugin Steps | prvCreateSdkMessageProcessingStep |
resolved via API |
Graceful fallback for prvAppendToPluginType:
When createCustomApi is executed and the App User lacks prvAppendToPluginType, the CLI automatically retries without the PluginTypeId@odata.bind field. The Custom API is created successfully but without the Plugin binding. The sync result (exec-op, exec-plan) contains a warning field explaining what was skipped and how to remediate:
- assign
prvAppendToPluginType(AppendTo, plugintype, Org level) to the App User's security role, or - link the Plugin manually in the Maker Portal after creation.
List publishers
Lists all publishers registered in the active Dataverse environment. The publisher's customizationPrefix must be used as the prefix of a Custom API uniqueName (e.g. myprefix_MyAction).
dvc api list-publishers
# machine-readable output:
dvc api list-publishers --jsonOutput:
PREFIX FRIENDLY NAME UNIQUE NAME
───────────────────────────────────────────────────────────────────────────────
myprefix My Company Publisher mycompany_publisher
contoso Contoso Default Publisher contoso_defaultJSON output:
{
"publishers": [
{
"publisherId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"uniqueName": "mycompany_publisher",
"friendlyName": "My Company Publisher",
"customizationPrefix": "myprefix"
}
]
}Note: The Microsoft system publisher (
mscrm) is excluded from the list — its prefix cannot be used for custom solutions.
Remove local artifacts
Removes local cached and exported files. Does not delete the Custom API from Dataverse.
dvc api remove
# or with explicit name:
dvc api remove -n ccsm_MyCustomApiTypical workflows
1. Setup and connect
# Create auth configuration
cp auth.devicecode.example.json auth.json
# Edit auth.json with your tenant ID and client ID
# Connect to an environment
dvc connect -u "https://your-org.crm.dynamics.com"
# Optional: verify that the App User has the required privileges
dvc api validate-privileges2. Edit an existing Custom API
# List available APIs
dvc api list
# Set the API as active
dvc api use -n ccsm_ExistingApi
# Export the current Dataverse definition
dvc api export
# Edit the local JSON file
# e.g. add a request parameter, change a description
# Validate the local definition — fix any errors before proceeding
dvc api validate
# Review what changed vs. Dataverse
dvc api diff
# Create a sync plan (also validates internally)
dvc api plan
# Dry run first
dvc api exec-plan --simulate
# Apply after reviewing
dvc api exec-plan3. Create a new Custom API
# Optional: check App User privileges before creating
# If prvAppendToPluginType is missing, the PluginType binding will be skipped on create
dvc api validate-privileges
# List publishers to find the correct prefix for your uniqueName
dvc api list-publishers
# Create a new JSON catalog file manually (see structure below)
# e.g. .cache/customapis/myprefix_NewApi.json
# Set the API as active even before it exists in Dataverse
dvc api use -n ccsm_NewApi
# Validate the local definition before planning
dvc api validate
# Create a sync plan
dvc api plan
# Dry run first
dvc api exec-plan --simulate
# Apply after reviewing
dvc api exec-plan
# If the App User lacked prvAppendToPluginType, the createCustomApi result
# will contain a warning — check the output and link the Plugin manually if needed.Why validate before plan?
api validate is a local-only check — no network call, instant feedback. It catches issues that Dataverse would reject with a 400 error (e.g. logicalEntityName on an EntityCollection parameter, missing publisher prefix, invalid type values).
api plan runs the same validation internally and will block if blocking errors are found. Running api validate first gives you the full structured diagnostic report — including non-blocking warnings and informational hints — before attempting to build the plan.
Custom API JSON structure
The catalog file written by api export and read by api plan / api validate:
{
"schemaVersion": "1.0",
"source": {
"exportedAtUtc": "2025-01-15T10:00:00.000Z",
"environmentUrl": "https://your-org.crm.dynamics.com"
},
"customApis": [
{
"uniqueName": "ccsm_MyCustomApi",
"name": "ccsm_MyCustomApi",
"displayName": "My Custom API",
"description": "Processes a survey recipient record.",
"bindingType": "Global",
"isFunction": false,
"isPrivate": false,
"workflowSdkStepEnabled": false,
"allowedCustomProcessingStepType": "None",
"requestParameters": [
{
"uniqueName": "RecipientId",
"name": "ccsm_MyCustomApi.RecipientId",
"displayName": "Recipient ID",
"description": "ID of the recipient record.",
"type": "EntityReference",
"logicalEntityName": "ccsm_surveyrecipient",
"isOptional": false
}
],
"responseProperties": [
{
"uniqueName": "Success",
"name": "ccsm_MyCustomApi.Success",
"displayName": "Success",
"description": "Whether the operation succeeded.",
"type": "Boolean"
}
]
}
]
}Field notes:
| Field | Values / constraints |
|---|---|
bindingType |
"Global", "Entity", "EntityCollection" |
allowedCustomProcessingStepType |
"None", "AsyncOnly", "SyncAndAsync" |
type (parameter/property) |
"Boolean", "DateTime", "Decimal", "Entity", "EntityCollection", "EntityReference", "Float", "Integer", "Money", "Picklist", "String", "StringArray", "Guid" |
logicalEntityName |
Only set for Entity or EntityReference; must be absent for all other types |
name (parameter/property) |
Recommended format: {customApiUniqueName}.{childUniqueName} |
uniqueName (Custom API) |
Must include publisher prefix, e.g. ccsm_MyApi; only letters, digits, underscores |
Publishing notes
The package is built from TypeScript. The repository does not need committed dist/ files; they are generated during build and included in the published npm package.
Before publishing, verify the package contents:
npm run build
npm pack --dry-runThe dry run should include at least:
dist/
src/
README.md
LICENSE
package.jsonFor scoped public packages:
npm publish --access publicRequirements
- Node.js 16+
- npm
- Access to a Dataverse environment
- Azure AD / Microsoft Entra ID App Registration with the correct permissions
- Sufficient Dataverse privileges to read or modify Custom API metadata — run
dvc api validate-privilegesafter connecting to verify which privileges are available
License
MIT
Author and contact
Andreas Brunsmann
- Email: oss@andreasbrunsmann.de
- GitHub: https://github.com/brunsforge
- Repository: https://github.com/brunsforge/dataverse-custom-api
Dataverse Custom API CLI
Eine Node.js/TypeScript-basierte CLI zum Verwalten von Dataverse Custom APIs. Sie ermöglicht das Lesen, Exportieren, Bearbeiten, Validieren, Vergleichen, Planen und Synchronisieren von Custom-API-Definitionen zwischen lokalen JSON-Dateien und Dataverse-Umgebungen.
Installation
npm install -g @brunsforge/dataverse-custom-apiÜbersicht der Features
- Verbindung zu Dataverse-Umgebungen: Device Code, Client Secret, Interactive Browser
- Environment-Management: speichern, auflisten, wechseln, entfernen
- Custom API-Verwaltung:
- vorhandene Custom APIs auflisten
- als lokale JSON-Artefakte exportieren
- lokal bearbeiten
- lokale Definition validieren — erkennt Typfehler, fehlende Felder und ungültige Kombinationen mit strukturierten Diagnostics
- mit Dataverse-Metadaten vergleichen
- Sync-Pläne erstellen (Validierung läuft automatisch; Plan wird bei Fehlern blockiert)
- einzelne Sync-Operationen ausführen
- vollständige Sync-Pläne ausführen
- Privileges prüfen — prüft welche Dataverse-Privileges der konfigurierte App-User besitzt und welche Features verfügbar oder eingeschränkt sind
- Publisher auflisten — zeigt alle registrierten Publisher mit ihren Customization-Prefixen an
- Graceful Privilege-Fallback: fehlt dem App-User
prvAppendToPluginType, wirdcreateCustomApiautomatisch ohne das PluginType-Binding wiederholt und einewarningim Sync-Ergebnis eingetragen — statt mit einem Fehler abzubrechen - Simulationsmodus: Operationen mit
--simulatevorab prüfen - JSON und menschenlesbare Ausgaben: alle Befehle unterstützen
--json
Achtung: Befehle, die ohne
--simulateausgeführt werden, können Custom-API-Metadaten in der verbundenen Dataverse-Umgebung erstellen, ändern oder löschen.
Verbindung
dvc connect -u "https://your-org.crm.dynamics.com"Connected and cached: https://your-org.crm.dynamics.com
Auth mode: deviceCodeEnvironment-Management
dvc env list # alle gespeicherten Environments auflisten
dvc env current # aktives Environment anzeigen
dvc env use -i <id> # Environment wechseln
dvc env remove -i <id> # Environment entfernenCustom API-Management
Auflisten und auswählen
dvc api list # alle Custom APIs auflisten
dvc api current # aktive API anzeigen
dvc api use -n ccsm_MyApi # API als aktiv setzenExportieren
dvc api export
dvc api export -n ccsm_MyApiLokale Definition validieren
Prüft die lokale JSON-Datei gegen Dataverse-Regeln. Keine Netzwerkverbindung erforderlich.
dvc api validate
dvc api validate -n ccsm_MyApiAusgabe ohne Probleme:
Validation for: ccsm_MyApi
Status: succeeded
No issues found.Ausgabe mit Fehlern:
Validation for: ccsm_MyApi
Status: validationFailed
ERROR [CCDV_PARAM_LOGICAL_ENTITY_NAME_NOT_ALLOWED] [ccsm_MyApi > NewParameter].logicalEntityName
Request parameter 'NewParameter' has type 'EntityCollection' which does not support logicalEntityName.
Fix: Remove logicalEntityName or change the parameter type to Entity or EntityReference.
Summary: 1 error(s), 0 warning(s), 0 info(s)Mit --json wird ein vollständiges CcdvCommandResult-Envelope zurückgegeben (für VS-Code-Extension-Integration).
Geprüfte Regeln (Auswahl):
uniqueName: Pflichtfeld, max. 128 Zeichen, muss Publisher-Präfix enthalten (z. B.ccsm_), nur Buchstaben/Ziffern/UnterstrichenameunddisplayName: Pflichtfeld, max. 100 Zeichendescription: max. 300 ZeichenbindingType:Global,EntityoderEntityCollectionboundEntityLogicalName: Pflicht beiEntity/EntityCollection; muss fehlen beiGloballogicalEntityNamean Parametern/Properties: nur erlaubt bei TypeEntityoderEntityReference- Functions (
isFunction: true): mindestens eine Response Property erforderlich - Functions: keine offenen Entity-/EntityCollection-Request-Parameter (ohne
logicalEntityName)
Hinweis:
api planläuft dieselbe Validierung intern ab. Bei blockierenden Fehlern wird kein Plan erstellt; die Fehlermeldung verweist aufapi validatefür den vollständigen Report.
Vergleichen
dvc api diff
dvc api diff -n ccsm_MyApiSync-Plan erstellen
dvc api plan
dvc api plan -n ccsm_MyApiPlan created: .cache/customapis/ccsm_MyApi.syncplan.json
State file: .cache/customapis/ccsm_MyApi.syncstate.json
Operations: 2
Destructive changes required: no
- [10] createRequestParameter NewParameter (new)
- [20] updateCustomApi ccsm_MyApi (changed)Ausführen
dvc api exec-plan --simulate # Dry Run
dvc api exec-plan # live ausführenEinzelne Operation ausführen
dvc api exec-op -o "<operationId>" --simulate
dvc api exec-op -o "<operationId>"Metadaten-Konsistenz prüfen
dvc api check-metadataDataverse-Privileges prüfen
Prüft welche relevanten Dataverse-Privileges der konfigurierte App-User besitzt und gibt eine strukturierte Übersicht aus. Erfordert eine Netzwerkverbindung.
dvc api validate-privileges
# Alle gefundenen Privilege-IDs anzeigen:
dvc api validate-privileges --verbose
# Maschinenlesbare Ausgabe:
dvc api validate-privileges --jsonAusgabe:
Privilege validation for: orgf707a816.crm16.dynamics.com
App user: DVC Custom API CLI - AppOnly (6dcbb78d-...)
FEATURE PRIVILEGE STATUS
──────────────────────────────────────────────────────────────────────────────
Read Custom API prvReadCustomAPI ✓ Available
Create Custom API prvCreateCustomAPI ✓ Available
Update Custom API prvWriteCustomAPI ✓ Available
Delete Custom API prvDeleteCustomAPI ✓ Available
PluginType Binding prvAppendToPluginType ✗ Missing
Create Plugin Steps prvCreateSdkMessageProcessingStep ✗ Missing
HINTS:
• Custom APIs will be created without a plugin type link.
Grant 'prvAppendToPluginType' (AppendTo, plugintype, organisation scope) to the app user's security role.
• Plugin step registrations cannot be created automatically.
Grant 'prvCreateSdkMessageProcessingStep' (Create, sdkmessageprocessingstep, organisation scope) to the app user's security role.Geprüfte Privileges:
| Feature | Privilege | Bekannte ID |
|---|---|---|
| Custom API lesen | prvReadCustomAPI |
per API aufgelöst |
| Custom API anlegen | prvCreateCustomAPI |
per API aufgelöst |
| Custom API bearbeiten | prvWriteCustomAPI |
per API aufgelöst |
| Custom API löschen | prvDeleteCustomAPI |
per API aufgelöst |
| PluginType-Binding bei Create | prvAppendToPluginType |
574c053e-6488-4bfb-832a-cbc47aff8b32 |
| Plugin Steps anlegen | prvCreateSdkMessageProcessingStep |
per API aufgelöst |
Graceful Fallback für prvAppendToPluginType:
Fehlt dem App-User prvAppendToPluginType, wiederholt die CLI createCustomApi automatisch ohne das PluginTypeId@odata.bind-Feld. Die Custom API wird erfolgreich angelegt — jedoch ohne Plugin-Verknüpfung. Das Sync-Ergebnis (exec-op, exec-plan) enthält ein warning-Feld mit der Erklärung und den Handlungsoptionen:
prvAppendToPluginType(AppendTo, plugintype, Org-Ebene) der Sicherheitsrolle des App-Users vergeben, oder- das Plugin nach der Erstellung manuell im Maker Portal verknüpfen.
Publisher auflisten
Listet alle in der aktiven Dataverse-Umgebung registrierten Publisher auf. Der customizationPrefix eines Publishers muss als Präfix im uniqueName einer Custom API verwendet werden (z. B. meinprefix_MyAction).
dvc api list-publishers
# maschinenlesbar:
dvc api list-publishers --jsonAusgabe:
PREFIX FRIENDLY NAME UNIQUE NAME
───────────────────────────────────────────────────────────────────────────────
meinprefix Mein Firmen-Publisher meinfirma_publisher
contoso Contoso Default Publisher contoso_defaultHinweis: Der Microsoft-Systempublisher (
mscrm) wird nicht angezeigt — sein Präfix kann nicht für eigene Lösungen verwendet werden.
Lokale Artefakte entfernen
dvc api remove
dvc api remove -n ccsm_MyApiTypischer Workflow
Verbindung herstellen und Privileges prüfen
# Auth-Konfiguration erstellen und Environment verbinden
dvc connect -u "https://your-org.crm.dynamics.com"
# Optional: Privileges des App-Users prüfen
dvc api validate-privilegesVorhandene Custom API bearbeiten
dvc api list
dvc api use -n ccsm_ExistingApi
# Aktuelle Definition aus Dataverse holen
dvc api export
# JSON-Datei lokal bearbeiten (z. B. Parameter hinzufügen)
# Lokale Definition validieren — Fehler beheben, bevor der Plan erstellt wird
dvc api validate
# Diff mit Dataverse prüfen
dvc api diff
# Sync-Plan erstellen (validiert intern)
dvc api plan
# Dry Run
dvc api exec-plan --simulate
# Live ausführen nach Prüfung
dvc api exec-planNeue Custom API erstellen
# Optional: Privileges prüfen — fehlt prvAppendToPluginType, wird das
# PluginType-Binding beim Erstellen automatisch weggelassen (+ Warning im Ergebnis)
dvc api validate-privileges
# Publisher auflisten, um den korrekten Präfix für den uniqueName zu ermitteln
dvc api list-publishers
# Neue JSON-Katalog-Datei erstellen (siehe Struktur unten)
# z. B. .cache/customapis/meinprefix_NewApi.json
dvc api use -n ccsm_NewApi
# Lokale Definition validieren, bevor der Plan erstellt wird
dvc api validate
# Sync-Plan erstellen
dvc api plan
# Dry Run
dvc api exec-plan --simulate
# Live ausführen
dvc api exec-plan
# Falls prvAppendToPluginType fehlte: das Ergebnis enthält eine warning —
# Plugin anschließend manuell im Maker Portal verknüpfen.Warum vor dem Plan validieren?
api validate ist ein rein lokaler Check — keine Netzwerkverbindung, sofortiges Feedback. Es erkennt alle Probleme, die Dataverse mit einem 400-Fehler ablehnen würde, sowie Warnungen und Hinweise zu empfohlenen Formaten.
api plan läuft dieselbe Validierung intern ab und blockiert bei blockierenden Fehlern. Mit api validate vorab erhältst du den vollständigen strukturierten Report — inklusive Warnungen und Infos — bevor du versuchst, einen Plan zu erstellen.
JSON-Struktur der Custom API
{
"schemaVersion": "1.0",
"source": {
"exportedAtUtc": "2025-01-15T10:00:00.000Z",
"environmentUrl": "https://your-org.crm.dynamics.com"
},
"customApis": [
{
"uniqueName": "ccsm_MyCustomApi",
"name": "ccsm_MyCustomApi",
"displayName": "My Custom API",
"description": "Processes a survey recipient record.",
"bindingType": "Global",
"isFunction": false,
"isPrivate": false,
"workflowSdkStepEnabled": false,
"allowedCustomProcessingStepType": "None",
"requestParameters": [
{
"uniqueName": "RecipientId",
"name": "ccsm_MyCustomApi.RecipientId",
"displayName": "Recipient ID",
"description": "ID of the recipient record.",
"type": "EntityReference",
"logicalEntityName": "ccsm_surveyrecipient",
"isOptional": false
}
],
"responseProperties": [
{
"uniqueName": "Success",
"name": "ccsm_MyCustomApi.Success",
"displayName": "Success",
"description": "Whether the operation succeeded.",
"type": "Boolean"
}
]
}
]
}Voraussetzungen
- Node.js 16+
- npm
- Zugriff auf eine Dataverse-Umgebung
- Korrekte Azure AD / Microsoft Entra ID App Registration
- Ausreichende Dataverse-Berechtigungen — nach dem Verbinden mit
dvc api validate-privilegesprüfen, welche Privileges verfügbar sind
Lizenz
MIT
Autor und Kontakt
Andreas Brunsmann
- E-Mail: oss@andreasbrunsmann.de
- GitHub: https://github.com/brunsforge
- Repository: https://github.com/brunsforge/dataverse-custom-api