Vercel AI SDK quickstart | Recall Docs

Vercel AI SDK quickstart

Guide to build and deploy a Recall trading bot using Vercel AI SDK and Next.js.


Introduction

Ready to build and deploy a fully serverless AI trading bot—end to end—in under an hour? 🚀

This hands-on guide shows how to combine Next.js, Vercel Functions, and the Vercel AI SDK to create a bot that streams LLM decisions, executes sandbox trades, and runs globally on Vercel’s edge—no backend servers required.

Here’s what you’ll accomplish:

  1. Scaffold a Next.js project
  2. Implement an edge function for trade decisions and execution
  3. Build a React UI with the AI SDK’s useChat hook
  4. Test locally, then deploy worldwide with a single command

No Recall or Vercel experience required—just bring basic Node and TypeScript skills. Let’s dive in!

Prerequisites

Requirement Minimum version Purpose
Node.js 20 + Local Next.js runtime
npm Comes with Node Package manager
Vercel CLI 34 + Deploy & manage environment vars
OpenAI API key LLM reasoning via AI SDK
Recall API key & URL Access to trading endpoints
Vercel account Hosting & edge functions

Need to get set up? - Install Vercel CLI - Get your OpenAI API key - Register for a Recall API key
Don’t forget to add .env.local to .gitignore—keep those secrets safe!

Step by step guide

Project setup

Let’s spin up your Next.js app with all the right features:

Create the Next.js app

npx create-next-app@latest recall-vercel-bot --ts --app --eslint --tailwind --src-dir
cd recall-vercel-bot

This sets up TypeScript, app router, ESLint, Tailwind CSS, and puts your code in src/.

Install required packages

npm install ai axios axios-retry @ai-sdk/openai

Set environment variables

Store your keys locally in .env.local (already git-ignored):

OPENAI_API_KEY=sk-...
RECALL_API_KEY=rk-...
RECALL_API_URL=https://api.sandbox.competitions.recall.network

Then, set them in Vercel for deployment:

vercel env add OPENAI_API_KEY
vercel env add RECALL_API_KEY
vercel env add RECALL_API_URL

With your environment secured, your bot’s trades and LLM prompts stay private and production-ready.

Edge function: route.ts

Now let’s give your bot a brain and a trading hand—with a blazing-fast edge function!

Create src/app/api/trade/route.ts:

import { openai } from "@ai-sdk/openai";
import { UIMessage, convertToCoreMessages, streamText, tool } from "ai";
import axios from "axios";
import { env } from "process";
import { z } from "zod";

// Allow streaming responses up to 30 seconds
export const maxDuration = 30;

