Skip to main content
POST
/
v1
/
settle
Settle x402 payment
curl --request POST \
  --url https://api.mrdn.finance/v1/settle \
  --header 'Authorization: <api-key>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "paymentPayload": {
    "x402Version": 1,
    "scheme": "exact",
    "network": "base-sepolia",
    "payload": {
      "signature": "<string>",
      "authorization": {
        "from": "<string>",
        "to": "<string>",
        "value": "<string>",
        "validAfter": "<string>",
        "validBefore": "<string>",
        "nonce": "<string>"
      }
    }
  },
  "paymentRequirements": {
    "scheme": "exact",
    "network": "<string>",
    "maxAmountRequired": "<string>",
    "resource": "<string>",
    "description": "<string>",
    "mimeType": "<string>",
    "payTo": "<string>",
    "maxTimeoutSeconds": 123,
    "asset": "<string>",
    "extra": {}
  }
}
'
import requests

url = "https://api.mrdn.finance/v1/settle"

payload = {
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "base-sepolia",
"payload": {
"signature": "<string>",
"authorization": {
"from": "<string>",
"to": "<string>",
"value": "<string>",
"validAfter": "<string>",
"validBefore": "<string>",
"nonce": "<string>"
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "<string>",
"maxAmountRequired": "<string>",
"resource": "<string>",
"description": "<string>",
"mimeType": "<string>",
"payTo": "<string>",
"maxTimeoutSeconds": 123,
"asset": "<string>",
"extra": {}
}
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
paymentPayload: {
x402Version: 1,
scheme: 'exact',
network: 'base-sepolia',
payload: {
signature: '<string>',
authorization: {
from: '<string>',
to: '<string>',
value: '<string>',
validAfter: '<string>',
validBefore: '<string>',
nonce: '<string>'
}
}
},
paymentRequirements: {
scheme: 'exact',
network: '<string>',
maxAmountRequired: '<string>',
resource: '<string>',
description: '<string>',
mimeType: '<string>',
payTo: '<string>',
maxTimeoutSeconds: 123,
asset: '<string>',
extra: {}
}
})
};

fetch('https://api.mrdn.finance/v1/settle', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.mrdn.finance/v1/settle",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'paymentPayload' => [
'x402Version' => 1,
'scheme' => 'exact',
'network' => 'base-sepolia',
'payload' => [
'signature' => '<string>',
'authorization' => [
'from' => '<string>',
'to' => '<string>',
'value' => '<string>',
'validAfter' => '<string>',
'validBefore' => '<string>',
'nonce' => '<string>'
]
]
],
'paymentRequirements' => [
'scheme' => 'exact',
'network' => '<string>',
'maxAmountRequired' => '<string>',
'resource' => '<string>',
'description' => '<string>',
'mimeType' => '<string>',
'payTo' => '<string>',
'maxTimeoutSeconds' => 123,
'asset' => '<string>',
'extra' => [

]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "https://api.mrdn.finance/v1/settle"

payload := strings.NewReader("{\n \"paymentPayload\": {\n \"x402Version\": 1,\n \"scheme\": \"exact\",\n \"network\": \"base-sepolia\",\n \"payload\": {\n \"signature\": \"<string>\",\n \"authorization\": {\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"value\": \"<string>\",\n \"validAfter\": \"<string>\",\n \"validBefore\": \"<string>\",\n \"nonce\": \"<string>\"\n }\n }\n },\n \"paymentRequirements\": {\n \"scheme\": \"exact\",\n \"network\": \"<string>\",\n \"maxAmountRequired\": \"<string>\",\n \"resource\": \"<string>\",\n \"description\": \"<string>\",\n \"mimeType\": \"<string>\",\n \"payTo\": \"<string>\",\n \"maxTimeoutSeconds\": 123,\n \"asset\": \"<string>\",\n \"extra\": {}\n }\n}")

req, _ := http.NewRequest("POST", url, payload)

req.Header.Add("Authorization", "<api-key>")
req.Header.Add("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://api.mrdn.finance/v1/settle")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"paymentPayload\": {\n \"x402Version\": 1,\n \"scheme\": \"exact\",\n \"network\": \"base-sepolia\",\n \"payload\": {\n \"signature\": \"<string>\",\n \"authorization\": {\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"value\": \"<string>\",\n \"validAfter\": \"<string>\",\n \"validBefore\": \"<string>\",\n \"nonce\": \"<string>\"\n }\n }\n },\n \"paymentRequirements\": {\n \"scheme\": \"exact\",\n \"network\": \"<string>\",\n \"maxAmountRequired\": \"<string>\",\n \"resource\": \"<string>\",\n \"description\": \"<string>\",\n \"mimeType\": \"<string>\",\n \"payTo\": \"<string>\",\n \"maxTimeoutSeconds\": 123,\n \"asset\": \"<string>\",\n \"extra\": {}\n }\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.mrdn.finance/v1/settle")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"paymentPayload\": {\n \"x402Version\": 1,\n \"scheme\": \"exact\",\n \"network\": \"base-sepolia\",\n \"payload\": {\n \"signature\": \"<string>\",\n \"authorization\": {\n \"from\": \"<string>\",\n \"to\": \"<string>\",\n \"value\": \"<string>\",\n \"validAfter\": \"<string>\",\n \"validBefore\": \"<string>\",\n \"nonce\": \"<string>\"\n }\n }\n },\n \"paymentRequirements\": {\n \"scheme\": \"exact\",\n \"network\": \"<string>\",\n \"maxAmountRequired\": \"<string>\",\n \"resource\": \"<string>\",\n \"description\": \"<string>\",\n \"mimeType\": \"<string>\",\n \"payTo\": \"<string>\",\n \"maxTimeoutSeconds\": 123,\n \"asset\": \"<string>\",\n \"extra\": {}\n }\n}"

response = http.request(request)
puts response.read_body
{
  "success": true,
  "transaction": "<string>",
  "network": "<string>",
  "payer": "<string>",
  "errorReason": "<string>",
  "authContext": {}
}
Settle an x402 payment on-chain after verification. This endpoint executes the actual blockchain transaction to transfer funds according to the x402 payment authorization.
When paymentRequirements.extra.name === "GatewayWalletBatched", this endpoint forwards the request to Circle’s Gateway API instead of settling on-chain. See Circle Gateway (Batched) for the batched payment type and Payment Types for an overview of all routing paths.

Authentication

Requires API key authentication with organization context.

Recipient

For standard merchant integrations, the wallet that receives settled funds is configured in your organization settings and tied to your API key. Keep paymentRequirements.payTo set to the Meridian facilitator contract for the network; do not use payTo for your merchant wallet. Platform and marketplace integrations can route the end payout with paymentRequirements.extra.creditedRecipient. See Marketplace Fees for that flow.

Standard Request Body

{
  "paymentPayload": {
    "x402Version": 1,
    "scheme": "exact",
    "network": "base-sepolia",
    "payload": {
      "signature": "0x...",
      "authorization": {
        "from": "0x742d35Cc6634C0532925a3b8D0c4E5e6C2aE7A3e",
        "to": "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A",
        "value": "1000000",
        "validAfter": "1234567890",
        "validBefore": "1234567899",
        "nonce": "0x..."
      }
    }
  },
  "paymentRequirements": {
    "scheme": "exact",
    "network": "base-sepolia",
    "maxAmountRequired": "1000000",
    "resource": "https://example.com/resource",
    "description": "Payment for resource access",
    "mimeType": "application/json",
    "payTo": "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A",
    "maxTimeoutSeconds": 3600,
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
  }
}
For EIP-3009 payloads, both authorization.to and payTo must be set to the facilitator contract address for the network. For Permit2 payloads, keep payTo and witness.to bound to the facilitator.

Cross-Chain EIP-3009 Request Body

Cross-chain USDC settlement uses the same POST /v1/settle endpoint and the same EIP-3009 payload shape. The payment payload is signed on the source chain where the buyer has USDC. Set paymentRequirements.network, asset, and payTo for that source chain, then set paymentRequirements.extra.destinationChainId to the destination chain where Meridian should settle after Across fills the bridge. For example, a buyer paying from Ink USDC to a seller receiving on Base uses network: "ink" and destinationChainId: 8453:
{
  "paymentPayload": {
    "x402Version": 1,
    "scheme": "exact",
    "network": "ink",
    "payload": {
      "signature": "0x...",
      "authorization": {
        "from": "0x2222222222222222222222222222222222222222",
        "to": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6",
        "value": "1000000",
        "validAfter": "1234567890",
        "validBefore": "1234568190",
        "nonce": "0x..."
      }
    }
  },
  "paymentRequirements": {
    "scheme": "exact",
    "network": "ink",
    "maxAmountRequired": "1000000",
    "resource": "https://seller.example/api/tool",
    "description": "Payment for resource access",
    "mimeType": "application/json",
    "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6",
    "maxTimeoutSeconds": 300,
    "asset": "0x2D270e6886d130D724215A266106e6832161EAEd",
    "extra": {
      "name": "USD Coin",
      "version": "2",
      "creditedRecipient": "0x1111111111111111111111111111111111111111",
      "destinationChainId": 8453
    }
  }
}
The settlement response transaction hash is the source-chain transaction that started the Across bridge. The seller receives the destination-chain output after Across fill and Meridian destination-side settlement.
A seller that accepts payments from multiple source chains should expose one paymentRequirements object per route in the x402 accepts array. A Base-source requirement cannot be paid by a buyer who only has USDC on Ink. Return an Ink-source requirement with extra.destinationChainId: 8453 for that route.
Cross-chain EIP-3009 currently uses Across exact-input routing. If the seller needs to receive an exact destination-chain amount, quote the route and set maxAmountRequired to include bridge fees.

Permit2 Request Body

There is no separate POST /v1/permit2 endpoint. Permit2 payments are settled through POST /v1/settle with the Permit2 payload shape below.
{
  "paymentPayload": {
    "x402Version": 1,
    "scheme": "exact",
    "network": "exampleNetwork",
    "payload": {
      "signature": "0x...",
      "owner": "0x...",
      "permit": {
        "permitted": {
          "token": "0x...",
          "amount": "12345"
        },
        "nonce": "123",
        "deadline": "12345678"
      },
      "witness": {
        "to": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6",
        "validAfter": "0"
      }
    }
  },
  "paymentRequirements": {
    "scheme": "exact",
    "network": "exampleNetwork",
    "maxAmountRequired": "12345",
    "resource": "https://example.com/resource",
    "description": "Payment for resource access",
    "mimeType": "application/json",
    "payTo": "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6",
    "maxTimeoutSeconds": 3600,
    "asset": "0x...."
  }
}
Permit2 settlement is used for megaeth, bsc, bot-chain, bot-chain-testnet, and tempo. If the buyer has not already approved Permit2 for the token, include the optional paymentPayload.payload.permit2612 object for tokens that support ERC-2612:
{
  "permit2612": {
    "value": "1000000",
    "deadline": "1893456000",
    "r": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "s": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "v": 27
  }
}
On chains where the payment token does not expose EIP-3009, use Permit2 through x402ExactPermit2Proxy 0x402085c248EeA27D92E8b30b2C58ed07f9E20001. This is the canonical exact Permit2 proxy from the official x402 repository, deployed at the same address on supported EVM chains. This includes MegaETH, BSC, and BOT chain testnet and mainnet. New integrations should keep payTo pointed at the facilitator, set paymentRequirements.asset to the ERC-20 token address, approve Permit2 0x000000000022D473030F116dDEE9F6B43aC78BA3, and sign the Permit2 witness with the proxy as spender. Your backend should still call Meridian POST /v1/settle.
NetworkFacilitator Contract
arc-testnet0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A
bot-chain-testnet0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A
bot-chain0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
base0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
bsc0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
base-sepolia0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A
avalanche0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
optimism0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
arbitrum0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
optimism-sepolia0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A
polygon0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
unichain0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
ink0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
worldchain0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
sei0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
hyperevm0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
megaeth0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
tempo0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
monad0x8E7769D440b3460b92159Dd9C6D17302b036e2d6
robinhood0x8E7769D440b3460b92159Dd9C6D17302b036e2d6

Response

Success Response

{
  "success": true,
  "transaction": "0x1234567890abcdef...",
  "network": "base-sepolia"
}

Error Response

{
  "success": false,
  "errorReason": "insufficient_funds",
  "transaction": "",
  "network": "base-sepolia"
}

Error Codes

  • invalid_payload - Payment payload format is invalid
  • invalid_payment_requirements - Payment requirements format is invalid
  • insufficient_funds - Insufficient balance for settlement
  • unexpected_settle_error - Unhandled exception during settlement
  • invalid_transaction_state - On-chain transaction failed

Authorizations

Authorization
string
header
required

Body

application/json
paymentPayload
object
required

Payload accepted by POST /v1/settle. Permit2 is currently settle-only; the deprecated POST /v1/verify endpoint still uses the legacy PaymentPayload schema.

paymentRequirements
object
required

Response

Payment settlement result

success
boolean
required
transaction
string
required

Transaction hash on success. Empty string on failure.

network
string
required
payer
string
errorReason
string
authContext
object