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

# Machine Payments Protocol (MPP)

> Enable AI agents to place orders and pay via HTTP 402 — no Zinc account required.

The [Machine Payments Protocol (MPP)](https://mpp.dev) is an open standard for machine-to-machine payments via HTTP 402, developed by Tempo and Stripe. Zinc supports MPP on the `/agent/orders` endpoint, allowing AI agents to place orders and pay in a single request flow — no Zinc account or API key required.

## Why MPP?

Traditional API integrations require account setup, API keys, and a pre-funded balance. MPP removes these steps by embedding payment directly into the HTTP request cycle:

* **No account needed** — Agents pay per-request using standard HTTP headers
* **Multiple payment methods** — Stripe cards/wallets, Tempo stablecoins, and more
* **Built for agents** — Designed for automated, machine-to-machine payment flows

## How It Works

MPP uses an HTTP 402 challenge-credential-receipt flow:

<Steps>
  <Step title="Request without payment">
    The agent sends a `POST /agent/orders` request without payment credentials.
  </Step>

  <Step title="Receive payment challenge">
    Zinc responds with HTTP `402 Payment Required` and `WWW-Authenticate` headers describing available payment methods and the amount due.
  </Step>

  <Step title="Complete payment">
    The MPP client completes payment through the chosen method (e.g., Stripe checkout, Tempo transfer) and receives a credential.
  </Step>

  <Step title="Resubmit with credential">
    The agent resubmits the original request with the payment credential in the `Authorization` header.
  </Step>

  <Step title="Order confirmed">
    Zinc validates the payment receipt and processes the order, returning a `201` response with order details and two important headers:

    * `X-Api-Key` — A temporary API key the agent can use to check order status via `GET /orders/{id}` without repeating the payment flow.
    * `Payment-Receipt` — The MPP payment receipt confirming the charge.
  </Step>
</Steps>

## Checking Order Status

The `201` response includes an `X-Api-Key` header containing a temporary API key. Use this key to poll the order status without going through the 402 payment flow again:

```bash theme={null}
curl https://api.zinc.com/orders/{order_id} \
  -H "Authorization: Bearer <X-Api-Key value>"
```

<Info>
  The `X-Api-Key` is scoped to the order it was issued for. Store it after placing the order so your agent can track fulfillment progress.
</Info>

## Quick Start

### Install an MPP Client

MPP client libraries handle the 402 flow automatically. Official SDKs are available in TypeScript, Python, and Rust.

<CodeGroup>
  ```bash npm theme={null}
  npm install mppx viem
  ```

  ```bash pip theme={null}
  pip install pympp
  ```
</CodeGroup>

### Place an Order

The easiest way to test is with the `mppx` CLI, which handles the 402 payment flow automatically:

```bash theme={null}
npx mppx https://api.zinc.com/agent/orders \
  --method POST \
  --body '{
    "products": [{"url": "https://www.amazon.com/dp/B0EXAMPLE"}],
    "shipping_address": {
      "first_name": "Jane",
      "last_name": "Smith",
      "address_line1": "123 Main St",
      "city": "Seattle",
      "state": "WA",
      "postal_code": "98101",
      "country": "US",
      "phone_number": "2065551234"
    }
  }'
```

To integrate programmatically, use the client SDKs:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Mppx, tempo } from "mppx/client";
  import { privateKeyToAccount } from "viem/accounts";

  const mppx = Mppx.create({
    methods: [
      tempo({
        account: privateKeyToAccount("0x..."),
        maxDeposit: "1",
      }),
    ],
  });

  // The MPP client handles the 402 challenge flow automatically
  const response = await mppx.fetch(
    "https://api.zinc.com/agent/orders",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        products: [
          { url: "https://www.amazon.com/dp/B0EXAMPLE" }
        ],
        shipping_address: {
          first_name: "Jane",
          last_name: "Smith",
          address_line1: "123 Main St",
          city: "Seattle",
          state: "WA",
          postal_code: "98101",
          country: "US",
          phone_number: "2065551234",
        },
      }),
    }
  );

  console.log(await response.json());
  ```
</CodeGroup>

## Supported Payment Methods

| Method     | Description                   |
| ---------- | ----------------------------- |
| **Stripe** | Cards and wallets via Stripe  |
| **Tempo**  | Stablecoin payments via Tempo |

<Info>
  The available payment methods are returned in the `WWW-Authenticate` headers of the 402 response. Your MPP client will automatically select a compatible method.
</Info>

## Using Stripe with MPP

The MPP client libraries (`mppx`, `pympp`) abstract over payment methods. If you'd rather integrate with Stripe directly — for example, minting Shared Payment Tokens (SPTs) via the [Stripe Link API](https://link.com/agents) or `link-cli` — here's the wire-level flow.

### 1. Request the challenge

POST your order to `/agent/orders` with no `Authorization` header:

```http theme={null}
POST /agent/orders HTTP/1.1
Content-Type: application/json

