> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://viem.sh/api/mcp` to find what you need.

# Connect & Pay

:::warning
The Gateway API is experimental and may change in future releases.
:::

## Overview

Every Gateway method goes through the `fetch` you give `gatewayActions`. That `fetch` decides who pays.
A plain `fetch` reaches only the free routes, such as capabilities; paid routes throw
`Gateway.PaymentRequiredError`. To pay, use one of three patterns:

| Pattern | Who signs payments | Use it for |
| --- | --- | --- |
| Local account | The Client's account, through `mppx` | Scripts, servers and agents you run |
| Tempo Wallet | An access key the user approves once, through the Accounts SDK | Browser apps paid by their users |
| Gateway API key | The Gateway, from a key funded by a wallet | Tools that only take an API key, and one operator key serving many tenants |

## Recipes

### Read Capabilities

`client.gateway.getCapabilities` is free and works with a plain `fetch`. It returns the chain, accepted currencies, the payment recipient,
prices, size limits, models and compute rates. Read it to pick models and to show prices before a user pays.

```ts twoslash
import { createClient, gatewayActions } from 'viem/tempo'

const client = createClient().extend(gatewayActions())

const { currencies, models, prices } = await client.gateway.getCapabilities()
```

### Use the Defaults

`gatewayActions()` with no options reaches the Tempo Gateway at `https://gateway.tempo-dev.workers.dev` in the `default` app. A Client with a local account pays with it; other Clients use `globalThis.fetch`. Pass `app` to keep your data apart, `url` to use another Gateway, and `fetch` to pay another way.

```ts twoslash
import { createClient, gatewayActions } from 'viem/tempo'

const client = createClient().extend(gatewayActions())

const capabilities = await client.gateway.getCapabilities()
```

### Pay from a Local Account

For a script, server or agent, install `mppx` (`npm i mppx`) and give the Client a local account.
`gatewayActions` pays every request with it, loading `mppx` on the first one. Without `mppx`, paid requests
throw `Gateway.MppxNotInstalledError`.

```ts twoslash
import { privateKeyToAccount } from 'viem/accounts'
import { createClient, gatewayActions } from 'viem/tempo'

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)

export const client = createClient({ account }).extend(gatewayActions())
```

### Fund an Account

The Gateway is paid in the currencies `getCapabilities().currencies` lists (MACH and OUSD). Fund a local account
with OUSD: send it from any wallet or exchange that supports Tempo. MACH can only be minted to an account,
through Tempo Wallet's deposit flow, not transferred to a new address.

On the first paid request, `gatewayActions` reads the account's balances and pays in the currency it holds
most of. An account that holds neither throws
`Gateway.InsufficientFundsError` before signing anything; after you fund it, the next request goes through.

:::code-group
```ts twoslash [example.ts]
import { Gateway } from 'viem/tempo'
import { client } from './viem.config'

try {
  await client.kv.get({ key: 'greeting' })
} catch (error) {
  if (error instanceof Gateway.InsufficientFundsError) { // [!code focus]
    console.log(error.address, error.balances) // [!code focus]
    // Ask the user to send OUSD to error.address, then retry.
  }
}
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/tempo/gateway.config.ts:setup]
```
:::

### Configure Payments

To tune payments, create the `mppx` client yourself and pass its `fetch`. `maxDeposit` caps how much each
payment channel locks up. The Gateway accepts only its advertised currencies (`capabilities.currencies`), and
`autoSwap` lets a wallet holding another stablecoin swap through the stablecoin DEX when it opens or tops up a
channel.

```ts twoslash
import { Mppx, tempo } from 'mppx/client'
import { privateKeyToAccount } from 'viem/accounts'
import { createClient, gatewayActions } from 'viem/tempo'

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)

const mppx = Mppx.create({
  methods: [tempo({ account, autoSwap: true, maxDeposit: '1' })],
  polyfill: false,
})

const client = createClient({ account }).extend(
  gatewayActions({ fetch: mppx.fetch }),
)
```

### Pay from Tempo Wallet

In a browser app, let users pay from Tempo Wallet: the Accounts SDK asks them once to approve an access key with
a spending limit, then pays every Gateway request with it. See [Use with Tempo Wallet](/tempo/guides/gateway/tempo-wallet).

### Pay with a Gateway API Key

A Gateway can exchange an access key for an `sk-tempo-` API key. Requests that carry it as a bearer token
are paid by the Gateway from that key's own channel, so they never answer `402`. Use it where a tool takes only
an API key, or when one operator key serves many tenants. Wrap `fetch` to add the header:

```ts twoslash
import { createClient, gatewayActions } from 'viem/tempo'

const apiKey = process.env.GATEWAY_API_KEY!

const client = createClient().extend(
  gatewayActions({
    fetch: (input, init) =>
      fetch(input, {
        ...init,
        headers: { ...init.headers, authorization: `Bearer ${apiKey}` },
      }),
  }),
)
```

### Isolate Tenants with Apps

Data is scoped by wallet and `app`. When one wallet or API key serves many users or projects, give each its own
app, such as `project-${id}`. A project then sees only its own keys, objects and sandboxes, and deleting a
tenant never touches another's data.

```ts twoslash
import { privateKeyToAccount } from 'viem/accounts'
import { createClient, gatewayActions } from 'viem/tempo'

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)

function forProject(id: string) {
  return createClient({ account }).extend(gatewayActions({ app: `project-${id}` })) // [!code focus]
}

await forProject('a1b2c3').kv.set({ key: 'status', value: 'running' })
```

An app name must match `[a-z0-9][a-z0-9-]{0,62}`; anything else throws `Gateway.InvalidAppError`.

### Handle Payment Errors

:::code-group
```ts twoslash [example.ts]
import { Gateway } from 'viem/tempo'
import { client } from './viem.config'

try {
  await client.ai.generateText({ model: '@cf/zai-org/glm-5.3', prompt: 'hi' })
} catch (error) {
  if (error instanceof Gateway.InsufficientFundsError) { // [!code focus]
    // The account can't cover the request: fund it with OUSD.
  } else if (error instanceof Gateway.MppxNotInstalledError) { // [!code focus]
    // A local account pays through mppx: install it.
  } else if (error instanceof Gateway.PaymentRequiredError) { // [!code focus]
    // The fetch could not pay: pass a payment-aware fetch, or fund the wallet.
  } else if (error instanceof Gateway.RequestError) {
    console.error(error.status, error.code, error.detail)
  }
}
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/tempo/gateway.config.ts:setup]
```
:::

## See More

<Cards>
  <Card icon="lucide:sparkles" title="Run Inference" description="Generate, stream and embed text, metered per token." to="/tempo/guides/gateway/ai" />

  <Card icon="lucide:plug" title="Payment Channels" description="How the channels that pay the Gateway open, settle and close." to="/tempo/guides/payment-channels" />
</Cards>
