Add marketing tool API endpoints#78
Conversation
Document the server config shape in a committed sample file, since the real pushServerConfig.json is gitignored. Copy it to pushServerConfig.json and fill in real values to run the server.
Expose two endpoints under /marketing that mirror the send-message and push-marketing CLI commands, so the marketing team can send a test push and then a marketing blast without developer help. Authentication uses the API keys in the push database (getApiKeyByKey), matching the v2 routes: every request must carry a valid x-api-key header. The push-marketing send is scoped to the devices registered under that key, then by location, via a new apiKeyLocation view. Each blast gets a generated campaignId echoed in the notification data (with an optional deep-link url) so the app can report opens to analytics.
Give a test-message push a fixed 'test' campaign id and an optional deep-link url, so tapping a test opens the same marketing path in the app (navigation plus open tracking) while its analytics stay distinguishable from real campaigns.
| } | ||
|
|
||
| if (loginId != null) { | ||
| await sender.sendToLogin(loginId, message) |
There was a problem hiding this comment.
🔒 Agentic Security Review
Severity: HIGH
/marketing/api/test-message authenticates that an API key exists but does not verify that the supplied loginId (or alternate deviceId path) belongs to that authenticated key before sending. This allows a caller with any valid key to target identifiers outside its own API-key scope.
Impact: Cross-tenant unauthorized push delivery is possible if attacker-controlled requests include known or guessable target identifiers.
Reviewed by Cursor Security Reviewer for commit e87a23a. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e87a23a. Configure here.
| key.push(part) | ||
| } | ||
| return key | ||
| } |
There was a problem hiding this comment.
City ignored without region
Medium Severity
When a blast request includes country and city but omits region, makeApiKeyLocationStartKey stops after country, so CouchDB returns every device under that API key in the country instead of the requested city. The UI can still print the city in the log line.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit e87a23a. Configure here.


CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none
Description
Adds a self-serve marketing push API to edge-push-server so the marketing team can send notifications without developer help. The web UI is hosted separately in the internal-tools project, which proxies these endpoints.
Two endpoints under
/marketing, mirroring thesend-messageandpush-marketingCLI commands:POST /marketing/api/test-message— a single test push to a device or login.POST /marketing/api/push-marketing— an audience blast, streaming its progress log back to the caller.Key points:
x-api-key, looked up in the API-key database viagetApiKeyByKey(same source as the v2 routes). No shared secret.apiKeyLocationCouchDB view.campaignIdechoed in the notificationdata(with an optional deep-linkurl), so the app can report opens to analytics. Test pushes carry a fixedtestcampaign id so they exercise the same navigation + tracking path while staying distinguishable from real campaigns.pushServerConfig.sample.jsondocumenting the config shape (the real config is gitignored).Note
Medium Risk
New HTTP surface can trigger real marketing blasts to many devices; mitigated by API-key lookup and per-key device scoping, but misconfiguration or key compromise could still cause broad sends.
Overview
Adds a
/marketingExpress router (mounted before Serverlet) so internal tools can trigger pushes without the CLI.POST /marketing/api/test-messagesends one test notification to adeviceIdorloginId, with optional title/body/url and a fixedcampaignIdoftestfor analytics.POST /marketing/api/push-marketingmirrors the marketing blast flow: requiresconfirmed, optional geo filters, streams plain-text progress (device count, campaign UUID, heartbeat, per-device errors), and sends marketing payloads with a generatedcampaignIdand optional deep link.Both routes authenticate via
x-api-key(getApiKeyByKey). Audience targeting is scoped to devices for that API key using a new CouchapiKeyLocationview pluscountDevicesByApiKeyLocation/streamDevicesByApiKeyLocation(prefix keys by country/region/city). Also addspushServerConfig.sample.jsonand a CHANGELOG unreleased note.Reviewed by Cursor Bugbot for commit e87a23a. Bugbot is set up for automated code reviews on this repo. Configure here.