# How to Integrate Bolt.new with Coinbase API

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 25 minutes
- Last updated: April 2026

## TL;DR

Bolt.new supports two Coinbase integrations: Coinbase Commerce for accepting Bitcoin, Ethereum, and USDC payments via hosted checkout, and Coinbase Advanced Trade API for live crypto prices and portfolio tracking. Commerce charge creation works in Bolt's WebContainer using coinbase-commerce-node. API keys go in .env. Commerce webhooks for payment confirmation require a deployed URL on Netlify or Bolt Cloud — test the full payment flow there.

## Accept Crypto Payments and Display Live Prices with Coinbase in Bolt.new

Coinbase's developer APIs split into two distinct products that serve different use cases in a Bolt.new app. Coinbase Commerce is the payment processing side: it generates hosted checkout pages where customers pay in Bitcoin, Ethereum, Litecoin, USDC, DAI, or other supported cryptocurrencies. You create a 'charge' via API, receive a hosted checkout URL, and redirect the customer there. Commerce handles the crypto wallet address generation, payment monitoring, exchange rate conversion, and underpayment handling. For non-technical founders building apps that want to accept crypto without dealing with blockchain complexity, Commerce is dramatically simpler than building raw crypto payment flows.

Coinbase Advanced Trade API (formerly Coinbase Pro API) is the market data side: it provides real-time and historical price data, order books, candlestick charts, and for authenticated users, portfolio balances and trading. If you are building a price dashboard, portfolio tracker, crypto calculator, or any feature that displays cryptocurrency prices, this is the API to use. The public market data endpoints require no authentication — you can fetch prices without any API key at all for basic use cases.

Both APIs communicate over HTTPS with JSON responses, making them fully compatible with Bolt.new's WebContainer. The `coinbase-commerce-node` npm package is pure JavaScript with no native binary dependencies and installs in the WebContainer. Outbound price requests to the Advanced Trade API work from both React components (for public endpoints) and API routes (for authenticated endpoints). The one capability that requires deployment is Commerce webhooks — Coinbase sends a POST request to your app when a payment is confirmed, and this incoming connection cannot reach the WebContainer preview URL.

## Before you start

- A Bolt.new account with a Next.js project
- For Commerce: a Coinbase Commerce account (commerce.coinbase.com) with Commerce API key
- For price data: no account required — public Advanced Trade API endpoints are unauthenticated
- For authenticated portfolio tracking: a Coinbase account with Advanced Trade API key (CDP Portal at portal.cdp.coinbase.com)

## Step-by-step guide

### 1. Set Up Coinbase Commerce and Get API Credentials

For accepting cryptocurrency payments, start with Coinbase Commerce — it is separate from the main Coinbase exchange and designed specifically for merchants. Go to commerce.coinbase.com and sign up with your email address. You do not need an existing Coinbase exchange account; Commerce is a standalone service. Complete the merchant setup including your business name and optionally connecting a bank account for USD payouts (optional for getting started).

Once logged in to Commerce, go to Settings > Security and scroll to 'API keys.' Click 'Create an API Key.' Coinbase generates a Commerce API key that looks like a UUID string. Copy it immediately — like most API keys, it is only shown once at creation. This key authenticates your server-side requests to create charges, list payments, and manage products.

For the Coinbase Advanced Trade API (price data and portfolio), the setup is different. Go to portal.cdp.coinbase.com (Coinbase Developer Platform). Navigate to 'API Keys' and click 'Create API Key.' Advanced Trade API uses a newer JWT-based authentication system: you get an API Key Name (a string like 'organizations/{id}/apiKeys/{id}') and a private key (an EC private key in PEM format). Store both securely — the private key is used to sign JWT tokens for each request. For public market data (prices without account access), you do not need any API key at all and can call the public endpoints directly.

**Expected result:** You have a Coinbase Commerce API key for payment processing and optionally a Coinbase Advanced Trade API key pair for authenticated account access.

