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

# Get started as a seller

> Complete Step 1 of the InFlow integration. Accept MPP and x402 in the sandbox, then go live and publish your service to the directory.

Complete Step 1 of the InFlow integration, Payments: take an MPP and an x402 payment on your own endpoint in the sandbox. Step 2 is [Enrollment](/sellers/enable-enrollment), and Step 3 is [Discovery](/sellers/make-products-discoverable). See [Integrate your service](/sellers/integrate-your-service).

Steps 1 to 5 need no live money and no public server.

<Tip>
  A coding agent can do this work with you. See [Integrate with a coding agent](/sellers/integrate-with-a-coding-agent).
</Tip>

## Before you start

This guide uses Node.js. For Python, Go, or Rust, see [Protect an endpoint in Python, Go, or Rust](/sellers/protect-an-endpoint-other-languages).

| Item | Detail |
| - | - |
| Node | Version 22 LTS or later. Node 20 reached end of life. |
| An InFlow **Seller** account | A Developer account authenticates, but every seller call fails with `403 SELLER_ACCOUNT_REQUIRED`. |
| A server you can edit | The examples use Express. Hono, Fastify, and Next 16 work the same way. |

## Step 1: Integrate both protocols

InFlow Payments accepts MPP and x402. Integrate both, so that agents of either protocol can pay you. Both protocols accept the InFlow balance. [Payments with MPP and x402](/concepts/payments) compares them.

Share the pricing and fulfillment logic between the two. Each request goes through one charging path only. You can start with one protocol and add the other later.

<Accordion title="Advertise both protocols on one endpoint">
  Each protocol protects its own endpoints, and one server can use both. To advertise both protocols in one `402` response on the same endpoint, you need custom work. No InFlow, x402, or `mppx` package ships a combinator for this. You build the endpoint from low-level primitives and you own its correctness.

  You cannot stack `paymentMiddlewareFromConfig` and `mppx.charge(...)` on one endpoint. Each middleware builds and sends the whole `402` response, then ends the request.
</Accordion>

## Step 2: Register a Seller account

