Standalone Charges

Create billable charges for your customers with a single HTTP call. You always send the final amount you want to bill – Meshpay does not calculate prices.

Standalone charges don't require a billing flow, making them perfect for most use cases.


High-Level Flow

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│                 │     │                 │     │                 │     │                 │
│  Create Charge  │ ──▶ │ x402_requirements│ ──▶ │   User Pays     │ ──▶ │   Confirmed     │
│    (Button 1)   │     │   (Response)     │     │   (Button 2)    │     │   (Webhook)     │
│                 │     │                 │     │                 │     │                 │
└─────────────────┘     └─────────────────┘     └─────────────────┘     └─────────────────┘
  1. Create Charge - Your backend calls POST /v1/charges to create a billable charge
  2. x402 Requirements - Meshpay returns formatted payment requirements with network, asset, and address
  3. User Pays - The buyer/agent executes payment on-chain using the x402 requirements
  4. Confirmed - Facilitator webhook confirms the payment and transaction status updates to succeeded

Endpoint

POST
/v1/charges

Authorization: Bearer <MESHPAY_API_KEY>

Content-Type: application/json


Request Body

FieldTypeRequiredDescriptionExample
amountnumber
Required
Final amount to charge (e.g. 100 = $100.00)100
currencystring
Required
ISO currency code (3 characters)USD
customer_refstring | nullOptionalYour internal customer ID or referenceuser_123
resource_refstring | nullOptionalResource being purchased (for verification matching)article:premium-guide-42
referencestring | nullOptionalOptional reference for this charge (order ID, job ID, etc.)order_456
metadataobject | nullOptionalFree-form JSON object stored with the transaction{"order_id": "456", "tier": "pro"}
receiver_config_idstring | nullOptionalID of payment receiver config to use (see Receiver Configs)config_abc123
networkstring | nullOptionalBlockchain network (e.g., 'solana-mainnet', 'base-mainnet')solana-mainnet
assetstring | nullOptionalAsset/token (e.g., 'USDC', 'USDT')USDC
pay_to_addressstring | nullOptionalWallet address to receive payment (required if network/asset provided)0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
escrow_addressstring | nullOptionalMeshpay settlement wallet (required if collection_mode is 'escrow')0xMeshpaySettlement...
facilitatorstring | nullOptionalFacilitator endpoint URL for payment confirmationhttps://facilitator.example.com/webhook
max_timeout_secondsnumber | nullOptionalMaximum timeout for payment in seconds (default: 60)60
collection_modestring | nullOptionalCollection mode: 'direct' (payments go to pay_to_address) or 'escrow' (payments go to escrow_address)direct

🔄 Detailed Step-by-Step Flow

What happens behind the scenes when a payment is made.


① Developer Creates a Charge

When a user/agent accesses a paid endpoint, the developer's backend calls Meshpay API to create a charge with the amount and currency.

POST /v1/charges
{
  "amount": 100,
  "currency": "USD",
  "customer_ref": "user_123",
  "resource_ref": "api:premium-endpoint",
  "reference": "api_request_456"
}

② Meshpay Resolves x402 Configuration

Meshpay resolves the payment configuration using the fallback chain:

  1. Charge request x402 fields
  2. Receiver config ID (if provided)
  3. Default receiver config
  4. Connected Solana wallet
  5. Error (no config found)

③ Returns Formatted x402 Requirements

Meshpay creates a pending transaction and returns formatted x402 payment requirements.

What you provided: network, asset, pay_to_address

What Meshpay added: x402 protocol structure, transaction tracking

{
  "id": "txn_abc123",
  "status": "pending",
  "x402_requirements": {
    "rail_config": {
      "scheme": "exact",
      "network": "solana-mainnet",
      "asset": "USDC",
      "pay_to_address": "0x742d...",
      "max_timeout_seconds": 60
    },
    "amount": "100.00",
    "currency": "USD",
    "external_ref": "billing:txn_abc123"
  }
}

④ Buyer/Agent Executes Payment

The buyer's wallet (or AI agent's automated wallet) reads the x402 requirements and executes an on-chain transaction:

  • Sends 100 USDC
  • To the pay_to_address
  • On solana-mainnet
  • With reference to billing:txn_abc123

⑤ Transaction Confirmed

The blockchain network confirms the transaction and generates a unique transaction hash (tx_hash) that serves as immutable proof of payment.

// Transaction Hash
"0x7f3a8b2c4d5e6f..."

⑥ Facilitator Sends Confirmation

The facilitator monitors the blockchain and detects the payment. It sends a webhook to Meshpay with the transaction ID and tx_hash.

POST /v1/billing/webhook/facilitator
{
  "transaction_id": "txn_abc123",
  "tx_hash": "0x7f3a8b2c4d5e6f...",
  "status": "succeeded"
}

⑦ Transaction Status Updated

Meshpay updates the transaction status from pending to succeeded, stores the tx_hash, and records the confirmation timestamp.

{
  "id": "txn_abc123",
  "status": "succeeded",  // ✅ Updated!
  "tx_hash": "0x7f3a8b2c4d5e6f...",
  "confirmed_at": "2025-01-15T10:35:00Z"
}

📊 What Data Flows Where

Understanding the data exchange at each step:

| Step | From → To | Data | |------|-----------|------| | 1 | Developer → Meshpay | Amount, currency, customer reference, optional metadata, and payment receiver config | | 2 | Meshpay → Developer | Transaction ID, pending status, and x402_requirements (network, asset, pay_to_address, amount) | | 3 | Developer → Buyer/Agent | The x402_requirements object that contains all payment instructions | | 4 | Buyer → Blockchain | Signed transaction with amount, destination address, and memo/reference | | 5 | Blockchain → Facilitator | Transaction confirmation event with tx_hash | | 6 | Facilitator → Meshpay | Webhook with transaction_id, tx_hash, and status (succeeded/failed) |


Payment Configuration Options

You have three ways to provide x402 payment configuration:

Option A: Receiver Configs (Recommended)

Create reusable payment configs that can be referenced across charges. Best for production use.

Create a receiver config:

cURL
curl -X POST "https://api.orvion.sh/v1/billing/receiver-configs" \
-H "Authorization: Bearer $MESHPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Main Wallet",
"network": "solana-mainnet",
"asset": "USDC",
"pay_to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"is_default": true
}'

Then create charges:

cURL
# Reference specific config
curl -X POST "https://api.orvion.sh/v1/charges" \
-H "Authorization: Bearer $MESHPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 100,
"currency": "USD",
"receiver_config_id": "config_abc123"
}'
# Or use default config (no receiver_config_id needed)
curl -X POST "https://api.orvion.sh/v1/charges" \
-H "Authorization: Bearer $MESHPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 100,
"currency": "USD"
}'

See Receiver Configs for complete documentation.

Option B: Direct x402 Fields

Provide payment details directly in each charge request:

cURL
curl -X POST "https://api.orvion.sh/v1/charges" \
-H "Authorization: Bearer $MESHPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 100,
"currency": "USD",
"network": "solana-mainnet",
"asset": "USDC",
"pay_to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}'

Option C: Connected Solana Wallet (Automatic)

If you have a Solana wallet connected in the dashboard, it's used automatically:

  • Network: solana-mainnet
  • Asset: USDC
  • Address: Your primary connected wallet
cURL
curl -X POST "https://api.orvion.sh/v1/charges" \
-H "Authorization: Bearer $MESHPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 100,
"currency": "USD"
}'

Note: Only works if you have a connected Solana wallet and no default receiver config.


x402 Configuration Resolution

When creating a charge, Meshpay resolves x402 payment configuration using this priority:

  1. Charge request x402 fields - If network, asset, and pay_to_address are provided directly
  2. Receiver config - If receiver_config_id is provided
  3. Default receiver config - If a config with is_default: true exists
  4. Connected Solana wallet - If you have a primary Solana wallet connected
  5. Error - If no configuration can be resolved

See Receiver Configs for details.


Collection Modes

Direct Mode

  • Description: Payments go directly to pay_to_address
  • Use Case: Simple flows, fast settlement
  • Requirement: pay_to_address must be provided

Escrow Mode

  • Description: Payments go to escrow_address first, then distributed according to payout splits
  • Use Case: Splits, complex payout logic
  • Requirement: escrow_address is required

Error Codes

400 Bad Request

  • Invalid request (amount ≤ 0, missing fields, invalid JSON)
  • Invalid currency format
  • x402 configuration required (no receiver config, default config, or connected wallet found)
  • escrow_address required when collection_mode is escrow

401 Unauthorized

  • Invalid or missing API key

404 Not Found

  • Receiver config not found (if receiver_config_id provided)

500 Internal Server Error

  • Internal error (retry may succeed)

Transaction Status Lifecycle

| Status | Description | |--------|-------------| | pending | Charge created, x402 requirements returned, awaiting payment | | succeeded | Payment confirmed via facilitator webhook | | failed | Payment failed (network error, insufficient funds, etc.) |

Charges start as pending. After the buyer pays on-chain, the facilitator webhook confirms the payment and status updates to succeeded.


After Payment: Verification

Once a charge is paid, use the Payment Verification endpoint to confirm payment before showing content:

POST /v1/charges/verify
{
  "transaction_id": "txn_abc123",
  "customer_ref": "user_123",
  "resource_ref": "api:premium-endpoint"
}

Returns verified: true if payment is confirmed and references match.

See the Seller Integration Guide for complete examples.


Key Takeaways

  • One-time setup: Create a receiver config with your wallet address and mark it as default
  • Simple API: Just POST to /v1/charges with amount and currency
  • x402 Protocol: Meshpay generates standardized payment requirements that any compatible wallet can execute
  • Async confirmation: Charges start as "pending" and update to "succeeded" via webhook
  • Flexible configs: Use default config, specific config ID, or provide payment details directly per charge
  • AI Agent ready: The x402 requirements format allows autonomous AI agents to make payments programmatically

Best Practices

  1. Use Receiver Configs - Create reusable payment configs instead of hardcoding per charge
  2. Set a Default Config - Mark your primary config as is_default: true for automatic use
  3. Always include customer_ref - Helps track charges per customer and enables verification matching
  4. Always include resource_ref - Identifies what's being purchased, prevents payment reuse
  5. Use reference for traceability - Link charges to your internal order IDs, job IDs, etc.
  6. Store context in metadata - Include any additional data you might need later
  7. Handle pending status - Charges start as pending, wait for webhook confirmation
  8. Verify before showing content - Always call /v1/charges/verify before granting access
  9. Use x402_requirements - Pass the returned requirements to the buyer's wallet
  10. Test with small amounts first - Verify your integration before processing real charges

Related Documentation