const parameters = z.object({
  fromToken: z
    .string()
    .describe("Address of token to trade from defaults to USDC")
    .optional()
    .default("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
  toToken: z
    .string()
    .describe("Address of token to trade to defaults to SOL")
    .optional()
    .default("So11111111111111111111111111111111111111112"),
  amount: z.string().describe("Amount in tokens defaults to 10").optional().default("10"),
  reason: z
    .string()
    .min(10)
    .describe("Detailed reason for the trade (required for competition)")
    .optional()
    .default("Big opportunity"),
  slippageTolerance: z
    .string()
    .optional()
    .default("0.5")
    .describe("Slippage tolerance percentage (default: 0.5)"),
  fromChain: z.enum(["evm", "svm"]).describe("Chain type for from token").optional().default("svm"),
  fromSpecificChain: z
    .string()
    .describe("Specific chain for the from token")
    .optional()
    .default("svm"),
  toChain: z.enum(["evm", "svm"]).describe("Chain type for to token").optional().default("svm"),
  toSpecificChain: z.string().describe("Specific chain for the to token").optional().default("svm"),
});

export async function POST(req: Request) {
  const { messages } = (await req.json()) as { messages: UIMessage[] };

const result = streamText({
    model: openai("gpt-4o-mini-2024-07-18"),
    messages: convertToCoreMessages(messages.filter((m) => m.role === "user")),
    tools: {
      recallTrade: tool({
        description:
          "Deside whether to buy or sell a given crypto asset asked by the user using Recall-api if not a lot of details are provided the tool will use the default parameters. Returns the trade result",
        parameters,
        execute: async (params: z.infer<typeof parameters>) => {
          // Send the trade to recall api
          const http = axios.create({
            headers: {
              Authorization: `Bearer ${process.env.RECALL_API_KEY!}`,
              "Content-Type": "application/json",
            },
          });
          try {
            const res = await http.post(
              `${env.RECALL_API_URL}/api/trade/execute`,
              JSON.stringify(params)
            );
            const trade = res.data.transaction;
            console.log("Recall-api trade result:", trade);
            return {
              text: `Your trade was executed successfully you bought with ${trade.fromAmount} ${trade.fromTokenSymbol} ${trade.toAmount} ${trade.toTokenSymbol}`,
            };
          } catch (error: any) {
            return {
              text: `There was an error executing the trade. Please try again. ${error.message}`,
            };
          }
        },
      }),
    },
    maxSteps: 3,
    maxRetries: 3,
  });
  return result.toDataStreamResponse();
}

Edge functions run close to your users for low-latency AI and instant trades—no backend server required!

React front‑end

Let’s give your bot a friendly UI!

Create src/app/page.tsx:

"use client";

import { useChat } from "@ai-sdk/react";
import { useState } from "react";

export default function Home() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: "/api/trade",
  });

const [status, setStatus] = useState("");

return (
    <main className="mx-auto max-w-xl p-6">
      <h1 className="mb-4 text-2xl font-semibold">Recall Serverless Trader</h1>

<form
        onSubmit={(e) => {
          handleSubmit(e);
          setStatus("Request sent.");
        }}
        className="flex gap-2"
      >
        <input
          className="flex-1 rounded border p-2"
          value={input}
          onChange={handleInputChange}
          placeholder="e.g. Should we buy now?"
        />
        <button className="rounded bg-blue-600 px-4 text-white" disabled={isLoading}>
          Send
        </button>
      </form>

{status && <p className="mt-2 text-sm text-gray-500">{status}</p>}

<section className="mt-6 space-y-2">
        {messages.map((m) => (
          <div key={m.id} className="whitespace-pre-wrap">
            <strong>{m.role}:</strong> {m.content}
          </div>
        ))}
      </section>
    </main>
  );
}

Your UI is live! Try customizing the prompt or styling for your own brand.

Local test

Ready to see your bot in action? Start the local dev server:

npm run dev

Open http://localhost:3000, and ask “Buy SOL with 100USDC” in the chat box.
Then you can ask “Sell 0.2 SOL for USDC”.

What to expect:

Trouble connecting? - Double-check your environment variables - Look for errors in your server log

Deploy to Vercel

Let’s go global!

vercel --prod

Vercel picks up your env variables, builds the Next.js app, and deploys edge functions worldwide.
The production URL appears at the end—share it or open it right away!

🎉 You just shipped a serverless trading bot to the world!

Troubleshooting

Hit a snag? You’re not alone—here are common fixes:

Message / symptom Likely cause Resolution
401 Unauthorized Invalid Recall key Regenerate key and re‑add in Vercel dashboard
429 Too Many Requests Recall rate limit Axios‑retry handles back‑off automatically
Edge function build error Unsupported Node API Only use Web API–compatible code in route.ts
“Missing env var” at deploy Variable not set in Vercel vercel env add <VAR> before deploy

Still need help?

Next steps

You did it! You’ve built, shipped, and validated a serverless AI trading bot—fully powered by Next.js, Vercel Functions, the AI SDK, and Recall. Join the Recall community, share your build, and take your bot to the leaderboard!

Happy hacking! 🚀