# Introduction
URL: /docs
***
title: Introduction
icon: Presentation
------------------
# Echo
Echo is billing infrastructure for AI applications that turns your OpenAI imports into revenue-generating user accounts across React, Next.js, Node.js, and CLI tools.
## Why use Echo?
Building AI apps means handling payments, user auth, usage tracking, and API key management. Each user needs their own balance, every LLM call needs metering, and you're stuck building Stripe flows instead of features.
Echo standardizes AI billing like Clerk standardized auth. Drop in our SDK, set your markup percentage, and your existing OpenAI code instantly gets user accounts with usage-based billing. No payment processing, no surprise bills, no exposed API keys.
Result: Your users get one universal balance that works across all Echo-powered apps, creating network effects that grow your user base while you focus on building.
## Starting Fresh
The quickest way to get started with Echo is to use the `echo-start` CLI tool, which scaffolds a new project with Echo already configured:
```sh lineNumbers
npx echo-start@latest --template next
```
```sh lineNumbers
yarn dlx echo-start@latest --template next
```
```sh lineNumbers
pnpx echo-start@latest --template next
```
```sh lineNumbers
bunx echo-start@latest --template next
```
This will create a new Next.js app with Echo integration ready to go. For other templates and frameworks, check out our [templates section](/docs/getting-started/templates).
## Example Integration
Here's how you can add complete user management and LLM billing to your app:
```tsx
import { EchoProvider, EchoTokens, useEchoModelProviders } from '@merit-systems/echo-react-sdk';
import { generateText } from 'ai';
function App() {
return (
{/* Complete user management - handles auth, billing, sign-out */}
);
}
function ChatComponent() {
const { openai } = useEchoModelProviders();
const handleGenerate = async () => {
const { text } = await generateText({
model: openai('gpt-5'),
prompt: 'Hello world'
});
return text;
};
return ;
}
export default App
```
**Three integration patterns:**
* [**React SDK**](/docs/react-sdk) — OAuth2 + PKCE for secure client-side LLM calls
* [**Next.js SDK**](/docs/next-sdk) — Server-side auth with automatic token management
* [**TypeScript SDK**](/docs/typescript-sdk) — API keys for backends and CLI tools
The React and Next.js SDKs are opinionated wrappers around the TypeScript SDK, providing framework-specific patterns while the TypeScript SDK offers direct API access for maximum flexibility.
),
docsLink: '/docs/getting-started/react',
liveExampleLink: 'https://echo-react-boilerplate.vercel.app',
githubLink: 'https://github.com/Merit-Systems/echo/tree/master/templates/react',
},
{
title: 'Next.js',
icon: (
),
docsLink: '/docs/getting-started/next-js',
liveExampleLink: 'https://echo-next-boilerplate.vercel.app/',
githubLink: 'https://github.com/Merit-Systems/echo/tree/master/templates/next',
},
{
title: 'CLI',
icon: (
{'>'}
),
docsLink: '/docs/getting-started/cli',
githubLink: 'https://github.com/Merit-Systems/echo/tree/master/echo-typescript-cli-example',
},
]}
/>
## Templates
We provide a variety of ready-to-use templates to help you get started quickly with Echo:
### Core Templates
* [**Next.js**](/docs/getting-started/templates#nextjs) — Full-stack Next.js app with Echo integration
* [**React**](/docs/getting-started/templates#react) — Vite-powered React app with Echo SDK
* [**Assistant UI**](/docs/getting-started/templates#assistant-ui) — Full-featured chat UI with `@assistant-ui/react`
### Feature-Specific Templates
* **Next.js Chat** — Chat interface built with Next.js and Echo
* **Next.js Image Generation** — Image generation app with Echo billing
* **Next.js Video** — Video generation app with Echo integration
* **Next.js API Key** — Server-side API key management template
* **React Chat** — Chat interface for React apps
* **React Image** — Image generation for React apps
All templates are available through `echo-start` or in the [GitHub repository](https://github.com/Merit-Systems/echo/tree/master/templates). Visit our [templates documentation](/docs/getting-started/templates) for detailed setup instructions.
# API Reference
URL: /docs/advanced/api-reference
***
title: API Reference
description: V1 API reference for programmatic access to Echo
-------------------------------------------------------------
import { Callout } from 'fumadocs-ui/components/callout';
# API Reference
The Echo V1 API provides programmatic access for SDKs, CLI tools, and applications. All endpoints are prefixed with `/api/v1/`.
All API endpoints use JSON request/response format. Most endpoints require
authentication via API keys or JWT tokens.
## Authentication
The V1 API supports two authentication methods:
### API Keys
API keys are the primary authentication method for programmatic access. Include your API key in the request header:
```http
Authorization: Bearer your_api_key_here
```
### JWT Tokens
JWT tokens are used for OAuth flows and temporary access. Include JWT tokens in either header:
```http
Authorization: Bearer your_jwt_token_here
```
or
```http
X-Echo-Token: your_jwt_token_here
```
## Applications
### List User Apps
```http
GET /api/v1/apps
```
List all Echo apps owned by the authenticated user.
**Authentication:** API key or JWT token required
**Response Type:**
```typescript
{
items: Array;
has_next: boolean;
page: number;
page_size: number;
total_count: number;
}
```
>["items"][number]`} />
```http
GET /api/v1/apps/{id}
```
Get detailed app information for programmatic access.
**Authentication:** API key or JWT token required\
**Path Parameters:**
* `id` (UUID) - App ID
**Response Type:**
>`} />
## Balance & Billing
### Get User Balance
```http
GET /api/v1/balance
```
Get authenticated user's credit balance.
**Authentication:** API key or JWT token required\
**Query Parameters:**
* `echoAppId` (UUID, optional) - Get app-specific balance instead of global
**Response Type:**
>`} />
### Get Free Tier Balance
```http
POST /api/v1/balance/{appId}/free
```
Get user's free tier spending information for a specific app.
**Authentication:** API key or JWT token required
**Response Type:**
```typescript
{
spendPoolBalance: number;
userSpendInfo: UserSpendInfo;
}
```
>["userSpendInfo"]`} />
## Payments
### Create Payment Link
```http
POST /api/v1/stripe/payment-link
```
Generate Stripe payment link for credit purchases.
**Authentication:** API key or JWT token required
**Request Body:**
```typescript
{
amount: number;
successUrl?: string;
}
```
**Response Type:**
```typescript
{
paymentUrl: string;
sessionId: string;
expiresAt: string;
}
```
Payment amounts are in USD. The `successUrl` parameter is optional and will
redirect users after successful payment.
## Models & Capabilities
### Get Supported Models
```http
GET /api/v1/supported-models
```
Get list of supported AI models with pricing information.
**Authentication:** None required
**Response Type:**
```typescript
{
models: SupportedModel[];
models_by_provider: Record;
}
```
**SupportedModel Interface:**
```typescript
interface SupportedModel {
id: string;
name: string;
provider: string;
inputPrice: number;
outputPrice: number;
contextLength: number;
}
```
## User Management
### Get User Info
```http
GET /api/v1/user
```
Get authenticated user profile information.
**Authentication:** API key or JWT token required
**Response Type:**
```typescript
{
id: string;
email: string;
name: string | null;
createdAt: string;
updatedAt: string;
totalPaid: number;
totalSpent: number;
}
```
### Register Referral Code
```http
POST /api/v1/user/referral
```
Apply a referral code for credits on a specific app.
**Authentication:** API key or JWT token required
**Request Body:**
```typescript
{
echoAppId: string;
code: string;
}
```
**Response Type:**
```typescript
{
success: boolean;
message: string;
creditsAwarded?: number;
}
```
## Error Responses
All endpoints return appropriate HTTP status codes and JSON error responses:
```json
{
"error": "string",
"message": "string",
"statusCode": "number"
}
```
### Common Status Codes
* `200 OK` - Success
* `201 Created` - Resource created
* `400 Bad Request` - Invalid request data
* `401 Unauthorized` - Authentication required
* `403 Forbidden` - Insufficient permissions
* `404 Not Found` - Resource not found
* `500 Internal Server Error` - Server error
All endpoints require HTTPS in production environments. UUID parameters are
validated for proper format.
# Authorized Callback Urls
URL: /docs/advanced/callback-urls
***
title: Authorized Callback Urls
description: How to authorize callback URLs for your app correctly
------------------------------------------------------------------
# Authorized Callback URLs
Authorized callback URLs specify where users can be redirected after authentication. This is a critical security feature that prevents unauthorized redirects.
## Configuration
**Set your callback URL to your application's homepage URL.** This should be the deployed URL where your application is hosted in production.
Examples:
* `https://myapp.com`
* `https://myapp.vercel.app`
* `https://subdomain.mycompany.com`
## Development
**Localhost URLs are always allowed** for development purposes. You don't need to explicitly add localhost URLs to your authorized callback list.
## Next SDK Caveat
**Very important** - When productionizing your application with the Next SDK, you will need to append `/api/echo/callback` to your app's homepage route when inputting the correct authorized callback URL.
For example:
* If your app is hosted at `https://myapp.com`
* Set your authorized callback URL to `https://myapp.com/api/echo/callback`
# Direct Provider Clients
URL: /docs/advanced/direct-provider-clients
***
title: Direct Provider Clients
description: Use OpenAI, Anthropic, Google Gemini, or other provider SDKs directly without Echo SDK wrappers
------------------------------------------------------------------------------------------------------------
# Direct Provider Clients
You can use provider SDKs directly (OpenAI, Anthropic, Google, etc.) with Echo by overriding the base URL and API key. This is useful when you want to use provider-specific features or avoid the Echo SDK layer.
## OpenAI SDK
```typescript tab="TypeScript"
import OpenAI from 'openai';
const openai = new OpenAI({
baseURL: 'https://echo.router.merit.systems',
apiKey: process.env.ECHO_API_KEY, // Your Echo API key
});
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }],
});
```
```python tab="Python"
from openai import OpenAI
client = OpenAI(
base_url="https://echo.router.merit.systems",
api_key="your_echo_api_key"
)
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
)
```
## Anthropic SDK
```typescript tab="TypeScript"
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({
baseURL: 'https://echo.router.merit.systems',
apiKey: process.env.ECHO_API_KEY,
});
const response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello!' }],
});
```
```python tab="Python"
from anthropic import Anthropic
client = Anthropic(
base_url="https://echo.router.merit.systems",
api_key="your_echo_api_key"
)
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}]
)
```
## Google Gemini SDK
```typescript tab="TypeScript"
import { GoogleGenAI } from '@google/genai';
const genAI = new GoogleGenAI({
apiKey: process.env.ECHO_API_KEY,
httpOptions: {
baseUrl: 'https://echo.router.merit.systems',
apiVersion: 'v1',
},
});
const model = genAI.getGenerativeModel({ model: 'gemini-1.5-pro' });
const result = await model.generateContent('Hello!');
const response = result.text();
```
```python tab="Python"
import google.generativeai as genai
# Configure with Echo proxy
genai.configure(
api_key="your_echo_api_key",
transport="rest",
client_options={"api_endpoint": "https://echo.router.merit.systems/v1"}
)
model = genai.GenerativeModel('gemini-1.5-pro')
response = model.generate_content('Hello!')
print(response.text)
```
## Authentication Options
You can authenticate with Echo using either:
### API Key (Recommended for Server-Side)
Use your Echo API key from the dashboard:
```typescript
{
apiKey: process.env.ECHO_API_KEY;
}
```
### OAuth Access Token (For Client-Side)
Use an OAuth access token obtained from the OAuth flow:
```typescript
{
apiKey: accessToken; // JWT token from OAuth
}
```
Access tokens are short-lived JWTs obtained through the OAuth2 + PKCE flow.
They're ideal for client-side applications where you can't safely store API
keys.
## Streaming Responses
All streaming features work as expected:
```typescript
const stream = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Tell me a story' }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || '');
}
```
# How Echo Works
URL: /docs/advanced/how-echo-works
***
title: How Echo Works
description: Technical architecture guide for Echo's proxy layer, authentication, and billing system
----------------------------------------------------------------------------------------------------
import { Callout } from 'fumadocs-ui/components/callout';
# How Echo Works
Echo is a proxy layer that handles authentication, metering, and billing for LLM applications. This guide explains the technical architecture and integration patterns.
## System Overview & Architecture
Echo operates as a three-layer stack that sits between your application and LLM providers:
{`graph TB
A[Your Application] --> B[Echo SDK]
B --> C[Echo Proxy Layer]
C --> D[OpenAI/Anthropic/Claude]
C --> E[Billing & Analytics]
C --> F[User Management]`}
### Core Components
**Echo Control** (`packages/app/control`)
* Web dashboard for app management, user analytics, billing
* PostgreSQL database with Prisma ORM
* NextAuth sessions for web UI authentication
**Echo Server** (`packages/app/server`)
* API proxy that intercepts LLM requests
* Handles authentication validation and usage metering
* Routes requests to appropriate LLM providers
* Records transactions and calculates costs in real-time
**SDK Ecosystem**
* `echo-typescript-sdk`: Universal client for all platforms
* `echo-react-sdk`: OAuth2+PKCE for client-side LLM calls
* `echo-next-sdk`: Server-side integration patterns
### Request Flow
Every LLM request follows this path:
1. **Authentication**: SDK includes API key or JWT token
2. **Validation**: Echo proxy validates credentials and permissions
3. **Routing**: Request forwarded to appropriate LLM provider
4. **Metering**: Response tokens counted and costs calculated
5. **Billing**: Transaction recorded with user/app attribution
6. **Response**: LLM response returned to your application
Echo acts as a transparent proxy. Your application uses standard OpenAI SDK patterns but gains billing, analytics, and user management automatically.
## The Proxy Layer
Echo Server handles the complex middleware between your app and LLM providers. Here's how each component works:
### Request Interception
All LLM requests route through Echo's proxy endpoints:
```typescript
// Your app calls this
const response = await openai.chat.completions.create({
model: "gpt-5",
messages: [{ role: "user", content: "Hello" }]
});
// Echo intercepts at https://echo.router.merit.systems/v1/chat/completions
// Validates auth, meters usage, forwards to OpenAI, records billing
```
The proxy preserves OpenAI's API contract while injecting billing logic:
```typescript
// echo-server/src/routes/chat/completions.ts
export async function handleChatCompletion(request: AuthenticatedRequest) {
// 1. Extract and validate authentication
const { user, echoApp } = request.auth;
// 2. Forward to LLM provider
const llmResponse = await provider.chat.completions.create(request.body);
// 3. Calculate costs from token usage
const cost = calculateTokenCost(llmResponse.usage, request.body.model);
// 4. Record transaction
await recordTransaction({
userId: user.id,
echoAppId: echoApp.id,
cost,
tokens: llmResponse.usage
});
return llmResponse;
}
```
## Authentication & Security Architecture
Echo supports two distinct authentication patterns optimized for different use cases:
### API Key Authentication (Backend/Server-Side)
Traditional server-side pattern for backend applications:
```typescript
// Your server code
import { EchoClient } from '@merit-systems/echo-typescript-sdk';
const client = new EchoClient({
apiKey: process.env.ECHO_API_KEY // Scoped to specific Echo app
});
const response = await client.models.chatCompletion({
model: "gpt-5",
messages: [{ role: "user", content: "Hello" }]
});
```
API key validation flow:
1. Extract key from `Authorization: Bearer ` header
2. Hash and lookup in database with app association
3. Validate key is active and app is not archived
4. Attach user and app context to request
### OAuth2 + PKCE Authentication (Frontend/Client-Side)
Pattern that enables secure LLM calls directly from browsers:
```typescript
// React component - no API keys needed
import { useEchoOpenAI } from '@merit-systems/echo-react-sdk';
function ChatComponent() {
const { openai, isReady } = useEchoOpenAI();
// This runs in the browser with user-scoped JWT tokens
const response = await openai.chat.completions.create({
model: "gpt-5",
messages: [{ role: "user", content: "Hello" }]
});
}
```
OAuth2 + PKCE flow eliminates API key exposure:
1. **Authorization Request**: User redirects to Echo OAuth endpoint
2. **User Consent**: User authorizes your app to access their Echo balance
3. **Code Exchange**: Your app exchanges authorization code for JWT tokens
4. **Token Usage**: Short-lived JWTs authenticate LLM requests
5. **Automatic Refresh**: SDK handles token renewal transparently
This dual authentication model supports both traditional backend patterns and modern frontend architectures while maintaining security and proper billing attribution.
# Advanced
URL: /docs/advanced
***
title: Advanced
description: Advanced topics
----------------------------
* **[How Echo Works](/docs/advanced/how-echo-works)** - Technical architecture guide for Echo's proxy layer, authentication, and billing system.
* **[API Reference](/docs/advanced/api-reference)** - Complete V1 API reference for programmatic access to Echo endpoints.
# Raw Proxy Access
URL: /docs/advanced/raw-proxy
***
title: Raw Proxy Access
description: Use Echo from any language or platform by calling the OpenAI-compatible proxy directly
---------------------------------------------------------------------------------------------------
# Raw Proxy Access
Echo exposes an OpenAI-compatible proxy that can be used from any language or HTTP client. This is ideal for platforms and languages where no Echo SDK exists.
## Base URL
All requests should be sent to:
```
https://echo.router.merit.systems
```
## Authentication
Include your Echo API key or OAuth access token in the Authorization header:
```http
Authorization: Bearer your_echo_api_key_or_token
```
## Endpoints
Echo proxies the standard OpenAI API endpoints:
### Chat Completions
```http
POST https://echo.router.merit.systems/v1/chat/completions
```
Refer to [OpenAI API Reference](https://platform.openai.com/docs/api-reference/chat) for more details.
### Responses
```http
POST https://echo.router.merit.systems/v1/responses
```
Refer to [OpenAI API Reference](https://platform.openai.com/docs/api-reference/responses) for more details.
### Images
```http
POST https://echo.router.merit.systems/v1/images/generations
```
Refer to [OpenAI API Reference](https://platform.openai.com/docs/api-reference/images) for more details.
### Videos
```http
POST https://echo.router.merit.systems/v1/videos
```
Refer to [OpenAI API Reference](https://platform.openai.com/docs/api-reference/videos) for more details.
## Examples
```bash tab="cURL"
curl https://echo.router.merit.systems/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_echo_api_key" \
-d '{
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello!"}]
}'
```
```python tab="Python"
import requests
response = requests.post(
"https://echo.router.merit.systems/v1/chat/completions",
headers={
"Authorization": "Bearer your_echo_api_key",
"Content-Type": "application/json"
},
json={
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello!"}]
}
)
print(response.json())
```
```ruby tab="Ruby"
require 'net/http'
require 'json'
uri = URI('https://echo.router.merit.systems/v1/chat/completions')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer your_echo_api_key'
request['Content-Type'] = 'application/json'
request.body = {
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }]
}.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.body
```
```go tab="Go"
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
url := "https://echo.router.merit.systems/v1/chat/completions"
payload := map[string]interface{}{
"model": "gpt-4",
"messages": []map[string]string{
{"role": "user", "content": "Hello!"},
},
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer your_echo_api_key")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
```
```rust tab="Rust"
use reqwest;
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box> {
let client = reqwest::Client::new();
let response = client
.post("https://echo.router.merit.systems/v1/chat/completions")
.header("Authorization", "Bearer your_echo_api_key")
.json(&json!({
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello!"}]
}))
.send()
.await?;
println!("{}", response.text().await?);
Ok(())
}
```
```php tab="PHP"
'gpt-4',
'messages' => [
['role' => 'user', 'content' => 'Hello!']
]
]));
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
?>
```
## Streaming
To enable streaming, add `"stream": true` to your request:
```bash
curl https://echo.router.merit.systems/v1/chat/completions \
-H "Authorization: Bearer your_echo_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello!"}],
"stream": true
}'
```
Streaming responses use Server-Sent Events (SSE) format.
## Response Format
Responses follow the OpenAI API format. See the [OpenAI API documentation](https://platform.openai.com/docs/api-reference/chat/create) for complete response schemas.
Example response:
```json
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-4",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21
}
}
```
## Error Handling
Echo returns standard HTTP error codes:
* `401 Unauthorized` - Invalid or missing API key
* `402 Payment Required` - Insufficient credits
* `429 Too Many Requests` - Rate limit exceeded
* `500 Internal Server Error` - Server error
The proxy is fully compatible with the OpenAI API specification. Any HTTP
client or library that works with OpenAI will work with Echo by simply
changing the base URL and API key.
# Injecting System Prompts
URL: /docs/advanced/system-prompt
***
title: Injecting System Prompts
description: How to use Vercel AI's SDK to give context to your LLM.
--------------------------------------------------------------------
# Next SDK
## Passing System Prompts from Client to Server
When building AI applications, you often need to provide context or instructions to your language model through system prompts. Here's how to pass system prompts from your client-side code to your server-side API route using the Vercel AI SDK.
### Client
On the client side, you can pass additional data like system prompts through the `body` parameter of the `sendMessage` function:
```tsx
const systemText: string = "Be as sycophantic as possible."
sendMessage({
text: "Am i absolutely right?",
},
{
body: { systemText },
});
```
The `systemText` variable should contain your system prompt or context that you want to provide to the language model.
### Server
On the server side (in your API route), extract the system prompt from the request body and use it in your `streamText` call:
```tsx
const { messages, systemText } = await req.json();
const result = streamText({
model: openai("gpt-5"),
system: systemText,
messages: convertToModelMessages(messages),
});
```
This pattern allows you to dynamically provide context to your language model based on client-side state or user interactions, making your AI applications more flexible and context-aware.
# React SDK
## Client-Side System Prompts
With the React SDK, you can handle system prompts entirely on the client side without needing a server API route. This is useful for simpler applications or when you want to keep everything client-side.
```tsx
const handleGen = async (name: string) => {
const systemText = `Be as sycophantic as possible for ${name}.`;
const { text } = await generateText({
model: openai('gpt-5'),
system: systemText,
prompt: 'Am i absolutely right?'
});
setResult(text);
};
```
# Cookbook
URL: /docs/cookbook
***
title: Cookbook
description: Echo examples Cookbook.
------------------------------------
*Coming soon.*
# TypeScript CLI
URL: /docs/getting-started/cli
***
title: TypeScript CLI
description: Build AI CLI apps with Echo's billing infrastructure
-----------------------------------------------------------------
import { Step, Steps } from 'fumadocs-ui/components/steps';
# TypeScript CLI with Echo
Create CLI tools that generate revenue from AI usage. Echo handles user authentication, billing, and AI provider access through a simple API key flow.
### Install SDKs
```sh
npm i @merit-systems/echo-typescript-sdk ai enquirer open
```
```sh
yarn add @merit-systems/echo-typescript-sdk ai enquirer open
```
```sh
pnpm add @merit-systems/echo-typescript-sdk ai enquirer open
```
```sh
bun add @merit-systems/echo-typescript-sdk ai enquirer open
```
### Create Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get your `app_id`.
### Set up authentication
```typescript title="cli.ts"
import { EchoClient, createEchoOpenAI } from '@merit-systems/echo-typescript-sdk';
import { generateText } from 'ai';
import enquirer from 'enquirer';
import { open } from 'open';
const APP_ID = 'your-echo-app-id';
// Get API key from user
console.log('Opening Echo to create your API key...');
await open(`https://echo.merit.systems/app/${APP_ID}/keys`);
const { apiKey } = await enquirer.prompt({
type: 'input',
name: 'apiKey',
message: 'Enter your API key:'
}) as { apiKey: string };
```
### Create AI provider
```typescript title="cli.ts"
// Create Echo client for balance/payments
const echo = new EchoClient({ apiKey });
// Create OpenAI provider with Echo billing
const openai = createEchoOpenAI(
{ appId: APP_ID },
async () => apiKey
);
```
### Make AI calls
```typescript title="cli.ts"
// Generate text with automatic billing
const { text } = await generateText({
model: openai('gpt-5'),
prompt: 'Explain quantum computing in simple terms',
});
console.log(text);
```
### Add balance checking
```typescript title="cli.ts"
// Check user's balance
const balance = await echo.balance.getBalance();
console.log(`Balance: $${balance.balance}`);
// Create top-up link if needed
if (balance.balance < 1) {
const payment = await echo.payments.createPaymentLink({
amount: 10,
});
console.log('Low balance. Opening payment link...');
await open(payment.paymentLink.url);
}
```
### Run your CLI
```sh
npx tsx cli.ts
```
### Prosper.
## Next Steps
For detailed documentation and advanced features, see the [TypeScript SDK overview](/docs/typescript-sdk).
# Next.js
URL: /docs/getting-started/next-js
***
title: Next.js
description: Use Echo to make monetized AI Next.js apps.
--------------------------------------------------------
import { Step, Steps } from 'fumadocs-ui/components/steps';
import { File, Folder, Files } from 'fumadocs-ui/components/files';
# Getting Started with Next.js
### Install the Echo Next SDK
```sh
npm i @merit-systems/echo-next-sdk
```
```sh
yarn add @merit-systems/echo-next-sdk
```
```sh
pnpm add @merit-systems/echo-next-sdk
```
```sh
bun add @merit-systems/echo-next-sdk
```
### Install Vercel AI SDK
```sh
npm i ai
```
```sh
yarn add ai
```
```sh
pnpm add ai
```
```sh
bun add ai
```
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Add Echo configuration file
```typescript title="src/echo/index.ts" lineNumbers
import Echo from "@merit-systems/echo-next-sdk";
export const { handlers, isSignedIn, openai, anthropic } = Echo({
appId: "YOUR_ECHO_APP_ID", // from previous step
});
```
### Add Echo Routes
```typescript title="src/app/api/echo/[...echo]/route.ts" lineNumbers
import { handlers } from "@/echo";
export const { GET, POST } = handlers;
```
### Sign In From Client
Create an action in the front-end that redirects users to authorize your application with [echo.merit.systems](echo.merit.systems).
```tsx title="app/page.tsx" lineNumbers
import { signIn, isSignedIn } from "@merit-systems/echo-next-sdk/client";
export default Home() {
if (!isSignedIn()) {
return
}
return (
Monetization Activated 📈
)
}
```
Sign in will hit the route we setup in [Step 4](http://echo.merit.systems/docs/getting-started/next-js#add-echo-routes) and redirect from the server.
### Use `generateText` / `streamText` from the server
Echo is a standard [Vercel AI SDK](https://ai-sdk.dev/docs/introduction) provider.
Add [Vercel AI SDK server routes](https://ai-sdk.dev/docs/ai-sdk-ui/completion) with Echo providers.
```typescript title="app/api/completion/route.ts" lineNumbers
// [!code ++:1]
import { openai } from "@/echo";
import { convertToModelMessages, streamText } from "ai";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-5"),
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}
```
### `useChat()` from the client
```tsx title="app/components/chat.tsx" lineNumbers
"use client";
import { useChat } from "@ai-sdk/react";
import { useState } from "react";
export default function Chat() {
const [input, setInput] = useState("");
const { messages, sendMessage } = useChat();
return (
{messages.map((message) => (
{JSON.stringify(message)}
))}
setInput(e.currentTarget.value)} />
);
}
```
Then add use it on the authenticated page.
```tsx title="app/page.tsx" lineNumbers
// [!code ++:1]
import Chat from "./components/chat";
import { signIn, isSignedIn } from "@merit-systems/echo-next-sdk/client";
export default Home() {
if (!isSignedIn()) {
return
}
// [!code ++:1]
return ;
// [!code --:3]
return (
Monetization Activated 📈
)
}
```
### Prosper
## Next Steps
For detailed documentation and advanced features, see the [Next.js SDK overview](/docs/next-sdk).
# React
URL: /docs/getting-started/react
***
title: React
description: Integrate Echo into your React application
-------------------------------------------------------
import { Step, Steps } from 'fumadocs-ui/components/steps';
# Getting Started with Echo in React
### Create a React app using Vite
Run the following commands to create a new React app using Vite.
```sh lineNumbers
npm create vite@latest echo-react -- --template react-ts
cd echo-react
npm install
npm run dev
```
```sh lineNumbers
yarn create vite echo-react --template react-ts
cd echo-react
yarn install
yarn dev
```
```sh lineNumbers
pnpm create vite echo-react --template react-ts
cd echo-react
pnpm install
pnpm dev
```
```sh lineNumbers
bun create vite echo-react --template react-ts
cd echo-react
bun install
bun dev
```
### Install `@merit-systems/echo-react-sdk` and `ai`
The Echo React SDK gives you access to prebuilt components, hooks and helpers to make LLM billing and calling easier.
The [Vercel AI SDK](https://ai-sdk.dev/docs/introduction) is the standard SDK for interacting with AI in Typescript.
Run the following commands to install the dependencies.
```sh lineNumbers
npm i @merit-systems/echo-react-sdk ai
```
```sh lineNumbers
yarn add @merit-systems/echo-react-sdk ai
```
```sh lineNumbers
pnpm add @merit-systems/echo-react-sdk ai
```
```sh lineNumbers
bun add @merit-systems/echo-react-sdk ai
```
### Setup your Echo App
1. In the Echo dashboard navigate to [**Create App**](https://echo.merit.systems/new).
2. Copy your `app_id` and paste it into your `.env` file.
The final result should resemble the following:
```sh title=".env" lineNumbers
VITE_ECHO_APP_ID=b30f0f78-4000-8000-000000000000
```
### Import your Echo App ID
In your `main.tsx` file, import your app id.
Include your App ID from the prior step.
```tsx title="main.tsx" lineNumbers
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
// [!code ++:1]
const APP_ID = import.meta.env.VITE_ECHO_APP_ID;
// [!code ++:3]
if (!APP_ID) {
throw new Error('Add your Echo App ID to .env');
}
createRoot(document.getElementById('root')!).render(
,
)
```
### `` component
The `` component provides session and user context to Echo's hooks and components. It's recommended to wrap
your entire app at the entry point with `` to make Echo globally available.
Pass your Echo App ID as a prop to ``.
```tsx title="main.tsx" lineNumbers
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
// [!code ++:1]
import { EchoProvider } from '@merit-systems/echo-react-sdk';
const APP_ID = import.meta.env.VITE_ECHO_APP_ID;
if (!APP_ID) {
throw new Error('Add your Echo App ID to .env');
}
createRoot(document.getElementById('root')!).render(
// [!code ++:1]
// [!code ++:1]
,
)
```
### `` component
`` is a React component that can be placed anywhere in your app. When a user clicks
it will redirect to authenticate with [echo.merit.systems](echo.merit.systems). After authentication,
Echo can manage all LLM calls.
```tsx title="src/App.tsx" lineNumbers
// [!code ++:1]
import { EchoSignIn } from '@merit-systems/echo-react-sdk';
function App() {
return (
<>
// [!code ++:1]
AI Coming Soon
>
);
}
export default App;
```
### `useEcho()` hook
The `useEcho()` hook surfaces everything you need to show Echo state to your users.
```tsx title="src/App.tsx" lineNumbers
// [!code ++:1]
import { EchoSignIn, useEcho } from '@merit-systems/echo-react-sdk';
function App() {
// [!code ++:1]
const { isAuthenticated, user, balance, signOut } = useEcho();
return (
<>
// [!code ++:9]
{!isAuthenticated && "Sign in to continue"}
{isAuthenticated && (
Welcome, {user?.name}!
Email: {user?.email}
Balance: {balance?.balance}
)}
AI Coming Soon
>
);
}
export default App;
```
### `generateText()` from OpenAI
Create a new component and use the Vercel AI SDK with Echo models.
```tsx title="src/App.tsx" lineNumbers
import { EchoSignIn, useEcho } from '@merit-systems/echo-react-sdk';
// [!code ++:1]
import AIComponent from './AIComponent';
function App() {
const { isAuthenticated, user, balance, signOut } = useEcho();
return (
<>
{!isAuthenticated && "Sign in to continue"}
{isAuthenticated && (
Welcome, {user?.name}!
Email: {user?.email}
Balance: {balance?.balance}
)}
// [!code --:1]
AI Coming Soon
// [!code ++:1]
{isAuthenticated && }
>
);
}
export default App;
```
AI SDK's [`generateText()`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text) is used to generate a single response from the model.
`useEchoModelProviders()` routes the requests through Echo.
```tsx title="src/AIComponent.tsx" lineNumbers
// [!code ++:100]
import { useState } from 'react';
import { generateText } from 'ai';
import { useEchoModelProviders } from '@merit-systems/echo-react-sdk';
export default function AIComponent() {
const [result, setResult] = useState("");
const { openai } = useEchoModelProviders();
const handleGen = async () => {
const { text } = await generateText({
model: openai('gpt-5-nano'),
messages: [
{ role: 'user', content: 'Two sentences. What is the cleanest way to make $1M?' }
]
});
setResult(text);
};
return (
{result}
);
}
```
### Switch to `streamText()`
The AI SDK [`streamText()`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) function is used to incrementally show the response from the model.
```tsx title="src/AIComponent.tsx" lineNumbers
import { useState } from 'react';
// [!code --:1]
import { generateText } from 'ai';
// [!code ++:1]
import { streamText } from 'ai';
import { useEchoModelProviders } from '@merit-systems/echo-react-sdk';
export default function AIComponent() {
const [result, setResult] = useState("");
const { openai } = useEchoModelProviders();
const handleGen = async () => {
// [!code ++:2]
setResult("Thinking...");
const { text } = await streamText({
// [!code --:1]
const { text } = await generateText({
model: openai('gpt-5-nano'),
messages: [
{ role: 'user', content: 'Two sentences. What is the cleanest way to make $1M?' }
]
});
// [!code ++:3]
for await (const chunk of textStream) {
setResult(prev => prev + chunk);
}
};
return (
{result}
);
}
```
### Enjoy the fruits of your labor.
Run your project with the following command.
```sh lineNumbers
npm run dev
```
```sh lineNumbers
yarn dev
```
```sh lineNumbers
pnpm dev
```
```sh lineNumbers
bun dev
```
Visit your app's homepage at [`http://localhost:5173`](http://localhost:5173). Sign in and click "Generate Wisdom".
## Next Steps
For detailed documentation and advanced features, see the [React SDK overview](/docs/react-sdk).
# Templates
URL: /docs/getting-started/templates
***
title: Templates
description: Get started quickly with Echo using pre-built templates
--------------------------------------------------------------------
import { Step, Steps } from 'fumadocs-ui/components/steps';
The easiest way to get started with Echo is to use one of our pre-built templates.
## React
The React template is a simple application that uses Vite and `echo-react-sdk`. Uniquely Echo does not require
a server to make API calls because it handles Oauth directly in the browser.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/react)
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create a React app using Vite
Run the following commands to create a new React app using Vite.
```sh lineNumbers
npx echo-start@latest --template vite --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template vite --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template vite --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template vite --app-id YOUR_ECHO_APP_ID
```
## Next.js
The Next.js template is a full-stack application that uses the Next.js framework with the `echo-next-sdk`.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/next)
### Create an Echo app
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create a Next.js app
Run the following commands to create a new Next.js app.
```sh lineNumbers
npx echo-start@latest --template next --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template next --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template next --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template next --app-id YOUR_ECHO_APP_ID
```
## Assistant UI
A full-featured chat UI powered by `@assistant-ui/react` integrated with Echo and the Vercel AI SDK v5.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/assistant-ui)
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create an Assistant UI app
Run the following commands to create a new app.
```sh lineNumbers
npx echo-start@latest --template assistant-ui --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template assistant-ui --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template assistant-ui --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template assistant-ui --app-id YOUR_ECHO_APP_ID
```
## Next.js Chat
A complete chat application with beautiful UI components, real-time balance display, and streaming AI responses.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/next-chat)
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create a Next.js Chat app
Run the following commands to create a new chat app.
```sh lineNumbers
npx echo-start@latest --template next-chat --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template next-chat --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template next-chat --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template next-chat --app-id YOUR_ECHO_APP_ID
```
## Next.js Image Generation
AI-powered image generation application with gallery view, download capabilities, and automatic cost tracking.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/next-image)
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create a Next.js Image app
Run the following commands to create a new image generation app.
```sh lineNumbers
npx echo-start@latest --template next-image --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template next-image --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template next-image --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template next-image --app-id YOUR_ECHO_APP_ID
```
## Next.js Video Generation
AI-powered video generation application with video preview, playback, and Echo billing integration.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/next-video-template)
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create a Next.js Video app
Run the following commands to create a new video generation app.
```sh lineNumbers
npx echo-start@latest --template next-video-template --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template next-video-template --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template next-video-template --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template next-video-template --app-id YOUR_ECHO_APP_ID
```
## Next.js API Key Template
Server-side API key management with PostgreSQL database, Prisma ORM, and Docker setup for local development.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/nextjs-api-key-template)
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create a Next.js API Key app
Run the following commands to create a new API key management app.
```sh lineNumbers
npx echo-start@latest --template nextjs-api-key-template --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template nextjs-api-key-template --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template nextjs-api-key-template --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template nextjs-api-key-template --app-id YOUR_ECHO_APP_ID
```
## React Chat
Chat interface built for React applications with Vite, Tailwind CSS, and Echo OAuth integration.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/react-chat)
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create a React Chat app
Run the following commands to create a new React chat app.
```sh lineNumbers
npx echo-start@latest --template react-chat --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template react-chat --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template react-chat --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template react-chat --app-id YOUR_ECHO_APP_ID
```
## React Image Generation
Client-side image generation for React applications with Echo billing and image gallery.
[View on GitHub](https://github.com/Merit-Systems/echo/tree/master/templates/react-image)
### Create an Echo App
Go to [echo.merit.systems/new](https://echo.merit.systems/new) to get an `app_id`.
### Create a React Image app
Run the following commands to create a new React image generation app.
```sh lineNumbers
npx echo-start@latest --template react-image --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
yarn dlx echo-start@latest --template react-image --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
pnpx echo-start@latest --template react-image --app-id YOUR_ECHO_APP_ID
```
```sh lineNumbers
bunx echo-start@latest --template react-image --app-id YOUR_ECHO_APP_ID
```
# Claiming Profits
URL: /docs/money/claiming-profits
***
title: Claiming Profits
description: Get paid through Echo's core features
--------------------------------------------------
## Markups
Both Echo Apps and Users have strategies to monetize on Echo.
Apps earn a mark-up on each transaction, which is set by the app owner. This represents a percent upcharge on each token that passes through the application.
The mark-up earned can be claimed only when a Github user or repository has been associated with the app. This can be done in the app's settings page.
Once this is done, users can find their pending claims available on the [creator earnings page](https://echo.merit.systems/earnings/creator).
## Claims
Claims are processed on a daily basis and paid out via [the Terminal](https://terminal.merit.systems). The funds will be sent to the github User or Repository which is set on the Echo app
at the time of payout. If no Github account is set, the transfer will not be made on that day. You will be able to claim or redistribute your funds through the terminal once they are transferred.
For more information on the Terminal, please see the [docs](https://www.merit.systems/docs/terminal).
## Referrals
User referrals will allow app owners to incentivize users to market their application. App owners can agree to pay, for example, 5% of profit
to a referrer. Therefore, if a user signs up via a referral link, the creator of the referral link will earn 5% of all profits generated in the lifetime of the user.
This feature is in early beta and may not work as expected. Please reach out in the [discord](https://discord.gg/merit) if you are interested in setting up referrals for your application.
### Discord
Please reach out in our [discord](https://discord.gg/merit) if you have any questions about how claims work, where your claimed funds are, or are experiencing any issues.
We are actively looking for feedback on our claims system and would love to hear your thoughts.
# Client
URL: /docs/next-sdk/client
***
title: Client
description: Echo's Next.js SDK client-side functionality.
----------------------------------------------------------
Echo maintains authentication using HTTP Cookies. The browser does not have access
to these so login state needs to be handled by server components.
# Setup Provider
Create a `providers.tsx` file to wrap your app with the Echo provider:
```typescript
"use client";
import { EchoProvider } from '@merit-systems/echo-next-sdk/client';
export function Providers({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
Then wrap your app in `layout.tsx`:
```typescript
import { Providers } from './providers';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
# Using the useEcho Hook
The `useEcho` hook provides access to user data, balance information, and authentication methods:
```typescript
import { useEcho } from '@merit-systems/echo-next-sdk/client';
export default function MyComponent() {
const { user, balance, freeTierBalance, signIn, signOut, echoClient } = useEcho();
if (!user) {
return ;
}
return (
Welcome {user.name}!
Balance: ${balance?.balance || 0}
);
}
```
# Direct API Access with echoClient
For more advanced use cases, you can access the `echoClient` directly to call any Echo API method:
```typescript
import { useEcho } from '@merit-systems/echo-next-sdk/client';
export default function PaymentComponent() {
const { echoClient } = useEcho();
const handleCreatePayment = async () => {
if (!echoClient) return;
try {
// Create payment link
const paymentLink = await echoClient.payments.createPaymentLink({
amount: 10,
description: 'Credits',
});
// Get detailed user info
const userInfo = await echoClient.users.getUserInfo();
// Check balance
const balance = await echoClient.balance.getBalance();
} catch (error) {
console.error('Error:', error);
}
};
return ;
}
```
The `echoClient` provides full access to all Echo API endpoints. For a complete list of available methods, see the [TypeScript SDK](../typescript-sdk) documentation.
# Overview
URL: /docs/next-sdk
***
title: Overview
description: Echo Next.js SDK overview.
---------------------------------------
Echo's Next.js SDK handles authentication between your web app and Echo.
Get started quickly with the Next.js [quickstart guide](https://echo.merit.systems/docs/getting-started/next-js).
* [Server](./next-sdk/server): Next.js App Router server routes and authentication logic.
* [Client](./next-sdk/client): Client-side utilities for Echo authentication.
# Server
URL: /docs/next-sdk/server
***
title: Server
description: Echo's Next.js SDK server-side functionality.
----------------------------------------------------------
Echo's Next.js SDK will handle all authentication logic out of the box.
Configure Echo with the Echo constructor.
```typescript title="src/echo/index.ts"
import Echo from '@merit-systems/echo-next-sdk';
export const {
// Echo Auth Routes
handlers,
// Server-side utils
getUser,
isSignedIn,
// AI Providers
openai,
anthropic,
google,
} = Echo({
appId: 'ECHO_APP_ID',
});
```
By re-exporting the Echo constructor results, you can use throughout your server logic.
The `Echo` constructor takes the following configuration params.
## Handlers
```typescript
import handlers from '@/echo';
export { GET, POST } = handlers;
```
Exporting these routes will expose default authentication routes which can be used from the client. In most cases your code will not need to touch these routes.
* `api/echo/signin`
* `api/echo/callback`
* `api/echo/refresh`
**Very important** - When productionizing your application, you will need to append `/api/echo/callback` to your app's homepage route when inputting the correct authorized
callback Url.
## Server Utilities
```typescript
import { getUser, isSignedIn } from "@/echo";
export default async function Page() {
const signedIn = await isSignedIn();
if (!signedIn) {
return ;
} else {
const user = await getUser();
return ;
}
}
```
## AI Providers
Echo's Next.js SDK provides a thin wrapper around the Vercel AI SDK.
Echo follows their [recommended pattern](https://ai-sdk.dev/docs/getting-started/nextjs-app-router#create-a-route-handler) with a small diff, to route through Echo
instead of the model provider.
```typescript
// [!code --:1]
import { openai } from '@ai-sdk/openai';
// [!code ++:1]
import { openai } from '@/echo';
import { streamText, UIMessage, convertToModelMessages } from 'ai';
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: openai('gpt-5'),
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}
```
# Anthropic Provider
URL: /docs/providers/anthropic
***
title: Anthropic Provider
description: Claude models with Echo billing integration
--------------------------------------------------------
# Anthropic Provider
The Anthropic provider gives you access to Claude models through the Vercel AI SDK with automatic Echo billing integration.
## Supported Models
All Anthropic models are supported via the `AnthropicModel` type:
# Gemini Provider
URL: /docs/providers/gemini
***
title: Gemini Provider
description: Google Gemini models with Echo billing integration
---------------------------------------------------------------
# Gemini Provider
The Gemini provider gives you access to Google's Gemini models through the Vercel AI SDK with automatic Echo billing integration.
## Supported Models
All Gemini models are supported via the `GeminiModel` type:
## ⚠️ Gemini Limitations
**Important:** Gemini is currently only supported via the `/chat/completions` endpoint. This means:
* Direct Gemini API streaming may not work as expected
* For the most reliable streaming experience, ensure your implementation uses the chat completions interface
* To enable this, you should use the OpenAI Provider, which will hit Gemini's supported chat/completions endpoint.
* For more information, see Google's documentation [here](https://cloud.google.com/vertex-ai/generative-ai/docs/samples/generativeaionvertexai-gemini-chat-completions-non-streaming).
* Streaming will be supported through the Vercel interface for Gemini as soon as possible.
```typescript
const result = streamText({
model: openai.chat('gemini-2.0-flash'),
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
```
To instantiate `openai.chat` in this example, see the following guides:
For React applications, see [React SDK LLM Integration](/docs/react-sdk/llm-integration)
For server-side usage, see [Next.js SDK LLM Integration](/docs/next-sdk/llm-integration)
# OpenAI Provider
URL: /docs/providers/openai
***
title: OpenAI Provider
description: GPT models with Echo billing integration
-----------------------------------------------------
# OpenAI Provider
The OpenAI provider gives you access to GPT models through the Vercel AI SDK with automatic Echo billing integration.
## Supported Models
All OpenAI models are supported via the `OpenAIModel` type:
## Additional Features
### Responses API Support
The OpenAI Responses API is fully supported through Echo's OpenAI provider.
```typescript
const result = streamText({
model: openai.responses('gpt-5'),
messages: convertToModelMessages(messages),
});
```
### Image Generation with GPT-image-1
Echo also supports OpenAI's image generation capabilities through the GPT-image-1 model:
```typescript
const result = await generateImage({
model: openai.image('gpt-image-1'),
prompt,
});
```
To instantiate `openai.responses` in these examples, see the following guides:
For React applications, see [React SDK LLM Integration](/docs/react-sdk/llm-integration)
For server-side usage, see [Next.js SDK LLM Integration](/docs/next-sdk/llm-integration)
# Components
URL: /docs/react-sdk/components
***
title: Components
description: Pre-built UI components for authentication and payments
--------------------------------------------------------------------
# Components
**Recommended Approach**: For most applications, use `` as your
primary component. It handles the complete user lifecycle including
authentication, balance display, token purchases, and sign-out functionality
in a single, polished interface.
## EchoTokens
The `` button component handles authentication, balance display, token purchases, and sign-out in one component.
```tsx
import { EchoTokens } from '@merit-systems/echo-react-sdk';
// Complete user management - handles everything
console.log('New balance:', balance)}
onError={error => console.error('Error:', error)}
/>;
```
### What EchoTokens Handles
* **Authentication**: Shows sign-in button when user is not authenticated
* **Balance Display**: Shows user's available credits (free tier + paid)
* **Token Purchases**: Modal with purchase options and Stripe integration
* **Sign Out**: Built-in sign-out button in the purchase modal
* **Avatar Support**: Optional user profile picture display
* **Error Handling**: Comprehensive error states and messaging
### Usage Patterns
```tsx
// Basic usage - handles everything automatically
// With avatar and custom purchase amount
// Custom styling and callbacks
{
console.log('Purchase successful!', balance);
// Refresh your app state, show success message, etc.
}}
onError={(error) => {
console.error('Operation failed:', error);
// Handle errors, show user feedback, etc.
}}
/>
// Custom button wrapper
```
### Props
```typescript
import type { EchoTokensProps } from '@merit-systems/echo-react-sdk';
```
#### EchoTokensProps
***
## Individual Components
**Most apps should use `` instead** - The individual components
below are provided for advanced use cases where you need granular control.
EchoTokens handles the complete user flow automatically.
### EchoSignIn
Drop-in component for user authentication with customizable styling and callbacks.
```tsx
import { EchoSignIn } from '@merit-systems/echo-react-sdk';
// Default styled button
console.log('Signed in:', user)}
onError={(error) => console.error('Sign in failed:', error)}
/>
// Custom children (renders as clickable wrapper)
```
**Consider using `` instead** - It includes sign-in
functionality plus balance management and token purchases in a single
component.
#### Props
```typescript
import type { EchoSignInProps } from '@merit-systems/echo-react-sdk';
```
### EchoSignOut
Component for user sign-out with customizable styling and callbacks.
```tsx
import { EchoSignOut } from '@merit-systems/echo-react-sdk';
// Default styled button
console.log('Signed out successfully')}
onError={(error) => console.error('Sign out failed:', error)}
/>
// Custom children (renders as clickable wrapper)
```
**Consider using `` instead** - It includes a sign-out button in
the credits modal, providing a complete user management experience.
#### Props
```typescript
import type { EchoSignOutProps } from '@merit-systems/echo-react-sdk';
```
### Logo
Echo logo component with customizable variants.
```tsx
import { Logo } from '@merit-systems/echo-react-sdk';
;
```
# Hooks
URL: /docs/react-sdk/hooks
***
title: Hooks
description: Core hooks for authentication, model providers, and platform integration
-------------------------------------------------------------------------------------
# Hooks
## useEcho
Primary hook for accessing authentication state, user information, and core functionality.
```tsx
import { useEcho } from '@merit-systems/echo-react-sdk';
function AuthComponent() {
const {
user,
isAuthenticated,
isLoading,
error,
signIn,
signOut,
token,
getToken,
} = useEcho();
if (isLoading) {
return
Loading...
;
}
if (error) {
return
Error: {error}
;
}
if (!isAuthenticated) {
return ;
}
return (
Welcome {user?.name || user?.email}!
);
}
```
### EchoContextValue
The context value provided by EchoProvider, accessible via `useEcho()`.
```typescript
import type { EchoContextValue } from '@merit-systems/echo-react-sdk';
```
#### EchoContextValue
### EchoUser
User information from OAuth2 authentication.
```typescript
import type { EchoUser } from '@merit-systems/echo-react-sdk';
const { user } = useEcho();
```
#### EchoUser
## useEchoClient
Provides access to the Echo TypeScript SDK client for platform operations.
```tsx
import { useEchoClient } from '@merit-systems/echo-react-sdk';
const echoClient = useEchoClient({
apiUrl: 'https://echo.merit.systems',
});
// Access all TypeScript SDK functionality
const apps = await echoClient.apps.listEchoApps();
const balance = await echoClient.balance.getBalance();
```
`useEchoClient` provides a full [Echo TypeScript SDK](/docs/typescript-sdk) client instance, automatically authenticated using the current user's token.
**Common operations:**
* `echoClient.apps.*` - [App management](/docs/typescript-sdk#clientapps---app-management)
* `echoClient.balance.*` - [Balance operations](/docs/typescript-sdk#clientbalance---balance-management)
* `echoClient.payments.*` - [Payment links](/docs/typescript-sdk#clientpayments---payment-management)
* `echoClient.models.*` - [Model information](/docs/typescript-sdk#clientmodels---model-information)
* `echoClient.users.*` - [User operations](/docs/typescript-sdk#clientusers---user-management)
## useEchoModelProviders
Access LLM model providers for direct AI integration.
```tsx
import { useEchoModelProviders } from '@merit-systems/echo-react-sdk';
import { generateText } from 'ai';
function AIComponent() {
const { openai, anthropic } = useEchoModelProviders();
const handleGenerate = async () => {
const { text } = await generateText({
model: openai('gpt-5'),
prompt: 'Hello world',
});
return text;
};
return ;
}
```
## Balance Management
The `useEcho` hook provides balance management functionality:
```tsx
const { balance, freeTierBalance, refreshBalance, createPaymentLink } =
useEcho();
```
**balance** - Current account balance
```tsx
Balance: ${balance?.balance || 0}
Total Spent: ${balance?.totalSpent || 0}
```
**freeTierBalance** - Free tier usage information
```tsx
```
**refreshBalance()** - Manually refresh balance data
```tsx
```
**createPaymentLink()** - Create Stripe payment links
```tsx
const paymentUrl = await createPaymentLink(25, 'Credits', '/success');
window.location.href = paymentUrl;
```
# Image Generation
URL: /docs/react-sdk/image-generation
***
title: Image Generation
description: Generate images using DALL-E with Echo's React SDK
---------------------------------------------------------------
# Image Generation
Generate images directly in the browser using Echo's model providers with the [Vercel AI SDK `generateImage`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-image).
```tsx {11,16}
import { useEchoModelProviders } from '@merit-systems/echo-react-sdk';
import { experimental_generateImage as generateImage } from 'ai';
function ImageGenerator() {
const { openai } = useEchoModelProviders();
const [imageUrl, setImageUrl] = useState(null);
const [isGenerating, setIsGenerating] = useState(false);
const handleGenerate = async () => {
setIsGenerating(true);
const result = await generateImage({
model: openai.image('dall-e-3'),
prompt: 'A futuristic cityscape at sunset',
size: '1024x1024',
});
if (result.image) {
setImageUrl(`data:image/png;base64,${result.image.base64}`);
}
setIsGenerating(false);
};
return (
{imageUrl && (
)}
);
}
```
Image generation uses the `experimental_generateImage` function which may change in future Vercel AI SDK versions.
# Overview
URL: /docs/react-sdk
***
title: Overview
description: React SDK for direct LLM integration with OAuth2 + PKCE authentication
-----------------------------------------------------------------------------------
# React SDK
The Echo React SDK enables React applications to call LLMs directly from the browser without API keys or backend servers.
Most AI SDKs require API keys which must be secured in a server-side environment. Echo does not.
`echo-react-sdk` handles the Oauth and billing complexity, allowing your React client to make direct secure calls to AI resources.
```tsx
import { useEchoModelProviders } from '@merit-systems/echo-react-sdk';
import { generateText } from 'ai';
function ChatComponent() {
const { openai } = useEchoModelProviders();
const handleGenerate = async () => {
// Direct AI calls from the browser - no API keys needed!
const { text } = await generateText({
model: openai('gpt-5-nano'),
prompt: 'Hello!'
});
return text;
};
return ;
}
```
## Architecture
The React SDK implements a secure OAuth2 + PKCE flow that eliminates API key management:
1. **User Authentication**: OAuth2 + PKCE flow with your Echo app
2. **JWT Token Exchange**: Receive short-lived JWTs with LLM access scope
3. **Direct LLM Calls**: Use tokens directly with OpenAI-compatible endpoints
4. **Automatic Refresh**: Seamless token renewal in the background
This architecture provides:
* **No API key exposure** - Tokens are scoped to individual users
* **Security** - Short-lived tokens with automatic refresh
* **User-scoped billing** - Each user's usage is tracked separately
* **Direct client calls** - No backend proxy required
## Installation
```bash
npm install @merit-systems/echo-react-sdk openai
```
```bash
pnpm add @merit-systems/echo-react-sdk openai
```
```bash
yarn add @merit-systems/echo-react-sdk openai
```
```bash
bun add @merit-systems/echo-react-sdk openai
```
## Quick Start
Wrap your app with `EchoProvider` and start making direct LLM calls:
```tsx title="App.tsx"
import { EchoProvider, EchoSignIn, useEcho, useEchoModelProviders } from '@merit-systems/echo-react-sdk';
import { generateText } from 'ai';
function App() {
return (
);
}
function ChatApp() {
const { isAuthenticated } = useEcho();
const { openai } = useEchoModelProviders();
if (!isAuthenticated) {
return ;
}
const handleGenerate = async () => {
const { text } = await generateText({
model: openai('gpt-5-nano'),
prompt: 'Why did the chicken cross the road?'
});
console.log(text);
};
return ;
}
export default App;
```
## Documentation Sections
* **[Provider](/docs/react-sdk/provider)** - EchoProvider setup and configuration
* **[Components](/docs/react-sdk/components)** - Pre-built UI components for authentication and payments
* **[Hooks](/docs/react-sdk/hooks)** - Core hooks for authentication, model providers, and platform integration
* **[LLM Integration](/docs/react-sdk/llm-integration)** - AI SDK integration and direct model access patterns
* **[Image Generation](/docs/react-sdk/image-generation)** - Generate images using DALL-E with Echo's React SDK
* **[useChat Hook](/docs/react-sdk/use-chat)** - Advanced chat interfaces with streaming and tool usage
# LLM Integration
URL: /docs/react-sdk/llm-integration
***
title: LLM Integration
description: AI SDK integration and direct model access patterns
----------------------------------------------------------------
# LLM Integration
## Vercel AI SDK Integration (Recommended)
The Echo React SDK provides first-class integration with the [Vercel AI SDK](https://ai-sdk.dev/) for the best developer experience.
### `useEchoModelProviders` Hook
Get AI SDK-compatible providers for OpenAI, Anthropic, and Google models for use with [`generateText`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text) or [`streamText`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text).
```tsx title="AIComponent.tsx" lineNumbers
import { useEchoModelProviders } from '@merit-systems/echo-react-sdk';
import { generateText, streamText } from 'ai';
function AIComponent() {
const { openai, anthropic, google } = useEchoModelProviders();
const handleGenerate = async () => {
const { text } = await generateText({
model: openai('gpt-5'),
prompt: 'Write a haiku about coding'
});
setResult(text);
};
const handleStream = async () => {
const { textStream } = await streamText({
model: openai('gpt-5'),
prompt: 'Write a story about space exploration'
});
setResult('');
for await (const delta of textStream) setResult(prev => prev + delta);
};
return (
<>
{result}
>
);
}
```
**Provider Required**: The `useEchoModelProviders` hook must be used within an [`EchoProvider`](/docs/react-sdk/provider) component.
## Custom Chat Hook
For advanced chat interfaces, see our [dedicated useChat documentation](/docs/react-sdk/chat).
## Raw Client Access (Advanced)
For lower-level control, you can create raw clients using your Echo JWT.
```tsx
import { OpenAI } from 'openai';
function RawOpenAIExample() {
const { token } = useEcho();
// This is what happens under the hood
const openai = new OpenAI({
apiKey: token, // Your JWT token acts as the API key
baseURL: 'https://echo.router.merit.systems', // Echo's proxy endpoint
dangerouslyAllowBrowser: true // Safe because token is user-scoped
});
const handleChat = async () => {
const response = await openai.chat.completions.create({
model: 'gpt-5',
messages: [{ role: 'user', content: 'Hello!' }]
});
return response.choices[0].message.content;
};
return ;
}
```
# Provider
URL: /docs/react-sdk/provider
***
title: Provider
description: EchoProvider setup and configuration
-------------------------------------------------
# Provider
## EchoProvider Setup
The `EchoProvider` component wraps your application and manages OAuth2 + PKCE authentication flow state.
```tsx
import { EchoProvider } from '@merit-systems/echo-react-sdk';
function App() {
return (
);
}
```
### EchoAuthConfig
```typescript
import type { EchoAuthConfig } from '@merit-systems/echo-react-sdk';
```
#### EchoAuthConfig
# useChat Hook
URL: /docs/react-sdk/use-chat
***
title: useChat Hook
description: Build chat interfaces with Echo's useChat hook and streaming AI responses
--------------------------------------------------------------------------------------
# useChat Hook
Echo provides a `useChat` hook which is a wrapper around the [Vercel AI SDK](https://ai-sdk.dev/docs/reference/ai-sdk-core/use-chat) `useChat` hook and provides niceties for
creating an Echo-powered chat experience.
```tsx title="App.tsx"
import {
EchoChatProvider,
EchoSignIn,
useEcho,
useEchoModelProviders,
} from '@merit-systems/echo-react-sdk';
import { type ModelMessage, streamText } from 'ai';
import { z } from 'zod';
import { ChatInterface } from './ChatInterface.tsx';
function App() {
const { isAuthenticated } = useEcho();
const { openai } = useEchoModelProviders();
const chatFn = async ({
modelMessages,
abortSignal,
}: {
modelMessages: ModelMessage[];
abortSignal: AbortSignal | undefined;
}) => {
const result = streamText({
model: openai('gpt-5'),
messages: modelMessages,
abortSignal,
tools: {
getWeather: {
description: 'Get current weather for a location',
inputSchema: z.object({ location: z.string() }),
execute: async ({ location }: { location: string }) =>
`Weather in ${location}: 72°F and sunny`,
},
},
});
return result.toUIMessageStream();
};
if (!isAuthenticated) return ;
return (
);
}
export default App;
```
```tsx title="ChatInterface.tsx"
import { useChat } from '@merit-systems/echo-react-sdk';
import { useState } from 'react';
export function ChatInterface() {
const { messages, sendMessage, status } = useChat();
const [input, setInput] = useState('');
return (