---
title: Quickstart
description: Get your first AI agent commerce interaction running in under 5 minutes.
---

import { Callout } from "fumadocs-ui/components/callout";
import { Steps, Step } from "fumadocs-ui/components/steps";
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
import { Cards, Card } from "fumadocs-ui/components/card";

# Quickstart

<VersionBadge pkg="@codespar/sdk" />

This guide walks you through installing the SDK, creating a session, executing a tool call, and wiring it into a full agent loop -- all in under 5 minutes. By the end, you will have a working commerce agent that can process payments in Brazil.

## Prerequisites

- Node.js 18+ installed
- A CodeSpar API key — mint one at [Dashboard → API Keys](https://codespar.dev/dashboard/api-keys). New accounts get a `test`-environment project plus a `csk_test_*` key auto-created at signup, and the mint modal defaults each new key's prefix to match the active project's environment.
- An LLM provider account (Anthropic, OpenAI, or any Vercel AI SDK provider)

<Callout type="info">
**On Python?** See [Quickstart (Python)](/docs/quickstart-python) for the same flow with `pip install codespar` — sync and async clients are published on PyPI.
</Callout>

<Callout type="info">
This quickstart builds a **seller agent** (your agent collects: checkout, charge, invoice). Building an agent that **buys and pays** instead? Start at the [buyer quickstart](/docs/quickstart-buyer) — MCP-first, no code.
</Callout>

<Steps>

<Step>
### Install the SDK and an adapter

Install the core SDK and the adapter for your preferred framework:

<Tabs items={["Claude", "OpenAI", "Vercel AI SDK"]}>
<Tab value="Claude">
```bash
npm install @codespar/sdk @codespar/claude @anthropic-ai/sdk
```
</Tab>
<Tab value="OpenAI">
```bash
npm install @codespar/sdk @codespar/openai openai
```
</Tab>
<Tab value="Vercel AI SDK">
```bash
npm install @codespar/sdk @codespar/vercel ai @ai-sdk/anthropic
```
</Tab>
</Tabs>

<Callout type="info">
Using pnpm or yarn? Replace `npm install` with `pnpm add` or `yarn add`. All packages are published on the public npm registry.
</Callout></Step>

<Step>
### Set your API keys

You need two environment variables -- one for CodeSpar, one for your LLM provider:

<Tabs items={["Claude", "OpenAI", "Vercel AI SDK"]}>
<Tab value="Claude">
```bash
export CODESPAR_API_KEY="csk_test_your_key_here"
export ANTHROPIC_API_KEY="sk-ant-your_key_here"
```
</Tab>
<Tab value="OpenAI">
```bash
export CODESPAR_API_KEY="csk_test_your_key_here"
export OPENAI_API_KEY="sk-your_key_here"
```
</Tab>
<Tab value="Vercel AI SDK">
```bash
export CODESPAR_API_KEY="csk_test_your_key_here"
export ANTHROPIC_API_KEY="sk-ant-your_key_here"
```
</Tab>
</Tabs>

<Callout type="info">
A `csk_test_*` key authorizes against a `test`-environment project. Tool calls route to the catalog's `base_url_test` provider sandbox where one is defined, and to the real production endpoint otherwise. For deterministic responses without any upstream call, declare per-session fixtures via `cs.create({ mocks: {...} })` — see [Test Mode](/docs/concepts/test-mode) for the full reference.
</Callout></Step>

<Step>
### Run a session against an inline mock

The fastest way to confirm the SDK is wired correctly — and the pattern most tests use — is to declare a mock on session create. The runtime substitutes the fixture for the upstream call; policy, audit, and commerce-memory still fire on every invocation.

```typescript title="test-mode.ts"
// Requires @codespar/sdk@0.10.0+
import { CodeSpar } from "@codespar/sdk";

const cs = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });

const session = await cs.create("user_test", {
  servers: ["asaas"],
  mocks: {
    "asaas/create_payment": { id: "pay_test_42", status: "PENDING" },
  },
});

const result = await session.execute("asaas/create_payment", { value: 100 });
console.log(result.id); // "pay_test_42"
```

A non-empty `mocks` field puts the session into strict mode for its lifetime: any tool call whose canonical name is not declared returns a `tool_not_mocked` envelope rather than falling through to the real upstream. See [Test Mode](/docs/concepts/test-mode) for the strict-mode contract, the five `tool_result` envelopes, and how to assert on them in TypeScript and Python.

</Step>

<Step>
### Create a session

A **session** is a scoped connection to one or more MCP servers. It manages authentication, tool routing, and usage tracking. Think of it as a database connection pool -- you create one, use it for the duration of your interaction, and close it when done.

```typescript title="index.ts"
import { CodeSpar } from "@codespar/sdk";

const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });

const session = await codespar.create("user_123", {
  servers: ["stripe", "mercadopago"],
});

console.log("Session ID:", session.id);
console.log("Status:", session.status);
console.log("Servers:", session.servers);
```

```json title="Output"
{
  "id": "ses_a1b2c3d4e5f6",
  "status": "active",
  "servers": ["stripe", "mercadopago"],
  "created_at": "2026-04-15T14:30:00Z",
  "expires_at": "2026-04-15T15:30:00Z"
}
```

Passing `preset: "brazilian"` instead of a `servers` array bundles the Brazilian rails (payments, fiscal, logistics, messaging, banking, ERP) in one line; the two forms can be combined. The presets are listed under [Sessions → Country presets](/docs/concepts/sessions#country-presets).

<Callout type="warn">
Sessions expire after 30 minutes of inactivity. For long-running agents, implement session renewal or create a new session per interaction. See [Sessions](/docs/concepts/sessions) for lifecycle details.
</Callout></Step>

<Step>
### List available tools

Once your session is active, retrieve the tools available from the connected servers. The `session.tools()` method is async and returns an array of tool definitions:

```typescript title="index.ts"
const tools = await session.tools();

console.log(`${tools.length} tools available:`);
tools.forEach((t) => {
  console.log(`  - ${t.name}: ${t.description}`);
});
```

```json title="Output"
[
  {
    "name": "codespar_discover",
    "description": "Find available commerce tools by domain",
    "input_schema": {
      "type": "object",
      "properties": {
        "domain": {
          "type": "string",
          "enum": ["payments", "fiscal", "logistics", "messaging", "banking", "erp", "crypto"]
        }
      },
      "required": ["domain"]
    }
  },
  {
    "name": "codespar_checkout",
    "description": "Create a checkout session for a product or service",
    "input_schema": { "..." : "..." }
  },
  {
    "name": "codespar_pay",
    "description": "Process a payment via Pix, boleto, or card",
    "input_schema": { "..." : "..." }
  }
]
```

<Callout type="info">
The tools returned depend on which servers you connected in the session. Connecting `["stripe", "mercadopago"]` gives you payment tools. Add `"correios"` to also get shipping tools.
</Callout></Step>

<Step>
### Execute a tool call

Call a tool directly against the session to verify everything is wired correctly:

```typescript title="index.ts"
const result = await session.execute("codespar_discover", {
  domain: "payments",
});

console.log(JSON.stringify(result, null, 2));
```

```json title="Response"
{
  "domain": "payments",
  "servers": ["stripe", "mercadopago"],
  "tools": [
    {
      "name": "codespar_checkout",
      "description": "Create a checkout session for a product or service"
    },
    {
      "name": "codespar_pay",
      "description": "Process a payment via Pix, boleto, or card"
    }
  ],
  "capabilities": ["pix", "boleto", "credit_card", "checkout_link", "recurring"]
}
```

Now try creating a checkout link:

```typescript title="index.ts"
const checkout = await session.execute("codespar_checkout", {
  provider: "stripe",
  amount: 4990,
  currency: "BRL",
  description: "Pro Plan - Monthly",
  payment_methods: ["pix", "card"],
});

console.log(JSON.stringify(checkout, null, 2));
```

```json title="Response"
{
  "checkout_id": "chk_7f8g9h0i1j2k",
  "url": "https://checkout.stripe.com/c/pay/cs_live_a1b2c3...",
  "amount": 4990,
  "currency": "BRL",
  "description": "Pro Plan - Monthly",
  "payment_methods": ["pix", "card"],
  "status": "open",
  "expires_at": "2026-04-16T14:30:00Z"
}
```
</Step>

<Step>
### Wire it into an agent

Now connect everything to a real LLM. Choose your framework:

<Tabs items={["Claude", "OpenAI", "Vercel AI SDK"]}>
<Tab value="Claude">
```typescript title="agent-claude.ts"
import Anthropic from "@anthropic-ai/sdk";
import { CodeSpar } from "@codespar/sdk";
import { getTools, handleToolUse, toToolResultBlock } from "@codespar/claude";

const anthropic = new Anthropic();
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });

async function run(userMessage: string) {
  // 1. Create session with the servers you need
  const session = await codespar.create("user_123", {
    servers: ["stripe", "mercadopago"],
  });

  // 2. Get tools in Anthropic format
  const tools = await getTools(session);

  // 3. Start the conversation
  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: userMessage },
  ];

  let response = await anthropic.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 4096,
    system:
      "You are a commerce assistant for a Brazilian e-commerce store. " +
      "Use the available tools to help with payments, invoicing, and shipping. " +
      "Always confirm amounts and details before processing payments.",
    tools,
    messages,
  });

  // 4. Tool-use loop: keep going until Claude stops calling tools
  while (response.stop_reason === "tool_use") {
    const toolUseBlocks = response.content.filter(
      (b) => b.type === "tool_use"
    );

    const toolResults = await Promise.all(
      toolUseBlocks.map(async (block) => {
        try {
          const result = await handleToolUse(session, block);
          return toToolResultBlock(block.id, result);
        } catch (error) {
          return toToolResultBlock(block.id, {
            error: error instanceof Error ? error.message : "Tool call failed",
          });
        }
      })
    );

    messages.push({ role: "assistant", content: response.content });
    messages.push({ role: "user", content: toolResults });

    response = await anthropic.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 4096,
      tools,
      messages,
    });
  }

  // 5. Clean up
  await session.close();

  // 6. Return the final text
  const text = response.content.find((b) => b.type === "text");
  return text?.type === "text" ? text.text : "";
}

// Run it
const reply = await run("Create a R$49.90 checkout link for 'Pro Plan' using Stripe");
console.log(reply);
```

See the full [Claude adapter guide](/docs/api/sdk/providers/claude) for streaming, error handling, and advanced patterns.
</Tab>

<Tab value="OpenAI">
```typescript title="agent-openai.ts"
import OpenAI from "openai";
import { CodeSpar } from "@codespar/sdk";
import { getTools, handleToolCall } from "@codespar/openai";

const openai = new OpenAI();
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });

async function run(userMessage: string) {
  const session = await codespar.create("user_123", {
    servers: ["stripe", "mercadopago"],
  });

  const tools = await getTools(session);

  const messages: OpenAI.ChatCompletionMessageParam[] = [
    {
      role: "system",
      content:
        "You are a commerce assistant for a Brazilian store. " +
        "Use the available tools for payments, invoicing, and shipping.",
    },
    { role: "user", content: userMessage },
  ];

  let response = await openai.chat.completions.create({
    model: "gpt-4o",
    tools,
    messages,
  });

  let message = response.choices[0].message;

  while (message.tool_calls && message.tool_calls.length > 0) {
    messages.push(message);

    for (const toolCall of message.tool_calls) {
      let content: string;
      try {
        content = await handleToolCall(session, toolCall);
      } catch (error) {
        content = JSON.stringify({
          error: error instanceof Error ? error.message : "Tool call failed",
        });
      }
      messages.push({
        role: "tool",
        tool_call_id: toolCall.id,
        content,
      });
    }

    response = await openai.chat.completions.create({
      model: "gpt-4o",
      tools,
      messages,
    });
    message = response.choices[0].message;
  }

  await session.close();
  return message.content ?? "";
}

const reply = await run("Generate a Pix QR code for R$250");
console.log(reply);
```

See the full [OpenAI adapter guide](/docs/api/sdk/providers/openai) for streaming and advanced patterns.
</Tab>

<Tab value="Vercel AI SDK">
```typescript title="agent-vercel.ts"
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { CodeSpar } from "@codespar/sdk";
import { getTools } from "@codespar/vercel";

const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });

const session = await codespar.create("user_123", {
  servers: ["stripe", "mercadopago"],
});

const tools = await getTools(session);

const { text } = await generateText({
  model: anthropic("claude-sonnet-4-20250514"),
  tools,
  maxSteps: 5,
  system:
    "You are a commerce assistant for a Brazilian e-commerce store. " +
    "Use the available tools for payments, invoicing, and shipping.",
  prompt: "Create a R$49.90 checkout link for 'Pro Plan' via Stripe",
});

console.log(text);
await session.close();
```

See the full [Vercel AI SDK guide](/docs/api/sdk/providers/vercel) for `streamText`, Next.js API routes, and the `useChat` hook.
</Tab>
</Tabs>
</Step>

</Steps>

## The Complete Loop in action

Here is what happens when you run the Claude example above. The agent performs the [Complete Loop](/docs/index#the-complete-loop) automatically:

1. **Claude receives the prompt** and sees the available tools from `getTools`.
2. **Claude calls `codespar_discover`** (optional) to understand available payment providers.
3. **Claude calls `codespar_checkout`** with the amount, currency, and provider.
4. **CodeSpar routes** the request to the Stripe MCP server, which creates a real checkout session.
5. **Claude receives the result** (checkout URL, status, expiry) and formats a response for the user.

```
User: "Create a R$49.90 checkout link for 'Pro Plan' using Stripe"

Claude thinks: I need to create a checkout link. I have the codespar_checkout tool.

Claude calls: codespar_checkout({
  provider: "stripe",
  amount: 4990,
  currency: "BRL",
  description: "Pro Plan",
  payment_methods: ["pix", "card"]
})

CodeSpar returns: {
  checkout_id: "chk_7f8g9h0i1j2k",
  url: "https://checkout.stripe.com/c/pay/cs_live_...",
  status: "open"
}

Claude responds: "I've created your checkout link for the Pro Plan at R$49.90.
Here it is: https://checkout.stripe.com/c/pay/cs_live_...
The link accepts both Pix and card payments and expires in 24 hours."
```

## Two places to explore without leaving the loop

These are different surfaces — pick the one that matches what you're doing:

- **In-dashboard sandbox** at [`/dashboard/sandbox`](https://codespar.dev/dashboard/sandbox) — a hosted chat interface where you can pick tools and watch them dispatch, without writing any code. Use it to scan what a server exposes before wiring it into your agent.
- **SDK-driven test mode** — the `mocks` field on `cs.create` shown above. Use it inside your test suite to assert deterministic responses for the tools your agent actually calls. See [Test Mode](/docs/concepts/test-mode) for the canonical reference.
## Common errors

| Error | Cause | Fix |
|-------|-------|-----|
| `INVALID_API_KEY` | Missing or malformed API key | Check that `CODESPAR_API_KEY` is set and starts with `csk_test_` (test-environment project) or `csk_live_` (live-environment project) |
| `SESSION_EXPIRED` | Session timed out after 30 minutes of inactivity | Create a new session |
| `SERVER_NOT_FOUND` | Requested a server that does not exist | Check available servers with `codespar_discover` |
| `TOOL_NOT_FOUND` | Called a tool not available in the current session | Verify the tool exists with `session.tools()` |
| `RATE_LIMITED` | Too many requests in a short period | Implement exponential backoff or reduce request frequency |

## Next steps

<NextStepsGrid items={[
  { label: "CONCEPT", title: "How CodeSpar works", description: "Sessions, meta-tools and the Complete Loop, end to end.", href: "/docs/how-it-works" },
  { label: "CONCEPT", title: "Test Mode", description: "Inline mocks, strict-mode behavior, the five tool-result envelopes, and bidirectional OSS parity.", href: "/docs/concepts/test-mode" },
  { label: "CONCEPT", title: "Authentication", description: "API keys, environments, scopes and provider auth.", href: "/docs/concepts/authentication" },
  { label: "CONCEPT", title: "Sessions", description: "Deep dive into the session lifecycle, server selection, and scoping.", href: "/docs/concepts/sessions" },
  { label: "CONCEPT", title: "Tools & Meta-Tools", description: "Understand the 15 meta-tools and how server routing works.", href: "/docs/concepts/tools" },
  { label: "PROVIDER", title: "Claude Adapter", description: "Full guide for building Claude-powered commerce agents.", href: "/docs/api/sdk/providers/claude" },
  { label: "PROVIDER", title: "OpenAI Adapter", description: "Integrate with GPT-4o and the OpenAI function-calling API.", href: "/docs/api/sdk/providers/openai" },
  { label: "PROVIDER", title: "Vercel AI SDK", description: "Streaming-first agents for Next.js applications.", href: "/docs/api/sdk/providers/vercel" },
  { label: "MCP", title: "Hosted MCP server", description: "Connect Claude Code, Claude Desktop, Cursor or Windsurf to the 15 meta-tools with one URL.", href: "/docs/concepts/meta-tools" },
  { label: "COOKBOOK", title: "E-Commerce Checkout", description: "Full cookbook — discover, checkout, invoice, ship, notify.", href: "/docs/cookbooks/ecommerce-checkout" },
  { label: "COOKBOOK", title: "Pix Payment Agent", description: "The simplest agent you can ship — Pix + WhatsApp in ~50 lines.", href: "/docs/cookbooks/pix-payment-agent" },
  { label: "BLOG", title: "The Complete Loop deep dive", description: "Why the loop closes on a fiscal document, and what each step guarantees.", href: "https://codespar.dev/blog/complete-loop-deep-dive" },
]} />
