Skip to main content

Connect Microsoft Dynamics 365

Dynamics 365 connects to PMFriend through Power Automate, the way Microsoft connects most outside systems. It uses PMFriend's webhook and status API; nothing is installed in Dynamics and there is no PMFriend connector to buy.

Two flows:

  1. PMFriend → Dynamics: every new PMFriend job becomes a record in Dynamics.
  2. Dynamics → PMFriend: when the job is completed in Dynamics, PMFriend is told "work done", and the resident is asked to confirm.

You need: integrations switched on for your PMFriend account (ask support@pmfriend.com), an agency admin in PMFriend, and someone who can build flows in your Power Platform environment. The "When an HTTP request is received" trigger and the HTTP action are premium connectors, so the flows need a Power Automate Premium (or Dynamics-included) licence.

What we have and haven't tested. The PMFriend side (the webhook, its JSON, the status call) is tested end to end. The Dynamics side below is the standard Power Automate pattern; every Dynamics environment has its own required fields, so your Dynamics admin adapts the field mapping. We are happy to do the first one with you on a call.

Flow 1 — new PMFriend jobs into Dynamics​

  1. In Power Automate, create an Automated cloud flow with the trigger When an HTTP request is received (Who can trigger the flow: Anyone). Paste the JSON schema below into Request Body JSON Schema. Save the flow: Power Automate shows the HTTP POST URL.
  2. In PMFriend, Settings → Integrations → Or a webhook: paste that URL, press Add webhook, and keep the keys it shows (you need the inbound key for flow 2). PMFriend sends a ping.
  3. In the flow, add a Condition: event is equal to ping → Response with status 200 and stop. Otherwise continue.
  4. Add a Condition: event is equal to work_order.created. In the yes branch, create the record (see below). Status changes arrive as work_order.status_changed; you can use them to update the record, or choose New jobs only in PMFriend.
  5. End with a Response, status 200. Anything else and PMFriend retries the delivery later.

Where the job goes, by Dynamics app:

Dynamics appPower Automate actionTypical mapping
Field ServiceMicrosoft Dataverse → Add a new row → table Work OrdersworkOrder.scope → Summary / Primary Incident Description; workOrder.property.address → address or a lookup of the Service Account for that building; workOrder.reference → a text column you add, such as PMFriend reference
Customer ServiceDataverse → Add a new row → table CasesworkOrder.scope → Title; workOrder.residentMessage → Description; workOrder.reference → a text column
Finance & Operations (Asset Management)Fin & Ops Apps → Create record in your maintenance request entityworkOrder.scope → description; workOrder.reference → a reference field; the asset or functional location your admin picks for the building

Field Service needs a Service Account and a Work Order Type on every work order, and usually a Price List: your admin picks fixed values or looks them up by building. Keep workOrder.reference on the Dynamics record — flow 2 sends it back to PMFriend.

Security: Power Automate can't check an HMAC signature on its own. The trigger URL itself carries a signature (the sig= part), so treat the whole URL like a password: don't paste it in e-mails or tickets. If you route through Azure Functions or Logic Apps with code, verify X-PMFriend-Signature as described in the webhook guide.

Request body schema (for "When an HTTP request is received")​

{
"type": "object",
"properties": {
"id": { "type": "string" },
"event": { "type": "string" },
"occurredAt": { "type": "string" },
"agency": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } } },
"workOrder": {
"type": ["object", "null"],
"properties": {
"id": { "type": "string" },
"reference": { "type": "string" },
"status": { "type": "string" },
"previousStatus": { "type": ["string", "null"] },
"scope": { "type": "string" },
"property": { "type": ["object", "null"], "properties": {
"id": { "type": "string" }, "address": { "type": "string" }, "timeZone": { "type": "string" } } },
"category": { "type": ["string", "null"] },
"urgency": { "type": ["string", "null"] },
"resident": { "type": ["object", "null"], "properties": {
"name": { "type": ["string", "null"] }, "contact": { "type": ["string", "null"] } } },
"residentMessage": { "type": ["string", "null"] },
"contractor": { "type": ["object", "null"], "properties": {
"id": { "type": "string" }, "name": { "type": "string" } } },
"scheduledAt": { "type": ["string", "null"] },
"createdAt": { "type": "string" },
"workDoneAt": { "type": ["string", "null"] },
"completedAt": { "type": ["string", "null"] },
"link": { "type": "string" }
}
}
}
}

Flow 2 — "completed" in Dynamics → "work done" in PMFriend​

  1. Store the PMFriend inbound key as a secure value (an environment variable of type Secret, or Azure Key Vault) — not as plain text in the flow.
  2. Trigger:
    • Field Service / Customer Service: Dataverse → When a row is added, modified or deleted, change type Modified, table Work Orders (or Cases), with a filter on the status your team uses for "completed" (System Status = Completed; for cases, Status = Resolved).
    • Finance & Operations: Fin & Ops Apps → When a Business Event occurs, category Asset Management, event Maintenance work order reported completed.
  3. Action HTTP:
    • Method POST
    • URI https://app.pmfriend.com/api/v1/integrations/inbound/work-orders/@{the PMFriend reference column}/status
    • Headers Content-Type: application/json and Authorization: Bearer @{your inbound key}
    • Body { "status": "work_done", "externalRef": "@{the Dynamics work order number}", "note": "Completed in Dynamics" }
  4. A 200 answer means PMFriend recorded it: the resident is asked "fixed or not?" (or, with nobody to ask, the job closes). The job's history in PMFriend shows "Reported by … (ticket …)".

The same call with "status": "scheduled" (and "scheduledAt"), "in_progress" or "cancelled" keeps PMFriend in step at the other stages. Details and error codes: Webhook and status API.