### 2. Install coinbase-commerce-node and Create Charge Endpoint

The `coinbase-commerce-node` package is the official Node.js SDK for Coinbase Commerce. It is a pure JavaScript library with no native binary dependencies — it installs and runs without issues in Bolt's WebContainer. Install it from the Bolt terminal: in Bolt's terminal panel, run `npm install coinbase-commerce-node`. The installation completes in a few seconds from the CDN-backed package cache.

With the package installed, create a Next.js API route that generates Commerce charges. A charge is a payment request for a specific amount in a fiat currency (USD, EUR, etc.) that Coinbase Commerce converts to the equivalent crypto amount at the time of payment. The charge response includes a `hosted_url` — this is the Coinbase-hosted checkout page URL where you redirect the customer to complete payment.

The charge creation payload includes the `name` (product or service name displayed on the checkout), `description`, `pricing_type` (always 'fixed_price' for a specific amount), and `local_price` with the amount and currency. Optionally include `redirect_url` (where Coinbase redirects after payment) and `cancel_url` (where Coinbase redirects if the user cancels). Both of these redirect URLs should be your deployed app URLs for production — during development you can use any URL since the redirect happens in the user's browser after they complete payment on Coinbase's hosted page.

The Commerce SDK's `Charge.create()` method handles the HTTP request and response parsing. The result is a charge object with `id`, `code`, `hosted_url`, `expires_at`, and `addresses` (the crypto wallet addresses Coinbase generated for this payment).

```
// app/api/crypto/commerce/route.ts
import { NextResponse } from 'next/server';
// @ts-ignore — coinbase-commerce-node types are legacy
import { Client, resources } from 'coinbase-commerce-node';

const { Charge } = resources;

// Initialize Commerce client
Client.init(process.env.COINBASE_COMMERCE_API_KEY || '');

export async function POST(request: Request) {
  try {
    const { amount, currency = 'USD', productName, description } = await request.json();

    if (!amount || !productName) {
      return NextResponse.json(
        { error: 'amount and productName are required' },
        { status: 400 }
      );
    }

    const chargeData = {
      name: productName,
      description: description || productName,
      pricing_type: 'fixed_price',
      local_price: {
        amount: String(amount),
        currency: currency,
      },
      // Redirect URLs for after payment — use your deployed URL in production
      redirect_url: process.env.NEXT_PUBLIC_APP_URL
        ? `${process.env.NEXT_PUBLIC_APP_URL}/payment/success`
        : undefined,
      cancel_url: process.env.NEXT_PUBLIC_APP_URL
        ? `${process.env.NEXT_PUBLIC_APP_URL}/payment/cancelled`
        : undefined,
    };

    const charge = await Charge.create(chargeData);

    return NextResponse.json({
      id: charge.id,
      code: charge.code,
      hosted_url: charge.hosted_url,
      expires_at: charge.expires_at,
      // Do not expose the full charge object — it contains sensitive wallet addresses
    });
  } catch (err) {
    const message = err instanceof Error ? err.message : 'Charge creation failed';
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
```

**Expected result:** The Commerce API route creates charges successfully. Testing with a POST request returns a hosted_url you can open in a browser to see Coinbase's crypto checkout page.

### 3. Build the Crypto Price Dashboard with Advanced Trade API

Coinbase Advanced Trade API's public market data endpoints require no authentication. You can fetch current prices, 24-hour statistics, and order book data for any supported trading pair without an API key. This makes it straightforward to build public-facing price dashboards directly from API calls.

The key endpoint is `GET https://api.coinbase.com/api/v3/brokerage/market/products/{product_id}` for individual product stats, or you can fetch multiple products at once. The response includes `price` (last trade price), `price_percentage_change_24h`, `volume_24h`, `high_52_week`, `low_52_week`, and more. Product IDs follow the format `{BASE}-{QUOTE}` — for Bitcoin priced in US dollars it is `BTC-USD`.