{
  "products": [{"url": "https://www.amazon.com/dp/B0EXAMPLE", "quantity": 1}],
  "shipping_address": { ... },
  "max_price": 5000
}
```

Zinc responds with **HTTP 402** and one `WWW-Authenticate` header per configured payment method. Pick the one containing `method="stripe"`:

```
WWW-Authenticate: Payment realm="zinc", method="stripe", id="...", intent="charge", request="<base64>", ...
```

<Warning>
  Many HTTP clients collapse repeated response headers into a single comma-joined string, which corrupts the `WWW-Authenticate` param list. Read the **raw** header list and select by `method="stripe"`. In Python with `httpx`, use `resp.headers.raw`.
</Warning>

The `request` value is a base64-encoded JSON blob whose `methodDetails.networkId` is the Stripe agentic-commerce profile ID — you'll pass it to Stripe when minting the SPT.

### 2. Mint a Shared Payment Token

Use the Stripe Link API — directly or via `link-cli` — to mint an SPT bound to this challenge:

```bash theme={null}
# Decode the challenge to extract the network_id
link-cli mpp decode --challenge "<WWW-Authenticate value>"

# Create the spend request (prompts the user to approve in the Link app)
link-cli spend-request create \
  --credential-type shared_payment_token \
  --network-id <network_id> \
  --payment-method-id <pm_xxx> \
  --amount <max_price_cents + 100> \
  --currency usd \
  --context "<purchase description, at least 100 characters>" \
  --line-item "name:<product_url>,quantity:1,unit_amount:<max_price_cents>" \
  --total "type:total,display_text:Total,amount:<amount>" \
  --request-approval

# After approval, retrieve the SPT
link-cli spend-request retrieve <id> --include shared_payment_token
```

<Info>
  The spend-request amount must equal `max_price + $1.00`. The extra dollar is the Zinc API fee, baked into the challenge so that the full `max_price` remains available to the retailer.
</Info>

### 3. Resubmit with the credential

Reissue the original POST, this time with an `Authorization: Payment <credential>` header:

```http theme={null}
POST /agent/orders HTTP/1.1
Content-Type: application/json
Authorization: Payment <base64-encoded-credential>

{ ...same body as step 1... }
```

The credential is **not** just the SPT — it's an MPP `Credential` object containing:

* A `ChallengeEcho` echoing the exact fields from the challenge in step 1 (`id`, `realm`, `method`, `intent`, `request`, `expires`, `digest`)
* A `payload` of `{"type": "spt", "shared_payment_granted_token": "<spt_xxx>"}`

Constructing and serializing this by hand is error-prone. Use `pympp`'s helpers:

```python theme={null}
from mpp import (
    ChallengeEcho, Credential, format_authorization, parse_www_authenticate,
)

challenge = parse_www_authenticate(stripe_www_authenticate_value)
credential = Credential(
    challenge=ChallengeEcho(
        id=challenge.id,
        realm=challenge.realm,
        method=challenge.method,
        intent=challenge.intent,
        request=challenge.request_b64,
        expires=challenge.expires,
        digest=challenge.digest,
    ),
    payload={"type": "spt", "shared_payment_granted_token": spt},
)
auth_header = format_authorization(credential)
```

On success, Zinc returns **201** with the order details, an `X-Api-Key` header for polling status, and a `Payment-Receipt` header confirming the verified MPP receipt.

<Tip>
  **Save the `X-Api-Key` from the 201 response.** It's a temporary API key scoped to this order, and it's the only way to check fulfillment status after placement without repeating the 402 payment flow. See [Checking Order Status](#checking-order-status) for the polling request format.
</Tip>

## Order Lifecycle and Refunds

MPP orders include automatic safeguards so agents aren't charged for orders Zinc can't (or doesn't) fulfill.

### Validation runs before payment

When you submit an order with a payment credential, Zinc validates the order data — product URLs, retailer support, shipping country, and address — **before** charging the credential. A validation failure returns **HTTP 400** with no charge applied to the credential, leaving the SPT untouched and reusable for a corrected retry.

This applies to all MPP methods, not just Stripe.

### Automatic refunds (Stripe)

For orders paid via Stripe, the agent authorizes `max_price + $1.00 API fee` upfront. Zinc then refunds the credential automatically in two cases:

* **Partial refund** — When the retailer's actual total comes in below `max_price`, Zinc refunds the difference via the Stripe PaymentIntent. The agent effectively pays `actual_total + $1.00`.
* **Full refund** — If the order ultimately fails (status `failed` — e.g., the retailer rejects the order, the item is out of stock, etc.), Zinc refunds the entire charged amount.

<Info>
  Refunds are issued server-side against the original Stripe PaymentIntent and follow Stripe's standard settlement timelines. No agent-side action is required.
</Info>

## Next Steps

<CardGroup cols={3}>
  <Card title="MPP Playground" icon="play" href="https://agent.zinc.com">
    Try placing an MPP order in our interactive playground.
  </Card>

  <Card title="API Reference" icon="code" href="/v2/api-reference/agent/create-order">
    See the full API spec for the MPP order endpoint.
  </Card>

  <Card title="MPP Specification" icon="book" href="https://mpp.dev">
    Read the full MPP protocol specification.
  </Card>
</CardGroup>
