> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Machine Payments on Tempo (Node.js)

> Create a server wallet, fund it via Tempo's testnet faucet, and make MPP payments to 402-protected APIs using Dynamic's Node SDK and mppx

## Overview

[Tempo](https://docs.tempo.xyz) is a blockchain built for machine-to-machine payments. Its **Machine Payment Protocol (MPP)** extends the HTTP 402 "Payment Required" status code so that agents and servers can automatically pay for API access in a single round-trip — no checkout flow, no user accounts, no manual approval.

This recipe shows the minimum code to:

1. Create an EVM wallet on Tempo Moderato testnet with Dynamic's Node SDK
2. Fund it with test stablecoins from Tempo's faucet
3. Wire up a `viem` `LocalAccount` backed by Dynamic's MPC signing that understands Tempo's custom transaction format
4. Use `mppx` to make a paid request to any MPP-protected endpoint

For a full example, see the [tempo-ppm-integration example on GitHub](https://github.com/dynamic-labs-oss/tempo-mpp-example).

For how HTTP 402 payment flows work in general—and how MPP relates to the separate [x402](/recipes/integrations/x402/implementation) stack—see the [HTTP 402 overview](/recipes/integrations/x402/overview).

***

## Prerequisites

* Node.js 22+
* Dynamic Environment ID and API token — find these in the [Dynamic Dashboard](https://app.dynamic.xyz/dashboard/developer/api)
* Embedded wallets enabled in your Dynamic dashboard

***

## Setup

Install the required packages:

<CodeGroup>
  ```bash npm theme={"system"}
  npm install @dynamic-labs-wallet/node-evm mppx viem
  ```

  ```bash pnpm theme={"system"}
  pnpm add @dynamic-labs-wallet/node-evm mppx viem
  ```

  ```bash yarn theme={"system"}
  yarn add @dynamic-labs-wallet/node-evm mppx viem
  ```
</CodeGroup>

Create a `.env` file with your credentials:

```env .env theme={"system"}
DYNAMIC_ENVIRONMENT_ID=your_environment_id
DYNAMIC_AUTH_TOKEN=your_api_token
```

<Note>
  Use `node --env-file=.env` to load the file automatically, or a library like `dotenv`.
</Note>

***

## Step 1: Initialize the Dynamic wallet client

```ts theme={"system"}
import { DynamicEvmWalletClient } from '@dynamic-labs-wallet/node-evm';

const client = new DynamicEvmWalletClient({
  environmentId: process.env.DYNAMIC_ENVIRONMENT_ID!,
});
await client.authenticateApiToken(process.env.DYNAMIC_AUTH_TOKEN!);
```

***

## Step 2: Create a wallet

Wallets are created with a 2-of-2 threshold signature scheme (MPC). The returned `externalServerKeyShares` are your signing credentials — store them securely alongside the wallet address.

```ts theme={"system"}
const result = await client.createWalletAccount({
  thresholdSignatureScheme: 'TWO_OF_TWO',
  backUpToClientShareService: true,
});

const walletAddress = result.accountAddress;
const keyShares = result.externalServerKeyShares;

console.log('Wallet created:', walletAddress);
```

***

## Step 3: Fund with test stablecoins

Tempo's Moderato testnet faucet distributes test stablecoins (pathUSD, AlphaUSD, BetaUSD, ThetaUSD). These are used as payment tokens for MPP requests.

```ts theme={"system"}
const FAUCET_URL = 'https://docs.tempo.xyz/api/faucet';

const res = await fetch(FAUCET_URL, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ address: walletAddress.toLowerCase() }),
});

if (!res.ok) throw new Error(`Faucet error: ${await res.text()}`);
console.log('Funded:', await res.json());
```

<Note>
  You can also visit [docs.tempo.xyz](https://docs.tempo.xyz/quickstart/faucet) to request tokens manually.
</Note>

***

## Step 4: Build a Tempo-compatible viem account

Dynamic's Node SDK signs transactions using its internal viem serializer, which doesn't understand Tempo's custom transaction format. You need to create a `LocalAccount` adapter that uses Tempo's serializer instead.

```ts theme={"system"}
import { toAccount } from 'viem/accounts';
import { Transaction as TempoTx } from 'viem/tempo';
import type { Address, Hex, SignableMessage } from 'viem';

function createDynamicTempoAccount(address: string, keyShares: any[]) {
  return toAccount({
    address: address as Address,

    async signMessage({ message }: { message: SignableMessage }): Promise<Hex> {
      const msg = typeof message === 'string' ? message : message;
      return client.signMessage({
        message: msg as string,
        accountAddress: address,
        externalServerKeyShares: keyShares,
      });
    },

    async signTransaction(transaction: any, options?: any): Promise<Hex> {
      const serializer = options?.serializer ?? TempoTx.serialize;

      // 1. Serialize the unsigned transaction with Tempo's serializer
      const serializedTx = await serializer(transaction);
      const serializedTxBytes = Uint8Array.from(
        Buffer.from((serializedTx as string).slice(2), 'hex'),
      );

      // 2. Sign raw bytes via Dynamic's MPC
      const signatureEcdsa = await (client as any).sign({
        message: serializedTxBytes,
        accountAddress: address,
        chainName: 'EVM',
        externalServerKeyShares: keyShares,
      });

      // 3. Re-serialize with the ECDSA signature components
      const r = `0x${Buffer.from(signatureEcdsa.r).toString('hex')}` as Hex;
      const s = `0x${Buffer.from(signatureEcdsa.s).toString('hex')}` as Hex;
      const yParity = BigInt(signatureEcdsa.v) === 27n ? 0 : 1;

      return (await serializer(transaction, { r, s, yParity })) as Hex;
    },

    async signTypedData(typedData: any): Promise<Hex> {
      return client.signTypedData({
        accountAddress: address,
        typedData,
        externalServerKeyShares: keyShares,
      });
    },
  });
}

const account = createDynamicTempoAccount(walletAddress, keyShares);
```

***

## Step 5: Make an MPP payment

Initialize `mppx` with the Tempo payment method and use `mppx.fetch()` in place of the global `fetch` for any 402-protected URL. The client handles the negotiation, signs the payment, and resends the request automatically.

```ts theme={"system"}
import { Mppx, tempo } from 'mppx/client';

// polyfill: false prevents mppx from wrapping globalThis.fetch,
// which can interfere with other API clients in the same process.
const mppx = Mppx.create({
  methods: [tempo({ account })],
  polyfill: false,
});

const response = await mppx.fetch('https://mpp.dev/api/ping/paid');

console.log('Status:', response.status);
console.log('Body:', await response.text());

// The payment receipt is returned in a response header
const receipt = response.headers.get('x-payment-receipt');
if (receipt) console.log('Receipt:', receipt);
```

<Note>
  `https://mpp.dev/api/ping/paid` is a public test endpoint that accepts any valid MPP payment. Use it to verify your setup before pointing at a real resource.
</Note>

***

## Putting it all together

```ts theme={"system"}
import { DynamicEvmWalletClient } from '@dynamic-labs-wallet/node-evm';
import { toAccount } from 'viem/accounts';
import { Transaction as TempoTx } from 'viem/tempo';
import { Mppx, tempo } from 'mppx/client';
import type { Address, Hex, SignableMessage } from 'viem';

// 1. Authenticate
const client = new DynamicEvmWalletClient({
  environmentId: process.env.DYNAMIC_ENVIRONMENT_ID!,
});
await client.authenticateApiToken(process.env.DYNAMIC_AUTH_TOKEN!);

// 2. Create wallet
const { accountAddress, externalServerKeyShares } = await client.createWalletAccount({
  thresholdSignatureScheme: 'TWO_OF_TWO',
  backUpToClientShareService: true,
});
console.log('Wallet:', accountAddress);

// 3. Fund from faucet
const faucetRes = await fetch('https://docs.tempo.xyz/api/faucet', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ address: accountAddress.toLowerCase() }),
});
if (!faucetRes.ok) throw new Error(`Faucet: ${await faucetRes.text()}`);

// 4. Build Tempo account adapter
const account = toAccount({
  address: accountAddress as Address,
  async signMessage({ message }: { message: SignableMessage }) {
    return client.signMessage({
      message: message as string,
      accountAddress,
      externalServerKeyShares,
    });
  },
  async signTransaction(transaction: any, options?: any) {
    const serializer = options?.serializer ?? TempoTx.serialize;
    const serializedTx = await serializer(transaction);
    const bytes = Uint8Array.from(Buffer.from((serializedTx as string).slice(2), 'hex'));
    const sig = await (client as any).sign({
      message: bytes,
      accountAddress,
      chainName: 'EVM',
      externalServerKeyShares,
    });
    const r = `0x${Buffer.from(sig.r).toString('hex')}` as Hex;
    const s = `0x${Buffer.from(sig.s).toString('hex')}` as Hex;
    const yParity = BigInt(sig.v) === 27n ? 0 : 1;
    return (await serializer(transaction, { r, s, yParity })) as Hex;
  },
  async signTypedData(typedData: any) {
    return client.signTypedData({ accountAddress, typedData, externalServerKeyShares });
  },
});

// 5. Make an MPP payment
const mppx = Mppx.create({ methods: [tempo({ account })], polyfill: false });
const response = await mppx.fetch('https://mpp.dev/api/ping/paid');
console.log('Status:', response.status);
console.log(await response.text());
```

***

## Additional Resources

* [Tempo documentation](https://docs.tempo.xyz)
* [Tempo Machine Payments guide](https://docs.tempo.xyz/guide/machine-payments/)
* [mppx client SDK](https://github.com/tempohq/mppx)
* [Dynamic Node SDK Quickstart](/node/quickstart)
* [Dynamic API Reference](/api-reference/overview)
