Package Information
Documentation
@marefati110/n8n-nodes-dynamics365
n8n community nodes for Microsoft Dynamics 365 / Dataverse on-premises (CRM 9.x), using the Web API with NTLM (Windows) authentication.
The package has no runtime dependencies. NTLMv2 (including MD4) is implemented in the package itself, because n8n's HTTP helpers do not support NTLM.
The nodes don't hard-code any tables or messages. Tables, columns, option sets, relationships, views, workflows, and all actions and functions are read from the server's metadata at runtime. Custom tables and custom actions work with no extra setup.
Tested against Dynamics 365 9.1.12.17 on-premises with n8n 2.36.7.
Nodes
| Node | Purpose |
|---|---|
| Dynamics 365 (On-Premises) | Records, actions/functions, metadata, $batch, raw requests. Also available as an AI agent tool. |
| Dynamics 365 (On-Premises) Trigger | Polling trigger for created and/or updated records. |
Resources and operations
Record
- Create, Update, Upsert (create-or-update, create-only, update-only), Delete, Get, Get Many
- Query (FetchXML): automatic paging cookie handling. Very long queries are sent through
$batchto avoid URL length limits. - Get View Records: system (
savedQuery) or personal (userQuery) views - Get Related: navigate any 1:N, N:1 or N:N relationship
- Associate / Disassociate: collection-valued and single-valued navigation properties
- Assign (user/team), Set State (statecode/statuscode)
- Get / Set / Clear Field Value
- Download / Upload File or Image (image columns such as
entityimage) - Count: exact count, optionally filtered
Action / Function
- Execute Action: any of the ~2000 system and custom actions, unbound, bound to a record, or bound to a table
- Execute Function: any function, with OData parameter aliases built automatically
- Run Workflow: on-demand (classic) workflows via
ExecuteWorkflow
Parameters are shown as typed fields based on the CSDL (booleans, numbers, dates, enum dropdowns, record references, collections, complex types), or can be passed as JSON.
Metadata: list tables, get a table definition, list columns, list relationships, get option sets (column or global), list global option sets, list/get actions and functions, clear the metadata cache.
Batch: send many requests in one $batch call, optionally in an atomic change set, with continue-on-error. Each response is mapped back to its input item.
Custom API Call: any method and path against the Web API with the NTLM credentials. Supports JSON, raw or binary bodies, binary responses, and following @odata.nextLink.
Common options
- Annotations: formatted values (option set labels, lookup names, formatted money and dates) or all annotations
- Impersonate User ID: sent as
MSCRMCallerID - Enable Duplicate Detection, Bypass Custom Plugins
- Timeout
Installation
In n8n go to Settings → Community Nodes → Install and enter @marefati110/n8n-nodes-dynamics365.
To install offline instead, build a tarball:
pnpm install
pnpm pack # creates marefati110-n8n-nodes-dynamics365-1.0.3.tgz
Then install it into n8n's community nodes folder, for example in a Docker image:
FROM n8nio/n8n:2.36.7
COPY marefati110-n8n-nodes-dynamics365-1.0.3.tgz /tmp/
RUN mkdir -p /home/node/.n8n/nodes && cd /home/node/.n8n/nodes \
&& npm install /tmp/marefati110-n8n-nodes-dynamics365-1.0.3.tgz
Or on a plain installation:
cd ~/.n8n/nodes && npm install /path/to/marefati110-n8n-nodes-dynamics365-1.0.3.tgz
Restart n8n afterwards. The package has no dependencies; n8n provides n8n-workflow at runtime.
Credentials
Create Dynamics 365 On-Premises (NTLM) API credentials:
| Field | Example |
|---|---|
| Organization URL | http://crm-server/OrganizationName. A full …/api/data/v9.1 URL also works. |
| API Version | 9.1 |
| Username | crmadmin, DOMAIN\crmadmin or crmadmin@domain |
| Password | |
| Domain | optional; defaults to the domain in the username or the one announced by the server |
The Test button calls WhoAmI().
The server must offer NTLM, either directly or through Negotiate. Kerberos-only servers are not supported.
Value formats
Lookup columns (Create / Update / Upsert, Map Columns mode) accept:
- a GUID:
5e80e09c-b131-e911-8148-000c29f57f53(single-table lookups;Ownerdefaults tosystemuser) table:GUID:account:5e80…entityset(GUID):accounts(5e80…)or/accounts(5e80…)nullto clear the lookup
The node turns these into the right navigationproperty@odata.bind reference. For multi-table lookups such as parentcustomerid or regardingobjectid, it picks the navigation property for the referenced table. Clearing is done with DELETE …/$ref, because 9.x rejects a null @odata.bind.
In JSON mode the body is sent as-is, except that "nav@odata.bind": null is also converted into a DELETE …/$ref.
Record parameters of actions and functions (for example Target) accept the same notations, or a full JSON object such as {"@odata.type": "Microsoft.Dynamics.CRM.account", "accountid": "…"}. Collection parameters accept a JSON array or a comma-separated list.
Examples:
- Execute Action → GrantAccess
Target:contact:<id>PrincipalAccess:{"Principal": {"@odata.type": "Microsoft.Dynamics.CRM.team", "teamid": "<id>"}, "AccessMask": "ReadAccess"}
- Execute Function → RetrieveTotalRecordCount
EntityNames:account,contact
Filters
Get Many, Count, Get Related, and the trigger have a Filter setting with three modes:
| Mode | What you enter |
|---|---|
| None | Nothing. All records are returned. |
| Build Conditions | One or more conditions (column, operator, value), combined with AND or OR. |
| Raw OData Expression | A $filter expression, for example statecode eq 0 and contains(name,'contoso'). |
In Build Conditions mode, the node formats each value from the column's metadata:
- Text columns get quotes. Numbers and choice columns use the option value, for example
1. - Lookup columns take a GUID and are compared through
_<column>_value. - Dates take ISO values such as
2026-01-31. - A column that isn't in the list, such as the lookup path
primarycontactid/fullname, can be set with an expression. Its value type is inferred.
Operators:
| Group | Operators | Value |
|---|---|---|
| Comparison | equals, not equals, greater/less than (or equal) | one value |
| Empty | is empty, is not empty | none |
| Text | contains, starts with, ends with, and their negations | text |
| Lists | in list, not in list, choices contain / don't contain values | comma-separated values |
| Range | between, not between | two comma-separated values |
| Dates | today, yesterday, this/last/next week, month, and year, last/next 7 days | none |
| Dates | on, on or before, on or after | a date |
| Dates | last/next X hours, days, weeks, months, years; older than X minutes to years | a whole number |
| User | equals current user, current user's teams, current user or teams, current business unit | none |
| Hierarchy | above, under (or equal), not under | a record ID |
Lists, ranges, dates, users, and hierarchy use Dynamics query functions such as Microsoft.Dynamics.CRM.LastXDays.
Trigger
The trigger polls on createdon (created) or modifiedon (updated / created or updated):
- On activation it starts from "now".
- On a manual test it returns the most recent record.
- Records with identical timestamps are not emitted twice.
You can add an extra OData filter and limit the selected columns.
Metadata cache
Table, column, relationship, and CSDL metadata are cached in memory for 30–60 minutes per credential. After customizing the CRM, run Metadata → Clear Cache, or restart n8n.
The first action/function lookup downloads $metadata, which is about 13 MB on a typical organization, and takes a few seconds.
Development
The project uses pnpm (see packageManager in package.json).
pnpm install
pnpm build
pnpm test # offline unit tests (NTLM vectors, CSDL, OData helpers, batch)
# Live tests (read-only unless DYNAMICS_TEST_WRITE=1)
DYNAMICS_URL=http://server/org DYNAMICS_USERNAME=user DYNAMICS_PASSWORD=secret \
pnpm test:integration
# Include write tests; supply the columns your org requires
DYNAMICS_TEST_WRITE=1 DYNAMICS_TEST_RECORD='{"lastname":"n8n-node-test","new_type":100000005}' \
DYNAMICS_URL=… DYNAMICS_USERNAME=… DYNAMICS_PASSWORD=… pnpm test:integration
Release a new version
pnpm release does the whole release in one command:
- Checks that the git working tree is clean.
- Bumps the version, starting from the newest version on npm.
- Runs the unit tests.
- Publishes to npm.
- Commits, tags (
v<version>), and pushes.
pnpm release # patch, for example 1.1.0 -> 1.1.1
pnpm release minor # 1.1.0 -> 1.2.0
pnpm release major # 1.1.0 -> 2.0.0
pnpm release 1.4.0 # explicit version
pnpm release --dry-run # check everything without publishing or committing
If npm asks for a one-time password, confirm it in the browser or terminal. If publishing fails, the version change is reverted.
pnpm-workspace.yaml disables the build script of isolated-vm, a native module that n8n-workflow pulls in. Only the TypeScript types of n8n-workflow are needed to build this package.
Source layout:
credentials/ NTLM credential type
nodes/Dynamics365/
transport/ntlm.ts NTLMv2 + MD4
transport/httpClient.ts keep-alive connection pool with NTLM handshake
transport/index.ts Web API requests, errors, paging, cache
helpers/csdl.ts $metadata parser (actions, functions, enums)
helpers/metadata.ts EntityDefinitions: tables, columns, option sets, relationships
helpers/odata.ts keys, lookups, parameter literals, $batch, FetchXML paging
methods/ dropdowns, field mappers, credential test
descriptions/ UI definitions per resource
actions/ execution per resource
nodes/Dynamics365Trigger/ polling trigger
Limitations
- HTTP proxies are not supported. The node connects directly to the CRM server.
- File columns larger than one request (chunked upload) are not implemented. 9.1 on-premises does not have file columns; image columns work.
- FetchXML aggregates are limited by the server to 50,000 records (
AggregateQueryRecordLimit). The Count operation falls back to paging in that case.
License
MIT