Register a Seller account at [sandbox.inflowpay.ai](https://sandbox.inflowpay.ai). Create an API key in the dashboard. Put the key and its environment in the same `.env` file. The two must always change together.

```bash .env theme={null}
INFLOW_ENVIRONMENT=sandbox
INFLOW_API_KEY=your_sandbox_key_here
PORT=3000
```

<Warning>
  Never set a `baseUrl`. The SDK resolves the correct host from `environment` alone. A `baseUrl` sends your traffic to an unexpected host.
</Warning>

For account types and key safety, see [Create a Seller account](/sellers/create-a-seller-account).

## Step 3: Protect an endpoint

Follow both tabs. One server protects some endpoints with x402 and other endpoints with MPP. The code reads both values from the environment and stops at startup if one is missing.

<Tabs>
  <Tab title="x402 (Express)">
    ```bash theme={null}
    pnpm add @inflowpayai/x402-seller @x402/express @x402/core @x402/extensions express
    pnpm add -D dotenv
    ```

    ```ts server.ts theme={null}
    import 'dotenv/config';
    import express from 'express';
    import { paymentMiddlewareFromConfig } from '@x402/express';
    import {
      createInflowFacilitator,
      createInflowSellerClient,
      inflowAccepts,
      inflowSchemeRegistrations,
    } from '@inflowpayai/x402-seller';

    const environment = process.env.INFLOW_ENVIRONMENT as 'sandbox' | 'production';
    const apiKey = process.env.INFLOW_API_KEY;
    if (!environment || !apiKey) {
      console.error('Set INFLOW_ENVIRONMENT and INFLOW_API_KEY (see .env).');
      process.exit(1);
    }

    // Verifies and settles payments through InFlow.
    const inflow = createInflowFacilitator({ environment, apiKey });

    // Async. It primes its caches before it resolves.
    const seller = await createInflowSellerClient({ environment, apiKey });

    const app = express();
    app.use(express.json());

    app.use(
      paymentMiddlewareFromConfig(
        {
          'GET /api/widgets': {
            accepts: await inflowAccepts(seller, { price: '$0.01' }),
          },
        },
        [inflow],
        // Required third argument. The middleware refuses to boot without it.
        await inflowSchemeRegistrations(seller),
      ),
    );

    app.get('/api/widgets', (_req, res) => res.json({ widgets: [1, 2, 3] }));
    app.get('/free', (_req, res) => res.json({ ok: true }));

    app.listen(Number(process.env.PORT ?? 3000));
    ```

    `inflowAccepts` reads a price as `'$0.01'`, as `'0.10 USDC'`, or as a bare `'0.01'` with a `currency` field. Stablecoin payments have no minimum amount. Card payments in USD have a minimum of USD 0.50, in whole cents.
  </Tab>

  <Tab title="MPP (Express)">
    ```bash theme={null}
    pnpm add @inflowpayai/mpp-seller mppx@^0.8.17
    pnpm add -D dotenv
    ```

    MPP needs one more value. Generate it with `openssl rand -base64 32`. Keep it stable across deploys.

    ```bash .env theme={null}
    INFLOW_ENVIRONMENT=sandbox
    INFLOW_API_KEY=your_sandbox_key_here
    MPP_SECRET_KEY=paste_the_openssl_output_here
    PORT=3000
    ```

    ```ts server.ts theme={null}
    import 'dotenv/config';
    import express from 'express';
    import { Mppx } from 'mppx/express';
    import { inflow, paymentHttpTransport } from '@inflowpayai/mpp-seller';

    const environment = process.env.INFLOW_ENVIRONMENT as 'sandbox' | 'production';
    const apiKey = process.env.INFLOW_API_KEY;
    if (!environment || !apiKey) {
      console.error('Set INFLOW_ENVIRONMENT and INFLOW_API_KEY (see .env).');
      process.exit(1);
    }

    const mppx = Mppx.create({
      transport: paymentHttpTransport(),
      methods: [inflow({ apiKey, environment })],
      secretKey: process.env.MPP_SECRET_KEY,
    });

    const app = express();
    app.use(express.json());

    app.get('/api/widgets', mppx.charge({ amount: '0.01', currency: 'USDC' }), (_req, res) =>
      res.json({ widgets: [1, 2, 3] }),
    );
    app.get('/free', (_req, res) => res.json({ ok: true }));

    app.listen(Number(process.env.PORT ?? 3000));
    ```

    Import `Mppx` from `mppx/express`, not from `@inflowpayai/mpp-seller`. The other `mppx` adapters are `mppx/hono`, `mppx/nextjs`, and `mppx/elysia`.

    The currency picks the rail. The buyer never picks it. Crypto such as `USDC` uses the `balance` rail. Fiat such as `USD` uses the `instrument` rail. Stablecoin payments have no minimum amount. Card payments in USD have a minimum of USD 0.50, in whole cents.

    An unsupported currency such as `JPY` throws `MppUnsupportedCurrencyError`. If you accept dynamic currencies, read the rail map with `createConfigClient`.
  </Tab>
</Tabs>

<Accordion title="Other frameworks">
  Only the adapter import changes.

  * [x402 on Hono](https://github.com/inflowpayai/inflow-node/tree/main/examples/x402-seller-hono)
  * [x402 on Fastify](https://github.com/inflowpayai/inflow-node/tree/main/examples/x402-seller-fastify). `paymentMiddlewareFromConfig` changes the Fastify instance in place. It does not return a middleware. Call it as `paymentMiddlewareFromConfig(app, routes, [inflow], regs)`.
  * [x402 on Next 16](https://github.com/inflowpayai/inflow-node/tree/main/examples/x402-seller-next). Next 16 renamed `middleware.ts` to `proxy.ts`. Use a root-level `proxy.ts` with `paymentProxyFromConfig`.
  * [MPP on Express](https://github.com/inflowpayai/inflow-node/tree/main/examples/mpp-seller-express) and [MPP on Hono](https://github.com/inflowpayai/inflow-node/tree/main/examples/mpp-seller-hono).
</Accordion>

For schemes, multi-currency charges, and subscriptions, see [Protect an endpoint](/sellers/protect-an-endpoint).

## Step 4: Confirm the challenge

Start your server. Call the paid endpoint with no payment.

<CodeGroup>
  ```bash x402 theme={null}
  curl -i http://localhost:3000/api/widgets
  # HTTP/1.1 402 Payment Required
  # PAYMENT-REQUIRED: <base64 JSON>

  curl -i http://localhost:3000/free
  # HTTP/1.1 200 OK
  ```

  ```bash MPP theme={null}
  curl -i http://localhost:3000/api/widgets
  # HTTP/1.1 402 Payment Required
  # WWW-Authenticate: Payment id="…", realm="…", method="inflow", intent="charge", request="…"
  ```
</CodeGroup>

A `200` on a paid endpoint means that the middleware did not run.

## Step 5: Pay your own endpoint

The CLI uses `production` by default. Your server runs in the sandbox, so add `--sandbox` to the login and to each pay command. See [Authenticate](/agents/authenticate#choose-an-environment).

<Steps>
  <Step title="Install the CLI">
    <CodeGroup>
      ```bash macOS / Linux theme={null}
      curl -fsSL https://inflowcli.ai/install.sh | bash
      ```

      ```powershell Windows theme={null}
      irm https://inflowcli.ai/install.ps1 | iex
      ```
    </CodeGroup>
  </Step>

  <Step title="Unlock the vault">
    Run this command yourself in a terminal. Choose a PIN or a passphrase. Never give that PIN or passphrase to an agent.

    ```bash theme={null}
    inflow vault unlock
    ```
  </Step>

  <Step title="Log in">
    ```bash theme={null}
    inflow auth login --sandbox --client-name "seller-test" --interval 5
    ```

    Open the verification URL that the CLI prints. Approve the login. Then run `inflow auth status` to confirm.
  </Step>

  <Step title="Inspect, then pay">
    `inspect` reports the capabilities of the endpoint. It pays nothing.

    ```bash theme={null}
    inflow inspect http://localhost:3000/api/widgets
    ```

    Pay with the command for your protocol.

    <CodeGroup>
      ```bash x402 theme={null}
      inflow x402 pay http://localhost:3000/api/widgets --sandbox --interval 5
      ```

      ```bash MPP theme={null}
      inflow mpp pay http://localhost:3000/api/widgets --sandbox --interval 5
      ```
    </CodeGroup>

    `--interval 5` polls every 5 seconds until the payment settles. The default of `0` returns a transaction id at once and does not block.
  </Step>
</Steps>

For buyer examples and more checks, see [Test a payment](/sellers/test-a-payment).

## Step 6: Go live

Production changes your `.env` file and nothing else. Register a production Seller account at [app.inflowpay.ai](https://app.inflowpay.ai). Create a key there. Change both values together.

```bash .env theme={null}
INFLOW_ENVIRONMENT=production
INFLOW_API_KEY=your_production_key_here
```

For MPP, set a production `MPP_SECRET_KEY` as well. Never reuse the sandbox secret. Deploy the same build that you tested. See [Go live](/sellers/go-live).

## Step 7: Get listed in the directory

Steps 1 to 6 let an agent pay you, but not find you. Complete [Step 2: Enrollment](/sellers/enable-enrollment) and [Step 3: Discovery](/sellers/make-products-discoverable). Then [publish to the directory](/sellers/publish-to-the-directory). The directory has no sandbox and does not accept `localhost`.

| Document | Path | Node.js package |
| - | - | - |
| ODP Service Document | `/.well-known/odp` | [`@offering-protocol/service`](https://www.npmjs.com/package/@offering-protocol/service) |
| AEP Service Document | `/.well-known/aep` | [`@aep-foundation/service`](https://www.npmjs.com/package/@aep-foundation/service) with [`@aep-foundation/express`](https://www.npmjs.com/package/@aep-foundation/express), `@aep-foundation/hono`, or `@aep-foundation/next` |

Point every ODP purchase action at the endpoint that you protected in step 3. The [`mpp-aep-seller-express`](https://github.com/inflowpayai/inflow-node/tree/main/examples/mpp-aep-seller-express) example puts AEP in front of MPP. It uses `x-aep-api-key` for the AEP credential, which leaves `Authorization: Payment` free for MPP.

## Troubleshooting

For other errors, such as `403 SELLER_ACCOUNT_REQUIRED` and `RouteConfigurationError`, see [Errors](/reference/errors).

<AccordionGroup>
  <Accordion title="createInflowSellerClient hangs or rejects">
    It makes authenticated calls at construction to prime its caches. Check the API key. Check that `INFLOW_ENVIRONMENT` matches the dashboard that issued the key.
  </Accordion>

  <Accordion title="Every request returns 402, even after payment">
    Check the header the buyer sends. x402 uses `PAYMENT-SIGNATURE`. MPP uses `Authorization: Payment`.

    Then check that the buyer speaks the protocol your server implements. An MPP buyer cannot pay an x402 endpoint.
  </Accordion>

  <Accordion title="X402PriceParseError">
    `inflowAccepts` could not read your `price` string. Catch this at startup, not per request. Step 3 lists the accepted forms.
  </Accordion>

  <Accordion title="MppCredentialProblemError">
    Credential validation or broadcast failed. Read the `type` field of its RFC 9457 problem detail. Use the `type` to decide between a new challenge and a hard failure.

    A status of `500` means that the result is not known. Do not create a new payment. Check the payment status first.
  </Accordion>
</AccordionGroup>

## Check your work

* [ ] An unpaid call to your endpoint returns `402` with the correct header.
* [ ] An unpriced endpoint still returns `200`.
* [ ] `inflow inspect <your-url>` reports the protocol you advertise.
* [ ] A CLI payment returns your endpoint's real response body.
* [ ] The payment appears in your dashboard.
* [ ] Your production deploy uses the same build as your sandbox deploy.

## Next step

<CardGroup cols={2}>
  <Card title="Step 2: Enable agent enrollment" icon="id-card" href="/sellers/enable-enrollment">
    Let agents enroll with your service and get credentials.
  </Card>

  <Card title="Ask in Discord" icon="discord" href="https://discord.gg/Z9nmMAgaR4">
    Tell the team which step failed and which error you got.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.