NoHoldPay

Quick Start

From a blank account to a confirmed test payment in about 10 minutes. No real funds needed.

This guide walks you through the actual dashboard flow merchants follow today. Test mode is fully sandboxed: a pk_test_* key cannot touch mainnet. New accounts get $100 test credit and $50 live credit automatically, which covers your first integration without any top-up.

1. Create your account

Open /auth/login and switch to the Create account tab.

Enter your work email and a password (minimum 12 characters). Click Create account. Or click Continue with Google to sign up via Google OAuth.

For email/password signup, check your inbox for the verification email and click the link. Email verification is required before you can sign in with email and password at all. Google OAuth signups are verified automatically.

Sign in from /auth/login. The dashboard lands you on /dashboard in live mode by default.

2. Switch to test mode

Toggle the mode switch at the bottom of the sidebar to Test (on mobile, open the navigation menu - the switch is at the bottom of the drawer). Live and test modes are isolated: a pk_test_* key cannot create live payments, and vice versa. The toggle is saved to your account and re-applied on every sign-in, from any browser or device.

3. Add a wallet

API keys cannot be created until at least one wallet exists in the current mode. The API key creation page otherwise shows an empty state: "No test wallets yet" when your wallets are all in live mode, or "Add a wallet before creating an API key" when you have no wallets at all.

Not sure which wallet to use for each chain? See Choosing your wallets.

Go to Wallets in the sidebar.
Click Add Wallet in the top right. An Add Wallet panel opens inline.

Pick a testnet chain. Two simple options:

  • ETH_SEPOLIA: paste a 0x... receive address from MetaMask connected to Sepolia. The mode picker defaults to the per-payment CREATE2 forwarder (badged Recommended). Your pasted address becomes the sweep treasury, and a short one-time on-chain onboarding (attest and register treasury signatures) runs after the wallet is added.
  • BTC_TESTNET: paste a vpub5... BIP-84 account key from Sparrow on testnet4.

The form validates the address inline. EVM pastes show one of: EIP-55 verified, Canonicalized, EIP-55 applied, Duplicate of #N, or Checksum mismatch. UTXO pastes are rejected on wrong-variant keys.

Click Add Wallet to save. The wallet appears in the list immediately.

4. Create a test API key

Go to API Keys in the sidebar.

Click Create API key. The form heading reads Create API key (the key's mode follows the dashboard toggle).

Fill in a Label (shown in logs, e.g. my-first-test). The form auto-selects every chain and token your wallets cover. Deselect what you do not want this key to accept.

Optional: scroll to the Webhook endpoint card and paste an HTTPS URL plus the events you want to receive. webhook.site works for quick testing. You can also add a webhook later from the key's expand panel on the API Keys list.

Click Create key. The success page shows your API key (and Webhook secret if you added one). Both are shown only once. Copy them and click I've saved these.

The key starts with pk_test_. Store it server-side, never in browser code.

5. Create a test payment

Call POST /api/v1/payments with your test API key:

POST /api/v1/payments
Authorization: Bearer pk_test_...
Content-Type: application/json

{
  "fiat_amount": "10.00",
  "fiat_currency": "USD",
  "accepted_chains": ["ETH_SEPOLIA"]
}

Required fields:

FieldTypeNotes
fiat_amountstringDecimal string, e.g. "10.00". Never a number (preserves precision).
fiat_currencystringISO 4217 code, e.g. "USD", "EUR".

Optional fields: accepted_chains (array), accepted_coins (array), accepted_tokens (array of CHAIN:SYMBOL), restrict_to_accepted (boolean, default false), order_id, description (200 char max), customer_email, success_url, cancel_url.

accepted_chains / accepted_coins / accepted_tokens set the checkout default coin (which coin the checkout opens on). By default they are not a restriction: the customer can switch to any coin your API key accepts. Omit them and the checkout shows the full picker. To limit which coins you receive, set the allowlist on the API key.

Set restrict_to_accepted: true to turn the accepted set into a hard restriction for that one payment: the checkout then offers only the coins/chains in the set, and select-chain rejects anything outside it with 422 COIN_NOT_IN_ACCEPTED_SET. restrict_to_accepted: true with no accepted set returns 422 RESTRICT_TO_ACCEPTED_REQUIRES_ALLOWLIST. Use it to accept a specific coin group (for example stablecoins only) with a single API key instead of one key per group.

Response (201 Created):

{
  "data": {
    "id": "018f7a3e-12b4-4a8d-bc5e-9f234567890a",
    "status": "pending",
    "fiat_amount": "10.00",
    "fiat_currency": "USD",
    "checkout_url": "https://checkout.noholdpay.com/018f7a3e-12b4-4a8d-bc5e-9f234567890a",
    "expires_at": "2026-05-14T12:20:00Z",
    "created_at": "2026-05-14T12:00:00Z",
    "chain_options": [
      {
        "chain_id": "ETH_SEPOLIA",
        "coin_symbol": "ETH",
        "crypto_amount": "3456789012345678",
        "display_amount": "0.003456",
        "address": "",
        "payment_uri": "",
        "exchange_rate": "2892.45",
        "exchange_rate_locked_at": "2026-05-14T12:00:00Z"
      }
    ]
  }
}

address and payment_uri are empty at creation. The deposit address is derived lazily when the customer picks a chain on the checkout page. Server integrations that need the address up-front can call POST /api/v1/checkout/{id}/select-chain and then GET /api/v1/checkout/{id}.

FieldNotes
idUUIDv4 of the payment.
statuspending at creation. Transitions to awaiting_payment once the customer picks a chain on the checkout.
checkout_urlHand this to the customer. They open it, confirm or switch the coin, and pay.
chain_optionsOne entry per chain+coin option the payment can be paid in: every pair your API key accepts, or only the accepted set when restrict_to_accepted is true. crypto_amount is in the coin's smallest unit (wei, satoshi, lamport). display_amount is the human-readable version.
expires_atPayment window deadline. See Payment Lifecycle.

Open checkout_url in a browser to see the hosted checkout.

6. Pay the test invoice

Pay with testnet funds:

The checkout updates in real time as the transaction is detected and confirmed.

7. Verify the webhook

If you added a webhook in step 4, your endpoint receives a signed payment.confirmed event after the required confirmations.

Go to Webhooks in the sidebar. The page shows every delivery attempt with status code, latency, and event name.

Click a delivery row to open the full request and response body in a side drawer.

A failing endpoint retries on the standard schedule (10s, 30s, 2m, 10m, 30m, 2h, 8h, 24h with +/-25% jitter - 9 total attempts over about 35 hours). Use Send test on the endpoint card to fire a synthetic event.

See Webhooks for signature verification.

Webhook URLs are managed on the API Keys page, not the Webhooks page. The Webhooks page is a read-only delivery inspector. To add, edit, or remove a webhook, expand the relevant API key on /api-keys.

Test vs live

TestLive
API key prefixpk_test_pk_live_
ChainsTestnets onlyMainnets only
Real fundsNoYes
Prepaid CreditRequired (separate test reserve)Required (separate live reserve)
Signup grant$100 test credit$50 live credit

Both modes deduct platform fees from a Prepaid Credit reserve. The reserves are separate. New accounts get both grants automatically at signup, so no top-up is needed to start integrating.

Switch to live by toggling the mode switch at the bottom of the sidebar, creating a live wallet, and issuing a pk_live_* key. The API calls are identical.

Next steps

On this page