> For the complete documentation index, see [llms.txt](https://docs.grandprotocol.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.grandprotocol.org/guides/typescript.md).

# TypeScript Examples

The Grand Protocol API is a standard REST API. These examples use the native `fetch` API available in Node.js 18+ and all modern runtimes.

***

## Setup

```typescript
const BASE_URL = "https://api.grandprotocol.org/v1";
const API_KEY = process.env.GRAND_API_KEY!;

async function grand(path: string, options: RequestInit = {}) {
  const res = await fetch(`${BASE_URL}${path}`, {
    ...options,
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
      ...options.headers,
    },
  });

  const body = await res.json();
  if (!res.ok) throw Object.assign(new Error(body.detail ?? "API error"), { body });
  return body;
}
```

***

## Create an agent wallet

```typescript
const wallet = await grand("/wallets", {
  method: "POST",
  body: JSON.stringify({ agentId: "my-agent" }),
});

console.log(wallet.address); // 0x7A3f9C2E8b41D6a05F1c94E2B8D3a7C6e5F0b1A2
console.log(wallet.network); // robinhood-mainnet
```

***

## Set spend policies

```typescript
await grand("/policies/my-agent", {
  method: "PUT",
  body: JSON.stringify({
    dailyBudget: { usdc: 25 },
    perCallLimit: { usdc: 1.00 },
    allowedDomains: ["api.dune.com", "api.browserbase.com", "fal.ai"],
    rateLimit: { calls: 200, window: "1h" },
  }),
});
```

***

## Make a payment

```typescript
const result = await grand("/pay", {
  method: "POST",
  body: JSON.stringify({
    agentId: "my-agent",
    endpoint: "https://api.dune.com/v1/query/1234/results",
    method: "GET",
    idempotencyKey: `run-${crypto.randomUUID()}`,
  }),
});

console.log(result.protocol);           // "x402"
console.log(result.amountPaid.usdc);    // 0.002
console.log(result.txHash);             // "0x5e9a3f...c41f"
console.log(result.data);               // the API response body
```

***

## Handle policy rejections

```typescript
try {
  const result = await grand("/pay", {
    method: "POST",
    body: JSON.stringify({
      agentId: "my-agent",
      endpoint: "https://api.expensive-service.com/query",
      method: "POST",
      body: { query: "..." },
    }),
  });
} catch (err: any) {
  if (err.body?.error === "policy_violation") {
    console.warn("Payment blocked:", err.body.reason, err.body.detail);
    // reason: "per_call_limit_exceeded"
  } else if (err.body?.error === "insufficient_funds") {
    console.error("Wallet needs funding:", err.body.detail);
  } else {
    throw err;
  }
}
```

***

## Check wallet balance

```typescript
const wallet = await grand("/wallets/my-agent");
console.log(`Balance: ${wallet.balance.usdc} USDC`);
```

***

## List recent transactions

```typescript
const { transactions } = await grand(
  "/transactions?agentId=my-agent&limit=20"
);

for (const tx of transactions) {
  console.log(`${tx.timestamp}  ${tx.protocol}  ${tx.amountPaid.usdc} USDC  ${tx.endpoint}`);
}
```

***

## Multi-agent: invoke a sub-agent with a budget cap

```typescript
const invocation = await grand("/agents/invoke", {
  method: "POST",
  body: JSON.stringify({
    agentEndpoint: "https://research-agent.yourplatform.com/run",
    task: {
      query: "Summarize Ethereum L2 TVL trends for Q2 2026",
      outputFormat: "markdown",
    },
    budget: { usdc: 2.00 },
  }),
  headers: { "X-Agent-Id": "orchestrator-agent" },
});

console.log(invocation.output);
console.log(`Total cost: ${invocation.totalCost.usdc} USDC`);
console.log("Payment chain:", invocation.paymentChain);
```

***

## Fetch spend metrics

```typescript
const metrics = await grand(
  "/agents/my-agent/metrics?metrics=spend,tx_count,protocol_split&window=7d"
);

console.log(`7-day spend: ${metrics.metrics.spend.total} USDC`);
console.log(`Transactions: ${metrics.metrics.tx_count}`);
console.log(`x402 share: ${(metrics.metrics.protocol_split.x402 * 100).toFixed(1)}%`);
```

***

## Verify a webhook signature

```typescript
import { createHmac, timingSafeEqual } from "crypto";

function verifyWebhook(rawBody: string, signatureHeader: string, secret: string): boolean {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  try {
    return timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
  } catch {
    return false;
  }
}

// In your webhook handler (Express example):
app.post("/webhooks/grand", express.raw({ type: "application/json" }), (req, res) => {
  const valid = verifyWebhook(
    req.body.toString(),
    req.headers["x-grandprotocol-signature"] as string,
    process.env.WEBHOOK_SECRET!
  );

  if (!valid) return res.status(401).end();

  const event = JSON.parse(req.body.toString());

  if (event.event === "policy.violation") {
    console.warn(`Policy violation for ${event.agentId}: ${event.reason}`);
  }

  res.status(200).end();
});
```

***

## Further reading

* [Quickstart](/getting-started/quickstart.md)
* [Payment Router](/features/payment-router.md)
* [Error Reference](/platform/errors.md)
* [Webhooks](/platform/webhooks.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.grandprotocol.org/guides/typescript.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
