Skip to main content
The Machine Payments Protocol (MPP) 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:
1

Request without payment

The agent sends a POST /agent/orders request without payment credentials.
2

Receive payment challenge

Zinc responds with HTTP 402 Payment Required and WWW-Authenticate headers describing available payment methods and the amount due.
3

Complete payment

The MPP client completes payment through the chosen method (e.g., Stripe checkout, Tempo transfer) and receives a credential.
4

Resubmit with credential

The agent resubmits the original request with the payment credential in the Authorization header.
5

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.

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:
curl https://api.zinc.com/orders/{order_id} \
  -H "Authorization: Bearer <X-Api-Key value>"
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.

Quick Start

Install an MPP Client

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

Place an Order

The easiest way to test is with the mppx CLI, which handles the 402 payment flow automatically:
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:
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());

Supported Payment Methods

MethodDescription
StripeCards and wallets via Stripe
TempoStablecoin payments via Tempo
The available payment methods are returned in the WWW-Authenticate headers of the 402 response. Your MPP client will automatically select a compatible method.

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 or link-cli — here’s the wire-level flow.

1. Request the challenge

POST your order to /agent/orders with no Authorization header:
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>", ...
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.
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:
# 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
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.

3. Resubmit with the credential

Reissue the original POST, this time with an Authorization: Payment <credential> header:
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:
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.
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 for the polling request format.

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.
Refunds are issued server-side against the original Stripe PaymentIntent and follow Stripe’s standard settlement timelines. No agent-side action is required.

Next Steps

MPP Playground

Try placing an MPP order in our interactive playground.

API Reference

See the full API spec for the MPP order endpoint.

MPP Specification

Read the full MPP protocol specification.