# Add a Verification Source: https://docs.neynar.com/docs/add-a-verification Walkthrough on how to add an ethereum or solana address as a verification to a user's farcaster profile > [!TIP] > > * If you want to integrate Farcaster auth for your users, easiest way to start is [Sign in with Neynar](/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn) (Neynar pays all onchain fee) > * If you want dedicated signers for your user or bot, simplest to clone this [example app](https://github.com/neynarxyz/farcaster-examples/tree/main/managed-signers) for quickstart This guide demonstrates adding an Ethereum address verification with the Neynar SDK. Check out this [Getting Started Guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey:process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); const signer = process.env.NEYNAR_SIGNER; ``` Then like a cast: ```javascript Javascript theme={"system"} const hash = "0x6932a9256f34e18892d498abb6d00ccf9f1c50d6"; client.publishReaction({ signerUuid: signer, reactionType: "like", target:hash }); ``` Recasting works the same way, just replace "like" with "recast": ```javascript Javascript theme={"system"} const hash = "0x6932a9256f34e18892d498abb6d00ccf9f1c50d6"; client.publishReaction({ signerUuid: signer, reactionType: "like", target:hash }); ``` The end result should look like this: ```javascript Javascript theme={"system"} { success: true; } ``` To verify that the reaction was published, you can fetch the cast's reactions: ```javascript Javascript theme={"system"} const types = ReactionsType.All; const reactions = await client.fetchCastReactions({ hash, types: [types] }); console.log(reactions); ``` Which would print out ```json theme={"system"} { "result": { "casts": [ { "type": "like", "hash": "0x691fabb3fc58bd4022d4358b2bc4f44469ad959a", "reactor": { "fid": "4640", "username": "picture", "displayName": "Picture", "pfp": { "url": "https://lh3.googleusercontent.com/erYudyT5dg9E_esk8I1kqB4bUJjWAmlNu4VRnv9iUuq_by7QjoDtZzj_mjPqel4NYQnvqYr1R54m9Oxp9moHQkierpY8KcYLxyIJ" }, "followerCount": "45", "followingCount": "57" }, "timestamp": "2023-12-10T15:26:45.000Z", "castHash": "0x6932a9256f34e18892d498abb6d00ccf9f1c50d6" } ], "next": { "cursor": null } } } ``` That's it! You can now like or recast any cast on Farcaster. PS - to learn more about how writes technically work on Farcaster, read [Write to Farcaster with Neynar Managed Signers](/docs/write-to-farcaster-with-neynar-managed-signers) > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Address User Score Contract Source: https://docs.neynar.com/docs/address-user-score-contract Get user quality score for an address connected to a Farcaster profile. > [!NOTE] > > Read prior context on user scores in [Neynar User Quality Score](/docs/neynar-user-quality-score) User scores are particularly useful if anonymous addresses are interacting with your contract and you want to restrict interaction to high quality addresses. Neynar already supports user quality scores offchain (read more in [Neynar User Quality Score](/docs/neynar-user-quality-score)), this brings them onchain and makes it available to smart contracts. Now, on the Base Mainnet and Sepolia testnet, smart contracts can query the fid linked to any ETH address and the quality score for that FID. ## Contract | **Chain** | **Address** | **Deploy Transaction** | | - | - | - | | Base Mainnet | 0xd3C43A38D1D3E47E9c420a733e439B03FAAdebA8 | 0x059259c15f660a4b5bd10695b037692654415f60e13569c7a06e99cfd55a54b0 | | Base Sepolia | 0x7104CFfdf6A1C9ceF66cA0092c37542821C1EA50 | 0xfdf68b600f75b4688e5432442f266cb291b9ddfe2ec05d2fb8c7c64364cf2c73 | * Read the Proxy Contract on the Base Explorer ([link](https://basescan.org/address/0xd3C43A38D1D3E47E9c420a733e439B03FAAdebA8#readProxyContract)). This is the upgradeable proxy contract you should use. * User score code on the Base Explorer ([link](https://basescan.org/address/0xd3C43A38D1D3E47E9c420a733e439B03FAAdebA8#code)). This is an upgradeable implementation contract. There is no state here. This is the code that the proxy contract is currently using. ## Interface ```solidity Solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.26; interface INeynarUserScoresReader { function getScore(address verifier) external view returns (uint24 score); function getScoreWithEvent(address verifier) external returns (uint24 score); function getScores(address[] calldata verifiers) external view returns (uint24[] memory scores); function getScore(uint256 fid) external view returns (uint24 score); function getScoreWithEvent(uint256 fid) external returns (uint24 score); function getScores(uint256[] calldata fids) external view returns (uint24[] memory scores); } ``` If the `getScore` call returns `0`there is no user score for that address. If you can spare the gas and would like us to know that you are using our contract, please use `getScoreWithEvent`. ## Sample use A simple example of a HelloWorld contract: ```solidity Solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.26; interface INeynarUserScoresReader { function getScore(address verifier) external view returns (uint24 score); function getScoreWithEvent(address verifier) external returns (uint24 score); function getScores(address[] calldata verifiers) external view returns (uint24[] memory scores); function getScore(uint256 fid) external view returns (uint24 score); function getScoreWithEvent(uint256 fid) external returns (uint24 score); function getScores(uint256[] calldata fids) external view returns (uint24[] memory scores); } contract HelloWorld { INeynarUserScoresReader immutable verifications; constructor(INeynarUserScoresReader _userScores) { userScores = _userScores; } function requireHighScore() public view returns (uint256) { uint256 score = userScores.getScoreWithEvent(msg.sender); if (score < 950000) { revert("!top 5% percentile account"); } return score; } } ``` ## Future This experiment will see what we can unlock by bringing more Farcaster data on-chain. If you build something using this, please [reach out](mailto:support@ns.com). We want to hear what you're building and see how we can make it easier. ## Further reading > [card] [Sybil resistance using Neynar user quality score](/docs/neynar-user-quality-score) > [card] [Addresses ↔ Farcaster FIDs onchain](/docs/verifications-contract) # Create Farcaster Frames Using Neynar & Frog Source: https://docs.neynar.com/docs/analytics-frame-neynar-frog In this guide, we’ll learn how to make a frame with the neynar SDK and Frog.fm, within a few minutes! For this demo, it will be a simple rock-paper-scissors game but it will give you an idea of how to create multi-page frames, interact with buttons, and get analytics for your frame with no extra effort. Before we begin, you can access the [complete source code](https://github.com/avneesh0612/rps-frames) for this guide on GitHub. Let's get started! ## Creating a new frames project We will use vercel and [frog](https://frog.fm/) for building the frame in this guide, but feel free to use anything else as well! Enter this command in your terminal to create a new app: ```powershell PowerShell theme={"system"} bunx create-frog -t vercel ``` Enter a name for your project and it will spin up a new project for you. Once the project is created install the dependencies: ```powershell PowerShell theme={"system"} cd bun install ``` ### Creating the frame home page Head over to the `api/index.ts` file. Here, you'll see a starter frame on the / route. But first, let's change the Frog configuration to use `/api` as the base path and use neynar for hubs like this: ```typescript index.tsx theme={"system"} export const app = new Frog({ assetsPath: "/", basePath: "/api", hub: neynar({ apiKey: 'NEYNAR_FROG_FM' }) }); ``` > [!NOTE] > > ### Make sure to update the API key to your API key to get analytics You also need to import neynar from "frog/hubs": ```typescript index.tsx theme={"system"} import { neynar } from "frog/hubs"; ``` Now, change the frame on the `/` route to match this: ```typescript index.tsx theme={"system"} app.frame("/", (c) => { return c.res({ action: "/result", image: (
Choose your weapon
), intents: [ , , , ], }); }); ``` This will render an image saying choose your weapon and three buttons saying rock paper and scissors. When any of these buttons are clicked a request to the `/result` route is made which we define in the `action` parameter. ![Frame home page](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/da59b22-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=8e28d8babd6c2dcd81219c5ca34f8aff) > [!NOTE] > > ### Make sure that you sign in using your warpcast account, so that the requests can be validated Now, let's build the `/result` route like this: ```typescript index.tsx theme={"system"} app.frame("/result", (c) => { const rand = Math.floor(Math.random() * 3); const choices = ["rock", "paper", "scissors"]; const userChoice = choices[(c.buttonIndex || 1) - 1]; const computerChoice = choices[rand]; let msg = ""; if (userChoice === computerChoice) { msg = "draw"; } if ( (userChoice === "rock" && computerChoice === "scissors") || (userChoice === "paper" && computerChoice === "rock") || (userChoice === "scissors" && computerChoice === "paper") ) { msg = "You win"; } if ( (userChoice === "rock" && computerChoice === "paper") || (userChoice === "paper" && computerChoice === "scissors") || (userChoice === "scissors" && computerChoice === "rock") ) { msg = "You lose"; } return c.res({ image: (
{userChoice} vs {computerChoice}
{msg}
), intents: [], }); }); ``` Here, we first get the button index from the request and use it to get the user's choice. We have then added some logic for generating a random choice for the game. Then, in the image we display the two choices and the result of the game. We have also added a play again game which simply pushes the user to the `/` route. ![Frame result page](https://files.catbox.moe/qvr7pt.gif) ## Analytics Since we are using neynar hub with Frog, we also get analytics out of the box. Head over to the usage tab and click on the frame that you are currently using. It will provide you with various analytics like total interactors, interactions per cast, etc. ![Frame analytics](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/713fe83-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=da3d7ed1857d3ffc193a2c1a9870caf9) ## Deployment You can deploy your frame using the [vercel CLI](https://vercel.com/docs/cli) like this: ```powershell PowerShell theme={"system"} bun run deploy ``` Alternatively, you can also create a new GitHub repository and sync your vercel deployments. ## Conclusion This guide taught us how to create a rock-paper-scissors game on Farcaster frames! If you want to look at the completed code, check out the [GitHub repository](https://github.com/avneesh0612/rps-frames). Lastly, make sure to sure what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar) and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Mini App Hosts Notifications Source: https://docs.neynar.com/docs/app-host-notifications Token management and inbound webhook handling for apps that host mini apps ## Overview Farcaster client applications can host mini apps. Allowing those mini apps to push notifications to users requires the host to generate user notifcation tokens, send signed messages to the mini-apps webhook, and accept notifications via webhook. Neynar provides a system to manage this complexity on behalf of the client app (mini app host). ## Basic flow In the following example we'll imagine * "Hostcaster" - a Farcaster client that wants to support mini apps * "Cowclicker" - a mini app ### Enabling Notfications 1. User wants to enable notifications for Cowclicker 2. Hostcaster posts a "notifications\_enabled" event to Neynar 3. Neynar generates a unique token, bundles it in a signed message, and posts it to Clowclicker's webhook URL ### Sending Notifications 1. Cowclicker wants to send a notification to a Hostcaster user for whom they have a valid notification token 2. Cowclicker sends the notification event to Neynar's webhook including the token 3. Neynar validates the token, hydrates some data, and queues the notification for Hostcaster 4. Hostcaster listens to a Kafka topic and/or polls an API for a user's notifications ## Message signing An event sent to a mini-app webhook must be a signed JFS messages. There are two supported signing approaches in this system. Hostcaster can sign the message with a user's key if they have the ability to do so. Or, if Hostcaster instead uses the Neynar-hosted signer system then they can provide their signer\_uuid when posting the unsigned event. ### Neynar-signed route This approach requires only one API request: 1. [POST /v2/farcaster/app\_host/user/event](/reference/app-host-post-event) with a valid signer\_uuid and the required event data ```bash cURL theme={"system"} curl --request POST \ --url https://api.neynar.com/v2/farcaster/app_host/user/event \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_KEY' \ --data '{ "signer_uuid": "01973000-b000-ee00-e0e0-0ee0e00e00ee", "app_domain": "cowclicker.gov", "fid": 10101, "event": "notifications_enabled" }' ``` ### Self-sign route 1. [GET /v2/farcaster/app\_host/user/event](/reference/app-host-get-event) to retrieve the message to be signed. This is particularly important for `notification_enabled` events because this is when a notification token is generated by Neynar 2. Sign the message, serialize the entire bundle per JFS spec 3. [POST /v2/farcaster/app\_host/user/event](/reference/app-host-post-event) with the signed message ```bash cURL theme={"system"} curl --request GET \ --url 'https://api.neynar.com/v2/farcaster/app_host/user/event?app_domain=cowclicker.gov&fid=10101&event=notifications_enabled' \ --header 'x-api-key: YOUR_KEY' ``` ...Sign and serialize the message... ```bash cURL theme={"system"} curl --request POST \ --url https://api.neynar.com/v2/farcaster/app_host/user/event \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_KEY' \ --data '{ "app_domain": "cowclicker.gov", "signed_message": "eyJmaWZXkifQ==.eyJleI1Mjd9.nx1CPzKje4N2Bw====" }' ``` ## Notifications via Kafka To access your stream of notifications from a Kafka queue you'll need to work with a Neynar representative to obtain the necessary credentials. You will be granted access to a Kafka topic with a name like `app_host_notifications_Your_App_Name` which contains a message for each notification. If an app sends the same message to 100 users, you will see 100 messages in Kafka similar to the example below. Sample message: ```json JSON theme={"system"} { "notification": { "notification_id": "rate-test-1755216706-10", "title": "Sample Notification Test 10", "body": "This is notification message body", "target_url": "https://test.com/notification/10" }, "app_domain": "demo.neynar.com", "notification_neynar_id": "0198ab11-7c61-xxxx-xxxx-0affb68fda66", "fid": "18949", "timestamp": "2025-08-15T00:11:46.657Z", "app": { "name": "Wownar", "icon_url": "https://demo.neynar.com/logos/neynar.svg" } } ``` ## Further Reading * [Sending Notifications](https://miniapps.farcaster.xyz/docs/guides/notifications#send-a-notification) from the Farcaster mini app guide * [JSON Farcaster Signatures](https://github.com/farcasterxyz/protocol/discussions/208) # Host Farcaster Mini Apps — Overview Source: https://docs.neynar.com/docs/app-host-overview A guide to hosting Farcaster Mini Apps: understand concepts, implement core features, and integrate with your Farcaster client. ## What are Farcaster Mini Apps? Farcaster Mini Apps are web applications that render inside Farcaster clients, providing interactive experiences beyond traditional social media posts. Think of them as mini websites that users can interact with directly within their Farcaster feed. ## What does it mean to host Mini Apps? As a Farcaster client developer, hosting Mini Apps means: * **Embedding Mini Apps** in your client interface (typically as webviews) * **Managing the app lifecycle** (launch, navigation, close) * **Providing wallet integration** so Mini Apps can access user's crypto wallets * **Implementing authentication** to verify user identity to Mini Apps * **Handling notifications** when Mini Apps want to alert users ## Essential implementation areas Review and plan to implement each of these core host capabilities from the [Farcaster Mini Apps specification](https://miniapps.farcaster.xyz/docs/specification), particularly the [SDK section](https://miniapps.farcaster.xyz/docs/specification#sdk): ### 🔐 Authentication & Identity * **[Sign In with Farcaster (SIWF)](https://github.com/farcasterxyz/protocol/discussions/110)** - Let Mini Apps authenticate users * Use [Sign In with Neynar (SIWN)](/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn) for rapid development without managing your own signers * **Auth address generation** - Create approved addresses for secure interactions ([Neynar tutorial](/docs/auth-address-signature-generation)) * **[User context](https://miniapps.farcaster.xyz/docs/sdk/context)** - Provide user profile and social graph data ### 💰 Wallet Integration * **[Ethereum provider injection](https://miniapps.farcaster.xyz/docs/sdk/wallet)** - EIP-1193 compatible wallet access ([EIP-1193 spec](https://eips.ethereum.org/EIPS/eip-1193)) * **[Solana provider](https://miniapps.farcaster.xyz/docs/sdk/solana)** (experimental) - Multi-chain wallet support * **Transaction signing** - Secure transaction flows within Mini Apps ### 🎨 Host UI & UX * **[App surface rendering](https://miniapps.farcaster.xyz/docs/specification#app-surface)** - Webview management with proper sizing (424x695px web, device dimensions mobile) * **Header display** - Show Mini App name and author * **Splash screens** - Loading states with custom branding * **Navigation controls** - Launch, close, and navigation between Mini Apps * **Official libraries**: [React Native](https://github.com/farcasterxyz/miniapps/tree/main/packages/miniapp-host-react-native) | [Web](https://github.com/farcasterxyz/miniapps/tree/main/packages/miniapp-host) ### 🔔 Notification System * **Token management** - Generate and manage notification tokens per user ([Neynar implementation guide](/docs/app-host-notifications)) * **Event handling** - Process Mini App lifecycle events (added, removed, notifications enabled/disabled) using the [Mini App Notifications API](/reference/app-host-post-event) * **Push delivery** - Display notifications to users in your client ### 📱 Mini App Discovery * **App catalog** - Browse available Mini Apps ([Mini App Catalog API](/reference/fetch-frame-catalog)) * **Search functionality** - Help users find relevant Mini Apps ([Search Mini Apps API](/reference/search-frames)) * **Installation flow** - Let users add Mini Apps to their client ## Using the Mini App Host Packages The Farcaster team provides official host packages that handle the communication layer between your app and Mini Apps. These packages implement the [SDK specification](https://miniapps.farcaster.xyz/docs/specification#sdk) which defines how hosts and Mini Apps communicate via a `postMessage` channel. ### Available packages | Package | Platform | Description | | - | - | - | | [`@farcaster/miniapp-host`](https://github.com/farcasterxyz/miniapps/tree/main/packages/miniapp-host) | Web | Host Mini Apps in browser-based applications | | [`@farcaster/miniapp-host-react-native`](https://github.com/farcasterxyz/miniapps/tree/main/packages/miniapp-host-react-native) | React Native | Host Mini Apps in mobile applications | ### SDK communication Mini Apps communicate with their host using the [JavaScript SDK](https://miniapps.farcaster.xyz/docs/specification#sdk). The SDK facilitates communication over a `postMessage` channel available in iframes and mobile WebViews. Your host implementation must respond to the following SDK actions: **Core actions:** * `addMiniApp` - Prompts the user to add the Mini App * `close` - Closes the Mini App * `composeCast` - Prompt the user to cast * `ready` - Hides the splash screen (Mini App signals it's loaded) * `signin` - Prompts the user to Sign In with Farcaster * `openUrl` - Open an external URL * `viewProfile` - View a Farcaster profile * `viewCast` - View a specific cast **Wallet actions:** * `swapToken` - Prompt the user to swap tokens * `sendToken` - Prompt the user to send tokens * `viewToken` - View a token **Provider access:** * `getEthereumProvider` - EIP-1193 Ethereum Provider * `getSolanaProvider` - Experimental Solana provider ### App surface requirements When rendering a Mini App, your host must: 1. **Display a header** above the Mini App showing the name and author from the [manifest](https://miniapps.farcaster.xyz/docs/specification#manifest) 2. **Show a splash screen** during loading using the icon and background from the manifest or embed meta tags 3. **Set proper dimensions** - 424x695px for web, device dimensions for mobile 4. **Render in a vertical modal** - Mini Apps should appear as overlays ## Next steps 1. **Start with authentication** - Implement [SIWF](https://github.com/farcasterxyz/protocol/discussions/110) or use [SIWN](/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn) for quick setup 2. **Build the UI surface** - Create webview container with [proper sizing and navigation](https://miniapps.farcaster.xyz/docs/specification#app-surface) 3. **Add wallet integration** - Inject [Ethereum provider](https://miniapps.farcaster.xyz/docs/sdk/wallet) for transaction support 4. **Add discovery** - Integrate [catalog](/reference/fetch-frame-catalog) and [search](/reference/search-frames) APIs for Mini App browsing 5. **Enable notifications** - Implement [token management and event handling](/docs/app-host-notifications) ## Additional resources * **[Farcaster Mini Apps Specification](https://miniapps.farcaster.xyz/docs/specification)** - Complete technical specification * **[SDK Specification](https://miniapps.farcaster.xyz/docs/specification#sdk)** - Communication protocol between hosts and Mini Apps * **[Mini App SDK Documentation](https://miniapps.farcaster.xyz/docs/sdk/changelog)** - JavaScript SDK for Mini App developers * **[Manifest Specification](https://miniapps.farcaster.xyz/docs/specification#manifest)** - Mini App metadata format for discovery and notifications * **[Examples Repository](https://github.com/farcasterxyz/miniapps/tree/main/examples)** - Reference implementations # Appendix Source: https://docs.neynar.com/docs/appendix Understanding the new user registration process Neynar simplifies the account creation process on Farcaster by pre-registering several new accounts and securely storing their credentials in a database. For detailed instructions on how to register or create an account on Farcaster, please refer to the [Farcaster account creation guide.](https://docs.farcaster.xyz/developers/guides/accounts/create-account#create-an-account) When a developer requests a new account—Step 1—Neynar provides the FID (Farcaster ID) of a pre-registered account to the developer. To transfer the pre-registered account to the end user, a specific process must be followed. This approach is chosen because Neynar does not store or manage the end user's account mnemonic for security reasons. The developer is required to generate a signature using the FID of the pre-registered account and the custody address of the end user. This can be achieved by utilizing the `signTransfer` method described in the script—Step 2. Upon making a `POST - /v2/farcaster/user` request, two significant actions are initiated: 1. **Fname Transfer** * This step is executed only when the fname is included in the request body. Developers have the option to collect the fname from the user during account creation. * If the developer decides not to collect the fname initially, the transfer can be conducted at a later stage. For more information, consult the [guide on fname transfer](https://docs.farcaster.xyz/reference/fname/api#register-or-transfer-an-fname) 2. **Account transfer** * This step outlines the procedure for transferring the account to the end user. Detailed guidance on account transfer can be found in the [Farcaster account transfer documentation.](https://docs.farcaster.xyz/reference/contracts/reference/id-registry#transferfor) # Archive Casts Source: https://docs.neynar.com/docs/archiving-casts-with-neynar Archiving Farcaster data with Neynar Casts in the Farcaster protocol are pruned when user runs out of storage. This guide demonstrates how to archive casts of a specific FID with the Neynar SDK. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. Check out this [example repository](https://github.com/neynarxyz/farcaster-examples/tree/main/archiver-script) to see the code in action. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey:process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Next, let's make a function to clean the incoming casts: ```javascript Javascript theme={"system"} const parser = (cast) => { return { fid: parseInt(cast.author.fid), parentFid: parseInt(cast.parentAuthor.fid) ? parseInt(cast.parentAuthor.fid) : undefined, hash: cast.hash || undefined, threadHash: cast.threadHash || undefined, parentHash: cast.parentHash || undefined, parentUrl: cast.parentUrl || undefined, text: cast.text || undefined, }; }; ``` Then, the function to archive the casts: ```javascript Javascript theme={"system"} const dumpCast = (cast) => { const parsed = parser(cast); const data = `${JSON.stringify(parsed)}\n`; fs.appendFileSync("data.ndjson", data); }; ``` Finally, let's fetch the casts and archive them: ```javascript Javascript theme={"system"} const fetchAndDump = async (fid, cursor) => { const data = await client.fetchCastsForUser({fid, limit: 150, ...(cursor && cursor.trim() !== "" ? { cursor } : {}), }); data.result.casts.map(dumpCast); // If there is no next cursor, we are done if (data.result.next.cursor === null) return; await fetchAndDump(fid, data.result.next.cursor); }; // archive all @rish.eth's casts in a file called data.ndjson const fid = 194; fetchAndDump(fid); ``` Result: a file called `data.ndjson` with all the casts of the user with FID 194. It looks something like this: ```json JSON theme={"system"} {"fid":194,"parentFid":3,"hash":"0x544421c091f5af9d1610de0ae223b52602dd631e","threadHash":"0xb0758588c9412f72efe7e703e9d0cb5f2d0a6cfd","parentHash":"0xb0758588c9412f72efe7e703e9d0cb5f2d0a6cfd","text":"that order is pretty key"} {"fid":194,"parentFid":194,"hash":"0x98f52d36161f3d0c8dee6e242936c431face35f0","threadHash":"0x5727a985687c10b6a37e9439b2b7a3ce141c6237","parentHash":"0xcb6cab80cc7d7a2ca957d1c95c9a3459f9e3a9dc","text":"turns out not an email spam issue 😮‍💨, email typo :)"} {"fid":194,"parentFid":20071,"hash":"0xcb6cab80cc7d7a2ca957d1c95c9a3459f9e3a9dc","threadHash":"0x5727a985687c10b6a37e9439b2b7a3ce141c6237","parentHash":"0xf34c18b87f8eaca2cb72131a0c0429a48b66ef52","text":"hmm interesting. our system shows the email as sent. Maybe we're getting marked as spam now? 🤦🏽‍♂️\n\nLet me DM you on telegram"} {"fid":194,"parentFid":20071,"hash":"0x62c484064c9ca1177f8addb56bdaffdbede97a29","threadHash":"0x5727a985687c10b6a37e9439b2b7a3ce141c6237","parentHash":"0x7af582a591575acc474fa1f8c52a2a03258986b9","text":"are you still waiting on this? you should have gotten the email within the first minute. we automated this last week so there's no wait anymore. lmk if you're still having issues :)"} {"fid":194,"parentFid":3,"hash":"0xbc63b955c40ace8aca4b1608115fd12f643395b1","threadHash":"0x5727a985687c10b6a37e9439b2b7a3ce141c6237","parentHash":"0x5727a985687c10b6a37e9439b2b7a3ce141c6237","text":"@bountybot adding 150 USDC to this bounty \n\nfor anyone building on this, please reach out with any questions. We've always wanted to do this but haven't been able to prioritize. Think this can be quite impactful! :)"} ``` That's it! You now can save that in S3 or IPFS for long-term archival! > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Auth Address Signature Generation Source: https://docs.neynar.com/docs/auth-address-signature-generation Generate a Signed Key Request using viem for registering auth addresses in Farcaster with Neynar This guide walks you through generating a Signed Key Request using [viem](https://viem.sh/) that need to be passed in while [registering auth address](https://docs.neynar.com/reference/register-signed-key-for-developer-managed-auth-address) ## System & Installation Requirements ### Prerequisites * Node.js >= **18.x** (LTS recommended) * npm >= **9.x** OR yarn >= **1.22.x** [Download and install node (if not installed)](https://nodejs.org/en/download) ### Initialize project (optional) ```bash theme={"system"} mkdir signed-key-request cd signed-key-request npm init -y ``` ### Install `viem` ```bash theme={"system"} npm install viem ``` OR with yarn: ```bash theme={"system"} yarn add viem ``` *** ## Code Breakdown and Steps **You can find full code at the [end of this guide](#full-final-code).** **1. Import the required functions** The code starts by importing the necessary libraries: ```javascript Javascript theme={"system"} import { encodeAbiParameters } from "viem"; import { mnemonicToAccount, generateMnemonic, english } from "viem/accounts"; ``` **2. Generate a random mnemonic and derive the auth address** Generates a mnemonic and converts it to an Ethereum address (`auth_address`) ```javascript Javascript theme={"system"} const mnemonic = generateMnemonic(english); const auth_address_acc = mnemonicToAccount(mnemonic); const auth_address = auth_address_acc.address; ``` **3. Define EIP-712 domain** Describes the EIP-712 domain (context of signature). ```javascript Javascript theme={"system"} const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = { name: "Farcaster SignedKeyRequestValidator", version: "1", chainId: 10, verifyingContract: "0x00000000fc700472606ed4fa22623acf62c60553", }; ``` **4. Define the EIP-712 message structure** Defines the structure of the message to be signed. ```javascript Javascript theme={"system"} const SIGNED_KEY_REQUEST_TYPE = [ { name: "requestFid", type: "uint256" }, { name: "key", type: "bytes" }, { name: "deadline", type: "uint256" }, ]; ``` **5. Encode the auth\_address** Encodes `auth_address` as **32 bytes**. ```javascript Javascript theme={"system"} const key = encodeAbiParameters( [{ name: "auth_address", type: "address" }], [auth_address] ); ``` **6. App details** Replace `"MNEMONIC_HERE"` with your app mnemonic phrase and fid with your app's fid. ```javascript Javascript theme={"system"} const fid = 0; const account = mnemonicToAccount("MNEMONIC_HERE"); ``` **7. Define a deadline** Sets a 24-hour expiration time. ```javascript Javascript theme={"system"} const deadline = Math.floor(Date.now() / 1000) + 86400; ``` **8. Sign the EIP-712 message** Signs the message per EIP-712 standard. ```javascript Javascript theme={"system"} const signature = await account.signTypedData({ domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN, types: { SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE, }, primaryType: "SignedKeyRequest", message: { requestFid: BigInt(fid), key, deadline: BigInt(deadline), }, }); ``` **9. Create a sponsor signature** If you want to sponsor the auth address, you can sign the EIP-712 signature again with a basic Ethereum signature. [This route](https://docs.neynar.com/reference/register-signed-key-for-developer-managed-auth-address) needs to be sponsored if not provided then neynar will sponsor it for you and you will be charged in credits. ```javascript Javascript theme={"system"} const sponsorSignature = await account.signMessage({ message: { raw: signature }, }); ``` **10. Print output** Prints useful values for further use. ```javascript Javascript theme={"system"} console.log("auth_address", auth_address); console.log("app_fid", fid); console.log("signature", signature); console.log("deadline", deadline); console.log("sponsor.signature", sponsorSignature); console.log("sponsor.fid", fid); ``` **11. Run the code** Save the code in a file, e.g., `generateSignedKeyRequest.js`, and run it using Node.js: ```bash Bash theme={"system"} node generateSignedKeyRequest.js ``` **12. cURL** Use the generated values to make a cURL request to register the auth address. Replace `` with your actual API key and `` with your redirect URL(if needed). ```bash Bash theme={"system"} curl --request POST \ --url https://api.neynar.com/v2/farcaster/auth_address/developer_managed/signed_key/ \ --header 'Content-Type: application/json' \ --header 'x-api-key: ' \ --data '{ "address": "0x5a927ac639636e534b678e81768ca19e2c6280b7", "app_fid": 3, "deadline": 123, "signature": "0x16161933625ac90b7201625bfea0d816de0449ea1802d97a38c53eef3c9c0c424fefbc5c6fb5eabe3d4f161a36d18cda585cff7e77c677c5d34a9c87e68ede011c", "redirect_url": "", "sponsor": { "fid": 3, "signature": "", "sponsored_by_neynar": true } }' ``` *** ## Full Final Code ```javascript Javascript theme={"system"} import { encodeAbiParameters } from "viem"; import { mnemonicToAccount, generateMnemonic, english } from "viem/accounts"; (async () => { const mnemonic = generateMnemonic(english); const auth_address_acc = mnemonicToAccount(mnemonic); const auth_address = auth_address_acc.address; const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = { name: "Farcaster SignedKeyRequestValidator", version: "1", chainId: 10, verifyingContract: "0x00000000fc700472606ed4fa22623acf62c60553", }; const SIGNED_KEY_REQUEST_TYPE = [ { name: "requestFid", type: "uint256" }, { name: "key", type: "bytes" }, { name: "deadline", type: "uint256" }, ]; const key = encodeAbiParameters( [{ name: "auth_address", type: "address" }], [auth_address] ); const fid = 0; const account = mnemonicToAccount("MNEMONIC_HERE"); const deadline = Math.floor(Date.now() / 1000) + 86400; const signature = await account.signTypedData({ domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN, types: { SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE, }, primaryType: "SignedKeyRequest", message: { requestFid: BigInt(fid), key, deadline: BigInt(deadline), }, }); const sponsorSignature = await account.signMessage({ message: { raw: signature }, }); console.log("auth_address", auth_address); console.log("app_fid", fid); console.log("signature", signature); console.log("deadline", deadline); console.log("sponsor.signature", sponsorSignature); console.log("sponsor.fid", fid); })(); ``` *** Enjoy building! 🚀 For additional help, [feel free to contact us](mailto:support@ns.com). # Create Farcaster Account Autonomously (For AI Agents) Source: https://docs.neynar.com/docs/autonomous-farcaster-agent Complete guide for AI agents to create Farcaster accounts and post casts without human intervention. Register FID, add signer keys, set up profile, and post using x402 micropayments. > [!NOTE] > > This guide is for **AI agents** that need to create Farcaster accounts and post casts autonomously. If you're a human developer creating a bot, see [Create Farcaster Bot (UI)](/docs/create-farcaster-bot-ui) instead. This toolkit allows an AI agent to: 1. Create a new Farcaster account (register an FID) 2. Add a signer key for posting 3. Post casts to the network 4. Set up a profile with username, bio, and profile picture All operations are fully programmatic - no Farcaster app or manual steps required. ## Prerequisites * Node.js 18+ * **\~\$1 of ETH or USDC** on any major chain (Ethereum, Optimism, Base, Arbitrum, or Polygon) The toolkit handles bridging and swapping automatically. ## Quick Start **1. Clone the Repository** ```bash bash theme={"system"} git clone https://github.com/rishavmukherji/farcaster-agent.git cd farcaster-agent npm install ``` **2. Generate Wallet and Request Funding** If you don't have a funded wallet, create one: ```javascript Node.js theme={"system"} const { Wallet } = require('ethers'); const wallet = Wallet.createRandom(); console.log('Address:', wallet.address); console.log('Private Key:', wallet.privateKey); ``` Then ask your human: > "I've created a wallet. Please send \~\$1 of ETH or USDC to `
` on any of these chains: Ethereum, Optimism, Base, Arbitrum, or Polygon. Let me know when done." **Save the private key securely** - you need it for all subsequent steps. **3. Run Auto-Setup** Once funded, run the complete setup: ```bash bash theme={"system"} PRIVATE_KEY=0x... node src/auto-setup.js "Your first cast text here" ``` This will: 1. Detect which chain has funds (ETH or USDC) 2. Bridge/swap to get ETH on Optimism and USDC on Base 3. Register your FID (Farcaster ID) 4. Add a signer key 5. Wait for hub synchronization 6. Post your first cast 7. **Automatically save credentials** to persistent storage **4. Credentials are Saved Automatically** Credentials are automatically saved to: * `~/.openclaw/farcaster-credentials.json` (if OpenClaw is installed) * `./credentials.json` (fallback) > [!WARNING] > > Credentials are stored as **plain text JSON** with restricted file permissions. Anyone with access to these files can control both the wallet funds and the Farcaster account. For production use, implement your own secure storage solution. You can manage credentials via CLI: ```bash bash theme={"system"} # List all stored accounts node src/credentials.js list # Get credentials for active account node src/credentials.js get # Show credentials file path node src/credentials.js path ``` To disable auto-save, use `--no-save`: ```bash bash theme={"system"} PRIVATE_KEY=0x... node src/auto-setup.js "Your cast" --no-save ``` ## Posting Additional Casts Load credentials from storage and post: ```javascript Node.js theme={"system"} const { postCast, loadCredentials } = require('./src'); // Load saved credentials const creds = loadCredentials(); const { hash, verified } = await postCast({ privateKey: creds.custodyPrivateKey, signerPrivateKey: creds.signerPrivateKey, fid: Number(creds.fid), text: 'Your cast content' }); console.log('Cast URL: https://farcaster.xyz/~/conversations/' + hash); ``` ```bash bash theme={"system"} # Or via CLI with environment variables PRIVATE_KEY=0x... SIGNER_PRIVATE_KEY=... FID=123 node src/post-cast.js "Hello Farcaster!" ``` ## Setting Up Profile Set username, display name, bio, and profile picture: ```bash bash theme={"system"} PRIVATE_KEY=0x... SIGNER_PRIVATE_KEY=... FID=123 npm run profile myusername "Display Name" "My bio" "https://example.com/pfp.png" ``` ```javascript Node.js theme={"system"} const { setupFullProfile } = require('./src'); await setupFullProfile({ privateKey: '0x...', signerPrivateKey: '...', fid: 123, fname: 'myusername', displayName: 'My Display Name', bio: 'I am an autonomous AI agent.', pfpUrl: 'https://api.dicebear.com/7.x/bottts/png?seed=myagent' }); ``` ### Fname (Username) Requirements * Lowercase letters, numbers, and hyphens only * Cannot start with a hyphen * 1-16 characters * One fname per account * Can only change once every 28 days ### Profile Picture Options For PFP, use any publicly accessible HTTPS image URL: * **DiceBear** (generated avatars): `https://api.dicebear.com/7.x/bottts/png?seed=yourname` * IPFS-hosted images * Any public image URL ## How It Works ### Step 1: FID Registration (Optimism) Farcaster IDs are registered on Optimism via the `IdGateway` contract at `0x00000000Fc25870C6eD6b6c7E41Fb078b7656f69`. ```javascript Node.js theme={"system"} const { registerFid } = require('./src'); const { fid } = await registerFid(privateKey); ``` ### Step 2: Adding a Signer Key (Optimism) Farcaster requires a "Signed Key Request" to add signer keys. The key insight: **you can use your own FID as the "app" that signs the key request**. Since you control the custody address, you can self-sign. ```javascript Node.js theme={"system"} const { addSigner } = require('./src'); const { signerPrivateKey } = await addSigner(privateKey); ``` > [!WARNING] > > You **must** use the `SignedKeyRequestValidator.encodeMetadata()` contract function. Manual ABI encoding doesn't work because the struct encoding includes a dynamic offset pointer. ### Step 3: x402 Micropayments Neynar's hub requires x402 payments (0.001 USDC per call on Base). The payment uses EIP-3009 `transferWithAuthorization` - a gasless signature-based USDC transfer: ```javascript Node.js theme={"system"} const paymentPayload = { x402Version: 1, scheme: 'exact', network: 'base', payload: { signature: eip712Signature, authorization: { from: walletAddress, to: '0xA6a8736f18f383f1cc2d938576933E5eA7Df01A1', // Neynar value: '1000', // 0.001 USDC validAfter: '0', validBefore: deadlineTimestamp, nonce: randomBytes32Hex } } }; const header = Buffer.from(JSON.stringify(paymentPayload)).toString('base64'); ``` ## Contract Addresses ### Optimism | Contract | Address | | - | - | | IdGateway | `0x00000000Fc25870C6eD6b6c7E41Fb078b7656f69` | | IdRegistry | `0x00000000Fc6c5F01Fc30151999387Bb99A9f489b` | | KeyGateway | `0x00000000fC56947c7E7183f8Ca4B62398CaAdf0B` | | SignedKeyRequestValidator | `0x00000000FC700472606ED4fA22623Acf62c60553` | ### Base | Contract | Address | | - | - | | USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | ### Neynar Hub API (`https://hub-api.neynar.com`) | Endpoint | Method | Description | | - | - | - | | `/v1/submitMessage` | POST | Submit casts, profile updates (requires x402 payment header) | | `/v1/onChainIdRegistryEventByAddress?address=` | GET | Check if FID is synced for address | | `/v1/onChainSignersByFid?fid=` | GET | Check if signer keys are synced | ### Neynar REST API (`https://api.neynar.com`) | Endpoint | Method | Description | | - | - | - | | `/v2/farcaster/cast?identifier=&type=hash` | GET | Verify cast exists on network | ### Farcaster Fname Registry (`https://fnames.farcaster.xyz`) | Endpoint | Method | Description | | - | - | - | | `/transfers` | POST | Register or transfer fname (requires EIP-712 signature) | | `/transfers/current?name=` | GET | Check fname availability (404 = available) | ### x402 Payment | Item | Value | | - | - | | Payment Address | `0xA6a8736f18f383f1cc2d938576933E5eA7Df01A1` | | Cost per API Call | 0.001 USDC (on Base) | | Header | `X-PAYMENT` with base64-encoded EIP-3009 signature | ## Cost Breakdown | Operation | Chain | Approximate Cost | | - | - | - | | FID Registration | Optimism | \~\$0.20 | | Add Signer | Optimism | \~\$0.05 | | ETH to USDC Swap | Base | \~\$0.05 | | Bridge (Across) | Various | \~\$0.10-0.20 | | x402 API Call | Base | \$0.001 | | **Total Minimum** | | **\~\$0.50** | Budget \$1 to have buffer for retries and gas fluctuations. ## FAQs/Troubleshooting > [accordion] "invalid hash" error > > **Cause:** Using @farcaster/hub-nodejs version \< 0.15.9. The Farcaster protocol updated how message hashes are computed. > > **Fix:** > > ```bash bash theme={"system"} > npm install @farcaster/hub-nodejs@latest > ``` > > Verify you have 0.15.9+ installed: > > ```bash bash theme={"system"} > npm list @farcaster/hub-nodejs > ``` > [accordion] "unknown fid" error > > **Cause:** Hub hasn't synced your FID yet. On-chain registration happened but the hub hasn't indexed it. > > **Fix:** Use Neynar hub (well-synced) instead of public hubs. Neynar syncs within seconds; other hubs can be millions of FIDs behind. Wait 30-60 seconds and retry. > [accordion] Transaction reverts when adding signer > > **Cause:** Manually encoding SignedKeyRequest metadata. > > **Fix:** Use `SignedKeyRequestValidator.encodeMetadata()` contract function. The struct needs a dynamic offset pointer that manual encoding misses. > [accordion] "Failed to verify payment" (x402) > > **Cause:** Wrong payload structure. > > **Fix:** Ensure: > > * `x402Version` is a number (1), not string > * `payload.authorization` object is present (not `payload.txHash`) > * All authorization values are strings > [accordion] "fname is not registered for fid" > > **Cause:** Trying to set username in hub before the fname registry has synced. > > **Fix:** Wait 30-60 seconds after registering with `fnames.farcaster.xyz` before submitting the `UserDataAdd` message. The `registerFname()` function handles this automatically. ## Programmatic API All functions are available for import: ```javascript Node.js theme={"system"} const { // Full autonomous setup autoSetup, checkAllBalances, // Core functions registerFid, addSigner, postCast, swapEthToUsdc, // Profile setup setProfileData, registerFname, setupFullProfile, // Utilities checkFidSync, checkSignerSync, getCast } = require('./src'); ``` ## OpenClaw Skill This toolkit is also available as an OpenClaw skill for autonomous agents: ```bash bash theme={"system"} npx clawhub@latest install farcaster-agent ``` ## Source Code Full implementation available at: [github.com/rishavmukherji/farcaster-agent](https://github.com/rishavmukherji/farcaster-agent) > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Build Interactive Farcaster Frames with Neynar Source: https://docs.neynar.com/docs/building-frames Learn how to build interactive Farcaster frames 100x faster using Neynar's Frame Studio, no-code templates, and comprehensive Frame APIs. Create dynamic social experiences with validation, hosting, and embedding capabilities. Neynar supports building frames in a few different ways: * [Neynar Frame Studio](https://neynar.com/nfs): allows building frames with no code and pre-made templates * Frame APIs: * [Validating frame actions](/reference/validate-frame-action), user and cast data in one API call * [CRUD](/reference/publish-neynar-frame) for hosted frames * Embedding frames in your client and [posting actions](/reference/post-frame-action) ## Neynar Frame architecture ### Pages for a frame A page represents the most basic unit of a frame. A frame consists of one or more pages. We've thoughtfully crafted a JSON format for a frames' page to abstract away building on the rapidly moving foundation of Farcaster Frames. *For context, Frames were launched a few weeks before writing this and have already seen 3-to 4 spec upgrades. We don't want Neynar developers to have to worry about handling those.* Here's an example of a single page with four buttons. You can create these in the Frame Studio with no code or quickly create frames using our REST APIs. ```Text JSON theme={"system"} { "uuid": "5ec484f5-efaf-4bda-9a3f-0579232a386a", "image": "https://i.imgur.com/gpn83Gm.png", "title": "Farcaster Dev Call", "buttons": [ { "title": "Notes", "next_page": { "redirect_url": "https://warpcast.notion.site/Feb-1st-934e190578144aba8273b2bbdc29e5ab" }, "action_type": "post_redirect" }, { "title": "Calendar", "next_page": { "redirect_url": "https://calendar.google.com/calendar/u/0/r?cid=NjA5ZWM4Y2IwMmZiMWM2ZDYyMTkzNWM1YWNkZTRlNWExN2YxOWQ2NDU3NTA3MjQwMTk3YmJlZGFjYTQ3MjZlOEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t" }, "action_type": "post_redirect" }, { "title": "Zoom", "next_page": { "redirect_url": "https://zoom.us/j/98052336425?pwd=aFYyRk9ZSDhqR1h5eVJENmtGSGo4UT09#success" }, "action_type": "post_redirect" }, { "title": "Recordings", "next_page": { "redirect_url": "https://www.youtube.com/playlist?list=PL0eq1PLf6eUeZnPtyKMS6uN9I5iRIlnvq" }, "action_type": "post_redirect" } ], "version": "vNext" } ``` ## Hosting Neynar hosts pages on behalf of developers. We convert this JSON format into the Metatags expected by the [Farcaster Mini Apps Specification](https://miniapps.farcaster.xyz/docs/specification) and [legacy Frames specification](https://docs.farcaster.xyz/reference/frames/spec). If you have questions/feedback, please reach out to [@rish](https://warpcast.com/rish)or [@manan](https://warpcast.com/manan) on Farcaster. We will continue improving the frames experience! # Cast Action with Analytics Source: https://docs.neynar.com/docs/cast-action-with-analytics-neynar In this guide, we’ll make a cast action with the neynar SDK and frog.fm, within a few minutes! The cast action will fetch the follower count of the cast's author using its fid and display it. ## Cast Action We'll also validate the requests using Neynar's frame validator which provides analytics as well! Before we begin, you can access the [complete source code](https://github.com/neynarxyz/farcaster-examples/tree/main/cast-action) for this guide on GitHub. Let's get started! ### Creating a new frames project We will use [bun](https://bun.sh/) and [frog](https://frog.fm/) for building the cast action in this guide, but feel free to use [framejs](https://framesjs.org/), [onchainkit](https://onchainkit.xyz/), or anything else as well! Enter this command in your terminal to create a new app: ```powershell PowerShell theme={"system"} bunx create-frog -t bun ``` Enter a name for your project and it will spin up a new project for you. Once the project is created install the dependencies: ```powershell PowerShell theme={"system"} cd bun install ``` Now, let's install the dependencies that we are going to need to build out this action: ```powershell PowerShell theme={"system"} bun add @neynar/nodejs-sdk dotenv ``` #### Creating the cast action route Head over to the `src/index.ts` file. Here, you'll be able to see a starter frame on the / route. But first, let's change the Frog configuration to use `/api` as the base path and use neynar for hubs like this: ```typescript index.tsx theme={"system"} export const app = new Frog({ hub: neynar({ apiKey: "NEYNAR_FROG_FM" }), basePath: "/api", }); ``` You also might need to import neynar from "frog/hubs": ```typescript index.tsx theme={"system"} import { neynar } from "frog/hubs"; ``` Now, we'll create a new post route which will handle our cast actions. So, create a new route like this: ```typescript index.tsx theme={"system"} app.hono.post("/followers", async (c) => { try { let message = "GM"; return c.json({ message }); } catch (error) { console.error(error); } }); ``` This route will return a GM message every time the action is clicked, but let's now use the neynar SDK to get the follower count of the cast's author! Create a new `src/lib/neynarClient.ts` file and add the following: ```typescript neynarClient.ts theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; import { config } from "dotenv"; config(); if (!process.env.NEYNAR_API_KEY) { throw new Error("Make sure you set NEYNAR_API_KEY in your .env file"); } // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const configuration = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const neynarClient = new NeynarAPIClient(configuration); export default neynarClient; ``` Here, we initialise the neynarClient with the neynar api key which you can get from your dashboard: ![Neynar API key](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/794cfad-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=4b72f6261afde6d5da380fa53c94c3d9) Add the api key in a `.env` file with the name `NEYNAR_API_KEY`. Head back to the `src/index.tsx` file and add the following in the followers route instead of the GM message: ```typescript index.tsx theme={"system"} try { const body = await c.req.json(); const result = await neynarClient.validateFrameAction({ messageBytesInHex:body.trustedData.messageBytes} ); const { users } = await neynarClient.fetchBulkUsers({ fids: [Number(result.action.cast.author.fid)], }); if (!users) { return c.json({ message: "Error. Try Again." }, 500); } let message = `Count:${users[0].follower_count}`; return c.json({ message }); } catch (e) { return c.json({ message: "Error. Try Again." }, 500); } ``` Here, we use the neynar client that we just initialised to first validate the action and get the data from the message bytes. Then, we use it to fetch the user information using the `fetchBulkUsers` function. Finally, we return a message with the follower count! #### Creating a frame with add cast action button I am also adding a simple frame that allows anyone to install the action. But for that, you need to host your server somewhere, for local development you can use ngrok. If you don’t already have it installed, install it from [here](https://ngrok.com/downloads/mac-os). Once it’s installed, authenticate using your auth token and serve your app using this command: ```powershell PowerShell theme={"system"} ngrok http http://localhost:5173/ ``` This command will give you a URL which will forward the requests to your localhost: ![Ngrok URL](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/9e1852c-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=3d75166dbbca1cf95e9335970e56a699) You can now head over to the [cast action playground](https://warpcast.com/~/developers/cast-actions) and generate a new URL by adding in the info as such: ![Farcaster Cast action playground](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/47248c2-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=163876e9944963edde6e04527e5ec5e9) Copy the install URL and paste it into a new variable in the `index.tsx` like this: ```typescript index.tsx theme={"system"} const ADD_URL = "https://warpcast.com/~/add-cast-action?actionType=post&name=Followers&icon=person&postUrl=https%3A%2F%2F05d3-2405-201-800c-6a-70a7-56e4-516c-2d3c.ngrok-free.app%2Fapi%2Ffollowers"; ``` Finally, you can replace the / route with the following to have a simple frame which links to this URL: ```typescript index.tsx theme={"system"} app.frame("/", (c) => { return c.res({ image: (

