Skip to main content

Magic-Link Dispatch

Tradies do not log in to software. They won't install your app. They won't remember your password. PMFriend's magic-link dispatch is built around this truth.

The contractor experience

  1. You click Dispatch on a work order.
  2. A SHA-256-hashed token is generated server-side. Plaintext is sent once to the contractor's email or SMS.
  3. The contractor taps the link. No login screen — the URL itself is the auth.
  4. They see the WO: address, scope, PM contact, tenant contact, cost ceiling.
  5. Buttons: Accept / Schedule (date picker) / On site now / Complete (mark done + upload invoice) / Raise a question.
  6. Each button transitions the WO status. Transitions push back to the PM inbox in real-time.

Token security

  • 14-day TTL. After that the link 404s; you can regenerate with a fresh dispatch.
  • SHA-256 hashed at rest — the server stores a hash, not the token. If an attacker dumps the database, they still can't forge links.
  • Single-use transition safety — a completed WO can't be re-opened via magic link. The PM has to re-dispatch.
  • Rate-limited per IP — 30 req/min on the public magic-link endpoints.

Why not SSO / password / OTP?

We piloted with 12 tradies across 4 agencies. Summary:

  • SSO — most tradies don't have a work email; they use their personal Gmail from 2011.
  • Password — "I'll write it in my ute book" → forgotten → support ticket → PM dispatches via SMS anyway.
  • OTP — works but adds a 15-second pause before accepting. 85% of dispatches are accepted within 2 minutes; friction matters.

Magic-link + 14-day TTL won. We'd revisit if a security concern surfaced, but so far the SHA-256 hash + one-time plaintext distribution has held up.

The flow the PM sees

[ASSIGNED]   PM dispatched to Harbour Plumbing at 14:22
└─ status chip: "Magic link sent (14d valid)"

[ACCEPTED] Contractor opened the link at 14:31 and clicked Accept
└─ status chip: "Accepted 14:31"

[SCHEDULED] Contractor scheduled for 2026-04-26 10:00
└─ status chip: "Scheduled Fri 10:00"

[ON_SITE] Contractor clicked On site at 09:58
└─ status chip: "On site since 09:58"

[COMPLETED] Contractor clicked Complete + uploaded invoice PDF at 11:42
└─ status chip: "Completed 11:42 · invoice attached"

API

The magic-link endpoints are on /api/v1/jobs/* (public, no auth required beyond the token in the URL):

GET  /api/v1/jobs/{token}                    # WO details
POST /api/v1/jobs/{token}/transitions # body: { action: ACCEPT | SCHEDULE | ... }
POST /api/v1/jobs/{token}/invoices # multipart file upload

The internal PM-facing endpoints sit at /api/v1/work-orders/*.

See also