Home / Property Management / Work order status updates and an aging digest: vendors tap a link, tenants hear back
Property Management problem 2: Tenants call to ask 'any update on my repair?' and nobody notices a work order has been open for two weeks
Work order status updates and an aging digest
Vendors tap a link, tenants hear back
Each work order gets a signed status link the office forwards to the vendor or technician. They tap Scheduled (with a window), Waiting on parts, Couldn't get in or Completed; the tenant gets a short email (and a text, with consent) with standard wording, and the vendor's own notes go to the office only. Completed messages carry a 'not fixed? submit a new request' link. Every hour, emergencies with no update after a few hours and routine orders with none after 24 hours get one nudge to the manager. Every morning the manager gets one digest of orders open 7+ days, oldest first.
How the workflow runs
Trigger · When an event arrives
Status update (POST)
Steps
- Check link + save status row
- Tell the tenant (+ office)
Outcome
- Answer the vendor
- Send updates
Also runs on: Status link (GET) → Check link + render → Send page
Also runs on: Every hour → Held texts + aging check → Send alerts
Drawn from the node names in the workflow file inside the download (workflow.json).
The problem
Vendor benchmarks from a maintenance software company's work-order data; your own repair times will differ.
- Property Meld's June 2025 benchmark: average repair speed was 7.0 days, and 'once timelines exceed 6.8 days, churn increases, reviews dip, and investors take notice'. Source: Property Meld: Property Maintenance Operations Benchmarking Report, June 2025 (Property Meld work-order data) (Vendor benchmark)
- Property Meld's 2025 annual report put average repair speed at 6.81 days, up from 6.32 days in 2024, and calls speed 'a retention lever, not just an operations metric'. Source: Property Meld: Annual Benchmarking Report 2025 (Vendor benchmark)
Background reading: A maintenance request system for small landlords, built from a form and a spreadsheet
Who it's for
Property managers using the maintenance intake recipe (or any work_orders sheet) who send repairs to outside vendors.
Tools and monthly cost
| Tool | Official price |
|---|---|
| Twilio US local phone number | $1.15 / month pricing page |
| Twilio US SMS, sent or received | $0.0083 per segment, plus carrier fees on outbound texts of $0.0035-$0.005 per segment pricing page |
| A2P 10DLC registration (required to text US tenants from a local number) | One-time: $4.50 brand registration and $15 campaign vetting. Monthly: $1.50-$10 per campaign by use case pricing page |
| Resend (sends the emails; any email API that accepts an Idempotency-Key works) | Free: 3,000 emails / month, 100 / day. Pro: $20 / month for 50,000 emails pricing page |
| Google Sheets (rent roll, work orders, consent log, opt-out list, payment ledger) | Free with a Google Account pricing page |
| n8n | Community Edition: free if you self-host (you pay for the server). n8n Cloud: Starter EUR 20 / month or Pro EUR 50 / month, billed annually pricing page |
Example month: Illustrative arithmetic, not a measured result. One local number sending 80 single-segment texts and receiving 80 a month, at the highest listed carrier fee ($0.005): 80 x ($0.0083 + $0.005) + 80 x $0.0083 + $1.15 number = $2.88 a month in Twilio usage, plus the A2P 10DLC campaign fee ($1.50-$10 a month) and one-time registration ($4.50 brand + $15 campaign vetting). Emails stay inside Resend's free tier at this volume. n8n comes on top. Your volumes will differ.
Setup
- Google Sheet: a rent_roll tab with columns unit, property, address, tenant_name, email, phone, rent, due_day, late_fee_note, active. Your property management software can export this; nothing here connects to Buildium or AppFolio (Buildium's Open API needs its Premium plan, and AppFolio's API is for approved partners).
- Texts need a Twilio number on an approved A2P 10DLC campaign. Set TWILIO_ACCOUNT_SID, TWILIO_BASIC_AUTH (base64 of SID:auth token) and PM_TWILIO_FROM. Point the number's Messaging webhook at /webhook/mss-pm3-sms so STOP and replies are handled in one place.
- Email: MSS_EMAIL_API (e.g. https://api.resend.com), EMAIL_API_KEY, PM_EMAIL_FROM, PM_OFFICE_EMAIL, PM_MANAGER_EMAIL. Every send carries an Idempotency-Key, so a retried run never emails twice.
- Company details: PM_COMPANY_NAME, PM_COMPANY_PHONE, PM_COMPANY_ADDRESS, COMPANY_TZ (your time zone; texting hours follow it).
- Google Sheet stand-in: MSS_SHEET_URL points at the shared sheets (rent_roll, sms_consent, sms_optouts, inbound_texts and the recipe's own sheets). In production, swap these HTTP calls for n8n's Google Sheets node.
- Remove the test-only mss-pm*-tick webhooks (or put them behind n8n header auth) and make sure MSS_TEST_CLOCK is NOT set in production.
- PUBLIC_WEBHOOK_BASE and PM_LINK_SECRET (the same as the intake recipe) for the signed /webhook/mss-pm2-update links. A wrong or edited link gets a 403.
- PM_FIRST_UPDATE_HOURS (24 for routine), EMERGENCY_UPDATE_HOURS, PM_AGING_DAYS (7) and DIGEST_HOUR (8) for the nudges and the digest; REQUEST_FORM_URL for the 'not fixed?' link.
Download
Downloadpm-work-order-status-and-aging.zip
Contains the n8n workflow, the mock data, the test runner with its last results, and the mock server we tested against. All keys in .env.example are fake.
Test result
Every workflow was imported into n8n 2.45.4 on an isolated test instance and run end to end. Twilio (signed inbound webhooks, HTTP 429), the email API (Resend-style), Stripe webhook events (signed with a mock secret, verified on the raw body) and Google Sheets were replaced by a local mock server built from the vendors' public docs. A frozen test clock checks due dates and the 8 a.m.-9 p.m. texting window. These recipes use no AI model; every decision is a written rule. Tenants, prospects, units and listings are invented. Verified with mocks; nothing was sent to a real person or account. How we test.
15/15 scenarios passed (run 2026-10-31)
| PASS | PM2-01 | Signed link opens the status page (problem, entry permission, statuses, office-only note); a wrong signature gets 403 |
| PASS | PM2-02 | Vendor taps Scheduled with a window: row appended; tenant gets email + text with the window and the entry permission |
| PASS | PM2-03 | Same update tapped twice: 'Already saved', tenant not told twice |
| PASS | PM2-04 | Scheduled without a date/time: 400, nothing saved |
| PASS | PM2-05 | Forged signature on POST: 403, no row, nobody told |
| PASS | PM2-06 | Vendor note goes to the office only; the tenant gets the standard 'waiting on a part' line |
| PASS | PM2-07 | Completed at 10:15 pm: tenant email now with 'not fixed? new request' link, text held; office copy |
| PASS | PM2-08 | Held completion text goes at 8 am, not 7 am |
| PASS | PM2-09 | Couldn't get in: tenant asked to pick a new time; office alert |
| PASS | PM2-10 | Hourly check at 1:30 pm: the 2.5-hour-old emergency with no update gets a manager nudge; the 3.5-hour-old routine one doesn't (24 h) |
| PASS | PM2-11 | Next morning (24 h): routine order nudged once; the emergency isn't nudged again |
| PASS | PM2-12 | Hour after: no repeat nudges |
| PASS | PM2-13 | 8 am digest: open orders 7+ days old, oldest first, with last status; completed and newer ones left out; once a day |
| PASS | PM2-14 | Tenant without text consent: email only |
| PASS | PM2-15 | Validly signed link for an order that isn't in the sheet: 404, nothing saved |
Actual output from the test run
Scenario PM2-02: the text a tenant gets when the vendor schedules the visit (invented tenant). Names and numbers are invented.
Hi Rosa, your maintenance request WO-270309-001 is scheduled: Tue Mar 10, 1-3 pm. You said we may enter if you are not home. Reply STOP to opt out.
Verified with mocks
- Signed link opens the status page (problem, entry permission, statuses, office-only note); a wrong signature gets 403
- Vendor taps Scheduled with a window: row appended; tenant gets email + text with the window and the entry permission
- Same update tapped twice: 'Already saved', tenant not told twice
- Scheduled without a date/time: 400, nothing saved
- Forged signature on POST: 403, no row, nobody told
- Vendor note goes to the office only; the tenant gets the standard 'waiting on a part' line
- Completed at 10:15 pm: tenant email now with 'not fixed? new request' link, text held; office copy
- Held completion text goes at 8 am, not 7 am
- Couldn't get in: tenant asked to pick a new time; office alert
- Hourly check at 1:30 pm: the 2.5-hour-old emergency with no update gets a manager nudge; the 3.5-hour-old routine one doesn't (24 h)
- Next morning (24 h): routine order nudged once; the emergency isn't nudged again
- Hour after: no repeat nudges
- 8 am digest: open orders 7+ days old, oldest first, with last status; completed and newer ones left out; once a day
- Tenant without text consent: email only
- Validly signed link for an order that isn't in the sheet: 404, nothing saved
- Verified with mocks; invented tenants, prospects, units and listings only.
Documented from official API docs only
Built to the vendor's documentation but not run against a live account. Check these on yours.
- Stripe webhook signing (Stripe-Signature t= / v1=, HMAC-SHA256 of timestamp.raw_body, 5-minute tolerance), Checkout Session fields (client_reference_id, payment_status, amount_total) and the async ACH events come from Stripe's public docs; tests sign events locally with a mock secret, not against a live Stripe account.
- Twilio request signing, the Messages API, 429 behavior and built-in STOP handling come from Twilio's docs. The email API follows Resend's documented request shape.
Limitations
- Status comes from whoever taps the link; nothing checks the work was really done. The tenant's 'not fixed?' link opens a new request so it isn't lost.
- Internal nudges and the digest go to your own staff; tenants only ever get the standard status lines.
- Every outgoing message is checked against a word list first (PM_LINT in pmlib.py): eviction, court action, lawsuit, collections, credit reporting, lockouts, shut-offs, 'notice to quit/pay' and police are blocked, and so are Fair Housing red-flag phrases ('no kids', 'adults only', 'perfect for a young couple', 'English only', 'able-bodied'...). A blocked message is held and the office is told why.
- Late fees, grace periods, partial-rent rules and notice periods vary by state and city, so nothing legal is hard-coded: every day, hour and amount is a setting, and late-fee wording is only ever your own sentence from the rent roll, sent word for word. The recipes never write notices to pay or quit, never mention eviction, court or credit reporting, and never act on a lease.
- Generic stack (web form, Google Sheet, Twilio, email API, Stripe Payment Links). Not built for Buildium, AppFolio, Rent Manager or any other property management system; we make no claim these recipes plug into them.
- Not legal advice and not a compliance guarantee. Texts to tenants and prospects go only to numbers with consent recorded on our own forms, only between 8 a.m. and 9 p.m. local time, and plain-English opt-outs (STOP, 'stop texting me', ...) are honored until the person texts START (47 CFR 64.1200 is the federal rule we design around). Some states are stricter; have your attorney review texting consent before going live.
- We're not lawyers. Nothing here is legal advice. Check HUD's Fair Housing pages (hud.gov/helping-americans/fair-housing-act-overview), the FCC texting rules (47 CFR 64.1200 on ecfr.gov) and your state's landlord-tenant law, or ask a local attorney.