Context
Our cleaning teams are independent contractors, paid weekly for each job at a rate per estimated hour. Rates differ by team and change over time, for example when a team sends in their insurance. Residential jobs live in our booking software, Airbnb turnovers only show up as Operto emails, and changes like a covered job or a late tip get posted in our #accounting channel in Slack. Pulling all of that together by hand took time every week.
What it does
- Runs on Monday. It starts when n8n starts on my laptop, with a 7am backup, and it won’t run twice for the same week.
- Pulls the week. ConvertLabs bookings, Operto turnovers from the notification emails, each team’s rate on the day of the job, and the hours for each Airbnb property.
- Reads #accounting. Claude turns the team’s messages into structured changes, like who covered which job. The bot confirms each one in the thread, and a change for a future job or an old week gets paid in the right week.
- Checks for cancelled turnovers. It compares the week’s Operto jobs with the texts on our customer and cleaner lines, and it only trusts a cancellation when it can quote the real message.
- Math in code. Claude never does the math. The pay is worked out in plain code, every line carries its source, and anything missing or unclear becomes a question for me instead of a guess.
- Review in a DM. The draft comes to me in a direct message, so team pay stays out of the shared channel. I answer its questions in the thread, and it re-runs and shows only what changed.
- Finalize. When I say it’s final, it drafts each team’s statement in Gmail for me to send, writes the week’s total to our KPI sheet, gives me each team’s amount for Gusto and starts the weekly KPI run.
Why it’s built this way
- Pay has to be right every time, so the AI only reads messages. The numbers come from code that does the same thing every week.
- Rates are kept as a history, one row per rate period, so a re-run of an old week still uses the rate that was in effect then.
- Statements are drafts, not sent emails. I read them before they go to the teams.
What’s next
- Read Operto through its API instead of its emails, once Operto turns on API access.