For public endpoints, you can call the Advanced Trade API directly from a React component or from an API route. Using an API route is still recommended because it avoids potential CORS issues in different browser configurations and allows you to add caching. Implement a polling interval in the React component using `useEffect` with a `setInterval` — refresh every 30-60 seconds for live price data. Exponential backoff in error handling prevents hammering the API during outages.

For authenticated endpoints (portfolio balances, order history), Advanced Trade API uses JWT authentication. Each request must include a JWT token signed with your EC private key, with a short expiration. The `@coinbase/coinbase-sdk` package handles this signing automatically. Authenticated requests must go through your API route — the private key must never appear in client-side code.

```
// app/api/crypto/prices/route.ts
import { NextResponse } from 'next/server';

const PRODUCTS = ['BTC-USD', 'ETH-USD', 'SOL-USD', 'DOGE-USD', 'USDC-USD'];

// Simple in-memory cache to reduce Coinbase API calls
let priceCache: { data: unknown; timestamp: number } | null = null;
const CACHE_TTL = 15_000; // 15 seconds

export async function GET() {
  if (priceCache && Date.now() - priceCache.timestamp < CACHE_TTL) {
    return NextResponse.json(priceCache.data);
  }

  try {
    const results = await Promise.all(
      PRODUCTS.map(async (productId) => {
        const res = await fetch(
          `https://api.coinbase.com/api/v3/brokerage/market/products/${productId}`,
          { headers: { 'Content-Type': 'application/json' } }
        );
        if (!res.ok) throw new Error(`Failed to fetch ${productId}`);
        return res.json();
      })
    );

    const prices = results.map((product) => ({
      id: product.product_id,
      price: parseFloat(product.price || '0'),
      change24h: parseFloat(product.price_percentage_change_24h || '0'),
      volume24h: parseFloat(product.volume_24h || '0'),
      high52w: parseFloat(product.high_52_week || '0'),
      low52w: parseFloat(product.low_52_week || '0'),
    }));

    priceCache = { data: { prices, updatedAt: new Date().toISOString() }, timestamp: Date.now() };
    return NextResponse.json(priceCache.data);
  } catch (err) {
    const message = err instanceof Error ? err.message : 'Failed to fetch prices';
    // Return stale cache on error if available
    if (priceCache) return NextResponse.json(priceCache.data);
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
```

**Expected result:** The price ticker displays live cryptocurrency prices for BTC, ETH, SOL, DOGE, and USDC with 24h change percentages updating every 30 seconds in Bolt's preview.

### 4. Configure Environment Variables

Add your Coinbase credentials to the `.env` file in your Bolt project root. The variable naming convention depends on which Coinbase products you are using. Commerce uses a single API key. Advanced Trade API authenticated calls require both an API key name and a private key — the private key is a multi-line PEM string which requires careful handling in environment variables.

For Coinbase Advanced Trade private keys in .env, the PEM format contains newlines. When storing a PEM key in a single-line environment variable, replace the newline characters with `\n` literal characters — your server-side code then replaces them back when constructing the key. Alternatively, use Bolt Cloud's Secrets manager for multi-line values, which handles the encoding automatically.

The `NEXT_PUBLIC_APP_URL` variable enables proper redirect URL configuration for Commerce charges. During development, this can be blank (redirects will use the Commerce page's default behavior). After deployment, set it to your Netlify URL so Commerce redirects users back to your app after payment.

```
# .env
# Coinbase Commerce (for accepting crypto payments)
COINBASE_COMMERCE_API_KEY=your-commerce-api-key

# Coinbase Advanced Trade API (for price data and portfolio — optional)
COINBASE_API_KEY_NAME=organizations/your-org-id/apiKeys/your-key-id
COINBASE_PRIVATE_KEY="-----BEGIN EC PRIVATE KEY-----\nYOUR_PRIVATE_KEY_HERE\n-----END EC PRIVATE KEY-----"

# App URL for Commerce redirects (set to deployed URL in production)
NEXT_PUBLIC_APP_URL=https://your-app.netlify.app
```

**Expected result:** Environment variables are configured. Commerce charge creation works in the preview. Price fetching works without authentication for public endpoints.

### 5. Deploy and Configure Commerce Webhooks

Coinbase Commerce webhooks are the mechanism by which your app learns that a payment has been confirmed. When a customer pays, Coinbase monitors the blockchain, waits for sufficient confirmations, and then sends a POST request to your webhook URL with a signed event payload. Without webhooks, your app has no reliable way to know that payment succeeded — you would have to poll the Commerce API repeatedly.

Because Coinbase sends an incoming HTTP POST to your app, webhooks cannot be received in Bolt's WebContainer preview. The WebContainer has no way to accept incoming connections from the internet. Deploy your app to Netlify or Bolt Cloud first to get a stable HTTPS URL.

After deployment, go to your Coinbase Commerce dashboard > Settings > Notifications. Click 'Add an endpoint' and enter your webhook URL (e.g., `https://your-app.netlify.app/api/crypto/webhook`). Coinbase generates a webhook signing secret — copy this and add it to your Netlify environment variables as `COINBASE_COMMERCE_WEBHOOK_SECRET`. Your webhook handler verifies this signature on every incoming event to confirm it genuinely came from Coinbase.

The most important Commerce event is `charge:confirmed` — this fires when a payment has received enough blockchain confirmations to be considered final. Also handle `charge:failed` (payment not received before the 60-minute expiry), `charge:delayed` (payment received but under-confirmed — wait for confirmed event), and `charge:pending` (transaction broadcast but awaiting confirmations).

```
// app/api/crypto/webhook/route.ts
// IMPORTANT: This webhook handler requires a deployed URL.
// Coinbase Commerce cannot send webhooks to Bolt's WebContainer preview URL.
// Deploy to Netlify or Bolt Cloud, then register the deployed URL in Commerce Settings.
import { NextResponse } from 'next/server';
import crypto from 'crypto';

export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get('X-CC-Webhook-Signature');
  const secret = process.env.COINBASE_COMMERCE_WEBHOOK_SECRET;

  if (!secret) {
    console.error('COINBASE_COMMERCE_WEBHOOK_SECRET is not configured');
    return NextResponse.json({ error: 'Webhook secret not configured' }, { status: 500 });
  }

  // Verify webhook signature
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  if (signature !== expectedSignature) {
    console.error('Invalid Commerce webhook signature');
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }

  const event = JSON.parse(rawBody);
  const { type, data } = event;
  const chargeCode = data?.object?.code;

  console.log(`Commerce webhook: ${type} for charge ${chargeCode}`);

  switch (type) {
    case 'charge:confirmed':
      // Payment confirmed — fulfill the order
      // e.g., update your database, send confirmation email, grant access
      console.log(`Payment confirmed for charge ${chargeCode}`);
      break;
    case 'charge:failed':
      // Payment failed or expired — handle appropriately
      console.log(`Charge failed: ${chargeCode}`);
      break;
    case 'charge:pending':
      // Transaction broadcast, awaiting confirmations
      console.log(`Charge pending: ${chargeCode}`);
      break;
    case 'charge:delayed':
      // Payment received after expiry — manual review needed
      console.log(`Delayed payment for charge: ${chargeCode}`);
      break;
  }

  // Always return 200 quickly — Coinbase will retry on non-200 responses
  return NextResponse.json({ received: true });
}
```

**Expected result:** The webhook handler is deployed and registered in Coinbase Commerce. Test payments show confirmed events arriving at the endpoint. The Netlify function logs show the webhook events being processed.

## Best practices

- Never create Commerce charges speculatively — only create a charge when the user explicitly initiates checkout, since each charge occupies a unique blockchain address and has a 60-minute expiry.
- Store COINBASE_COMMERCE_API_KEY and Advanced Trade private keys as server-side environment variables without any NEXT_PUBLIC_ prefix to ensure they are never bundled into client JavaScript.
- Implement idempotency in your webhook handler by storing processed charge IDs and ignoring duplicate events — Coinbase may retry webhook delivery multiple times if your endpoint does not respond quickly enough.
- Handle the charge:delayed event specifically — this represents a valid payment made after the charge expired and requires manual review rather than automatic fulfillment.
- Cache public price data for at least 15 seconds to stay within Coinbase's rate limits and reduce latency for users loading price widgets simultaneously.
- Display crypto amounts with appropriate precision — Bitcoin prices are typically shown with 2 decimal places for USD values, but small-cap coins may need 4-6 decimal places.
- Always show the USD equivalent prominently alongside crypto prices in payment flows — most customers think in fiat currency and the crypto amount alone may cause confusion or trust issues.

## Use cases

### Crypto Payment Button

Add a 'Pay with Crypto' button to any Bolt-built app. Clicking it creates a Coinbase Commerce charge via your API route and redirects the user to Coinbase's hosted checkout page. The checkout page accepts multiple cryptocurrencies and shows a QR code and wallet address. After payment confirmation, Coinbase Commerce triggers a webhook to update the payment status in your app.

Prompt example:

```
Add a Coinbase Commerce payment button to my app. Create a Next.js API route at /api/crypto/checkout that uses coinbase-commerce-node to create a Commerce charge for a given amount in USD. The charge should accept Bitcoin, Ethereum, and USDC. Return the hosted_url to redirect the user to Coinbase's payment page. Add a 'Pay with Crypto' button component that calls this endpoint and redirects. Store COINBASE_COMMERCE_API_KEY in process.env.
```

### Live Crypto Price Dashboard

Build a real-time cryptocurrency price ticker showing BTC, ETH, SOL, and other coins with 24-hour price change percentages and sparkline charts. Fetches data from Coinbase Advanced Trade's public market data endpoints every 30 seconds. No API key required for public price data — perfect for customer-facing price displays.

Prompt example:

```
Build a crypto price dashboard using Coinbase Advanced Trade API. Create a Next.js API route at /api/crypto/prices that fetches current prices and 24h stats for BTC-USD, ETH-USD, SOL-USD, and USDC-USD from Coinbase's public market data endpoint at https://api.coinbase.com/api/v3/brokerage/market/products. Display results as price cards with current price, 24h change percentage (green if positive, red if negative), and 24h high/low. Refresh every 30 seconds.
```

### Portfolio Tracker with Authenticated Balance

Build a personal crypto portfolio tracker that shows Coinbase account balances, recent transactions, and total portfolio value in USD. Requires Coinbase Advanced Trade API authentication with an API key and secret (JWT-signed requests). The balance data is sensitive and must be fetched server-side through an API route — never directly from a React component.

Prompt example:

```
Build a Coinbase portfolio tracker. Create a Next.js API route at /api/crypto/portfolio that authenticates with Coinbase Advanced Trade API using COINBASE_API_KEY_NAME and COINBASE_PRIVATE_KEY from process.env. Fetch account balances for all cryptocurrency holdings. Calculate total portfolio value in USD using current prices. Display each holding with coin name, quantity, current price, and USD value. Sort by value descending. Note that API key credentials must be stored server-side only.
```

## Troubleshooting

### coinbase-commerce-node import fails with 'Cannot find module' or TypeScript type errors

Cause: The package installed correctly but lacks TypeScript type definitions. The @ts-ignore comment in the example suppresses the type error.

Solution: Add // @ts-ignore above the import, or install a community types package. Alternatively, use direct fetch() calls to the Commerce REST API instead of the SDK to avoid the type issue entirely.

```
// Alternative: use fetch directly instead of the SDK:
const response = await fetch('https://api.commerce.coinbase.com/charges', {
  method: 'POST',
  headers: {
    'X-CC-Api-Key': process.env.COINBASE_COMMERCE_API_KEY || '',
    'X-CC-Version': '2018-03-22',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(chargeData),
});
```

### Commerce webhook signature verification fails — all webhooks return 401

Cause: The raw request body is being parsed as JSON before signature verification, which changes the exact byte sequence and invalidates the HMAC signature.

Solution: Use request.text() (not request.json()) to read the raw body before verification. Only parse it as JSON after verifying the signature. The signature is computed over the exact raw bytes Coinbase sent.

```
// CORRECT — read raw text first, then parse JSON:
export async function POST(request: Request) {
  const rawBody = await request.text(); // Must be text, not json
  // Verify signature using rawBody string
  const event = JSON.parse(rawBody); // Parse AFTER verification
}
```

### Commerce webhooks are not arriving — payments succeed in Coinbase but the webhook endpoint logs nothing

Cause: The webhook URL is pointing to Bolt's WebContainer preview URL, which is not publicly accessible. Coinbase cannot reach browser-based runtimes.

Solution: Deploy your app to Netlify or Bolt Cloud and update the webhook URL in Coinbase Commerce Settings > Notifications to use your deployed domain. The WebContainer preview URL (ending in .webcontainer-api.io) is only accessible within your browser session.

### Advanced Trade API price requests return 403 or 'Unauthorized' for public endpoints

Cause: A stale or malformed Authorization header is being sent with the public endpoint request. Public endpoints do not require authentication and reject some malformed auth headers.

Solution: Remove any Authorization headers from public endpoint requests. The public Advanced Trade API endpoints (GET /api/v3/brokerage/market/products) do not need authentication and should be called with only the Content-Type header.

```
// Public endpoint — no authorization header needed:
const res = await fetch(
  `https://api.coinbase.com/api/v3/brokerage/market/products/${productId}`,
  { headers: { 'Content-Type': 'application/json' } } // No Authorization header
);
```

## Frequently asked questions

### Can I test Coinbase Commerce payments in Bolt's preview without deploying?

You can create charges and get the hosted checkout URL in the Bolt preview — clicking the URL opens Coinbase's hosted payment page and you can simulate a payment there. However, the webhook confirming payment success cannot reach the WebContainer preview since it cannot accept incoming HTTP connections. Full end-to-end payment testing (create charge → customer pays → webhook confirms → app updates) requires deployment to Netlify or Bolt Cloud.

### Does Bolt.new have a native Coinbase integration?

No — Bolt.new's native Stripe integration is for fiat payments only. Coinbase Commerce and Coinbase Advanced Trade API require manual setup using Next.js API routes. Bolt's AI assistant generates the full integration from a prompt, so setup is fast even without a native connector.

### Are Coinbase public price API endpoints free to use?

Yes — Coinbase Advanced Trade's public market data endpoints (product prices, 24h stats, order books) are free and require no API key or account. They have rate limits (typically a few hundred requests per minute) which are more than sufficient for a dashboard polling every 30 seconds. Only authenticated endpoints for account data, trading, and portfolio access require an API key.

### What cryptocurrencies does Coinbase Commerce accept?

Coinbase Commerce supports Bitcoin (BTC), Ethereum (ETH), Litecoin (LTC), Bitcoin Cash (BCH), USD Coin (USDC), DAI, and several other ERC-20 tokens. The list changes as Coinbase adds support for new currencies. Commerce displays all supported options to the customer on the checkout page, so you do not need to specify which coins to accept — customers choose their preferred currency.

### How long does it take for a Coinbase Commerce charge:confirmed webhook to fire?

Confirmation time depends on the blockchain and network congestion. Bitcoin typically requires 6 confirmations (approximately 60 minutes), though Commerce may confirm faster for smaller amounts. Ethereum is faster, typically 35 confirmations taking 5-10 minutes. USDC and other stablecoins follow Ethereum's timing. Build your UX to expect a delay between payment submission and confirmation — show the customer a 'processing' state while they wait.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/coinbase-api
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/coinbase-api
