Neynar Documentation

Documentation Index

Fetch the complete documentation index at: /llms.txt

Use this file to discover all available pages before exploring further.

Reference: Notifications Spec

Mini Apps can send notifications to users who have added the Mini App to their Farcaster client and enabled notifications. An in-app notification is sent to a user and launches them into the app

Overview

At a high-level notifications work like so:

Terms

To make our life easier, let’s call:

A notification token is basically a permission that a Farcaster client gives your app (on behalf of a user) to send them notifications.

Steps

  1. Listen for events
    You’ll need a notification server to receive webhook events and a database to store notification tokens for users:

  2. Add your webhook URL in farcaster.json
    If you haven’t already, follow the Publishing your app guide to host a farcaster.json on your app’s domain. Define the webhookUrl property in your app’s configuration in farcaster.json:

{
  "accountAssociation": {
    "header": "eyJmaWQiOjU0NDgsInR5cGUiOiJjdXN0b2R5Iiwia2V5IjoiMHg2MWQwMEFENzYwNjhGOEQ0NzQwYzM1OEM4QzAzYUFFYjUxMGI1OTBEIn0",
    "payload": "eyJkb21haW4iOiJleGFtcGxlLmNvbSJ9",
    "signature": "MHg3NmRkOWVlMjE4OGEyMjliNzExZjUzOTkxYTc1NmEzMGZjNTA3NmE5OTU5OWJmOWFmYjYyMzAyZWQxMWQ2MWFmNTExYzlhYWVjNjQ3OWMzODcyMTI5MzA2YmJhYjdhMTE0MmRhMjA4MmNjNTM5MTJiY2MyMDRhMWFjZTY2NjE5OTFj"
  },
  "miniapp": {
    "version": "1",
    "name": "Example App",
    "iconUrl": "https://example.com/icon.png",
    "homeUrl": "https://example.com",
    "imageUrl": "https://example.com/image.png",
    "buttonTitle": "Check this out",
    "splashImageUrl": "https://example.com/splash.png",
    "splashBackgroundColor": "#eeccff",
    "webhookUrl": "https://example.com/api/webhook"
  }
}

For a real example, this is Yoink’s manifest: https://yoink.party/.well-known/farcaster.json

  1. Get users to add your app
    For a Mini App to send notifications, it needs to first be added by a user to their Farcaster client and for notifications to be enabled. Use the addMiniApp action while a user is using your app to prompt them to add it:

  2. Caution
    The addMiniApp() action only works when your app is deployed to its production domain (matching your manifest). It will not work with tunnel domains during development.

  3. Save the notification tokens
    When notifications are enabled, the Farcaster client generates a unique notification token for the user. This token is sent to webhookUrl defined in your farcaster.json along with a url that the app should call to send a notification. The token and url need to be securely saved to the database so they can be looked up when you want to send a notification to a particular user.

  4. Send a notification
    Once you have a notification token for a user, you can send them a notification by sending a POST request to the url associated with that token.

If you are sending the same notification to multiple users, you batch up to a 100 sends in a single request by providing multiple tokens. You can safely use the same notificationId for all batches.

The body of that request must match the following JSON schema:

Property Type Required Description Constraints
notificationId string Yes Identifier that is combined with the FID to form an idempotency key. Maximum length of 128 characters
title string Yes Title of the notification. Max length 32 characters
body string Yes Body of the notification. Max length 128 characters
targetUrl string Yes URL to open when the user clicks the notification. Max length 1024 characters. Must be on the same domain as the Mini App.
tokens string[] Yes Array of notification tokens to send to. Max 100 tokens.

The targetUrl hostname must exactly match the domain your Mini App is registered on. Subdomains matter: if your app is on example.com, using www.example.com in the targetUrl will cause a target_url_mismatch error and the affected tokens will be permanently invalidated.

The server should respond with an HTTP 200 OK and the following JSON body:

