Skip to main content

Validate Frames & Actions Message

Airstack provides an easy-to-use Validation API to validate your Farcaster Frames and Actions with Farcaster Hubs.

Pre-requisites

To access the Airstack Validation API, make sure that you have your Airstack API key.

For access to the Airstack API key, you'll need to hold at least 1 /airstack Channel Fan Token in either your Farcaster custodial wallet or one of your Farcaster verified wallets AND make sure that you have connected one of those wallet or your Farcaster account to your Airstack account.

If you don't have it yet, you can buy it here.

Methods

Depending on the programming language and the framework you are using, you can easily integrate the Validation API to your Frames and Actions to earn Moxie Everyday Rewards.

To determine the best method, follow the flowchart below from top to bottom for guidance:

Method #1: Frog Framework Integration

If you are using the Frog Framework, validation logic is embedded so you can instead set the hubs config to validate with the Airstack Validation API by providing the Hub API URL and the Airstack API key:

import { Frog } from "frog";

const app = new Frog({
hub: {
apiUrl: "https://hubs.airstack.xyz",
fetchOptions: {
headers: {
"x-airstack-hubs": "YOUR_AIRSTACK_API_KEY",
},
},
},
});

Once you have added the code snippets above, all the Frames and Actions messages will automatically be validated with the Airstack Validation API.

Method #2: Frames.js Framework Integration

If you are using the Frames.js Framework, you can use the farcasterHubContext middleware to set the hubs config to validate with the Airstack Validation API by providing the Hub API URL and the Airstack API key:

import { farcasterHubContext } from "frames.js/middleware";

const frames = createFrames({
middleware: [
farcasterHubContext({
...(process.env.NODE_ENV === "production"
? {
hubHttpUrl: "https://hubs.airstack.xyz",
hubRequestOptions: {
headers: {
"x-airstack-hubs": process.env.AIRSTACK_API_KEY as string,
},
},
}
: {
hubHttpUrl: "http://localhost:3010/hub",
}),
}),
],
});

Once you have added the code snippets above, all the Frames and Actions messages will automatically be validated with the Airstack Validation API.

Method #3: Airstack Frames SDK

You can validate your Farcaster Frames and Actions using the validateFramesMessage function from the Airstack Frames SDK:

import {
validateFramesMessage,
ValidateFramesMessageInput,
ValidateFramesMessageOutput,
} from "@airstack/frames";

try {
// Your Frames Signature Packet from the request body
const body: ValidateFramesMessageInput = {
untrustedData: {
fid: 289309,
url: "https://sample.frames",
messageHash: "0xabc",
timestamp: 1709198011100,
network: 1,
buttonIndex: 1,
castId: {
fid: 289309,
hash: "0x0000000000000000000000000000000000000001",
},
},
trustedData: {
messageBytes:
"0a61080d109dd41118d0c9c72f20018201510a3168747470733a2f2f70656c6963616e2d666f6e642d64697374696e63746c792e6e67726f6b2d667265652e6170702f6f6710011a1a089dd4111214000000000000000000000000000000000000000112146357261fa893e4be85f78178babaca876f9a1fac18012240d1ed649964018377641a78638f0c19d3c346c1eb1a47e856c0fcd87d3fc72ff98172f939fc18ffdd16af746144279e6debb3f4913f491c69d22f6703e554510a280132200295183aaa021cad737db7ddbc075964496ece1c0bcc1009bdae6d1799c83cd4",
},
};
const res: ValidateFramesMessageOutput = await validateFramesMessage(body);
} catch (error) {
console.error(error);
}

This snippet should be added as the first few lines on your Frames or Actions API endpoint before proccessing further. Once the message is validated, you will receive a response with the validation result as follows:

