Home / Property Management / Maintenance request intake: one form, a work order number, and emergencies routed to on-call
Property Management problem 1: Maintenance requests arrive by text, voicemail and sticky note, and the flood at 2 a.m. looks the same as the dripping faucet
Maintenance request intake
One form, a work order number, and emergencies routed to on-call
Tenants use one web form: unit, category, what's wrong, whether you may enter, and optional text consent. Each request gets a work order number (WO-yymmdd-nnn) on the confirmation page, a row in the work_orders sheet, and an email (plus a text, with consent) saying it was received. The office gets the request with a suggested vendor from your vendors sheet and a signed status link to forward. Requests with emergency words (gas, flooding, sparks, no heat... you set the list, whole-word match so 'gasket' isn't gas) text and email your on-call person right away, day or night, and the tenant's page says to call 911 first if anyone is in danger. A duplicate of the same request within minutes isn't filed twice.
How the workflow runs
Trigger · When an event arrives
Form submit (POST)
Steps
- Honeypot + form key + dedupe + WO number
- Work order row + compose
Outcome
- Answer the browser
- Send ack + office + on-call
Also runs on: Form page (GET) → Render form → Send form page
Also runs on: Every 15 min, 8 am-8 pm → Send held texts → Send held
Drawn from the node names in the workflow file inside the download (workflow.json).
The problem
One vendor industry survey; we found no public figure for how many requests get lost, so we quote none.
- Buildium's 2025 industry report: maintenance was the second-greatest challenge for property management companies and rental owners' #1 source of stress; residents' expectations for quick fixes are 'higher than ever'. Source: Buildium: 2025 Property Management Industry Report (surveys of 1,796 property managers and 1,507 renters, 2024) (Vendor industry survey)
- The same report found email is renters' preferred way to communicate with their property manager, so every acknowledgment goes by email and texts are extra. Source: Buildium: 2025 Property Management Industry Report (surveys of 1,796 property managers and 1,507 renters, 2024) (Vendor industry survey)
Background reading: A maintenance request system for small landlords, built from a form and a spreadsheet
Who it's for
Small US property management companies and self-managing landlords (roughly 20-500 units) who take requests by phone, text and email today.
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 60 single-segment texts and receiving 60 a month, at the highest listed carrier fee ($0.005): 60 x ($0.0083 + $0.005) + 60 x $0.0083 + $1.15 number = $2.45 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.
- Share /webhook/mss-pm1-request (or embed its form; keep the form_ts / form_key fields). PM_FORM_HMAC_SECRET signs the form key; FORM_MIN_SECONDS (default 3) is the minimum fill time and company_url is a hidden honeypot.
- A vendors sheet (category, name, phone, email, emergency yes/no, active) for the suggested vendor; PM_ONCALL_PHONE and PM_ONCALL_EMAIL for emergencies; EMERGENCY_WORDS to change the list.
- PUBLIC_WEBHOOK_BASE and PM_LINK_SECRET so the office email can carry a signed status link for the work-order status recipe.
Download
Downloadpm-maintenance-request-intake.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 | PM1-01 | Form page: signed key, honeypot, unit/category/description/entry fields, 911 line first, consent optional |
| PASS | PM1-02 | Routine leak at 10 am with consent: WO number on the page, row in work_orders, email + text ack, office email with suggested vendor and signed status link, no on-call text |
| PASS | PM1-03 | Same unit, same problem 4 minutes later: 'already have this request', no second work order |
| PASS | PM1-04 | Second, different request: next number WO-270309-002; no vendor on file for that category is said plainly |
| PASS | PM1-05 | Flooding at 2:10 am: on-call texted now (staff), emergency vendor suggested, tenant page and email say call 911 if in danger; tenant text held for 8 am |
| PASS | PM1-06 | Held tenant text: not at 7:45, once at 8:00, not again at 8:15 |
| PASS | PM1-07 | Gas smell (category appliance): emergency by keyword |
| PASS | PM1-08 | 'gasket' is not 'gas': whole-word match, routine |
| PASS | PM1-09 | Unit not on the rent roll: still accepted, row flagged, office told to check before dispatching |
| PASS | PM1-10 | Tenant who texted STOP earlier ticks the box: no text (opt-out wins until they text START), email still sent |
| PASS | PM1-11 | Honeypot filled: polite page, nothing saved or sent |
| PASS | PM1-12 | Bad form key: 400 'reload', nothing saved |
| PASS | PM1-13 | Missing email: 400 with what's missing |
| PASS | PM1-14 | Email API answers 429 once: retried with the same idempotency key; ack and office email each sent once |
| PASS | PM1-15 | Twilio 429 once on an emergency: retried, on-call and tenant each texted once |
Actual output from the test run
Scenario PM1-02: the text a tenant gets after submitting a routine request (invented tenant and company). Names and numbers are invented.
Hi Rosa, we got your maintenance request WO-270309-001 for unit 12-4B (Plumbing / leak). We'll let you know when it's scheduled. Reply STOP to opt out.
Verified with mocks
- Form page: signed key, honeypot, unit/category/description/entry fields, 911 line first, consent optional
- Routine leak at 10 am with consent: WO number on the page, row in work_orders, email + text ack, office email with suggested vendor and signed status link, no on-call text
- Same unit, same problem 4 minutes later: 'already have this request', no second work order
- Second, different request: next number WO-270309-002; no vendor on file for that category is said plainly
- Flooding at 2:10 am: on-call texted now (staff), emergency vendor suggested, tenant page and email say call 911 if in danger; tenant text held for 8 am
- Held tenant text: not at 7:45, once at 8:00, not again at 8:15
- Gas smell (category appliance): emergency by keyword
- 'gasket' is not 'gas': whole-word match, routine
- Unit not on the rent roll: still accepted, row flagged, office told to check before dispatching
- Tenant who texted STOP earlier ticks the box: no text (opt-out wins until they text START), email still sent
- Honeypot filled: polite page, nothing saved or sent
- Bad form key: 400 'reload', nothing saved
- Missing email: 400 with what's missing
- Email API answers 429 once: retried with the same idempotency key; ack and office email each sent once
- Twilio 429 once on an emergency: retried, on-call and tenant each texted once
- 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
- The emergency flag is a keyword match, not a judgment. The form tells tenants to call 911 or the gas company first; your on-call person decides what happens next.
- A unit that isn't on the rent roll is still accepted and flagged so the office can check before sending anyone. Dispatching a vendor is your call; nothing is booked automatically.
- 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.