# 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. Read prior context on user scores [here](/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 [here](/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 // 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 // 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](https://t.me/rishdoteth). We want to hear what you're building and see how we can make it easier. ## Further reading # Build Farcaster Frames & Analytics with 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 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 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 export const app = new Frog({ assetsPath: "/", basePath: "/api", hub: neynar({ apiKey: 'NEYNAR_FROG_FM' }) }); ``` ### 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 import { neynar } from "frog/hubs"; ``` Now, change the frame on the `/` route to match this: ```typescript index.tsx 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 ### 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 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 ## 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 ## Deployment You can deploy your frame using the [vercel CLI](https://vercel.com/docs/cli) like this: ```powershell PowerShell 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 [Telegram](https://t.me/rishdoteth)! # 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 // 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 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 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 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 {"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! ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Build Interactive Farcaster Frames with Neynar Source: https://docs.neynar.com/docs/building-frames Build frames 100x quicker than starting from scratch 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 { "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 Frame spec [here](https://warpcast.notion.site/Farcaster-Frames-4bd47fe97dc74a42a48d3a234636d8c5). 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 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 cd bun install ``` Now, let's install the dependencies that we are going to need to build out this action: ```powershell PowerShell 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 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 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 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 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 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 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 ngrok http http://localhost:5173/ ``` This command will give you a URL which will forward the requests to your localhost: Ngrok URL 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 Copy the install URL and paste it into a new variable in the `index.tsx` like this: ```typescript index.tsx 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 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 Click on Add action and it'll prompt you to add a new action like this: Add farcaster action 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 ### 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 [Telegram](https://t.me/rishdoteth)! # 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: 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 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 Once the app is created, install the packages that we are going to need for the command: ```powershell npm npm i @neynar/react @neynar/nodejs-sdk axios @prisma/client prisma frames.js ``` ```powershell yarn yarn add @neynar/react @neynar/nodejs-sdk axios ``` ```powershell bash 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: Frame creation Frame creation Frame creation Now, let's go ahead and set up Prisma in our project. Run the following command to initialise: ```powershell PowerShell 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`. ### 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 model User { fid String @id @default(cuid()) @map("_id") signerUUID String @unique } ``` Once you have added the model, run these two commands: ```powershell PowerShell 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 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 "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 ### 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 "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 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 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 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 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 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 Now, create a new file `start/route.tsx` in the `frame` folder and add the following: ```typescript start/route.tsx 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 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 ## 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 [Telegram](https://t.me/rishdoteth)! # 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 ### Related set of APIs [here](/reference/fetch-subscribers-for-fid) 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 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 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 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 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 [here](/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 [Telegram](https://t.me/rishdoteth)! # 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 If looking to create a new mini app from scratch, see [here](/docs/create-v2-farcaster-frame-in-60s). Converting an existing JavaScript-based web app to a Farcaster mini app involves the following steps: * install the [Mini App SDK](https://www.npmjs.com/package/@farcaster/frame-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 The Mini App SDK (formerly the Frame SDK) can be added to your project with: ```{bash} npm install @farcaster/frame-sdk ``` Then call the ready function when your interface is loaded and ready to be displayed: ```{JavaScript} import { sdk } from '@farcaster/frame-sdk'; await sdk.actions.ready(); ``` ## Connecting to the wallet provider It's recommended to use `wagmi` for your wallet provider, as the Farcaster team provides the [@farcaster/frame-wagmi-connector package](https://www.npmjs.com/package/@farcaster/frame-wagmi-connector) for easy configuration. Run `npm i @farcaster/frame-wagmi-connector` to install, and then connecting is as simple as adding the connector to the wagmi config: ```{JavaScript} import { http, createConfig } from 'wagmi'; import { base } from 'wagmi/chains'; import { farcasterFrame as miniAppConnector } from '@farcaster/frame-wagmi-connector'; export const wagmiConfig = createConfig({ chains: [base], transports: { [base.id]: http(), }, connectors: [ miniAppConnector() // 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()`. ## 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 `frame` 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 `frame` 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 frame portion of the manifest: ```{JSON} { "frame": { "version": "1", "name": "Yoink!", "iconUrl": "https://yoink.party/logo.png", "homeUrl": "https://yoink.party/framesV2/" } } ``` Once your domain is live and serving something like the above example at `yourURL.com/.well-known/farcaster.json`, * go to [the manifest tool on Warpcast](https://warpcast.com/~/developers/mini-apps/manifest), * 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} { "accountAssociation": { "header": "eyJmaWQiOjM2MjEsInR5cGUiOiJjdXN0b2R5Iiwia2V5IjoiMHgyY2Q4NWEwOTMyNjFmNTkyNzA4MDRBNkVBNjk3Q2VBNENlQkVjYWZFIn0", "payload": "eyJkb21haW4iOiJ5b2luay5wYXJ0eSJ9", "signature": "MHgwZmJiYWIwODg3YTU2MDFiNDU3MzVkOTQ5MDRjM2Y1NGUxMzVhZTQxOGEzMWQ5ODNhODAzZmZlYWNlZWMyZDYzNWY4ZTFjYWU4M2NhNTAwOTMzM2FmMTc1NDlmMDY2YTVlOWUwNTljNmZiNDUxMzg0Njk1NzBhODNiNjcyZWJjZTFi" }, "frame": { "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 like on Warpcast, you must add a meta tag with the name "fc:frame" to the `` section of the HTML page serving your mini app. ```{HTML} ``` 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_frame`, so unless you have a specific use case, you can safely copy the following example: ```{JSON} { "version": "next", "imageUrl": "https://yoink.party/framesV2/opengraph-image", "button": { "title": "🚩 Start", "action": { "type": "launch_frame", "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 For this guide, we'll go over: 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 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 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 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 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 ngrok http http://localhost:3000 ``` Serve your app using ngrok ### 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 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 Create a new `.env` file in the root of your project and add the following: ```bash .env 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 Create a `neynarClient.ts` file and add the following: ```typescript neynarClient.ts // 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 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 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 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 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](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 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 [Telegram](https://t.me/rishdoteth)! # 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 a new node.js app using the following commands: ```bash 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 bun add @farcaster/hub-nodejs ``` Now, let's get to building our stream. In the index.ts file add the following to initialise the client: ```typescript index.ts import { getSSLHubRpcClient, HubEventType } from "@farcaster/hub-nodejs"; const options = { interceptors: [ createDefaultMetadataKeyInterceptor( 'x-api-key', "YOUR_NEYNAR_API_KEY" ), ], }; const hubRpcEndpoint = "hub-grpc-api.neynar.com"; const client = getSSLHubRpcClient(hubRpcEndpoint, options); ``` 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 [here](https://www.thehubble.xyz/docs/events.html). So, add the following in your code: ```typescript index.ts 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 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. Finally, you can run the script using `bun run index.ts` and it will provide you with a stream like this: Cast Stream ## Conclusion Here's what the completed code looks like ```typescript import { getSSLHubRpcClient, HubEventType } from "@farcaster/hub-nodejs"; const options = { interceptors: [ createDefaultMetadataKeyInterceptor( 'x-api-key', "YOUR_NEYNAR_API_KEY" ), ], }; const hubRpcEndpoint = "hub-grpc-api.neynar.com"; const client = getSSLHubRpcClient(hubRpcEndpoint, options); 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(); } }); ``` 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 [Telegram](https://t.me/rishdoteth)! # 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 Tap on "Create Agent" to make the agent. create agent 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 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 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 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 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 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 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 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 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 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 ngrok http http://localhost:5173/ ``` This command will give you a URL which will forward the requests to your localhost: Ngrok URL If you go ahead and try out your action you'll see a frame like this Multi step cast action ## 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 [Telegram](https://t.me/rishdoteth)! # Create Transaction Frames Source: https://docs.neynar.com/docs/create-transaction-frames See [Make agents prompt transactions](/docs/make-agents-prompt-transactions) # Deploy a Token on Base with 1 API Call Source: https://docs.neynar.com/docs/deploy-token-on-base-with-api-call This guide provides a step-by-step process to deploy a fungible token on the Base network using Neynar's API. The deployment process is simplified to a single API call, eliminating the need for developers to write Solidity code or handle on-chain transaction signing. Neynar covers the on-chain deployment fees and assigns the specified owner address as the token owner. ### 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). * **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 { "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 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 { "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. This process abstracts the complexities of blockchain development, allowing you to focus on building your application. For further assistance, reach out to Neynar support or consult the [Neynar API documentation](/reference/deploy-fungible). # Farcaster Actions Spec Source: https://docs.neynar.com/docs/farcaster-actions-spec ### API Endpoints Related API reference [here](/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 * Checkout the documentation on [managed signers](/docs/integrate-managed-signers) * Define the action payload, including the type and any necessary data. * Specify the destination base URL of App B. * Make a POST request to the Neynar API endpoint (POST - `/v2/farcaster/action`) with the following structure: ```json json { "signer_uuid": "uuid-of-the-signer", "url": "https://appb.xyz", "action": { "type": "actionType", "payload": { // Action-specific data } } } ``` * 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 * 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. * 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. * 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. * 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 { "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 { "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 { "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 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 PM2 is a process manager for Node.js applications. Install it globally using npm: ```bash bash npm install -g pm2 ``` Navigate to the project directory and run one of the following commands to install all required dependencies: ```Text Yarn yarn install ``` ```bash npm npm install ``` * Copy the example environment file: ```bash bash 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 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. Go to Farcaster KeyGateway optimism explorer [https://optimistic.etherscan.io/address/0x00000000fc56947c7e7183f8ca4b62398caadf0b#writeContract](https://optimistic.etherscan.io/address/0x00000000fc56947c7e7183f8ca4b62398caadf0b#writeContract) Connect to Web3. 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. Press "Write" to execute the transaction. This will create a signer for your mnemonic on the OP mainnet. ## Running the Bot Launch the bot using the following command: ```bash Yarn yarn start ``` ```Text npm npm run start ``` Ensure that the bot is running correctly with: ```bash bash pm2 status ``` To check the bot's activity logs, use: ```bash bash pm2 logs ``` If you need to stop the bot, use: ```bash bash 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 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. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Farcaster For You Feed Source: https://docs.neynar.com/docs/feed-for-you-w-external-providers Retrieve a personalized For You feed for a user ### Related API reference [here](/reference/fetch-feed-for-you) **Neynar is set as the default provider**. To choose a different provider, simply pass in a different value in the `provider` field. `openrank` is set as the default. (`karma3` is an older name for `openrank` --kept here for backwards compatiblity--) If you pick `mbd` as provider, you can further customize your feed by passing in additional filter values in an optional`filters` object inside the `provider_metadata` field in the request e.g. ```javascript Javascript const provider_metadata = encodeURIComponent(JSON.stringify({ "filters": { "channels": [ "https://warpcast.com/~/channel/neynar" ], "languages": [ "en" ], "author_ids": [ "194", "191" ], // remove_author_fids only works when author_ids isn't passed in // "remove_author_ids": [ // "18949" // ], "frames_only": false, "embed_domains": [ "neynar.com", "frames.neynar.com" ], "ai_labels": [ "science_technology" ], "remove_ai_labels": [ "spam" ] } })); ``` The filters available for MBD that you can pass in that object are: | Name | Type | Description | | ------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `start_timestamp` | string | return only casts after this start\_timestamp, specified as Epoch time (Unix timestamp) | | `end_timestamp` | string | return only casts before this end\_timestamp, specified as Epoch time (Unix timestamp) | | `channels` | string\[] | return only casts that belong to these channels, specified by channel urls (root\_parent\_url) | | `languages` | string\[] | returns only casts that use these languages | | `author_ids` | string\[] | returns only casts created by authors with these fids | | `remove_author_ids` | string\[] | does not return casts created by authors with these fid's NOTE: this is ignored if author\_ids is defined | | `frames_only` | boolean | whether to limit search to only frames | | `embed_domains` | string\[] | return only casts with specific domains embedded | | `ai_labels` | string\[] | Return only casts that have these AI labels. Available values below. Labels in *topics* category: - `arts_culture` - `business_entrepreneurs` - `celebrity_pop_culture` - `diaries_daily_life` - `family` - `fashion_style` - `film_tv_video` - `fitness_health` - `food_dining` - `gaming` - `learning_educational` - `music` - `news_social_concern` - `other_hobbies` - `relationships` - `science_technology` - `sports` - `travel_adventure` - `youth_student_life` Labels in *sentiment* category: - `positive` - `neutral` - `negative` Labels in *emotion* category: - `anger` - `anticipation` - `disgust` - `fear` - `joy` - `love` - `optimism` - `pessimism` - `sadness` - `surprise` - `trust` Labels in *moderation* category: - `llm_generated` - `spam` - `sexual` - `hate` - `violence` - `harassment` - `self_harm` - `sexual_minors` - `hate_threatening` - `violencegraphic` Labels in *web3\_topics* category: - `web3_nft` - `web3_defi` - `web3_infra` - `web3_industry` - `web3_consumer` | | `remove_ai_labels` | string\[] | do not return casts with these AI labels NOTE: this is ignored if ai\_labels is defined | A full request to the feed API with the custom mbd filters object looks like below ```javascript Javascript const fetch = require('node-fetch'); const provider_metadata = encodeURIComponent(JSON.stringify({ "filters": { "channels": [ "https://warpcast.com/~/channel/neynar" ], "languages": [ "en" ], "author_ids": [ "194", "191" ], // Note: remove_author_ids only works when author_ids isn't passed in // "remove_author_ids": [ // "18949" // ], "frames_only": false, "embed_domains": [ "neynar.com", "frames.neynar.com" ], "ai_labels": [ "science_technology" ] } })); const url = `https://api.neynar.com/v2/farcaster/feed/for_you?fid=3&viewer_fid=2&provider=mbd&limit=10&provider_metadata=${provider_metadata}`; const options = { method: 'GET', headers: { 'accept': 'application/json', 'api_key': 'NEYNAR_API_DOCS' } }; // Fetch request with the metadata and options fetch(url, options) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('error:', error)); ``` # 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 ### Related API reference [here](/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 // 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 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 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 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 { 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 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! ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Relevant Holders for Coins on Base 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 on the Base network. ### Related API: [Relevant owners](/reference/fetch-relevant-fungible-owners) ## Fetching Relevant Holders for a Coin on Base Network The API identifies relevant users on Farcaster who hold the same coin, based on the users followed by the viewer. ### 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**: Currently, only the Base network is supported. * **Viewer Context**: The API customizes the response based on the `viewer_fid`, respecting the viewer's mutes and blocks. * **Relevance**: Users are considered relevant if they are followed by the viewer. ### API Endpoint #### Endpoint ``` GET /fungible/owner/relevant ``` #### Parameters * **contract\_address** (string, required): The contract address of the fungible asset. * Example: `0x0db510e79909666d6dec7f5e49370838c16d950f` * **networks** (array, required): A comma-separated list of networks to fetch balances for. Currently, only "base" is supported. * 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 GET /fungible/owner/relevant?contract_address=0x0db510e79909666d6dec7f5e49370838c16d950f&networks=base&viewer_fid=194 ``` ### Example Response ```json JSON { "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: ```javascript javascript import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` ```javascript javascript 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); ``` ## Conclusion This guide provides the necessary steps to fetch relevant fungible token holders on the Base network using the Neynar API. By leveraging this API, developers can enhance their applications with social insights into token ownership, fostering a more connected user experience. 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 on [Telegram](https://t.me/rishdoteth). # 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 # 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)) ### Related API: [List signers](/reference/fetch-signers) ## **Prerequisites** Ensure you have Nodejs installed on your system. You can download it from [Node.js' official website](https://nodejs.org/). 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. Install the required packages: ```bash Shell yarn add siwe viem @neynar/nodejs-sdk ``` ## **Code Breakdown and Steps** The code begins by importing the necessary libraries: ```javascript Javascript import { SiweMessage } from "siwe"; import { mnemonicToAccount } from "viem/accounts"; import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; ``` Replace `"YOUR_MNEMONIC_HERE"` with your Ethereum mnemonic phrase: ```javascript Javascript const mnemonic = "YOUR_MNEMONIC_HERE"; ``` The `mnemonicToAccount` function converts your mnemonic into an account object: ```javascript Javascript const account = mnemonicToAccount(mnemonic); ``` Replace `"YOUR_API_KEY_HERE"` with your API key and set the correct base path for the Neynar API: ```javascript Javascript const config = new Configuration({ apiKey: "YOUR_API_KEY_HERE", }); const client = new NeynarAPIClient(config); ``` The `createSiweMessage` function generates a SIWE message with details such as domain, address, and [nonce](/reference/fetch-nonce): ```javascript Javascript 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(); } ``` 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 here](/reference/fetch-bulk-users)) ```javascript Javascript 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; } ``` Call the `fetchSigners` function and handle success or errors: ```javascript Javascript fetchSigners() .then((signers) => { console.log("\n\nsigners:", signers, "\n\n"); }) .catch((error) => { console.error("error:", error); }); ``` ## **Expected Output** ```json JSON [ { "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](https://t.me/rishdoteth). # 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)) ### 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** Browser Ensure you are using a browser with a wallet like MetaMask installed. Obtain an API key from [dev portal](https://dev.neynar.com/app) Install the required packages: ```shell shell yarn add siwe viem @neynar/nodejs-sdk ``` ## **Code Breakdown and Steps** The code starts by importing the necessary libraries: ```javascript Javascript import { SiweMessage } from "siwe"; import { createWalletClient, custom, publicActions } from "viem"; import { optimism } from "viem/chains"; import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; ``` 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 const config = new Configuration({ apiKey: "YOUR_API_KEY_HERE", }); const client = new NeynarAPIClient(config); ``` The `createWalletClient` function initializes a wallet client using the `viem` library. It uses `window.ethereum` to connect to the browser's wallet: ```javascript Javascript const wallet = createWalletClient({ chain: optimism, transport: custom(window.ethereum), }).extend(publicActions); ``` The `createSiweMessage` function generates a SIWE message with details such as domain, address, and [nonce](/reference/fetch-nonce): ```javascript Javascript 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(); } ``` 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 here](/reference/fetch-bulk-users)) ```javascript Javascript 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; } ``` Call the `fetchSigners` function and handle the response or errors: ```javascript Javascript fetchSigners() .then((signers) => { console.log("\n\nsigners:", signers, "\n\n"); }) .catch((error) => { console.error("error:", error); }); ``` ## **Expected Output** ```json JSON [ { "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](https://t.me/rishdoteth). # Fetch & Display Farcaster Feeds with Neynar API Source: https://docs.neynar.com/docs/fetching-casts-from-memes-channel-in-farcaster Show casts from a Farcaster channel with Neynar ### Related API reference [here](/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`. 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 // 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 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 { 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 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. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Notifications in Channel Source: https://docs.neynar.com/docs/fetching-channel-specific-notification-in-farcaster Show notifications from a specific channel for a Farcaster user ### 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) 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 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. ### 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 const nounsChannelUrl = "chain://eip155:1/erc721:0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03"; const userFID = 3; const notifications = await client.fetchChannelNotificationsForUser({fid:userFID,channelIds: [ nounsChannelUrl, ]}); ``` Example output: ```json { "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 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! ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) 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 ### 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 // 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 // vitalik.eth const addr = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"; const user = await client.fetchBulkUsersByEthOrSolAddress({addresses: [addr]}); console.log(user); ``` Example output: ```json { 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 // 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 { 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" } } } ``` ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Follow NFT Holders Source: https://docs.neynar.com/docs/following-all-farcaster-users-owning-cryptopunk How to follow all Farcaster users who own a certain 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 // 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 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 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 const result = await client.followUser({ signerUuid:signer, targetFids:fids}); console.log(result); ``` Example output: ```json { "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. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) 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 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 cd bun install ``` ### Using the neynar middleware Create a new file `frame.ts` and add the following: ```typescript frame.ts 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. ### 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 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 ### 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 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 ## 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 ## 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 [Telegram](https://t.me/rishdoteth)! # From Kafka Stream Source: https://docs.neynar.com/docs/from-kafka-stream Ingest hydrated events from a hosted Kafka stream (as compared to dehydrated events from gRPC hub) With Kafka, you can subscribe to the same data that we use for sending webhook notifications ### To get entire dataset, Kafka is best paired with [one of our other data products](/docs/how-to-choose-the-right-data-product-for-you) (such as [Parquet](/docs/parquet) ) Kafka is not suitable to build a database with all of the data from Farcaster day 1. Our kafka topics currently keep data for 14 days. It's a good solution for streaming recent data in real time (P95 data latency of \<1.5s). ## Why If you’re using Hub gRPC streaming, you’re getting dehydrated events that you have to put together yourself later to make useful (see [here](https://warpcast.com/rish/0x7c2997ec) for example). With Neynar’s Kafka stream, you get a fully hydrated event (e.g., [user.created](/docs/from-kafka-stream#data-schema)) that you can use in your app/product immediately. See the example between the gRPC hub event and the Kafka event below. Kafka stream ## How * [Reach out](https://t.me/rishdoteth), we will create credentials for you and send them via 1Password. * For authentication, the connection requires `SASL/SCRAM SHA512`. * The connection requires TLS (sometimes called SSL for legacy reasons) for encryption. * `farcaster-mainnet-events` is the primary topic name. There may be more topics in the future, but for now, there is just one. It has two partitions. There are three brokers available over the Internet. Provide them all to your client: * `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 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 ``` You can use `kcat` (formerly `kafkacat`) to test things locally: ```bash cURL brew install kcat brew home kcat ``` ```bash cURL 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 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 1 topics: topic "farcaster-mainnet-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 ``` ## Consumer nodejs example ## Data schema ```typescript user.created // _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 // _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 // _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 follow.created // _when a user follows another user_ interface UserDehydrated { object: "user_dehydrated"; fid: number; username: string; } interface EventData { object: "follow"; 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 FollowCreatedEvent { event_type: "follow.created"; data: EventData; custom_headers: CustomHeaders; idempotency_key?: string } ``` ```typescript follow.deleted // _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 // _when a reaction is added to a cast_ interface UserDehydrated { object: "user_dehydrated"; fid: number; username: 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; cast: CastDehydrated; } interface CustomHeaders { "x-convoy-message-type": "broadcast"; } interface ReactionCreatedEvent { event_type: "reaction.created"; data: EventData; custom_headers: CustomHeaders; idempotency_key?: string; } ``` ```typescript reaction.deleted // _when a reaction is removed from a cast_ interface UserDehydrated { object: "user_dehydrated"; fid: number; username: 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; cast: CastDehydrated; } interface CustomHeaders { "x-convoy-message-type": "broadcast"; } interface ReactionDeletedEvent { event_type: "reaction.deleted"; data: EventData; custom_headers: CustomHeaders; idempotency_key?: string; } ``` # Getting Started Source: https://docs.neynar.com/docs/getting-started-with-neynar Start building on Farcaster with Neynar Farcaster is a protocol for building decentralized social apps. Neynar makes it easy to build on Farcaster. ## 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: 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. 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". 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? Click "Subscribe" on one of the plans below Upon successful payment, we'll send you an email. Once the email arrives, you'll be able to sign in to the [Developer Portal](https://dev.neynar.com) Don't hesitate to reach out to us on our [channel](https://warpcast.com/~/channel/neynar) or [Telegram](https://t.me/rishdoteth) with any questions! ## Set up Neynar SDK Neynar [`nodejs` SDK](https://github.com/neynarxyz/nodejs-sdk) is an easy way to use the APIs. This section must only be done once when setting up the SDK for the first time. To install the Neynar TypeScript SDK: ```typescript yarn yarn add @neynar/nodejs-sdk ``` ```typescript npm npm install @neynar/nodejs-sdk ``` ```typescript pnpm pnpm install @neynar/nodejs-sdk ``` ```typescript bun bun add @neynar/nodejs-sdk ``` To get started, initialize the client in a file named `index.ts`: ```typescript Typescript 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: check the `type` field in package.json. Since we're using ES6 modules, you may need to set it to "module". ```json package.json { "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" } } ``` If you hit errors, try adding a `tsconfig.json` file in the directory to help with typescript compilation ```typescript Typescript { "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 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 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 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 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 yarn start ``` It should show you a response like the one below: ```typescript Typescript 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. We have numerous guides available [here](/docs), and our complete API reference is [here](/reference). If you have questions or feedback, please contact [rish](https://warpcast.com/rish) on Farcaster or [Telegram](https://t.me/rishdoteth) . # 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 ### 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: * 5000 cast messages * 2500 reaction messages * 2500 link messages * 50 user\_data messages * 25 verifications messages * 5 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 // 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 const rishFID = 194; const storageUsage = await client.lookupUserStorageUsage({fid:rishFID}); console.log(storageUsage); ``` Example output: ```json { 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 const storageAllocation = await client.lookupUserStorageAllocations({fid:rishFID}); console.log(storageAllocation); ``` Example output: ```json { 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. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) 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. ## 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. ## 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 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 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 ### Code Example: ```javascript Javascript const response = await fetch("/api/user"); 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); ``` ## 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 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 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. ### Code Example: ```javascript Javascript 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, }, }; const response = await fetch("/api/user", { method: "POST", headers: { "Content-Type": "application/json", }, 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!"); ``` ## 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 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 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 const result = await client.validateFrameAction({ messageBytesInHex: payload.trustedData.messageBytes, }); console.log(result); ``` ```json json { "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 Warpcast URL](/docs/how-to-get-cast-information-from-warpcast-url) guide. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Frame Quickstart Source: https://docs.neynar.com/docs/how-to-build-farcaster-frames-with-neynar Build it 10x quicker than starting from scratch Building a new Farcaster Frame? Read the Frame spec [here](https://warpcast.notion.site/Farcaster-Frames-4bd47fe97dc74a42a48d3a234636d8c5) to get started. 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)! # 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 | | | [Hosted SQL](/docs/how-to-query-neynar-sql-playground-for-farcaster-data) | Directly write sql queries against a postgres hosted db, no need to set up anything additional | Developer has no write access to db creating new indexes, etc. requires reaching out to Neynar no changes to overall table schema | | [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. | | | [Kafka stream](/docs/from-kafka-stream) | Good real time complement to services like Parquet -- backfill with Parquet and ingest real time with Kafka stream | Need to set up Kafka ingestion. See open source code [here](/docs/from-kafka-stream). | # React client 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: 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 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 Once the app is created, install the packages that we are going to need for the command: ```powershell npm npm i @neynar/react @neynar/nodejs-sdk ``` ```powershell yarn yarn add @neynar/react @neynar/nodejs-sdk ``` ```powershell bash 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 "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 ### 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 "use client"; import { NeynarAuthButton } from "@neynar/react"; import Link from "next/link"; export const Header: FC = () => { return (
NeynarClient
); }; ```
We'll add the header to the `layout.tsx` file since we are going to need it on all the pages: ```typescript layout.tsx "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 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 "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 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 import { NeynarAPIClient } from "@neynar/nodejs-sdk"; const neynarClient = new NeynarAPIClient(process.env.NEYNAR_API_KEY!); export default neynarClient; ``` ### 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 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 "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 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 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 "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 ### 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 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 ## 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 [Telegram](https://t.me/rishdoteth). # Create Via Script Source: https://docs.neynar.com/docs/how-to-create-a-farcaster-bot Create a Farcaster bot on Neynar in a few quick steps ### 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 PM2 is a process manager for Node.js applications. Install it globally using npm: ```bash Bash npm install -g pm2 ``` Navigate to the project directory and run one of the following commands to install all required dependencies: ```Text Yarn yarn install ``` ```bash npm npm install ``` * Copy the example environment file: ```bash bash 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). Login to your Neynar dev portal App -> "Agents and bots" -> "use existing account" Login to your Neynar dev portal Click the Sign in With Neynar button Connect the bot's address, the Warpcast logged-in user must be the bot If everything goes well, there will be a signer UUID, which can be used to cast as the bot! ## Running the Bot Launch the bot using the following command: ```bash Yarn yarn start ``` ```Text npm npm run start ``` Ensure that the bot is running correctly with: ```bash bash pm2 status ``` To check the bot's activity logs, use: ```bash bash pm2 logs ``` If you need to stop the bot, use: ```bash bash 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 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. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Create new Farcaster Account Source: https://docs.neynar.com/docs/how-to-create-a-new-farcaster-account-with-neynar Currently, this API is allowlisted. Please get in touch with us if you wish to access this feature. Currently, this API is allowlisted. Please get in touch with us if you wish to access this feature. ### Each new user account costs \$X (based on the contract) and the total will be charged at the end of the month. Related API: [Register new account](/reference/register-account) This guide enables developers to seamlessly create and register new user accounts on Farcaster through Neynar. This API is allowlisted so if you're interested in using it, reach out to [@rish](https://t.me/rishdoteth) . 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. ### Prerequisites * Ensure you're allowlisted for the [Register new user](/reference/register-new-user) API (contact [rish](https://t.me/rishdoteth) if needed) * 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/). ### 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. ### API Call ```bash Shell curl --location 'https://api.neynar.com/v2/farcaster/user/fid' \ --header 'api_key: NEYNAR_API_KEY' ``` **Note:** Replace NEYNAR\_API\_KEY with your actual API key. ### Responses ```json 200 { "fid": UNIQUE_FID // UNIQUE_FID is a number } ``` ```json 500 { "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 mkdir generate-required-parameters cd generate-required-parameters ``` ### Install Dependencies ```Text yarn yarn add @farcaster/hub-nodejs viem ``` ```Text npm npm install @farcaster/hub-nodejs viem ``` ### Create the Script Create a file named `generate-required-parameters.js` ```bash 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 // 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 node generate-required-parameters.js ``` ### Script Output You'll receive output containing several values, including `deadline`, `requested_user_custody_address`, `fid`, `signature` ## Step 3: 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 4: Register the User Construct a POST request with the generated parameters to finalize the user's registration. ### Finalize Registration ```json curl --location 'https://api.neynar.com/v2/farcaster/user' \ --header 'api_key: NEYNAR_API_KEY' \ --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" }' ``` ### Responses ```json 200 { "success": true, "message": "Account transferred successfully.", "signer": { "fid": 0, "signer_uuid": "35b0bbd5-4e20-4213-a30c-4183258a73ab", "status": "APPROVED", "public_key": "0x123412341234123412341234123412341234" } } ``` ```json 400 { "message": "Account not found" } ``` ```json 401 { "message": "Account is not issued to you" } ``` ```json 404 { "message": "Account not found" } ``` ```json 409 { "message": "Account is already registered to another user" } ``` ```json 500 { "success": false, "message": "Failed to sign transfer", } ``` ## Step 5: Profile setup (optional) Using the approved signer\_uuid from the response in Step 4, 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 ### 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 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 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 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 [here](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 node script.js ``` Frame creation 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. ### Mint button won't work in the validator, to test out the mint button you'll need to create a new cast Frame creation 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 [Telegram](https://t.me/rishdoteth)! # 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. ### 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 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 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 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 [here](/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 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 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 ngrok http http://localhost:3000 ``` ### 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 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 [Telegram](https://t.me/rishdoteth)! # Customize SIWN Source: https://docs.neynar.com/docs/how-to-customize-sign-in-with-neynar-button-in-your-app Customize the Farcaster sign in experience for your users ### Sign In with Neynar (SIWN) is the easiest way to let users connect their Farcaster account to your app See [here](/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: 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. # Mutual Follows/Followers Source: https://docs.neynar.com/docs/how-to-fetch-mutual-followfollowers-in-farcaster Find mutual follows with another Farcaster user ### This guide refers to [this API](/reference/fetch-relevant-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 // 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 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 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 const mutualFollowings = rishFollowings.filter((following) => mananFollowers.some((follower) => follower.fid === following.fid) ); console.log(mutualFollowings); ``` Example output: ```json [ { 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" } // ... ] ``` 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. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) 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. ### 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 npm install @neynar/nodejs-sdk ``` ### Fetching User Balances First, set up the Neynar client using your API key. ```javascript javascript import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; const config = new Configuration({ apiKey: process.env.NEYNAR_API_KEY, }); const client = new NeynarAPIClient(config); ``` Use the `fetchUserBalance` method to retrieve the token balances for a user by their FID. ```javascript javascript 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 { "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. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com/) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Warpcast URLs Source: https://docs.neynar.com/docs/how-to-get-cast-information-from-warpcast-url Convert Warpcast URL into full cast data from Farcaster network through Neynar ### Related API: [Lookup cast by hash or URL](/reference/lookup-cast-by-hash-or-warpcast-url) Warpcast cast url doesn't contain all the full cast hash value, it usually looks like this: `https://warpcast.com/dwr.eth/0x029f7cce`. This guide demonstrates how to fetch cast information from Warpcast 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 // 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 // @dwr.eth AMA with @balajis.eth on Farcaster const url = "https://warpcast.com/dwr.eth/0x029f7cce"; const cast = await client.lookupCastByHashOrWarpcastUrl({ identifier: url, type: CastParamType.Url, }); console.log(cast); ``` Example output: ```json { 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 // full hash of the warpcast.com/dwr.eth/0x029f7cce const hash = "0x029f7cceef2f0078f34949d6e339070fc6eb47b4"; const cast = (await client.lookupCastByHashOrWarpcastUrl({ identifier: hash, type: CastParamType.Hash, })); console.log(cast); ``` Which will return the same result as above. ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Trending Feed on Farcaster Source: https://docs.neynar.com/docs/how-to-get-trending-casts-on-farcaster Show casts trending on the Farcaster network through Neynar ### Related API reference [here](/reference/fetch-trending-feed) 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 // 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 const feed = await client.fetchFeed({ feedType: FeedType.Filter, filterType: FilterType.GlobalTrending, limit: 1, }) console.log(feed); ``` Example output: ```json { "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 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! ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) 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, [click here](https://github.com/neynarxyz/farcaster-examples/tree/main/wownar-react-sdk#how-to-securely-implement-write-actions). 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 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 [Telegram](https://t.me/rishdoteth)! # How to Ingest Source: https://docs.neynar.com/docs/how-to-ingest Simple guide on how to ingest parquet files for Farcaster data ### 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 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` 1. Install amazon’s command line tool: ```bash cURL brew install awscli parquet-cli ``` 1. Configure amazon’s command line tool: ```bash cURL aws configure --profile neynar_parquet_exports ``` ```bash cURL 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 export AWS_PROFILE=neynar_parquet_exports ``` 1. List all the archive exports: ```bash cURL aws s3 ls s3://tf-premium-parquet/public-postgres/farcaster/v2/full/ ``` You’ll see some output that will look something like this (the timestamps will likely be different): ```bash cURL 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 aws s3 ls s3://tf-premium-parquet/public-postgres/farcaster/v2/incremental/ ``` ```bash cURL 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 aws s3 ls s3://tf-premium-parquet/public-postgres/farcaster/v2/incremental/ | grep "\-1712685900\-1712686200" ``` 1. Download a specific file: ```bash cURL aws s3 cp \ s3://tf-premium-parquet/public-postgres/farcaster/v2/incremental/farcaster-fids-1712685900-1712686200.parquet \ ~/Downloads/farcaster-fids-1712685900-1712686200.parquet ``` 1. Download all the tables for a specific time range: ```bash cURL aws s3 cp s3://tf-premium-parquet/public-postgres/farcaster/v2/incremental/ ~/Downloads/ \ --recursive \ --exclude "*" \ --include "*-1712685900-1712686200.parquet" ``` 1. Use the parquet cli: ```bash cURL parquet --help ``` 1. Check some data: ```bash cURL parquet head ~/Downloads/farcaster-fids-0-1711678800.parquet ``` ```bash cURL {"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} ``` # SIWN: Connect Farcaster accounts Source: https://docs.neynar.com/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn Connect for free using Sign In with Neynar (SIWN). The app gets read and/or write access, Neynar pays for onchain registration. ## 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 ## How to integrate SIWN? ### 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 ``` ### Example above is for web. See [here](/docs/react-implementation) for react and [here](/docs/sign-in-with-neynar-react-native-implementation) for react native. 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 Query real time Farcaster data for your data analyses, create and share dashboards ## Neynar Farcaster SQL playground ### Available at [data.hubs.neynar.com](https://data.hubs.neynar.com/) ## Subscription If you don’t have access yet, subscribe at [neynar.com](https://neynar.com) . *Please reach out to rish on [Telegram](http://t.me/rishdoteth) or [Farcaster](http://warpcast.com/rish) with feedback, questions or to ask for access* ## Schema You can always get the latest schema from the database directly by running this query ```sql SQL SELECT table_name, column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position; ``` If you give chatgpt the table schema and tell it what you want, it’ll write the sql query for you! Schema as of Nov 21, 2024 is [here](https://neynar.notion.site/Public-postgres-schema-145655195a8b80fc969cc766fbcde86b?pvs=4). We recommend you get the latest schema when working with an LLM agent. ## Overview * Query any Farcaster data in the playground * SQL access is also available over API, check your Redash profile for your API key. This is a separate API key for SQL only *(not the same key as our read and write APIs)* Neynar SQL Playground ## SQL over API * Documentation on how to use SQL over API is [**here**](https://redash.io/help/user-guide/integrations-and-api/api) ## Notes on the database Data is more raw compared to our APIs, please let us know if any particular views would be useful; we encourage API use instead of SQL if you’re building clients. You will likely need to join different tables when using 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 Redash UI automatically converts *bytea* data to hex format. However, when writing sql queries, you have to do the conversion yourself e.g. * bytea to hex ```sql SQL select ENCODE(hash, 'hex') as hash from casts limit 1 ``` * hex to bytea ```sql SQL select * from casts where hash = DECODE('hex_hash_without_0x', 'hex') ``` (swap `hex_hash_without_0x` with the actual cast hash minus the \`0x) # 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: 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. ### 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 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 { 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 [Telegram](https://t.me/rishdoteth)! # 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. 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 * Add the fid of the user whose feed you want to get * 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** * Set the query parameters to the following * 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** * Set the query parameters in the explorer 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 Fetch channel feed: # Verify Webhooks 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 { "Content-Type": "application/json", "X-Neynar-Signature": "6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39" } ``` ## Verification Process 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 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 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) # HTML & OpenGraph Metadata in Mini Apps Source: https://docs.neynar.com/docs/html-metadata-in-frames-and-catalogs Neynar's API now supports HTML metadata for mini apps (prev. called Frames) and catalogs, providing rich information about embedded content. This feature allows you to access Open Graph (OG) data and oEmbed information for frames and catalogs, similar to how it works with casts. ## Overview HTML metadata includes two main components: Standard metadata used across the web to describe content, including title, description, images, and more. A format for allowing embedded content from one site to be displayed on another site. ## Accessing HTML Metadata When retrieving frames or catalogs through the API, you can now access the `html` property which contains all the metadata associated with the frame or catalog URL. ### Example Response ```json JSON { "html": { "title": "Example Frame", "description": "This is an example frame with metadata", "image": "https://example.com/image.jpg", "url": "https://example.com/frame", "oembed": { "type": "rich", "version": "1.0", "title": "Example Frame", "author_name": "Example Author", "provider_name": "Example Provider", "html": "", "width": 600, "height": 400 } } } ``` ## Metadata Types ### Open Graph Properties The Open Graph properties available include: * `title`: The title of the content * `description`: A description of the content * `image`: URL to an image representing the content * `url`: The canonical URL of the content * `site_name`: The name of the site * And many other standard OG properties ### oEmbed Types The oEmbed data can be one of four types: General embeddable content with HTML Video content with playback capabilities Image content Simple link content Each type has specific properties relevant to that media type. ## Use Cases HTML metadata in frames and catalogs enables: * Rich previews of frame content in applications * Better understanding of frame content without loading the full frame * Enhanced display of catalog entries with proper titles, descriptions, and images * Improved accessibility for frame content ## Implementation Notes * HTML metadata is automatically extracted when frames and catalogs are processed * No additional parameters are needed to retrieve this data * The metadata follows standard Open Graph and oEmbed specifications This feature makes it easier to build rich, informative interfaces that display frame and catalog content with proper context and visual elements. # Username Search Source: https://docs.neynar.com/docs/implementing-username-search-suggestion-in-your-farcaster-app Show good recommendations when users search for Farcaster users in your app ### This guide refers to [Search for Usernames](/reference/search-user) API. If you have a Farcaster React app, chances are your users will want to search for other users. This guide demonstrates how to implement user search recommendations in your Farcaster React app 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. Here's what the username search recommendation looks like: Username search We'll see the entire React component, and we'll dissect it afterwards. ```jsx JSX 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); import { useState, useEffect } from "react"; const App = () => { const [searchTerm, setSearchTerm] = useState(""); const [users, setUsers] = useState([]); useEffect(() => { const q = searchTerm; const limit = 10; const fetchUsers = async () => { try { const data = await client.searchUser({q, limit}); setUsers(data.result.users); } catch (error) { console.error("Error fetching users:", error); } }; if (searchTerm.length > 0) { // to prevent searching with an empty string fetchUsers(); } }, [searchTerm]); // This will trigger the useEffect when searchTerm changes return (
setSearchTerm(e.target.value)} />
    {users.map((user) => (
  • {user.username} - {user.display_name}
  • ))}
); }; export default App; ```
Alright, now that we've seen the React component, time to go through it slowly. We're using the [useState](https://react.dev/reference/react/useState) hook to keep track of the search term and the users we get back from the API. ```jsx JSX const [searchTerm, setSearchTerm] = useState(""); const [users, setUsers] = useState([]); ``` We're using the [useEffect](https://react.dev/reference/react/useEffect) hook to fetch the users when the search term changes. The API reference can be found [here](/reference/search-user). ```jsx JSX useEffect(() => { const q = searchTerm; const limit = 10; const fetchUsers = async () => { try { const data = await client.searchUser({q, limit}); setUsers(data.result.users); } catch (error) { console.error("Error fetching users:", error); } }; if (searchTerm.length > 0) { // to prevent searching with an empty string fetchUsers(); } }, [searchTerm]); // This will trigger the useEffect when searchTerm changes ``` This input field listens to changes and updates the search term accordingly, thus triggering the `useEffect` hook. ```jsx JSX setSearchTerm(e.target.value)} /> ``` We're using the `map` function to iterate over the users and display them in a list. This list will be updated when the `users` state changes, which happens after the API call, which happens when the search term changes. ```jsx JSX
    {users.map((user) => (
  • {user.username} - {user.display_name}
  • ))}
```
That's it, you can now implement user search recommendations inside your Farcaster app! ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Indexer Service Source: https://docs.neynar.com/docs/indexer-service-pipe-farcaster-data Pipe Farcaster data, or subsets of it, directly into your db ### Reach out for setup and pricing A service that reads real-time data from hubs and indexes it into your Postgres database. ### **Benefits** * Full control over a Farcaster dataset that is synced in real-time * custom indexes, derivative tables, and custom APIs * No need to maintain a hub * No need to maintain an indexer with new protocol updates * Neynar handles all protocol changes for newly available data ### **Requirements** See [Requirements for indexer service](/docs/requirements-for-indexer-service) ### **Steps** * **Contact for setup** * Reach out to [@rish](https://warpcast.com/rish) or [@manan](https://warpcast.com/manan) on Farcaster * **Backfill** * Once Neynar receives the credentials from you, we will verify access to the database and permissions. * We will set up the schema and start the backfill process. * Expect 24-48 hours for the backfill to complete * **Livestream indexing** * Post backfill, all data will be indexed from the live stream from the hub ### **Notes** * We read data from hubs directly, and hubs can differ from Warpcast from time to time: see [here](https://blog.neynar.com/understanding-message-propagation-on-farcaster-mainnet) for more on this topic * By default, we pipe data in this schema: [link](https://docs.dune.com/data-catalog/community/farcaster/overview) , reach out if you want subsets of data or custom schemas ### **Questions** For any questions, message [@manan](https://warpcast.com/~/inbox/191-3253) or [@rish](https://warpcast.com/~/inbox/194-3253) on Warpcast # Write Data with Managed Signers Source: https://docs.neynar.com/docs/integrate-managed-signers Write to Farcaster protocol and let Neynar manage your signers for you ### 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. 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! ### To get started immediately, you can clone this [GitHub repository](https://github.com/neynarxyz/farcaster-examples/tree/main/managed-signers) Run the app as per the readme page in the repo ## Context This guide covers setting up a Next.js backend server to create and use Neynar Managed Signers for publishing a cast on Farcaster. The backend will: 1. Create a signer for the user. 2. Generate signature. 3. Provide API routes for frontend interactions. 4. Allow publishing casts on behalf of authenticated users. You can integrate this backend with a compatible frontend to enable a seamless authentication and authorization flow using Neynar Managed Signers. ## 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! ## Requirements 1. **Node.js** ([LTS recommended](https://nodejs.org/en/download)) ## Installation 1. **Next.js** ```bash npx create-next-app@latest neynar-managed-signers && cd neynar-managed-signers ``` 2. **Dependencies** (to install via `npm`, `yarn`, etc.): ```bash npm npm i @farcaster/hub-nodejs @neynar/nodejs-sdk viem ``` ```bash yarn yarn add @farcaster/hub-nodejs @neynar/nodejs-sdk viem ``` 3. **Environment Variables**: Create a `.env.local` file in your Next.js project root and add: ```bash NEYNAR_API_KEY=... FARCASTER_DEVELOPER_MNEMONIC=... ``` * **NEYNAR\_API\_KEY**: Get from [Neynar Dashboard](https://dev.neynar.com/app). * **FARCASTER\_DEVELOPER\_MNEMONIC**: The mnemonic for your developer account on Farcaster. e.g. `@your_company_name` account on Farcaster (to state the obvious out loud, you won't need user mnemonics at any point) ## Directory Structure Make the following directory structure in your codebase ```bash └── app ├── api │ ├── signer │ │ └── route.ts │ └── cast │ └── route.ts └── ... └── lib └── neynarClient.ts └── utils ├── getFid.ts └── getSignedKey.ts └── .env.local └── ... ``` ## 1. `lib/neynarClient.ts` Copy the following into a file called `lib/neynarClient.ts` ```typescript Typescript 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; ``` ## 2. `utils/getFid.ts` Copy the following code into a file called `utils/getFid.ts` ```typescript Typescript 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); const { user: farcasterDeveloper } = await neynarClient.lookupUserByCustodyAddress({ custodyAddress: account.address, }); return Number(farcasterDeveloper.fid); }; ``` ## 3. `utils/getSignedKey.ts` Copy the following code into a file called `utils/getSignedKey.ts` ```typescript Typescript 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); const deadline = Math.floor(Date.now() / 1000) + 86400; // 24 hours 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 `createSigner` 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 register the signedKey. `registerSignedKey` returns `signer_approved_url` that needs to be handled (More on this in step 7) ## 4. `app/api/signer/route.ts` Copy the following code into `app/api/signer/route.ts` ```typescript Typescript 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) { console.error(error); return NextResponse.json({ error: "An error occurred" }, { status: 500 }); } } ``` ## 5. `app/api/cast/route.ts` Copy the following code into `app/api/cast/route.ts` ```typescript Typescript 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 }); } } ``` ## 6. Run the app Make sure nothing is running on port 3000. Ensure that your `.env.local` file is correctly set up before running the application. ```bash Shell yarn run dev ``` Now, the app is ready to serve requests. You can build a frontend to handle these requests, but for this demo, we'll use cURL. ## 7. cURL `/api/signer` ```bash Shell curl -X POST http://localhost:3000/api/signer \ -H "Content-Type: application/json" ``` Response ```json JSON { "signer_uuid": "1234-abcd-5678-efgh", "public_key": "0xabcdef1234567890...", "status": "pending_approval", "signer_approval_url": "https://client.warpcast.com/deeplinks/signed-key-request?token=0xf707aebde...d049" } ``` You can either store the `signer_uuid` in the database or [use fetchSigners api to fetch signers for the user](/docs/fetch-signers-1) (We recommend the latter method) Convert `signer_approved_url` to a QR code (For testing, you can use any online tool, e.g., [QRFY](https://qrfy.com/)) If the user is using the application on desktop, then ask the user to scan this QR code. If the user is on mobile, ask them to click the link. This will deeplink the user into Warpcast, and they will see the following screenshot. Signer approval The user will need to pay for this on-chain transaction. (If you don't want users to pay, [you can sponsor it yourself or let neynar sponsor the signer](/docs/two-ways-to-sponsor-a-farcaster-signer-via-neynar)) ## 8. cURL `/api/cast` ```bash Shell curl -X POST http://localhost:3000/api/cast \ -H "Content-Type: application/json" \ -d '{ "signer_uuid": "1234-abcd-5678-efgh", "text": "Hello Farcaster!" }' ``` Response ```json JSON { "success": true, "cast": { "hash": "0xcda4f957b4c68883080f0daf9e75cea1309147da", "author": { "object": "user", "fid": 195494, "username": "rishav", "display_name": "Neynet tester 1/14", "pfp_url": "https://cdn-icons-png.freepik.com/256/17028/17028049.png?semt=ais_hybrid", "custody_address": "0x7355b6af053e5d0fdcbc23cc8a45b0cd85034378", "profile": { "bio": { "text": "12/19" }, "location": { "latitude": 36.2, "longitude": 138.25, "address": { "city": "Nagawa", "state": "Nagano Prefecture", "country": "Japan", "country_code": "jp" } } }, "follower_count": 6, "following_count": 50, "verifications": [], "verified_addresses": { "eth_addresses": [], "sol_addresses": [] }, "verified_accounts": [ { "platform": "x", "username": "unverified" }, { "platform": "github", "username": "unverified" } ], "power_badge": false }, "text": "Hello Farcaster!" } } ``` ### Read more [here](/docs/two-ways-to-sponsor-a-farcaster-signer-via-neynar) to see how to *sponsor a signer* on behalf of the user ## Conclusion With this backend setup, your Next.js app can: * Generate Neynar Managed Signers for a single app. (`@your_company_name` account) * Provide API routes for frontend interactions to handle signers and publishing casts. This backend should be integrated with the corresponding frontend to enable a seamless login and cast publishing experience. For the completed code with frontend intergation, check out the [GitHub repository](https://github.com/neynarxyz/farcaster-examples/tree/main/managed-signers). If you encounter any issues, reach out to the [Neynar team for support](https://t.me/rishdoteth). # Like & Recast Source: https://docs.neynar.com/docs/liking-and-recasting-with-neynar-sdk Add "like" and "recast" reactions on Farcaster casts * 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 how to like or recast a cast 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 // 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 const hash = "0x6932a9256f34e18892d498abb6d00ccf9f1c50d6"; client.publishReaction({ signerUuid: signer, reactionType: "like", target:hash }); ``` Recasting works the same way, replace "like" with "recast": ```javascript Javascript const hash = "0x6932a9256f34e18892d498abb6d00ccf9f1c50d6"; client.publishReaction({ signerUuid: signer, reactionType: "like", target:hash }); ``` The response status code should return a 200 status code. To verify that the reaction was published, you can fetch the cast's reactions: ```javascript Javascript const types = ReactionsType.All; const reactions = await client.fetchCastReactions({ hash, types: [types] }); console.log(reactions); ``` Which would print out ```json { "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 works on Farcaster, read [here](/docs/write-to-farcaster-with-neynar-managed-signers) ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Listen for @bot Mentions Source: https://docs.neynar.com/docs/listen-for-bot-mentions Get notified when someone tags your bot in a cast The easiest way to listen for when your bot gets tagged in a cast is to set up a webhook in the Neynar developer portal and integrate it into your bot codebase. ### ngrok is having issues with webhook deliveries. Even though this guide uses ngrok, we suggest you use your own domain or a provider like cloudflare tunnels. ## Set up a webhook for your bot on Neynar Dev portal 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). #### Get events when your bot gets tagged in a cast e.g. `@bot` Click on the new webhook and enter the `fid` of your bot in the `mentioned_fids` field for a `cast.created` filter. What this does is anytime a cast is created on the protocol, it checks if your bot that has that `fid` is *mentioned* in the cast. If it is, it fires a webhook event to your backend. #### Get events when someone replies to your bot In the same webhook as above, insert the `fid` of your bot in the `parent_author_fids` field. See screenshot below. This will fire an event for whenever someone casts a reply where your bot is the *parent cast's author*. You will notice that the same webhook now has two filters for the `cast.created` event. This is because webhook filters are logical `OR` filters meaning that the event will fire if any one of the conditions are fulfilled. In this case, the webhook server will notify your backend if someone * tags your bot i.e. `mentioned_fids` filter * replies to your bot i.e. `parent_author_fids`filter #### Expand as needed If you build more than one bot, you can continue adding those fids to these fields in comma separated format and you will get webhook events for any of the bots (or users, doesn't have to be bots). Now let's move on to processing the events you receive on your backend. ## Receive real time webhook events on your backend #### Setting up a POST url Your backend needs a POST url to listen for incoming webhook events. The webhook will fire to the specified `target_url`. For the purpose of this demo, we used [ngrok](https://ngrok.com/) to create a public URL. You can also use a service like [localtunnel](https://theboroer.github.io/localtunnel-www/) that will forward requests to your local server. **Note** that *free endpoints like ngrok, localtunnel, etc. usually have issues because service providers start blocking events. Ngrok is particularly notorious for blocking our webhook events.* This is best solved by using a url on your own domain. #### Server to process events received by the POST url Let's create a simple server that logs out the event. We will be using [Bun JavaScript](https://bun.sh). ```javascript Javascript 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 to receive all casts that mention `@neynar` in the text by putting in @neynar's `fid`: 6131. Now the server will log out the event when it is fired. It will look something like this: ```javascript Javascript { 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: "@neynar LFG", timestamp: "2024-02-15T19:23:22.000Z", embeds: [], reactions: { likes: [], recasts: [], }, replies: { count: 0, }, mentioned_profiles: [], }, } ``` These events will be delivered real time to your backend as soon as they appear on the protocol. If you see a cast on a client but you haven't received it on your backend: 1. make sure you're not using ngrok or a similar service, use your own domain. 2. check the cast hash/url on [https://explorer.neynar.com](https://explorer.neynar.com) to see where it's propagated on the network. If it hasn't propagated to the hubs, the network doesn't have the cast and thus the webhook didn't fire. 3. make sure your backend server is running to receive the events. Once you receive the event, return a `200` success to the webhook server else it will keep retrying the same event delivery. ## Create webhooks programmatically Now that you know how to set up webhooks manually on the dev portal, you might be wondering how to create them dynamically. You can do so using our [Webhook](/reference/lookup-webhook) APIs. They will allow you to create, delete or update webhooks. Few things to remember when creating webhooks programmatically: 1. You can add an almost infinite number of filters to the same webhook so no need to create new webhooks for new filters. 2. Filters are overwritten with new filters. So for e.g. if you are listening to mentions of fid 1 and now you want to listen to mentions of fid 1 and 2, you should pass in both 1 and 2 in the filters when you update. ## You're ready to build! That's it, it's that simple! 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 [Telegram](https://t.me/rishdoteth)! # Make Agents Prompt Transactions Source: https://docs.neynar.com/docs/make-agents-prompt-transactions Agents prompt transactions to humans with Farcaster frames ### Related API reference: [Create transaction pay frame](/reference/create-transaction-pay-frame) ## Agents need to transact To become full digital citizens of our digital universes, agents need to transact with other agents and humans. A large portion of such transactions will happen onchain. Today, it's hard to AI agents to prompt humans to transact with them. They can * set up a full app on a webpage (takes time and effort) * link out to a contract address (bad UX) * tell the user what to do in text prose (confusing) We are changing that with Farcaster frames. Developers can now *dynamically* generate transaction frames on the fly and prompt a transaction as part of the cast! ## Creating a transaction frame ### Pay transactions We are starting with simple pay transactions where the agent can prompt the user to pay for a certain action. To do so, agent (developers) can use the [Create transaction pay frame](/reference/create-transaction-pay-frame). It takes in 1. Transaction object with details of the receiver, network (limited to Base to start), token contract and amount 2. Configuration object that allows configuring what the frame page should show e.g. the line item for the transaction 3. Action object to configure the primary CTA of the frame 4. It even lets you allowlist a certain list of FIDs if your agent isn't open to transacting with everyone Your API request will look like below: ```javascript Javascript curl --request POST \ --url https://api.neynar.com/v2/farcaster/frame/transaction/pay \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --header 'x-api-key: NEYNAR_API_DOCS' \ --data ' { "transaction": { "to": { "network": "base", "address": "0x5a927ac639636e534b678e81768ca19e2c6280b7", "token_contract_address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "amount": 0.01 } }, "config": { "line_items": [ { "name": "eth is down", "description": "lets bring it back up", "image": "https://i.imgur.com/E12sUoO_d.webp?maxwidth=1520&fidelity=grand" } ], "action": { "text": "take some eth", "text_color": "#FFFFFF", "button_color": "#000000" } } } ' ``` and it will return a response that contains a frame URL: ```javascript Javascript { "transaction_frame": { "id": "01JP3SQS2R2YQ6FAJGH3C5K5HB", "url": "https://app.neynar.com/frame/pay/01JP3SQS2R2YQ6FAJGH3C5K5HB", "type": "pay", "app": { "name": "readme.com", "logo_url": "https://cdn-icons-png.flaticon.com/512/2815/2815428.png" }, "transaction": { "to": { "amount": 0.01, "address": "0x5a927ac639636e534b678e81768ca19e2c6280b7", "network": "base", "token_contract_address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913" } }, "config": { "action": { "text": "take some eth", "text_color": "#FFFFFF", "button_color": "#000000" }, "line_items": [ { "name": "eth is down", "image": "https://i.imgur.com/nYvX36t.png", "description": "lets bring it back up" } ] }, "status": "created" } } ``` It will dynamically generate a frame like that at the above URL: You can now cast out this frame programmatically using our [Publish Cast](/reference/publish-cast) API. You might want to save the `frame_id` for future purposes to look up details for this frame. Once you use the frame url in a cast, it will automatically create a splash embed like the following: ### Other transaction types We are starting with pay transactions and will add other transaction types shortly! ## Fetching details for an existing transaction frame If you have an existing transaction frame you made in the past, you can fetch the details for it through [Get transaction pay frame](/reference/get-transaction-pay-frame). Pass in the `frame_id` in the request and it will return frame details. # Farcaster Feed of NFT Owners Source: https://docs.neynar.com/docs/making-a-farcaster-feed-of-miladies Make a Farcaster feed showing casts from a specific set of users ### Related API reference [here](/reference/fetch-feed) This guide demonstrates how to make a feed of Farcaster casts from 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 // 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); ``` 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 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 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); ``` Lastly, fetch the feed using the FIDs. ```javascript Javascript const feedType = FeedType.Filter; const filterType= FilterType.Fids; const feed = await client.fetchFeed({feedType, filterType, fids }); console.log(feed); ``` Example output: ```json Json { casts: [ { object: "cast_hydrated", hash: "0x4b02b1ef6daa9fe111d3ce871ec004936f19b979", thread_hash: "0x4b02b1ef6daa9fe111d3ce871ec004936f19b979", parent_hash: null, parent_url: "https://veryinter.net/person", parent_author: [Object ...], author: [Object ...], text: "What'd you buy for Black Friday / Cyber Monday?\n\nI got a new webcam and bought a Roomba+mop that I'm excited to fiddle with.", timestamp: "2023-11-27T14:46:01.000Z", embeds: [], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] }, { object: "cast_hydrated", hash: "0xf1210d9eb6b21bbf3847ca5983539ed9c2baee13", thread_hash: "0xf1210d9eb6b21bbf3847ca5983539ed9c2baee13", parent_hash: null, parent_url: null, parent_author: [Object ...], author: [Object ...], text: "Great couple days mostly off the internet. 🦃🤗\n\nAlso excited to be back in the mix.\n\nWhat will be the biggest stories to end the year?", timestamp: "2023-11-27T14:44:19.000Z", embeds: [], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] }, { object: "cast_hydrated", hash: "0x7d3ad4be401c0050cf20a060ebbd108383b6357c", thread_hash: "0x7d3ad4be401c0050cf20a060ebbd108383b6357c", parent_hash: null, parent_url: "https://foundation.app", parent_author: [Object ...], author: [Object ...], text: "Consisting of 50 1/1 works, Ver Clausi's new drop Blaamius imagines life after the Anthropocene. His rich, colorful illustrations that meld subject and scenery remind me of old sci-fi comics and H.R. Giger in the best possible way. \nPrice: 0.025\nhttps://foundation.app/collection/bla-cebc", timestamp: "2023-11-27T14:29:37.000Z", embeds: [ [Object ...] ], reactions: [Object ...], replies: [Object ...], mentioned_profiles: [] } ], next: { cursor: "eyJ0aW1lc3RhbXAiOiIyMDIzLTExLTI3IDE0OjI5OjM3LjAwMDAwMDAifQ==" } } ``` Farcaster feed of Milady owners! ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Mutes, Blocks, and Bans Source: https://docs.neynar.com/docs/mutes-blocks-and-bans Hide users and their activity based on user or developer preference ## Mutes When Alice mutes Bob: * All of Bob's casts, likes, replies, and recasts are hidden from Alice in feeds, search, and reaction APIs when Alice's `viewer_fid` is specified. See the full list of endpoints below. * Notifications from Bob are hidden when fetching notifications for Alice. * Bob can still view and interact with Alice's casts. Mutes can be created or deleted via our allowlisted [Mute APIs](/reference/publish-mute). Note that Neynar mutes are separate from Warpcast mutes. Mute's are private – they are not explicitly disclosed by any of our APIs. ## Blocks When Alice blocks Bob: * Bob's casts, likes, replies, and recasts are hidden from Alice – *and vice versa* – when either user's`viewer_fid` is provided. * Both users are always hidden from each other in the [Notifications](/reference/fetch-all-notifications) based on the provided `fid`. Blocks are public and can be listed, created, and deleted via our [block list](/reference/fetch-block-list). Blocks are part of the Farcaster protocol and synced with Warpcast. ## Bans A user can be banned from a client at the discretion of the app owner. This will hide that user's account and all of their activity in the endpoints listed below for all API calls made from that app's API key. Bans are only viewable by the app owner, and can be listed, created, and deleted via our [Ban APIs](/reference/publish-bans). ## List of Endpoints The endpoints listed below respect mutes, blocks and bans. For these endpoints, the mutes and blocks apply to the optional `viewer_fid` provided. ```typescript Typescript /v2/farcaster/following /v2/farcaster/cast/conversation /v2/farcaster/feed/channels /v2/farcaster/feed/following /v2/farcaster/feed/frames /v2/farcaster/feed /v2/farcaster/feed/parent_urls /v2/farcaster/feed/trending /v2/farcaster/feed/users/replies_and_recasts /v2/farcaster/followers /v2/farcaster/followers/relevant /v2/farcaster/reactions/cast /v2/farcaster/reactions/user /v2/farcaster/cast/search /v2/farcaster/user/search /v2/farcaster/channel/followers /v2/farcaster/channel/followers/relevant ``` These endpoints apply mutes and blocks to the `fid` provided. ```typescript Typescript /v2/farcaster/notifications /v2/farcaster/notifications/channel /v2/farcaster/notifications/parent_url ``` # Developer Ecosystem Source: https://docs.neynar.com/docs/neynar-developer-ecosystem-for-farcaster Building blocks for developing on Farcaster protocol with Neynar infrastructure Tools below can help you build on Farcaster more easily ## Libraries and SDKs * [@neynar/nodejs-sdk](https://github.com/neynarxyz/nodejs-sdk) by Neynar (official library) * [@neynar/react-native-signin](https://github.com/neynarxyz/siwn/tree/main/react-native-sign-in-with-neynar) by Neynar (official library) * [farcaster-js library](https://www.npmjs.com/package/@standard-crypto/farcaster-js) for Neynar by [Standard Crypto](https://warpcast.com/gavi/0x69ab635a) * [Next-js library](https://github.com/alex-grover/neynar-next) for Neynar by [Alex Grover](https://warpcast.com/alexgrover.eth/0x3b27d4e3) * [GoLang signer generation library](https://gist.github.com/benny-conn/57c073dab01488f3107d126979ee14fd) for Neynar by [Gallery](https://warpcast.com/robin/0x629551fd) * [SDK](https://github.com/cameronvoell/fcrn) to build on React Native, fetch feed with Neynar APIs by [Cameron Voell](https://warpcast.com/cyrcus/0x8cad5b56) * [farcaster-py library](https://github.com/vrypan/farcaster-py) for using Neynar Hubs by [vrypan.eth](https://warpcast.com/vrypan.eth) ## Open source clients and bots using Neynar Hubs and APIs ### Web * [Opencast](https://opencast.stephancill.co.za/) ([github](https://github.com/stephancill/opencast/tree/main)) by [Stephan Cill](https://warpcast.com/stephancill) * [Herocast](https://app.herocast.xyz/feed) ([github](https://github.com/hellno/herocast/) by [Hellno](https://warpcast.com/hellno.eth/0xcf26ab28) * [Managed signer client](https://github.com/neynarxyz/farcaster-examples/tree/main/managed-signers) by Neynar ### Mobile * [Farcaster Expo example](https://github.com/dylsteck/farcaster-expo-example) by [Dylan Steck](https://warpcast.com/dylsteck.eth/0x9bbef5d4) * [Wownar Expo/RN example](https://github.com/neynarxyz/farcaster-examples/tree/main/wownar-react-native) by Neynar * [Farcaster Expo example](https://github.com/buidlerfoundation/farcaster-expo-example) by [Edric](https://warpcast.com/edricnguyen.eth/0x20ba4055) * [Farcaster Expo](https://github.com/mko4444/farcaster-expo) by [Matthew](https://warpcast.com/matthew/0x99a7a6b5) * [FCRN](https://github.com/cameronvoell/fcrn) by [Cameron Voell](https://warpcast.com/cyrcus/0x8cad5b56) * [React Native Farcaster Starter](https://github.com/natedevxyz/rn-farcaster-starter) by [Nate](https://warpcast.com/natedevxyz) ### Bots * [Open source repo](https://github.com/davidfurlong/farcaster-bot-template) to build bots that reply using Neynar webhooks by [David Furlong](https://warpcast.com/df) * [Follow all bot](https://github.com/wbarobinson/followall) that follows users by [Will Robinson](https://warpcast.com/dangerwillrobin) * [Frames bot](https://github.com/neynarxyz/farcaster-examples/tree/main/frames-bot) that replies with frames by Neynar ## Frames and Cast actions using Neynar frame validate ### Frames * [farcards](https://warpcast.com/~/channel/farcards) by [undefined](https://warpcast.com/undefined) ### Cast actions * [Follower count cast action](https://github.com/neynarxyz/farcaster-examples/tree/main/cast-action) by Neynar * [Bot or not](https://warpcast.com/~/channel/bot-or-not) by [Angel](https://warpcast.com/sayangel) ## Data science and analytics * [Neynar tables on Dune](https://dune.com/docs/data-tables/community/neynar/farcaster/) allowing Farcaster queries on Dune dashboards -- sample dashboards by [pixel](https://dune.com/pixelhack/farcaster), [yesyes](https://dune.com/yesyes/farcaster-users-onchain-activities) and [shoni](https://dune.com/alexpaden/farcaster-x-dune) * [Neynar SQL playground](https://neynar.notion.site/Neynar-Farcaster-SQL-playground-08ca6b55953b4d9386c59a91cb823af5?pvs=4) to query real-time Farcaster protocol data -- sample [dashboard by ghostlinkz](https://data.hubs.neynar.com/public/dashboards/U6aGGq6CQOZXIx6IO71NbaUFDMwX14nYs0OyhT88) ## Tools and debugging * [Vasco](https://vasco.wtf) Neynar and Warpcast debugging tools by [Pedro](https://warpcast.com/pedropregueiro) * [ChatGPT Neynar SQL bot](https://warpcast.com/iconia.eth/0x42ad25a9) to write analytics Neynar SQL queries for Farcaster data by [iconia](https://warpcast.com/iconia.eth/0x42ad25a9) * [ChatGPT Dune SQL bot](https://chat.openai.com/g/g-lKnQHXJKS-dune-x-farcaster-gpt) to write dune queries for Farcaster data by [iconia](https://warpcast.com/iconia.eth) * [Fario](https://github.com/vrypan/fario) Farcaster command line tools in Python on by [Vrypan](https://warpcast.com/vrypan.eth) * [CastStorage](https://caststorage.com) to check user storage by [Sam](https://warpcast.com/sammdec.eth) * [Farcaster Scraper](https://github.com/leo5imon/farcaster-scraper) to fetch, filter, and fine-tune your channel scraping by [Leo Simon](https://warpcast.com/leo5) * [ChatGPT bot to help write code](https://chat.openai.com/g/g-rSpJCtUwJ-code-caster) using Neynar APIs and Dune by [Emre](https://warpcast.com/ekinci.eth/0xf3b54700) ### APIs and hosted hubs For all the above, you can use APIs and hosted hubs from Neynar * [Neynar APIs](/reference/neynar-farcaster-api-overview) for reading and writing Farcaster data (profiles, casts, feed, etc.) * [Open source Farcaster channel data](https://github.com/neynarxyz/farcaster-channels/) including Warpcast channels -- community contributed * [Neynar hosted hubs](https://neynar.com) # Set up Neynar with Cursor and MCP server Source: https://docs.neynar.com/docs/neynar-farcaster-with-cursor Start developing on Farcaster with Neynar and AI enabled Cursor ### llms.txt * Full file [here](https://docs.neynar.com/llms-full.txt). FYI this file can be too large for LLMs on free plan, you might need to upgrade. * Abridged version [here](https://docs.neynar.com/llms.txt). ### MCP server Install by running `npx @mintlify/mcp@latest add neynar` in your terminal. The steps below will help you get started with Farcaster development in a few simple clicks: Create an empty folder on your computer e.g. `rish/Code/farcaster`. *This tutorial assumes you're starting from scratch. If you already have a build environment, your steps might vary.* Download Cursor from [cursor.com](https://www.cursor.com/) . This tutorial uses `Version: 0.43.5`, it's possible that future versions behave slightly differently. Open the `farcaster` folder you created in Cursor and then open the right chat pane. That's the icon next to the icon in the screenshot below. *This tutorial assumes you're using `claude-3.5-sonnet` as your AI model. With a different model, your results might differ.* Give it the following prompt. > I want to build a Farcaster app with Neynar. > > Neynar's openapi spec is here: @[https://github.com/neynarxyz/OAS/](https://github.com/neynarxyz/OAS/) > > Neynar's nodejs sdk is here: @[https://github.com/neynarxyz/nodejs-sdk](https://github.com/neynarxyz/nodejs-sdk) > > can you help set up the repo for me? we will use the latest version of neynar's sdk. No need to build a frontend for now, we will focus on backend only to start. Cursor should run a bunch of commands based on the above prompt and set up the directory for you already. The right pane will look something like the one below (it slightly differs on each run, so don't worry if it's different for you). At this point, your left pane should have the following directory structure (Cursor does something slightly different on each run, so don't worry if this isn't the same as this tutorial) To incorporate these changes into your repository, you can start by tapping "accept all" in the chat pane. You might need to troubleshoot this a bit, but accepting is a reasonable way to start. Insert your Neynar API key (subscribe at [neynar.com](https://neynar.com/#pricing) to get your key) in the `.env` file. Replace the placeholder with your API key directly, no quotes needed. You will need to run the backend server on your local machine. So go ahead and ask Cursor how to do that. > how do I run this? Cursor should give you a set of commands you can run from the chat pane directly. Tap "run" on each command and wait for it to finish running before moving to the next one. After running the `npm` commands above, if you run the curl commands, it should start printing results to your console! From here on, you can prompt Cursor as you need and continue to build on this foundation! If you have any questions, post them on the [/neynar](https://www.supercast.xyz/channel/neynar) channel on Farcaster. ## Troubleshooting After the above steps, you likely still have a few issues. Below, we describe the easiest way to debug with Cursor. * If you're getting errors in the terminal, you can simply click "Debug with AI" to have Cursor generate the prompt and output a solution. Alternatively, click "add to chat" and put in a better prompt yourself * Ensure that it has the correct files as context. `neynar-api-client.d.ts` needs to be added to context to suggest suitable solutions (or else it will just make things up!). You can search for the filename to add it easily. * For long files, it will remove them from context at each step and require you to re-add them. * When it outputs a solution, it will look like something below. You will notice that each code block has an "Apply" or "Run" action. * You will need to apply/run each block separately. Each apply/run action might create file changes that show up like below. If two actions occur on the same file, "accept" the first change and save the file before taking the next action. * Sometimes they also show up in this manner. Accept each change and save before trying again. * You will likely need to go back and forth with Cursor as you work through your code. While AI agents are helpful at getting the project started, they are still bad at navigating through repositories and picking the proper functions. # Tips Each time you run a command from the chat pane, Cursor opens a new terminal. You can close extra terminal windows that are *not* running your server without any adverse effects on your project. # Neynar User Score Source: https://docs.neynar.com/docs/neynar-user-quality-score Check for quality users using Neynar's user score ## What is the Neynar user score? Neynar user score is generated based on user behavior on the platform. It scores between 0 and 1 and reflects the confidence in the user being a high-quality user. Users can improve their scores by having high-quality interactions with other good users. Scores update weekly. If you want to see your score as a user, you can use the [By username](/reference/lookup-user-by-username) API, put in your username, and turn the `x-neynar-experimental` flag to *true*. ### Scores are also available onchain, see [Address \<> user score contract](/docs/address-user-score-contract) ## Interpreting the score You can see a distribution of users across score ranges on this [dashboard](https://data.hubs.neynar.com/dashboards/51-neynar-score-distribution). A screenshot from Dec 5, 2024 is below. Neynar user score distribution **We recommend starting with a threshold around 0.5 and then changing up or down as needed.** As of Dec 5, 2024, there are: * \~2.5k accounts with 0.9+ scores * \~27.5k accounts with 0.7+ scores \_Hence, starting with a very high threshold will restrict the product to a tiny user cohort. \_Developers should assess their own thresholds for their applications (Neynar does not determine thresholds in other apps). Scores update at least once a week, so new users might take a few days to show an updated score. If the user has not been active for a while, their scores will be reduced. ## Getting the score on webhook events If you're using Neynar webhooks to get data on your backend, you might want to separate high-quality data from low-quality data. A simple way to do this is to look at the `neynar_user_score` inside each user object. ```json user: { fid: 263530, object: "user", pfp_url: "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/68c1cd39-bcd2-4f5e-e520-717cda264d00/original", profile: { bio: { text: "Web3 builder" } }, username: "m00n620", power_badge: false, display_name: "tonywen.base.eth", experimental: { neynar_user_score: 0.9 // THIS IS THE SCORE }, verifications: [ "0xc34da1886584aa1807966c0e018709fafffed143" ], follower_count: 29, custody_address: "0x22c1898bddb8e829a73ca6b53e2f61e7f02a6e6d", following_count: 101, verified_accounts: null, verified_addresses: { eth_addresses: [ "0xc34da1886584aa1807966c0e018709fafffed143" ], sol_addresses: [] } } ``` ## Fetching the score on API calls If you're using APIs, you can get the same score as part of the user objects by passing in`x-neynar-experimental` boolean in the header. See the screenshot below of [User by FIDs](/reference/fetch-bulk-users) for example. Neynar user score in API response Turning on this boolean flag will return the same score in the API or SDK response. The same can be done when fetching users [By Eth or Sol addresses](/reference/fetch-bulk-users-by-eth-or-sol-address). If looking to restrict activity on your contract to a specific cohort of users, you can run their address against this API to fetch their score and then allow them to take actions as appropriate. ## Pulling the scores from hosted SQL [Neynar SQL playground](/docs/how-to-query-neynar-sql-playground-for-farcaster-data) has user scores available and you can pull from there for larger analysis as needed. [Reach out](https://t.me/rishdoteth) if you don't yet have access to it. Neynar user score in SQL ## Report errors If you know a score misrepresents a user, that's helpful information we can use to label our data. Please send feedback to `@rish` on [Warpcast DC](https://warpcast.com/rish) or [Telegram DM](https://t.me/rishdoteth) . # Webhooks with DCs Source: https://docs.neynar.com/docs/neynar-webhooks-warpcast-dcs In this guide, we’ll make a webhook which will send a DC to the user based on any action they perform on Farcaster! For this guide, I'll send direct casts to people whose casts include a specific keyword. Before we begin, you can access the [source code](https://github.com/neynarxyz/farcaster-examples/tree/main/cast-action) for this guide on [GitHub Gist](https://gist.github.com/avneesh0612/9fa31cdbb5aa86c46cdb1d50deef9001). Let's get started! ### Creating the webhook To create a new webhook, head to the [neynar dashboard](https://dev.neynar.com) and go to the [webhooks tab](https://dev.neynar.com/webhook). Click on the new webhook and enter the details as such: Webhook creation 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. ### Free endpoints like ngrok, localtunnel, etc. can have issues because service providers start blocking events over a certain limit ### Creating the server Let's create a simple server that logs out the event. We will be using [Bun JavaScript](https://bun.sh). ```typescript index.ts 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 neynarDC present in the text. Now the server will log out the event when it is fired. It will look something like this: ```typescript index.ts { 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: "neynarDC", timestamp: "2024-02-15T19:23:22.000Z", embeds: [], reactions: { likes: [], recasts: [], }, replies: { count: 0, }, mentioned_profiles: [], }, } ``` ### Adding DC functionality Firstly, you need a warpcast API key to send DCs. So, head over to [https://warpcast.com/\~/developers/api-keys](https://warpcast.com/~/developers/api-keys) and create a new API key. Once you have the API key add this fetch request in your try block: ```typescript index.ts const info = await req.json(); const DCreq = await fetch("https://api.warpcast.com/v2/ext-send-direct-cast", { method: "PUT", headers: { Authorization: "Bearer ", "Content-Type": "application/json", }, body: JSON.stringify({ recipientFid: info.data.author.fid, message: "gm", idempotencyKey: uuidv4(), }), }); const res = await DCreq.json(); console.log(res); ``` Here, you need to replace `` with the api key that you generated from the Warpcast dashboard. In the request, we need to provide the FID to send the message to, the message body, and an idempotencyKey to retry if the request fails. For the `recipientFid` we are using the FID of the author of the cast and the `idempotencyKey` is a random key generated by `uuid` which we need to install and import: ```powershell Powershell bun i uuid ``` ```typescript index.ts import { v4 as uuidv4 } from "uuid"; ``` If you restart the server and cast again, it will send a DC to the account creating the cast . ## 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, 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 [Telegram](https://t.me/rishdoteth)! # From Parquet Exports Source: https://docs.neynar.com/docs/parquet Ingest farcaster data into your database or data warehouse from parquet exports ### Contact for setup and pricing ## Frequency * Full exports are created every day * Incremental exports are created every 1s or 10s depending on the tier you chose ## Ingestion See [How to ingest](/docs/how-to-ingest) Full archives: `s3://tf-premium-parquet/public-postgres/farcaster/v2/full/` Incremental archives: `s3://tf-premium-parquet/public-postgres/farcaster/v2/incremental/` ## Notifications using SNS You can subscribe to SNS to get notifications whenever a new file is added to the s3 bucket: `arn:aws:sns:us-east-1:252622759102:tf-s3-premium-parquet` ## Schema ## Notes 1. Data is exported every 30 mins and once a day. You can start by downloading a daily snapshot and then ingesting the incremental 30m files. 2. Originally timestamps were stored with nanoseconds, but as of April 23, 2024 they are all using microseconds. 3. The “messages” table is very very large and we are not currently exporting it. Please talk with us if you need the table and we can figure out the best solution for you. # Parquet Schema Source: https://docs.neynar.com/docs/parquet-schema Schema for data available in parquet ingestion ### Data is piped in this [schema](https://docs.dune.com/data-catalog/community/farcaster/overview) Details below: ```typescript Typescript % for x in data/*.parquet; do echo $x; parquet schema $x | jq; done data/farcaster-casts-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "deleted_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "timestamp", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "fid", "type": [ "null", "long" ], "default": null }, { "name": "hash", "type": [ "null", "bytes" ], "default": null }, { "name": "parent_hash", "type": [ "null", "bytes" ], "default": null }, { "name": "parent_fid", "type": [ "null", "long" ], "default": null }, { "name": "parent_url", "type": [ "null", "string" ], "default": null }, { "name": "text", "type": [ "null", "string" ], "default": null }, { "name": "embeds", "type": [ "null", "string" ], "default": null }, { "name": "mentions", "type": [ "null", "string" ], "default": null }, { "name": "mentions_positions", "type": [ "null", "string" ], "default": null }, { "name": "root_parent_hash", "type": [ "null", "bytes" ], "default": null }, { "name": "root_parent_url", "type": [ "null", "string" ], "default": null }, { "name": "id", "type": [ "null", "long" ], "default": null } ] } data/farcaster-fids-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "custody_address", "type": [ "null", "bytes" ], "default": null }, { "name": "fid", "type": [ "null", "long" ], "default": null } ] } data/farcaster-fnames-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "custody_address", "type": [ "null", "bytes" ], "default": null }, { "name": "expires_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "fid", "type": [ "null", "long" ], "default": null }, { "name": "deleted_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "fname", "type": [ "null", "string" ], "default": null } ] } data/farcaster-links-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "fid", "type": [ "null", "long" ], "default": null }, { "name": "target_fid", "type": [ "null", "long" ], "default": null }, { "name": "hash", "type": [ "null", "bytes" ], "default": null }, { "name": "timestamp", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "deleted_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "type", "type": [ "null", "string" ], "default": null }, { "name": "display_timestamp", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "id", "type": [ "null", "long" ], "default": null } ] } data/farcaster-reactions-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "deleted_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "timestamp", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "reaction_type", "type": [ "null", "long" ], "default": null }, { "name": "fid", "type": [ "null", "long" ], "default": null }, { "name": "hash", "type": [ "null", "bytes" ], "default": null }, { "name": "target_hash", "type": [ "null", "bytes" ], "default": null }, { "name": "target_fid", "type": [ "null", "long" ], "default": null }, { "name": "target_url", "type": [ "null", "string" ], "default": null }, { "name": "id", "type": [ "null", "long" ], "default": null } ] } data/farcaster-signers-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "deleted_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "timestamp", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "fid", "type": [ "null", "long" ], "default": null }, { "name": "signer", "type": [ "null", "bytes" ], "default": null }, { "name": "name", "type": [ "null", "string" ], "default": null }, { "name": "app_fid", "type": [ "null", "long" ], "default": null }, { "name": "id", "type": [ "null", "long" ], "default": null } ] } data/farcaster-storage-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "deleted_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "timestamp", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "fid", "type": [ "null", "long" ], "default": null }, { "name": "units", "type": [ "null", "long" ], "default": null }, { "name": "expiry", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "id", "type": [ "null", "long" ], "default": null } ] } data/farcaster-user_data-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "deleted_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "timestamp", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "fid", "type": [ "null", "long" ], "default": null }, { "name": "hash", "type": [ "null", "bytes" ], "default": null }, { "name": "type", "type": [ "null", "long" ], "default": null }, { "name": "value", "type": [ "null", "string" ], "default": null }, { "name": "id", "type": [ "null", "long" ], "default": null } ] } data/farcaster-verifications-1713814200-1713814500.parquet { "type": "record", "name": "schema", "fields": [ { "name": "created_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "updated_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "deleted_at", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "timestamp", "type": [ "null", { "type": "long", "logicalType": "timestamp-micros" } ], "default": null }, { "name": "fid", "type": [ "null", "long" ], "default": null }, { "name": "hash", "type": [ "null", "bytes" ], "default": null }, { "name": "claim", "type": [ "null", "string" ], "default": null }, { "name": "id", "type": [ "null", "long" ], "default": null } ] } data/farcaster-profile_with_addresses-1713975600-1713975900.parquet { "type" : "record", "name" : "schema", "fields" : [ { "name" : "fname", "type" : [ "null", "string" ], "default" : null }, { "name" : "display_name", "type" : [ "null", "string" ], "default" : null }, { "name" : "avatar_url", "type" : [ "null", "string" ], "default" : null }, { "name" : "bio", "type" : [ "null", "string" ], "default" : null }, { "name" : "verified_addresses", "type" : [ "null", "string" ], "default" : null }, { "name" : "updated_at", "type" : [ "null", { "type" : "long", "logicalType" : "timestamp-micros" } ], "default" : null }, { "name" : "fid", "type" : [ "null", "long" ], "default" : null } ] } ``` # Write Casts to Channel Source: https://docs.neynar.com/docs/posting-dank-memes-to-farcasters-memes-channel-with-neynars-sdk Write casts to a Farcaster channel with Neynar Channels are "subreddits inside Farcaster." Technically, a channel is a collection of casts that share a common parent\_url. For example, the [memes channel](https://warpcast.com/~/channel/memes) is a collection of casts that share the parent\_url `chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61`. Got a dank meme you want to share with Farcaster? This guide demonstrates how to use the Neynar SDK to post a cast to 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. Before all that, initialize Neynar client: ```javascript Javascript // npm i @neynar/nodejs-sdk import { NeynarAPIClient, Configuration } from "@neynar/nodejs-sdk"; import { FeedType } 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); const signer = process.env.NEYNAR_SIGNER; ``` Poast meme to memes channel ### 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 const memesChannelUrl = "chain://eip155:1/erc721:0xfd8427165df67df6d7fd689ae67c8ebf56d9ca61"; const memeUrl = "https://i.imgur.com/cniMfvm.jpeg"; const signerUuid =signer; const text="me irl"; const embeds = [ { url: memeUrl, }, ]; const replyTo = memesChannelUrl; const result = await client.publishCast({signerUuid, text, embeds, parent: replyTo, }); ``` Example output: ```json { hash: "0xccabe27a04b1a63a7a24a035b0ffc2a2629e1af1", author: { object: "user", fid: 4640, custody_address: "0x86dd7e4af49829b895d24ea2ab581c7c32e87332", username: "picture", display_name: "Picture", pfp_url: "https://lh3.googleusercontent.com/erYudyT5dg9E_esk8I1kqB4bUJjWAmlNu4VRnv9iUuq_by7QjoDtZzj_mjPqel4NYQnvqYr1R54m9Oxp9moHQkierpY8KcYLxyIJ", profile: { bio: [Object ...] }, follower_count: 45, following_count: 124, verifications: [], active_status: "inactive" }, text: "me irl" } ``` There you go, make sure to share your good memes with the Farcaster! Meme posted to memes channel ### Ready to start building? Get your subscription at [neynar.com](https://neynar.com) and reach out to us on [Telegram](https://t.me/rishdoteth) with any questions! # Rank High Quality Conversations Source: https://docs.neynar.com/docs/ranking-for-high-quality-conversations Neynar APIs rank high quality casts higher Neynar abstracts away ranking challenges across most of its APIs e.g. [Trending Feed](/reference/fetch-trending-feed) and [Conversation for a cast](/reference/lookup-cast-conversation). ## Feed There are a few different ways to rank casts for users. * [For you](/reference/fetch-feed-for-you) feed provides a ranked, personalized, *for you* feed for a given user * [Trending casts](/reference/fetch-trending-feed) feed provides trending casts globally or filtered to a channel. In this case, you can choose a provider of your choice - `neynar`, `openrank`, `mbd`. Neynar serves ranking from other providers without any changes. ## Conversation [Conversation for a cast](/reference/lookup-cast-conversation) API allows developers to fetch a conversation thread for a given cast. In addition, it's possible to: * rank casts in the conversation by order of quality by changing the `sort_type` parameter * hide low quality conversations below the fold by setting the `fold` param to *above* or *below*. Not passing in a param shows the full conversation without any fold. Rank high quality conversations All put together, this makes it possible to serve high quality information to users. If you've ideas on how we can improve ranking further, please message rish on [Warpcast DC](https://warpcast.com/rish) or [Telegram DM](https://t.me/rishdoteth) . # SIWN: React Source: https://docs.neynar.com/docs/react-implementation In this guide, we'll take a look at how to implement sign-in with neynar in a React app. For this guide, I am going to be using next.js but the same would work for CRA, remix, or anything based on react! For this guide, we'll go over: 1. Setting up auth using the Neynar React SDK 2. Using the signer to create casts Before we begin, you can access the [complete source code](https://github.com/neynarxyz/farcaster-examples/tree/main/wownar-react-sdk) 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 npx create-next-app app-name ``` You can choose the configuration based on your personal preference, I am using this config for the guide: Once the app is created, install the packages that we are going to need for the command: ```shell npm npm i @neynar/react @neynar/nodejs-sdk axios ``` ```shell yarn yarn add @neynar/react @neynar/nodejs-sdk axios ``` ```shell bun bun add @neynar/react @neynar/nodejs-sdk axios ``` Install peer dependencies for `@neynar/react` ```shell npm npm i @pigment-css/react@^0.0.30 hls.js@^1.5.20 react@^19.0.0 react-dom@^19.0.0 swr@^2.3.2 ``` ```shell yarn yarn add @pigment-css/react@^0.0.30 hls.js@^1.5.20 react@^19.0.0 react-dom@^19.0.0 swr@^2.3.2 ``` ```shell bun bun add @pigment-css/react@^0.0.30 hls.js@^1.5.20 react@^19.0.0 react-dom@^19.0.0 swr@^2.3.2 ``` Once the dependencies are installed you can open it in your favourite and we can start working on adding sign-in with neynar! ### Adding the sign-in button Head over to the `layout.tsx` file and wrap your app in a `NeynarContextProvider` like this: ```typescript layout.tsx "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`. ### 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 "use client"; import { NeynarAuthButton } from "@neynar/react"; export default function Home() { 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! Now that our sign-in button is working let's use the signer we get to publish casts! ### Using the signer UUID to publish casts In the `page.tsx` file access the user data using the `useNeynarContext` hook like this: ```typescript page.tsx const { user } = useNeynarContext(); ``` The user object contains various information like the username, fid, display name, pfp url, signer uuid, etc. Then, we can use this user object to check whether the user has signed in and display a screen conditionally like this: ```typescript page.tsx "use client"; import { NeynarAuthButton, useNeynarContext } from "@neynar/react"; import Image from "next/image"; export default function Home() { const { user } = useNeynarContext(); return (
{user && ( <>
{user.pfp_url && ( User Profile Picture )}

{user?.display_name}

)}
); } ```
Now, let's add a text area and a cast button here like this: ```typescript page.tsx {user && ( <>
{user.pfp_url && ( User Profile Picture )}

{user?.display_name}