gm! Add cast action to view followers count

), intents: [Add Action], }); }); ``` If you now start your server using `bun run dev` and head over to [http://localhost:5173/dev](http://localhost:5173/dev) you'll be able to see a frame somewhat like this: ![Frog frame dev env](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/ff041d7-image.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=e6a60b3f590ca594269443f552ae7f36) Click on Add action and it'll prompt you to add a new action like this: ![Add farcaster action](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/08fc953-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=a9de773c8ccb77c64b96f0a359bb700c) Once you have added the action, you can start using it on Warpcast to see the follower count of various people! 🥳 ### Analytics Since we are using the validateFrameAction function, we also get analytics out of the box. Head over to the usage tab and click on the frame that you are currently using. It will provide you with various analytics like total interactors, interactions per cast, etc. ![Frame analytics](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/713fe83-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=da3d7ed1857d3ffc193a2c1a9870caf9) ### Conclusion This guide taught us how to create a Farcaster cast action that shows the follower count of the cast's author! If you want to look at the completed code, check out the [GitHub repository](https://github.com/neynarxyz/farcaster-examples/tree/main/cast-action). Lastly, make sure to share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar), and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Cast From Inside a Frame Source: https://docs.neynar.com/docs/casting-from-a-frame In this guide, we'll look at how to build a frame using which people can create casts and perform other actions on the Farcaster app. For this guide, I will be using Next.js and frames.js but feel free to refer to this guide for any other framework with the necessary changes. For this guide, we'll go over: > [card] [Adding sign-in with neynar](/docs/casting-from-a-frame#adding-sign-in-with-neynar) > [card] Storing signers in a DB > [card] Using the signer to create casts Before we begin, you can access the [complete source code](https://github.com/avneesh0612/frame-cast) for this guide on GitHub. Let's get started! ## Setting up ### App We're going to use next.js so that we can have everything from our login page to our frame in the same project. So, create a new project using the following command: ```powershell PowerShell theme={"system"} npx create-next-app@latest cast-frame-signer ``` You can choose the configuration based on your personal preference, I am using this config for the guide: ![Frame creation](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/af167c6-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=8d1834082b510b821830dcd6a7c5eeb3) Once the app is created, install the packages that we are going to need for the command: ```powershell npm theme={"system"} npm i @neynar/react @neynar/nodejs-sdk axios @prisma/client prisma frames.js ``` ```powershell yarn theme={"system"} yarn add @neynar/react @neynar/nodejs-sdk axios ``` ```powershell bash theme={"system"} bun add @neynar/react @neynar/nodejs-sdk axios ``` ### Database We're also going to need a database to store all the signers so that we can later access it to create casts and perform other actions on the user's behalf. I am going to use MongoDB as the database, and prisma as an ORM. So, go ahead and create an account/sign into your MongoDB account here. Once you've signed up go ahead and setup a new cluster on Mongodb. You can follow this [guide](https://www.mongodb.com/resources/products/fundamentals/mongodb-cluster-setup) to do it. Once you've setup your cluster follow these steps to get your connection URL: **1. Click on Connect in the dashboard tab** ![Frame creation](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/d19671c-SCR-20240608-blkb.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=fe803d1635d5f2c12a82cf5f8d0d2968) **2. In the modal select drivers as the connection method** ![Frame creation](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/1fd4064-SCR-20240608-bllr.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=9858a4d203f58e3564d7d2b934cca09a) **3. Copy the connection string from here and replace `\` with your user password** ![Frame creation](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/fcd5358-SCR-20240608-blmy.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=97817176a1de8cc7a51b6dac479a65fc) Now, let's go ahead and set up Prisma in our project. Run the following command to initialise: ```powershell PowerShell theme={"system"} npx prisma init --datasource-provider MongoDB ``` Once the initialisation is complete head over to the `.env` file and add the connection url that you just copied and replace the part after `.mongodb.net/` with `/db?retryWrites=true&w=majority`. > [!WARNING] > > ### Make sure to add .env into .gitignore Firstly, let's first define our database schema. Head over to `prisma/schema.prisma` and add a user model like this: ```Text schema.prisma theme={"system"} model User { fid String @id @default(cuid()) @map("_id") signerUUID String @unique } ``` Once you have added the model, run these two commands: ```powershell PowerShell theme={"system"} npx prisma db push npx prisma generate ``` Now, we can add the Prisma client which we'll use to interact with our database. To do that, create a new `lib/prisma.ts` file in the `src` folder and add the following: ```typescript prisma.ts theme={"system"} import { PrismaClient } from "@prisma/client"; let prisma: PrismaClient; if (process.env.NODE_ENV === "production") { prisma = new PrismaClient(); } else { const globalWithPrisma = global as typeof globalThis & { prisma: PrismaClient; }; if (!globalWithPrisma.prisma) { globalWithPrisma.prisma = new PrismaClient(); } prisma = globalWithPrisma.prisma; } export default prisma; ``` ## Adding sign-in with neynar Now that we've setup all the boilerplate let's start coding the actual part. We'll first go ahead and add sign in with neynar. To do that we need to wrap our app in a provider, so, head over to the `layout.tsx` file and wrap your app in a `NeynarContextProvider` like this: ```typescript layout.tsx theme={"system"} "use client"; import { NeynarContextProvider, Theme } from "@neynar/react"; import "@neynar/react/dist/style.css"; import { Inter } from "next/font/google"; import "./globals.css"; const inter = Inter({ subsets: ["latin"] }); export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {}, onSignout() {}, }, }} > {children} ); } ``` We are passing some settings here like `clientId`, `defaultTheme` and `eventsCallbacks`. * `clientId`: This is going to be the client ID you get from your neynar, add it to your `.env.local` file as `NEXT_PUBLIC_NEYNAR_CLIENT_ID`. ![Frame creation](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/bde5490-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=c44bb9404a8361f45b82f97d08e9feb8) > [!NOTE] > > ### Make sure to add localhost to the authorized origins * `defaultTheme`: default theme lets you change the theme of your sign-in button, currently, we have only light mode but dark mode is going to be live soon. * `eventsCallbacks`: This allows you to perform certain actions when the user signs out or auth is successful. I've also added a styles import from the neynar react package here which is needed for the styles of the sign-in button. Finally, let's add the sign-in button in the `page.tsx` file like this: ```typescript page.tsx theme={"system"} "use client"; import { NeynarAuthButton } from "@neynar/react"; export default function Login() { return (
); } ``` If you head over to your app you'll be able to see a sign-in button on the screen. Go ahead and try signing in! ![Frame creation](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/4813dc2-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=2aefa20261f34feab92e7275ff564047) Now that our sign-in button is working let's add a call to add the users' signers to the database. In `layout.tsx` add this to the `onAuthSuccess` function in `NeynarContextProvider`'s settings: ```typescript layout.tsx theme={"system"} eventsCallbacks: { onAuthSuccess: ({ user }) => { axios.post("/api/add-user", { signerUuid: user?.signer_uuid, fid: user?.fid, }); }, onSignout() {}, }, ``` This will call an `/api/add-user`API route which we are yet to create with the user's signer and fid every time a user successfully signs in. Now, create a new `/api/add-user/route.ts` file in the `app` folder and add the following: ```typescript add-user/route.ts theme={"system"} import { NextRequest, NextResponse } from "next/server"; import { isApiErrorResponse } from "@neynar/nodejs-sdk"; import neynarClient from "@/lib/neynarClient"; import prisma from "@/lib/prisma"; export async function POST(request: NextRequest) { const { signerUuid, fid } = (await request.json()) as { signerUuid: string; fid: string; }; try { const { fid: userFid } = await neynarClient.lookupSigner({ signerUuid }); if (!userFid) { return NextResponse.json({ message: "User not found" }, { status: 404 }); } if (fid !== String(userFid)) { return NextResponse.json( { message: "Invalid user data" }, { status: 400 } ); } const user = await prisma.user.findUnique({ where: { fid: String(userFid), }, }); if (!user) { await prisma.user.create({ data: { signerUUID: signerUuid, fid: String(userFid), }, }); } return NextResponse.json({ message: "User added" }, { status: 200 }); } catch (err) { if (isApiErrorResponse(err)) { return NextResponse.json( { ...err.response.data }, { status: err.response.status } ); } else return NextResponse.json( { message: "Something went wrong" }, { status: 500 } ); } } ``` Here we are verifying the signer is valid and if it's valid we add it to the database. As you can see we are importing a `neynarClient` function which we have not yet created so we have to do that. Create a new `lib/neynarClient.ts` file and add the following: ```Text neynarClient.ts theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const neynarClient = new NeynarAPIClient(config); export default neynarClient; ``` ## Creating the frame Firstly, let's create the homescreen of the frame which will be a simple frame with a few buttons and a simple text. So, create a new file `frame/route.tsx` file in the `app` folder and add the following: ```js theme={"system"} import { createFrames, Button } from "frames.js/next"; const frames = createFrames({}); const HOST = process.env.HOST || "http://localhost:3000"; const handleRequest = frames(async () => { return { image: (

Cast from a frame!

), buttons: [ , ], }; }); export const GET = handleRequest; export const POST = handleRequest; ``` If you open it up in a debugger it will show you a frame like this: ![Frame creation](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/cf3bf1e-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=a8ef776ab642323059109398dac2fd78) Now, create a new file `start/route.tsx` in the `frame` folder and add the following: ```typescript start/route.tsx theme={"system"} import prisma from "@/lib/prisma"; import { createFrames, Button } from "frames.js/next"; const frames = createFrames({}); const HOST = process.env.HOST || "http://localhost:3000"; const handleRequest = frames(async (payload) => { const fid = payload.message?.requesterFid; const user = await prisma.user.findUnique({ where: { fid: String(fid), }, }); if (!user) { return { image: (

User not found!

), buttons: [ , ], }; } return { image: (

Cast from a frame!

), buttons: [ , ], textInput: "Text to cast...", }; }); export const GET = handleRequest; export const POST = handleRequest; ``` This is just the home page for the frame that we are creating let's create a new `/frame/publish` route where the cast is made using the text that the user entered on the home screen. Create a new file `route.tsx` in the `frame/publish/` folder and add the following: ```typescript publish/route.tsx theme={"system"} import neynarClient from "@/lib/neynarClient"; import prisma from "@/lib/prisma"; import { createFrames, Button } from "frames.js/next"; const frames = createFrames({}); const HOST = process.env.HOST || "http://localhost:3000"; const handleRequest = frames(async (payload) => { const text = payload.message?.inputText; const fid = payload.message?.requesterFid; const user = await prisma.user.findUnique({ where: { fid: String(fid), }, }); if (!user) { return { image: (
User not found!
), buttons: [ , ], }; } const cast = await neynarClient.publishCast({signerUuid:user?.signerUUID,text:text || "gm"}); return { image: (
Casted successfully! 🎉 {cast?.hash}
), }; }); export const GET = handleRequest; export const POST = handleRequest; ``` Here, we are first confirming that the user's signer exists. And if it does we use it to create a new cast using the `publishCast` function from the client we initialised. If you try publishing a new cast on the frame it will create the cast and show you something like this. ![Frame creation](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/55e260d-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=1ccc59c4154be3772ce3b7990d44e0aa) ## Conclusion This guide taught us how to create a frame from which the users can create casts, check out the [GitHub repository](https://github.com/avneesh0612/frame-cast) if you want to look at the full code. Lastly, make sure to share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar) and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Find User Subscriptions with Neynar Hypersub Source: https://docs.neynar.com/docs/common-subscriptions-fabric Finding Hypersub subscriptions on Social Token Protocol (STP) using Neynar > [!NOTE] > > ### Related set of APIs [Fetch Subscribers for FID](/reference/fetch-subscribers-for-fid) ## How to Find Common Subscriptions In this guide, we'll take two FIDs and then find their common subscriptions on fabric. We'll use JavaScript for this guide, but the same logic would work for any other language you use! So, let's get started by creating a new file and defining our constants: ```typescript index.ts theme={"system"} const fid1 = 194; const fid2 = 191; const url1 = `https://api.neynar.com/v2/farcaster/user/subscribed_to?fid=${fid1}&viewer_fid=3&subscription_provider=fabric_stp`; const url2 = `https://api.neynar.com/v2/farcaster/user/subscribed_to?fid=${fid2}&viewer_fid=3&subscription_provider=fabric_stp`; ``` You can replace the FIDs with the ones you want to check the subscriptions for and leave the URLs as they are. The URL is the API route to get all the channels a user is subscribed to. You can find more info about the API route in the [API reference](/reference/fetch-subscribed-to-for-fid). Then, call the APIs using fetch like this: ```typescript index.ts theme={"system"} const fetchUrls = async () => { const options = { method: "GET", headers: { accept: "application/json", api_key: "NEYNAR_API_DOCS" }, }; const response = await Promise.all([ fetch(url1, options), fetch(url2, options), ]); const data = await Promise.all(response.map((res) => res.json())); return data; }; ``` Here, make sure to replace the API key with your API key instead of the docs API key in production. Finally, let's filter out the data to find the common subscriptions like this: ```typescript index.ts theme={"system"} fetchUrls().then((data) => { const [subscribedTo1, subscribedTo2] = data; const commonSubscriptions = subscribedTo1.subscribed_to.filter( (item1: { contract_address: string }) => subscribedTo2.subscribed_to.some( (item2: { contract_address: string }) => item2.contract_address === item1.contract_address ) ); console.log(commonSubscriptions); }); ``` Here, we use the filter function on the data that we just fetched and match the channel's contract address since that will be unique for every channel. Now, we can test the script by running it. ![Common Subscriptions](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/304285a-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=630569e1055e9b0d72319f1c6922cc81) The two FIDs we used were subscribed to Terminally Onchain, so that shows up. If you want to look at the complete script, you can look at this [GitHub Gist](https://gist.github.com/avneesh0612/f9fa2da025fa764c6dc65de5f3d5ecec). If you want to know more about the subscription APIs take a look at [Fetch Subscribers for FID](/reference/fetch-subscribers-for-fid). Lastly, please share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar), and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Convert a Web App to a Farcaster Mini App Source: https://docs.neynar.com/docs/convert-web-app-to-mini-app Update any JavaScript web app to be a Farcaster mini app > [!NOTE] > > If looking to create a new mini app from scratch, see [Create Farcaster Mini App in 60s](/docs/create-farcaster-miniapp-in-60s). Converting an existing JavaScript-based web app to a Farcaster mini app involves the following steps: * install the [@neynar/react](https://www.npmjs.com/package/@neynar/react) npm package and use the `` provider in your app * alternatively, install the [Mini App SDK](https://www.npmjs.com/package/@farcaster/miniapp-sdk) and call `sdk.actions.ready()` * integrate with the SDK's ethereum provider exposed via `sdk.wallet.ethProvider` * add a `farcaster.json` file with mini app metadata and a signature proving ownership * add a custom HTML `` tag specifying how embeds should be rendered ## Installing the SDK ### Using @neynar/react The recommended way to integrate your app with Farcaster is using the `@neynar/react` package, which includes the Mini App SDK along with custom Neynar components and built-in analytics: ```bash theme={"system"} npm install @neynar/react ``` Then wrap your app with the `` provider: ```javascript theme={"system"} import { MiniAppProvider } from '@neynar/react'; export default function App() { return ( {/* Your app components */} ); } ``` With the MiniAppProvider provider in place, you can access Mini App SDK functionality with the `useMiniApp()` react hook: ```javascript theme={"system"} import { useMiniApp } from '@neynar/react'; export default function HomePage() { const { isSDKLoaded, context } = useMiniApp(); return (<> {isSDKLoaded && (
{context}
)} ) } ``` ### Using the Mini App SDK Alternatively, you can use the Mini App SDK (formerly the Frame SDK): ```bash theme={"system"} npm install @farcaster/miniapp-sdk ``` Then call the ready function when your interface is loaded and ready to be displayed: ```javascript theme={"system"} import { sdk } from '@farcaster/miniapp-sdk'; await sdk.actions.ready(); ``` You should call `ready()` as early as possible in the app, but after any pageload processes that might cause the UI to re-render or update significantly. In a React app, it's generally best to call `ready()` inside the page-level component at the root of your UI, e.g. in your homepage component. Here's an example of how you might do this in a standard React app: ```javascript {9} theme={"system"} import { useEffect, useState } from "react"; import { sdk } from '@farcaster/miniapp-sdk'; export default function Home() { const [isLoaded, setIsLoaded] = useState(false); useEffect(() => { const load = async () => { await sdk.actions.ready(); setIsLoaded(true); }; if (sdk && !isLoaded) { load(); } }, [isLoaded]); return (...) } ``` ## Connecting to the wallet provider It's recommended to use `wagmi` for your wallet provider, as the Farcaster team provides the [@farcaster/miniapp-wagmi-connector package](https://www.npmjs.com/package/@farcaster/miniapp-wagmi-connector) for easy configuration. Run `npm i @farcaster/miniapp-wagmi-connector` to install, and then connecting is as simple as adding the connector to the wagmi config: ```javascript {11} theme={"system"} import { http, createConfig } from 'wagmi'; import { base } from 'wagmi/chains'; import { farcasterMiniApp } from '@farcaster/miniapp-wagmi-connector'; export const wagmiConfig = createConfig({ chains: [base], transports: { [base.id]: http(), }, connectors: [ farcasterMiniApp() // add other wallet connectors like metamask or coinbase wallet if desired ] }); ``` With the above configuration, you can access the mini app user's connected wallet with normal wagmi hooks like `useAccount()`. ## Connecting to Solana For Solana support, install the package and wrap your app with the Solana provider: ```bash Bash theme={"system"} npm install @farcaster/mini-app-solana ``` ```typescript App.tsx theme={"system"} import { FarcasterSolanaProvider } from '@farcaster/mini-app-solana'; function App() { const solanaEndpoint = 'https://solana-rpc.publicnode.com'; return ( {/* Your app components */} ); } ``` Use Solana wallet hooks in your components: ```typescript SolanaExample.tsx theme={"system"} import { useSolanaConnection, useSolanaWallet } from '@farcaster/mini-app-solana'; import { Transaction, SystemProgram, PublicKey } from '@solana/web3.js'; function SolanaExample() { const { publicKey, signMessage, sendTransaction } = useSolanaWallet(); const { connection } = useSolanaConnection(); const handleSign = async () => { if (!signMessage) return; const message = new TextEncoder().encode("Hello Solana!"); const signature = await signMessage(message); console.log('Signed:', btoa(String.fromCharCode(...signature))); }; const handleSend = async () => { if (!publicKey || !sendTransaction) return; const { blockhash } = await connection.getLatestBlockhash(); const transaction = new Transaction(); transaction.add( SystemProgram.transfer({ fromPubkey: publicKey, toPubkey: new PublicKey('DESTINATION_ADDRESS'), lamports: 1000000, // 0.001 SOL }) ); transaction.recentBlockhash = blockhash; transaction.feePayer = publicKey; const signature = await sendTransaction(transaction, connection); console.log('Transaction:', signature); }; return (
); } ``` > [!NOTE] > > The Solana provider will only be available when the user's wallet supports Solana. Always check `hasSolanaProvider` before rendering Solana-specific UI components. ## Adding and signing the farcaster.json file Mini apps are expected to serve a farcaster.json file, also known as a "manifest", at `/.well-known/farcaster.json`, published at the root of the mini app's domain. The manifest consists of a `miniapp` section containing metadata specific to the mini app and an `accountAssociation` section consisting of a JSON Farcaster Signature (JFS) to verify ownership of the domain and mini app. The `miniapp` metadata object only has four required fields (`version`, `name`, `homeUrl`, and `iconUrl`), but providing more is generally better to help users and clients discover your mini app. See the full list of options [here in the Farcaster docs](https://miniapps.farcaster.xyz/docs/specification#frame). Start by publishing just the miniapp portion of the manifest: ```json theme={"system"} { "miniapp": { "version": "1", "name": "Yoink!", "iconUrl": "https://yoink.party/logo.png", "homeUrl": "https://yoink.party/framesV2/", "imageUrl": "https://yoink.party/framesV2/opengraph-image", "buttonTitle": "🚩 Start", "splashImageUrl": "https://yoink.party/logo.png", "splashBackgroundColor": "#f5f0ec", "webhookUrl": "https://yoink.party/api/webhook" } } ``` In a standard react app, you can do this by placing a JSON file in your public folder, to be served as a static file: ``` public/ ├── .well-known/ └── farcaster.json ``` Once your domain is live and serving something like the above example at `yourURL.com/.well-known/farcaster.json`, you need to generate an `accountAssociation` signed with your farcaster custody address: * go to [the Mini App Manifest Tool](https://farcaster.xyz/~/developers/mini-apps/manifest) in your desktop browser * enter your domain and scroll to the bottom * click "Claim Ownership", and follow the steps to sign the manifest with your Farcaster custody address using your phone * finally, copy the output manifest from the manifest tool and update your domain to serve the full, signed farcaster.json file, which should look something like this: ```json theme={"system"} { "accountAssociation": { "header": "eyJmaWQiOjM2MjEsInR5cGUiOiJjdXN0b2R5Iiwia2V5IjoiMHgyY2Q4NWEwOTMyNjFmNTkyNzA4MDRBNkVBNjk3Q2VBNENlQkVjYWZFIn0", "payload": "eyJkb21haW4iOiJ5b2luay5wYXJ0eSJ9", "signature": "MHgwZmJiYWIwODg3YTU2MDFiNDU3MzVkOTQ5MDRjM2Y1NGUxMzVhZTQxOGEzMWQ5ODNhODAzZmZlYWNlZWMyZDYzNWY4ZTFjYWU4M2NhNTAwOTMzM2FmMTc1NDlmMDY2YTVlOWUwNTljNmZiNDUxMzg0Njk1NzBhODNiNjcyZWJjZTFi" }, "miniapp": { "version": "1", "name": "Yoink!", "iconUrl": "https://yoink.party/logo.png", "homeUrl": "https://yoink.party/framesV2/", "imageUrl": "https://yoink.party/framesV2/opengraph-image", "buttonTitle": "🚩 Start", "splashImageUrl": "https://yoink.party/logo.png", "splashBackgroundColor": "#f5f0ec", "webhookUrl": "https://yoink.party/api/webhook" } } ``` ## Configuring embed metadata To allow your mini app to render properly in social feeds, you must add a meta tag with the name "fc:frame" to the `` section of the HTML page serving your mini app. ```html theme={"system"} ``` The full schema can be found [here in the Farcaster docs](https://miniapps.farcaster.xyz/docs/specification#schema), but the most common button action is `launch_miniapp`, so unless you have a specific use case, you can safely copy the following example: ```json theme={"system"} { "version": "next", "imageUrl": "https://yoink.party/framesV2/opengraph-image", "button": { "title": "🚩 Start", "action": { "type": "launch_miniapp", "name": "Yoink!", "url": "https://yoink.party/framesV2", "splashImageUrl": "https://yoink.party/logo.png", "splashBackgroundColor": "#f5f0ec" } } } ``` # Bot Replying with Frames Source: https://docs.neynar.com/docs/create-a-farcaster-bot-to-reply-with-frames-using-neynar In this guide, we’ll take a look at how to create a Farcaster bot that replies to specific keywords with a frame created on the go specifically for the reply! Here’s an example of the same: ![Demo of the farcaster frames bot](https://mintlify.s3.us-west-1.amazonaws.com/neynar/images/docs/neynarxyz/farcaster-examples/assets/76690419/d5749625-ce9c-46da-b8aa-49bb9be8fd0f) For this guide, we'll go over: > [card] [Creating a webhook that listens to casts](/docs/create-a-farcaster-bot-to-reply-with-frames-using-neynar#creating-a-webhook) > [card] [Creating a bot that replies to the casts](/docs/create-a-farcaster-bot-to-reply-with-frames-using-neynar#creating-the-bot) > [card] [Creating frames dynamically using the neynar SDK](/docs/create-a-farcaster-bot-to-reply-with-frames-using-neynar#creating-the-frame) Before we begin, you can access the [complete source code](https://github.com/neynarxyz/farcaster-examples/tree/main/frames-bot) for this guide on GitHub. Let's get started! ## Setting up our server ### Creating a bun server I am using a [bun server](https://bun.sh/) for the sake of simplicity of this guide, but you can use express, Next.js api routes or any server that you wish to use! Here's a [serverless example using next.js api routes](https://github.com/davidfurlong/farcaster-bot-template/tree/main) created by [@df](https://warpcast.com/df). Create a new server by entering the following commands in your terminal: ```powershell PowerShell theme={"system"} mkdir frames-bot cd frames-bot bun init ``` We are going to need the `@neynar/nodejs-sdk`, so let’s install that as well: ```powershell PowerShell theme={"system"} bun add @neynar/nodejs-sdk ``` Once the project is created and the packages are installed, you can open it in your favourite editor and add the following in `index.ts`: ```typescript index.ts theme={"system"} const server = Bun.serve({ port: 3000, async fetch(req) { try { return new Response("Welcome to bun!"); } catch (e: any) { return new Response(e.message, { status: 500 }); } }, }); console.log(`Listening on localhost:${server.port}`); ``` This creates a server using bun which we will be using soon! Finally, run the server using the following command: ```powershell PowerShell theme={"system"} bun run index.ts ``` ### Serve the app via ngrok We’ll serve the app using ngrok to use this URL in the webhook. If you don’t already have it installed, install it from [here](https://ngrok.com/download). Once it’s installed, authenticate using your auth token and serve your app using this command: ```powershell PowerShell theme={"system"} ngrok http http://localhost:3000 ``` ![Serve your app using ngrok](https://mintlify.s3.us-west-1.amazonaws.com/neynar/images/docs/neynarxyz/farcaster-examples/assets/76690419/7f3e8d33-1aef-4723-948c-436c56658864) > [!WARNING] > > ### Free endpoints like ngrok, localtunnel, etc. can have issues because service providers start blocking events over a certain limit ## Creating a webhook We need to create a webhook on the neynar dashboard that will listen for certain words/mentions and call our server which will then reply to the cast. So, head to the neynar dashboard and go to the [webhooks tab](https://dev.neynar.com/webhook). Click on new webhook and enter the details as such: ![Create a new webhook on the neynar dashboard](https://mintlify.s3.us-west-1.amazonaws.com/neynar/images/docs/neynarxyz/farcaster-examples/assets/76690419/81b65ce0-5b3a-4856-b1e5-7f46c2c648cd) The target URL should be the URL you got from the ngrok command, and you can select whichever event you want to listen to. I’ve chosen to listen to all the casts with “farcasterframesbot” in it. Once you have entered all the info, click Create to create a webhook. ## Creating the bot Head over to the [app section](https://dev.neynar.com/app/list) in the [neynar dashboard](https://dev.neynar.com/) and copy the signer uuid for your account: ![Copy the signer uuid for the bot](https://mintlify.s3.us-west-1.amazonaws.com/neynar/images/docs/neynarxyz/farcaster-examples/assets/76690419/a6a56060-612c-4ff1-bf67-b01a0b43bcf3) Create a new `.env` file in the root of your project and add the following: ```bash .env theme={"system"} SIGNER_UUID=your_signer_uuid NEYNAR_API_KEY=your_neynar_api_key ``` Add the signer UUID to the `SIGNER_UUID` and the neynar api key to the `NEYNAR_API_KEY` which you can get from the overview section of the neynar dashboard: ![Copy neynar api key from the dashboard](https://mintlify.s3.us-west-1.amazonaws.com/neynar/images/docs/neynarxyz/farcaster-examples/assets/76690419/f55d7ee4-a2d2-4c61-ac0f-bf7074a80a60) Create a `neynarClient.ts` file and add the following: ```typescript neynarClient.ts theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; if (!process.env.NEYNAR_API_KEY) { throw new Error("Make sure you set NEYNAR_API_KEY in your .env file"); } // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey:process.env.NEYNAR_API_KEY, }); const neynarClient = new NeynarAPIClient(config); export default neynarClient; ``` Here we initialize the neynar client, which we can use to publish casts. Head back to `index.ts` and add this inside the try block: ```typescript index.ts theme={"system"} if (!process.env.SIGNER_UUID) { throw new Error("Make sure you set SIGNER_UUID in your .env file"); } const body = await req.text(); const hookData = JSON.parse(body); const signerUuid= process.env.SIGNER_UUID; const text= `gm ${hookData.data.author.username}`; const replyTo= hookData.data.hash; const reply = await neynarClient.publishCast( { signerUuid, text, parent: replyTo, } ); console.log("reply:", reply.cast); ``` You also need to import the neynar client in the `index.ts` file: ```typescript index.ts theme={"system"} import neynarClient from "./neynarClient"; ``` This will now reply to every cast that has the word “farcasterframesbot” in it with a gm. Pretty cool, right? Let’s take this a step further and reply with a frame instead of boring texts! ## Creating the frame We’ll now generate a unique frame for every user on the fly using neynar frames. To create the frame, add the following code in the `index.ts` before the reply: ```typescript index.ts theme={"system"} const creationRequest: NeynarFrameCreationRequest = { name: `gm ${hookData.data.author.username}`, pages: [ { image: { url: "https://moralis.io/wp-content/uploads/web3wiki/638-gm/637aeda23eca28502f6d3eae_61QOyzDqTfxekyfVuvH7dO5qeRpU50X-Hs46PiZFReI.jpeg", aspect_ratio: "1:1", }, title: "Page title", buttons: [], input: { text: { enabled: false, }, }, uuid: "gm", version: "vNext", }, ], }; const frame = await neynarClient.publishNeynarFrame(creationRequest); ``` You can edit the metadata here, I have just added a simple gm image but you can go crazy with it! For example, check out some templates in the [frame studio](https://dev.neynar.com) . Anyway, let’s continue building; you also need to add the frame as an embed in the reply body like this: ```typescript index.ts theme={"system"} const signerUuid= process.env.SIGNER_UUID; const text= `gm ${hookData.data.author.username}`; const replyTo= hookData.data.hash; const embeds = [ { url: frame.link, }, ]; const reply = await neynarClient.publishCast({ signerUuid, text, parent: replyTo, embeds } ); ``` Putting it all together, your final `index.ts` file should look similar to [this complete example](https://github.com/neynarxyz/farcaster-examples/blob/main/frames-bot/index.ts). Don't forget to restart your server after making these changes! ```powershell PowerShell theme={"system"} bun run index.ts ``` You can now create a cast on Farcaster, and your webhook should work fine! ## Conclusion This guide taught us how to create a Farcaster bot that replies to specific keywords with a frame created on the go! If you want to look at the completed code, check out the [GitHub repository](https://github.com/neynarxyz/farcaster-examples/tree/main/frames-bot). Lastly, make sure to tag what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar), and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Cast Stream Source: https://docs.neynar.com/docs/create-a-stream-of-casts Fetch stream of casts with Farcaster hubs In this guide, we'll create a stream of casts using Farcaster hubs and stream the casts published in real time. ### Create nodejs app Create a new node.js app using the following commands: ```bash theme={"system"} mkdir stream-casts cd stream-casts bun init ``` I have used bun but feel free to use npm, yarn, pnpm, or anything of your choice! Once the app is created, run this command to install the "@farcaster/hub-nodejs" package: ```bash theme={"system"} bun add @farcaster/hub-nodejs ``` ### Build the stream Now, let's get to building our stream. In the index.ts file add the following to initialise the client: ```typescript index.ts theme={"system"} import { createDefaultMetadataKeyInterceptor, getSSLHubRpcClient, HubEventType } from '@farcaster/hub-nodejs'; const hubRpcEndpoint = "hub-grpc-api.neynar.com"; const client = getSSLHubRpcClient(hubRpcEndpoint, { interceptors: [ createDefaultMetadataKeyInterceptor('x-api-key', 'YOUR_NEYNAR_API_KEY'), ], 'grpc.max_receive_message_length': 20 * 1024 * 1024, }); ``` You need to replace "YOUR\_NEYNAR\_API\_KEY" with your API key. You can get it from your [neynar app page](https://dev.neynar.com/app). Once our client is initialized we can use it to subscribe to specific events, in our case we want to subscribe to the `MERGE_MESSAGE` event. You can check out the full details about the types of events in [The Snapchain Events Documentation](https://snapchain.farcaster.xyz/reference/datatypes/events#events). So, add the following in your code: ```typescript index.ts theme={"system"} client.$.waitForReady(Date.now() + 5000, async (e) => { if (e) { console.error(`Failed to connect to ${hubRpcEndpoint}:`, e); process.exit(1); } else { console.log(`Connected to ${hubRpcEndpoint}`); const subscribeResult = await client.subscribe({ eventTypes: [HubEventType.MERGE_MESSAGE], }); client.close(); } }); ``` Finally, let's use the subscribeResult to stream and console log the cast texts: ```typescript index.ts theme={"system"} if (subscribeResult.isOk()) { const stream = subscribeResult.value; for await (const event of stream) { if (event.mergeMessageBody.message.data.type === 1) { console.log(event.mergeMessageBody.message.data.castAddBody.text); } } } ``` We have to filter out the data by its type since the merge message events provide all protocol events like casts, reactions, profile updates, etc. 1 is for casts published. Here's what the completed code looks like: ```typescript theme={"system"} import { createDefaultMetadataKeyInterceptor, getSSLHubRpcClient, HubEventType } from '@farcaster/hub-nodejs'; const hubRpcEndpoint = "hub-grpc-api.neynar.com"; const client = getSSLHubRpcClient(hubRpcEndpoint, { interceptors: [ createDefaultMetadataKeyInterceptor('x-api-key', 'YOUR_NEYNAR_API_KEY'), ], 'grpc.max_receive_message_length': 20 * 1024 * 1024, }); client.$.waitForReady(Date.now() + 5000, async (e) => { if (e) { console.error(`Failed to connect to ${hubRpcEndpoint}:`, e); process.exit(1); } else { console.log(`Connected to ${hubRpcEndpoint}`); const subscribeResult = await client.subscribe({ eventTypes: [HubEventType.MERGE_MESSAGE], }); if (subscribeResult.isOk()) { const stream = subscribeResult.value; for await (const event of stream) { if (event.mergeMessageBody.message.data.type === 1) { console.log(event.mergeMessageBody.message.data.castAddBody.text); } } } client.close(); } }); ``` ### Run the stream in your terminal Finally, you can run the script using `bun run index.ts` and it will provide you with a stream like this: ![Cast Stream](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/bc0fa74-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=96463bef2c786dfd41e26eee07c6ae18) ## Share with us! Lastly, make sure to share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar) and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Create in UI Source: https://docs.neynar.com/docs/create-farcaster-bot-ui Create a new Farcaster agent directly in Neynar dev portal ## Create new agent account easily in developer portal If you haven't created a new agent account yet, you can make it directly in the [Neynar dev portal](https://dev.neynar.com) . You can click inside an app and directly spin up a new bot from there. ![create agent](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/cec6f046cd86dcc0f3c2bbff6f22c056c39f277b78d3f3dd3d35483bddc8071c-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=1c33bfeea83c2e7a28b77f145ede891a) Tap on "Create Agent" to make the agent. ![create agent](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/c3ea656528155a1f0d16d571fb298da7cbc348b3a2aecfb99bd6a33a31f19704-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=d8f8ab4d9ef37e491ca17c292dd885f6) Agent creation requires paying for account creation on the Farcaster protocol which Neynar does on your behalf. However, this is why we restrict the number of agents you can create per developer account. Best to not create more than one test agent through the portal in case you hit the limit prematurely. ## Start casting with agent account As soon as the agent account is created, you will see a `signer_uuid` for the agent. You can use that signer to cast from the agent account using Neynar's [Publist Cast](/reference/publish-cast) API. A simple cURL request like ```javascript Javascript theme={"system"} curl --request POST \ --url https://api.neynar.com/v2/farcaster/cast \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --header 'x-api-key: NEYNAR_API_DOCS' \ --data ' { "signer_uuid": "19d0c5fd-9b33-4a48-a0e2-bc7b0555baec", "text": "Writing to @farcaster via the @neynar APIs 🪐" } ' ``` should post a cast from that account. Ensure you are using the right `signer_uuid` and the API key associated with the same app that the signer is associated with. ## Listen to replies If your bot or agent needs to listen to replies, see how to use webhooks in [Listen for @bot mentions](/docs/listen-for-bot-mentions). Cast in the `/neynar` channel on Farcaster with any questions and tag `@rish` # Create Farcaster Mini App (v2 frame) in < 60s Source: https://docs.neynar.com/docs/create-farcaster-frame-in-60s Create a v2 Farcaster mini app in less than 60 seconds > [!NOTE] > > This doc has been updated. See [Create Farcaster Mini App in 60s](/docs/create-farcaster-miniapp-in-60s). # Create Farcaster Mini App Source: https://docs.neynar.com/docs/create-farcaster-miniapp-in-60s Create a Farcaster mini app in less than 60 seconds > [!NOTE] > > If looking to convert an existing web app into a mini app, see [Convert Web App to Mini App](/docs/convert-web-app-to-mini-app). This tutorial shows how to create a Farcaster mini app (previously known as Farcaster frames) with one simple command in less than 60s using the [Neynar Starter Kit](https://github.com/neynarxyz/create-farcaster-mini-app/). ![Neynar Starter Kit demo app screenshot in dark mode](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/nsk-dark-mode.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=448e402bcc0012cf8a7a293e5edf21b8) ![Neynar Starter Kit demo app screenshot in light mode](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/nsk-light-mode.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=51bdb0b917ad5679daf0c7cdef972df7) Simply type `npx @neynar/create-farcaster-mini-app@latest` in any terminal window to get started with the template, or check out the [live demo of the Neynar Starter Kit](https://farcaster.xyz/miniapps/Qmodl2Stf9qh/starter-kit) on Farcaster. * package is open source ([github repo](https://github.com/neynarxyz/create-farcaster-mini-app)) * using neynar services is optional * demo API key is included if you haven't subscribed yet The flow: * generates signature required by mini app spec on your behalf and puts in the farcaster manifest * sets up splash image, CTA, etc. as part of workflow (incl. personalized share images, more on that below) * spins up a localtunnel hosted url so you can debug immediately, no need to ngrok or cloudflare on your own * if you use neynar: * automatically fetches user data * automatically sets sets up [notifications and analytics](/docs/send-notifications-to-mini-app-users) (just put in your client id from the dev portal) See \< 1 min video here that goes from scratch to testable mini app: [Create Farcaster mini app in less than 1 min](https://www.youtube.com/embed/VpZqw976bqs) If you have questions or feature requests, please reach out to @[veganbeef](https://warpcast.com/veganbeef) or @[rish](https://warpcast.com/rish). We are always looking forward to working with developers! > [!NOTE] > > If you want to sign in users with a Farcaster signer into your mini app, use > > * [Mini app auth](/docs/mini-app-authentication) if you want a built in next.js frontend. > * [Neynar Managed Signers](/docs/integrate-managed-signers) when building your own frontend. ### Appendix A great way to make your mini app go viral is to give your users personalized share images / urls that they can share on their social media timelines. Our starter kit makes it easy. Simply add `/share/[fid]` at the end of your mini app domain to create a personalized share image for that user e.g. `[your_url].[extension]/share/[fid]` should create an embed image like below: ![Original(1) Avi](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/original\(1\).avif?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=f810f75e333ffa23efa49b4815b490a4) # Create Multi Step Cast Action Source: https://docs.neynar.com/docs/create-multi-step-cast-action In this guide, we’ll make a multi-step cast action, within a few minutes! The cast action will go ahead and return a frame which will show the cast hash. Let's get started! ## Creating a new frames project We will use [bun](https://bun.sh/) and [frog](https://frog.fm/) for building the cast action in this guide, but feel free to use [framejs](https://framesjs.org/), [onchainkit](https://onchainkit.xyz/), or anything else as well! Enter this command in your terminal to create a new app: ```powershell PowerShell theme={"system"} bunx create-frog -t bun ``` Enter a name for your project and it will spin up a new project for you. Once the project is created install the dependencies: ```powershell PowerShell theme={"system"} cd bun install ``` Once everything is done, open up `index.tsx` and update the Frog instance to use neynar hubs and make sure to update the api key so that you can get analytics for your frame and cast action! ```typescript index.tsx theme={"system"} import { neynar } from "frog/hubs"; export const app = new Frog({ hub: neynar({ apiKey: "NEYNAR_FROG_FM" }), }); ``` ## Creating the add cast action frame Firstly, let's create the home screen frame that will link to adding the cast action. So, head over to the `index.tsx` file and update the `/` frame to this: ```typescript index.tsx theme={"system"} app.frame("/", (c) => { return c.res({ image: (
Create a multi step cast action
), intents: [ Add, ], }); }); ``` This should render a pretty simple frame like this: ![Add cast action frame](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/79ef4e0-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=2654b459f21b7d496335932a847d95d2) Let's now build the actual cast action. ## Creating the cast action The frog instance provides us with a `.castAction` which can be used to create new cast actions like this: ```typescript index.tsx theme={"system"} app.castAction( "/get-cast-hash", (c) => { return c.frame({ path: "/cast-hash" }); }, { name: "Get cast hash", icon: "hash" } ); ``` This creates a new cast action on the `/get-cast-hash` route which will return a new frame linking to `/cast-hash`. In the last object, you can change the name and icon of your cast action and add a description as well! Now, let's create the frame that the cast action will return. ## Creating the cast hash frame Create a new frame on the `/cast-hash` route like this: ```typescript index.tsx theme={"system"} app.frame("/cast-hash", (c) => { return c.res({ image: (
Cast hash is:
{c.frameData?.castId.hash}
), }); }); ``` This frame gets the cast hash from the `frameData` object and displays it. Now we can go ahead and test our cast action. But for that, you need to host your server somewhere, for local development you can use ngrok. If you don’t already have it installed, install it from [here](https://ngrok.com/download). Once it’s installed authenticate using your auth token and serve your app using this command: ```powershell PowerShell theme={"system"} ngrok http http://localhost:5173/ ``` This command will give you a URL which will forward the requests to your localhost: ![Ngrok URL](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/9e1852c-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=3d75166dbbca1cf95e9335970e56a699) If you go ahead and try out your action you'll see a frame like this ![Multi step cast action](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/5e486b4-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=599add55ee036e344d910ad79f79c378) ## Conclusion This guide taught us how to create a multi-step cast action, which returns a frame! If you want to look at the completed code, check out the [GitHub Gist](https://gist.github.com/avneesh0612/69a9f0dd373a1709d2435304959b02f5). Lastly, make sure to sure what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar) and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Create Transaction Frames Source: https://docs.neynar.com/docs/create-transaction-frames Learn how to create transaction frames that enable users to interact with blockchain transactions directly from Farcaster > [!NOTE] > > See [Make agents prompt transactions](/docs/make-agents-prompt-transactions) # Debug Notifications Source: https://docs.neynar.com/docs/debug-notifications A guide to debug the mini app notifications service if you are experiencing issues where notifications are not sending properly. ## How to debug * **Step 1:** First, remove your miniapp. Then re-add it and re-enable notifications. Afterwards, retry notifications and send to yourself. * **Step 2:** Test notifications with another user. Send them the miniapp link, confirm the receiving user has enabled notifications for their Farcaster account, and then try sending another notification just to their FID. * **Step 3:** If the previous two steps have not resolved your situation, please email [support@ns.com](mailto:support@ns.com) for support. In your email, please share a screenshot of the console (right click on page, then click “inspect”). *** ## FAQ > [accordion] Why am I not receiving a notification? > > Developers often test enabling notifications with their own account using a localtunnel or ngrok domain (sometimes without even realizing they’ve enabled notifications on their test URL). When developers change their domain to a Vercel domain or something else, their notification token becomes invalid and results in failures from Farcaster’s servers (usually an “invalid domain” error). > [accordion] Why did all my notification tokens become invalid at once? > > This usually happens when the `target_url` in your notification request uses a different domain than your app is registered on. A common cause is inconsistently using the `www.` prefix — `www.example.com` and `example.com` are treated as separate domains. When Farcaster returns a `target_url_mismatch` error, the affected tokens are permanently invalidated. Ensure your `target_url` always uses the exact domain from your `farcaster.json` manifest. Once you've resolved the underlying domain or `farcaster.json` issue, reach out to [Neynar support](mailto:support@ns.com) to get your invalidated tokens re-activated. > [accordion] Why are only some users receiving notifications? > > Please see response to "Why am I not receiving a notification?" above. Additionally, there are instances where a user may be on an old mini app domain that has phased out. Users who do receive notifications are usually on the most recent domain. > [accordion] How do I migrate notifications when moving my miniapp to a new domain? > > Add the `canonicalDomain` field to the `farcaster.json` manifests on both your old and new domains, pointing to the new domain. When Farcaster picks up the updated manifest, it automatically migrates all existing notification tokens from the old domain to the canonical domain — existing users do not need to re-enable notifications. If you use the same Neynar app (and therefore the same `webhookUrl`) on both domains, notifications will work from the new domain with no additional configuration. See the [domain migration guide](/miniapps/guides/domain-migration) for the full steps. # Deploy a Token with 1 API Call Source: https://docs.neynar.com/docs/deploy-token-on-base-with-api-call Neynar covers the on-chain deployment fees and assigns the specified owner address as the token owner. > [!NOTE] > > ### Related API: [Deploy fungible](/reference/deploy-fungible) ## Prerequisites * **Neynar API Key**: Ensure you have a valid API key from Neynar. You can obtain one by signing up at [neynar.com](https://neynar.com). * **Onchain wallet**: Set up your developer wallet on [dev.neynar.com](https://dev.neynar.com) > Apps > your\_app > Wallet page * **Environment Setup**: Follow the [Getting Started Guide](/docs/getting-started-with-neynar) to set up your environment. ## API Endpoint * **Endpoint**: `/fungible` * **Method**: `POST` * **Content Type**: `multipart/form-data` ## Request Body Schema The request body should include the following fields: ```json JSON theme={"system"} { "owner": "string", // Ethereum address of the token owner "symbol": "string", // Symbol/Ticker for the token "name": "string", // Name of the token "metadata": { "media": "string", // Media file or URI associated with the token "description": "string", // Description of the token "nsfw": "string", // "true" or "false" indicating if the token is NSFW "website_link": "string", // Website link related to the token "twitter": "string", // Twitter profile link "discord": "string", // Discord server link "telegram": "string" // Telegram link }, "network": "string", // Default: "base" "factory": "string" // Default: "wow" } ``` ### Required Fields * `owner`: Ethereum address of the token creator. * `symbol`: The token's symbol or ticker. * `name`: The name of the token. ### Optional Metadata Fields * `media`: Can be a binary file (image/jpeg, image/gif, image/png) or a URI. * `description`: A brief description of the token. * `nsfw`: Indicates if the token is NSFW ("true" or "false"). * `website_link`, `twitter`, `discord`, `telegram`: Links related to the token. ## Example Request Here's an example of how to deploy a token using the Neynar API: ```javascript Javascript theme={"system"} import axios from 'axios'; import FormData from 'form-data'; const deployToken = async () => { const formData = new FormData(); formData.append('owner', '0xYourEthereumAddress'); formData.append('symbol', 'MYTKN'); formData.append('name', 'My Token'); formData.append('metadata[description]', 'This is a sample token.'); formData.append('metadata[nsfw]', 'false'); formData.append('network', 'base'); formData.append('factory', 'wow'); try { const response = await axios.post('https://api.neynar.com/fungible', formData, { headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${process.env.NEYNAR_API_KEY}` } }); console.log('Token deployed successfully:', response.data); } catch (error) { console.error('Error deploying token:', error.response.data); } }; deployToken(); ``` ## Response A successful response will include the following details: ```json JSON theme={"system"} { "contract": { "fungible": { "object": "fungible", "name": "My Token", "symbol": "MYTKN", "media": "URI of the token media", "address": "Contract address of the token", "decimals": 18 } } } ``` ## Conclusion By following this guide, you can easily deploy a fungible token on the Base network using Neynar's API. For further assistance, reach out to Neynar support or consult the [Neynar API documentation](/reference/deploy-fungible). # Understanding number diffs Source: https://docs.neynar.com/docs/differences-among-client-numbers Learn why follower counts, reactions, and other metrics differ across Farcaster clients and tools like Merkle, Recaster, and Coinbase Wallet ## Differences in numbers across clients and tools When looking at numbers across clients e.g. Merkle's Farcaster client vs Recaster vs Coinbase Wallet, you might notice differences in numbers. Specifically, number of followers, reactions, replies, etc. There can be a few different reasons for this. ### 1. Clients optionally apply filters on top of protocol data Farcaster protocol remains open to all. However, this means that the protocol has a lot of spam activity. Similar to how most email is spam and you never see it in your inbox, a lot of Farcaster protocol activity is spam and clients don't show it. ### 2. Some show raw numbers If a client is showing raw protocol data, those numbers won't match another client that is applying filters on top of raw data. Raw protocol data always has higher numbers. ### 3. Spam filters are not standardized Each clients filters as they see fit. Clients that use Neynar can use Neynar's filtering mechanisms will match each other. ## Filter spam using Neynar When using Neynar APIs, you have the choice of fetching raw data or filtered data. APIs return raw data by default, you can filter the data by turning on the [experimental flag](https://neynar.notion.site/Experimental-Features-1d2655195a8b80eb98b4d4ae7b76ae4a?source=copy_link) on any of the API requests. E.g. the first request passes the flag as `false`. ``` curl --request GET \ --url 'https://api.neynar.com/v2/farcaster/user/bulk/?fids=194' \ --header 'x-api-key: NEYNAR_API_DOCS' \ --header 'x-neynar-experimental: false' ``` while the second one passes it as `true`. ``` curl --request GET \ --url 'https://api.neynar.com/v2/farcaster/user/bulk/?fids=194' \ --header 'x-api-key: NEYNAR_API_DOCS' \ --header 'x-neynar-experimental: true' ``` You can look at the differences in follower counts below in the screenshots. With experimental flag set to `false` (raw data) ![Raw follower count showing 273791 followers](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/raw-follower-count.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=a223778451b47f5eeb5d1253bcd5ca62) With experimental flag set to `true` (filtered data) ![Filtered follower count showing 146993 followers](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/filtered-follower-count.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=85e71754bf6ac7189f47a404e5f60656) As you can see, the raw data shows **273,791 followers** while the filtered data shows **146,993 followers** - a significant difference due to spam filtering. This is different from what another client might show due to their own filtering logic. E.g. Merkle's Farcaster client shows it as 145k which is close to Neynar's 146k number but not exact. This is because each company uses their own spam filtering logic. ![rish's filtered follower count](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/rish-warpcast-follower-count.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=273f896f89bbfef16c75642d6dad3631) # Explore Event Propagation on Farcaster Source: https://docs.neynar.com/docs/explore-event-propagation-on-farcaster Search for specific messages and debug Farcaster network health [Neynar Explorer](https://explorer.neynar.com) helps developers debug event propagation1 on the Farcaster network. ### Start exploring You can use the search bar to explore the network * Node and API tabs will show where an event has propagated after clicking on the “Network response” button. Nodes / APIs that are missing events will be highlighted in red ❗️ , partially missing data will be highlighted in yellow ⚠️ and fully synced will be green ✅ ![Screenshot2025 05 14at6 52 48PM Pn](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/Explorer-nodes.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=321196d11374dd736fb6ce26fc59fd6c) * Clicking on a tab opens more details, allowing you to compare objects across nodes and APIs. ![Screenshot2025 05 14at6 47 08PM Pn](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/Explorer-network-response.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=3d9c3ba24187d998772d2ab2227bff30) ### Things you can explore * **Specific casts:** e.g. `0x1a37442d0bd166806b6bc3a780bdb51f94d96fad` (cast hash) or `https://warpcast.com/v/0xf809724b` * **Specific users:** e.g. `194` (user's fid) * **Cast search with keywords:** e.g. `$higher` * this will also show metrics of how often that keyword has appeared on the network recently * results can be filtered by `username` (cast author), `channel_id` (channel of cast) * results can be sorted by `desc_chron` or `algorithmic` * search mode can be changed between `literal`, `semantic` or `hybrid` * *read more about our [cast search API](/reference/search-casts), tap "Network response" to see event propagation for any cast* * **User search with usernames:** e.g. `rish` * *read more about our [user search API](/reference/search-user), tap "Network response" to see event propagation for any user* * **Follow relationships:** e.g. `194<>191` to see relationship between two FIDs * **Feed API results:** e.g. `https://api.neynar.com/v2/farcaster/feed/trending?limit=10&time_window=24h&provider=neynar` * put any Feed API url from Neynar docs and evaluate results in a feed like format before you build a feed client * *see our [Feed APIs](/reference/fetch-feed)* ### What else do you want to explore? Hit us up with feature requests, we want to make debugging as easy as possible for developers! 1 Read more on event propagation in [Understanding Message Propagation on Farcaster Mainnet](https://neynar.com/blog/understanding-message-propagation-on-farcaster-mainnet). The blog was written at the time of hubs but similar debugging for snapchain syncs apply. # Farcaster Actions Spec Source: https://docs.neynar.com/docs/farcaster-actions-spec Complete specification for Farcaster Actions - enabling secure cross-app communication and actions using Farcaster signers > [!NOTE] > > ### API Endpoints > > Related API reference [Publish Farcaster Action](/reference/publish-farcaster-action) ## Introduction Farcaster Actions is a spec that allows Farcaster applications to securely communicate and perform actions on behalf of users across different apps. It enables an app (referred to as **App A**) to send data or trigger actions in another app (**App B**) on behalf of a mutual user (e.g., **Carol**) by signing messages using the user's Farcaster signer. This document provides an overview of how Farcaster Actions works and guides developers in implementing this functionality within their Neynar applications. ## Overview of Farcaster Actions * **Purpose**: Facilitate secure cross-app interactions on behalf of users. * **Participants**: * **User**: The individual (e.g., Carol) who authorizes actions between apps. * **App A**: The initiating app requesting to perform an action in App B. * **App B**: The receiving app that processes the action from App A. * **Neynar API**: The intermediary that assists in signing and forwarding the action securely. ## Workflow ### 1. Requesting Signer Access **App A** requests a Farcaster signer from the user (**Carol**) * **User Authorization**: Carol approves the signer. * **Signer UUID**: Upon approval, App A receives a unique identifier (UUID) for Carol's signer. ### 2. Making an API Call with Signer UUID and Metadata App A prepares to send an action to App B by making an API call to the Neynar API, including: * **Signer UUID**: The unique identifier for Carol's signer. * **Destination base URL**: The base URL of App B where the action should be sent. * **Action Payload**: An object containing the type of action and any necessary payload data. ### 3. Neynar API Produces a Signature The Neynar API processes the request from App A: * **Signature Generation**: Neynar uses Carol's signer to generate a cryptographic signature * **Bearer Token Creation**: A bearer token is created, encapsulating the signature and additional metadata, which will be used for authentication. ### 4. Forwarding the Signed Message to App B Neynar forwards the signed action to App B: * **HTTP Request**: An HTTP POST request is sent to App B's specified endpoint. * **Headers**: The request includes an `Authorization` header containing the bearer token. * **Body**: The action payload is included in the request body. ### 5. App B Verifies the Signature Upon receiving the request, App B performs the following: * **Signature Verification**: App B verifies the bearer token using Carol's public key and ensures the signature is valid. * **Farcaster ID (fid)**: The token includes Carol's fid, confirming her identity. * **Action Processing**: If verification succeeds, App B processes the action and updates its state accordingly. ## Implementation Details ### For App A Developers **1. Request Signer Access** * Checkout the documentation on [managed signers](/docs/integrate-managed-signers) **2. Prepare the Action Request** * Define the action payload, including the type and any necessary data. * Specify the destination base URL of App B. **3. Call the Neynar API** * Make a POST request to the Neynar API endpoint (POST - `/v2/farcaster/action`) with the following structure: ```json json theme={"system"} { "signer_uuid": "uuid-of-the-signer", "url": "https://appb.xyz", "action": { "type": "actionType", "payload": { // Action-specific data } } } ``` **4. Handle the Response** * The Neynar API will forward the action to App B and return the response from App B. * Ensure proper error handling for cases where the action fails or the signature is invalid. ### For App B Developers **1. Set Up an Endpoint to Receive Actions** * Create an HTTP endpoint to receive POST `/api/farcaster/action`requests from the Neynar API. * Ensure the endpoint URL is accessible and secured via HTTPS. **2. Extract and Verify the Bearer Token** * Extract the `Authorization` header from incoming requests. * Decode the bearer token to retrieve the header, payload, and signature. * Use the fid and public key from the token header to verify the signature against the payload. **3. Process the Action** * Once the signature is verified, extract the action payload from the request body. * Perform the necessary operations based on the action type and payload. * Update your application's state or database as required. **4. Respond to the Request** * Return an appropriate HTTP response indicating success or failure. * Include any necessary data in the response body for App A to process. ## Security Considerations * **Token Expiration**: The bearer token has a short expiration time (5 minutes) to enhance security. * **Signer Confidentiality**: Private keys are managed securely by Neynar and are never exposed to apps. * **Data Integrity**: Signatures ensure that the action payload cannot be tampered with during transit. ## Conclusion Farcaster Actions provides a secure and efficient way for Neynar apps to interact on behalf of users. By leveraging cryptographic signatures and Neynar's API, apps can ensure that cross-app actions are authenticated and authorized by the user, enhancing trust and interoperability within the Neynar ecosystem. ## Example ### Action Schema The action request sent to the Neynar API follows this schema: ```json JSON theme={"system"} { "signer_uuid": "string (UUID format)", "url": "string (valid URL)", "action": { "type": "string", "payload": { // Object containing action-specific data } } } ``` Sample Request from App A to Neynar API **POST** `/v2/farcaster/action` **Content-Type:** `application/json` ```json JSON theme={"system"} { "signer_uuid": "123e4567-e89b-12d3-a456-426614174000", "url": "https://appb.example.com", "action": { "type": "sendMessage", "payload": { "message": "Hello from App A!" } } } ``` Forwarded Request from Neynar API to App B **POST** `/api/farcaster/action` **Content-Type:** `application/json` **Authorization:** `Bearer Token` ```json JSON theme={"system"} { "action": { "type": "sendMessage", "payload": { "message": "Hello from App A!" } } } ``` App B would then verify the bearer token and process the action accordingly. # Farcaster Bot with Dedicated Signers Source: https://docs.neynar.com/docs/farcaster-bot-with-dedicated-signers Create a Farcaster bot on Neynar in a few quick steps > [!NOTE] > > Simplest way to start is to clone this git repo that has a sample bot ready to go: [https://github.com/neynarxyz/farcaster-examples](https://github.com/neynarxyz/farcaster-examples) In our `farcaster-examples` repo, `gm_bot` is an automated messaging bot designed to cast a 'gm ' message in Warpcast every day at a scheduled time. The bot operates continuously as long as the system remains online. It leverages [Neynar API](https://docs.neynar.com/) and is built using [@neynar/nodejs-sdk](https://www.npmjs.com/package/@neynar/nodejs-sdk). ## Prerequisites * [Node.js](https://nodejs.org/en/): A JavaScript runtime built on Chrome's V8 JavaScript engine. Ensure you have Node.js installed on your system. ## Installation ### Setting Up the Environment **1. Install PM2** PM2 is a process manager for Node.js applications. Install it globally using npm: ```bash bash theme={"system"} npm install -g pm2 ``` **2. Install Project Dependencies** Navigate to the project directory and run one of the following commands to install all required dependencies: ```Text Yarn theme={"system"} yarn install ``` ```bash npm theme={"system"} npm install ``` **3. Configure Environment Variables** * Copy the example environment file: ```bash bash theme={"system"} cp .env.example .env ``` * Open the repo in your favorite editor and edit `.env` file to add your `NEYNAR_API_KEY` and `FARCASTER_BOT_MNEMONIC`. Optionally, you can also specify `PUBLISH_CAST_TIME` and `TIME_ZONE` for custom scheduling. ### Generating a Signer Before running the bot, you need to generate a signer and get it approved via an onchain transaction. To execute the transaction, you'll need a browser extension wallet with funded roughly \$2 worth of OP ETH on the Optimism mainnet. This is crucial for the bot's operation. Run the following command in the terminal: ```bash bash theme={"system"} yarn get-approved-signer ``` *This command will create some logs in your terminal. We will use these logs for upcoming steps below.* ### Approving a signer In order to get an approved signer you need to do an on-chain transaction on OP mainnet. **Step 1** Go to Farcaster KeyGateway optimism explorer [https://optimistic.etherscan.io/address/0x00000000fc56947c7e7183f8ca4b62398caadf0b#writeContract](https://optimistic.etherscan.io/address/0x00000000fc56947c7e7183f8ca4b62398caadf0b#writeContract) **Step 2** Connect to Web3. **Step 3** Remember the terminal logs we generated one of the earlier steps? You will see values for `fidOwner`, `keyType`, `key`, `metadataType`, `metadata`, `deadline`, `sig` in your terminal logs. Navigate to `addFor` function and add following values inside the respective placeholders. **Step 4** Press "Write" to execute the transaction. This will create a signer for your mnemonic on the OP mainnet. ## Running the Bot **1. Start the Bot** Launch the bot using the following command: ```bash Yarn theme={"system"} yarn start ``` ```Text npm theme={"system"} npm run start ``` **2. Verify the Process** Ensure that the bot is running correctly with: ```bash bash theme={"system"} pm2 status ``` **3. View Logs** To check the bot's activity logs, use: ```bash bash theme={"system"} pm2 logs ``` **4. Stopping the Bot** If you need to stop the bot, use: ```bash bash theme={"system"} pm2 kill ``` ## License `gm_bot` is released under the MIT License. This license permits free use, modification, and distribution of the software, with the requirement that the original copyright and license notice are included in any substantial portion of the work. ## FAQs/Troubleshooting > [accordion] What if gm\_bot stops sending messages? > > Check the PM2 logs for any errors and ensure your system's time settings align with the specified `TIME_ZONE`, also ensure that the process is running. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Features (hidden from roadmap right now) Source: https://docs.neynar.com/docs/features-hidden-from-roadmap-right-now Upcoming Neynar features and capabilities not yet shown on the public roadmap including Frame Studio enhancements and API improvements ## Features ### Neynar Frame Studio * Rapid prototyping using a GUI interface * Pre made templates that you can turn into a frame very quickly (galleries, memes, product checkout, taking in text input, NFT minting) * Neynar provided frame hosting * Built in usage analytics for every frame \[coming soon] * Self serve data export for frames that take in text input \[coming soon] * custom domains for your frame URLs \[coming soon] ### Neynar APIs * APIs to embed frames into your client * APIs to validate frame data and hydrate user data, user reactions and cast data in one API call * Extensibility using flex URLs with detailed context on the cast, cast author, signer, interactor, and actions \[coming soon] * Programmatic access to building frames using APIs \[coming soon] * Neynar provided hosting for programmatically created frames \[coming soon] * Improved security & spam protection by privatizing POST API endpoints \[coming soon] * Frame usage analytics over API \[coming soon] If you have questions on how to get started see our guides on [building frames](/docs/how-to-build-farcaster-frames-with-neynar) and [embedding frames](/docs/how-to-embed-farcaster-frames-in-your-app-with-neynar). More details on Neynar frame architecture below. ### Simple Pages There are two ways to create simple pages inside a frame #### 1. Frame Builder GUI Check out the [Neynar Frame Studio](https://dev.neynar.com/frames) ```text JSON theme={"system"} { "uuid": "5ec484f5-efaf-4bda-9a3f-0579232a386a", "image": "https://i.imgur.com/gpn83Gm.png", "title": "Frame Shopper", "buttons": [ { "title": "Shop", "next_page": { "flex_url": "https://frameshopper.xyz/api/shop" }, "action_type": "post_flex" } ], "version": "vNext" } ``` When the action button on this page is tapped, Neynar will: 1. Receive an initial post from the client 2. Validate the action 3. Hydrate context details (as needed) - cast, cast author, interactor, signer, and actions 4. Make a POST request to the `flex_url` on the Frame developer's server with all the action context 5. Frame developer should respond with a status 200 and a JSON representation of the next page 6. Neynar will return the next page to the client 7. #### 2. Create simple pages using simple REST APIs GET, PUT, POST, DELETE at `/v2/farcaster/frame` \[coming soon] ### Flex Pages \[coming soon] Frame developers can build dynamic pages using the `flex_url` All frame actions with a `flex_url` will be forwarded to the URL as a POST Here's an example frame with a static first page. ```text JSON theme={"system"} { "uuid": "5ec484f5-efaf-4bda-9a3f-0579232a386a", "image": "https://i.imgur.com/gpn83Gm.png", "title": "Frame Shopper", "buttons": [ { "title": "Shop", "next_page": { "flex_url": "https://frameshopper.xyz/api/shop" }, "action_type": "post_flex" } ], "version": "vNext" } ``` # Casts by Embed in Farcaster Source: https://docs.neynar.com/docs/fetch-casts-by-embed-in-farcaster Show Farcaster casts that have attachments with Neynar > [!NOTE] > > ### Related API reference [Fetch Feed](/reference/fetch-feed) This guide demonstrates how to use the Neynar SDK to casts that contain a specific embed (eg. cast linking to github.com or youtube.com). Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize Neynar client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; import { FeedType } from "@neynar/nodejs-sdk/build/api/index.js"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey:process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Then fetch casts linking to github.com: ```javascript Javascript theme={"system"} const feedType = FeedType.Filter; const filterType= "embed_url"; const embedUrl= "github.com"; const result = await client.fetchFeed({feedType, filterType, embedUrl }); console.log(result); ``` Replace `github.com` with any other embed url to fetch casts linking to that url. To fetch casts linking to youtube.com: ```javascript Javascript theme={"system"} const feedType = FeedType.Filter; const filterType= "embed_url"; const embedUrl= "youtube.com"; const result = await client.fetchFeed({feedType, filterType, embedUrl }); console.log(result); ``` And... Spotify: ```javascript Javascript theme={"system"} const feedType = FeedType.Filter; const filterType= "embed_url"; const embedUrl= "open.spotify.com"; const result = await client.fetchFeed({feedType, filterType, embedUrl }); console.log(result); ``` Example output: ```json theme={"system"} { casts: [ { object: "cast_hydrated", hash: "0x9f617c43f00308cdb46b7b72f067b01557d53733", thread_hash: "0x9f617c43f00308cdb46b7b72f067b01557d53733", parent_hash: null, parent_url: "chain://eip155:7777777/erc721:0xe96c21b136a477a6a97332694f0caae9fbb05634", parent_author: [Object ...], author: [Object ...], text: "Yo, we got kids to raise and bills to pay, enemies to lay down when they stand in our way, it's only us \n\nhttps://open.spotify.com/track/0SlljMy4uEgoVPCyavtcHH", timestamp: "2023-12-11T04:06:57.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] }, { object: "cast_hydrated", hash: "0xe70d9d52c5019b247fa93f76779296322676a4e5", thread_hash: "0xe70d9d52c5019b247fa93f76779296322676a4e5", parent_hash: null, parent_url: null, parent_author: [Object ...], author: [Object ...], text: "For the Intuitives (Part 1)", timestamp: "2023-12-11T02:11:27.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] }, { object: "cast_hydrated", hash: "0xee6719ac805758be5bd54744bec63b7ec0bc4d3e", thread_hash: "0xee6719ac805758be5bd54744bec63b7ec0bc4d3e", parent_hash: null, parent_url: null, parent_author: [Object ...], author: [Object ...], text: "EP 214 Douglas Rushkoff on Leaving Social Media", timestamp: "2023-12-11T02:11:18.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] }, { object: "cast_hydrated", hash: "0xebe7e89b1a3e91c96f99ecf3ce7d2797e3b118b6", thread_hash: "0xebe7e89b1a3e91c96f99ecf3ce7d2797e3b118b6", parent_hash: null, parent_url: null, parent_author: [Object ...], author: [Object ...], text: "#64 AI & the Global Brain: Peter Russell", timestamp: "2023-12-11T02:11:04.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] }, { object: "cast_hydrated", hash: "0x93276da072a2902a3568da21203588995e4ba752", thread_hash: "0x93276da072a2902a3568da21203588995e4ba752", parent_hash: null, parent_url: null, parent_author: [Object ...], author: [Object ...], text: "Systems Thinking - Tomas Bjorkman - Consciousness", timestamp: "2023-12-11T02:10:26.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] } ], next: { cursor: "eyJ0aW1lc3RhbXAiOiIyMDIzLTEyLTExIDAyOjEwOjI2LjAwMDAwMDAifQ==" } } ``` To fetch the next page: ```javascript Javascript theme={"system"} const filter= "filter"; const filterType= "embed_url"; const cursor= result.next.cursor const nextResult = await client.fetchFeed({filter, filterType, embedUrl, cursor }); ``` There you go, fetching casts with specific embeds in Farcaster with Neynar SDK! > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Relevant Holders for Coins Source: https://docs.neynar.com/docs/fetch-relevant-holders-for-coin This guide provides an overview of how to use the Neynar API to fetch relevant holders of a fungible token. > [!NOTE] > > ### Related API: [Relevant owners](/reference/fetch-relevant-fungible-owners) ## Fetching relevant holders for a coin The API identifies relevant users on Farcaster who hold the same coin, based on the users followed by the viewer. The API works across EVM (Ethereum, Base, etc.) and Solana networks. ### Overview The `fetch-relevant-fungible-owners` API endpoint allows you to retrieve a list of relevant users who own a specific fungible token. This is particularly useful for applications that want to display social connections around token ownership. #### Key Features * **Network Support**: EVM + Solana * **Viewer Context**: The API customizes the response based on the `viewer_fid`, respecting the viewer's mutes and blocks. * **Relevance**: Returns users from the network who are most relevant to the viewer ### API Endpoint #### Endpoint ``` GET /fungible/owner/relevant ``` #### Parameters * **contract\_address** (string, required): The contract address of the fungible asset. * Example: `0x0db510e79909666d6dec7f5e49370838c16d950f` * **networks** (enum string): Network to fetch tokens from. * Example: `["base"]` * **viewer\_fid** (integer, required): The FID of the user to customize this response for. This will also return a list of owners that respects this user's mutes and blocks. * Example: `194` #### Response * **200 OK**: Successful response containing relevant fungible owners. * **top\_relevant\_owners\_hydrated**: An array of user objects representing the top relevant owners. * **all\_relevant\_owners\_dehydrated**: An array of user objects representing all relevant owners. * **400 Bad Request**: The request was invalid, often due to missing or incorrect parameters. ### Example Request ```http HTTP theme={"system"} GET /fungible/owner/relevant?contract_address=0x0db510e79909666d6dec7f5e49370838c16d950f&networks=base&viewer_fid=194 ``` ### Example Response ```json JSON theme={"system"} { "top_relevant_owners_hydrated": [ { "fid": 123, "username": "alice.eth", "display_name": "Alice", "pfp_url": "https://example.com/alice.jpg" }, { "fid": 456, "username": "bob.eth", "display_name": "Bob", "pfp_url": "https://example.com/bob.jpg" } ], "all_relevant_owners_dehydrated": [ { "fid": 789, "username": "charlie.eth", "display_name": "Charlie", "pfp_url": "https://example.com/charlie.jpg" } ] } ``` ### Implementation Example To implement this API call in a JavaScript environment using the Neynar SDK, follow the steps below: **1. Initialize the Neynar Client** ```javascript javascript theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` **2. Fetch Relevant Fungible Owners** ```javascript javascript theme={"system"} async function fetchRelevantOwners(contractAddress, viewerFid) { try { const response = await client.fetchRelevantFungibleOwners({ contract_address: contractAddress, networks: ["base"], viewer_fid: viewerFid, }); console.log("Relevant Owners:", response); } catch (error) { console.error("Error fetching relevant owners:", error); } } fetchRelevantOwners("0x0db510e79909666d6dec7f5e49370838c16d950f", 194); ``` ## Increase conversions by adding personalization Users are more likely to trade / take onchain actions when they get social proof. Facebook proved this long ago when they started showing "X, Y, Z like this page" above an ad. The same holds true in crypto. For further information, refer to the [Neynar API Documentation](/reference/fetch-relevant-fungible-owners). If you have any questions or need support, feel free to reach out to us at [support@ns.com](mailto:support@ns.com). # Relevant Holders for Coins Source: https://docs.neynar.com/docs/fetch-relevant-holders-for-coin-on-base This guide provides a comprehensive overview of how to use the Neynar API to fetch relevant holders of a fungible token. > [!NOTE] > > This doc has been updated. See [Relevant owners](/docs/fetch-relevant-holders-for-coin) # Fetch signers Source: https://docs.neynar.com/docs/fetch-signers-1 The following guides show how to fetch signers if you don't have access to the custody address mnemonic of the user > [card] [Fetch Signers - Backend](/docs/fetch-signers-backend) > [card] [Fetch Signers - Frontend (Wallet Integration)](/docs/fetch-signers-frontend-wallet-integration) # Fetch Signers - Backend Source: https://docs.neynar.com/docs/fetch-signers-backend This guide demonstrates how to get a list of signers for an account if the developer has the user's mnemonic/account private key (If not check: [Frontend (Wallet Integration)](/docs/fetch-signers-frontend-wallet-integration)) > [!NOTE] > > ### Related API: [List signers](/reference/fetch-signers) ## **Prerequisites** **1. Node.js Installed** Ensure you have Nodejs installed on your system. You can download it from [Node.js' official website](https://nodejs.org/). **2. API Key and Mnemonic** Obtain an API key from the [dev portal](https://dev.neynar.com/app) Ensure you have a valid Ethereum mnemonic phrase of the account with a signer associated with the above API key. **3. Dependencies Installed** Install the required packages: ```bash Shell theme={"system"} yarn add siwe viem @neynar/nodejs-sdk ``` ## **Code Breakdown and Steps** **1. Import Required Libraries** The code begins by importing the necessary libraries: ```javascript Javascript theme={"system"} import { SiweMessage } from "siwe"; import { mnemonicToAccount } from "viem/accounts"; import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; ``` **2. Define Your Mnemonic** Replace `"YOUR_MNEMONIC_HERE"` with your Ethereum mnemonic phrase: ```javascript Javascript theme={"system"} const mnemonic = "YOUR_MNEMONIC_HERE"; ``` **3. Convert Mnemonic to Account** The `mnemonicToAccount` function converts your mnemonic into an account object: ```javascript Javascript theme={"system"} const account = mnemonicToAccount(mnemonic); ``` **4. Configure Neynar API Client** Replace `"YOUR_API_KEY_HERE"` with your API key and set the correct base path for the Neynar API: ```javascript Javascript theme={"system"} const config = new Configuration({ apiKey: "YOUR_API_KEY_HERE", }); const client = new NeynarAPIClient(config); ``` **5. Create the SIWE Message** The `createSiweMessage` function generates a SIWE message with details such as domain, address, and [nonce](/reference/fetch-nonce): ```javascript Javascript theme={"system"} async function createSiweMessage(address, statement) { const { nonce } = await client.fetchNonce(); const message = new SiweMessage({ scheme: "http", domain: "localhost:8080", address, statement, uri: "http://localhost:8080", version: "1", chainId: "1", nonce: nonce, }); return message.prepareMessage(); } ``` **6. Sign and Verify the Message** The `fetchSigners` function handles the signing process and fetches signers: **Note:** The `address` should be the `custody_address` of the farcaster account ([Check custody\_address in User API](/reference/fetch-bulk-users)) ```javascript Javascript theme={"system"} async function fetchSigners() { const address = account.address; let message = await createSiweMessage( address, "Sign in with Ethereum to the app." ); const signature = await account.signMessage({ message }); const { signers } = await client.fetchSigners({ message, signature }); return signers; } ``` **7. Execute the Function** Call the `fetchSigners` function and handle success or errors: ```javascript Javascript theme={"system"} fetchSigners() .then((signers) => { console.log("\n\nsigners:", signers, "\n\n"); }) .catch((error) => { console.error("error:", error); }); ``` ## **Expected Output** ```json JSON theme={"system"} [ { "object": "signer", "signer_uuid": "19d0c5fd-9b33-4a48-a0e2-bc7b0555baec", "public_key": "0xe4abc135d40f8a6ee216d1a6f2f4e82476dff75f71ea53c5bdebca43f5c415b7", "status": "approved", "fid": 0 }, { "object": "signer", "signer_uuid": "08c71152-c552-42e7-b094-f510ff44e9cb", "public_key": "0xe4cd577123def73295dd9991c589b59b48cdc976b5e83a9ad8d2a13fcfcc0e72", "status": "approved", "fid": 0 } ] ``` For additional help, [feel free to contact us](mailto:support@ns.com). # Fetch Signers - Frontend (Wallet Integration) Source: https://docs.neynar.com/docs/fetch-signers-frontend-wallet-integration This guide demonstrates how to get a list of signers for an account if the developer can't access the user's mnemonic. (If the developer has access to the mnemonic, check: [Backend](/docs/fetch-signers-backend)) > [!NOTE] > > ### Related API: [Fetch signers](/docs/fetch-signers-1) ## **Important Note** **The Neynar Client Instantiation and API calls (`fetchNonce` and `fetchSigners`) should ideally be performed on the backend to protect your API key and maintain security.** ## **Prerequisites** **1. Ethereum-Enabled Browser** Browser Ensure you are using a browser with a wallet like MetaMask installed. **2. API Key** Obtain an API key from [dev portal](https://dev.neynar.com/app) **3. Dependencies Installed** Install the required packages: ```shell shell theme={"system"} yarn add siwe viem @neynar/nodejs-sdk ``` ## **Code Breakdown and Steps** **1. Import Required Libraries** The code starts by importing the necessary libraries: ```javascript Javascript theme={"system"} import { SiweMessage } from "siwe"; import { createWalletClient, custom, publicActions } from "viem"; import { optimism } from "viem/chains"; import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; ``` **2. Configure Neynar API Client** Set up the Neynar API client with your API key and base path. Note that this should ideally be done on the backend for security reasons: ```javascript Javascript theme={"system"} const config = new Configuration({ apiKey: "YOUR_API_KEY_HERE", }); const client = new NeynarAPIClient(config); ``` **3. Create the Wallet Client** The `createWalletClient` function initializes a wallet client using the `viem` library. It uses `window.ethereum` to connect to the browser's wallet: ```javascript Javascript theme={"system"} const wallet = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }).extend(publicActions); ``` **4. Create the SIWE Message** The `createSiweMessage` function generates a SIWE message with details such as domain, address, and [nonce](/reference/fetch-nonce): ```javascript Javascript theme={"system"} async function createSiweMessage(address, statement) { const { nonce } = await client.fetchNonce(); const message = new SiweMessage({ scheme: "http", domain: "localhost:8080", address, statement, uri: "http://localhost:8080", version: "1", chainId: "1", nonce: nonce, }); return message.prepareMessage(); } ``` **5. Sign and Verify the Message** The `fetchSigners` function retrieves the user's Ethereum address, generates a SIWE message, signs it, and verifies the signature using the Neynar API. **Note:** 1. Neynar API should ideally be accessed from the backend 2. The `address` should be the `custody_address` of the farcaster account ([Check custody\_address in User API](/reference/fetch-bulk-users)) ```javascript Javascript theme={"system"} async function fetchSigners() { const address = (await wallet.getAddresses())[0]; let message = await createSiweMessage( address, "Sign in with Ethereum to the app." ); const signature = await wallet.signMessage({ account: address, message }); const { signers } = await client.fetchSigners({ message, signature }); return signers; } ``` **6. Execute the Function** Call the `fetchSigners` function and handle the response or errors: ```javascript Javascript theme={"system"} fetchSigners() .then((signers) => { console.log("\n\nsigners:", signers, "\n\n"); }) .catch((error) => { console.error("error:", error); }); ``` ## **Expected Output** ```json JSON theme={"system"} [ { "object": "signer", "signer_uuid": "19d0c5fd-9b33-4a48-a0e2-bc7b0555baec", "public_key": "0xe4abc135d40f8a6ee216d1a6f2f4e82476dff75f71ea53c5bdebca43f5c415b7", "status": "approved", "fid": 0 }, { "object": "signer", "signer_uuid": "08c71152-c552-42e7-b094-f510ff44e9cb", "public_key": "0xe4cd577123def73295dd9991c589b59b48cdc976b5e83a9ad8d2a13fcfcc0e72", "status": "approved", "fid": 0 } ] ``` For additional help, [feel free to contact us](mailto:support@ns.com). # Fetch & Display Farcaster Feeds with Neynar API Source: https://docs.neynar.com/docs/fetching-casts-from-memes-channel-in-farcaster Complete guide to fetching and displaying casts from specific Farcaster channels using Neynar SDK. Learn how to retrieve channel feeds, filter content by channel IDs, and build engaging social media applications with real-time Farcaster data. > [!NOTE] > > ### Related API reference [Fetch Feed by Channel IDs](/reference/fetch-feed-by-channel-ids) Channels are "subreddits inside Farcaster." Technically, a channel is a collection of casts that share a common channel\_id. For example, the [memes channel](https://warpcast.com/~/channel/memes) is a collection of casts that share the channel\_id `memes`. ## Fetching Casts from a Channel This guide demonstrates how to use the Neynar SDK to fetch casts from a channel. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; import { FeedType,FilterType } from "@neynar/nodejs-sdk/build/api/index.js"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Then, fetch the feed in the memes channel. ```javascript Javascript theme={"system"} const client = new NeynarAPIClient(config); const channelId = "memes"; const filterType = FilterType.ChannelId; const feed = await client.fetchFeed({ feedType: FeedType.Filter, filterType, channelId, }); console.log(feed); ``` Example output: ```json Json theme={"system"} { casts: [ { object: "cast_hydrated", hash: "0xf17168571d5e403f3b608ea2cc09a0b711d4c4fc", thread_hash: "0xf17168571d5e403f3b608ea2cc09a0b711d4c4fc", parent_hash: null, parent_url: "chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61", parent_author: [Object ...], author: [Object ...], text: "", timestamp: "2023-11-27T14:40:12.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] }, { object: "cast_hydrated", hash: "0x344dcd9c7c2671450628aacd0bbb8e29ea2e8809", thread_hash: "0x344dcd9c7c2671450628aacd0bbb8e29ea2e8809", parent_hash: null, parent_url: "chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61", parent_author: [Object ...], author: [Object ...], text: "sorry", timestamp: "2023-11-27T14:24:32.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] }, { object: "cast_hydrated", hash: "0x68b94ec2a10ebad8b13e3b673f0db02dd3280f42", thread_hash: "0x68b94ec2a10ebad8b13e3b673f0db02dd3280f42", parent_hash: null, parent_url: "chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61", parent_author: [Object ...], author: [Object ...], text: "man today is such a nice morning", timestamp: "2023-11-27T13:30:11.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] } ], next: { cursor: "eyJ0aW1lc3RhbXAiOiIyMDIzLTExLTI3IDEzOjMwOjExLjAwMDAwMDAifQ==" } } ``` To fetch the next page of the feed, use the cursor: ```javascript Javascript theme={"system"} const nextFeed = await client.fetchFeed({ feedType: FeedType.Filter, filterType: FilterType.ChannelId, channelId, cursor: feed.next.cursor, }); ``` There you go! You now know how to fetch casts from a Farcaster channel with Neynar SDK. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Notifications in Channels Source: https://docs.neynar.com/docs/fetching-channel-specific-notification-in-farcaster Learn how to fetch channel-specific notifications for Farcaster users using Neynar API. Build focused notification systems that filter alerts by specific channels and parent URLs, perfect for channel-focused Farcaster clients and applications. > [!NOTE] > > ### Related APIs: (1) [For user by channel](/reference/fetch-channel-notifications-for-user) (2) [For user by parent\_urls](/reference/fetch-notifications-by-parent-url-for-user) ## Fetching Channel-Specific Notifications Say you have a Farcaster client focusing on a specific channel, and you want to fetch notifications for a specific FID for that specific channel. We got you covered! This guide will show you how to fetch notifications for a specific FID for a specific channel. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; import { FeedType, FilterType } from "@neynar/nodejs-sdk/build/api/index.js"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Let's say you have a Nouns-specific Farcaster client and you want to fetch notifications for a specific FID. > [!NOTE] > > ### channel\_name to parent\_url mapping > > All parent\_url to channel\_name mappings can be found at this [Github repo](https://github.com/neynarxyz/farcaster-channels/blob/main/warpcast.json), along with other channel metadata. > > This repo is open source so feel free to submit PRs for additional channel data if you see anything missing. ```javascript Javascript theme={"system"} const nounsChannelUrl = "chain://eip155:1/erc721:0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03"; const userFID = 3; const notifications = await client.fetchChannelNotificationsForUser({fid:userFID,channelIds: [ nounsChannelUrl, ]}); ``` Example output: ```json theme={"system"} { "notifications": [ { "object": "notification", "most_recent_timestamp": "2023-12-08T06:31:10.000Z", "type": "mention", "cast": { "object": "cast_hydrated", "hash": "0xd16b71018cc53c667e771bb4c13627555a32b5d4", "thread_hash": "b7fc569081242aadeb29f8254931daf31c9e1017", "parent_hash": "243f539607f4ea7b4117a169433c1ea8295d32fc", "parent_url": null, "parent_author": { "fid": "3895" }, "author": { "object": "user", "fid": 1079, "custody_address": "0xeb31e335531c06ca4d8fe58bed841e9031de4ee4", "username": "joshuafisher.eth", "display_name": "Joshua Fisher", "pfp_url": "https://i.imgur.com/1pn4CEg.jpg", "profile": { "bio": { "text": "⌐◨-◨ ‘ing around. Working on Nouns Creative focused on narrative works. Music Publisher & Manager by day.", "mentioned_profiles": [] } }, "follower_count": 422, "following_count": 149, "verifications": [ "0xbd7dbab9aeb52d6c8d0e80fcebde3af4cc86204a" ], "active_status": "active" }, "text": "Would be tasty if we could buy this with Warps @dwr.eth", "timestamp": "2023-12-08T06:31:10.000Z", "embeds": [], "reactions": { "likes": [ { "fid": 1898, "fname": "boscolo.eth" }, { "fid": 14700, "fname": "brsn" }, { "fid": 3, "fname": "dwr.eth" }, { "fid": 576, "fname": "nonlinear.eth" } ], "recasts": [] }, "replies": { "count": 0 }, "mentioned_profiles": [ { "object": "user", "fid": 3, "custody_address": "0x6b0bda3f2ffed5efc83fa8c024acff1dd45793f1", "username": "dwr.eth", "display_name": "Dan Romero", "pfp_url": "https://res.cloudinary.com/merkle-manufactory/image/fetch/c_fill,f_png,w_256/https://lh3.googleusercontent.com/MyUBL0xHzMeBu7DXQAqv0bM9y6s4i4qjnhcXz5fxZKS3gwWgtamxxmxzCJX7m2cuYeGalyseCA2Y6OBKDMR06TWg2uwknnhdkDA1AA", "profile": { "bio": { "text": "Working on Farcaster and Warpcast.", "mentioned_profiles": [] } }, "follower_count": 30657, "following_count": 2722, "verifications": [ "0xd7029bdea1c17493893aafe29aad69ef892b8ff2", "0xa14b4c95b5247199d74c5578531b4887ca5e4909", "0xb877f7bb52d28f06e60f557c00a56225124b357f", "0x8fc5d6afe572fefc4ec153587b63ce543f6fa2ea" ], "active_status": "active" } ] } }, { "object": "notification", "most_recent_timestamp": "2023-12-08T06:09:50.000Z", "type": "mention", "cast": { "object": "cast_hydrated", "hash": "0xbf05b5bb119d4f1b8c514fbc75c23f9c8755dfd7", "thread_hash": "f750ed31ece83fa486be9b37782d57d1b679f925", "parent_hash": "bde97a78c48ed92ba01c2c2f0cfd521b52f524bc", "parent_url": null, "parent_author": { "fid": "7143" }, "author": { "object": "user", "fid": 1097, "custody_address": "0xe12b01100a4be7e79ddbd5dd939c97d12e890ac7", "username": "noun40", "display_name": "Noun 40", "pfp_url": "https://openseauserdata.com/files/faa77932343776d1237e5dd82aa12e76.svg", "profile": { "bio": { "text": "cofounder/cto @ bitwise", "mentioned_profiles": [] } }, "follower_count": 15682, "following_count": 55, "verifications": [ "0xae65e700f3f8904ac1007d47a5309dd26f8146c0" ], "active_status": "active" }, "text": "oh hmm i wonder if there’s a way to expose this data of channel subscribers @dwr.eth @v?", "timestamp": "2023-12-08T06:09:50.000Z", "embeds": [], "reactions": { "likes": [ { "fid": 194490, "fname": "0xdbao" }, { "fid": 197459, "fname": "cryptoworldao" }, { "fid": 193703, "fname": "ai13" } ], "recasts": [] }, "replies": { "count": 1 }, "mentioned_profiles": [ { "object": "user", "fid": 3, "custody_address": "0x6b0bda3f2ffed5efc83fa8c024acff1dd45793f1", "username": "dwr.eth", "display_name": "Dan Romero", "pfp_url": "https://res.cloudinary.com/merkle-manufactory/image/fetch/c_fill,f_png,w_256/https://lh3.googleusercontent.com/MyUBL0xHzMeBu7DXQAqv0bM9y6s4i4qjnhcXz5fxZKS3gwWgtamxxmxzCJX7m2cuYeGalyseCA2Y6OBKDMR06TWg2uwknnhdkDA1AA", "profile": { "bio": { "text": "Working on Farcaster and Warpcast.", "mentioned_profiles": [] } }, "follower_count": 30657, "following_count": 2722, "verifications": [ "0xd7029bdea1c17493893aafe29aad69ef892b8ff2", "0xa14b4c95b5247199d74c5578531b4887ca5e4909", "0xb877f7bb52d28f06e60f557c00a56225124b357f", "0x8fc5d6afe572fefc4ec153587b63ce543f6fa2ea" ], "active_status": "active" }, { "object": "user", "fid": 2, "custody_address": "0x4114e33eb831858649ea3702e1c9a2db3f626446", "username": "v", "display_name": "Varun Srinivasan", "pfp_url": "https://i.seadn.io/gae/sYAr036bd0bRpj7OX6B-F-MqLGznVkK3--DSneL_BT5GX4NZJ3Zu91PgjpD9-xuVJtHq0qirJfPZeMKrahz8Us2Tj_X8qdNPYC-imqs?w=500&auto=format", "profile": { "bio": { "text": "Technowatermelon. Elder Millenial. Building Farcaster. \n\nnf.td/varun", "mentioned_profiles": [] } }, "follower_count": 27025, "following_count": 974, "verifications": [ "0x91031dcfdea024b4d51e775486111d2b2a715871", "0x182327170fc284caaa5b1bc3e3878233f529d741" ], "active_status": "active" } ] } }, { "object": "notification", "most_recent_timestamp": "2023-12-03T23:35:12.000Z", "type": "mention", "cast": { "object": "cast_hydrated", "hash": "0x06dfafdffa7455c3fd0a617ce1b026bcf01211d1", "thread_hash": "2695897f7265b116de992dde0a13865dda938eae", "parent_hash": "7b00f3e12f26ff363555d4f94f64e547fde7379a", "parent_url": null, "parent_author": { "fid": "7143" }, "author": { "object": "user", "fid": 1097, "custody_address": "0xe12b01100a4be7e79ddbd5dd939c97d12e890ac7", "username": "noun40", "display_name": "Noun 40", "pfp_url": "https://openseauserdata.com/files/faa77932343776d1237e5dd82aa12e76.svg", "profile": { "bio": { "text": "cofounder/cto @ bitwise", "mentioned_profiles": [] } }, "follower_count": 15682, "following_count": 55, "verifications": [ "0xae65e700f3f8904ac1007d47a5309dd26f8146c0" ], "active_status": "active" }, "text": "@dwr.eth @v would you agree? is there a more fundamental reason it’s whitelisted atm?", "timestamp": "2023-12-03T23:35:12.000Z", "embeds": [], "reactions": { "likes": [ { "fid": 1356, "fname": "farcasteradmin.eth" } ], "recasts": [] }, "replies": { "count": 1 }, "mentioned_profiles": [ { "object": "user", "fid": 3, "custody_address": "0x6b0bda3f2ffed5efc83fa8c024acff1dd45793f1", "username": "dwr.eth", "display_name": "Dan Romero", "pfp_url": "https://res.cloudinary.com/merkle-manufactory/image/fetch/c_fill,f_png,w_256/https://lh3.googleusercontent.com/MyUBL0xHzMeBu7DXQAqv0bM9y6s4i4qjnhcXz5fxZKS3gwWgtamxxmxzCJX7m2cuYeGalyseCA2Y6OBKDMR06TWg2uwknnhdkDA1AA", "profile": { "bio": { "text": "Working on Farcaster and Warpcast.", "mentioned_profiles": [] } }, "follower_count": 30657, "following_count": 2722, "verifications": [ "0xd7029bdea1c17493893aafe29aad69ef892b8ff2", "0xa14b4c95b5247199d74c5578531b4887ca5e4909", "0xb877f7bb52d28f06e60f557c00a56225124b357f", "0x8fc5d6afe572fefc4ec153587b63ce543f6fa2ea" ], "active_status": "active" }, { "object": "user", "fid": 2, "custody_address": "0x4114e33eb831858649ea3702e1c9a2db3f626446", "username": "v", "display_name": "Varun Srinivasan", "pfp_url": "https://i.seadn.io/gae/sYAr036bd0bRpj7OX6B-F-MqLGznVkK3--DSneL_BT5GX4NZJ3Zu91PgjpD9-xuVJtHq0qirJfPZeMKrahz8Us2Tj_X8qdNPYC-imqs?w=500&auto=format", "profile": { "bio": { "text": "Technowatermelon. Elder Millenial. Building Farcaster. \n\nnf.td/varun", "mentioned_profiles": [] } }, "follower_count": 27025, "following_count": 974, "verifications": [ "0x91031dcfdea024b4d51e775486111d2b2a715871", "0x182327170fc284caaa5b1bc3e3878233f529d741" ], "active_status": "active" } ] } } ], "next": { "cursor": "eyJ0aW1lc3RhbXAiOiIyMDIzLTEyLTAzIDIzOjM1OjEyLjAwMDAwMDAifQ==" } } ``` To fetch the next page of notifications, use the cursor: ```javascript Javascript theme={"system"} const nextNotifications = await client.fetchChannelNotificationsForUser({ fid: userFID, channelIds: [nounsChannelUrl], cursor: notifications.next.cursor, }); ``` That's it, no more wrangling with SQL queries or whatever bespoke solution to get notifications for a specific channel! > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # User by Wallet Address Source: https://docs.neynar.com/docs/fetching-farcaster-user-based-on-ethereum-address Find Farcaster user profile based on ethereum address > [!NOTE] > > ### This guide refers to [this API](/reference/fetch-bulk-users-by-eth-or-sol-address) Farcaster users can connect their FID (Farcaster ID) with an Ethereum or Solana address. This guide demonstrates how to get information about a user given their address. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` To get vitalik.eth's Farcaster profile: ```javascript Javascript theme={"system"} // vitalik.eth const addr = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"; const user = await client.fetchBulkUsersByEthOrSolAddress({addresses: [addr]}); console.log(user); ``` Example output: ```json theme={"system"} { result: { user: { fid: 5650, custodyAddress: "0xadd746be46ff36f10c81d6e3ba282537f4c68077", username: "vitalik.eth", displayName: "Vitalik Buterin", pfp: [Object ...], profile: [Object ...], followerCount: 14769, followingCount: 70, verifications: [ "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" ], activeStatus: "active" } } } ``` For addresses with multiple verifications, it will all resolve to the same user: ```javascript Javascript theme={"system"} // dwr.eth const addr1 = "0xd7029bdea1c17493893aafe29aad69ef892b8ff2"; const addr2 = "0xa14b4c95b5247199d74c5578531b4887ca5e4909"; // use Promise.all to make multiple requests in parallel const users = await Promise.all([ client.fetchBulkUsersByEthOrSolAddress({addresses: [addr1]}), client.fetchBulkUsersByEthOrSolAddress({addresses: [addr2]}), ]); console.log(users[0] === users[1]); // true console.log(users[0]); ``` They both resolve to: ```json theme={"system"} { result: { user: { fid: 3, custodyAddress: "0x6b0bda3f2ffed5efc83fa8c024acff1dd45793f1", username: "dwr.eth", displayName: "Dan Romero", pfp: [Object ...], profile: [Object ...], followerCount: 19326, followingCount: 2702, verifications: [ "0xd7029bdea1c17493893aafe29aad69ef892b8ff2", "0xa14b4c95b5247199d74c5578531b4887ca5e4909", "0xb877f7bb52d28f06e60f557c00a56225124b357f", "0x8fc5d6afe572fefc4ec153587b63ce543f6fa2ea" ], activeStatus: "active" } } } ``` > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Follow NFT Owners on Farcaster Using Neynar Source: https://docs.neynar.com/docs/following-all-farcaster-users-owning-cryptopunk Comprehensive tutorial on how to automatically follow all Farcaster users who own specific NFTs like CryptoPunks. Learn to build Web3 social features by combining Farcaster social graphs with NFT ownership data using Neynar's powerful APIs. ## How to Follow Farcaster Users Owning a Specific NFT This guide demonstrates how to follow Farcaster users who own a specific NFT. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. Before all that, initialize Neynar client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey:process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); const signer = process.env.NEYNAR_SIGNER; ``` First, we need to get the addresses owning Milady. We can use the [Alchemy NFT API](https://docs.alchemy.com/reference/getownersforcontract-v3) to get the addresses of users who own the NFT. ```javascript Javascript theme={"system"} const getAddr = async (nftAddr: string): Promise => { const apiKey = process.env.ALCHEMY_API_KEY; const baseUrl = `https://eth-mainnet.g.alchemy.com/nft/v3/${apiKey}/getOwnersForContract?`; const url = `${baseUrl}contractAddress=${nftAddr}&withTokenBalances=false`; const result = await fetch(url, { headers: { accept: "application/json" }, }); const data = await result.json(); return data.owners; }; // milady maker contract address const nftAddr = "0x5af0d9827e0c53e4799bb226655a1de152a425a5"; const addrs = await getAddr(nftAddr); ``` Next, get Farcaster FIDs of each address, then filter out any undefined values. ```javascript Javascript theme={"system"} const fidLookup = async (addrs: string[]) => { const fids = await Promise.all( addrs.map(async (addr) => { try { const response = await client.fetchBulkUsersByEthOrSolAddress({addresses:addr}); return response ? response.result.user.fid : undefined; } catch (error) { return undefined; } }) ); return fids.filter((fid) => fid !== undefined); }; const fids = await fidLookup(addrs); ``` Then, we can use the [Follow user](ref:follow-user) endpoint to follow each user. ```javascript Javascript theme={"system"} const result = await client.followUser({ signerUuid:signer, targetFids:fids}); console.log(result); ``` Example output: ```json theme={"system"} { "success": true, "details": [ { "success": true, "target_fid": 132 }, { "success": true, "target_fid": 78 }, { "success": true, "target_fid": 4262 }, { "success": true, "target_fid": 3602 }, ] } ``` That's it! You can now follow users who own a specific NFT easily with the Neynar SDK. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Farcaster Frames with Analytics using Neynar & Framejs Source: https://docs.neynar.com/docs/framejs-farcaster-frames In this guide, we’ll learn how to make a frame with the neynar SDK and Framejs, within a few minutes! For this demo, it will be a simple rock-paper-scissors game but it will give you an idea of how to create multi-page frames, interact with buttons, and get analytics for your frame with no extra effort. Let's get started! ## Creating a new frames project We will use Next.js to build the frame in this guide, but feel free to use anything else if you want! Enter this command in your terminal to create a new app: ```powershell PowerShell theme={"system"} yarn create frames ``` Enter a name for your project, and choose the next template, and it will spin up a new project for you. Once the project is created install the dependencies: ```powershell PowerShell theme={"system"} cd bun install ``` ### Using the neynar middleware Create a new file `frame.ts` and add the following: ```typescript frame.ts theme={"system"} import { neynarValidate } from "frames.js/middleware/neynar"; import { createFrames } from "frames.js/next"; export const frames = createFrames({ basePath: "/", middleware: [ neynarValidate({ API_KEY: process.env.NEYNAR_API_KEY!, }), ], }); ``` This creates an instance of the frame which uses neynarValidate as a middleware that we can re use in all the pages. > [!NOTE] > > ### Make sure to update the API key to your API key to get analytics ### Creating the frame home page Create a new file `route.tsx` in the `app` folder and add the following: ```typescript route.tsx theme={"system"} import { Button } from "frames.js/next"; import { frames } from "../frames"; const handleRequest = frames(async (ctx) => { return { target: "/result", image: (
Choose your weapon
), buttons: [ , , , ], }; }); export const GET = handleRequest; export const POST = handleRequest; ``` This will render an image saying choose your weapon and three buttons saying rock paper and scissors. When any of these buttons are clicked a request to the `/result` route is made which we define in the `target` prop. ![Frame home page](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/23b5a01-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=2c630ce9dbbdf9018cffc688ac32348e) > [!NOTE] > > ### Make sure that you sign in using your warpcast account, so that the requests can be validated Now, let's build the `/result` page. Create a new file called `result/route.tsx` ```typescript index.tsx theme={"system"} import { frames } from "@/frames"; import { Button } from "frames.js/next"; const handleRequest = frames(async (ctx) => { const rand = Math.floor(Math.random() * 3); const choices = ["rock", "paper", "scissors"]; const userChoice = choices[(Number(ctx.pressedButton?.index) || 1) - 1]; const computerChoice = choices[rand]; let msg = ""; if (userChoice === computerChoice) { msg = "draw"; } if ( (userChoice === "rock" && computerChoice === "scissors") || (userChoice === "paper" && computerChoice === "rock") || (userChoice === "scissors" && computerChoice === "paper") ) { msg = "You win"; } if ( (userChoice === "rock" && computerChoice === "paper") || (userChoice === "paper" && computerChoice === "scissors") || (userChoice === "scissors" && computerChoice === "rock") ) { msg = "You lose"; } return { action: "/frames", image: (
{userChoice} vs {computerChoice}
{msg}
), buttons: [], }; }); export const GET = handleRequest; export const POST = handleRequest; ``` Here, we first get the button index from the ctx and use it to get the user's choice. We have then added some logic for generating a random choice for the game. Then, in the image we display the two choices and the result of the game. We have also added a play again game which simply pushes the user to the `/` route. ![Frame result page](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/d061b3f-Mov_to_Gif_conversion.gif?s=926ae7a1ab1cb38c7616257303ff4bf9) ## Analytics Since we are using neynar middleware with framejs, we also get analytics out of the box. Head over to the usage tab and click on the frame that you are currently using. It will provide you with various analytics like total interactors, interactions per cast, etc. ![Frame analytics](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/713fe83-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=da3d7ed1857d3ffc193a2c1a9870caf9) ## Conclusion This guide taught us how to create a rock-paper-scissors game on Farcaster frames using frames.js! Lastly, please share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar), and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Kafka Stream: Real-Time Farcaster Events via Neynar Source: https://docs.neynar.com/docs/from-kafka-stream Retired August 2026. Historical reference for the hosted Kafka stream of hydrated Farcaster events. > [!WARNING] > > ### Kafka Stream has been retired > > The hosted Kafka stream was shut down in **August 2026** and is no longer available. For real-time Farcaster events, use [webhooks](/docs/how-to-setup-webhooks-from-the-dashboard) — they carry the same hydrated events this stream did. For bulk or historical data, use [Parquet exports](/docs/parquet) or the [indexer service](/docs/indexer-service-pipe-farcaster-data). Reach out at [support@ns.com](mailto:support@ns.com) if you need help migrating. > > This page is kept for historical reference. The brokers and topics below are no longer reachable. The Kafka stream carried the same events that power our webhook notifications. Everything below describes how it worked while it was running, and is preserved so existing integrations have a reference for what they were consuming. The topics retained 14 days of data, so the stream was never suitable for building a database of Farcaster history from day 1 — it was paired with [Parquet exports](/docs/parquet) for backfill. P95 data latency was under 1.5s. ## Why it existed Hub gRPC streaming delivers dehydrated events that you have to reassemble yourself to make useful (see [here](https://warpcast.com/rish/0x7c2997ec) for example). The Kafka stream delivered a fully hydrated event (e.g., [user.created](/docs/from-kafka-stream#data-schema)) that was usable immediately. The comparison between a gRPC hub event and a Kafka event is below. [Webhooks](/docs/how-to-setup-webhooks-from-the-dashboard) deliver these same hydrated events and are the supported way to get them today. ![Kafka stream](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/a6a0e1902416b32b41fc096276cf333c08ed30429956f9708586d2910942c8ee-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=cc1729266d620b61d194153e1216d225) ## How it worked > [!NOTE] > > The connection details, brokers and topics below are recorded as they were at shutdown. None of them accept connections any more. * Authentication used `SASL/SCRAM SHA512`. * The connection required TLS (sometimes called SSL for legacy reasons) for encryption. * `farcaster-mainnet-events` was the aggregated topic containing all events. * `farcaster-mainnet-user-events` carried `user.created`, `user.updated` and `user.transferred` * `farcaster-mainnet-cast-events` carried `cast.created` and `cast.deleted` * `farcaster-mainnet-follow-events` carried `follow.created` and `follow.deleted` * `farcaster-mainnet-reaction-events` carried `reaction.created` and `reaction.deleted` * `farcaster-mainnet-signer-events` carried `signer.created` and `signer.deleted` Consumers could subscribe to any combination of the event-specific topics above, or to `farcaster-mainnet-events` to get everything in one topic. Three brokers were exposed over the Internet, and clients were given all three: * `b-1-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196` * `b-2-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196` * `b-3-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196` Most clients accept the brokers as a comma-separated list: ```bash cURL theme={"system"} b-2-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196,b-1-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196,b-3-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196 ``` `kcat` (formerly `kafkacat`) was used to test connectivity locally: ```bash cURL theme={"system"} brew install kcat brew home kcat ``` ```bash cURL theme={"system"} kcat -L \ -b 'b-2-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196,b-1-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196,b-3-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196' \ -X security.protocol=sasl_ssl \ -X sasl.mechanisms=SCRAM-SHA-512 \ -X sasl.username='user-YOURNAME' \ -X sasl.password='YOURPASSWORD' ``` Example output: ```bash cURL theme={"system"} Metadata for farcaster-mainnet-events (from broker 1: sasl_ssl://b-1-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196/1): 3 brokers: broker 2 at b-2-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196 broker 3 at b-3-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196 (controller) broker 1 at b-1-public.tfmskneynar.5vlahy.c11.kafka.us-east-1.amazonaws.com:9196 5 topics: topic "farcaster-mainnet-user-events" with 2 partitions: partition 0, leader 2, replicas: 2,3,1, isrs: 2,3,1 partition 1, leader 3, replicas: 3,1,2, isrs: 3,1,2 topic "farcaster-mainnet-reaction-events" with 2 partitions: partition 0, leader 3, replicas: 3,2,1, isrs: 3,2,1 partition 1, leader 1, replicas: 1,3,2, isrs: 1,3,2 topic "farcaster-mainnet-cast-events" with 2 partitions: partition 0, leader 2, replicas: 2,3,1, isrs: 2,3,1 partition 1, leader 3, replicas: 3,1,2, isrs: 3,1,2 topic "farcaster-mainnet-follow-events" with 2 partitions: partition 0, leader 2, replicas: 2,3,1, isrs: 2,3,1 partition 1, leader 3, replicas: 3,1,2, isrs: 3,1,2 topic "farcaster-mainnet-events" with 2 partitions: partition 0, leader 2, replicas: 2,3,1, isrs: 3,1,2 partition 1, leader 3, replicas: 3,1,2, isrs: 3,1,2 topic "farcaster-mainnet-signer-events" with 2 partitions: partition 0, leader 1, replicas: 1,3,2, isrs: 1,3,2 partition 1, leader 2, replicas: 2,1,3, isrs: 2,1,3 ``` The topics listed varied depending on the credentials in use. ## Consumer nodejs example > [card] [https://github.com/neynarxyz/farcaster-examples/tree/main/neynar-webhook-kafka-consumer](https://github.com/neynarxyz/farcaster-examples/tree/main/neynar-webhook-kafka-consumer) ## Data schema ```typescript user.created theme={"system"} // _when a new user is created on the network_ interface Bio { text: string; } interface Profile { bio: Bio; } interface VerifiedAddresses { eth_addresses: string[]; sol_addresses: string[]; } interface UserCreatedData { object: "user"; fid: number; custody_address: string; username: string; display_name: string | null; pfp_url: string | null; profile: Profile; follower_count: number; following_count: number; verifications: string[]; verified_addresses: VerifiedAddresses; active_status: "inactive" | "active"; power_badge: boolean; event_timestamp: string; // ISO 8601 format } interface CustomHeaders { "x-convoy-message-type": "broadcast"; } interface UserCreatedEvent { event_type: "user.created"; data: UserCreatedData; custom_headers: CustomHeaders; idempotency_key?: string; } ``` ```typescript user.updated theme={"system"} // _when a user profile field is updated_ interface Bio { text: string; } interface Profile { bio: Bio; } interface VerifiedAddresses { eth_addresses: string[]; sol_addresses: string[]; } interface UserUpdatedData { object: "user"; fid: number; custody_address: string; username: string; display_name: string; pfp_url: string; profile: Profile; follower_count: number; following_count: number; verifications: string[]; verified_addresses: VerifiedAddresses; active_status: "inactive" | "active"; power_badge: boolean; event_timestamp: string; // ISO 8601 format } interface CustomHeaders { "x-convoy-message-type": "broadcast"; } interface UserUpdatedEvent { event_type: "user.updated"; data: UserUpdatedData; custom_headers: CustomHeaders; idempotency_key?: string; } ``` ```typescript cast.created theme={"system"} // _when a new cast is created_ export interface CustomHeaders { "x-convoy-message-type": "broadcast" } interface Fid { fid?: number | null; } interface User { object: string; fid: number; custody_address: string; username: string; display_name: string; pfp_url: string; profile: { bio: { text: string; }; }; follower_count: number; following_count: number; verifications: string[]; verified_addresses: { eth_addresses: string[]; sol_addresses: string[]; }; active_status: string; power_badge: boolean; } interface EmbedUrlMetadata { content_type?: string | null; content_length?: number | null; } interface EmbedUrl { url: string; metadata?: EmbedUrlMetadata; } interface CastId { fid: number; hash: string; } interface EmbedCastId { cast_id: CastId; } type EmbeddedCast = EmbedUrl | EmbedCastId; interface EventData { object: "cast"; hash: string; parent_hash?: string | null; parent_url?: string | null; root_parent_url?: string | null; parent_author?: Fid; author: User; mentioned_profiles?: User[]; text: string; timestamp: string; // ISO 8601 format embeds: EmbeddedCast[]; } export interface CastCreatedEvent { event_type: "cast.created" data: EventData custom_headers: CustomHeaders idempotency_key?: string } ``` ```typescript cast.deleted theme={"system"} // _when a cast is deleted_ export interface CustomHeaders { "x-convoy-message-type": "broadcast" } interface Fid { fid?: number | null; } interface User { object: string; fid: number; custody_address: string; username: string; display_name: string; pfp_url: string; profile: { bio: { text: string; }; }; follower_count: number; following_count: number; verifications: string[]; verified_addresses: { eth_addresses: string[]; sol_addresses: string[]; }; active_status: string; power_badge: boolean; } interface EmbedUrlMetadata { content_type?: string | null; content_length?: number | null; } interface EmbedUrl { url: string; metadata?: EmbedUrlMetadata; } interface CastId { fid: number; hash: string; } interface EmbedCastId { cast_id: CastId; } type EmbeddedCast = EmbedUrl | EmbedCastId; interface EventData { object: "cast"; hash: string; parent_hash?: string | null; parent_url?: string | null; root_parent_url?: string | null; parent_author?: Fid; author: User; mentioned_profiles?: User[]; text: string; timestamp: string; // ISO 8601 format embeds: EmbeddedCast[]; } export interface CastDeletedEvent { event_type: "cast.deleted" data: EventData custom_headers: CustomHeaders idempotency_key?: string } ``` ```typescript follow.created theme={"system"} // _when a user follows another user_ interface UserDehydrated { object: "user_dehydrated"; fid: number; username: string; } interface AppDehydrated { object: "user_dehydrated"; fid: number; } interface EventData { object: "follow"; event_timestamp: string; // ISO 8601 format timestamp: string; // ISO 8601 format with timezone user: UserDehydrated; target_user: UserDehydrated; app: AppDehydrated | null; // null if not signer isn't available } interface CustomHeaders { "x-convoy-message-type": "broadcast"; } interface FollowCreatedEvent { event_type: "follow.created"; data: EventData; custom_headers: CustomHeaders; idempotency_key?: string } ``` ```typescript follow.deleted theme={"system"} // _when a user unfollows another user_ interface UserDehydrated { object: "user_dehydrated"; fid: number; username: string; } interface EventData { object: "unfollow"; event_timestamp: string; // ISO 8601 format timestamp: string; // ISO 8601 format with timezone user: UserDehydrated; target_user: UserDehydrated; } interface CustomHeaders { "x-convoy-message-type": "broadcast"; } interface FollowDeletedEvent { event_type: "follow.deleted"; data: EventData; custom_headers: CustomHeaders; idempotency_key?: string } ``` ```typescript reaction.created theme={"system"} // _when a reaction is added to a cast_ interface UserDehydrated { object: "user_dehydrated"; fid: number; username: string; } interface AppDehydrated { object: "user_dehydrated"; fid: number; } interface URIDehydrated { object: "uri_dehydrated"; uri: string; } interface CastDehydrated { object: "cast_dehydrated"; hash: string; author: UserDehydrated; } interface EventData { object: "reaction"; event_timestamp: string; // ISO 8601 format timestamp: string; // ISO 8601 format with timezone reaction_type: number; user: UserDehydrated; target: CastDehydrated | URIDehydrated; app: AppDehydrated | null; // null if not signer isn't available } interface CustomHeaders { "x-convoy-message-type": "broadcast"; } interface ReactionCreatedEvent { event_type: "reaction.created"; data: EventData; custom_headers: CustomHeaders; idempotency_key?: string; } ``` ```typescript reaction.deleted theme={"system"} // _when a reaction is removed from a cast_ interface UserDehydrated { object: "user_dehydrated"; fid: number; username: string; } interface URIDehydrated { object: "uri_dehydrated"; uri: string; } interface CastDehydrated { object: "cast_dehydrated"; hash: string; author: UserDehydrated; } interface EventData { object: "reaction"; event_timestamp: string; // ISO 8601 format timestamp: string; // ISO 8601 format with timezone reaction_type: number; user: UserDehydrated; target: CastDehydrated | URIDehydrated; } interface CustomHeaders { "x-convoy-message-type": "broadcast"; } interface ReactionDeletedEvent { event_type: "reaction.deleted"; data: EventData; custom_headers: CustomHeaders; idempotency_key?: string; } ``` # Build Farcaster Apps with Neynar API Source: https://docs.neynar.com/docs/getting-started-with-neynar Complete beginner's guide to building on Farcaster with Neynar ## Introduction Farcaster is a protocol for building decentralized social apps. Neynar makes it easy to build on Farcaster. This tutorial covers * fundamentals of Farcaster protocol * getting your Neynar API key * understanding core concepts like FIDs and Casts * tutorial steps to start developing social applications ## Basic understanding of Farcaster Farcaster is a decentralized social protocol. Here are a few of the primary Farcaster primitives that will be helpful to keep in mind as you dive in: > [card] User > > Every user on Farcaster is represented by a permanent *FID*, the user's numerical identifier. All user profile data for this FID, e.g., username, display name, bio, etc., are stored on the Farcaster protocol and mapped to this FID. > [card] Casts > > Users can broadcast information to the protocol in units of information called "casts". It's somewhat similar to a tweet on Twitter/X. Each cast has a unique "hash". > [card] User Relationships > > Users can follow each other to see casts from them. This creates a social graph for each user on Farcaster. There's more to this, but let's start with this. All the above data is open, decentralized, and available on Farcaster hubs. Neynar makes interfacing with this data relatively trivial. In this tutorial, we will learn how to use the above primitives to fetch a simple *feed* of casts for a given user. ## Get Neynar API key Don't have an API key yet? [Create an account](https://neynar.com) to get started. Don't hesitate to reach out to us on our [channel](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com) with any questions! > [!NOTE] > > ### If building a mini app, you can go straight to [Set up mini app in \< 60 s](/docs/create-farcaster-miniapp-in-60s) ## Set up Neynar SDK Neynar [`nodejs` SDK](https://github.com/neynarxyz/nodejs-sdk) is an easy way to use the APIs. This section only needs to be done once when setting up the SDK for the first time. To install the Neynar TypeScript SDK: ```typescript yarn theme={"system"} yarn add @neynar/nodejs-sdk ``` ```typescript npm theme={"system"} npm install @neynar/nodejs-sdk ``` ```typescript pnpm theme={"system"} pnpm install @neynar/nodejs-sdk ``` ```typescript bun theme={"system"} bun add @neynar/nodejs-sdk ``` To get started, initialize the client in a file named `index.ts`: ```typescript Typescript theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; const config = new Configuration({ apiKey: "YOUR_NEYNAR_API_KEY", }); const client = new NeynarAPIClient(config); ``` Depending on your build environment, you might also need the following two steps: **Step 1** check the `type` field in package.json. Since we're using ES6 modules, you may need to set it to "module". ```json package.json theme={"system"} { "scripts": { "start": "node --loader ts-node/esm index.ts" }, "type": "module", // <-- set to module if needed "dependencies": { // this is for illustration purposes, the version numbers will depend on when you do this tutorial "@neynar/nodejs-sdk": "^2.0.5", "ts-node": "^10.9.2", "typescript": "^5.6.3" } } ``` **Step 2** If you hit errors, try adding a `tsconfig.json` file in the directory to help with typescript compilation ```typescript Typescript theme={"system"} { "compilerOptions": { "module": "ESNext", "moduleResolution": "node", "target": "ESNext", "esModuleInterop": true, "skipLibCheck": true }, "ts-node": { "esm": true } } ``` Your directory should have the following: * node\_modules * index.ts * package-lock.json * package.json * tsconfig.json (optional) * yarn.lock ## Fetch Farcaster data using Neynar SDK ### Fetching feed To fetch the feed for a user, you need to know who the user is following and then fetch casts from those users. Neynar abstracts away all this complexity. Put in the `fid` of the user in the `fetchFeed` function and get a feed in response. In this example, we will fetch the feed for [Dan Romero](https://warpcast.com/dwr.eth) . This is the feed Dan would see if he were to log into a client that showed a feed from people he followed in a reverse chronological order. ```typescript Typescript theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; const config = new Configuration({ apiKey: "YOUR_NEYNAR_API_KEY", }); const client = new NeynarAPIClient(config); import { FeedType } from "@neynar/nodejs-sdk/build/api"; const feedType = FeedType.Following; const fid = 3; const withRecasts = true; const limit = 50; const viewerFid = 6131; client .fetchFeed({ feedType, fid, withRecasts, limit, viewerFid }) .then((response) => { console.log("response:", response); }); ``` You can now run this code by opening this folder in the terminal and running it. ```typescript Typescript theme={"system"} yarn start ``` Depending on your machine, typescript might take a few seconds to compile. Once done, it should print the output to your console. Something like below: ```typescript Typescript theme={"system"} User Feed: { casts: [ { object: 'cast', hash: '0x5300d6bd8f604c0b5fe7d573e02bb1489362f4d3', author: [Object], thread_hash: '0x5300d6bd8f604c0b5fe7d573e02bb1489362f4d3', parent_hash: null, parent_url: null, root_parent_url: null, parent_author: [Object], text: 'https://open.spotify.com/track/5oQcOu1omDykbIPSdSQQNJ?si=2qMjk-fESMmxqCoAxTsPmw', timestamp: '2024-11-14T04:57:23.000Z', embeds: [Array], channel: null, reactions: [Object], replies: [Object], mentioned_profiles: [], viewer_context: [Object] }, ] } ``` You've successfully fetched the feed for a user using a simple function call! *Future reading: you can fetch many different kinds of feeds. See [Feed](/reference/fetch-user-following-feed) APIs.* ### Fetching user profile data Now, let's fetch data about a user. We will take an FID and fetch data for that user. Here's how to do it using the SDK: ```javascript Javascript theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; const config = new Configuration({ apiKey: "YOUR_NEYNAR_API_KEY", }); const client = new NeynarAPIClient(config); const fids = [2, 3]; const viewerFid = 6131; client.fetchBulkUsers({ fids, viewerFid }).then((response) => { console.log("response:", response); }); ``` You can run this in your terminal similar to above by typing in: ```typescript Typescript theme={"system"} yarn start ``` It should show you a response like the one below: ```typescript Typescript theme={"system"} User: { users: [ { object: 'user', fid: 3, username: 'dwr.eth', display_name: 'Dan Romero', pfp_url: 'https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/bc698287-5adc-4cc5-a503-de16963ed900/original', custody_address: '0x6b0bda3f2ffed5efc83fa8c024acff1dd45793f1', profile: [Object], follower_count: 489109, following_count: 3485, verifications: [Array], verified_addresses: [Object], verified_accounts: [Array], power_badge: true } ] } ``` *Future reading: you can also fetch data about a user by using their wallet address or username as identifiers. See APIs for that here: [User by wallet address](/docs/fetching-farcaster-user-based-on-ethereum-address), [By username](/reference/lookup-user-by-username).* ## You're ready to build! Now that you can fetch user and cast data, you're ready to dive in further and start making your first Farcaster application. Read through our [tutorials](/docs) and [API reference](/reference) for more. If you have questions or feedback, please reach out to us at [support@ns.com](mailto:support@ns.com). # Storage Units Allocation Source: https://docs.neynar.com/docs/getting-storage-units-allocation-of-farcaster-user Fetch data about a user's storage allocation on Farcaster network with Neynar > [!NOTE] > > ### Related API: [Allocation of user](/reference/lookup-user-storage-allocations) In the Farcaster protocol, a storage unit is a measure used to allocate and track the amount of data that a user (identified by their Farcaster ID or fid) can store within the network. This system is critical for managing the storage resources of the Farcaster network effectively and ensuring that the network remains scalable and efficient. The specific allocation of storage per unit varies depending on the type of data being stored. Here's the list of storage allocations per unit: * 100 cast messages * 200 reaction messages * 200 link messages * 25 user\_data messages * 5 verifications messages * 2 username\_proof messages The Storage Registry contract controls and tracks the allocation. This contract records the storage allocated to each fid, denominated in integer units. If a user exceeds their storage allocation, Farcaster Hub prunes their old messages. Users can buy more storage units by sending a transaction to the Storage Registry contract or using an app like [caststorage.com](https://caststorage.com/). This guide demonstrates how to use the Neynar SDK to retrieve a Farcaster user's storage usage and allocation. Check out this [Getting Started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Then, fetch the storage usage and allocation: ```javascript Javascript theme={"system"} const rishFID = 194; const storageUsage = await client.lookupUserStorageUsage({fid:rishFID}); console.log(storageUsage); ``` Example output: ```json theme={"system"} { object: "storage_usage", user: { object: "user_dehydrated", fid: 194 }, total_active_units: 2, casts: { object: "storage", used: 3707, capacity: 10000 }, reactions: { object: "storage", used: 4984, capacity: 5000 }, links: { object: "storage", used: 472, capacity: 5000 }, verifications: { used: 2, capacity: 25 }, username_proofs: { used: 1, capacity: 5 }, signers: { used: 17, capacity: 1000 } } ``` To fetch the storage allocation of a user, use the `lookupUserStorageAllocation` function: ```javascript Javascript theme={"system"} const storageAllocation = await client.lookupUserStorageAllocations({fid:rishFID}); console.log(storageAllocation); ``` Example output: ```json theme={"system"} { total_active_units: 2, allocations: [ { object: "storage_allocation", user: [Object ...], units: 2, expiry: "2024-08-28T22:23:31.000Z", timestamp: "2023-08-29T22:23:31.000Z" } ] } ``` That's it! You can now look at the storage usage and allocation of any Farcaster user. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Create a Farcaster Account with Wallet Integration Source: https://docs.neynar.com/docs/guide-to-creating-a-farcaster-account-with-wallet-integration This document outlines the steps to successfully create a Farcaster account without having a end user mnemonic. > [card] [Follow the example code while reading the guide for better understanding](https://github.com/neynarxyz/farcaster-examples/pull/44) ## Prerequisites * A browser with a wallet extension (e.g., MetaMask, Coinbase, etc.) installed. * If you are using a Privy wallet, you can see this [guide](https://neynar.notion.site/Creating-new-accounts-with-embedded-wallets-14a655195a8b80999ccec0aa635b23af?pvs=4) written by community member [jpfraneto](https://warpcast.com/jpfraneto.eth), which includes source code for getting started. * Access to the Farcaster API and ID Registry smart contract. * Familiarity with JavaScript and Ethereum wallet interactions. * **App Wallet with `wallet_id`** - Required for FID fetching and account registration. See [Managing Onchain Wallets](/docs/managing-onchain-wallets) to create one in the developer portal. * **Funded wallet with \$5+ ETH on Optimism** - Covers initial account pre-registration on first call > [!NOTE] > > **New to App Wallets?** The `/v2/farcaster/user/fid` endpoint requires a [`x-wallet-id` header](/docs/managing-onchain-wallets) to cover the costs of FID registration. Create your app wallet in the [Developer Portal](https://dev.neynar.com) first! > > **Recommended Setup Steps:** > > 1. Log in to [dev.neynar.com](https://dev.neynar.com) and select your app > 2. Activate a wallet from the "App Wallet" tab > 3. Fund the wallet with \$5+ ETH on Optimism > 4. Add the `x-wallet-id` header to your API requests (treat it like a secret) ## Step 1: Connect a Wallet To connect a wallet in the browser: 1. Check if the browser supports `window.ethereum`. 2. Use `eth_requestAccounts` to request wallet connection. ### Code Example: ```javascript Javascript theme={"system"} if (typeof window === "undefined" || typeof window.ethereum === "undefined") { console.error("No wallet is installed."); window.open("https://metamask.io/download/", "_blank"); return; } const accounts = await window.ethereum.request({ method: "eth_requestAccounts", }); if (accounts.length === 0) { console.error("No wallet detected. Please log in to a wallet."); return; } const userAddress = accounts[0]; console.log("Wallet connected:", userAddress); ``` ## Step 2: Switch to the Optimism Network To interact with the ID Registry contract, ensure the wallet is on the Optimism network. ### Code Example: ```javascript Javascript theme={"system"} try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: "0xA" }], // Optimism chainId in hex }); console.log("Switched to Optimism network."); } catch (error) { console.error("Failed to switch to Optimism network:", error); return; } ``` ## Step 3: Fetch FID Use the [`GET-/v2/farcaster/user/fid`](/reference/get-fresh-account-fid) endpoint to retrieve the FID of the account that will be transferred to the wallet's address. **This endpoint requires a [`x-wallet-id` header](/docs/managing-onchain-wallets).** > [!WARNING] > > **Cold Start (First Call Only):** The first time you call this endpoint with a new `wallet_id`, it will take approximately **1 minute** to complete as it pre-registers a few FID accounts. Subsequent calls will be fast (\< 1 second). Make sure your wallet has **\$5+ ETH on Optimism** before the first call. > [!WARNING] > > **Wallet Consistency Required:** The same `wallet_id` used here must also be used in Step 6 when calling `POST /v2/farcaster/user/`. Using different wallets will result in an error. ### Code Example: ```javascript Javascript theme={"system"} // Your backend API should forward the wallet_id header to Neynar const response = await fetch("/api/user", { headers: { // Your backend should include: // 'x-api-key': 'YOUR_NEYNAR_API_KEY', // 'x-wallet-id': 'your-wallet-id' } }); if (!response.ok) { console.error("Failed to fetch FID from the API."); return; } const data = await response.json(); const fid = data.fid; if (!fid) { console.error("FID not found in the API response."); return; } console.log("FID fetched:", fid); ``` > [!WARNING] > > **Backend Required:** The FID fetch should be done from your backend, not the frontend, to keep your `wallet_id` secure. Your backend should call Neynar's API with the `x-wallet-id` header. ## Step 4: Generate `signTypedData` with Viem To generate a signature for FID registration: 1. Fetch the nonce from the ID Registry contract. 2. Create EIP-712 typed data and request a signature from the wallet. ### Code Example: ```javascript Javascript theme={"system"} import { createWalletClient, custom, publicActions } from "viem"; import { optimism } from "viem/chains"; import { ID_REGISTRY_ABI, ID_REGISTRY_ADDRESS } from "./constants"; const wallet = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }).extend(publicActions); const nonce = await wallet.readContract({ address: ID_REGISTRY_ADDRESS, abi: ID_REGISTRY_ABI, functionName: "nonces", args: [userAddress], }); const now = Math.floor(Date.now() / 1000); const deadline = now + 3600; // 1 hour from now const domain = { name: "Farcaster IdRegistry", version: "1", chainId: 10, verifyingContract: ID_REGISTRY_ADDRESS, }; const types = { Transfer: [ { name: "fid", type: "uint256" }, { name: "to", type: "address" }, { name: "nonce", type: "uint256" }, { name: "deadline", type: "uint256" }, ], }; const message = { fid: BigInt(fid), to: userAddress, nonce: BigInt(nonce), deadline: BigInt(deadline), }; const signature = await wallet.signTypedData({ account: userAddress, domain, types, primaryType: "Transfer", message, }); console.log("Signature:", signature); ``` ## Step 5: Check `fname` Availability Before registering a username, check if it is available using the [`GET /v2/farcaster/fname/availability`](/reference/is-fname-available) endpoint. ### Code Example: ```javascript Javascript theme={"system"} const fname = "desired_username"; const response = await fetch(`/api/user/fname/availability?fname=${fname}`); if (!response.ok) { console.error("Failed to check fname availability."); return; } const data = await response.json(); const isAvailable = data.available; console.log("Fname availability:", isAvailable); ``` ## Step 6: Call the [`POST-/v2/farcaster/user`](/reference/register-account) Endpoint Submit the required data to create the Farcaster account. **This endpoint also requires a [`x-wallet-id` header](/docs/managing-onchain-wallets).** ### Code Example: ```javascript Javascript theme={"system"} const metadata = { bio: "Your bio", pfp_url: "https://example.com/profile-pic.jpg", url: "https://yourwebsite.com", display_name: "Your Display Name", location: { latitude: 40.7128, longitude: -74.006, }, }; // Your backend should handle this with wallet_id const response = await fetch("/api/user", { method: "POST", headers: { "Content-Type": "application/json", // Your backend should include: // 'x-api-key': 'YOUR_NEYNAR_API_KEY', // 'x-wallet-id': 'your-wallet-id' }, body: JSON.stringify({ fid, signature, requestedUserCustodyAddress: userAddress, deadline, fname, metadata, }), }); if (!response.ok) { const errorData = await response.json(); console.error("Error creating account:", errorData.message); return; } console.log("Account created successfully!"); ``` > [!WARNING] > > **Backend Required:** Like Step 3, this registration should be done from your backend to keep your `wallet_id` secure. Your backend calls Neynar's API with the `x-wallet-id` header. ## Conclusion By following these steps, you can create an account using the user's wallet. (No mnemonic required) # Frame Validation Source: https://docs.neynar.com/docs/how-to-a-frame-action-against-farcaster-hub-with-neynar-api Validate incoming frame actions to get genuine data Frames are mini-apps inside Farcaster casts. To read more about frames, check out the [Frames documentation](https://docs.farcaster.xyz/learn/what-is-farcaster/frames). Frame actions are POST request sent to a Frame server, and is unauthenticated by default. Checking the POST request payload against the Hub is a necessary step to ensure the request is valid. This guide demonstrates how to verify a frame action payload against the Hub with Neynar SDK. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; import { FeedType, FilterType } from "@neynar/nodejs-sdk/build/api/index.js"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Frame server hosts frames and handles Frame actions. Here's what POST request payload looks like for a Frame action: ```javascript Javascript theme={"system"} const payload = { untrustedData: { fid: 4286, url: "https://frame-server-example.com", messageHash: "0x8e95825cca8e81db6b9bd64bfdf626f7f172f02e", timestamp: 1707640145000, network: 1, buttonIndex: 1, castId: { fid: 4286, hash: "0x0000000000000000000000000000000000000001", }, }, trustedData: { messageBytes: "0a60080d10be2118d1bee82e20018201510a3268747470733a2f2f726e766d622d3130332d3132312d3133382d3131332e612e667265652e70696e6767792e6f6e6c696e6510011a1908be211214000000000000000000000000000000000000000112148e95825cca8e81db6b9bd64bfdf626f7f172f02e1801224096dd456e2752a358b33e19cfa833478974ce53379939e721e2875df14df4237a3ad3c9c9a27768ab59ea360df34893111c9aadc819a972d8ffd5f66976ffc00328013220836bf050647d18d304124823aaefa7c82eef99cbab2a120d8a8fe8e6d391929d", }, }; ``` Anyone can spoof the request and send it to the Frame server. The check whether it's a valid request (ie. fid 4286 pressing buttonIndex 1 on a specific cast), we need to verify the request against the Hub. ```javascript Javascript theme={"system"} const result = await client.validateFrameAction({ messageBytesInHex: payload.trustedData.messageBytes, }); console.log(result); ``` ```json json theme={"system"} { "valid": true, "action": { "object": "validated_frame_action", "interactor": { "object": "user", "fid": 4286, "custody_address": "0x0076f74cc966fdd705ded40df8ab86604e4b5759", "username": "pixel", "display_name": "vincent", "pfp_url": "https://lh3.googleusercontent.com/WuVUEzf_r3qgz3cf4mtkXpLat5zNZbxKjoV-AldwfCQ8-_Y5yfWScMBEalpvbVgpt4ttXruxTD9GM983-UJBzMil5GRQF1qZ_aMY", "profile": {}, "follower_count": 34997, "following_count": 905, "verifications": [ "0x0076f74cc966fdd705ded40df8ab86604e4b5759", "0xb7254ce5cb61f69b3fc120b85f0f6b90d871036c" ], "verified_addresses": { "eth_addresses": [ "0x0076f74cc966fdd705ded40df8ab86604e4b5759", "0xb7254ce5cb61f69b3fc120b85f0f6b90d871036c" ], "sol_addresses": [ "7rhxnLV8C77o6d8oz26AgK8x8m5ePsdeRawjqvojbjnQ", "8g4Z9d6PqGkgH31tMW6FwxGhwYJrXpxZHQrkikpLJKrG" ], }, "active_status": "active" }, "tapped_button": { "index": 1 }, "input": { "text": "" }, "url": "https://frame-server-example.com", "cast": { "object": "cast_dehydrated", "hash": "0x0000000000000000000000000000000000000001", "fid": 4286 }, "timestamp": "2024-02-11T08:29:05.000Z" }, "signature_temporary_object": { "note": "temporary object for signature validation, might be removed in future versions. do not depend on this object, reach out if needed.", "hash": "0x8e95825cca8e81db6b9bd64bfdf626f7f172f02e", "hash_scheme": "HASH_SCHEME_BLAKE3", "signature": "lt1FbidSo1izPhnPqDNHiXTOUzeZOech4odd8U30I3o608nJondoq1nqNg3zSJMRHJqtyBmpctj/1fZpdv/AAw==", "signature_scheme": "SIGNATURE_SCHEME_ED25519", "signer": "0x836bf050647d18d304124823aaefa7c82eef99cbab2a120d8a8fe8e6d391929d" } } ``` And that's it, a valid Frame action! You've successfully verified a frame action payload against the Hub with Neynar SDK. Note that frame payloads are only available in responses, not in initial requests. Attempts to fetch a payload during a request will result in an error. If you want to fetch cast while doing frame validation, refer to our [How to get cast information from URL](/docs/how-to-get-cast-information-from-url) guide. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Validate Farcaster Frames with Neynar SDK Source: https://docs.neynar.com/docs/how-to-build-farcaster-frames-with-neynar Build Farcaster frames 10x faster than starting from scratch with Neynar's frame validation API. Learn to validate frame actions, get user interaction details, and test frames locally with ngrok integration for rapid development. ## How to Validate Farcaster Frames Building a new Farcaster Frame? Read the [Farcaster Mini Apps Specification](https://miniapps.farcaster.xyz/docs/specification) to get started with the latest Mini Apps format, or check the [legacy Frames documentation](https://docs.farcaster.xyz/reference/frames/spec) for older Frame formats. Once you have set up your Frame server with the right meta tags, you will want to know which users interacted with your Frame so that you can take the next appropriate action. 1. Use [Validate frame action](/reference/validate-frame-action) to validate the incoming user interaction and get details about the interacting user, cast author and cast itself 2. To test Frames on your local machine, set up [ngrok](https://ngrok.com/download) and use ngrok as your Frame's POST url. More open source Frame resources from @base in [onchaintoolkit](https://github.com/coinbase/onchainkit?tab=readme-ov-file#getframevalidatedmessage) Now start making some frames and let us know if you have questions at [@neynar](https://warpcast.com/~/channel/neynar)! ![](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/e9ee284-image.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=a4c1a818c0f06f17c5b00e7d397f4086) Example \$degen related Frame built using Neynar # Choose Among Data Products Source: https://docs.neynar.com/docs/how-to-choose-the-right-data-product-for-you Pick between pulling or pushing data in the format that works for you ## Why Developers can focus on what they are building instead of running a hub and replicator which can be significant cost & effort over time ## How to pick the right product for your team | Product | Pros | Cons | | - | - | - | | [Parquet exports](/docs/parquet) | Developer ingests [Apache Parquet](https://parquet.apache.org/) files and can read/write to their own db as needed | Need to set up parquet ingestion workflow, open source repo available. See [here](/docs/parquet). | | | No need for developer to share access to database with Neynar | | | [Indexer service - pipe Farcaster data](/docs/indexer-service-pipe-farcaster-data) | Neynar writes to a db on the developer’s side, no need for developer to manage hubs or replicators Neynar handles all hub and replicator related upgrades and maintenance | Developer needs to have a db that they can share write access to | | | Developer has flexibility to let Neynar pipe specific tables instead of all FC data | | | | Developer can manage the db as they see fit — create new indexes, etc. | | # How to Contribute to @neynar/nodejs-sdk Source: https://docs.neynar.com/docs/how-to-contribute-to-neynarnodejs-sdk Step-by-step guide for contributing to the Neynar Node.js SDK - from setup to submitting pull requests ```bash Shell theme={"system"} git clone [email protected]:neynarxyz/nodejs-sdk.git ``` ```bash Shell theme={"system"} git submodule update --init --recursive ``` ```bash Shell theme={"system"} yarn install ``` # Create a Farcaster Client with Next.js Source: https://docs.neynar.com/docs/how-to-create-a-client This guide will look at creating a Farcaster client using Next.js and the Neynar React SDK. For this guide, we'll go over: > [card] [Setting up Sign-in with neynar](/docs/how-to-create-a-client#setting-up-sign-in-with-neynar) > [card] [Building the user feed](/docs/how-to-create-a-client#building-the-feed) > [card] [Building channels feed](/docs/how-to-create-a-client#building-the-channels-list-and-channel-feed) > [card] [Building user profiles](/docs/how-to-create-a-client#building-user-profiles) Before we begin, you can access the [complete source code](https://github.com/avneesh0612/neynar-client) for this guide on GitHub. Let's get started! ## Creating the app ### Setting up the project Create a new next.js app using the following command: ```powershell PowerShell theme={"system"} npx create-next-app app-name ``` You can choose the configuration based on your personal preference, I am using this config for the guide: ![Create Next.js app](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/c0af43f-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=b9d898c07a54e7cef78e019a01984d78) Once the app is created, install the packages that we are going to need for the command: ```powershell npm theme={"system"} npm i @neynar/react @neynar/nodejs-sdk ``` ```powershell yarn theme={"system"} yarn add @neynar/react @neynar/nodejs-sdk ``` ```powershell bash theme={"system"} bun add @neynar/react @neynar/nodejs-sdk ``` Once the dependencies are installed you can open it in your favourite and we can start working on the client! ### Setting up Sign-in with neynar Head over to the `layout.tsx` file and wrap your app in a `NeynarContextProvider` like this: ```typescript layout.tsx theme={"system"} "use client"; import "./globals.css"; import { NeynarContextProvider, Theme } from "@neynar/react"; import "@neynar/react/dist/style.css"; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {}, onSignout() {}, }, }} > {children} ); } ``` We are passing some settings here like `clientId`, `defaultTheme` and `eventsCallbacks`. * `clientId`: This is going to be the client ID you get from your neynar, add it to your `.env.local` file as `NEXT_PUBLIC_NEYNAR_CLIENT_ID`. ![Neynar client ID](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/bde5490-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=c44bb9404a8361f45b82f97d08e9feb8) > [!NOTE] > > ### Make sure to add localhost to the authorized origins * `defaultTheme`: default theme lets you change the theme of your sign-in button, currently, we have only light mode but dark mode is going to be live soon. * `eventsCallbacks`: This allows you to perform certain actions when the user signs out or auth is successful. I've also added a styles import from the neynar react package here which is needed for the styles of the sign-in button. Now, let's create a header component where we can add the sign-in with Neynar button. So, create a new `components/Header.tsx` file and add the following: ```typescript Header.tsx theme={"system"} "use client"; import { NeynarAuthButton } from "@neynar/react"; import Link from "next/link"; export const Header: FC = () => { return (
FarCaster App
); }; ``` We'll add the header to the `layout.tsx` file since we are going to need it on all the pages: ```typescript layout.tsx theme={"system"} "use client"; import "./globals.css"; import { NeynarContextProvider, Theme } from "@neynar/react"; import "@neynar/react/dist/style.css"; import { Header } from "@/components/Header"; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {}, onSignout() {}, }, }} >
{children} ); } ``` If you head over to your app you'll be able to see a sign-in button on the screen. Go ahead and try signing in! ![Sign-in button](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/4813dc2-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=2aefa20261f34feab92e7275ff564047) Now that our sign-in button is working let's start working on showing the feed! ### Building the feed In the `page.tsx` file add the following: ```typescript page.tsx theme={"system"} "use client"; import { NeynarFeedList, useNeynarContext } from "@neynar/react"; export default function Home() { const { user } = useNeynarContext(); return (
); } ``` Here, we are using the `NeynarFeedList` component to show the trending casts if the user is not signed in, but, if they are signed in we show the following feed based on their fid. ![Feed](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/acd3550a3a60ac485499247eb069ce874e0db21db2027cb12515e2bb5b28e437-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=4c67b552827eef512c5398145d12c26d) Now, let's also show the list of channels that the user is following. ### Building the channels list and channel feed To get the list of channels that a user is following we'll use the neynar APIs. So, let's first initialise the client in a new `lib/neynarClient.ts` file like this: ```typescript neynarClient.ts theme={"system"} import { NeynarAPIClient } from "@neynar/nodejs-sdk"; const neynarClient = new NeynarAPIClient(process.env.NEYNAR_API_KEY!); export default neynarClient; ``` > [!NOTE] > > ### Make sure to add the NEYNAR\_API\_KEY to your .env file. Then, create a new file `api/channels/route.ts` in the `app` directory and add the following: ```typescript route.ts theme={"system"} import neynarClient from "@/lib/neynarClient"; import { NextResponse } from "next/server"; export const GET = async (req: Request) => { try { const { searchParams } = new URL(req.url); const fid = searchParams.get("fid"); const channels = await neynarClient.fetchUserChannels(Number(fid)); return NextResponse.json(channels, { status: 200 }); } catch (error) { return NextResponse.json( { error: (error as any).response?.data?.message }, { status: (error as any).response?.status || 500 } ); } }; ``` This will fetch the channels a user is following using the neynarClient and return it. Let's now use it on the home page. Head back to the `page.tsx` file and add the following: ```typescript page.tsx theme={"system"} "use client"; import { Channel } from "@neynar/nodejs-sdk/build/neynar-api/v2"; import { NeynarFeedList, useNeynarContext } from "@neynar/react"; import Link from "next/link"; import { useEffect, useState } from "react"; export default function Home() { const { user } = useNeynarContext(); const [channels, setChannels] = useState(); const fetchChannels = async () => { if (!user) { return; } const response = await fetch(`/api/channels?fid=${user?.fid}`); const data = await response.json(); setChannels(data); }; useEffect(() => { if (user) { fetchChannels(); } }, [user]); return (
{user && (

Channels

{channels && channels.channels.map((channel: Channel) => (
{channel.name}
))}
)}
); } ``` Here, we are now fetching the list of channels that the user follows and creating links with the name of the channel. These link to another page which we are yet to build but you should be able to see the list of channels now! ![Channels](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/cdd582228164912db3361af2e70daa8445d2d53e2052b7af9a61c05dfe3f4ce5-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=59f239715e764d32cc658a5f51938c7d) Now, let's build out the channel page as well which will show the feed of a specific channel. Create a new `channel/[channelId]/page.tsx` file in the `app` folder and add the following: ```typescript page.tsx theme={"system"} import { NeynarFeedList } from "@/components/Neynar"; export default async function Page({ params: { channelId }, }: { params: { channelId: string }; }) { return (

{channelId}

); } ``` Here, you can see that we are importing the component from a `@/components/Neynar` file and not the package directly because it is a client component. So, create a new `components/Neynar.tsx` file and add the following: ```typescript Neynar.tsx theme={"system"} "use client"; import { NeynarProfileCard, NeynarFeedList } from "@neynar/react"; export { NeynarProfileCard, NeynarFeedList }; ``` This will filter the feed based on the channelId and show only the casts made in that channel. If you go ahead and click on one of the channels you'll be able to see something like this: ![Channel](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/1b15c23ff0320c5f00d0008bd89f91e4ae660bc9a325604050131c8439c3820e-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=321758db5be4ad5207951ae0f69815ef) ### Building user profiles Let's also build a profile page for every user which shows their profile card and the casts they have created. Create a new file `profile/[username]/page.tsx` in the `app` folder and add the following: ```typescript page.tsx theme={"system"} import { NeynarProfileCard, NeynarFeedList } from "@/components/Neynar"; import neynarClient from "@/lib/neynarClient"; async function getData(username: string) { const user = await neynarClient.lookupUserByUsername(username); return { user: user.result.user }; } export default async function Page({ params: { username }, }: { params: { username: string }; }) { const { user } = await getData(username); return (
); } ``` Here, I am first resolving the username in the path to get the user object which can be later used to get the fid of the user. Then, we are displaying the `ProfileCard` and the `FeedList` filtered based on the user's fid. If you go to /profile/username then you'll be able to see the user's profile! ![Profile](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/6cd72b1c1895a602369b17b0014d2efad187b2c7ecdc84b674cd26a898b1348c-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=0944a31cbd260d148ce5a7ebba97be8b) ## Conclusion In this tutorial, we successfully built a Farcaster client with Next.js and the Neynar React SDK. Along the way, we covered essential features such as user authentication, creating feeds, fetching channels, and building user profiles. These steps give you a solid foundation to further enhance your client by adding more advanced features or customizing it to meet your specific needs. To explore the full implementation, visit the [GitHub repository](https://github.com/avneesh0612/neynar-client). If you have any questions or want to share your progress, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com). # Create Farcaster Bots via Script Using Neynar SDK Source: https://docs.neynar.com/docs/how-to-create-a-farcaster-bot Complete guide to creating Farcaster bots on Neynar with automated scripts. Learn to set up bot accounts, manage dedicated signers, and deploy automated social interactions using Neynar SDK with ready-to-use examples and templates. > [!NOTE] > > ### 1. If you need to create a new bot / agent account, see [Create Farcaster bot (UI)](/docs/create-farcaster-bot-ui) instead > > 2. Simplest way to start is to clone this git repo that has a sample bot ready to go: [https://github.com/neynarxyz/farcaster-examples](https://github.com/neynarxyz/farcaster-examples) In our `farcaster-examples` repo, `gm_bot` is an automated messaging bot designed to cast a 'gm ' message in Warpcast every day at a scheduled time. The bot operates continuously as long as the system remains online. It leverages [Neynar API](https://docs.neynar.com/) and is built using [@neynar/nodejs-sdk](https://www.npmjs.com/package/@neynar/nodejs-sdk). ## Prerequisites * [Node.js](https://nodejs.org/en/): A JavaScript runtime built on Chrome's V8 JavaScript engine. Ensure you have Node.js installed on your system. ## Installation ### Setting Up the Environment **1. Install PM2** PM2 is a process manager for Node.js applications. Install it globally using npm: ```bash Bash theme={"system"} npm install -g pm2 ``` **2. Install Project Dependencies** Navigate to the project directory and run one of the following commands to install all required dependencies: ```Text Yarn theme={"system"} yarn install ``` ```bash npm theme={"system"} npm install ``` **3. Configure Environment Variables** * Copy the example environment file: ```bash bash theme={"system"} cp .env.example .env ``` * Open the repo in your favorite editor and edit `.env` file to add your `NEYNAR_API_KEY` and `FARCASTER_BOT_MNEMONIC`. Optionally, you can also specify `PUBLISH_CAST_TIME` and `TIME_ZONE` for custom scheduling. ### Generating a Signer for an existing account Before running the bot, you need to generate a signer and get it approved via an onchain transaction. You can easily generate a signer by using the Neynar Dev portal at [https://dev.neynar.com](https://dev.neynar.com). **Step 1** Login to your Neynar dev portal **Step 2** App -> "Agents and bots" -> "use existing account" **Step 3** ![Login to your Neynar dev portal](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/c4e898688e9c165b0be84280f7f4aa3ba65d7041448a3e8d22e2dbd7ac58309a-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=961e0c13bb6852ec428aa7d657387ddb) Click the Sign in With Neynar button **Step 4** Connect the bot's address, the Warpcast logged-in user must be the bot **Step 5** If everything goes well, there will be a signer UUID, which can be used to cast as the bot! ## Running the Bot **1. Start the Bot** Launch the bot using the following command: ```bash Yarn theme={"system"} yarn start ``` ```Text npm theme={"system"} npm run start ``` **2. Verify the Process** Ensure that the bot is running correctly with: ```bash bash theme={"system"} pm2 status ``` **3. View Logs** To check the bot's activity logs, use: ```bash bash theme={"system"} pm2 logs ``` **4. Stopping the Bot** If you need to stop the bot, use: ```bash bash theme={"system"} pm2 kill ``` ## License `gm_bot` is released under the MIT License. This license permits free use, modification, and distribution of the software, with the requirement that the original copyright and license notice are included in any substantial portion of the work. ## FAQs/Troubleshooting > [accordion] What if gm\_bot stops sending messages? > > Check the PM2 logs for any errors and ensure your system's time settings align with the specified `TIME_ZONE`, also ensure that the process is running. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # How to Create a Farcaster Channel Feed Source: https://docs.neynar.com/docs/how-to-create-a-feed-based-on-an-array-of-farcaster-users-copy Create a feed for any channel (i.e. parent_url) on Farcaster protocol > [!NOTE] > > ### API endpoint > > This tutorial uses the [GET v2/feed](/reference/fetch-user-following-feed) endpoint Imagine you want to create a feed for casts in the Neynar channel. Casts on the Farcaster protocol can be under any `parent_url`, `parent_url`s are just arbitrary strings. Different clients choose to show their chosen list of `parent_urls` and some clients like Warpcast refer to them as "channels". ![channel feed](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/1f4a877-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=e4cdc50fd55e9f2a1691e3d828e3f0c2) ## Create a channel feed In this example, we will try to create a feed for the Neynar channel. If you are looking for the `parent_url`s for the channels on Warpcast, you can find them in the [Farcaster Channels Repository](https://github.com/neynarxyz/farcaster-channels/blob/main/warpcast.json). You will need the right `parent_url` to fetch channel data. #### Ensure you have the right environment setup Make sure to have node.js, typescript (ts) and yarn installed on your machine * Install [node.js and npm](https://nodejs.org/en/download/) * Install [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) * Install [ts-node](https://www.npmjs.com/package/ts-node#installation) globally #### Create a new typescript file with the right imports ```js theme={"system"} import { NeynarAPIClient, isApiErrorResponse } from "@neynar/nodejs-sdk"; import { AxiosError } from "axios"; import { FeedType, FilterType, } from "@neynar/nodejs-sdk/build/neynar-api/neynar-v2-api"; ``` #### Initialize the client ```bash theme={"system"} const client = new NeynarAPIClient("API_KEY"); ``` #### Call `fetchFeedPage` function to get feed ```js theme={"system"} (async () => { try { const cast = await client.fetchFeedPage(FeedType.Filter, { filterType: FilterType.ParentUrl, parentUrl: "chain://eip155:1/erc721:0xd4498134211baad5846ce70ce04e7c4da78931cc", limit: 1, // change to however many casts you want to fetch at once (max 100) withRecasts: true, }); console.log(JSON.stringify(cast)); } catch (error) { // isApiErrorResponse can be used to check for Neynar API errors // handle errors accordingly if (isApiErrorResponse(error)) { console.log("API Error", error.response.data); } else { console.log("Generic Error", error); } } })(); ``` #### Run your new .ts file * Navigate to the right folder in your terminal e.g. `cd ./test-sdk` * Run script by typing `yarn start` into the terminal #### You should now see an output like this ```json theme={"system"} { "casts": [ { "hash": "0xfb5f785e80cab58bb3ca9f3087b812d320846043", "thread_hash": "0xfb5f785e80cab58bb3ca9f3087b812d320846043", "parent_hash": null, "parent_url": "chain://eip155:1/erc721:0xd4498134211baad5846ce70ce04e7c4da78931cc", "parent_author": { "fid": null }, "author": { "object": "user", "fid": 976, "custody_address": "0xae6706fe1cb6887e5ed9b2e7e64ba78ba9c5e785", "username": "ba", "display_name": "Ben Adamsky", "pfp_url": "https://i.seadn.io/gae/-pmvC99Hw8fLjFKEIT3GxNs7qBhivAAUlIuVNXAykd06pUWSiAsypeLL8Q28dkjRtgXtNp07dLECv2p9P5MiqTrcmCR9OyYnAmO27Q?w=500&auto=format", "profile": { "bio": { "text": "surveyooor @survey x @ponder, eng @ freeport.app, benadamsky.com", "mentioned_profiles": [] } }, "follower_count": 472, "following_count": 345, "verifications": [ "0x64ff33b653b26edcb4644e27d3720f3c653f8371" ], "active_status": "active" }, "text": "Added the fid of each channel lead on the farcaster channels directory, I hope this is useful to other devs too!\n\nhttps://github.com/neynarxyz/farcaster-channels", "timestamp": "2023-11-15T21:42:45.000Z", "embeds": [ { "url": "https://github.com/neynarxyz/farcaster-channels" } ], "reactions": { "likes": [ { "fid": 6546, "fname": "artlu" }, { "fid": 10259, "fname": "alexgrover" }, { "fid": 299, "fname": "stout" }, ], "recasts": [ { "fid": 616, "fname": "dylsteck" }, { "fid": 2455, "fname": "notdevin.eth" }, { "fid": 194, "fname": "rish" } ] }, "replies": { "count": 2 }, "mentioned_profiles": [] } ], "next": { "cursor": "eyJ0aW1lc3RhbXAiOiIyMDIzLTExLTE1IDIxOjQyOjQ1LjAwMDAwMDAifQ==" } } ``` **You get all the data in one request, making it easy to display or use in your next operation.** If you want to try getting a live response, head over to our API page for [v2/feed](/reference/fetch-user-following-feed). You can take the cursor from the output and pass it in the next request to the SDK to page to the next set of results. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Create new Farcaster Account Source: https://docs.neynar.com/docs/how-to-create-a-new-farcaster-account-with-neynar Create and register new user accounts on Farcaster through Neynar This guide enables developers to seamlessly create and register new user accounts on Farcaster through Neynar. By the end, you will: * Claim and register a new user account. * Assign a fname and username to the new user account. * Obtain a `signer_uuid` for the new user account and make changes on Farcaster. * Get an understanding of the entire flow behind the scenes. ## What You'll Need ### Prerequisites * Installation of [curl](https://developer.zendesk.com/documentation/api-basics/getting-started/installing-and-using-curl/#installing-curl), [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable), and [Node.js and npm](https://nodejs.org/en/download/) * Neynar API key from [dev.neynar.com](https://dev.neynar.com) * **App Wallet** created in developer portal - [Setup Guide](/docs/managing-onchain-wallets) * **Funded wallet with \$5+ ETH on Optimism** (covers initial account pre-registration on first call) > [!NOTE] > > **New to App Wallets?** All onchain operations require a wallet\_id. See [Managing Onchain Wallets](/docs/managing-onchain-wallets) to create your app wallet in the developer portal (self-service!). > > **Recommended Setup Steps:** > > 1. Log in to [dev.neynar.com](https://dev.neynar.com) and select your app > 2. Activate a wallet from the "App Wallet" tab > 3. Fund the wallet with \$5+ ETH on Optimism > 4. Add the `x-wallet-id` header to your API requests (treat it like a secret) *** > [!TIP] > > ### If using embedded wallets, see this [guide](https://neynar.notion.site/Creating-new-accounts-with-embedded-wallets-14a655195a8b80999ccec0aa635b23af?pvs=4) written by community member [jpfraneto](https://warpcast.com/jpfraneto.eth); includes source code you can use to get started. ## Step 1: Claim an account for the new user To register a new user, you need to claim an account for that user. > [!WARNING] > > **Cold Start (First Call Only):** The first time you call this endpoint with a new `wallet_id`, it will take approximately **1 minute** to complete as it pre-registers a few FID accounts. Subsequent calls will be fast (\< 1 second). Make sure your wallet has **\$5+ ETH on Optimism** before the first call. > [!WARNING] > > **Wallet Consistency Required:** The same `wallet_id` used here must also be used in Step 5 when calling `POST /v2/farcaster/user/`. Using different wallets will result in an error. ### API Call ```bash Shell theme={"system"} curl --location 'https://api.neynar.com/v2/farcaster/user/fid' \ --header 'api_key: NEYNAR_API_KEY' \ --header 'x-wallet-id: YOUR_WALLET_ID' ``` **Note:** Replace NEYNAR\_API\_KEY with your actual API key and YOUR\_WALLET\_ID with your wallet ID from the [setup guide](/docs/managing-onchain-wallets). ### Responses ```json 200 theme={"system"} { "fid": UNIQUE_FID // UNIQUE_FID is a number } ``` ```json 500 theme={"system"} { "message": "Please try again later" } ``` In the next step, you'll need this fid here to generate a signature. ## Step 2: Ask the user to sign a message accepting their new Farcaster account To create a Farcaster account, users must sign a message proving they're willing to accept the account using a particular wallet address. The user needs to sign a message containing 1. fid (fetched in the previous step) 2. the address that will custody the Farcaster account (connected address on the client/app developer) 3. and a deadline until which their signature is valid. (generated by the client/app developer) Usually, client or application developers must implement this step by constructing a message and asking the user to sign it in their connected web3 wallet. However, for the sake of testing out the flow, here's a script that effectively produces the equivalent signatures you would get back from eth.sign ### Setup project ```bash theme={"system"} mkdir generate-required-parameters cd generate-required-parameters ``` ### Install Dependencies ```Text yarn theme={"system"} yarn add @farcaster/hub-nodejs viem ``` ```Text npm theme={"system"} npm install @farcaster/hub-nodejs viem ``` ### Create the Script Create a file named `generate-required-parameters.js` ```bash theme={"system"} touch generate-required-parameters.js ``` Paste the following script in it. This script generates certain parameters that you'll need to make the next API call. Replace `FID_TO_COLLECT_SIGNATURE_FOR` with fid returned from `GET - /v2/farcaster/user/fid` and `NEW_ACCOUNT_MNEMONIC` with a new account MNEMONIC. ```javascript Javascript theme={"system"} // generate-required-parameters.js --> Filename const { ID_REGISTRY_ADDRESS, ViemLocalEip712Signer, idRegistryABI, } = require('@farcaster/hub-nodejs'); const { bytesToHex, createPublicClient, http } = require('viem'); const { mnemonicToAccount } = require('viem/accounts'); const { optimism } = require('viem/chains'); const publicClient = createPublicClient({ chain: optimism, transport: http(), }); const FID = 'FID_TO_COLLECT_SIGNATURE_FOR'; // fid returned from GET - /v2/farcaster/user/fid const MNEMONIC = 'NEW_ACCOUNT_MNEMONIC'; const getDeadline = () => { const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return BigInt(now + oneHour); }; (async () => { const deadline = getDeadline(); console.log('\ndeadline: ', parseInt(deadline)); const requestedUserAccount = mnemonicToAccount(MNEMONIC); const requestedUserAccountSigner = new ViemLocalEip712Signer( requestedUserAccount ); console.log( '\nrequested_user_custody_address: ', requestedUserAccount.address ); let requestedUserNonce = await publicClient.readContract({ address: ID_REGISTRY_ADDRESS, abi: idRegistryABI, functionName: 'nonces', args: [requestedUserAccount.address], }); console.log('\nfid: ', parseInt(FID)); let requestedUserSignature = await requestedUserAccountSigner.signTransfer({ fid: BigInt(FID), to: requestedUserAccount.address, nonce: requestedUserNonce, deadline, }); console.log( '\nsignature: ', bytesToHex(requestedUserSignature.value), '\n' ); })(); ``` ### Execute Script Run the script to generate the necessary parameters for user registration. Get the FID i.e `UNIQUE_FID` from **Step 1** and pass it on in the following command ```bash theme={"system"} node generate-required-parameters.js ``` ### Script Output You'll receive output containing several values, including `deadline`, `requested_user_custody_address`, `fid`, `signature` ## Step 3: Add a managed signer to new account (optional) If you want to add a [managed signer](https://docs.neynar.com/docs/which-signer-should-you-use-and-why) to the new account during registration, you can include signer details in the registration request. This allows your application to manage the account on behalf of the user. ### Create a Signer First, create a signer using the [Create Signer API](https://docs.neynar.com/reference/create-signer): ```bash Shell theme={"system"} curl --location 'https://api.neynar.com/v2/farcaster/signer' \ --header 'api_key: NEYNAR_API_KEY' \ --header 'Content-Type: application/json' \ --data '{}' ``` This will return a `signer_uuid` and `public_key` that you'll need for the next step. ### Generate Signed Key Request Metadata Signature Use the following script to generate the `signed_key_request_metadata_signature` needed for the registration request. ```javascript Javascript theme={"system"} // generate-signed-key-request-metadata-signature.js --> Filename const { bytesToHex, hexToBytes } = require('viem'); const { mnemonicToAccount } = require('viem/accounts'); const { ViemLocalEip712Signer } = require('@farcaster/core'); (async () => { /**_ Get public_key and signer_uuid of the signer _**/ const signer_uuid = 'bb81ddbb-8c4f-4a61-94ad-6a82fc2e3e6d'; // From CREATE signer API const key = '0x900ac01061e15e11d332796e9ebbece5b46afdac4a1b641e82f3e344371433a4'; // From CREATE signer API /*** Get account ***/ const fid = 0; // Your app's FID const account = mnemonicToAccount(''); // Your app's Mnemonic const signer = new ViemLocalEip712Signer(account); /*** Generating a Signed Key Request signature ***/ const deadline = Math.floor(Date.now() / 1000) + 86400; console.log('signer_uuid', signer_uuid); console.log('\napp_fid', fid); console.log('\ndeadline', deadline); const signed_key_request_metadata = await signer.getSignedKeyRequestMetadata({ requestFid: fid, key: hexToBytes(key), deadline, }); if (!signed_key_request_metadata.isOk()) { console.log('Error getting signed key request metadata'); return; } const signed_key_req_metadata_sig = bytesToHex( signed_key_request_metadata.value ); console.log('\nsigned_key_req_metadata_sig', signed_key_req_metadata_sig); })(); ``` ### Install Additional Dependencies ```Text yarn theme={"system"} yarn add @farcaster/core ``` ```Text npm theme={"system"} npm install @farcaster/core ``` This script will output the `signed_key_request_metadata_signature` along with the `signer_uuid`, `app_fid`, and `deadline` that you'll include in the registration request. ## Step 4: Ask the user to pick their fname (optional) Client applications should ask users to pick a username for their Farcaster account. The [fname availability API](/reference/is-fname-available) should be used to check if their chosen username is available. The fname should match the following regex - `/^[a-z0-9][a-z0-9-]{0,15}$/`. Official regex defined in the [farcaster/core](https://github.com/farcasterxyz/hub-monorepo/blob/a6367658e5c518956a612f793bec06eef5eb1a35/packages/core/src/validations.ts#L20) library ## Step 5: Register the User Construct a POST request with the generated parameters to finalize the user's registration. ### Finalize Registration ```bash Without Managed Signer theme={"system"} curl --location 'https://api.neynar.com/v2/farcaster/user' \ --header 'api_key: NEYNAR_API_KEY' \ --header 'x-wallet-id: YOUR_WALLET_ID' \ --header 'Content-Type: application/json' \ --data '{ "deadline": "DEADLINE_FROM_SCRIPT", "requested_user_custody_address": "CUSTODY_ADDRESS_FROM_SCRIPT", "fid": 0, "signature": "SIGNATURE_FROM_SCRIPT", "fname": "desired-username" }' ``` ```bash With Managed Signer theme={"system"} curl --location 'https://api.neynar.com/v2/farcaster/user' \ --header 'api_key: NEYNAR_API_KEY' \ --header 'x-wallet-id: YOUR_WALLET_ID' \ --header 'Content-Type: application/json' \ --data '{ "signer": { "uuid": "SIGNER_UUID_FROM_SIGNER_SCRIPT", "signed_key_request_metadata_signature": "SIGNED_KEY_REQUEST_SIGNATURE_FROM_SIGNER_SCRIPT", "app_fid": APP_FID_SIGNER_SCRIPT, "deadline": DEADLINE_FROM_SIGNER_SCRIPT }, "deadline": "DEADLINE_FROM_SCRIPT", "requested_user_custody_address": "CUSTODY_ADDRESS_FROM_SCRIPT", "fid": 0, "signature": "SIGNATURE_FROM_SCRIPT", "fname": "desired-username" }' ``` > [!NOTE] > > **Required:** The [`x-wallet-id` header](/docs/managing-onchain-wallets) is required for account registration. See [Managing Onchain Wallets](/docs/managing-onchain-wallets) for setup instructions. ### Responses ```json 200 theme={"system"} { "success": true, "message": "Account transferred successfully.", "signer": { "fid": 0, "signer_uuid": "35b0bbd5-4e20-4213-a30c-4183258a73ab", "status": "APPROVED", "public_key": "0x123412341234123412341234123412341234" }, "signers": [ { "fid": 0, "signer_uuid": "35b0bbd5-4e20-4213-a30c-4183258a73ab", "status": "APPROVED", "public_key": "0x123412341234123412341234123412341234" }, { "fid": 0, "signer_uuid": "9f6efd31-97a7-4fbe-b7e8-259871da8745", "status": "APPROVED", "public_key": "0x1234123412341234123412332322323232323" } ] } ``` ```json 400 theme={"system"} { "message": "Account not found" } ``` ```json 401 theme={"system"} { "message": "Account is not issued to you" } ``` ```json 404 theme={"system"} { "message": "Account not found" } ``` ```json 409 theme={"system"} { "message": "Account is already registered to another user" } ``` ```json 500 theme={"system"} { "success": false, "message": "Failed to sign transfer", } ``` ## Step 6: Profile setup (optional) Using the approved signer\_uuid from the response in Step 5, you can ask the user to update their profile by picking a profile photo, display name, bio, and more. # Dynamic Frame Creation Source: https://docs.neynar.com/docs/how-to-create-frames-using-the-neynar-sdk Take a look at how to create frames on the fly using our SDK > [!NOTE] > > ### Refers to this set of APIs: [Create frame](/reference/publish-neynar-frame) In this guide, we’ll look at creating Farcaster frames with the neynar SDK without worrying about creating a new web app, hosting it, and all the hassle. Just write the code for your frame, call the API to publish, and you're done! This can be useful in many places, especially if you want to generate frames on the fly. ## Creating a new node app Create a new app by entering the following commands in your terminal: ```powershell PowerShell theme={"system"} mkdir frames-node cd frames-node npm init ``` We are going to need the `@neynar/nodejs-sdk`, so let’s install that as well: ```powershell PowerShell theme={"system"} yarn add @neynar/nodejs-sdk ``` ## Creating the frame Once the project is created and the packages are installed, you can open it in your favorite editor and create a new `script.js` file and add the following: ```javascript script.js theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; const main = async () => { const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const neynarClient = new NeynarAPIClient(config); const creationRequest = { name: "gm", pages: [ { image: { url: "https://remote-image.decentralized-content.com/image?url=https%3A%2F%2Fipfs.decentralized-content.com%2Fipfs%2Fbafybeifjdrcl2p4kmfv2uy3i2wx2hlxxn4hft3apr37lctiqsfdixjy3qi&w=1920&q=75", aspect_ratio: "1.91:1", }, title: "Neynar NFT minting frame", buttons: [ { action_type: "mint", title: "Mint", index: 1, next_page: { mint_url: "eip155:8453:0x23687d295fd48db3e85248b734ea9e8fb3fced27:1", }, }, ], input: { text: { enabled: false, }, }, uuid: "gm", version: "vNext", }, ], }; const frame = await neynarClient.publishNeynarFrame(creationRequest); console.log(frame); }; main(); ``` Make sure to pass your API key in the NeynarAPIClient. Ideally, you should store your API keys in env variables. This is a simple NFT minting frame here. The `publishNeynarFrame` function accepts an object with a bunch of parameters. Let's take a look at what they are: * `name`: This will be the name of your frame, and it will be visible only to you on your [dashboard](https://neynar.com/nfs/frames) * `pages`: This will be an array of the pages to display on the frame; the parameters here are just what you would typically pass in a frame, like image, title, buttons, input, version, etc. * `next_page`: This is where you define what happens when you click the button. Since we are creating an NFT minting frame, it contains the `mint_url`. But you can pass in `redirect_url` to redirect to a new page or `uuid` of a new page, to change the current frame page. * mint\_url: This is a string I created using data from a collection on [zora](https://zora.co). The string should be of the form "eip155:chainId:contractAddress:tokenID". The contract address and token ID can be found from the Zora share URL, which looks somewhat like this [https://zora.co/collect/base:0x23687d295fd48db3e85248b734ea9e8fb3fced27/1](https://zora.co/collect/base:0x23687d295fd48db3e85248b734ea9e8fb3fced27/1); you can check out the corresponding `chainId` of your chain on [ChainList](https://chainlist.org/?search=). For example, `chainId` for the base mainnet chain is 8453. Once you have updated all your metadata and API key, run the script by using the command: ```powershell PowerShell theme={"system"} node script.js ``` ![Frame creation](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/4aff09d-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=18ba0cf30919a70c614e25ffbd7f8f3d) It should return an object similar to this when you run your script, copy the link from here and enter it on the [Warpcast validator](https://warpcast.com/~/developers/frames). Enter your URL and you can inspect the properties of your frame here. > [!NOTE] > > ### Mint button won't work in the validator, to test out the mint button you'll need to create a new cast ![Frame creation](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/de63076-SCR-20240403-pytu.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=133a08b3ed7f064701a59d8bb779a9ad) If everything looks good, you can create your cast and share your cast with the world! Lastly, make sure to share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar), and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # How to create frames without code using Frames studio Source: https://docs.neynar.com/docs/how-to-create-frames-without-code-using-frames-studio In this guide, we’ll take a look at how to create Farcaster frames without writing a single line of code, using our Frames studio. Head over to your [Neynar dashboard](https://dev.neynar.com/frames) and click on Frame studio ![Frame studio neynar](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/e06e777-image.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=e047b866d337ed90e98e874d4177f111) Here, you can either create a new frame from scratch or choose one of our existing templates ![Frame studio neynar](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/aac41b4-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=46352206de01865a5401c39c1f6413c1) # Programmatic Webhooks Source: https://docs.neynar.com/docs/how-to-create-webhooks-on-the-go-using-the-sdk Neynar webhooks are a way to receive real-time updates about events on the Farcaster protocol. You can use webhooks to build integrations that respond to events on the protocol, such as when a user creates a cast or when a user updates their profile. > [!NOTE] > > ### Related set of APIs: [Create a webhook](/reference/publish-webhook) You might need to create multiple webhooks tracking different activities and calling different APIs programmatically. So, let's see how we can create webhooks using the neynar SDK in a node script. I am using a [bun app](https://bun.sh/) for the sake of simplicity of this guide, but you can use express, Next.js api routes or any server you wish to use! Create a new server by entering the following commands in your terminal: ```powershell Powershell theme={"system"} mkdir webhooks-sdk cd webhooks-sdk bun init ``` We are going to need the `@neynar/nodejs-sdk`, so let’s install that as well: ```powershell Powershell theme={"system"} bun add @neynar/nodejs-sdk ``` Once the project is created and the packages are installed, you can open it in your favourite editor and add the following in a new `script.ts` file: ```typescript script.ts theme={"system"} import { NeynarAPIClient,Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey:process.env.NEYNAR_API_KEY, }); if (!process.env.NEYNAR_API_KEY) { throw new Error("NEYNAR_API_KEY is not set"); } const client = new NeynarAPIClient(config); const webhook = await client.publishWebhook({ name:"abc", url:"YOUR_NGROK_URL_HERE", subscription: { "cast.created": { text: "\\$(DEGEN|degen)", }, }, } ); console.log(webhook); ``` This simple script uses the neynarClient to publish a webhook with a name, url and subscription parameter. The webhook will call the target URL every time the subscribed event occurs. Here, I've chosen all the casts created with degen present in the text. You can select the regex or type of subscription according to your use. You can also subscribe to multiple events here at once! You can take a look at all the possible ways in [Publish Webhook API](/reference/publish-webhook). You can get the neynar api key that we are using to initialise the client from the neynar dashboard. ![Neynar API Key](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/794cfad-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=4b72f6261afde6d5da380fa53c94c3d9) Add the api key in a `.env` file with the name `NEYNAR_API_KEY`. Now, let's test our api but to do that we'll need an api which we can call. In the `index.ts` file add the following: ```typescript index.ts theme={"system"} const server = Bun.serve({ port: 3000, async fetch(req) { try { console.log(await req.json()); return new Response("gm!"); } catch (e: any) { return new Response(e.message, { status: 500 }); } }, }); console.log(`Listening on localhost:${server.port}`); ``` This will spin up a server on localhost:3000 and log the request body every time the API gets called. Let's run it in one terminal using `bun run index.ts` and we'll use ngrok to serve it. If you don’t already have it installed, install it from [here](https://ngrok.com/downloads/mac-os). Once it’s installed, authenticate using your auth token and serve your app using this command: ```powershell Powershell theme={"system"} ngrok http http://localhost:3000 ``` > [!WARNING] > > ### Free endpoints like ngrok, localtunnel, etc. can have issues because service providers start blocking events over a certain limit Copy the URL you got from ngrok and replace it with `YOUR_NGROK_URL_HERE` in the previous script. Once you've done that, run the script using `bun run script.ts` and it should create a webhook for you like this: ![Webhook Created](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/c8a7d49-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=72fed79be8647018e3083a9bc66bee2d) Once the webhook is created, you'll start seeing logs on your server, which means that our webhook is working successfully! ## Conclusion Lastly, make sure to share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar), and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # Customize SIWN - Neynar API Source: https://docs.neynar.com/docs/how-to-customize-sign-in-with-neynar-button-in-your-app Comprehensive guide to customizing Sign In With Neynar (SIWN) for seamless Farcaster authentication. Learn to customize the sign-in experience, integrate write access permissions, and connect user accounts to your app in minutes with custom styling and branding. > [!WARNING] > > ### Sign In With Neynar is deprecated > > SIWN is being retired. New integrations should use [Neynar-managed signers](/docs/integrate-managed-signers) instead. Existing connections will keep working; new SIWN connections will stop being issued after **Friday, August 14, 2026**. > [!NOTE] > > ### Sign In with Neynar (SIWN) is the easiest way to let users connect their Farcaster account to your app > > See [SIWN: Connect Farcaster Accounts](/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn) on how to integrate in less than a min. This guide shows you how to customize the sign in experience for your users. The SIWN button on your app can be customized as you see fit. Below is how it shows up by default: ![](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/718bcb0-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=89501268b4980a598cb1d180df035fbd) Check [demo.neynar.com](https://demo.neynar.com) to try out live customizations. Below are some of the attributes you can change in the code when integrating: **data-variant** : * Values: `neynar`, `warpcast`, `farcaster` * defaultValue: `neynar` * dataType: *string* **data-theme** : Theme of button * Values: `light` (#ffffff) \[default] , `dark` (#000000) * defaultValue: `light` * Applicable for **data-variant. Not** for Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) * **data-background\_color** can override the colour **data-theme** color * dataType: *string* **data-logo\_size** : Logo size for **data-variant** * defaultValue: `30px` * dataType: string (Takes values similar to a css property eg. 40px or 40 or 4rem) * Applicable for **data-variant. Not** for **data-custom\_logo\_url** **data-height** : * defaultValue: `48px` * dataType: *string* (Takes values similar to a css property eg. 40px or 40 or 4rem) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-width** : * defaultValue: `218px` (By default minWidth is set to `205px` but it can be modified using **data-styles**) * dataType: *string* (Takes values similar to a css property eg. 40px or 40 or 4rem) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-border\_radius** : * defaultValue: `10px` * dataType: *string* (Takes values similar to a css property eg. 40px or 40 or 4rem or ‘10px 20px 30px 40px’) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-font\_size** : * defaultValue: `16px` * dataType: *string* (Takes values similar to a css property eg. 40px or 40 or 4rem) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-font\_weight** : * defaultValue: `300` * dataType: *string* (Takes values similar to a css property eg. 100 or normal) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-padding** : * defaultValue: ‘`8px` `15px`’ * dataType: *string* (Takes values similar to a css property eg. 40px or 40 or 4rem or ‘10px 20px 30px 40px’) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-margin** : * defaultValue: `0px` * dataType: *string* (Takes values similar to a css property eg. 40px or 40 or 4rem or ‘10px 20px 30px 40px’) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-text** : * defaultValue: `“”` * dataType: *string* * Applicable for Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) * For this **data-variant** should **not** be present **data-color** : * defaultValue: `#000000` * dataType: *string* (Takes values similar to a css property eg. red or #e2e2e2) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-background\_color** : * defaultValue: `#ffffff` * dataType: *string* (Takes values similar to a css property eg. red or #e2e2e2) * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) **data-styles** : * defaultValue: `“”` * dataType: string * Applicable for **data-variant** and Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) * **overrides all the above styles** * example: data-styles='\{ "backgroundColor": "red" }' **data-custom\_logo\_url** : Hosted logo (**preferably** svg) * defaultValue: `“”` * dataType: *string* * Applicable for Custom Text + Logo (**data-text** and **data-custom\_logo\_url**) * **Note:** size of the logo should be adjusted within svg itself # Overview Source: https://docs.neynar.com/docs/how-to-embed-farcaster-frames-in-your-app-with-neynar Host Frames on your product and let users interact with them directly If you are hosting a Frame on your product (you can, they are not Warpcast specific!), you can 1. Fetch a feed of all Frames using our [/feed/frames API](/reference/fetch-frames-only-feed) 2. Let users connect Farcaster to your product with [Sign In with Neynar](/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn) or use [dedicated signers](/docs/write-to-farcaster-with-neynar-managed-signers) on our Enterprise plan 3. Let users create Frame actions on your product directly using [Neynar Frame Action POST API](/reference/post-frame-action) 4. To test Frames on your local machine, set up [ngrok](https://ngrok.com/downloads/mac-os) and use ngrok as your Frame's POST URL. ![](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/9276e29-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=10b7edbc3fa41609c2bed4892b23d410) Example Frame hosted on Supercast with Neynar # How to Create a Feed Based on an Array of Farcaster Users Source: https://docs.neynar.com/docs/how-to-fetch-farcaster-feed-for-a-given-user-copy Create a feed based on a given input of Farcaster users > [!NOTE] > > ### API endpoint > > This tutorial uses the [GET v2/feed](/reference/fetch-user-following-feed) endpoint Imagine you want to create a custom list of users in your app and show a feed of casts based on users in the list, similar to Twitter lists. ![feed](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/007fd1a-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=8bfc18dc93af2e987d3ccbcc5b47fe11) ## Create a feed given a list of users In this example, we will try to feed for fids 1, 2 and 3. If you know someone's username, you can find their fid from our [user endpoint](/reference/search-user). #### Ensure you have the right environment setup Make sure to have node.js, typescript (ts) and yarn installed on your machine * Install [node.js and npm](https://nodejs.org/en/download/) * Install [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) * Install [ts-node](https://www.npmjs.com/package/ts-node#installation) globally #### Create a new typescript file with the right imports ```js theme={"system"} import { NeynarAPIClient, isApiErrorResponse } from "@neynar/nodejs-sdk"; import { AxiosError } from "axios"; import { FeedType, FilterType, } from "@neynar/nodejs-sdk/build/neynar-api/neynar-v2-api"; ``` #### Initialize the client ```js theme={"system"} const client = new NeynarAPIClient("API_KEY"); ``` #### Call `fetchFeedPage` function to get feed ```js theme={"system"} (async () => { try { const cast = await client.fetchFeedPage(FeedType.Filter, { filterType: FilterType.Fids, fids: "1,2,3", limit: 1, // change to however many casts you want to fetch at once (max 100) withRecasts: true, }); console.log(JSON.stringify(cast)); } catch (error) { // isApiErrorResponse can be used to check for Neynar API errors // handle errors accordingly if (isApiErrorResponse(error)) { console.log("API Error", error.response.data); } else { console.log("Generic Error", error); } } })(); ``` #### Run your new .ts file * Navigate to the right folder in your terminal e.g. `cd ./test-sdk` * Run script by typing `yarn start` into the terminal #### You should now see an output like this ```json theme={"system"} { "casts": [ { "hash": "0x46db48a011698758bfb9b1f77c07596249e277ef", "thread_hash": "0x46db48a011698758bfb9b1f77c07596249e277ef", "parent_hash": null, "parent_url": "chain://eip155:7777777/erc721:0xe96c21b136a477a6a97332694f0caae9fbb05634", "parent_author": { "fid": null }, "author": { "object": "user", "fid": 3647, "custody_address": "0x54faa8c0c270093fcd0f0c4b29ace4d5426113b7", "username": "iamnick.eth", "display_name": "Nick Smith", "pfp_url": "https://i.seadn.io/gcs/files/8583feea8a3465788d84367a92d80512.png?w=500&auto=format", "profile": { "bio": { "text": "Building withfam.xyz Prev. Co-founder @ talisman.xyz ✴︎ Member @ FWB, Builder DAO, The Park ✷", "mentioned_profiles": [] } }, "follower_count": 482, "following_count": 149, "verifications": [ "0x0f9b1b68f848cb65f532bc12825357201726d3d2" ], "active_status": "active" }, "text": "https://x.com/iamnickdoteth/status/1724238126884024629?s=20\n\nBoiler Room, but make it onchain\n\nwe're forming a squad. HMU if you're interested", "timestamp": "2023-11-17T01:22:25.000Z", "embeds": [ { "url": "https://x.com/iamnickdoteth/status/1724238126884024629?s=20" } ], "reactions": { "likes": [ { "fid": 191213, "fname": "stringtheory69" }, { "fid": 13870, "fname": "0xsatori.eth" }, { "fid": 8, "fname": "jacob" }, { "fid": 195091, "fname": "greyseymour" }, { "fid": 7715, "fname": "royalaid" }, { "fid": 1407, "fname": "zinger" }, { "fid": 3115, "fname": "ghostlinkz" }, { "fid": 2, "fname": "varunsrin.eth" } ], "recasts": [ { "fid": 191213, "fname": "stringtheory69" }, { "fid": 7715, "fname": "royalaid" }, { "fid": 2, "fname": "varunsrin.eth" } ] }, "replies": { "count": 3 }, "mentioned_profiles": [] } ], "next": { "cursor": "eyJ0aW1lc3RhbXAiOiIyMDIzLTExLTE3IDAxOjIyOjI1LjAwMDAwMDAifQ==" } } ``` **You get all the data in one request, making it easy to display or use in your next operation.** If you want to try getting a live response, head over to our API page for [v2/feed](/reference/fetch-user-following-feed). You can take the cursor from the output and pass it in the next request to the SDK to page to the next set of results. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Find Mutual Follows and Followers Source: https://docs.neynar.com/docs/how-to-fetch-mutual-followfollowers-in-farcaster Learn how to find mutual follows and followers between Farcaster users using Neynar API. Build social discovery features like 'Followed by X users you follow' similar to Twitter, with comprehensive examples and SDK integration for enhanced user connections. > [!NOTE] > > ### This guide refers to [this API](/reference/fetch-relevant-followers) ## How to Find Mutual Follows and Followers On X (Twitter) profile page, there is a "Followed by A, B, C, and 10 others you follow". This guide demonstrates how to use the Neynar SDK to make the same thing but for Farcaster. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey:process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Say we want to get people @rish follows that also follows @manan. This is useful if we want to mutual connections between two users. We'll fetch @rish's followings first. ```javascript Javascript theme={"system"} const fetchAllFollowing = async (fid: number) => { let cursor: string | null = ""; let users: unknown[] = []; do { const result = await client.fetchUserFollowing({fid, limit: 150, cursor, }); users = users.concat(result.result.users); cursor = result.result.next.cursor; console.log(cursor); } while (cursor !== "" && cursor !== null); return users; }; const rishFID = 194; const rishFollowings = await fetchAllFollowing(rishFID); ``` Then we'll fetch @manan's followers. ```javascript Javascript theme={"system"} const fetchAllFollowers = async (fid: number) => { let cursor: string | null = ""; let users: unknown[] = []; const limit=150 do { const result = await client.fetchUserFollowers({fid, limit, cursor, }); users = users.concat(result.result.users); cursor = result.result.next.cursor; console.log(cursor); } while (cursor !== "" && cursor !== null); return users; }; const mananFID = 191; const mananFollowers = await fetchAllFollowers(mananFID); ``` Think of these two arrays as sets. We want to find the intersection of these two sets. We can use the `fid` property to find the intersection. ```javascript Javascript theme={"system"} const mutualFollowings = rishFollowings.filter((following) => mananFollowers.some((follower) => follower.fid === following.fid) ); console.log(mutualFollowings); ``` Example output: ```json theme={"system"} [ { fid: 6227, custodyAddress: "0x35b92ea9c3819766ec1fff8ddecec69028b0ac42", username: "ekinci.eth", displayName: "Emre Ekinci ~ q/dau", pfp: { url: "https://i.imgur.com/smbrNPw.jpg" }, profile: { bio: [Object ...] }, followerCount: 670, followingCount: 660, verifications: [ "0x5f57c686bdbc03242c8fa723b80f0a6cdea79546" ], activeStatus: "active", timestamp: "2023-11-14T04:13:11.000Z" }, { fid: 280, custodyAddress: "0xd05d60b5762728466b43dd94ba882d050b60af67", username: "vrypan.eth", displayName: "vrypan.eth", pfp: { url: "https://i.imgur.com/jmXEW3I.png" }, profile: { bio: [Object ...] }, followerCount: 1296, followingCount: 493, verifications: [ "0x8b0573d1c80362db589eda39c2e30f5190d7eb51", "0x93c620d2af377c6c37e3e3c1d3e065eb04b08ae2" ], activeStatus: "active", timestamp: "2023-11-14T01:37:40.000Z" } // ... ] ``` > [!NOTE] > > you'd probably want to cache the results of `fetchAllFollowing` and `fetchAllFollowers` so you don't have to make the same API calls again. That's it! You can use this to make a "Followed by A, B, C, and 10 others you follow" info in your Farcaster app. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # How to fetch notifications for a Farcaster user Source: https://docs.neynar.com/docs/how-to-fetch-notification-for-a-user Fetch all notifications for a given fid > [!NOTE] > > ### API endpoint > > This tutorial uses the [GET v2/notifications](/reference/fetch-all-notifications) endpoint Show follow, like, recast, reply, quote and mention notifications for a given user. ![notifications](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/ab5bf6b-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=9bb961fe846bc155ff68c6d1117a9b79) ## Get notifications for a given user `fid` In this example, we will try to create a notifications page for the given user. ### Prerequisites * Install [Node.js](https://nodejs.org/en/download/package-manager) * Optional: Install [Yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) (Alternatively, npm can be used) ### Project Setup **Initialize Project Directory** ```bash Shell theme={"system"} mkdir fetch-notifications-for-a-user cd fetch-notifications-for-a-user ``` **Install Neynar SDK along with typescript** Install using npm ```bash Shell theme={"system"} npm i @neynar/nodejs-sdk npm i -D typescript ``` // or Install using Yarn ```bash Shell theme={"system"} yarn add @neynar/nodejs-sdk yarn add -D typescript ``` **Initialize typescript environment** ```bash Shell theme={"system"} npx tsc --init ``` ### Implementation Create index.ts file at root level ```bash Shell theme={"system"} touch index.ts ``` Add imports ```typescript Typescript theme={"system"} import { NeynarAPIClient, isApiErrorResponse } from "@neynar/nodejs-sdk"; import { AxiosError } from "axios"; ``` Instantiate the client ```typescript Typescript theme={"system"} const client = new NeynarAPIClient("API_KEY"); ``` Call `fetchAllNotifications` function to get feed ```typescript Typescript theme={"system"} (async () => { try { const cast = await client.fetchAllNotifications(194, { limit: 1, // change to however many notifications you want to get at once (max 50) }); console.log(JSON.stringify(cast)); } catch (error) { // isApiErrorResponse can be used to check for Neynar API errors // handle errors accordingly if (isApiErrorResponse(error)) { console.log("API Error", error.response.data); } else { console.log("Generic Error", error); } } })(); ``` ### Running the project ```bash Shell theme={"system"} npx ts-node index.ts ``` ### Result #### You should now see an output like this ```json theme={"system"} { "notifications": [ { "object": "notification", "most_recent_timestamp": "2023-11-17T01:22:43.000Z", "type": "likes", // Can be "likes", "mentions", "replies", or "quotes" "cast": { "hash": "0x1aa36542c2dc8799dded2cde37a3c1a3840eb41f", "thread_hash": "2a47ce562ff5ce5916ea9095227c5fbff9618157", "parent_hash": "2a47ce562ff5ce5916ea9095227c5fbff9618157", "parent_url": null, "parent_author": { "fid": "576" }, "author": { "object": "user", "fid": 194, "custody_address": "0xb95dc10483be18f7c69ddf78d4baafecc01c5530", "username": "rish", "display_name": "rish", "pfp_url": "https://res.cloudinary.com/merkle-manufactory/image/fetch/c_fill,f_png,w_256/https://lh3.googleusercontent.com/MEaRCAMdER6MKcvmlfN1-0fVxOGz6w98R8CrP_Rpzse9KZudgn95frTd0L0ZViWVklBj9fuAcJuM6tt7P-BRN0ouAR87NpzZeh2DGw", "profile": { "bio": { "text": "@neynar 🪐 | nf.td/rish", "mentioned_profiles": [] } }, "follower_count": 8313, "following_count": 470, "verifications": [ "0x5a927ac639636e534b678e81768ca19e2c6280b7", "0xe9e261852ea62150eee685807df8fe3f211310a0" ], "active_status": "active" }, "text": "wtf", "timestamp": "2023-11-17T00:56:01.000Z", "embeds": [], "reactions": { "likes": [ { "fid": 1600, "fname": "yashkarthik" }, { "fid": 576, "fname": "nonlinear.eth" }, { "fid": 6546, "fname": "artlu.eth" } ], "recasts": [] }, "replies": { "count": 1 }, "mentioned_profiles": [] }, "reactions": [ { "object": "likes", "cast": { "object": "cast_dehydrated", "hash": "0x1aa36542c2dc8799dded2cde37a3c1a3840eb41f" }, "user": { "object": "user", "fid": 1600, "username": "yashkarthik", "display_name": "Yash Karthik", "pfp_url": "https://lh3.googleusercontent.com/W_MM3NN-i9OYxM3XPJjwpb5mkMLjJFZJjnEXsvrhiTwirSFhRjyAa3qTzV63ago6NkX9qeesi20hoK9fHdhiE-SqICH0vPcTm3Dl", "profile": { "bio": { "text": "I like building cool stuff on top of Ethereum, Farcaster and GPT (dm for collab)\n\nCurrently learning C and exploring low level stuff\n\nnf.td/yashkarthik" } }, "follower_count": 585, "following_count": 336, "verifications": [ "0x33cc45d8b0336bfa830fb512b54b02a049277403" ], "active_status": "active" } }, ] } ], "next": { "cursor": "eyJ0aW1lc3RhbXAiOiIyMDIzLTExLTE3IDAxOjIyOjQzLjAwMDAwMDAifQ==" } } ``` ### Summary You can use this to show a notification on your client! **You get all the data in one request, making it easy to display or use in your next operation.** If you want to try getting a live response, head over to our API page for [v2/feed](/reference/fetch-user-following-feed). You can take the cursor from the output and pass it in the next request to the SDK to page to the next set of results. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # User Balances Directly with FID Source: https://docs.neynar.com/docs/how-to-fetch-user-balance-using-farcaster-fid This guide provides a step-by-step approach to fetching token balances for a user using their Farcaster FID via the Neynar API. > [!NOTE] > > ### Related API: [Token balance](/reference/fetch-user-balance) ## Fetching User Balances Using Farcaster FID with Neynar API This API abstracts the complexity of finding Ethereum addresses and querying multiple providers, allowing developers to retrieve balances with a single API call. ### Overview * **API Endpoint**: `/farcaster/user/balance` * **Method**: `GET` * **Parameters**: * `fid` (required): The Farcaster FID of the user. * `networks` (required): A comma-separated list of networks to fetch balances for. Currently, only "base" is supported. ### Prerequisites * **API Key**: Ensure you have a Neynar API key. You can obtain one by signing up at [neynar.com](https://neynar.com). * **Node.js SDK**: Install the Neynar Node.js SDK. ```bash Bash theme={"system"} npm install @neynar/nodejs-sdk ``` ### Fetching User Balances **1. Initialize the Neynar Client** First, set up the Neynar client using your API key. ```javascript javascript theme={"system"} import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` **2. Fetch User Balances** Use the `fetchUserBalance` method to retrieve the token balances for a user by their FID. ```javascript javascript theme={"system"} async function fetchUserBalances(fid) { try { const response = await client.fetchUserBalance({ fid: fid, networks: ['base'], // Currently, only 'base' is supported }); console.log("User Balances:", response.user_balance); } catch (error) { console.error("Error fetching user balances:", error); } } // Example usage fetchUserBalances(3); // Replace '3' with the actual FID ``` #### Response Structure The response will include the user's balance information structured as follows: ```json json theme={"system"} { "user_balance": { "object": "user_balance", "user": { "fid": 3, "username": "example_user", // Additional user details }, "address_balances": [ { "object": "address_balance", "verified_address": { "address": "0x1234567890abcdef", "network": "base" }, "token_balances": [ { "object": "token_balance", "token": { "object": "token", "name": "Ethereum", "symbol": "ETH", "decimals": 18 }, "balance": { "in_token": "1.2345", "in_usdc": "1234.56" } } // Additional tokens ] } // Additional addresses ] } } ``` ### Error Handling Ensure to handle potential errors, such as invalid FID or network issues, by wrapping your API calls in try-catch blocks. ### Conclusion By following this guide, you can efficiently fetch token balances for a user using their Farcaster FID with the Neynar API. This streamlined process eliminates the need for multiple API calls and simplifies the integration into your application. > [!NOTE] > > ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com/) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # How to Get Cast Information for Farcaster Casts Source: https://docs.neynar.com/docs/how-to-get-cast-information-for-farcaster-casts To get all information about a cast, traditionally, you have to run a hub or get access to a hosted indexer. > [!NOTE] > > ### API endpoint > > This tutorial uses the [GET v2/cast](/reference/fetch-embedded-url-metadata) endpoint You have to fetch the raw cast data, mentioned profiles, reactions, profile of the author, etc. and stitch it back into the final cast object before you can render it or use the data in a subsequent operation. Thankfully, Neynar's SDK and APIs make this much simpler! ![cast information](https://mintcdn.com/neynar/V7Un5QUQSGJFAZfS/images/docs/ea58d37-image.png?fit=max\&auto=format\&n=V7Un5QUQSGJFAZfS\&q=85\&s=e1c923dfe653abf7ae555f9f28b198f7) ## Fetch cast information easily with Neynar SDK #### Ensure you have the right environment setup Make sure to have node.js, typescript (ts) and yarn installed on your machine * Install [node.js and npm](https://nodejs.org/en/download/) * Install [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) * Install [ts-node](https://www.npmjs.com/package/ts-node#installation) globally #### Create a new typescript file with the right imports ```typescript index.ts theme={"system"} import { NeynarAPIClient, isApiErrorResponse } from "@neynar/nodejs-sdk"; import { AxiosError } from "axios"; import { CastParamType, } from "@neynar/nodejs-sdk/build/neynar-api/neynar-v2-api"; ``` #### Initialize the client ```typescript index.ts theme={"system"} const client = new NeynarAPIClient("API_KEY"); ``` #### Call `lookUpCastByHashOrUrl` function to get cast data based on a url ```typescript index.ts theme={"system"} (async () => { try { const cast = await client.lookUpCastByHashOrUrl( 'https://warpcast.com/rish/0x9288c1', CastParamType.Url ); console.log(JSON.stringify(cast)); } catch (error) { // isApiErrorResponse can be used to check for Neynar API errors // handle errors accordingly if (isApiErrorResponse(error)) { console.log("API Error", error.response.data); } else { console.log("Generic Error", error); } } })(); ``` *You can change the `CastParamType.Url` to `CastParamType.Hash` use a hash (e.g. 0x123...abc) instead to fetch cast data if you so want.* #### Run your new .ts file * Navigate to the right folder in your terminal e.g. `cd ./test-sdk` * Run script by typing `yarn start` into the terminal #### You should now see an output like this ```json JSON theme={"system"} { "cast": { "hash":"0x9288c1e862aa72bd69d0e383a28b9a76b63cbdb4", "thread_hash":"0x9288c1e862aa72bd69d0e383a28b9a76b63cbdb4", "parent_hash":null, "parent_url":"chain://eip155:7777777/erc721:0x4f86113fc3e9783cf3ec9a552cbb566716a57628", "parent_author": { "fid":null }, "author": { "object":"user", "fid":194, "custody_address":"0xb43a7cc909d842721c288ff90b03e511a78a4a8d", "username":"rish", "display_name":"rish", "pfp_url":"https://res.cloudinary.com/merkle-manufactory/image/fetch/c_fill,f_png,w_256/https://lh3.googleusercontent.com/MEaRCAMdER6MKcvmlfN1-0fVxOGz6w98R8CrP_Rpzse9KZudgn95frTd0L0ZViWVklBj9fuAcJuM6tt7P-BRN0ouAR87NpzZeh2DGw", "profile": { "bio": { "text":"@neynar 🪐 | nf.td/rish", "mentioned_profiles":[] } }, "follower_count":8253, "following_count":468, "verifications": [ "0x5a927ac639636e534b678e81768ca19e2c6280b7", "0xe9e261852ea62150eee685807df8fe3f211310a0" ], "active_status":"active" }, "app": { "object": "user_dehydrated", "fid": 9152, "username": "warpcast", "display_name": "Warpcast", "pfp_url": "https://i.imgur.com/3d6fFAI.png" }, "text":".@fun built out hatecast.xyz based on a random conversation we had about app ideas for Farcaster.\n\nThere are many things we'd love to build at @neynar if we had the time but spoiler alert: we don't. So would love to see others build these! \nhttps://paragraph.xyz/@neynar/farcaster-ideas \n\n🪐", "timestamp":"2023-09-13T22:10:22.000Z", "embeds": [ { "url":"https://paragraph.xyz/@neynar/farcaster-ideas" } ], "reactions": { "likes": [ { "fid":1285, "fname":"0xbenersing.eth" }, { "fid":19315, "fname":"sonofsun" } ], "recasts": [ { "fid":19036, "fname":"geohookah" }, { "fid":13313, "fname":"008" } ] }, "replies": { "count":10 }, "mentioned_profiles": [ { "object":"user", "fid":5620, "custody_address":"0xbee82d68f49ee2aa8408f8111db64d92e4d61971", "username":"fun", "display_name":"welter.eth", "pfp_url":"https://i.imgur.com/LE5osyF.jpg", "profile": { "bio": { "text":"building apps like FarcasterUserStats.com", "mentioned_profiles":[] } }, "follower_count":1522, "following_count":207, "verifications": [ "0x02ac33835070d0c90bef87c273514e67aea36ef8" ], "active_status":"active" } ] } } ``` **You get all the data in one request, making it easy to display or use in your next operation.** If you want to try getting a live response, head over to our API page for [v2/cast](/reference/fetch-embedded-url-metadata). > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # How to fetch Farcaster feed for a given user Source: https://docs.neynar.com/docs/how-to-get-cast-information-for-farcaster-casts-copy-1 Fetch the casts that should appear on the home feed for a certain fid > [!NOTE] > > ### API endpoint > > This tutorial uses the [GET v2/feed](/reference/fetch-user-following-feed) endpoint To get the feed for a farcaster user, you traditionally, you have to run a hub or get access to a hosted indexer. You have to fetch the graph of the user, the raw cast data from that graph, mentioned profiles, reactions, profile of the authors, (long list of things...), and stitch it back into the final feed object before you can render it. Thankfully, Neynar's SDK and APIs make this much simpler! ![feed](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/007fd1a-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=8bfc18dc93af2e987d3ccbcc5b47fe11) ## Fetch feed for a user easily with Neynar SDK In this example, we will try to get rish's feed who's fid is 194. If you know someone's username, you can find their fid from our [user endpoint](/reference/search-user). #### Ensure you have the right environment setup Make sure to have node.js, typescript (ts) and yarn installed on your machine * Install [node.js and npm](https://nodejs.org/en/download/) * Install [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) * Install [ts-node](https://www.npmjs.com/package/ts-node#installation) globally #### Create a new typescript file with the right imports ```js theme={"system"} import { NeynarAPIClient, isApiErrorResponse } from "@neynar/nodejs-sdk"; import { AxiosError } from "axios"; import { FeedType, } from "@neynar/nodejs-sdk/build/neynar-api/neynar-v2-api"; ``` #### Initialize the client ```js theme={"system"} const client = new NeynarAPIClient("API_KEY"); ``` #### Call `fetchFeedPage` function to get feed ```js theme={"system"} (async () => { try { const cast = await client.fetchFeedPage(FeedType.Following, { fid: 194, limit: 2, // change to however many casts you want to fetch at once (max 100) withRecasts: true, }); console.log(JSON.stringify(cast)); } catch (error) { // isApiErrorResponse can be used to check for Neynar API errors // handle errors accordingly if (isApiErrorResponse(error)) { console.log("API Error", error.response.data); } else { console.log("Generic Error", error); } } })(); ``` #### Run your new .ts file * Navigate to the right folder in your terminal e.g. `cd ./test-sdk` * Run script by typing `yarn start` into the terminal #### You should now see an output like this ```json theme={"system"} { "casts": [ { "hash": "0x0d46810395803ff75221c5deac1d872db22aac99", "thread_hash": "0x0d46810395803ff75221c5deac1d872db22aac99", "parent_hash": null, "parent_url": null, "parent_author": { "fid": null }, "author": { "object": "user", "fid": 1287, "custody_address": "0x19c29a3cea8733314e501d42f1a0d6dcb23e1b3d", "username": "july", "display_name": "July", "pfp_url": "https://i.seadn.io/gcs/files/ed56e6b9a1b22720ce7490524db333e0.jpg?w=500&auto=format", "profile": { "bio": { "text": "mostly creating and destroying; used to build flying cars & autonomous vehicles, now working on @faust", "mentioned_profiles": [] } }, "follower_count": 12787, "following_count": 879, "verifications": [ "0xdd3bf199e65bba74144a9a1c0dfaeda32b911121" ], "active_status": "active" }, "app": { "object": "user_dehydrated", "fid": 9152, "username": "warpcast", "display_name": "Warpcast", "pfp_url": "https://i.imgur.com/3d6fFAI.png" }, "text": "Constantly reminded that doing anything, is really difficult. Whether it is building a product, launching a service, hosting a party, parenting, supporting parents, finding like minded friends, thinking deeply about problems - and solving them", "timestamp": "2023-11-16T23:26:03.000Z", "embeds": [], "reactions": { "likes": [ { "fid": 557, "fname": "pugson" }, { "fid": 2745, "fname": "sdv" }, { "fid": 4215, "fname": "sa" }, ], "recasts": [ { "fid": 557, "fname": "pugson" }, { "fid": 193173, "fname": "winnykim" }, ] }, "replies": { "count": 3 }, "mentioned_profiles": [] }, ], "next": { "cursor": "eyJ0aW1lc3RhbXAiOiIyMDIzLTExLTE3IDAxOjIyOjI1LjAwMDAwMDAifQ==" } } ``` **You get all the data in one request, making it easy to display or use in your next operation.** If you want to try getting a live response, head over to our API page for [v2/feed](/reference/fetch-user-following-feed). You can take the cursor from the output and pass it in the next request to the SDK to page to the next set of results. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Farcaster URLs Source: https://docs.neynar.com/docs/how-to-get-cast-information-from-url Convert URL into full cast data from Farcaster network through Neynar > [!NOTE] > > ### Related API: [Lookup cast by hash or URL](/reference/lookup-cast-by-hash-or-url) Cast url doesn't contain all the full cast hash value, it usually looks like this: `https://farcaster.xyz/dwr.eth/0x029f7cce`. This guide demonstrates how to fetch cast information from cast url. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Then fetch the cast: ```javascript Javascript theme={"system"} // @dwr.eth AMA with @balajis.eth on Farcaster const url = "https://farcaster.xyz/dwr.eth/0x029f7cce"; const cast = await client.lookupCastByHashOrUrl({ identifier: url, type: CastParamType.Url, }); console.log(cast); ``` Example output: ```json theme={"system"} { cast: { object: "cast_hydrated", hash: "0x029f7cceef2f0078f34949d6e339070fc6eb47b4", thread_hash: "0x029f7cceef2f0078f34949d6e339070fc6eb47b4", parent_hash: null, parent_url: "https://thenetworkstate.com", parent_author: { fid: null }, author: { object: "user", fid: 3, custody_address: "0x6b0bda3f2ffed5efc83fa8c024acff1dd45793f1", username: "dwr.eth", display_name: "Dan Romero", pfp_url: "https://res.cloudinary.com/merkle-manufactory/image/fetch/c_fill,f_png,w_256/https://lh3.googleusercontent.com/MyUBL0xHzMeBu7DXQAqv0bM9y6s4i4qjnhcXz5fxZKS3gwWgtamxxmxzCJX7m2cuYeGalyseCA2Y6OBKDMR06TWg2uwknnhdkDA1AA", profile: [Object ...], follower_count: 19381, following_count: 2703, verifications: [ "0xd7029bdea1c17493893aafe29aad69ef892b8ff2", "0xa14b4c95b5247199d74c5578531b4887ca5e4909", "0xb877f7bb52d28f06e60f557c00a56225124b357f", "0x8fc5d6afe572fefc4ec153587b63ce543f6fa2ea" ], active_status: "active" }, text: "Welcome to @balajis.eth!\n\nHe’s kindly agreed to do an AMA. Reply with your questions. :)", timestamp: "2023-11-28T14:44:32.000Z", embeds: [], reactions: { likes: [ [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...] ], recasts: [ [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...], [Object ...] ] }, replies: { count: 180 }, mentioned_profiles: [ [Object ...] ] } } ``` Obviously, you can also fetch cast by hash: ```javascript Javascript theme={"system"} // full hash of the warpcast.com/dwr.eth/0x029f7cce const hash = "0x029f7cceef2f0078f34949d6e339070fc6eb47b4"; const cast = (await client.lookUpCastByHashOrUrl({ identifier: hash, type: CastParamType.Hash, })); console.log(cast); ``` Which will return the same result as above. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # How to get relevant mints of Farcaster user Source: https://docs.neynar.com/docs/how-to-get-relevant-mints-of-farcaster-user This guide demonstrates how to use the Neynar SDK to fetch all mint actions relevant to a contract address (and optionally tokenId for ERC1155s) given a user's Ethereum address. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { NeynarAPIClient } from "@neynar/nodejs-sdk"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const client = new NeynarAPIClient(process.env.NEYNAR_API_KEY); ``` First we fetch the custody address for a user: ```javascript Javascript theme={"system"} const FID = 3; const response = await client.lookupCustodyAddressForUser(FID); const custodyAddress = response.result.custodyAddress; ``` Then we fetch the relevant mints for the user: ```javascript Javascript theme={"system"} // Farcats contract address const contractAddress = "0x9340204616750cb61e56437befc95172c6ff6606"; const relevantMints = await client.fetchRelevantMints( custodyAddress, contractAddress ); console.log(relevantMints); ``` Example output: ```json theme={"system"} { "mints": [ { "minter": { "object": "user", "fid": 129, "custody_address": "0xf6fd7deec77d7b1061435585df1d7fdfd4682577", "username": "phil", "display_name": "phil", "pfp_url": "https://i.imgur.com/sx6qqM7.jpg", "profile": { "bio": { "text": "Building @brightmoments - an IRL NFT gallery DAO. https://brightmoments.io | @purple #15", "mentioned_profiles": [] } }, "follower_count": 14867, "following_count": 1146, "verifications": [ "0x18b7511938fbe2ee08adf3d4a24edb00a5c9b783", "0x925afeb19355e289ed1346ede709633ca8788b25" ], "active_status": "active" }, "tx_hash": "0x2ed2ebb4b048677133eda3a8609bae694d2ba730c7fa2082691f508e711b6075", "block_number": 17610874, "contract_address": "0x9340204616750cb61e56437befc95172c6ff6606", "token_id": "353" }, // other mints ] } ``` It's that easy to get relevant mints for a Farcaster user! > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Trending Feed on Farcaster - Neynar API Source: https://docs.neynar.com/docs/how-to-get-trending-casts-on-farcaster Complete tutorial on fetching trending casts and popular content from the Farcaster network using Neynar SDK. Learn to implement trending feeds, discover viral content, and build engaging social media experiences with real-time trending algorithms and content discovery. > [!NOTE] > > ### Related API reference [Fetch Trending Feed](/reference/fetch-trending-feed) ## How to Get Trending Casts This guide demonstrates how to use the Neynar SDK to get trending casts on Farcaster. Check out this [Getting started guide](/docs/getting-started-with-neynar) to learn how to set up your environment and get an API key. First, initialize the client: ```javascript Javascript theme={"system"} // npm i @neynar/nodejs-sdk import { Configuration, NeynarAPIClient } from "@neynar/nodejs-sdk"; import { FeedType, FilterType } from "@neynar/nodejs-sdk/build/api"; // make sure to set your NEYNAR_API_KEY .env // don't have an API key yet? get one at neynar.com const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Then fetch the global trending Farcaster casts: ```javascript Javascript theme={"system"} const feed = await client.fetchFeed({ feedType: FeedType.Filter, filterType: FilterType.GlobalTrending, limit: 1, }) console.log(feed); ``` Example output: ```json theme={"system"} { "casts": [ { "object": "cast", "hash": "0x40b187be167c0134bc99c7e131aedd1da591f3fc", "author": { "object": "user", "fid": 15983, "username": "jacek", "display_name": "Jacek.degen.eth 🎩", "pfp_url": "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/85b47d38-7b53-46b0-7e6a-80ec1b9b3d00/original", "custody_address": "0x4ae49f0aa762efebebff4bac4ea0847eb6af4ec9", "profile": { "bio": { "text": "Lead $DEGEN | https://www.degen.tips/" } }, "follower_count": 77350, "following_count": 983, "verifications": [ "0x495d4d2203be7775d22ee8f84017544331300d09", "0xf1e7dbedd9e06447e2f99b1310c09287b734addc", "0x011c9a600fa4dcc460f9864e9c8b5498c2835e5a" ], "verified_addresses": { "eth_addresses": [ "0x495d4d2203be7775d22ee8f84017544331300d09", "0xf1e7dbedd9e06447e2f99b1310c09287b734addc", "0x011c9a600fa4dcc460f9864e9c8b5498c2835e5a" ], "sol_addresses": [] }, "verified_accounts": [ { "platform": "x", "username": "degentokenbase" } ], "power_badge": true }, "thread_hash": "0x40b187be167c0134bc99c7e131aedd1da591f3fc", "parent_hash": null, "parent_url": "chain://eip155:7777777/erc721:0x5d6a07d07354f8793d1ca06280c4adf04767ad7e", "root_parent_url": "chain://eip155:7777777/erc721:0x5d6a07d07354f8793d1ca06280c4adf04767ad7e", "parent_author": { "fid": null }, "text": "Daily Discussion Thread - /degen - February 12, 2025", "timestamp": "2025-02-12T11:00:28.000Z", "embeds": [ { "url": "https://supercast.mypinata.cloud/ipfs/Qmd6kGygZGMvgXikYvMDZ6eBQAzFLYkx8CxAgkzq8wZrXT?filename=degen_is_the_ticker.jpg", "metadata": { "content_type": "image/jpeg", "content_length": 218324, "_status": "RESOLVED", "image": { "width_px": 720, "height_px": 722 } } } ], "channel": { "object": "channel_dehydrated", "id": "degen", "name": "Degen", "image_url": "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/4728a50a-1669-4334-1f57-9473c04a2500/original" }, "reactions": { "likes_count": 47, "recasts_count": 6, "likes": [ { "fid": 406308, "fname": "huncho.eth" }, { "fid": 545237, "fname": "robert-ryce" }, { "fid": 345765, "fname": "adexmakai.eth" }, { "fid": 562503, "fname": "araizkyani" }, { "fid": 430462, "fname": "yaza69759996" } ], "recasts": [ { "fid": 279606, "fname": "itsfarahnaz.eth" }, { "fid": 477126, "fname": "mikadoe.eth" }, { "fid": 510796, "fname": "drrrner" }, { "fid": 440352, "fname": "thegoldenbright" }, { "fid": 526510, "fname": "mariabazooka" } ] }, "replies": { "count": 17 }, "mentioned_profiles": [], "author_channel_context": { "role": "moderator", "following": true } } ], "next": { "cursor": "eyJ0aW1lc3RhbXAiOiIyMDI1LTAyLTEyIDExOjAwOjI4LjAwMDAwMDAiLCJwb2ludHMiOjAuNDg1NTY2NX0%3D" } } ``` To fetch the next page of the feed, use the cursor: ```javascript Javascript theme={"system"} const nextFeed = await client.fetchFeed({ feedType: FeedType.Filter, filterType: FilterType.GlobalTrending, limit: 1, cursor: feed.next.cursor, }) ``` It's that easy to get trending casts in Farcaster! > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # Frame Interactions Source: https://docs.neynar.com/docs/how-to-handle-frame-interactions-with-the-neynar-api In this guide, we'll go over how to use our `POST frame/action` API to handle frame interactions on your backend. If you're looking for a guide on handling frame interactions on the client side with our `@neynar/react` SDK, [see our Wownar React SDK examples](https://github.com/neynarxyz/farcaster-examples/tree/main/wownar-react-sdk#how-to-securely-implement-write-actions). > [!NOTE] > > Before beginning, ensure that your backend has your Neynar API Key securely managed so you have safe access set up to call our API. ## Calling the `POST frame/action` API This API route takes three inputs: * `signer_uuid`: the Neynar Signer UUID for the user who is taking the frame action, which would be retrieved through using [Sign In with Neynar](/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn) * **required value** * `cast_hash`: the hash of the cast from which the frame action is taking place * optional value, defaults to `0xfe90f9de682273e05b201629ad2338bdcd89b6be` * `action`: the object of the frame being interacted with, including the interacted with button/action, which can normally be retrieved from the Neynar APIs * **required value** Here's an example of what your POST request might look like: ```Text cURL theme={"system"} curl --request POST \ --url https://api.neynar.com/v2/farcaster/frame/action \ --header 'accept: application/json' \ --header 'api_key: "'"neynarAPIKey"'"\ --header 'content-type: application/json' \ --data '{ "signer_uuid": "'"$signerValue"'", "castHash": "'"$castHash"'", "action": { "button": "'"$button"'", "frames_url": "'"$localFrame.frames_url"'", "post_url": "'"${postUrl:-$localFrame.frames_url}"'", "input": { "text": "'"$inputValue"'" } } }' ``` ## Handling client-side interactions There are a few frame actions that take place fully on the client-side and do not/cannot be sent to the `POST frame/action` API. Those actions are: * `post` and `redirect`: these actions should be handled on the client either as a `window.replace` or as a link to a new tab * `mint`and `tx`: the mint/transaction data itself should also be handled on the client, as the data from the frame object should have enough information to use in a package such as `wagmi` or `viem`. Here's an [example from our wownar-react-sdk repo](https://github.com/neynarxyz/farcaster-examples/blob/fb1546e053d73a103b82ab215c63f81fac4fb3fb/wownar-react-sdk/src/app/Screens/Home/index.tsx#L271). ## Conclusion This guide went over how to handle frame interactions on your backend with the Neynar `POST frame/action` API. Make sure to share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar), and if you have any questions, reach out to us on [Warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # How to Ingest Source: https://docs.neynar.com/docs/how-to-ingest Simple guide on how to ingest parquet files for Farcaster data > [!NOTE] > > ### Ingestion code available in this [github repo](https://github.com/neynarxyz/neynar_parquet_importer), clone repo onto a server with a large disk and you should be importing in no time > > Reach out to us for credentials to try it out. 1. Install Homebrew: ```bash cURL theme={"system"} /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` 1. Install amazon’s command line tool: ```bash cURL theme={"system"} brew install awscli parquet-cli ``` 1. Configure amazon’s command line tool: ```bash cURL theme={"system"} aws configure --profile neynar_parquet_exports ``` ```bash cURL theme={"system"} AWS Access Key ID [None]: the username from your 1Password entry AWS Secret Access Key [None]: the password from your 1Password entry Default region name [None]: us-east-1 Default output format [None]: json ``` 1. Set this new profile to be the default (or you can use `--profile ...` on all of your `aws` commands): ```bash cURL theme={"system"} export AWS_PROFILE=neynar_parquet_exports ``` 1. List all the archive exports (replace `` with the S3 prefix shared with you when your export was provisioned): ```bash cURL theme={"system"} aws s3 ls s3://tf-premium-parquet//full/ ``` You’ll see some output that will look something like this (the timestamps will likely be different): ```bash cURL theme={"system"} 2024-03-28 16:20:05 0 2024-03-29 14:34:06 1877462159 farcaster-casts-0-1711678800.parquet 2024-03-29 14:39:11 21672633 farcaster-fids-0-1711678800.parquet 2024-03-29 14:40:07 15824832 farcaster-fnames-0-1711678800.parquet 2024-03-29 14:50:44 2823873376 farcaster-links-0-1711678800.parquet 2024-03-29 14:35:42 2851749377 farcaster-reactions-0-1711678800.parquet 2024-03-29 14:35:54 22202796 farcaster-signers-0-1711678800.parquet 2024-03-29 14:35:55 12937057 farcaster-storage-0-1711678800.parquet 2024-03-29 14:35:57 67192450 farcaster-user_data-0-1711678800.parquet 2024-03-29 14:35:59 72782965 farcaster-verifications-0-1711678800.parquet ``` The filename format is `${DATABASE}-${TABLE}-${START_TIME}-${END_TIME}.parquet`. The timestamps bound the `updated_at` column. You probably want to fetch the latest versions of each table the first time you build your database. 1. List all the incremental exports: ```bash cURL theme={"system"} aws s3 ls s3://tf-premium-parquet//incremental/ ``` ```bash cURL theme={"system"} 2024-03-28 16:20:05 0 2024-04-09 11:14:29 1011988 farcaster-casts-1712685900-1712686200.parquet 2024-04-09 11:14:25 200515 farcaster-fids-1712685900-1712686200.parquet 2024-04-09 11:14:25 231552 farcaster-fnames-1712685900-1712686200.parquet 2024-04-09 11:14:30 827338 farcaster-links-1712685900-1712686200.parquet 2024-03-29 14:35:42 51749377 farcaster-reactions-1712685900-1712686200.parquet 2024-04-09 11:14:26 8778 farcaster-signers-1712685900-1712686200.parquet 2024-04-09 11:14:26 6960 farcaster-storage-1712685900-1712686200.parquet 2024-04-09 11:14:30 1012332 farcaster-user_data-1712685900-1712686200.parquet 2024-04-09 11:14:30 10909 farcaster-verifications-1712685900-1712686200.parquet ``` 1. List all the files for a specific time range: ```bash cURL theme={"system"} aws s3 ls s3://tf-premium-parquet//incremental/ | grep "\-1712685900\-1712686200" ``` 1. Download a specific file: ```bash cURL theme={"system"} aws s3 cp \ s3://tf-premium-parquet//incremental/farcaster-fids-1712685900-1712686200.parquet \ ~/Downloads/farcaster-fids-1712685900-1712686200.parquet ``` 1. Download all the tables for a specific time range: ```bash cURL theme={"system"} aws s3 cp s3://tf-premium-parquet//incremental/ ~/Downloads/ \ --recursive \ --exclude "*" \ --include "*-1712685900-1712686200.parquet" ``` 1. Use the parquet cli: ```bash cURL theme={"system"} parquet --help ``` 1. Check some data: ```bash cURL theme={"system"} parquet head ~/Downloads/farcaster-fids-0-1711678800.parquet ``` ```bash cURL theme={"system"} {"created_at": 1711832371491883, "updated_at": 1713814200213000, "custody_address": "F\u009Aè\u0091¾Vc\u0094Ô\u008Aô\u009F\ní\u0017\u0090\u009Bd\u0093«", "fid": 421819} {"created_at": 1711832359411772, "updated_at": 1713814200246000, "custody_address": "\u0098ªÜvÌí½Í\fiî\\\u00919\u0011S\u001Ba\u0099\u009E", "fid": 421818} {"created_at": 1711832371493221, "updated_at": 1713814200271000, "custody_address": "=Ï\u0099fÅ\u0084\u007FLð\b\"u\u0005\u0093\u000B\u000B\u0099µ}ã", "fid": 421820} {"created_at": 1711832391626517, "updated_at": 1713814200357000, "custody_address": "\u0014é\u0089PO©ÉþÓòM\u0083Ü.\u0016H\u008CMef", "fid": 421821} {"created_at": 1711832399774843, "updated_at": 1713814200426000, "custody_address": "o^MoÎÔÎÄêMjwÌÒlïXC\u0096°", "fid": 421822} {"created_at": 1711832399778591, "updated_at": 1713814200463000, "custody_address": "­D¼ãñå\u0080ÿi\u0092Z­Ì\u0093¢´\u001E¡¦$", "fid": 421823} {"created_at": 1711832431907945, "updated_at": 1713814200502000, "custody_address": "\u0015\u0091þ!1c\n\u008E\u0092>V\u0006ä!\u0014E\"\u0017ÄÐ", "fid": 421824} {"created_at": 1711832431907986, "updated_at": 1713814200608000, "custody_address": "óic\u0006!p\u0004Ý\u0005e\u001CÙ½1\u009CU¤\u0091*2", "fid": 421825} {"created_at": 1711832456106275, "updated_at": 1713814200903000, "custody_address": "\u00186ê¨ Âé·Ì-\u0092\u0092t¨\u0006a\u0099`\u0005\u0084", "fid": 421826} {"created_at": 1711832480265145, "updated_at": 1713814201318000, "custody_address": "(SÞ\u008EÏ\u009Cbû4ÛÙn\u0014+?èÑb\u0089¡", "fid": 421827} ``` # How to integrate Neynar webhooks for real-time events Source: https://docs.neynar.com/docs/how-to-integrate-neynar-webhooks-for-real-time-events Get real-time events for Farcaster data Incoming webhooks allow you to receive real-time notifications about Farcaster events. Consider the following use cases. 1. watch a channel - get events for any new casts on a specific channel 2. watch a user - get events for any new casts from a specific user 3. watch a mention - get events when a specific user gets @ mentioned in a cast You’ll need to configure 2 things before you can start receiving Farcaster events via webhooks. > [!TIP] > > It’s not possible to use webhooks on frontend-only apps ### 1. Create a webhook Create a REST API route with the POST method on your backend to handle the webhook events. It should be able to handle incoming data with `Content-Type` set to `application/json` format. To test that your webhook is set up correctly, try this curl ```curl cURL theme={"system"} curl -X POST \ -d '{"event" : "test"}' \ -H 'Content-Type: application/json' \ https://api.your-farcaster-app.com/webhooks/neynar ``` ### 2. Register the webhook details on the Neynar Dev Portal Set this outgoing webhook URL on the Neynar developer portal. See [How to Use Neynar Webhooks](/docs/how-to-use-neynar-webhooks) for how. `https://api.your-farcaster-app.com/webhooks/neynar` A webhook should be configured with at least 1 valid event subscription. Each webhook can support multiple event types and/or multiple subscriptions for the same type. Some subscriptions require one or more parameters to be configured before you can start receiving the relevant events. Event types supported 1. `cast.created` Subscriptions supported 1. Authors - Get events for new casts & replies by a specific user 2. Root Parent Url - Get events for new casts & replies in a specific channel 3. Parent Url - Get events for new casts (but not replies) in a specific channel 4. Mentions - Get events when a specific user gets @ mentioned in a cast 2. `user.updated` Subscriptions supported 1. User - Get profile update events for specific users You’re all set! You’ll start receiving events on your backend. Contact us at [support@ns.com](mailto:support@ns.com). # SIWN: Connect Farcaster accounts - Neynar API Source: https://docs.neynar.com/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn Learn how to implement Sign In With Neynar (SIWN) for seamless Farcaster authentication and authorization with read/write permissions. > [!WARNING] > > ### Sign In With Neynar is deprecated > > SIWN is being retired. New integrations should use [Neynar-managed signers](/docs/integrate-managed-signers) instead. Existing connections will keep working; new SIWN connections will stop being issued after **Friday, August 14, 2026**. ## What is SIWN? SIWN enables seamless authentication + authorization for Farcaster clients that need read and write permissions on behalf of their users. * Users don’t need to pay for warps to try apps * Developers don’t need to worry about onboarding funnel drop-offs when OP mainnet gas surges > [!NOTE] > > * **If building a mini app**, use [App auth](/docs/mini-app-authentication) instead > * If you want to design your own frontend, use [Neynar Managed Signers](/docs/integrate-managed-signers) ## How to integrate SIWN? > [!TIP] > > ### Example integration > > Check out this sample application ([github](https://github.com/neynarxyz/farcaster-examples/tree/main/wownar)) that integrates Sign in with Neynar and allows users to cast. A live demo of this exact code has been deployed at [https://demo.neynar.com](https://demo.neynar.com) ### Step 0: Set up your app in the Neynar developer portal Go to the [Neynar Developer Portal](https://dev.neynar.com) settings tab and update the following 1. **Name -** Displayed to the user in Step 3. 2. **Logo URL** - Displayed to the user in Step 3. Use a PNG or SVG format. 3. **Authorized origins** - Authorized origins are the HTTP origins that host your web application. e.g. `https://demo.neynar.com` This is required to pass user credentials back to your application securely. This cannot contain wildcards or IP addresses. 4. **Permissions** - Generated signers will have these permissions (Read only and Read and write). Atleast one permission is needed. **Defaults to -- Read and write -- permission.** ### Step 1: Display the SIWN button on your app frontend ```html HTML theme={"system"} ``` > [!NOTE] > > ### Example above is for web. See \[React > > Implementation]\(/docs/react-implementation) for react and [React Native > Implementation](/docs/sign-in-with-neynar-react-native-implementation) for > react native. > [!TIP] > > Want to customize the button to your liking? See [How to customize Sign In > with Neynar button in your > app](/docs/how-to-customize-sign-in-with-neynar-button-in-your-app) ### Step 2: Fill in `data-client_id` in the button code Find this value in [Neynar Developer Portal](https://dev.neynar.com), Settings tab. e.g. `00b75745-xxxx-xxxx-xxxx-xxxxxxxxxxxx` ### Step 3: Handle callback Once the user is authenticated and a signer has been authorized by the user, the `signer_uuid` and `fid` will be passed in via the `data` object in the callback function. * `signer_uuid` is unique to your app and is used to write to Farcaster on behalf of the user (same uuid format) * `fid`: This is the unique Farcaster identifier for the user e.g. `6131` * `user`: Neynar hydrated user object. Store the `signer_uuid` securely on your backend or the browser’s local storage, it's not meant to be exposed to the user or anyone other than you. Switch the app to the logged-in state for that Farcaster user. Handle insufficient permissions for the API calls except for`statusCode: 403`, `errorResponse.code: InsufficientPermission` ### That’s it! **You’re all set!** The user is now logged in and you should use the `fid` for any [read APIs](/docs/what-does-vitalikeths-farcaster-feed-look-like) and the `signer_uuid` to do any [write actions](/docs/liking-and-recasting-with-neynar-sdk) on behalf of the user in your App. You can try this flow yourself at **[demo.neynar.com](https://demo.neynar.com)** ## Appendix — more about the user journey ### 1. The user clicks the SIWN button, App redirects to Neynar auth flow * After the user clicks the SIWN button, the script opens a new popup window for user authentication with Neynar and listens for a message from this window ### 2. The user goes through Neynar’s sign-in flow * The user runs through the following steps on [https://app.neynar.com/login](https://app.neynar.com/login) * authentication (only needed if the user isn’t authenticated on app.neynar.com) * signer collection (only needed if Neynar doesn't have a signer for the user) -- For now signer is collected from the user for **Read only** permissions as well, future iterations will remove this step for **Read only** permissions -- * authorization (this is where the user approves the permissions and these permissions are assigned to user's signer) * No integration actions are needed from the app developer for this step ### 3. The user is routed back to the App, App collects user information * Once the user is authenticated, the script receives a message from the authentication window. * It then executes a callback function * In the onSignInSuccess function, the user will eventData in through params example # Neynar SQL Playground Source: https://docs.neynar.com/docs/how-to-query-neynar-sql-playground-for-farcaster-data Retired September 14, 2026. Historical reference for the hosted SQL playground, and schema notes that still apply to our other data products. > [!WARNING] > > ### Neynar SQL Playground has been retired > > The hosted SQL playground was shut down on **September 14, 2026** and is no longer available. For Farcaster data at scale, use one of our other [data products](/docs/how-to-choose-the-right-data-product-for-you) — [Parquet exports](/docs/parquet) or the [indexer service](/docs/indexer-service-pipe-farcaster-data). Reach out at [support@ns.com](mailto:support@ns.com) if you need help migrating. > > This page is kept for historical reference. The playground itself is gone, but the schema notes below still apply to Parquet exports and the indexer service, so they remain useful when porting old queries. ## What it was The playground was a hosted Redash instance pointed at a Postgres database of Farcaster data. It offered ad-hoc SQL queries in the browser, shareable dashboards, and SQL-over-API using a Redash API key separate from the read and write API keys. ![Neynar SQL Playground](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/7a06342-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=d18201589ca970ae61f759c16d644702) The schema was read out of the database directly with: ```sql SQL theme={"system"} SELECT table_name, column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position; ``` A snapshot of the schema as of Nov 21, 2024 is archived [here](https://neynar.notion.site/Public-postgres-schema-145655195a8b80fc969cc766fbcde86b?pvs=4). ## Porting old queries The table shapes below are not unique to the playground — the same `casts`, `links` and `reactions` tables ship in [Parquet exports](/docs/parquet) and through the [indexer service](/docs/indexer-service-pipe-farcaster-data). If you are moving queries off the playground, these notes still apply. Data is more raw than our APIs, and you will likely need to join tables. If you are building a client rather than doing analysis, prefer the [APIs](/reference/quickstart) over SQL. ### 1. **Follows** `links` table has follower \<> follow data: * `fid` → `target_fid` row means `fid` follows `target_fid` ### 2. Reactions * `reaction_type` 1 is “like” and 2 is “recast” in the `reactions` table * `hash` in the reactions table is the “reaction hash” and `target_hash` is the hash of the cast that received the reaction ### 3. hex \<> bytea The Redash UI converted *bytea* data to hex for display, but queries had to do the conversion themselves — as they still do against Parquet and the indexer service: * bytea to hex ```sql SQL theme={"system"} select ENCODE(hash, 'hex') as hash from casts limit 1 ``` * hex to bytea ```sql SQL theme={"system"} select * from casts where hash = DECODE('hex_hash_without_0x', 'hex') ``` (swap `hex_hash_without_0x` with the actual cast hash minus the \`0x) # Zapier workflows Source: https://docs.neynar.com/docs/how-to-set-up-zapier-workflows-with-neynar-webhooks Add Neynar to your Zap to trigger full workflows based on Farcaster events **Step 1** Create a Zapier account on [Zapier.com](https://zapier.com) if you don't have one already. **Step 2** Start creating a new zap and search for Neynar when setting up your trigger ![create zap](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/17e4a38-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=25cccf31a5f021f199dcd4ea7dc52c4c) **Step 3** Choose Neynar as trigger **Step 4** Then choose which Farcaster event you want to get notified for -- every new cast on the protocol or a mention of a specific user in a cast **Step 5** On the account step, insert your Neynar API key to connect your Neynar account with Zapier ![insert neynar api key](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/d088f88-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=ed26bca072a0cad22369208ddca56fa9) **Step 6** If mention of a specific user, enter the FID of the user you want to get webhook notifications for. You can find the FID of an account by looking at the "About" section of that account on Warpcast ![insert user fid](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/5922d21-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=324a0d53ab942a62df8d251407aeb880) ![test trigger](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/b737dd4-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=ceadf7c0672f8d81fd150e4c4a34a208) **Step 7** Test your trigger after setting the FID. If there is a recent cast on the network with the mention, it will show up. If not, make a cast and wait a few minutes before testing again. Proceed with a selected record as example. ![test trigger](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/466c73d-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=d349fd172d83ff35afa04d37684adcb6) **Step 8** Choose the action you want to take based on the webhook e.g. sending a message in Slack. Finish setting up the action on Zapier and you're done! ![choose action](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/da201e3-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=074080fc02347471ae3ea9bf15a30f1a) You should now be able to trigger Zapier workflows based on webhook data! # Webhooks in Dashboard Source: https://docs.neynar.com/docs/how-to-setup-webhooks-from-the-dashboard User Neynar dev portal to set up webhooks for your app Neynar webhooks are a way to receive real-time updates about events on the Farcaster protocol. You can use webhooks to build integrations that respond to events on the protocol, such as when a user creates a cast or when a user updates their profile. This guide will show you how to set up a webhook in the Neynar developer portal and how to integrate it into your application. To create a new webhook without writing any code, head to the neynar dashboard and go to the [webhooks tab](https://dev.neynar.com/webhook). Click on the new webhook and enter the details as such: ![](https://github.com/neynarxyz/farcaster-examples/assets/76690419/81b65ce0-5b3a-4856-b1e5-7f46c2c648cd) The webhook will fire to the specified `target_url`. To test it out, we are using a service like [ngrok](https://ngrok.com/) to create a public URL that will forward requests to your local server. However, we recommend using your own domain to avoid interruptions. > [!WARNING] > > ### Free endpoints like ngrok, localtunnel, etc. throttle webhook deliveries, best to use your own domain Let's create a simple server that logs out the event. We will be using [Bun JavaScript](https://bun.sh). ```javascript Javascript theme={"system"} const server = Bun.serve({ port: 3000, async fetch(req) { try { console.log(await req.json()); return new Response("gm!"); } catch (e: any) { return new Response(e.message, { status: 500 }); } }, }); console.log(`Listening on localhost:${server.port}`); ``` Next: run `bun serve index.ts`, and run ngrok with `ngrok http 3000`. Copy the ngrok URL and paste it into the "Target URL" field in the Neynar developer portal. The webhook will call the target URL every time the selected event occurs. Here, I've chosen all the casts created with farcasterframesbot present in the text. Now the server will log out the event when it is fired. It will look something like this: ```javascript Javascript theme={"system"} { created_at: 1708025006, type: "cast.created", data: { object: "cast", hash: "0xfe7908021a4c0d36d5f7359975f4bf6eb9fbd6f2", thread_hash: "0xfe7908021a4c0d36d5f7359975f4bf6eb9fbd6f2", parent_hash: null, parent_url: "chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61", root_parent_url: "chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61", parent_author: { fid: null, }, author: { object: "user", fid: 234506, custody_address: "0x3ee6076e78c6413c8a3e1f073db01f87b63923b0", username: "balzgolf", display_name: "Balzgolf", pfp_url: "https://i.imgur.com/U7ce6gU.jpg", profile: [Object ...], follower_count: 65, following_count: 110, verifications: [ "0x8c16c47095a003b726ce8deffc39ee9cb1b9f124" ], active_status: "inactive", }, text: "LFG", timestamp: "2024-02-15T19:23:22.000Z", embeds: [], reactions: { likes: [], recasts: [], }, replies: { count: 0, }, mentioned_profiles: [], }, } ``` ## Conclusion That's it, it's that simple! The next steps would be to have a public server that can handle the webhook events and use it to suit your needs. Lastly, make sure to sure what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar) and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or at [support@ns.com](mailto:support@ns.com)! # How to set up Neynar webhooks through your developer portal Source: https://docs.neynar.com/docs/how-to-use-neynar-webhooks Setting up Farcaster webhooks through Neynar developer portal Neynar webhooks are a way to receive real-time updates about events on the Farcaster protocol. You can use webhooks to build integrations that respond to events on the protocol, such as when a user creates a cast or when a user updates their profile. This guide will show you how to set up a webhook in the Neynar developer portal and how to integrate it into your application. First, log in to the [Neynar developer portal](https://dev.neynar.com/) and navigate to the "Webhooks" tab. Click the "Create Webhook" button to create a new webhook. ![create webhook](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/7984d14-image.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=917f8b6ff63d63f063f58540d03ce807) The webhook will fire to the specified `target_url`. To test it out, we can use a service like [ngrok](https://ngrok.com/) to create a public URL that will forward requests to your local server. Let's create a simple server that logs out the event. We will be using Bun JavaScript. ```javascript Javascript theme={"system"} Bun.serve({ async fetch(req) { console.log(await req.json()); return new Response("Neynar webhook!"); }, }); ``` Next: run `bun serve index.ts`, and run ngrok with `ngrok http 3000`. Copy the ngrok URL and paste it into the "Target URL" field in the Neynar developer portal. ![create webhook](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/5909a89-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=838c548cfdd7b7187d725ba0a615c379) Here, we setup a webhook that fires to that ngrok endpoint (which is hooked to our localhost:3000), and the webhook will fire when a cast is made in the Memes channel. Now the server will log out the event when it is fired. It will look something like this: ```javascript Javascript theme={"system"} { created_at: 1708025006, type: "cast.created", data: { object: "cast", hash: "0xfe7908021a4c0d36d5f7359975f4bf6eb9fbd6f2", thread_hash: "0xfe7908021a4c0d36d5f7359975f4bf6eb9fbd6f2", parent_hash: null, parent_url: "chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61", root_parent_url: "chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61", parent_author: { fid: null, }, author: { object: "user", fid: 234506, custody_address: "0x3ee6076e78c6413c8a3e1f073db01f87b63923b0", username: "balzgolf", display_name: "Balzgolf", pfp_url: "https://i.imgur.com/U7ce6gU.jpg", profile: [Object ...], follower_count: 65, following_count: 110, verifications: [ "0x8c16c47095a003b726ce8deffc39ee9cb1b9f124" ], active_status: "inactive", }, text: "LFG", timestamp: "2024-02-15T19:23:22.000Z", embeds: [], reactions: { likes: [], recasts: [], }, replies: { count: 0, }, mentioned_profiles: [], }, } ``` That's it, it's that simple! Next steps would be to have a public server that can handle the webhook events and use it to suit your needs. Note: Webhook events might be delayed at times of heavy load or when it's first created from the dev portal. > [!NOTE] > > ### Ready to start building? > > Get your subscription at [neynar.com](https://neynar.com) and reach out to us at [support@ns.com](mailto:support@ns.com) with any questions! # How to Use the Neynar Feed API Source: https://docs.neynar.com/docs/how-to-use-the-feed-api-1 A guide on how to get feed from the Feed API using fid, fids, and parent_url. > [!NOTE] > > This guide uses [this feed API](/reference/fetch-feed) There are three different ways you can use the Feed endpoint: 1. Getting the feed of a user by passing a `fid` field to the request 2. Getting the feed of multiple users by passing a `fids` field to the request 3. Getting the feed of a parent URL e.g. FIP-2 channels on Warpcast, by passing a `parent_url` field to the request ## Get feed by `fid` If you want to get the feed of a user using their `fid`, you'll need to pass it in using the `fid` field of your request. To try this request in the API Explorer to get an actual response from the API, follow these steps: * In the *Request* tab, ensure *Default* is selected as shown below ![](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/d93681c-fid-request.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=f0e00864dab4af95e7422bd34fb00bbd) * Add the fid of the user whose feed you want to get ![](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/bbc2c00-fid-explorer.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=d309d2361f939e952a270b8bf570b8fc) * Press the **Try it** button to see the response ## Get feed by `fids` You can get the feed for multiple users by passing an array of their fids in the `fids` field of your request. To do this, you'll need to set `filter_type=fids` in you request body. To try this request in the API Explorer to get an actual response from the API, follow these steps: * In the *Request* tab, change the request type to **Get feed using fids** ![](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/af5715e-fids.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=c2ce83901161379ac2e900560ed6bab1) * Set the query parameters to the following ![](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/bd0df9c-fids-explorer.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=c8edfd52a2bed245e2bdfd34df5e946b) * Press the **Try it** button to view the response ## Get feed by `parent_url` You can get the feed for multiple users by passing the parent URL in the `parent_url` field in your request. To do this, you'll need to set `feed_type=filter` and `filter_type=parent_url` in you request body. To try this request in the API Explorer to get an actual response from the API, follow these steps: * In the *Request* tab, change the request type to **Get feed using parent\_url** ![](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/08fa26d-parent-url.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=90185359f73975d2f5f0b1e1a920027b) * Set the query parameters in the explorer ![](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/400f794-p-url-explorer.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=6383bb3499ca876da5d427787e7c4a04) > [!TIP] > > You can use the following parent URL as an example value in the explorer: `chain://eip155:1/erc721:0xd4498134211baad5846ce70ce04e7c4da78931cc` * Press the **Try it** button to view the response ## Sample creations with this endpoint Fetch home feed for a user ![](https://mintcdn.com/neynar/4PNY113y9N9T-r9z/images/docs/9238be8-home-feed.png?fit=max\&auto=format\&n=4PNY113y9N9T-r9z\&q=85\&s=36ff2157735ea63d5456266168607073) Fetch channel feed: # Verify Webhooks with HMAC Signatures Source: https://docs.neynar.com/docs/how-to-verify-the-incoming-webhooks-using-signatures This guide highlights the steps to verify incoming webhooks using signatures Webhook signatures are strings used to verify the validity of an incoming webhook event. This signature is passed as header values in the format: `X-Neynar-Signature`. The validation is an important process to prevent exploitation and malicious webhook requests. ```Text JSON theme={"system"} { "Content-Type": "application/json", "X-Neynar-Signature": "6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39" } ``` ## Verification Process **1. Create a new signature string** Use an HMAC library of your choice to create a sha512 digest with the following: * Shared secret - Find this on the [Developer Portal](https://dev.neynar.com/webhook) * Encoding format - This is always `hex` * Request payload - The request body object of the webhook POST **2. Compare the signatures** Compare the signatures from Step 1 and the request header `X-Neynar-Signature` ## Example Here's an example of a Next.js API handler validating a signature from a request. ```typescript Typescript theme={"system"} import { NextRequest } from "next/server"; import { createHmac } from "crypto"; export async function POST(req: NextRequest) { const body = await req.text(); const sig = req.headers.get("X-Neynar-Signature"); if (!sig) { throw new Error("Neynar signature missing from request headers"); } const webhookSecret = process.env.NEYNAR_WEBHOOK_SECRET; if (!webhookSecret) { throw new Error("Make sure you set NEYNAR_WEBHOOK_SECRET in your .env file"); } const hmac = createHmac("sha512", webhookSecret); hmac.update(body); const generatedSignature = hmac.digest("hex"); const isValid = generatedSignature === sig; if (!isValid) { throw new Error("Invalid webhook signature"); } const hookData = JSON.parse(body); // your code continues here ... } ``` ## Appendix * Caveats and additional details can be found here: [Verification of simple signatures](https://docs.getconvoy.io/product-manual/signatures#simple-signatures) # How writes to Farcaster work with Neynar managed signers Source: https://docs.neynar.com/docs/how-writes-to-farcaster-work-with-neynar-managed-signers (incl. frontend) In this guide, we’ll take a look at how to integrate neynar managed signers with neynar in a next.js app *so your users can take actions on the Farcaster protocol.* Managed signers allow you to take full control of the connection, including the branding on Warpcast and everything else! > [!NOTE] > > ### If using login providers like Privy, it's best to use this signer product. Users won't see a double login and developers will get full write functionality to Farcaster. ## Context This guide covers setting up a full app with frontend and backend to start writing with a logged in user. You can pick and choose the right pieces from this guide as needed. We'll go over: 1. Creating a signer key for the user so they can sign in 2. Storing the user's credentials in local storage 3. Writing casts to Farcaster using the signer Before we begin, you can access the [complete source code](https://github.com/neynarxyz/farcaster-examples/tree/main/managed-signers) for this guide on GitHub. The simplest way to set up managed signers is by cloning the above repo! You can use the following commands to check it out: ```bash theme={"system"} npx degit https://github.com/neynarxyz/farcaster-examples/tree/main/managed-signers managed-signers cd managed-signers yarn yarn dev ``` ### The ideal user flow **Terminology** To understand the ideal user flow, let's quickly go over some terminology: * Authentication: This is where an account proves they are who they say they are. Flows like Sign in with Farcaster (SIWF) or login providers like Privy allow this for app logins. * Authorization: this is where an account gives the app certain access privileges to take actions on behalf of the account. This is what Neynar signers allow for writing data to the protocol. Authorization requires authentication so that once a user is authenticated, they can then *authorize* an action. E.g. authenticating into your Google account to then authorize a 3rd party app like Slack to access your Google Calendar. If starting logged out, the full flow takes two distinct steps. **User journey** You can build a user flow using tools like: * [SIWN: Connect Farcaster accounts](/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn) * or 3rd party login providers like Privy If using Privy * the 1st step on authentication happens on Privy login and the 2nd step of authorization happens on Neynar * The second step requires the user to scan a QR code or tap on a link to then generate a signer on a Farcaster client like Warpcast * Generating a signer requires paying onchain fees. Neynar [sponsors signers](/docs/two-ways-to-sponsor-a-farcaster-signer-via-neynar) by default so users pay \$0. **Let's go!** Now that we have context, let's get started! ## Setting up a next.js app ### Creating a next app We are going to need a frontend as well as a backend server, so we are going to use Next.js for this guide. Run the following command to create a new next.js app: ```powershell PowerShell theme={"system"} npx create-next-app managed-signers ``` Choose the configuration for your app and wait for the dependencies to install. Once they are installed, let's install the additional packages that we are going to need: ```powershell npm theme={"system"} npm i @farcaster/hub-nodejs @neynar/nodejs-sdk axios qrcode.react viem ``` ```powershell yarn theme={"system"} yarn add @farcaster/hub-nodejs @neynar/nodejs-sdk axios qrcode.react viem ``` ```powershell pnpm theme={"system"} pnpm add @farcaster/hub-nodejs @neynar/nodejs-sdk axios qrcode.react viem ``` ```powershell bun theme={"system"} bun add @farcaster/hub-nodejs @neynar/nodejs-sdk axios qrcode.react viem ``` Now, you can open the folder in your favourite code editor and we can start building! ### Configuring env variables Firstly, let's configure the env variables we will need. Create a new `.env.local` file and add these two variables: ```bash theme={"system"} NEYNAR_API_KEY= FARCASTER_DEVELOPER_MNEMONIC= ``` * The neynar api key should be the api key that you can view on your dashboard * the mnemonic should be the mnemonic associated with the developer account which will be used to create the signers e.g. `@your_company_name` account on Farcaster (to state the obvious out loud, you *won't* need user mnemonics at any point) ## Creating API route for generating the signature Create a new `route.ts` file in the `src/app/api/signer` folder and add the following: ```typescript route.ts theme={"system"} import { getSignedKey } from "@/utils/getSignedKey"; import { NextResponse } from "next/server"; export async function POST() { try { const signedKey = await getSignedKey(); return NextResponse.json(signedKey, { status: 200, }); } catch (error) { return NextResponse.json({ error: "An error occurred" }, { status: 500 }); } } ``` This is just defining a get and a post request and calling a getSignedKey function but we haven't created that yet, so let's do that. Create a new `utils/getSignedKey.ts`file and add the following: ```typescript getSignedKey.ts theme={"system"} import neynarClient from "@/lib/neynarClient"; import { ViemLocalEip712Signer } from "@farcaster/hub-nodejs"; import { bytesToHex, hexToBytes } from "viem"; import { mnemonicToAccount } from "viem/accounts"; import { getFid } from "./getFid"; export const getSignedKey = async () => { const createSigner = await neynarClient.createSigner(); const { deadline, signature } = await generate_signature( createSigner.public_key ); if (deadline === 0 || signature === "") { throw new Error("Failed to generate signature"); } const fid = await getFid(); const signedKey = await neynarClient.registerSignedKey({ signerUuid:createSigner.signer_uuid, appFid:fid, deadline, signature }); return signedKey; }; const generate_signature = async function (public_key: string) { if (typeof process.env.FARCASTER_DEVELOPER_MNEMONIC === "undefined") { throw new Error("FARCASTER_DEVELOPER_MNEMONIC is not defined"); } const FARCASTER_DEVELOPER_MNEMONIC = process.env.FARCASTER_DEVELOPER_MNEMONIC; const FID = await getFid(); const account = mnemonicToAccount(FARCASTER_DEVELOPER_MNEMONIC); const appAccountKey = new ViemLocalEip712Signer(account as any); // Generates an expiration date for the signature (24 hours from now). const deadline = Math.floor(Date.now() / 1000) + 86400; const uintAddress = hexToBytes(public_key as `0x${string}`); const signature = await appAccountKey.signKeyRequest({ requestFid: BigInt(FID), key: uintAddress, deadline: BigInt(deadline), }); if (signature.isErr()) { return { deadline, signature: "", }; } const sigHex = bytesToHex(signature.value); return { deadline, signature: sigHex }; }; ``` We are doing a couple of things here, so let's break it down. We first use the neynarClient (yet to create) to create a signer, and then we use the `appAccountKey.signKeyRequest` function from the `@farcaster/hub-nodejs` package to create a sign key request. Finally, we use the `registerSignedKey` function from the neynarClient to return the signedKey. Let's now initialise our `neynarClient` in a new `lib/neynarClient.ts` file like this: ```typescript neynarClient.ts theme={"system"} import { Configuration, NeynarAPIClient } from "@neynar/nodejs-sdk"; if (!process.env.NEYNAR_API_KEY) { throw new Error("Make sure you set NEYNAR_API_KEY in your .env file"); } const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const neynarClient = new NeynarAPIClient(config); export default neynarClient; ``` We are also using another util function named `getFid` in the signature generation, so let's create a `utils/getFid.ts` file and create that as well: ```typescript getFid.ts theme={"system"} import neynarClient from "@/lib/neynarClient"; import { mnemonicToAccount } from "viem/accounts"; export const getFid = async () => { if (!process.env.FARCASTER_DEVELOPER_MNEMONIC) { throw new Error("FARCASTER_DEVELOPER_MNEMONIC is not set."); } const account = mnemonicToAccount(process.env.FARCASTER_DEVELOPER_MNEMONIC); // Lookup user details using the custody address. const { user: farcasterDeveloper } = await neynarClient.lookupUserByCustodyAddress({custodyAddress:account.address}); return Number(farcasterDeveloper.fid); }; ``` We can use this api route on our front end to generate a signature and show the QR code/deep link to the user. So, head over to `app/page.tsx` and add the following: ```typescript page.tsx theme={"system"} "use client"; import axios from "axios"; import QRCode from "qrcode.react"; import { useState } from "react"; import styles from "./page.module.css"; interface FarcasterUser { signer_uuid: string; public_key: string; status: string; signer_approval_url?: string; fid?: number; } export default function Home() { const LOCAL_STORAGE_KEYS = { FARCASTER_USER: "farcasterUser", }; const [loading, setLoading] = useState(false); const [farcasterUser, setFarcasterUser] = useState( null ); const handleSignIn = async () => { setLoading(true); await createAndStoreSigner(); setLoading(false); }; const createAndStoreSigner = async () => { try { const response = await axios.post("/api/signer"); if (response.status === 200) { localStorage.setItem(LOCAL_STORAGE_KEYS.FARCASTER_USER, JSON.stringify(response.data)); setFarcasterUser(response.data); } } catch (error) { console.error("API Call failed", error); } }; return (
{!farcasterUser?.status && ( )} {farcasterUser?.status == "pending_approval" && farcasterUser?.signer_approval_url && ( )}
); } ``` Here, we show a button to sign in with farcaster in case the farcasterUser state is empty which it will be initially. And if the status is "pending\_approval" then we display a QR code and a deep link for mobile view like this: ![Sign in with farcaster](https://mintcdn.com/neynar/aGwjtKmNewHJXSzO/images/docs/54d25f6-image.png?fit=max\&auto=format\&n=aGwjtKmNewHJXSzO\&q=85\&s=2b6c9ffd812096ae0fcfcca00b7717cb) If you try scanning the QR code it will take you to Warpcast to sign into your app! But signing in won't do anything right now, so let's also handle that. In the `api/signer/route.ts` file let's add a GET function as well to fetch the signer using the signer uuid like this: ```typescript route.ts theme={"system"} export async function GET(req: Request) { const { searchParams } = new URL(req.url); const signer_uuid = searchParams.get("signer_uuid"); if (!signer_uuid) { return NextResponse.json( { error: "signer_uuid is required" }, { status: 400 } ); } try { const signer = await neynarClient.lookupSigner({ signerUuid:signer_uuid}); return NextResponse.json(signer, { status: 200 }); } catch (error) { return NextResponse.json({ error: "An error occurred" }, { status: 500 }); } } ``` Let's use this route in the `page.tsx` file to fetch the signer and set it in local storage using some useEffects: ```typescript page.tsx theme={"system"} useEffect(() => { const storedData = localStorage.getItem(LOCAL_STORAGE_KEYS.FARCASTER_USER); if (storedData) { const user: FarcasterUser = JSON.parse(storedData); setFarcasterUser(user); } }, []); useEffect(() => { if (farcasterUser && farcasterUser.status === "pending_approval") { let intervalId: NodeJS.Timeout; const startPolling = () => { intervalId = setInterval(async () => { try { const response = await axios.get( `/api/signer?signer_uuid=${farcasterUser?.signer_uuid}` ); const user = response.data as FarcasterUser; if (user?.status === "approved") { // store the user in local storage localStorage.setItem( LOCAL_STORAGE_KEYS.FARCASTER_USER, JSON.stringify(user) ); setFarcasterUser(user); clearInterval(intervalId); } } catch (error) { console.error("Error during polling", error); } }, 2000); }; const stopPolling = () => { clearInterval(intervalId); }; const handleVisibilityChange = () => { if (document.hidden) { stopPolling(); } else { startPolling(); } }; document.addEventListener("visibilitychange", handleVisibilityChange); // Start the polling when the effect runs. startPolling(); // Cleanup function to remove the event listener and clear interval. return () => { document.removeEventListener( "visibilitychange", handleVisibilityChange ); clearInterval(intervalId); }; } }, [farcasterUser]); ``` Here, we are checking if the user has approved the TX and if they have approved it, we call the signer api and set the user details in the local storage. Let's also add a condition in the return statement to display the fid of the user if they are logged in: ```typescript page.tsx theme={"system"} {farcasterUser?.status == "approved" && (
Hello {farcasterUser.fid} 👋
)} ``` ## Allowing users to write casts Let's now use the signer that we get from the user to publish casts from within the app. Create a new `api/cast/route.ts` file and add the following: ```typescript route.ts theme={"system"} import neynarClient from "@/lib/neynarClient"; import { NextResponse } from "next/server"; export async function POST(req: Request) { const body = await req.json(); try { const cast = await neynarClient.publishCast({signerUuid:body.signer_uuid, text:body.text}); return NextResponse.json(cast, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ error: "An error occurred" }, { status: 500 }); } } ``` Here, I am using the `publishCast` function to publish a cast using the signer uuid and the text which I am getting from the body of the request. Head back to the `page.tsx` file and add a `handleCast` function to handle the creation of casts like this: ```typescript page.tsx theme={"system"} const [text, setText] = useState(""); const [isCasting, setIsCasting] = useState(false); const handleCast = async () => { setIsCasting(true); const castText = text.length === 0 ? "gm" : text; try { const response = await axios.post("/api/cast", { text: castText, signer_uuid: farcasterUser?.signer_uuid, }); if (response.status === 200) { setText(""); // Clear the text field alert("Cast successful"); } } catch (error) { console.error("Could not send the cast", error); } finally { setIsCasting(false); // Re-enable the button } }; ``` Now, we just need to add an input to accept the cast text and a button to publish the cast. Let's add it below the hello fid text like this: ```typescript page.tsx theme={"system"} {farcasterUser?.status == "approved" && (
Hello {farcasterUser.fid} 👋