Write data w/ 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
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:
- Create a signer for the user.
- Generate signature.
- Provide API routes for frontend interactions.
- 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
- 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 by default so users pay $0.
Let's go!
Now that we have context, let's get started!
Requirements
- Node.js (LTS recommended)
Installation
-
Next.js
npx create-next-app@latest neynar-managed-signers && cd neynar-managed-signers
-
Dependencies (to install via
npm
,yarn
, etc.):npm i @farcaster/hub-nodejs @neynar/nodejs-sdk viem
yarn add @farcaster/hub-nodejs @neynar/nodejs-sdk viem
-
Environment Variables:
Create a.env.local
file in your Next.js project root and add:NEYNAR_API_KEY=... FARCASTER_DEVELOPER_MNEMONIC=...
- NEYNAR_API_KEY: Get from Neynar Dashboard.
- 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
└── app
├── api
│ ├── signer
│ │ └── route.ts
│ └── cast
│ └── route.ts
└── ...
└── lib
└── neynarClient.ts
└── utils
├── getFid.ts
└── getSignedKey.ts
└── .env.local
└── ...
1. lib/neynarClient.ts
lib/neynarClient.ts
Copy the following into a file called lib/neynarClient.ts
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
utils/getFid.ts
Copy the following code into a file called utils/getFid.ts
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
utils/getSignedKey.ts
Copy the following code into a file called utils/getSignedKey.ts
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
app/api/signer/route.ts
Copy the following code into app/api/signer/route.ts
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
app/api/cast/route.ts
Copy the following code into app/api/cast/route.ts
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.
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
/api/signer
curl -X POST http://localhost:3000/api/signer \
-H "Content-Type: application/json"
Response
{
"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 (We recommend the latter method)
Convert signer_approved_url
to a QR code (For testing, you can use any online tool, e.g., QRFY)
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.

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)
8. cURL /api/cast
/api/cast
curl -X POST http://localhost:3000/api/cast \
-H "Content-Type: application/json" \
-d '{
"signer_uuid": "1234-abcd-5678-efgh",
"text": "Hello Farcaster!"
}'
Response
{
"success": true,
"cast": {
"hash": "0xcda4f957b4c68883080f0daf9e75cea1309147da",
"author": {
"object": "user",
"fid": 195494,
"username": "rishav",
"display_name": "Neynet tester 1/14",
"pfp_url": "https://cdn-icons-png.freepik.com/256/17028/17028049.png?semt=ais_hybrid",
"custody_address": "0x7355b6af053e5d0fdcbc23cc8a45b0cd85034378",
"profile": {
"bio": {
"text": "12/19"
},
"location": {
"latitude": 36.2,
"longitude": 138.25,
"address": {
"city": "Nagawa",
"state": "Nagano Prefecture",
"country": "Japan",
"country_code": "jp"
}
}
},
"follower_count": 6,
"following_count": 50,
"verifications": [],
"verified_addresses": {
"eth_addresses": [],
"sol_addresses": []
},
"verified_accounts": [
{
"platform": "x",
"username": "unverified"
},
{
"platform": "github",
"username": "unverified"
}
],
"power_badge": false
},
"text": "Hello Farcaster!"
}
}
Read more here 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.
If you encounter any issues, reach out to the Neynar team for support.
Updated 11 days ago