Exposing Dynamics 365 Data to External Systems Without Losing Control
A technical guide for architects: rank the exposure patterns by risk, then build the gateway that holds, with per-consumer identity and limits, audit logging, versioned contracts, central PII handling and event fan-out.
When many external consumers need Dynamics 365 data, put a scoped, read-only API gateway in front of Dataverse and never give partners direct access. Each consumer gets its own credentials and a least-privilege application user, its own rate limit so it cannot starve other consumers or your users, and request logging for the audit trail. Publish versioned contracts that never mirror raw Dataverse schema, with contract tests in the deployment pipeline that fail a release when a Dataverse change would break a consumer. Handle PII in one place in the facade. For changes, fan events out through Azure Service Bus topics, one subscription per consumer, including on-premises systems, instead of letting everyone poll.
Which ways of exposing Dynamics 365 data are safest?
Rank the options by what a single mistake costs. The safest pattern gives each consumer a narrow, documented contract and nothing else. The riskiest hands out a key to the whole database and hopes the consumer behaves. Most estates that end up in trouble started with the third row because it was quick.
| Pattern | How it works | Risk | Use it when |
|---|---|---|---|
| Scoped read API through a gateway | A facade service exposes versioned endpoints over Dataverse; a gateway such as Azure API Management handles keys, tokens, rate limits and logging. | Lowest. Consumers see only what the contract returns. | Several consumers need current data, or any consumer is outside your organisation. |
| Scheduled extract | Data is exported on a schedule, for example through Azure Synapse Link for Dataverse, into a store the partner reads, or files are published to a secure location. | Low to medium. No live load on Dataverse, but the extract must still be filtered and access controlled. | Consumers need bulk or analytical data and can accept a delay. |
| Direct partner access to Dataverse | The partner receives credentials and calls the Dataverse Web API directly. | Highest. The partner can query any column the identity can read, bursts share Dataverse limits, and every schema change reaches them unannounced. | Avoid. If it exists today, plan its replacement. |
Why did the homegrown gateway lose records?
Home-built gateways rarely lose data through one dramatic bug. They lose it through missing mechanisms that nobody notices until a consumer reconciles. The pattern we see is a service that receives changes, calls downstream systems and moves on, with nothing to catch a failure in between.
If records are already missing, trace and contain first using our integration emergency triage method, then design the replacement. The detailed outbox, backoff and Service Bus settlement patterns are in point-to-point integration rationalisation; this page applies them to external consumers.
- No idempotency: a retried call created a second record, or a retried update overwrote a newer one, so the fix for duplicates became deleting data.
- Retries without keys: a timeout after a successful call was retried with no idempotency key or business key, so the consumer could not tell a resend from a new request.
- No dead-letter path: messages that failed repeatedly were logged and dropped, or completed anyway, instead of being parked for a person to replay.
- In-memory queues: work held in the gateway process vanished on restart or deployment.
- Shared credentials: every consumer used one identity, so nobody could tell which consumer caused load, errors or a data exposure.
- No alerting on silence: a consumer that stopped receiving anything looked the same as a quiet day.
How should each external consumer authenticate?
One consumer, one identity, end to end. Never share a key or an app registration between two consumers, because you lose the ability to rate limit, audit or revoke one of them without affecting the rest.
At the gateway, each consumer gets a subscription key and an OAuth 2.0 client credentials flow against its own app registration in Microsoft Entra ID; the gateway validates the token and the key together. Behind the gateway, the facade calls Dataverse through an application user tied to an app registration, with a custom security role that grants read on only the tables that consumer's contracts use. Where consumers have very different scopes, give each its own application user and role, so a bug in one contract cannot read another consumer's data. Rotate secrets on a schedule, or use certificates, and record who owns each consumer relationship.
- Gateway: subscription key per consumer plus a validated OAuth token from that consumer's app registration.
- Facade to Dataverse: application user per consumer or per scope, never a licensed human account and never a System Administrator role.
- Security role: read privilege only on the required tables, at the narrowest access level that works, with column security on sensitive columns.
- Revocation: disabling one consumer is one change at the gateway and one at the application user, tested before go-live.
Why must the facade filter fields and handle personal data instead of passing queries through?
Because a pass-through query hands the consumer your data model. If the gateway forwards a consumer's $select, $filter or $expand to the Dataverse Web API, the consumer decides which columns and related rows it receives, and your security role becomes the only control. Any column added to a table later becomes available the day it is created.
The facade owns the query instead. Each endpoint builds its own Dataverse query with a fixed column list, fixed relationships and validated parameters, then maps the result into the contract shape. Consumers can filter only on the parameters the contract offers, such as a date range or a status, and page sizes are capped by the facade. This is also where expensive queries are designed once and tested, rather than discovered in production when a consumer asks for everything.
Personal data belongs in the same place, handled once for every consumer. When each consumer receives full records and is trusted to discard what it should not keep, personal data handling is spread across as many systems as you have consumers, and a single weak one is your exposure.
Define per contract which personal fields are returned at all, which are masked or truncated (for example part of a phone number), and which are replaced with a pseudonymous key the consumer can use for matching without holding the identifier. Record the purpose of each consumer's access alongside its contract, apply consent or restriction flags in the facade query, and make erasure or restriction in Dynamics 365 flow through to what consumers can see. Involve your DPO in agreeing the contract fields; this page is technical guidance, not legal advice.
How do you rate limit consumers so they cannot starve each other or your users?
Limit per consumer at the gateway, and size those limits so the total stays well inside what Dataverse can serve alongside interactive users. API Management offers rate limit and quota policies keyed on the subscription or a value you choose, such as the client ID in the token; check which policies your chosen tier supports in current Microsoft documentation.
Dataverse applies its own service protection limits, evaluated per user identity per web server over a sliding window, and returns HTTP 429 with a Retry-After header when an identity exceeds them. Because the limits follow the identity, one application user shared by all consumers means one busy consumer throttles everyone. Separate identities, gateway limits below the platform limits, and caching of slow-changing reference data in the facade keep load predictable. The published limit figures and how to calculate against them are on our Dataverse bulk import guide, and keeping batch and integration traffic away from people working in the app is covered in protecting interactive users from API limits.
- Rate limit per consumer for bursts, and a quota per consumer for sustained volume.
- Return 429 with Retry-After from the gateway so well-behaved consumers back off before Dataverse is involved.
- Cache reference data such as product lists or status values in the facade for a short, documented period.
- Move bulk and analytical demand to the scheduled extract pattern rather than raising limits.
What should you log for audit without logging personal data unnecessarily?
Log who asked for what, when, and what happened, and keep bodies out of the log by default. An auditor usually needs to establish which consumer accessed which contract and records over a period; they rarely need a copy of the payload, and a log full of personal data becomes a second data store with its own retention and breach obligations.
At the gateway, log consumer identity, endpoint and version, parameters (masked where they contain personal identifiers), response status, record count, duration and a correlation ID that the facade and any downstream call also record. API Management can send request logs to Application Insights or Azure Monitor with header and body logging configured per API; switch body logging off, or limit it tightly, for endpoints returning personal data. Agree the log retention period with your data protection lead, and keep error details that help replay without storing the full record. This is technical guidance, not legal advice.
How do you stop a Dataverse change from silently breaking downstream consumers?
Publish contracts, not tables, and test them on every deployment. A consumer contract is a versioned API definition (for example an OpenAPI document) owned by you, mapped from Dataverse inside the facade. Renaming a column, changing a choice value or splitting a table then changes the mapping, not the contract.
- Version routes explicitly, such as /v1/ and /v2/, and support the previous version for an announced overlap period. API Management versions and revisions help publish and retire versions without changing consumer URLs unexpectedly.
- Additive changes (a new optional field) can ship within a version; removals, renames, type changes and changed meanings need a new version.
- Never return Dataverse logical names, option set integers or internal GUIDs a consumer has no business depending on.
- Contract tests in the pipeline: after the solution is imported into a test environment and the facade is deployed there, run each published contract's tests against it. If a response no longer matches the published schema, the deployment fails before production.
- Keep consumer-supplied example requests in the test suite, so a consumer's real usage is what the gate checks.
- Keep a change log per version that consumers can read, with retirement dates.
Why use event fan-out instead of letting every consumer poll?
Polling multiplies load by the number of consumers and still delivers changes late. Fifteen consumers each asking "what changed since my last call?" every few minutes is fifteen streams of queries against Dataverse, most of them returning nothing.
Publish changes once instead. A plug-in step or service endpoint registered with the Plug-in Registration Tool can post Dataverse change messages to Azure Service Bus or to a webhook, asynchronously so the user's save is not held up. Send to a topic, give each consumer its own subscription with filters for the events it is entitled to, and let each consumer read at its own pace. Where the message should carry only what the contract allows, route events through a small function that shapes them before publishing, rather than sending full entity images to partners.
- One subscription per consumer, so a slow or broken consumer builds up its own backlog without affecting others.
- Dead-letter monitoring per subscription, with a replay procedure someone has practised.
- Idempotent consumers keyed on the record ID plus a version or modified timestamp, because delivery is at least once.
- Legacy on-premises systems: an agent or service inside the network pulls from its subscription over an outbound connection, so no inbound firewall opening is needed. Where it needs full data, it calls the gateway with the ID from the event.
- Keep events thin (what changed, which record, which version) and let consumers fetch detail through their contract, so authorisation stays in one place.
What is the minimum viable gateway when there is no budget?
Build the controls first and the scale later. The mechanisms that stop data loss and exposure cost design effort, not large platform spend, and the minimum version keeps every control from this page in a smaller form.
API Management tiers differ in SLA, networking options, scale and policy support, and consumption-based and entry-level tiers exist; the developer tier is not intended for production. Check current Microsoft documentation for what each tier includes before choosing, and do not design around a feature your tier lacks.
- One facade, such as Azure Functions, with one endpoint per contract, fixed column lists and versioned routes from day one.
- An entry-level or consumption-based API Management instance in front for keys, token validation, per-consumer limits and logging, if the tier supports the policies you need.
- An app registration per consumer and a least-privilege application user per scope.
- One Service Bus topic with a subscription per event-driven consumer, instead of a custom queue.
- Scheduled extracts for any consumer that only needs daily data.
- Contract tests in the existing pipeline, even if there are only a handful.
Does exposing data to many consumers mean Dynamics 365 is the wrong system of record?
No. A governed gateway in front of Dataverse is a normal architecture, and the problems described here come from missing controls rather than from the platform. It is worth reviewing the choice if most of the value is in the external consumers and very few people work in the CRM itself.
We recommend the right solution - whether that's Microsoft Dynamics 365, Power Platform, or a custom-built CRM. Some businesses need the Microsoft ecosystem. Others need full control without licensing. We deliver both. When Microsoft licensing does not fit that shape, a custom-built CRM on React, Node.js, PostgreSQL or .NET can own the data and the API layer together.
Should your integration hub sit on Dynamics 365 or on a platform you own end to end?
Can afford licensing and want the Microsoft ecosystem
Dynamics 365
Microsoft 365, Teams and Outlook integration, a mature partner ecosystem, Copilot, and apps for sales, service and field operations that are configured rather than built.
Need full control and zero licensing
Custom CRM
A CRM built on React, Node.js, PostgreSQL or .NET that you own outright: your data model, your hosting, no per-user subscription, and features shaped exactly to your process.
Not sure which fits
We help you decide
A short discovery weighs licensing budget, process complexity, integrations and long-term ownership, then recommends one path. We deliver both, so the recommendation has no reason to lean.
How does Solzet help expose Dynamics 365 data safely?
We start by inventorying consumers, what each reads, how it authenticates today and what it has broken, then rank them by risk. We design the contracts, build the facade, gateway configuration, per-consumer identities, event fan-out and contract tests, and move consumers across one at a time with the old access switched off as each one lands. An independent review of the current integrations, security roles and environment is available through our Dynamics 365 health check and technical audit, and developers for the build through our Dynamics 365 development service.
Solzet delivers remotely from Yerevan, Armenia, with senior consultants and full-stack developers and 8+ years of Dynamics 365 Customer Engagement and Power Platform work, directly or white-label for Microsoft partners.
What do people ask us?
Should partners be given direct access to the Dataverse Web API?
Avoid it. Direct access lets a partner query any column its identity can read, puts its traffic against the same Dataverse limits as your users, and exposes it to every schema change without warning. Put a scoped, versioned read API behind a gateway such as Azure API Management instead, or publish a scheduled extract for consumers that only need periodic data.
How should external consumers authenticate to a Dynamics 365 API gateway?
Give each consumer its own subscription key and its own app registration using the OAuth 2.0 client credentials flow, validated at the gateway. Behind it, the facade calls Dataverse as an application user with a custom security role granting read only on the tables that consumer needs. Never share credentials between consumers, so each can be limited, audited and revoked separately.
How do you stop one integration from throttling Dynamics 365 for everyone else?
Rate limit and set quotas per consumer at the gateway, and give consumers separate Dataverse application users, because service protection limits are evaluated per user identity. Keep gateway limits below what Dataverse can serve alongside interactive users, cache reference data in the facade, and move bulk demand to scheduled extracts. Check current Microsoft documentation for the limit values.
How do you version an API over Dataverse without breaking consumers?
Publish contracts you own, such as OpenAPI definitions with versioned routes like /v1/ and /v2/, and map Dataverse into them in a facade. Additive optional fields can ship within a version; removals, renames and type changes need a new version with an overlap period. Run contract tests in the deployment pipeline so a Dataverse change that breaks a published contract fails before production.
Should external systems poll Dynamics 365 for changes?
Not at scale. Publish change events once from Dataverse to an Azure Service Bus topic through a registered service endpoint or webhook, with one subscription per consumer. Each consumer reads at its own pace, failures dead-letter per subscription, and on-premises systems can pull over an outbound connection. Keep events thin and let consumers fetch detail through their API contract.
Why do custom integration gateways lose records?
Usually because of missing mechanisms rather than one bug: no idempotency, so retries duplicate or overwrite records; retries without a business or idempotency key; no dead-letter path, so failed messages are logged and dropped; work held in memory that disappears on restart; and no alert when a consumer stops receiving data. Durable queues, idempotent writes and monitored dead-letter queues address these.
What is the cheapest safe way to expose Dynamics 365 data to a partner?
A small facade with fixed, versioned endpoints, an entry-level or consumption-based API Management tier for keys, token validation, limits and logging if it supports the policies you need, an app registration and least-privilege application user per consumer, and scheduled extracts where daily data is enough. Check current Microsoft documentation for tier capabilities; the developer tier is not for production.
Where should you go next?
Dynamics 365 developers and CRM consulting
Senior developers for Dataverse, C# plugins, custom API and the integrations around Dynamics 365.
Dataverse bulk import strategy
Service protection limits, 429 handling and loading millions of rows without a three day run.
Dynamics 365 health check and technical audit
An independent review of security, integrations, performance and ALM with evidence for every finding.
Point-to-point integration rationalisation
Outbox, retry, idempotent Service Bus consumers and the order of work for a fragile estate.
Dataverse API limits and interactive users
Keeping batch and integration traffic from slowing down the people working in the app.
Custom CRM Development
CRM and APIs on React, Node.js, PostgreSQL and .NET for organizations that need full control without Microsoft licensing.
Which solution is right for your business?
Tell us what you need. A senior consultant replies within one business day with a recommendation - Dynamics 365, Power Platform, or a custom-built CRM - not a sales script.