Throughout Q2 April sends, broadcast content (subject, body, schedule) could not be written via API. V3 POST/PUT silently returned success but made no changes. Early V4 testing appeared broken too — a 200 response with no effect. This forced Jason to paste every email manually into the Kit UI, a fragile workflow that introduced CSS artifacts if automated and took significant time per send.
Root cause: API key scope, not a broken API. The Kit V4 API writes broadcast content correctly. The older V4 key (kit_528917ec...) lacked write permissions for broadcast content — it returned 200 but silently skipped writes. A new key ("Cowork Send Automation") created with correct scopes resolves it completely.
Confirmed: PUT /v4/broadcasts/{id} with {"content": "...", "subject": "...", "send_at": "..."} and header X-Kit-Api-Key: kit_c6d81b86936b3b3c468b674cd1f3295c writes all fields and returns the updated broadcast. Tested against broadcast 23982498 — content (15,372 chars), subject, and schedule all persisted correctly.
All future Sperry emails can be sent end-to-end via kit_send.py — no manual paste, no Kit UI required for content. Manual paste workflow is retired. V3 remains broken and should never be used for writes.
Full rewrite from scratch. Now V4-only, with four safety improvements that prevent the most common send errors:
| Problem fixed | How |
|---|---|
| Schedule data loss on PUT (Friction #1) | Reads current send_at on every update and re-includes it automatically. Schedule can only be changed via explicit --send-at or cleared via --clear-schedule. |
| tel: links hard-rejected by Kit (Friction #2) | preprocess_html() strips all <a href="tel:..."> anchors before every API call. Phone numbers preserved as plain text. Stripped numbers logged. |
| No safety gate before live sends | --dry-run prints full payload and exits with zero API calls. Required habit before any send. |
| Key scope confusion | Inline comment in v4() explains why write-capable key is required. Older keys documented as read-only. |
# Full send — content + subject + preserved schedule python3 kit_send.py \ --broadcast-id 23982498 \ --subject "Enter to win + bring a friend" \ --html-file email_body.html # Dry run before committing python3 kit_send.py --broadcast-id 23982498 \ --subject "Test" --html-file email.html --dry-run # Content only (schedule auto-preserved) python3 kit_send.py --broadcast-id 23982498 \ --content-only --html-file email.html
Inject into Chrome DevTools or via the Claude Chrome MCP while on any Kit campaign page. Encodes the correct approach for all Kit UI edge cases discovered during Q2 sends — React state handling, modal side effects, datepicker interaction, off-screen inputs.
Exposes three functions that handle the full send workflow when UI access is required (sequences, custom templates, edge cases where API isn't available):
// Paste this file into DevTools console first, then: await Kit.insertContent(html); // React-safe, no DOM hacks await Kit.setSender('michele@sperrytreecare.com'); // Dropdown via native setter await Kit.setSchedule('2026-05-06T13:00:00Z'); // Unschedule → pencil → picker
Friction items encoded: #4 (React state), #5 (modal side effect), #6 (Unicode corruption), #7 (schedule edit flow), #8 (auto-save), #9 (ref vs coordinate clicks), #12 (scrollIntoView).
Two browser-injectable scripts for WordPress issues logged during WS1 and WS4 setup. Parametric — set config variables at the top and run from any WP admin console.
Rewrote to reflect V4 reality: new flags documented (--dry-run, --clear-schedule, --content-only), send_at preservation behavior explained, HTML preprocessing section added, UI inject and WP helpers referenced with usage examples.
The friction log at sperry-kit-friction-apr2026.jason-8ce.workers.dev was updated twice this session — items 13 and 14 added, summary counts updated, workflow section updated with a green banner pointing to kit_send.py as the preferred path.
| # | Issue | Status |
|---|---|---|
| 1 | PUT clears send_at on scheduled broadcasts | Fixed in kit_send.py |
| 2 | tel: links rejected by Kit editor | Stripped in preprocessor |
| 3 | Kit API blocked from Claude sandbox (exit 56) | kit_send.py runs on Mac via Desktop Commander |
| 4–9 | Kit UI React / Slate.js / datepicker edge cases | Encoded in kit_ui_inject.js |
| 10 | CF7 Add Action AJAX broken | cf7_add_redirect.js built |
| 11 | WP Webhooks Pro drops duplicated CF7 forms | webhooks_add_form.js built |
| 12 | elementFromPoint fails off-screen | scrollIntoView in kit_ui_inject.js |
| 13 | Kit content API broken (V3 and early V4) | Root cause: key scope. Resolved with new key. |
| 14 | Kit UI schedule / API send_at sync uncertainty | Caution documented — verify with --preview before sends |
Pre-implementation backups saved to /tmp/friction-rollback/. Restore script is executable and verified — line count checked on restore.
bash /tmp/friction-rollback/restore.sh — restores both files, verifies line counts, prints pass/fail per file.
Note: /tmp is cleared on reboot. Copy to a permanent location if rollback window needs to extend past this session.
| Item | Priority | Notes |
|---|---|---|
| SOW vs. actual status report | High | Pending since session start. Covers all 4 workstreams vs. Q2 SOW commitments. |
| Email 5 (ID 23982498) | Sending tomorrow | Scheduled May 6 6am PDT. Do not touch. Verify Kit UI shows correct schedule before EOD. |
| WS4 automation build | Overdue | Kit sequences + Zapier. Paste guide exists. Sequences need content via Kit UI. |
| Phase Zero design | Jun 1 | Deadline Jun 1 2026. |
| Meta campaign | Blocked | Awaiting Rob/Michele approval on creative. |