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
- You click Dispatch on a work order.
- A SHA-256-hashed token is generated server-side. Plaintext is sent once to the contractor's email or SMS.
- The contractor taps the link. No login screen — the URL itself is the auth.
- They see the WO: address, scope, PM contact, tenant contact, cost ceiling.
- Buttons: Accept / Schedule (date picker) / On site now / Complete (mark done + upload invoice) / Raise a question.
- 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
- Ranked Picker — the step before dispatch
- Contractors Overview