# x402 quickstart

> Charge for an API endpoint with x402: add a payout wallet, create a key, set a price, add the middleware for Express, Fastify or NestJS and make your first paid call.

In this guide you'll charge 0.01 USDC on Base for `GET /v1/weather/:city`, then make the first payment yourself. The payment lands in your own wallet.

You need:

- A Cryptonly account with business verification approved. Until it's approved, payments fail with `settlement_disabled`.
- Node.js 18 or later.
- A wallet on Base to receive payments, such as MetaMask or a Safe.
- A second wallet with a little USDC on Base, to act as the buyer. It needs no ETH.

Prices from the dashboard need one of the Node.js packages below. For a seller in Python or Go, see [Prices in your code](https://docs.cryptonly.net/guides/x402/prices-in-code.md).

<LinkButton href="https://merchant.cryptonly.net/x402" icon="external" target="_blank" rel="noopener noreferrer">Open dashboard</LinkButton>
<LinkButton href="https://merchant.cryptonly.net/sign-up" variant="secondary" icon="external" target="_blank" rel="noopener noreferrer">Create account</LinkButton>

Under **x402 payments**, the **Overview** tab tracks the steps below:

1. **Add a payout wallet**

   Go to **Settings → Payouts → Add wallet**, paste your address and sign the message. Payments will go to this wallet.

2. **Create an API key**

   Go to **Settings → Integration → Create x402 key**. Save it as `CRYPTONLY_API_KEY`. You'll only see it once.

3. **Set a price**

   On the **Endpoints** tab, click **Add endpoint**:

   - **URL**: your API's URL with the path `/v1/weather/:city`. `:city` matches any value.
   - **Price**: `0.01` USDC on Base.

   To accept another currency too, such as USDC on Solana, click **Add currency**. The 402 offers every price and the buyer pays with any of them.

   Leave **List publicly** off for now. It adds the endpoint to the [discovery feed](https://docs.cryptonly.net/guides/x402/discovery.md).

4. **Add the middleware**

   <Tabs syncKey="x402-framework">
   <TabItem label="Express">

   ```bash
   npm install @cryptonly/x402-express @x402/core @x402/express @x402/evm express
   ```

   ```ts

   const app = express();
   app.get('/health', (_req, res) => res.send('ok')); // free routes go first

   const payments = cryptonlyPaymentMiddleware({
     apiKey: process.env.CRYPTONLY_API_KEY!,
   });
   app.use(payments);

   app.get('/v1/weather/:city', (req, res) => {
     res.json({ city: req.params.city, temperatureC: 21 });
   });

   await payments.ready(); // fails at startup if the prices can't load
   app.listen(4021);
   ```

   </TabItem>
   <TabItem label="Fastify">

   ```bash
   npm install @cryptonly/x402-fastify @x402/core @x402/fastify @x402/evm fastify
   ```

   ```ts

   const app = Fastify();
   await app.register(cryptonlyPayments, {
     apiKey: process.env.CRYPTONLY_API_KEY!,
   });

   app.get('/v1/weather/:city', async (request) => {
     const { city } = request.params as { city: string };
     return { city, temperatureC: 21 };
   });

   await app.cryptonlyX402.ready(); // fails at startup if the prices can't load
   await app.listen({ port: 4021 });
   ```

   </TabItem>
   <TabItem label="NestJS">

   ```bash
   npm install @cryptonly/x402-nestjs @x402/core @x402/evm
   ```

   ```ts

   @Controller('v1/weather')
   class WeatherController {
     @X402() // the price comes from the dashboard
     @Get(':city')
     get(@Param('city') city: string) {
       return { city, temperatureC: 21 };
     }
   }

   @Module({
     imports: [
       X402Module.forRoot({ apiKey: process.env.CRYPTONLY_API_KEY!, isGlobal: true }),
     ],
     controllers: [WeatherController],
   })
   export class AppModule {}
   ```

   Listen on port 4021 in `main.ts` (`await app.listen(4021)`). Only handlers with `@X402()` ask for payment. It works on the Express and the Fastify platform.

   </TabItem>
   </Tabs>

   The middleware loads your prices from the dashboard and refreshes them every minute, so price changes need no deploy. It matches endpoints by path, so it works on `localhost` too.

5. **Check that the endpoint asks for payment**

   ```bash
   curl -i http://localhost:4021/v1/weather/paris
   # HTTP/1.1 402 Payment Required
   ```

6. **Pay for it**

   Run this as the buyer, with the buyer wallet's key in `BUYER_PRIVATE_KEY`:

   ```bash
   npm install @x402/fetch @x402/evm viem
   ```

   ```ts

   const account = privateKeyToAccount(process.env.BUYER_PRIVATE_KEY as `0x${string}`);
   const publicClient = createPublicClient({ chain: base, transport: http() });

   const pay = wrapFetchWithPaymentFromConfig(fetch, {
     schemes: [
       { network: 'eip155:8453', client: new ExactEvmScheme(toClientEvmSigner(account, publicClient)) },
     ],
   });

   const response = await pay('http://localhost:4021/v1/weather/paris');
   console.log(response.status, await response.json());
   // 200 { city: 'paris', temperatureC: 21 }
   ```

   The payment shows up on the **Payments** tab within seconds. For a buyer in Python or Go, see [buyer clients](https://docs.cryptonly.net/guides/x402/reference.md#buyer-clients).

<Aside type="caution" title="Before your 101st payment">
After the first 100 payments each month, fees come from [x402 credits](https://docs.cryptonly.net/guides/x402/payouts-and-fees.md#x402-credits). Top them up or turn on auto top-up, or payments will fail.
</Aside>

## Next steps

- [Webhooks](https://docs.cryptonly.net/guides/x402/payments-and-webhooks.md#webhooks): Get notified when a payment confirms
- [Payouts and fees](https://docs.cryptonly.net/guides/x402/payouts-and-fees.md): Add Solana, or get paid to your Cryptonly balance
- [Discovery](https://docs.cryptonly.net/guides/x402/discovery.md): List your endpoint so agents can find it
- [Prices in your code](https://docs.cryptonly.net/guides/x402/prices-in-code.md): Use stock x402 middleware instead of the dashboard

## Test in the sandbox

To try x402 without real money, or before your business verification is approved, use the free [sandbox](https://docs.cryptonly.net/guides/sandbox-environment.md). Follow the same steps with these changes:

- Use a sandbox account. Its keys and wallets are separate from production.
- Pass `sandbox: true` to `cryptonlyPaymentMiddleware`, `cryptonlyPayments` or `X402Module.forRoot()`.
- In the buyer code, use `baseSepolia` and `eip155:84532`. Get test USDC from the [Circle faucet](https://faucet.circle.com/).

<LinkButton href="https://sandbox-merchant.cryptonly.net/sign-up" variant="secondary" icon="external" target="_blank" rel="noopener noreferrer">Create sandbox account</LinkButton>