Property Type Required Description
successfulTokens string[] Yes Tokens for which the notification succeeded.
invalidTokens string[] Yes Tokens which are no longer valid and should never be used again.
rateLimitedTokens string[] Yes Tokens for which the rate limit was exceeded. Mini App server can try later.

When a user clicks the notification, the Farcaster client will:

export type MiniAppLocationNotificationContext = {
  type: 'notification';
  notification: {
    notificationId: string;
    title: string;
    body: string;
  };
};

Avoid duplicate notifications

To avoid duplicate notifications, specify a stable notificationId for each notification you send. This identifier is joined with the FID (e.g. (fid, notificationId) to create a unique key that is used to deduplicate requests to send a notification over a 24 hour period. For example, if you want to send a daily notification to users you could use daily-reminder-05-06-2024 as your notificationId. Now you can safely retry requests to send the daily reminder notifications within a 24 hour period.

Rate Limits

Host servers may impose rate limits per token. The standard rate limits, which are enforced by Warpcast, are:

Receiving webhooks

Users can add and configure notification settings Mini Apps within their Farcaster client. When this happens Farcaster clients will send events to your server that include data relevant to the event. This allows your app to:

Events

miniapp_added

Sent when the user adds the Mini App to their Farcaster client (whether or not this was triggered by an addMiniApp() prompt). The optional notificationDetails object provides the token and url if the client equates adding to enabling notifications (Warpcast does this).

Payload
{
  "event": "miniapp_added",
  "notificationDetails": {
    "url": "https://api.farcaster.xyz/v1/frame-notifications",
    "token": "a05059ef2415c67b08ecceb539201cbc6"
  }
}

miniapp_removed

Sent when a user removes a mini app, which means that any notification tokens for that fid and client app (based on signer requester) should be considered invalid:

Payload
{
  "event": "miniapp_removed"
}

notifications_disabled

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:

Payload
{
  "event": "notifications_disabled"
}

notifications_enabled

Sent when a user enables notifications (e.g. after disabling them). The payload includes a new token and url:

Payload
{
  "event": "notifications_enabled",
  "notificationDetails": {
    "url": "https://api.farcaster.xyz/v1/frame-notifications",
    "token": "a05059ef2415c67b08ecceb539201cbc6"
  }
}

Handling events

Farcaster clients will POST events to the webhookUrl specified in your farcaster.json. Your endpoint should:

If your app doesn’t respond with a 200, the Farcaster client will attempt to re-send the event. The exact number of retries is up to each client.

Verifying events

Events are signed by the app key of a user with a JSON Farcaster Signature. This allows Mini Apps to verify the Farcaster client that generated the notification and the Farcaster user they generated it for. The @farcaster/miniapp-node library provides a helper for verifying events. To use it, you’ll need to supply a validation function that can check the signatures against the latest Farcaster network state. An implementation that uses Neynar is provided. You can sign up and get an API key on their free tier. Make sure to set NEYNAR_API_KEY environment variable.

Example

const requestJson = "base64encodeddata";

import {
  ParseWebhookEvent,
  parseWebhookEvent,
  verifyAppKeyWithNeynar,
} from "@farcaster/miniapp-node";

try {
  const data = await parseWebhookEvent(requestJson, verifyAppKeyWithNeynar);
} catch (e: unknown) {
  const error = e as ParseWebhookEvent.ErrorType;

switch (error.name) {
    case "VerifyJsonFarcasterSignature.InvalidDataError":
    case "VerifyJsonFarcasterSignature.InvalidEventDataError":
      // The request data is invalid
    case "VerifyJsonFarcasterSignature.InvalidAppKeyError":
      // The app key is invalid
    case "VerifyJsonFarcasterSignature.VerifyAppKeyError":
      // Internal error verifying the app key (caller may want to try again)
  }
}

Reference implementation

For a complete example, check out the Mini App V2 Demo which has all of the above: