# x402

The payer-facing facilitator surface: public, unversioned, keyless. Quote, verify, settle.

## What is for sale, across every merchant

`GET /x402/resources`

The cross-merchant discovery index: every resource a merchant has opted into the public index, newest first. Pass `merchant` instead to read one merchant's resources — the per-merchant read is not paginated and is not limited to listed ones.

Nothing is listed by default. Listing only decides whether a discovery reader is shown a resource; an unlisted resource is still payable by anyone holding its URL, because the URL is the access control.

### Query parameters

- `limit` `integer | null`: Page size. Defaults to 50, capped at 100.
- `cursor` `string`: Opaque cursor from the previous page's `nextCursor`.
- `merchant` `string`

### Responses

#### `200`: A page of resources.

Body (`application/json`):

- `resources` `object[]` _(required)_
  - `id` `string` _(required)_
  - `url` `string` _(required)_: The URL an agent calls. A resource is looked up by it.
  - `description` `string`
  - `mimeType` `string`
- `nextCursor` `string`: Present only when another page exists. Absent means the walk is done.

#### `400`: Invalid `limit` or cursor.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `404`: x402 is not enabled on this deployment.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

### Example request

```bash
curl 'https://api.mayarin.xyz/x402/resources?limit=0&cursor=string&merchant=string'
```

```ts
fetch('https://api.mayarin.xyz/x402/resources?limit=0&cursor=string&merchant=string')
```

## Every listed obligation an agent can pay

`GET /x402/payables`

Invoices with an outstanding balance and fixed-amount payment links, across every merchant, newest first — what an agent reads before it has met anyone. Each entry carries the price a `402` would actually quote, so a reader deciding whether to pay need not ask each one.

Draft, void and paid-in-full invoices, disabled and expired links, and open-amount links never appear and never quote, listed or not.

### Query parameters

- `limit` `integer | null`: Page size. Defaults to 50, capped at 100.
- `cursor` `string`: Opaque cursor from the previous page's `nextCursor`.

### Responses

#### `200`: A page of payables.

Body (`application/json`):

- `payables` `object[]` _(required)_
  - `kind` `string` _(required)_
  - `id` `string` _(required)_
  - `createdAt` `string` _(required)_: ISO 8601.
  - `merchant` `string` _(required)_
  - `title` `string`
  - `amount` `object & unknown` _(required)_
  - `url` `string` _(required)_: Where to send the `402` request.
  - `dueAt` `string`: ISO 8601. Present on an invoice with a due date.
- `nextCursor` `string`

#### `400`: Invalid `limit` or cursor.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `404`: x402 payables are not enabled on this deployment.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

### Example request

```bash
curl 'https://api.mayarin.xyz/x402/payables?limit=0&cursor=string'
```

```ts
fetch('https://api.mayarin.xyz/x402/payables?limit=0&cursor=string')
```

## Quote one invoice or link — or settle it

`GET /x402/payables/{kind}/{id}`

One endpoint, two answers, decided by the `PAYMENT-SIGNATURE` header. Without one: a `402` whose `PAYMENT-REQUIRED` header quotes the **full** outstanding amount — an authorization for less than what is owed is refused, and the remedy is a new `402` for the whole balance. With one: verify, settle, and answer `200` naming the payment intent the settlement created.

A payable's price is held in a quote row for a short window together with the exact rails it offered, which is what the `409` and `410` are about.

### Path parameters

- `kind` `string` _(required)_
- `id` `string` _(required)_

### Responses

#### `200`: Settled. `PAYMENT-RESPONSE` carries the facilitator's receipt.

Body (`application/json`):

- `paymentIntent` `string` _(required)_: The intent this settlement created.
- `payable` `object` _(required)_
  - `kind` `string` _(required)_
  - `id` `string` _(required)_

#### `400`: Unknown payable kind, or a signature the chain did not accept.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `402`: Payment required. The price and rails ride the `PAYMENT-REQUIRED` header, base64-encoded.

#### `404`: No such payable, or it is not payable over x402.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `409`: Another authorization claimed this obligation first, or the amount signed is no longer the amount owed.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `410`: Quote expired — request a new `402` at the same URL.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

### Example request

```bash
curl https://api.mayarin.xyz/x402/payables/invoice/string
```

```ts
fetch('https://api.mayarin.xyz/x402/payables/invoice/string')
```

## Which rail to pay on, and why

`GET /x402/resources/{id}/rail`

Returns the first accepted rail in merchant configuration. Every accepted rail has already passed capability and pricing checks; a payer may still choose another entry from the `402`.

### Path parameters

- `id` `string` _(required)_

### Responses

#### `200`: The configured default rail.

Body (`application/json`):

- `chain` `string` _(required)_: The rail to pay on.
- `reason` `string` _(required)_: Why this deterministic default was selected.

#### `404`: No resource with that id, or x402 is not enabled on this deployment.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

### Example request

```bash
curl https://api.mayarin.xyz/x402/resources/string/rail
```

```ts
fetch('https://api.mayarin.xyz/x402/resources/string/rail')
```

## Quote a resource without calling it

`GET /x402/resources/{id}/payment-required`

The price and rails, without making the call being decided about. Not part of the x402 specification — the price normally rides a `402` — but a client deciding whether to pay should not have to make the call first.

### Path parameters

- `id` `string` _(required)_

### Responses

#### `200`: The price and the rails that offer it.

Body (`application/json`):

- `x402Version` `integer` _(required)_
- `error` `string`: Why a carried payment was refused — read it before retrying.
- `resource` `object` _(required)_
  - `url` `string` _(required)_
  - `description` `string`
  - `mimeType` `string`
- `accepts` `object[]` _(required)_: One price, several ways to pay; the payer picks.
  - `scheme` `string` _(required)_
  - `network` `string` _(required)_: CAIP-2, e.g. `eip155:84532`.
  - `amount` `string` _(required)_: Atomic units, as a decimal string.
  - `asset` `string` _(required)_: ERC-20 contract address.
  - `payTo` `string` _(required)_: Recipient. The operator, not the merchant, on a cross-asset rail.
  - `maxTimeoutSeconds` `integer` _(required)_
  - `extra` `object`: Scheme data: the token's EIP-712 domain, and `assetTransferMethod` when stated.
- `extensions` `object`

#### `404`: No resource with that id, or x402 is not enabled on this deployment.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

### Example request

```bash
curl https://api.mayarin.xyz/x402/resources/string/payment-required
```

```ts
fetch('https://api.mayarin.xyz/x402/resources/string/payment-required')
```

## Would this authorization go through?

`POST /x402/resources/{id}/verify`

Checks the signature against the chain without broadcasting. The body's `paymentRequirements` is not what gets verified — Mayarin rebuilds the requirements from the resource and uses the carried copy only to identify which quoted option the payer chose. A facilitator that verified against the requirements handed to it would verify a payment against its own claims.

### Path parameters

- `id` `string` _(required)_

### Request body (`application/json`)

- `x402Version` `number` _(required)_
- `paymentPayload` `object` _(required)_
  - `x402Version` `integer` _(required)_
  - `resource` `object`
    - `url` `string` _(required)_
    - `description` `string`
    - `mimeType` `string`
  - `accepted` `object & unknown` _(required)_
  - `payload` `object` _(required)_
    - `signature` `string` _(required)_
    - `authorization` `object` _(required)_
      - `from` `string` _(required)_
      - `to` `string` _(required)_
      - `value` `string` _(required)_
      - `validAfter` `string` _(required)_
      - `validBefore` `string` _(required)_
      - `nonce` `string` _(required)_: 32 bytes, `0x`-prefixed. Also the idempotency key server-side.
  - `extensions` `object`
- `paymentRequirements` `object` _(required)_
  - `scheme` `string` _(required)_
  - `network` `string` _(required)_: CAIP-2, e.g. `eip155:84532`.
  - `amount` `string` _(required)_: Atomic units, as a decimal string.
  - `asset` `string` _(required)_: ERC-20 contract address.
  - `payTo` `string` _(required)_: Recipient. The operator, not the merchant, on a cross-asset rail.
  - `maxTimeoutSeconds` `integer` _(required)_
  - `extra` `object`: Scheme data: the token's EIP-712 domain, and `assetTransferMethod` when stated.

### Responses

#### `200`: The verdict — `isValid`, and the closed-set reason when false.

Body (`application/json`):

- `isValid` `boolean` _(required)_
- `invalidReason` `string`: A closed set a payer's client can branch on.
- `payer` `string`

#### `400`: Validation error.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `404`: No resource with that id, or x402 is not enabled on this deployment.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

### Example request

```bash
curl https://api.mayarin.xyz/x402/resources/string/verify \
  --request POST \
  --header 'Content-Type: application/json' \
  --data '{
  "x402Version": 2,
  "paymentPayload": {
    "x402Version": 0,
    "resource": {
      "url": "string",
      "description": "string",
      "mimeType": "string"
    },
    "accepted": {
      "scheme": "string",
      "network": "string",
      "amount": "string",
      "asset": "string",
      "payTo": "string",
      "maxTimeoutSeconds": 0,
      "extra": {}
    },
    "payload": {
      "signature": "string",
      "authorization": {
        "from": "string",
        "to": "string",
        "value": "string",
        "validAfter": "string",
        "validBefore": "string",
        "nonce": "string"
      }
    },
    "extensions": {}
  },
  "paymentRequirements": {
    "scheme": "string",
    "network": "string",
    "amount": "string",
    "asset": "string",
    "payTo": "string",
    "maxTimeoutSeconds": 0,
    "extra": {}
  }
}'
```

```ts
fetch('https://api.mayarin.xyz/x402/resources/string/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    x402Version: 2,
    paymentPayload: {
      x402Version: 0,
      resource: {
        url: 'string',
        description: 'string',
        mimeType: 'string'
      },
      accepted: {
        scheme: 'string',
        network: 'string',
        amount: 'string',
        asset: 'string',
        payTo: 'string',
        maxTimeoutSeconds: 0,
        extra: {}
      },
      payload: {
        signature: 'string',
        authorization: {
          from: 'string',
          to: 'string',
          value: 'string',
          validAfter: 'string',
          validBefore: 'string',
          nonce: 'string'
        }
      },
      extensions: {}
    },
    paymentRequirements: {
      scheme: 'string',
      network: 'string',
      amount: 'string',
      asset: 'string',
      payTo: 'string',
      maxTimeoutSeconds: 0,
      extra: {}
    }
  })
})
```

## Broadcast it, and credit the merchant

`POST /x402/resources/{id}/settle`

The one that moves money: verifies authoritatively, broadcasts the transfer, confirms it on-chain, and posts the double-entry records that credit the merchant. The payment's nonce is the idempotency key server-side, so a second settle of the same authorization is refused rather than charged twice. An authorization that was valid but did not settle answers `200` with `success: false` and the reason in `errorReason`.

### Path parameters

- `id` `string` _(required)_

### Request body (`application/json`)

- `x402Version` `number` _(required)_
- `paymentPayload` `object` _(required)_
  - `x402Version` `integer` _(required)_
  - `resource` `object`
    - `url` `string` _(required)_
    - `description` `string`
    - `mimeType` `string`
  - `accepted` `object & unknown` _(required)_
  - `payload` `object` _(required)_
    - `signature` `string` _(required)_
    - `authorization` `object` _(required)_
      - `from` `string` _(required)_
      - `to` `string` _(required)_
      - `value` `string` _(required)_
      - `validAfter` `string` _(required)_
      - `validBefore` `string` _(required)_
      - `nonce` `string` _(required)_: 32 bytes, `0x`-prefixed. Also the idempotency key server-side.
  - `extensions` `object`
- `paymentRequirements` `object` _(required)_
  - `scheme` `string` _(required)_
  - `network` `string` _(required)_: CAIP-2, e.g. `eip155:84532`.
  - `amount` `string` _(required)_: Atomic units, as a decimal string.
  - `asset` `string` _(required)_: ERC-20 contract address.
  - `payTo` `string` _(required)_: Recipient. The operator, not the merchant, on a cross-asset rail.
  - `maxTimeoutSeconds` `integer` _(required)_
  - `extra` `object`: Scheme data: the token's EIP-712 domain, and `assetTransferMethod` when stated.

### Responses

#### `200`: The settlement outcome — check `success`.

Body (`application/json`):

- `success` `boolean` _(required)_
- `errorReason` `string`: Free text, unlike a verify's closed set.
- `payer` `string`
- `transaction` `string` _(required)_: Transaction hash; the empty string when settlement failed.
- `network` `string` _(required)_
- `amount` `string`
- `extensions` `object`

#### `400`: Validation error, or a signature the chain did not accept.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `404`: No resource with that id, or x402 is not enabled on this deployment.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `409`: Quote conflict — the price or rails changed under the authorization.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

#### `410`: Quote expired — request a new `402` at the same URL.

Body (`application/json`):

- `error` `object` _(required)_
  - `code` `string` _(required)_
  - `message` `string` _(required)_
  - `retryable` `boolean` _(required)_
  - `details` `object`

### Example request

```bash
curl https://api.mayarin.xyz/x402/resources/string/settle \
  --request POST \
  --header 'Content-Type: application/json' \
  --data '{
  "x402Version": 2,
  "paymentPayload": {
    "x402Version": 0,
    "resource": {
      "url": "string",
      "description": "string",
      "mimeType": "string"
    },
    "accepted": {
      "scheme": "string",
      "network": "string",
      "amount": "string",
      "asset": "string",
      "payTo": "string",
      "maxTimeoutSeconds": 0,
      "extra": {}
    },
    "payload": {
      "signature": "string",
      "authorization": {
        "from": "string",
        "to": "string",
        "value": "string",
        "validAfter": "string",
        "validBefore": "string",
        "nonce": "string"
      }
    },
    "extensions": {}
  },
  "paymentRequirements": {
    "scheme": "string",
    "network": "string",
    "amount": "string",
    "asset": "string",
    "payTo": "string",
    "maxTimeoutSeconds": 0,
    "extra": {}
  }
}'
```

```ts
fetch('https://api.mayarin.xyz/x402/resources/string/settle', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    x402Version: 2,
    paymentPayload: {
      x402Version: 0,
      resource: {
        url: 'string',
        description: 'string',
        mimeType: 'string'
      },
      accepted: {
        scheme: 'string',
        network: 'string',
        amount: 'string',
        asset: 'string',
        payTo: 'string',
        maxTimeoutSeconds: 0,
        extra: {}
      },
      payload: {
        signature: 'string',
        authorization: {
          from: 'string',
          to: 'string',
          value: 'string',
          validAfter: 'string',
          validBefore: 'string',
          nonce: 'string'
        }
      },
      extensions: {}
    },
    paymentRequirements: {
      scheme: 'string',
      network: 'string',
      amount: 'string',
      asset: 'string',
      payTo: 'string',
      maxTimeoutSeconds: 0,
      extra: {}
    }
  })
})
```
