Assumes no prior knowledge. Every command, what you should see, and what to do when it goes wrong.
The automations ("Workers") are small programs that run on a schedule in Cloudflare. They keep audiences in sync, push conversion data back to the ad platforms, and send the daily digest. You will not touch them most weeks. You need access so that when one stops, you can look at it yourself.
Check your email for an invite to the Homa Cloudflare account. Accept it and set up two-factor auth.
dash.cloudflare.com, a Homa account appears in the account picker at top-left.You need Node.js installed. Then everything below uses npx wrangler, which downloads on demand — nothing to install globally.
A browser window opens. Approve the request.
export CLOUDFLARE_API_TOKEN=...Download lab-fleet-handoff.zip from the handoff site and unzip it. Every worker is a folder inside workers/.
Before opening anything, check the fleet monitor. It watches every automation and reports which are healthy.
Cloudflare dashboard → Workers & Pages → click the worker by name.
The Metrics tab shows recent invocations. The Logs tab (Begin log stream) shows live output.
Most of these run on a timer. To force a run now:
The schedule lives in that worker wrangler.toml, in cron syntax.
Change the value, save, then deploy (A5).
It prompts for the value. Paste it and press enter.
SECRETS.md in the bundle; the values are in the restricted sheet.wrangler.toml. Anything in those files is in the repo forever.Dashboard → the worker → Edit code → Save and Deploy. Fine for a one-line fix.
wrangler.toml.wrangler.toml does not exist in this account — see A6.Dashboard → the worker → Deployments → find the previous version → Rollback.
Workers store state in KV namespaces. The ids in wrangler.toml point at Homa account. In a different account you must create your own.
Paste that id into wrangler.toml under the matching binding, then deploy.
main. Deploying with --branch=production succeeds, prints "Deployment complete", and gives you a working preview URL — but the live domain keeps serving the old version. Three deploys were silently invisible before this was spotted.A 200 response is not proof. The site returns a small fallback page with a 200 status for any unknown path.
catalog_sync.py ships in the bundle with its original launchd definition. Re-hosting it mechanically brings back the hourly full-feed refresh, which is what caused the crawler flood. See section D.Direction: they send us a file. Nothing arrives unless someone asks for it.
Contact details are in the vendor workbook. Send this, changing only the dates:
They reply with record counts by state and intent tier. Check the split looks like previous runs before saying yes. For reference, the first delivery was 20,000 records — FL 10,472 and TX 9,528, with 17,055 carrying a usable email.
/homa/out, and downloads it into data/. Safe to re-run — it skips what it already has.~/.homa-speedeon-sftp — read the file, never retype the value.Drop anyone who has already registered, is in a recovery sequence, or has unsubscribed. This is the suppression step and it is not optional.
Load into the email tool as a new campaign.
Build a Custom Audience with all the identity fields, not just email — email, first name, last name, zip, state, country. Multi-key roughly doubles the match rate.
Create the list, then add members through Data Manager (not the regular Ads API).
Direction: we send them a file. They match devices that were physically at those open houses and push audiences into our ad account.
Dates are the weekend just gone, comma separated.
For every row in every weekend file:
Simplest and safest: remove any row that fails. It costs about 1% of volume.
Repairing the citrus date rollover is possible but only where the local times validate. A wrong time window is worse than a missing row — it geofences the wrong hours.
Attach the weekend CSVs. Raw CSV is fine — each is roughly 2.6 to 3.0 MB, and three fit comfortably under the attachment limit.
State in the email: lat/long only, and one audience per weekend, kept separate.
In the ad account, list all custom audiences sorted by creation time, newest first.
Select all the new audiences together and read the platform delivery estimate for the union.
Attach the audience to each campaign one at a time, then explicitly set the audience mode to observation and verify it.
The property ads pull from a catalog of about 16,000 live listings. It is edited by hand, on purpose.
| Action | Cost | Safe? |
|---|---|---|
| Remove a listing | Nothing. No page fetch needed. | Always |
| Add a listing | One page fetch each. | In small batches |
| Change an item image URL | Re-fetches the image only. | Yes |
| Full re-upload / hourly schedule | Every page, at once, repeatedly. | NEVER |
Compare the catalog contents against live listing status. Anything sold or under contract that is still in the catalog should go.
Use the batch endpoint with method DELETE, in chunks of 200 or fewer.
Do not add everything. Start from what people are actually viewing: listings with at least two distinct viewers in the last 21 days, then join that set against the listings table.
Same batch endpoint, method CREATE, only genuinely new ids, a few hundred per call at most.
image must be an array — a plain string, or the bracket form image[0].url, both fail with a generic "invalid values" error and the item never displays. (2) Use the raw MLS photo here, not the badged URL — the badge has not been generated for a brand-new listing, so the badged address 404s and the ad shows a broken image.Anything added by hand skips the nightly badging job, which reads a frozen feed. So do it explicitly, in this order:
| Cadence | Job |
|---|---|
| Monthly, or when spend shifts | Remove off-market listings. |
| As needed | Add in-demand new listings, badge them. |
| Never | Full re-upload. Re-enabling the schedule. |
Homa operator guide. Companion to the handoff page.