{
"isValid": true,
"message": {
"data": {
"type": 13,
"fid": 289309,
"timestamp": 99738832,
"network": 1,
"castAddBody": undefined,
"castRemoveBody": undefined,
"reactionBody": undefined,
"verificationAddAddressBody": undefined,
"verificationRemoveBody": undefined,
"userDataBody": undefined,
"linkBody": undefined,
"usernameProofBody": undefined,
"frameActionBody": {
"url": [
104, 116, 116, 112, 115, 58, 47, 47, 112, 101, 108, 105, 99, 97, 110,
45, 102, 111, 110, 100, 45, 100, 105, 115, 116, 105, 110, 99, 116,
108, 121, 46, 110, 103, 114, 111, 107, 45, 102, 114, 101, 101, 46, 97,
112, 112, 47, 111, 103
],
"buttonIndex": 1,
"castId": {
"fid": 289309,
"hash": [
211, 29, 52, 211, 77, 52, 211, 77, 52, 211, 77, 52, 211, 77, 52,
211, 77, 52, 211, 77, 52, 211, 77, 52, 211, 77, 52, 211, 77, 52, 211
]
},
"inputText": [],
"state": [],
"transactionId": []
}
},
"hash": [
211, 30, 183, 231, 189, 186, 213, 246, 188, 247, 119, 184, 109, 239, 57,
127, 191, 53, 239, 198, 218, 109, 167, 26, 243, 190, 159, 245, 173, 95,
105
],
"hashScheme": 1,
"signature": [
209, 237, 100, 153, 100, 1, 131, 119, 100, 26, 120, 99, 143, 12, 25, 211,
195, 70, 193, 235, 26, 71, 232, 86, 192, 252, 216, 125, 63, 199, 47, 249,
129, 114, 249, 57, 252, 24, 255, 221, 22, 175, 116, 97, 68, 39, 158, 109,
235, 179, 244, 145, 63, 73, 28, 105, 210, 47, 103, 3, 229, 84, 81, 10
],
"signatureScheme": 1,
"signer": [
211, 29, 54, 247, 157, 124, 221, 166, 154, 211, 109, 92, 105, 222, 247,
237, 214, 251, 117, 214, 220, 211, 190, 125, 235, 142, 61, 233, 231, 30,
213, 205, 27, 113, 205, 116, 211, 214, 221, 105, 238, 157, 215, 191, 125,
115, 205, 220, 119
],
"dataBytes": undefined
}
}

If isValid is true, it indicates that the message is valid on the Hubs and you can proceed. Otherwise, you should throw and error.

Method #4: Direct API Call

To validate your Frames and Actions messages, you can use the following query:

query MyQuery(
$messageBytes: String!
) {
FarcasterValidateFrameMessage(
input: {filter: {messageBytes: $messageBytes}}
) {
isValid
message {
data {
fid
frameActionBody {
buttonIndex
castId {
fid
hash
}
inputText
state
}
}
}
interactedByFid
interactedBy {
profileName
}
castedByFid
castedBy {
profileName
}
}
}
With this GraphQL query, you can add it to your source code and call the API with the graphql-request library:
index.ts
import { gql, GraphQLClient } from "graphql-request";
import { config } from "dotenv";

config();

const graphQLClient = new GraphQLClient(
"https://api.airstack.xyz/graphql"
);

const query = gql`
query MyQuery(
$messageBytes: String!
) {
FarcasterValidateFrameMessage(
input: {filter: {messageBytes: $messageBytes}}
) {
isValid
message {
data {
fid
frameActionBody {
buttonIndex
castId {
fid
hash
}
inputText
state
}
}
}
interactedByFid
interactedBy {
profileName
}
castedByFid
castedBy {
profileName
}
}
}
`;

const variable = {
"messageBytes": "0a61080d109dd41118d0c9c72f20018201510a3168747470733a2f2f70656c6963616e2d666f6e642d64697374696e63746c792e6e67726f6b2d667265652e6170702f6f6710011a1a089dd4111214000000000000000000000000000000000000000112146357261fa893e4be85f78178babaca876f9a1fac18012240d1ed649964018377641a78638f0c19d3c346c1eb1a47e856c0fcd87d3fc72ff98172f939fc18ffdd16af746144279e6debb3f4913f491c69d22f6703e554510a280132200295183aaa021cad737db7ddbc075964496ece1c0bcc1009bdae6d1799c83cd4"
}

const headers = {
"Authorization": process.env.AIRSTACK_API_KEY as string,
}

try {
const data = await graphQLClient.request(query, variable, headers);
console.log(data);
} catch (e) {
throw new Error(e);
}

The example provided here is using TypeScript and JavaScript. However, you can make the same GraphQL call in any other programming language that you are using.

Developer Support

If you have any questions or need help with other use cases, feel free to join the /airstack Warpcast channel and ask your questions there.

Our team is always ready to help you with any questions you may have.