Skip to main content

HandCash Payment Requests API - Corrected Documentation

Based on real-world implementation and testing. This documentation corrects errors found in the official docs.

API Base URL

https://cloud.handcash.io/v3/paymentRequests

Authentication Headers

IMPORTANT: Headers are case-sensitive. Use lowercase:
app-id: your-target-app-id
app-secret: your-target-app-secret

Create Payment Request

Method: POST
URL: https://cloud.handcash.io/v3/paymentRequests

Required Fields

FieldTypeDescriptionNotes
productObjectProduct informationRequired
product.nameStringProduct nameRequired
product.descriptionStringProduct descriptionOptional but recommended
product.imageUrlStringProduct image URLOptional but recommended for UX
instrumentCurrencyCodeStringBlockchain currencyMust be “BSV” or “MNEE” (NOT USD)
currencyStringDisplay/denomination currencyUse “USD”, “EUR”, “GBP”, etc.
receiversArrayPayment recipientsRequired
receivers[].destinationStringHandCash handle or wallet IDRequired
receivers[].sendAmountNumberAmount to sendRequired
expirationTypeStringExpiration typeRequired: “limit”, “never”, or “onPaymentCompleted”

Common Mistakes in Official Docs

  1. WRONG: denominationCurrencyCode CORRECT: currency
  2. WRONG: instrumentCurrencyCode: "USD" CORRECT: instrumentCurrencyCode: "BSV" or "MNEE" only
  3. WRONG: receivers[].currencyCode field CORRECT: Do NOT include currencyCode in receivers array

Working Example

curl --request POST \
  --url https://cloud.handcash.io/v3/paymentRequests \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'app-id: your-app-id' \
  --header 'app-secret: your-app-secret' \
  --data '{
    "product": {
      "name": "Premium Subscription",
      "description": "Monthly premium access",
      "imageUrl": "https://example.com/product.jpg"
    },
    "instrumentCurrencyCode": "BSV",
    "currency": "USD",
    "receivers": [
      {
        "destination": "yourhandle",
        "sendAmount": 0.01
      }
    ],
    "expirationType": "never",
    "redirectUrl": "https://yourapp.com/success"
  }'

Response Format

{
  "id": "507f1f77bcf86cd799439011",
  "paymentRequestUrl": "https://pay.handcash.io/507f1f77bcf86cd799439011",
  "paymentRequestQrCodeUrl": "https://pay.handcash.io/api/paymentPreview/qr/507f1f77bcf86cd799439011",
  "isEnabled": true,
  "product": {
    "name": "Premium Subscription",
    "description": "Monthly premium access",
    "imageUrl": "https://example.com/product.jpg"
  },
  "paymentAmount": {
    "amount": 0.01,
    "currencyCode": "USD"
  },
  "receivers": [
    {
      "destination": "yourhandle",
      "sendAmount": 0.01
    }
  ],
  "createdAt": 1650558683,
  "expirationType": "never"
}

List Payment Requests

Method: GET
URL: https://cloud.handcash.io/v3/paymentRequests

Query Parameters (Optional)

  • status - Filter by status (e.g., “pending”, “completed”)

Example

curl --request GET \
  --url https://cloud.handcash.io/v3/paymentRequests \
  --header 'Accept: application/json' \
  --header 'app-id: your-app-id' \
  --header 'app-secret: your-app-secret'

Response

Returns array of payment request objects in items array.

Get Single Payment Request

Method: GET
URL: https://cloud.handcash.io/v3/paymentRequests/{id}
IMPORTANT: In Next.js 16+, you must await params:
export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  // Use id...
}

Example

curl --request GET \
  --url https://cloud.handcash.io/v3/paymentRequests/507f1f77bcf86cd799439011 \
  --header 'Accept: application/json' \
  --header 'app-id: your-app-id' \
  --header 'app-secret: your-app-secret'

Update Payment Request

Method: PUT
URL: https://cloud.handcash.io/v3/paymentRequests/{id}

Updatable Fields

  • product.name
  • product.description
  • product.imageUrl
  • redirectUrl
  • notifications.webhook.webhookUrl
  • notifications.webhook.customParameters
  • notifications.email
  • expirationInSeconds
  • decreaseRemainingUnits (only for expirationType: "limit")

Example

curl --request PUT \
  --url https://cloud.handcash.io/v3/paymentRequests/507f1f77bcf86cd799439011 \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'app-id: your-app-id' \
  --header 'app-secret: your-app-secret' \
  --data '{
    "product": {
      "name": "Updated Product Name"
    }
  }'

Delete Payment Request

Method: DELETE
URL: https://cloud.handcash.io/v3/paymentRequests/{id}
IMPORTANT: The API returns an empty response body on success. Do NOT attempt to parse as JSON.

Example

curl --request DELETE \
  --url https://cloud.handcash.io/v3/paymentRequests/507f1f77bcf86cd799439011 \
  --header 'app-id: your-app-id' \
  --header 'app-secret: your-app-secret'

Handling Delete Response

const response = await fetch(`https://cloud.handcash.io/v3/paymentRequests/${id}`, {
  method: 'DELETE',
  headers: {
    'app-id': process.env.TARGET_APP_ID!,
    'app-secret': process.env.TARGET_APP_SECRET!,
  },
});

if (!response.ok) {
  const text = await response.text();
  throw new Error(text || 'Failed to delete');
}

// Success - response body is empty
return { success: true };

Common Issues & Solutions

Issue: “instrumentCurrencyCode must be one of [MNEE, BSV]”

Solution: Never use “USD” for instrumentCurrencyCode. Only use “BSV” or “MNEE”.

Issue: “denominationCurrencyCode is not allowed”

Solution: The field is called currency, not denominationCurrencyCode.

Issue: “receivers[0].currencyCode is not allowed”

Solution: Do not include currencyCode in the receivers array.

Issue: “Unexpected end of JSON input” on DELETE

Solution: The DELETE endpoint returns an empty body. Handle this by reading as text first:
const text = await response.text();
const data = text ? JSON.parse(text) : { success: true };

Issue: “id must be a valid MongoDB ObjectId”

Solution: In Next.js 16+, dynamic route params must be awaited:
const { id } = await params; // NOT: const { id } = params;

Field Mapping Guide

Official DocsActually WorksType
denominationCurrencyCodecurrencyString
instrumentCurrencyCode: "USD"instrumentCurrencyCode: "BSV" or "MNEE"String
App-Id (uppercase)app-id (lowercase)Header
App-Secret (uppercase)app-secret (lowercase)Header
receivers[].currencyCode❌ Do not includeN/A

Complete Working Implementation

See the payment-requests-package folder for a complete, tested implementation including:
  • app/api/payment-requests/route.ts - POST and GET handlers
  • app/api/payment-requests/[id]/route.ts - GET, PUT, DELETE handlers
  • components/payment-request-management.tsx - Full UI component
All code has been tested and confirmed working with the HandCash API.