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. Your initial business receives configured test welcome credit at signup, when enabled. Live usage requires funding. An eligible account can receive the current first-top-up promotional match after its first qualifying confirmed live top-up and email verification. Check the Prepaid Credit page for the match percentage, minimum deposit, cap, and offer currency.

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 and receive a first-top-up promotional match. Google and GitHub OAuth signups already have a verified email, but still need a qualifying live top-up for the promotion.

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

If sign-up is refused

New registrations by email and password, Google, or GitHub are checked before the account is created, and a refused registration creates nothing. These checks never apply to signing in to an existing account or to linking Google or GitHub to an account you already have. The sign-up form shows what to do next.

New registrations also check the mail servers published for the email domain. A temporary DNS problem does not block registration. Registrations from hosting, datacenter, VPN or Tor networks have stricter limits than home and mobile networks, so if you are refused while connected through one, try again from your usual connection. The codes are:

CodeHTTPWhen
DISPOSABLE_EMAIL_NOT_ALLOWED422The address belongs to a temporary or disposable email provider. Use a permanent address.
EMAIL_DOMAIN_NOT_ALLOWED422The email domain, or a parent domain, is not accepted for new registrations. Use a different address.
EMAIL_SUBDOMAIN_NOT_ALLOWED422The address is at an email subdomain, such as you@mail.example.com. Use an address at the main domain, such as you@example.com.
EMAIL_DOMAIN_NO_MAIL_SERVER422This email domain cannot receive email. Check the address or use a different email address.
EMAIL_DOMAIN_RATE_LIMITED429Too many new accounts use this email domain today. Please try again later or contact support. The Retry-After header gives the seconds to wait.
SIGNUP_RATE_LIMITED429Too many new accounts were registered from your network recently. The Retry-After response header gives the seconds to wait.
SIGNUP_IP_BLOCKED403New registrations are not accepted from your network.
SIGNUP_SOURCE_UNVERIFIED403Your network address could not be verified. Try again later.
REGISTRATION_UNAVAILABLE403Registration could not be completed. Contact support.

The daily limit per email domain counts only accounts whose address is confirmed: a Google or GitHub registration counts at once, and an email and password registration counts when you verify the address. If the limit fills up before you verify, verification is refused with EMAIL_DOMAIN_RATE_LIMITED as well. Your verification link or code is not used up by that refusal, so try again after the Retry-After wait, and request a new code if it has expired by then.

If you think a refusal is a mistake, contact support from the email address you want to register.

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 with a test-mode key. Live keys refuse public request inspection services, so point a live webhook at a receiver you control. See Webhook URL rules. 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)
Introductory creditConfigured test amount at signupFirst qualifying live top-up match after verification, when eligible

Both modes deduct platform fees and platform-charged network costs from their own Prepaid Credit reserve. The initial business receives configured test welcome credit at signup, when enabled. Live credit comes from confirmed funding and any eligible first-top-up match. The promotion is available once across linked businesses, requires verified email, and is not refundable or transferable. Additional businesses start with no signup grant. See Prepaid Credit for offer and refund rules.

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