Host-side notification delivery service for the MiniApp Sandbox.
Meymar is part of the sandbox (host) infrastructure. It is the sandbox's equivalent of Farcaster's built-in notification system. Miniapps never interact with meymar directly — they receive a notificationDetails.url from the host that happens to point here.
meymar is NOT equivalent to Neynar. Neynar is a miniapp-developer-side service. Meymar is host-side infrastructure. See NOTIFICATIONS.md for the full architecture.
- Stores notification tokens in SQLite (generated by the sandbox host)
- Receives notifications from miniapps (via the
notificationDetails.urlthe host gave them) - Validates tokens and enforces rate limits
- Broadcasts notifications to the sandbox UI via SSE
Sandbox Host (:3100) meymar (:3200) MiniApp (:5173)
│ │ │
│ 1. User enables notifs │ │
│ in miniapp │ │
│ │ │
│── registers token ──────>│ │
│ POST /webhook │ 2. Stores token │
│ │ │
│── returns to miniapp ────────────────────────────── >│
│ { url: meymar/api/..., │ │
│ token: "abc-123" } │ │
│ │ │
│ Also forwards webhook │ │
│ to miniapp's own ────────────────────────────────> │
│ webhookUrl (if set) │ │
│ │ │
│ │ 3. MiniApp sends notif │
│ │<── POST /api/miniapps-notifications
│ │ (title, body, tokens) │
│ │ │
│<── SSE event ────────────│ 4. Broadcasts via SSE │
│ (notification appears) │ │
Key points:
- The sandbox host calls meymar's
/webhookto register tokens — miniapps don't call this directly - The sandbox gives miniapps
notificationDetails.urlpointing to meymar's/api/miniapps-notifications - Miniapps POST notifications to that URL using their token — they don't know it's meymar
- The miniapp's
webhookUrl(in their manifest) points to their OWN backend, not to meymar
pnpm install
pnpm devServer starts at http://localhost:3200.
Called by the sandbox host (not by miniapps) to register notification tokens.
{
"event": "miniapp_added",
"notificationDetails": {
"url": "http://localhost:3200/api/miniapps-notifications",
"token": "abc-123"
}
}Events: miniapp_added, miniapp_removed, notifications_enabled, notifications_disabled
Farcaster-compatible notification ingestion endpoint. This is the URL miniapps POST to when sending notifications (the url from notificationDetails they received from the host).
{
"notificationId": "unique-id",
"title": "Notification Title",
"body": "Notification body text",
"targetUrl": "http://localhost:5173/",
"tokens": ["token-1", "token-2"]
}Response:
{
"result": {
"successfulTokens": ["token-1"],
"invalidTokens": [],
"rateLimitedTokens": []
}
}Convenience endpoint — automatically uses all stored active tokens. Useful for testing.
{
"title": "Hello!",
"body": "Notification body",
"targetUrl": "http://localhost:5173/",
"notificationId": "optional-id",
"fids": [0]
}Only title, body, and targetUrl are required. notificationId auto-generates if omitted. fids filters by user; omit to send to all.
Server-Sent Events stream. The sandbox UI connects here to receive real-time notifications.
List stored notification tokens. Useful for debugging.
Query params: ?status=active ?fid=0
Health check with uptime, active token count, and connected SSE clients.
| Variable | Default | Description |
|---|---|---|
MEYMAR_PORT |
3200 |
Server port |
MEYMAR_DB_PATH |
./data/meymar.db |
SQLite database file path |
| Local (meymar) | Production (Farcaster) |
|---|---|
POST /webhook |
Farcaster's internal token registration |
POST /api/miniapps-notifications |
Farcaster's notification ingestion endpoint |
POST /send |
Your app server calling the notification URL |
| SQLite tokens table | Farcaster's production database |
| SSE broadcast | Push notifications to user's device |
In production, the host (Farcaster) handles all of this internally. Miniapp code stays the same — it just POSTs to whatever notificationDetails.url the host provides.