# 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 in [Neynar User Quality Score](/docs/neynar-user-quality-score) User scores are particularly useful if anonymous addresses are interacting with your contract and you want to restrict interaction to high quality addresses. Neynar already supports user quality scores offchain (read more in [Neynar User Quality Score](/docs/neynar-user-quality-score)), this brings them onchain and makes it available to smart contracts. Now, on the Base Mainnet and Sepolia testnet, smart contracts can query the fid linked to any ETH address and the quality score for that FID. ## Contract | **Chain** | **Address** | **Deploy Transaction** | | ------------ | ------------------------------------------ | ------------------------------------------------------------------ | | Base Mainnet | 0xd3C43A38D1D3E47E9c420a733e439B03FAAdebA8 | 0x059259c15f660a4b5bd10695b037692654415f60e13569c7a06e99cfd55a54b0 | | Base Sepolia | 0x7104CFfdf6A1C9ceF66cA0092c37542821C1EA50 | 0xfdf68b600f75b4688e5432442f266cb291b9ddfe2ec05d2fb8c7c64364cf2c73 | * Read the Proxy Contract on the Base Explorer ([link](https://basescan.org/address/0xd3C43A38D1D3E47E9c420a733e439B03FAAdebA8#readProxyContract)). This is the upgradeable proxy contract you should use. * User score code on the Base Explorer ([link](https://basescan.org/address/0xd3C43A38D1D3E47E9c420a733e439B03FAAdebA8#code)). This is an upgradeable implementation contract. There is no state here. This is the code that the proxy contract is currently using. ## Interface ```solidity Solidity // 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 # Mini App Hosts Notifications Source: https://docs.neynar.com/docs/app-host-notifications Token management and inbound webhook handling for apps that host mini apps ## Overview Farcaster client applications can host mini apps. Allowing those mini apps to push notifications to users requires the host to generate user notifcation tokens, send signed messages to the mini-apps webhook, and accept notifications via webhook. Neynar provides a system to manage this complexity on behalf of the client app (mini app host). ## Basic flow In the following example we'll imagine * "Hostcaster" - a Farcaster client that wants to support mini apps * "Cowclicker" - a mini app ### Enabling Notfications 1. User wants to enable notifications for Cowclicker 2. Hostcaster posts a "notifications\_enabled" event to Neynar 3. Neynar generates a unique token, bundles it in a signed message, and posts it to Clowclicker's webhook URL ### Sending Notifications 1. Cowclicker wants to send a notification to a Hostcaster user for whom they have a valid notification token 2. Cowclicker sends the notification event to Neynar's webhook including the token 3. Neynar validates the token, hydrates some data, and queues the notification for Hostcaster 4. Hostcaster listens to a Kafka topic and/or polls an API for a user's notifications ## Message signing An event sent to a mini-app webhook must be a signed JFS messages. There are two supported signing approaches in this system. Hostcaster can sign the message with a user's key if they have the ability to do so. Or, if Hostcaster instead uses the Neynar-hosted signer system then they can provide their signer\_uuid when posting the unsigned event. ### Self-sign route 1. [GET /v2/farcaster/app\_host/user/event](/reference/app-host-get-event) to retrieve the message to be signed. This is particularly important for `notification_enabled` events because this is when a notification token is generated by Neynar 2. Sign the message, serialize the entire bundle per JFS spec 3. [POST /v2/farcaster/app\_host/user/event](/reference/app-host-post-event) with the signed message ```bash cURL curl --request GET \ --url 'https://api.neynar.com/v2/farcaster/app_host/user/event?app_domain=cowclicker.gov&fid=10101&event=notifications_enabled' \ --header 'x-api-key: YOUR_KEY' ``` ...Sign and serialize the message... ```bash cURL curl --request POST \ --url https://api.neynar.com/v2/farcaster/app_host/user/event \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_KEY' \ --data '{ "app_domain": "cowclicker.gov", "signed_message": "eyJmaWZXkifQ==.eyJleI1Mjd9.nx1CPzKje4N2Bw====" }' ``` ### Neynar-signed route This approach requires only one API request: 1. [POST /v2/farcaster/app\_host/user/event](/reference/app-host-post-event) with a valid signer\_uuid and the required event data ```bash cURL curl --request POST \ --url https://api.neynar.com/v2/farcaster/app_host/user/event \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_KEY' \ --data '{ "signer_uuid": "01973000-b000-ee00-e0e0-0ee0e00e00ee", "app_domain": "cowclicker.gov", "fid": 10101, "event": "notifications_enabled" }' ``` ### Examples ## Further Reading * [Sending Notifications](https://miniapps.farcaster.xyz/docs/guides/notifications#send-a-notification) from the Farcaster mini app guide * [JSON Farcaster Signatures](https://github.com/farcasterxyz/protocol/discussions/208) # 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! # Auth Address Signature Generation Source: https://docs.neynar.com/docs/auth-address-signature-generation Generate a Signed Key Request using viem for registering auth addresses in Farcaster with Neynar This guide walks you through generating a Signed Key Request using [viem](https://viem.sh/) that need to be passed in while [registering auth address](https://docs.neynar.com/reference/register-signed-key-for-developer-managed-auth-address) ## System & Installation Requirements ### Prerequisites * Node.js >= **18.x** (LTS recommended) * npm >= **9.x** OR yarn >= **1.22.x** [Download and install node (if not installed)](https://nodejs.org/en/download) ### Initialize project (optional) ```bash mkdir signed-key-request cd signed-key-request npm init -y ``` ### Install `viem` ```bash npm install viem ``` OR with yarn: ```bash yarn add viem ``` *** ## Code Breakdown and Steps **You can find full code at the [end of this guide](#full-final-code).** The code starts by importing the necessary libraries: ```javascript Javascript import { encodeAbiParameters } from "viem"; import { mnemonicToAccount, generateMnemonic, english } from "viem/accounts"; ``` Generates a mnemonic and converts it to an Ethereum address (`auth_address`) ```javascript Javascript const mnemonic = generateMnemonic(english); const auth_address_acc = mnemonicToAccount(mnemonic); const auth_address = auth_address_acc.address; ``` Describes the EIP-712 domain (context of signature). ```javascript Javascript const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = { name: "Farcaster SignedKeyRequestValidator", version: "1", chainId: 10, verifyingContract: "0x00000000fc700472606ed4fa22623acf62c60553", }; ``` Defines the structure of the message to be signed. ```javascript Javascript const SIGNED_KEY_REQUEST_TYPE = [ { name: "requestFid", type: "uint256" }, { name: "key", type: "bytes" }, { name: "deadline", type: "uint256" }, ]; ``` Encodes `auth_address` as **32 bytes**. ```javascript Javascript const key = encodeAbiParameters( [{ name: "auth_address", type: "address" }], [auth_address] ); ``` Replace `"MNEMONIC_HERE"` with your app mnemonic phrase and fid with your app's fid. ```javascript Javascript const fid = 0; const account = mnemonicToAccount("MNEMONIC_HERE"); ``` Sets a 24-hour expiration time. ```javascript Javascript const deadline = Math.floor(Date.now() / 1000) + 86400; ``` Signs the message per EIP-712 standard. ```javascript Javascript const signature = await account.signTypedData({ domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN, types: { SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE, }, primaryType: "SignedKeyRequest", message: { requestFid: BigInt(fid), key, deadline: BigInt(deadline), }, }); ``` If you want to sponsor the auth address, you can sign the EIP-712 signature again with a basic Ethereum signature. [This route](https://docs.neynar.com/reference/register-signed-key-for-developer-managed-auth-address) needs to be sponsored if not provided then neynar will sponsor it for you and you will be charged in compute units. ```javascript Javascript const sponsorSignature = await account.signMessage({ message: { raw: signature }, }); ``` Prints useful values for further use. ```javascript Javascript console.log("auth_address", auth_address); console.log("app_fid", fid); console.log("signature", signature); console.log("deadline", deadline); console.log("sponsor.signature", sponsorSignature); console.log("sponsor.fid", fid); ``` Save the code in a file, e.g., `generateSignedKeyRequest.js`, and run it using Node.js: ```bash Bash node generateSignedKeyRequest.js ``` Use the generated values to make a cURL request to register the auth address. Replace `` with your actual API key and `` with your redirect URL(if needed). ```bash Bash curl --request POST \ --url https://api.neynar.com/v2/farcaster/auth_address/developer_managed/signed_key/ \ --header 'Content-Type: application/json' \ --header 'x-api-key: ' \ --data '{ "address": "0x5a927ac639636e534b678e81768ca19e2c6280b7", "app_fid": 3, "deadline": 123, "signature": "0x16161933625ac90b7201625bfea0d816de0449ea1802d97a38c53eef3c9c0c424fefbc5c6fb5eabe3d4f161a36d18cda585cff7e77c677c5d34a9c87e68ede011c", "redirect_url": "", "sponsor": { "fid": 3, "signature": "", "sponsored_by_neynar": true } }' ``` *** ## Full Final Code ```javascript Javascript import { encodeAbiParameters } from "viem"; import { mnemonicToAccount, generateMnemonic, english } from "viem/accounts"; (async () => { const mnemonic = generateMnemonic(english); const auth_address_acc = mnemonicToAccount(mnemonic); const auth_address = auth_address_acc.address; const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = { name: "Farcaster SignedKeyRequestValidator", version: "1", chainId: 10, verifyingContract: "0x00000000fc700472606ed4fa22623acf62c60553", }; const SIGNED_KEY_REQUEST_TYPE = [ { name: "requestFid", type: "uint256" }, { name: "key", type: "bytes" }, { name: "deadline", type: "uint256" }, ]; const key = encodeAbiParameters( [{ name: "auth_address", type: "address" }], [auth_address] ); const fid = 0; const account = mnemonicToAccount("MNEMONIC_HERE"); const deadline = Math.floor(Date.now() / 1000) + 86400; const signature = await account.signTypedData({ domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN, types: { SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE, }, primaryType: "SignedKeyRequest", message: { requestFid: BigInt(fid), key, deadline: BigInt(deadline), }, }); const sponsorSignature = await account.signMessage({ message: { raw: signature }, }); console.log("auth_address", auth_address); console.log("app_fid", fid); console.log("signature", signature); console.log("deadline", deadline); console.log("sponsor.signature", sponsorSignature); console.log("sponsor.fid", fid); })(); ``` *** Enjoy building! 🚀 For additional help, [feel free to contact us](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 [Fetch Subscribers for FID](/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 at [Fetch Subscribers for FID](/reference/fetch-subscribers-for-fid). Lastly, please share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar), and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or [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 [Create Farcaster Mini App in 60s](/docs/create-farcaster-miniapp-in-60s). Converting an existing JavaScript-based web app to a Farcaster mini app involves the following steps: * install the [@neynar/react](https://www.npmjs.com/package/@neynar/react) npm package and use the `` provider in your app * alternatively, install the [Mini App SDK](https://www.npmjs.com/package/@farcaster/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 ### Using @neynar/react The recommended way to integrate your app with Farcaster is using the `@neynar/react` package, which includes the Mini App SDK along with custom Neynar components and built-in analytics: ```bash npm install @neynar/react ``` Then wrap your app with the `` provider: ```javascript import { MiniAppProvider } from '@neynar/react'; export default function App() { return ( {/* Your app components */} ); } ``` With the MiniAppProvider provider in place, you can access Mini App SDK functionality with the `useMiniApp()` react hook: ```javascript import { useMiniApp } from '@neynar/react'; export default function HomePage() { const { isSDKLoaded, context } = useMiniApp(); return (<> {isSDKLoaded && (
{context}
)} ) } ``` ### Using the Mini App SDK Alternatively, you can use the Mini App SDK (formerly the Frame SDK): ```bash 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(); ``` You should call `ready()` as early as possible in the app, but after any pageload processes that might cause the UI to re-render or update significantly. In a React app, it's generally best to call `ready()` inside the page-level component at the root of your UI, e.g. in your homepage component. Here's an example of how you might do this in a standard React app: ```javascript {9} import { useEffect, useState } from "react"; import { sdk } from '@farcaster/frame-sdk'; export default function Home() { const [isLoaded, setIsLoaded] = useState(false); useEffect(() => { const load = async () => { await sdk.actions.ready(); setIsLoaded(true); }; if (sdk && !isLoaded) { load(); } }, [isLoaded]); return (...) } ``` ## Connecting to the wallet provider It's recommended to use `wagmi` for your wallet provider, as the Farcaster team provides the [@farcaster/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 {11} 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()`. ## Connecting to Solana For Solana support, install the package and wrap your app with the Solana provider: ```bash Bash npm install @farcaster/mini-app-solana ``` ```typescript App.tsx import { FarcasterSolanaProvider } from '@farcaster/mini-app-solana'; function App() { const solanaEndpoint = 'https://solana-rpc.publicnode.com'; return ( {/* Your app components */} ); } ``` Use Solana wallet hooks in your components: ```typescript SolanaExample.tsx import { useSolanaConnection, useSolanaWallet } from '@farcaster/mini-app-solana'; import { Transaction, SystemProgram, PublicKey } from '@solana/web3.js'; function SolanaExample() { const { publicKey, signMessage, sendTransaction } = useSolanaWallet(); const { connection } = useSolanaConnection(); const handleSign = async () => { if (!signMessage) return; const message = new TextEncoder().encode("Hello Solana!"); const signature = await signMessage(message); console.log('Signed:', btoa(String.fromCharCode(...signature))); }; const handleSend = async () => { if (!publicKey || !sendTransaction) return; const { blockhash } = await connection.getLatestBlockhash(); const transaction = new Transaction(); transaction.add( SystemProgram.transfer({ fromPubkey: publicKey, toPubkey: new PublicKey('DESTINATION_ADDRESS'), lamports: 1000000, // 0.001 SOL }) ); transaction.recentBlockhash = blockhash; transaction.feePayer = publicKey; const signature = await sendTransaction(transaction, connection); console.log('Transaction:', signature); }; return (
); } ```
The Solana provider will only be available when the user's wallet supports Solana. Always check `hasSolanaProvider` before rendering Solana-specific UI components. ## Adding and signing the farcaster.json file Mini apps are expected to serve a farcaster.json file, also known as a "manifest", at `/.well-known/farcaster.json`, published at the root of the mini app's domain. The manifest consists of a `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/", "imageUrl": "https://yoink.party/framesV2/opengraph-image", "buttonTitle": "🚩 Start", "splashImageUrl": "https://yoink.party/logo.png", "splashBackgroundColor": "#f5f0ec", "webhookUrl": "https://yoink.party/api/webhook" } } ``` In a standard react app, you can do this by placing a JSON file in your public folder, to be served as a static file: ``` public/ ├── .well-known/ └── farcaster.json ``` Once your domain is live and serving something like the above example at `yourURL.com/.well-known/farcaster.json`, you need to generate an `accountAssociation` signed with your farcaster custody address: * go to [the manifest tool on Farcaster](https://warpcast.com/~/developers/mini-apps/manifest) in your desktop browser * enter your domain and scroll to the bottom * click "Claim Ownership", and follow the steps to sign the manifest with your Farcaster custody address using your phone * finally, copy the output manifest from the manifest tool and update your domain to serve the full, signed farcaster.json file, which should look something like this: ```json { "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, 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" } } } ``` # Cast Stream Source: https://docs.neynar.com/docs/create-a-stream-of-casts Fetch stream of casts with Farcaster hubs In this guide, we'll create a stream of casts using Farcaster hubs and stream the casts published in real time. ### Create nodejs app Create a new node.js app using the following commands: ```bash 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 ``` ### Build the stream Now, let's get to building our stream. In the index.ts file add the following to initialise the client: ```typescript index.ts import { createDefaultMetadataKeyInterceptor, getSSLHubRpcClient, HubEventType } from '@farcaster/hub-nodejs'; const hubRpcEndpoint = "hub-grpc-api.neynar.com"; const client = getSSLHubRpcClient(hubRpcEndpoint, { interceptors: [ createDefaultMetadataKeyInterceptor('x-api-key', 'YOUR_NEYNAR_API_KEY'), ], 'grpc.max_receive_message_length': 20 * 1024 * 1024, }); ``` You need to replace "YOUR\_NEYNAR\_API\_KEY" with your API key. You can get it from your [neynar app page](https://dev.neynar.com/app). Once our client is initialized we can use it to subscribe to specific events, in our case we want to subscribe to the `MERGE_MESSAGE` event. You can check out the full details about the types of events in [The Hubble Events Documentation](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. Here's what the completed code looks like: ```typescript import { createDefaultMetadataKeyInterceptor, getSSLHubRpcClient, HubEventType } from '@farcaster/hub-nodejs'; const hubRpcEndpoint = "hub-grpc-api.neynar.com"; const client = getSSLHubRpcClient(hubRpcEndpoint, { interceptors: [ createDefaultMetadataKeyInterceptor('x-api-key', 'YOUR_NEYNAR_API_KEY'), ], 'grpc.max_receive_message_length': 20 * 1024 * 1024, }); client.$.waitForReady(Date.now() + 5000, async (e) => { if (e) { console.error(`Failed to connect to ${hubRpcEndpoint}:`, e); process.exit(1); } else { console.log(`Connected to ${hubRpcEndpoint}`); const subscribeResult = await client.subscribe({ eventTypes: [HubEventType.MERGE_MESSAGE], }); if (subscribeResult.isOk()) { const stream = subscribeResult.value; for await (const event of stream) { if (event.mergeMessageBody.message.data.type === 1) { console.log(event.mergeMessageBody.message.data.castAddBody.text); } } } client.close(); } }); ``` ### Run the stream in your terminal Finally, you can run the script using `bun run index.ts` and it will provide you with a stream like this: ![Cast Stream](https://mintlify.s3.us-west-1.amazonaws.com/neynar/images/docs/bc0fa74-image.png) ## Share with us! Lastly, make sure to share what you built with us on Farcaster by tagging [@neynar](https://warpcast.com/neynar) and if you have any questions, reach out to us on [warpcast](https://warpcast.com/~/channel/neynar) or [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 Farcaster Mini App (v2 frame) in < 60s Source: https://docs.neynar.com/docs/create-farcaster-miniapp-in-60s Create a v2 Farcaster mini app in less than 60 seconds If looking to convert an existing web app into a mini app, see [Convert Web App to Mini App](/docs/convert-web-app-to-mini-app). This tutorial shows how to create a Farcaster mini app (prev. called frames) with one simple command in less than 60s using the [Neynar Starter Kit](https://github.com/neynarxyz/create-farcaster-mini-app/).
Neynar Starter Kit demo app screenshot in dark mode Neynar Starter Kit demo app screenshot in light mode
Simply type `npx @neynar/create-farcaster-mini-app@latest` in any terminal window to get started with the template, or check out the [live demo of the Neynar Starter Kit](https://farcaster.xyz/miniapps/Qmodl2Stf9qh/starter-kit) on Farcaster. * package is open source ([github repo](https://github.com/neynarxyz/create-farcaster-mini-app)) * using neynar services is optional * demo API key is included if you haven't subscribed yet The flow: * generates signature required by frame spec on your behalf and puts in the farcaster manifest * sets up splash image, CTA, etc. as part of workflow (incl. personalized share images, more on that below) * spins up a localtunnel hosted url so you can debug immediately, no need to ngrok or cloudflare on your own * if you use neynar: * automatically fetches user data * automatically sets sets up [notifications and analytics](/docs/send-notifications-to-mini-app-users) (just put in your client id from the dev portal) See \< 1 min video here that goes from scratch to testable frame: ", "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 in [Search User](/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 [Understanding Message Propagation on Farcaster Mainnet](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 [Two Ways to Sponsor a Farcaster Signer via Neynar](/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 [Write to Farcaster with Neynar Managed Signers](/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. # Common UX Mistakes and Launch Strategies for Mini Apps Source: https://docs.neynar.com/docs/mini-app-common-mistakes Avoid these common pitfalls and learn proven launch strategies for viral Farcaster mini apps While the [main virality guide](/docs/mini-app-virality-guide) covers the core design principles, this page focuses on specific mistakes to avoid and proven launch strategies that can make or break your mini app's success. ## Common UX Mistakes That Kill Virality ### Don't Force Social Features ❌ **Bad:** Requiring users to share before accessing basic features
✅ **Good:** Making sharing the natural next step after achievements ❌ **Bad:** Generic "Share to unlock" mechanics
✅ **Good:** Exclusive rewards that make sharing genuinely valuable ### Don't Ignore Social Context ❌ **Bad:** Anonymous leaderboards and generic achievements
✅ **Good:** Social proof (profile pics, user stats) and friend-based competition ❌ **Bad:** Spammy notifications based on arbitrary timers
✅ **Good:** Social trigger notifications that create genuine FOMO ### Don't Waste the Wallet Advantage ❌ **Bad:** Financial incentives that feel disconnected from social mechanics
✅ **Good:** Token rewards that amplify friend competition and sharing ❌ **Bad:** Generic minting without social context
✅ **Good:** Collaborative minting or rewards users can send to friends that create network effects ## Launch Strategy Ideas: Building Momentum ### Pre-Launch: Create Anticipation * Use Neynar's APIs to identify influential accounts in your niche * Create a waitlist with referral bonuses and early token allocations * Seed content from beta users to create social proof (so your leaderboards aren't empty on day one) * Pre-mint exclusive NFTs for early supporters and testers ### Launch Day: Maximize Initial Virality * Use Neynar's notification system to alert waitlist users * Target high-follower users first to create cascade effects * Use limited-time minting opportunities to create urgency * Focus on getting early users to their first shareable moment and first token claim quickly ### Post-Launch: Sustain Growth * Create recurring events with daily activity streaks evolving reward structures * Use token incentives to reactivate dormant social connections * Build features that increase both network density and token circulation within your app * Analyze which social patterns and financial rewards drive the most viral sharing ## Key Takeaways The most successful mini apps avoid these common pitfalls: 1. **Don't force social features** - make them natural byproducts of fun experiences 2. **Always include social context** - anonymous experiences don't spread 3. **Leverage the wallet** - financial incentives should amplify social mechanics 4. **Plan your launch** - momentum matters more than perfect features 5. **Measure and iterate** - use analytics to understand what drives engagement *Remember: Viral mini apps are designed around social psychology first, technical features second. Focus on creating experiences that naturally encourage users to bring their friends into the fold.* # How to Build Viral Mini Apps Source: https://docs.neynar.com/docs/mini-app-virality-guide A developer's guide to designing user experiences that spread naturally through Farcaster's social graph using Neynar's infrastructure Many developers treat Farcaster mini apps like regular web apps that happen to live inside a social client. This fundamentally misses the unique opportunity of building on Farcaster. You're not just building an app; you're building inside a social network with rich user context, established social graphs, and a built-in crypto wallet. The mini apps that go viral understand this distinction. They're designed around social mechanics and financial incentives from the ground up, not bolted on as an afterthought. ## Design Principles ### #1: *Think Social-First, Not Social-Added* The traditional approach treats social features as an afterthought—build your app first, then add sharing later. Viral mini apps flip this paradigm by designing around social mechanics from day one, with every feature leveraging the user's social graph and network effects. Here are some practical examples: **Social competition:** In addition to a traditional leaderboard of all users, use Neynar's [following API](/reference/fetch-user-following) to query the accounts your users follow. ***Generic competition is boring; social competition is addictive.*** Show "3 people you follow are also playing" and their high scores, maybe allow users to challenge their friends or mutual follows, and suddenly your leaderboard becomes a much more rewarding experience. **Personalized onboarding:** When someone opens your app, immediately show them which of their friends are already using it. Encourage them to join or challenge their friends to get them started. **Friend activity feeds:** Don't just show what happened in your app - show what their network is doing through notifications, activity feeds, or popups/modals. "Your friend @charlie just completed a challenge" or "Hey @alice! @bob just beat your high score" creates FOMO and natural engagement. ### #2: *Make Sharing Inevitable* Viral mini apps can be thought of as ***effortless sharing engines*** - they don't ask users to share, they make sharing the obvious next step. **Dynamic Share Pages** Every achievement should generate a custom share URL with a user-specific embed image that serves dual purposes: celebration for the user and invitation for their network. Use the [Neynar Starter Kit](/docs/create-farcaster-miniapp-in-60s) to get this functionality out-of-the-box, or build it yourself using Neynar's APIs to pull in user avatars, usernames, and social proof. Structure your dynamically generated share image like this: * **Hero moment:** "You just beat 89% of players!" * **Social proof:** Show profile pics of friends who also played using their social graph * **Relevant entry point:** Clicking the "launch" button can send a new user directly to a page of your mini app challenging or referencing the user sharing the mini app, for example **Smart Cast Composition** Don't just share generic messages. Pre-fill the cast composer with social graph data to craft contextual casts for your users: * **First achievement:** "I just did X in this app and thought you would like it @friend1 @friend2 @friend3" * **Beat a friend:** "Just beat @friend's score in \[app]!" * **Clear invitation:** "Challenge your friends" cast pre-filled with tags of their best friends (as demonstrated in the [Neynar Starter Kit](/docs/create-farcaster-miniapp-in-60s) using the [best friends API](/reference/best-friends)) **The "Share to Claim" Pattern** Create exclusive rewards tied to social actions. This isn't about forcing sharing - it's about making sharing valuable. Use Neynar's casts API or reactions API and the wallet integration to create real financial incentives, either with established ERC20 tokens, or with reward tokens or NFTs made just for your app: * Bonus rewards for shares that get engagement or accounts that have more followers * Collaborative minting where friend groups unlock rewards together * Time-limited tokens tied to viral moments * Exclusive tokens or NFTs minted only for users who share ### #3: *Send Notifications That Bring Users Back* **Smart Re-engagement Through Social Triggers** Neynar's notification system lets you reach users in their Warpcast notification inbox. Use this strategically to keep users coming back by crafting notifications based on user actions and social graph data. **Social FOMO Triggers:** * "3 of your friends just played without you" * "You just lost your top spot to @friend" / "You're now ranked #X among your friends" * "@friend just joined and is catching up to your score" **Time-Sensitive Opportunities:** * "Daily challenge ends in 2 hours" * "Your friend challenged you to beat their score" * "Weekly leaderboard resets tomorrow" The key is triggering notifications based on social events, not arbitrary timers. People respond better to social context. Additionally, if you use the [Neynar Starter Kit](/docs/create-farcaster-miniapp-in-60s) or integrate the `MiniAppProvider` component from [@neynar/react](https://www.npmjs.com/package/@neynar/react), you can track notification open rates to understand what works and what doesn't for your specific use case. See the [notifications guide](/docs/send-notifications-to-mini-app-users) for more details. ### #4: *Use Financial Incentives That Feel Natural* **Token Rewards and Minting as Social Mechanics** The most viral Farcaster mini apps understand that users come with built-in wallets and respond to real value, not just points. Even if your app doesn't require transactions or cost money to use, you can still bake in financial incentives to drive growth. **Mint-to-Share Loops** Structure your rewards so that claiming tokens or NFTs naturally leads to sharing: * Mint exclusive badges for achievements, then land users on a share page * Time-limited tokens tied to viral moments ("First 100 to share get exclusive NFT") **Smart Financial Incentives** Use Farcaster's wallet integration to create seamless, social flows of value: * Encourage users to tip friends with an in-app currency * Staking mechanics where users lock up resources for extra functionality * Auction mechanics where social proof affects pricing **The Key:** Financial incentives should amplify social mechanics, not replace them. The best viral apps make earning tokens feel like a natural byproduct of having fun with friends. ## Technical Implementation Strategy ### Core Neynar Features for Viral Apps **Social Graph APIs:** Pull rich profile data, follower/following lists, and mutual connections to personalize every interaction. **Notifications with Analytics:** Re-engage users with social triggers and achievement celebrations, and track open rates of your notifications to know what drives the most engagement. **Mint Component:** Embed a mint button that lets users claim exclusive NFTs or tokens tied to achievements, then land on share pages to spread the word. **Share Component:** Embed a share button that composes a cast for the user pre-filled with best friend tags and a dynamically generated share image embed. ### The Neynar Starter Kit Advantage Instead of building everything from scratch, use the [Neynar Starter Kit](/docs/create-farcaster-miniapp-in-60s) to start from a mini app template that already includes all of the above features, which you can easily edit for your own purposes. Read about [common UX mistakes and launch strategies](/docs/mini-app-common-mistakes) that can make or break your mini app's virality. ## The Bottom Line Viral mini apps don't just happen—they're designed around social psychology and financial psychology. Every successfully viral mini app answers these fundamental questions: 1. **Why would someone want to share this?** (Achievement, status, challenge, financial reward) 2. **How can you make sharing effortless?** (Pre-filled casts, dynamic images, instant rewards) 3. **What social proof drives participation?** (Friends playing, mutual connections, token holders) 4. **How do you create habit loops?** (Social triggers over calendar reminders, plus rewards) 5. **What makes the financial incentives feel natural?** (Rewards that amplify social mechanics, not replace them) With Neynar's social graph infrastructure and Farcaster's built-in wallet integration, you have everything you need to answer these questions. The [Neynar Starter Kit](/docs/create-farcaster-miniapp-in-60s) handles both the technical complexity and wallet integration, so you can focus on designing experiences that naturally spread through Farcaster's social graph while creating real value for users. # Mint NFTs for Farcaster Users Source: https://docs.neynar.com/docs/mint-for-farcaster-users Mint NFTs directly to Farcaster users using their FID with Neynar's server wallets ### Related API: [Mint NFT](/reference/post-nft-mint) Want to reward your Farcaster community with NFTs? This guide shows you how to mint NFTs directly to Farcaster users using their FID (Farcaster ID) instead of wallet addresses. ### Currently Limited to Highlight This API currently only works with NFTs deployed through [Highlight](https://highlight.xyz) on EVM networks. We're working on expanding support to other NFT platforms, so if you have a specific request [let us know](https://t.me/rishdoteth). Server wallets need to be manually set up for each user. Contact us to get your server wallet configured. ## Simulate vs Execute: GET vs POST The API provides two modes of operation: * **GET (Simulate)**: Returns transaction calldata without executing - perfect for previewing costs and validating parameters * **POST (Execute)**: Actually executes the mint transaction using your server wallet ## Getting Transaction Calldata (Simulate) Use the GET endpoint to preview what the mint transaction will look like: ```javascript Node.js const response = await fetch('/farcaster/nft/mint?' + new URLSearchParams({ nft_contract_address: '0x8F01e875C816eC2C9d94E62E47771EbDB82d9A8B', network: 'base-sepolia', recipients: JSON.stringify([{ fid: 14206, quantity: 1 }]) })); const calldata = await response.json(); console.log(calldata[0]); ``` Example response: ```json { "recipient": { "fid": 14206, "quantity": 1 }, "abi": [...], "function_name": "mint", "args": [...], "to": "0x8F01e875C816eC2C9d94E62E47771EbDB82d9A8B", "data": "0x...", "value": "0", "network": "base-sepolia" } ``` ## Executing the Mint Transaction To actually mint the NFT, use the POST endpoint with your server wallet: ```javascript Node.js const response = await fetch('/farcaster/nft/mint', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-wallet-id': 'your-server-wallet-id' }, body: JSON.stringify({ nft_contract_address: '0x8F01e875C816eC2C9d94E62E47771EbDB82d9A8B', network: 'base-sepolia', recipients: [{ fid: 14206, quantity: 1 }], async: true }) }); const result = await response.json(); console.log(result.transactions[0].transaction_hash); ``` ## Async vs Sync Execution You can choose how to handle transaction execution: ### Sync Mode (Recommended) Set `async: false` to wait for transaction confirmation and get the receipt: ```json { "transactions": [{ "transaction_hash": "0xabc...", "recipient": { "fid": 14206, "quantity": 1 }, "receipt": { "status": "success", "gas_used": "150000", "block_number": "12345" } }] } ``` ### Async Mode Set `async: true` to get the transaction hash immediately and check status separately, will not work with large recipient lists: ```json { "transactions": [{ "transaction_hash": "0xabc...", "recipient": { "fid": 14206, "quantity": 1 } }] } ``` ## Batch Minting Mint to multiple Farcaster users in a single API call: ```javascript Node.js const recipients = [ { fid: 14206, quantity: 1 }, { fid: 14207, quantity: 2 }, { fid: 14208, quantity: 1 } ]; const response = await fetch('/farcaster/nft/mint', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-wallet-id': 'your-server-wallet-id' }, body: JSON.stringify({ nft_contract_address: '0x8F01e875C816eC2C9d94E62E47771EbDB82d9A8B', network: 'base-sepolia', recipients, async: true }) }); const result = await response.json(); // Returns 3 separate transactions console.log(`Minted to ${result.transactions.length} users`); ``` ## Server Wallets Server wallets are managed wallets you top up with funds that execute transactions on your behalf. Benefits include: * **No gas management**: We handle gas estimation and payment * **Reliable execution**: Built-in retry logic and error handling * **FID resolution**: Automatically resolves Farcaster IDs to wallet addresses ### Getting Set Up To use this API, you'll need: 1. A server wallet configured by our team 2. Your `x-wallet-id` header value 3. An NFT contract deployed on Highlight 4. Native gas tokens on the network of your choosing (top up the address we setup for you) [Contact us at](https://t.me/rishdoteth) to get started! ## Error Handling If you don't include the required `x-wallet-id` header, you'll get: ```json { "code": "RequiredField", "message": "x-wallet-id header is required" } ``` If you don't have a wallet id [reach out](https://t.me/rishdoteth) to get one setup. Each transaction in batch minting will either succeed with a `transaction_hash` or fail with an `error` field. That's it! You're now ready to reward your Farcaster community with NFTs using just their FIDs. Enjoy building! 🚀 For additional help, [feel free to contact us](https://t.me/rishdoteth). # 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 Farcaster 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 the Farcaster app. ## 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, blocks and bans 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/for_you /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, blocks and bans 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 [LLM Documentation - Complete Version](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 [LLM Documentation - Compact Version](https://docs.neynar.com/llms.txt). ### MCP server Install by running `npx @mintlify/mcp@latest add neynar` in your terminal. You will then need to [add the MCP server to Cursor](https://docs.cursor.com/context/model-context-protocol#configuring-mcp-servers). 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 [Neynar Explorer](https://explorer.neynar.com) and insert your fid in the search bar. Sample url `explorer.neynar.com/`. ### Scores are also available onchain, see [Address \<> user score contract](/docs/address-user-score-contract) ## Interpreting the score The score is *not* proof of humanity. It's a measure of the account quality / value added to the network by that account. It's capable of discriminating between high and low quality AI activity. E.g. agents like bracky / clanker are bots that have high scores while accounts posting LLM slop have lower scores. 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. ## Fetching the score for development ### 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 will see the same score in all user objects across all Neynar API endpoints. Try the following endpoints on our docs pages and look at the user object to see this in action: * [User by FIDs](/reference/fetch-bulk-users) to see this when fetching user data by fid * [By Eth or Sol addresses](/reference/fetch-bulk-users-by-eth-or-sol-address) If looking to restrict activity to a specific cohort of users, you can check user score on any Neynar API endpoint 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) . ## FAQs #### 1. How often do the scores update? There's two different things to note here: * (a) The algorithm runs **weekly** and calculates scores for accounts on the network based on their activity * (b) The algorithm itself is upgraded from time to time as activity on the network shifts. You can read about one such update in [Retraining Neynar User Score Algorithm](https://neynar.com/blog/retraining-neynar-user-score-algorithm) #### 2. As a user, how can I improve my score? The score is a reflection of account activity on the network. Since we have introduced this score, a few users have written helpful guides on how to contribute to the network in a positive manner: * see from @ted [here](https://warpcast.com/rish/0xbcbadc6f) * longer write up from @katekornish (now turned into a mini app by @jpfraneto) [here](https://startonfarcaster.xyz/) # 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}