Skip to content

Commit d7f72ce

Browse files
committed
update notifications with common formatting
1 parent 4a66be0 commit d7f72ce

1 file changed

Lines changed: 120 additions & 222 deletions

File tree

docs/mini-apps/core-concepts/notifications.mdx

Lines changed: 120 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -3,255 +3,153 @@ title: Notifications
33
description: Send push notifications to users who saved your Mini App
44
---
55

6-
Base app enables developers to send notifications to users who have added the mini app and enabled notifications.
76

8-
9-
## Overview
10-
11-
[**Neynar**](https://neynar.com/) is the easiest infrastructure platform for building Farcaster mini apps on Base and provides a simple way to:
12-
13-
- manage approved notification tokens, no need to store on developer side
14-
- send notifications in a single API call, no need to batch
15-
- automate handling of notification permission revokes, and mini app "remove" events
16-
- target notifications to specific user cohorts
17-
- send notifications using the dev portal without having to write code
18-
- track notification analytics including open rates
19-
20-
Mini app analytics will automatically populate in the Dev Portal dashboard once you use Neynar for notifications.
21-
22-
## Set up Notifications
23-
24-
<Steps>
25-
<Step title="Set up Neynar Developer Account">
26-
If you don't have a Neynar developer account yet, sign up for free [here](https://neynar.com).
27-
28-
This tutorial uses these two APIs:
29-
- [Send notifications](https://docs.neynar.com/reference/publish-frame-notifications)
30-
- [List of frame notification tokens](https://docs.neynar.com/reference/fetch-notification-tokens)
31-
</Step>
32-
33-
<Step title="Locate the Neynar Frame Events Webhook URL">
34-
The Neynar mini app events webhook URL is on the Neynar app page. Navigate to [dev.neynar.com/app](https://dev.neynar.com/app) and then click on the app.
35-
36-
It should be in this format: `https://api.neynar.com/f/app/<your_client_id>/event`
37-
38-
See the highlighted URL in the image below.
39-
40-
**NEED IMAGE**
41-
42-
</Step>
43-
44-
<Step title="Set the Webhook URL in Your Mini App Manifest">
45-
Frame servers must provide a JSON manifest file on their domain at the well-known URI, for example `https://your-frame-domain.com/.well-known/farcaster.json`.
46-
47-
Set the Neynar frame events URL as the `webhookUrl` in the Frame Config object inside the manifest. Here's an example manifest:
48-
49-
```json
50-
{
51-
"accountAssociation": {
52-
"header": "eyJmaWQiOjE5MSwidHlwZSI6ImN1c3RvZHkiLCJrZXkiOiIweDNhNmRkNTY5ZEU4NEM5MTgyOEZjNDJEQ0UyMGY1QjgyN0UwRUY1QzUifQ",
53-
"payload": "eyJkb21haW4iOiIxYmNlLTczLTcwLTE2OC0yMDUubmdyb2stZnJlZS5hcHAifQ",
54-
"signature": "MHg1ZDU1MzFiZWQwNGZjYTc5NjllNDIzNmY1OTY0ZGU1NDMwNjE1YTdkOTE3OWNhZjE1YjQ5M2MxYWQyNWUzMTIyM2NkMmViNWQyMjFhZjkxYTYzM2NkNWU3NDczNmQzYmE4NjI4MmFiMTU4Y2JhNGY0ZWRkOTQ3ODlkNmM2OTJlNDFi"
55-
},
56-
"frame": {
57-
"version": "4.2.0",
58-
"name": "Your Frame Name",
59-
"iconUrl": "https://your-frame-domain.com/icon.png",
60-
"splashImageUrl": "https://your-frame-domain.com/splash.png",
61-
"splashBackgroundColor": "#f7f7f7",
62-
"homeUrl": "https://your-frame-domain.com",
63-
"webhookUrl": "https://api.neynar.com/f/app//event"
7+
## Implementation
8+
9+
1. Create a webhook server to handle webhook events. This can be done at `app/api/webhook/route.ts`
10+
2. Add the Webhook URL to your manifest file
11+
3. Use the `useMiniApp()` hook to prompt users to add your Mini App
12+
4. Save the `token` and `url` from the webhook event to a database for later use
13+
5. Send notifications by sending a `POST` request to the `url` associated with the user's `token`
14+
15+
16+
<Panel>
17+
```ts app/api/webhook/route.ts
18+
export async function POST(request: NextRequest) {
19+
const requestJson = await request.json();
20+
21+
// Parse and verify the webhook event
22+
let data;
23+
try {
24+
data = await parseWebhookEvent(requestJson, verifyAppKeyWithNeynar);
25+
} catch (e: unknown) {
26+
// Handle verification errors (invalid data, invalid app key, etc.)
27+
// Return appropriate error responses with status codes 400, 401, or 500
28+
}
29+
30+
const fid = data.fid;
31+
const event = data.event;
32+
33+
// Handle different event types
34+
switch (event.event) {
35+
case "miniapp_added":
36+
// Save notification details and send welcome notification
37+
if (event.notificationDetails) {
38+
await setUserNotificationDetails(fid, event.notificationDetails);
39+
await sendFrameNotification({
40+
fid,
41+
title: "Welcome to Frames v2",
42+
body: "Mini app is now added to your client",
43+
});
6444
}
65-
}
66-
```
67-
68-
<Warning>
69-
Frame manifest caching: Farcaster clients might have your mini app manifest cached and would only get updated on a periodic basis.
70-
</Warning>
71-
72-
<Tip>
73-
If you're using Farcaster to test, you can go to their Settings > Developer Tools > Domains, put in your Frame URL and hit the Check domain status to force a refresh.
74-
</Tip>
75-
</Step>
76-
</Steps>
77-
78-
## Prompt Users to Add Your Mini App
79-
80-
<Steps>
81-
<Step title="Install @neynar/react">
82-
Install the Neynar React package to access the Mini App components:
83-
84-
```shell
85-
npm install @neynar/react
86-
```
87-
</Step>
88-
89-
<Step title="Set up the MiniAppProvider Context Provider">
90-
Wrap your app with the `MiniAppProvider` component to enable Mini App functionality:
91-
92-
```javascript
93-
import { MiniAppProvider } from '@neynar/react';
94-
95-
export default function App() {
96-
return (
97-
<MiniAppProvider>
98-
{/* Your app components */}
99-
</MiniAppProvider>
100-
);
101-
}
102-
```
103-
</Step>
104-
105-
<Step title="Prompt the User to Add Your Mini App">
106-
Use the `useMiniApp` hook to prompt users to add your Mini App:
107-
108-
```javascript
109-
import { useMiniApp } from '@neynar/react';
110-
111-
export default function HomePage() {
112-
const { isSDKLoaded, addMiniApp } = useMiniApp();
113-
114-
const handleAddMiniApp = async () => {
115-
if (!isSDKLoaded) return;
116-
117-
const result = await addMiniApp();
118-
if (result.added && result.notificationDetails) {
119-
// Mini app was added and notifications were enabled
120-
console.log('Notification token:', result.notificationDetails.token);
121-
}
122-
};
123-
124-
return (
125-
<button onClick={handleAddMiniApp}>
126-
Add Mini App
127-
</button>
128-
);
129-
}
130-
```
131-
132-
The result type is:
133-
134-
```ts
135-
export type FrameNotificationDetails = {
136-
url: string;
137-
token: string;
138-
};
139-
140-
export type AddFrameResult =
141-
| {
142-
added: true;
143-
notificationDetails?: FrameNotificationDetails;
144-
}
145-
| {
146-
added: false;
147-
reason: 'invalid_domain_manifest' | 'rejected_by_user';
148-
};
149-
```
150-
</Step>
151-
</Steps>
152-
153-
If `added` is **true** and `notificationDetails` is a valid object, then the client should have called POST to the Neynar frame events webhook URL with the same details.
154-
155-
Neynar will manage all mini app add/remove & notifications enabled/disabled events delivered on this events webhook.
156-
157-
#### Alternative: Using the Mini App SDK directly
158-
159-
If you prefer to use the Mini App SDK directly instead of the Neynar React components:
160-
161-
```shell
162-
yarn add @farcaster/frame-sdk
45+
break;
46+
47+
case "miniapp_removed":
48+
// Delete notification details
49+
await deleteUserNotificationDetails(fid);
50+
break;
51+
52+
case "notifications_enabled":
53+
// Save new notification details and send confirmation
54+
await setUserNotificationDetails(fid, event.notificationDetails);
55+
await sendFrameNotification({
56+
fid,
57+
title: "Ding ding ding",
58+
body: "Notifications are now enabled",
59+
});
60+
break;
61+
62+
case "notifications_disabled":
63+
// Delete notification details
64+
await deleteUserNotificationDetails(fid);
65+
break;
66+
}
67+
68+
return Response.json({ success: true });
69+
}
16370
```
71+
</Panel>
16472

165-
Then prompt the user:
73+
### Notification Schema
16674

167-
```ts
168-
import sdk from "@farcaster/frame-sdk";
75+
#### Send Notification Request Schema
76+
| Property | Type | Required | Description | Constraints |
77+
|----------------|----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
78+
| notificationId | string | Yes | Identifier that is combined with the FID to form an idempotency key. When the user opens the Mini App from the notification this ID will be included in the context object. | Maximum length of 128 characters |
79+
| title | string | Yes | Title of the notification. | Max length 32 characters |
80+
| body | string | Yes | Body of the notification. | Max length 128 characters. |
81+
| targetUrl | string | Yes | URL to open when the user clicks the notification. | Max length 1024 characters.<br />Must be on the same domain as the Mini App. |
82+
| tokens | string[] | Yes | Array of notification tokens to send to. | Max 100 tokens. |
16983

170-
const result = await sdk.actions.addFrame();
171-
```
17284

173-
## Send Notifications to Users
85+
#### Send Notification Response Schema
17486

175-
Notifications can be broadcast to all your mini app users with notifications enabled or to a limited set of FIDs. Notifications can also be filtered so that only users meeting certain criteria receive the notification.
17687

177-
The `target_fids` parameter is the starting point for all filtering. Pass an empty array for `target_fids` to start with the set of all FIDs with notifications enabled for your app, or manually define `target_fids` to list specific FIDs.
88+
| Property | Type | Required | Description |
89+
|-------------------|----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
90+
| successfulTokens | string[] | Yes | Tokens for which the notification succeeded. |
91+
| invalidTokens | string[] | Yes | Tokens which are no longer valid and should never be used again. This could happen if the user disabled notifications but for some reason the Mini App server has no record of it. |
92+
| rateLimitedTokens | string[] | Yes | Tokens for which the rate limit was exceeded. The Mini App server can try later. |
17893

179-
<Steps>
180-
<Step title="Target Users via the Neynar Dev Portal">
181-
The [Neynar dev portal](https://dev.neynar.com) offers the same functionality as the API for broadcasting notifications. Navigate to your app and click the "Mini App" tab. Once your mini app is configured with your Neynar webhook URL and users have enabled notifications for your mini app, you'll see a "Broadcast Notification" section with an expandable filters section.
18294

183-
**Neynar mini app Broadcast Notification panel**
184-
</Step>
18595

186-
<Step title="Target Users via the API">
187-
The following example uses the [@neynar/nodejs-sdk](https://github.com/neynarxyz/nodejs-sdk) to send notifications to users and includes a set of filtering criteria:
96+
## Events
18897

189-
```ts
190-
const targetFids = []; // target all relevant users
191-
const filters = {
192-
exclude_fids: [420, 69], // do not send to these FIDs
193-
following_fid: 3, // only send to users following this FID
194-
minimum_user_score: 0.5, // only send to users with score >= this value
195-
near_location: { // only send to users near a certain point
196-
latitude: 34.052235,
197-
longitude: -118.243683,
198-
radius: 50000, // distance in meters from the lat/log point (optional, defaults to 50km)
199-
}
200-
};
98+
Mini App events use the following object structure:
20199

202-
const notification = {
203-
title: "🪐",
204-
body: "It's time to savor farcaster",
205-
target_url: "https://your-frame-domain.com/notification-destination",
206-
};
207-
208-
client.publishFrameNotifications({ targetFids, filters, notification }).then((response) => {
209-
console.log("response:", response);
210-
});
211-
```
100+
* **`type`**: notification event type
101+
* **`notificationDetails.url`**: URL that the app should call to send a notification.
102+
* **`notificationDetails.token`**: A secret token generated by the Farcaster App and shared with the Notification Server. A token is unique for each (Farcaster Client, Mini App, user Fid) tuple.
212103

213-
Additional documentation on the API and its body parameters can be found at [publish-frame-notifications](https://docs.neynar.com/reference/publish-frame-notifications).
214-
</Step>
215-
</Steps>
104+
The optional `notificationDetails` object provides the `token` and `url` if the client equates adding to enabling notifications.
216105

217-
### Step 4: Check analytics
106+
### `miniapp_added`
218107

219-
Notification analytics will automatically show in your developer portal once you start using Neynar for frame notifications.
108+
Sent when the user adds the Mini App to their Farcaster client (whether or not it was triggered by an `addMiniApp()` prompt).
220109

221-
<Frame>
222-
<img src="/images/docs/b963bd7c8e35263317ab6e0d1354dee4b854b471587c4ad827342ed7b83d2218-image.png" alt="Notification analytics" />
223-
</Frame>
224110

225-
When using the `MiniAppProvider` context provider, you'll get additional analytics including notification open rates.
226-
<Frame>
227-
<img src="/images/docs/notification-campaigns.png" alt="Notification open analytics" />
228-
</Frame>
229111

230-
## FAQ
112+
```json miniapp_added_payload.json
113+
{
114+
"event": "miniapp_added",
115+
"notificationDetails": {
116+
"url": "https://api.farcaster.xyz/v1/frame-notifications",
117+
"token": "a05059ef2415c67b08ecceb539201cbc6"
118+
}
119+
}
120+
```
231121

232-
<AccordionGroup>
233-
<Accordion title="How do I determine if the user has already added my Frame?">
122+
### `miniapp_removed`
234123

235-
When using the `MiniAppProvider` context provider, you can check the `context` object from the `useMiniApp()` hook which contains the `added` boolean and `notificationDetails` object. More details in [Frame Core Types](https://github.com/farcasterxyz/frames/blob/main/packages/frame-core/src/types.ts#L58-L62)
236-
</Accordion>
124+
Sent when a user removes the Mini App, which means that any notification tokens for that FID and client app (based on signer requester) should be considered invalid:
237125

238-
<Accordion title="What happens if I send a notification via API to a user who has revoked notification permission?">
239126

240-
To avoid getting rate-limited by mini app clients, Neynar will filter out sending notifications to disabled tokens.
241-
</Accordion>
127+
```json miniapp_removed_payload
128+
{
129+
"event": "miniapp_removed"
130+
}
131+
```
242132

243-
<Accordion title="How do I fetch the notification tokens, URLs, and their status?">
133+
### `notifications_enabled`
244134

245-
The [fetch notification tokens API](/reference/fetch-notification-tokens) provides access to the underlying data.
246-
</Accordion>
135+
Sent when a user enables notifications (e.g. after disabling them). The payload includes a new `token` and `url`:
136+
137+
```json notifications_enabled_payload
138+
{
139+
"event": "notifications_enabled",
140+
"notificationDetails": {
141+
"url": "https://api.farcaster.xyz/v1/frame-notifications",
142+
"token": "a05059ef2415c67b08ecceb539201cbc6"
143+
}
144+
}
145+
```
247146

248-
<Accordion title="How do I debug notifications?">
249-
Check out this tutorial: [Debug Notifications](https://docs.neynar.com/docs/debug-notifications)
250-
</Accordion>
147+
### `notifications_disabled`
251148

252-
<Accordion title="Who should I reach out to if I am experiencing additional issues or have questions?">
253-
Reach out to the [Neynar Community Slack channel](https://neynar.com/slack)
254-
</Accordion>
149+
Sent when a user disables notifications from, e.g., a settings panel in the client app. Any notification tokens for that FID and client app (based on signer requester) should be considered invalid:
255150

256-
</AccordionGroup>
151+
```json notifications_disabled_json
152+
{
153+
"event": "notifications_disabled"
154+
}
257155
```

0 commit comments

Comments
 (0)