Developers & partners

Nectar.Pay API

Nectar.Pay is the official directory of merchants in the Honest Money ecosystem. Anyone can show it. Merchants accept payments through the same API.

Merchant directory feed

Free and public. You can see it as a page at /directory.

GET/api/public/v1/directory

Every listed merchant.

GET/api/public/v1/directory?since=2026-10-26T00:00:00Z

Only stores added or changed after that time, plus stores that were closed or unlisted.

Rules of the road
  • No key, no sign-up. Cross-site requests (CORS) are allowed from any site.
  • Use id as your permanent reference. Names and slugs can change.
  • Merchants choose how they appear. city_only stores carry no name, address or contact, and their location is rounded to about 10 km.
  • Only stores that accept at least one coin are listed.
  • Responses are cached for 5 minutes. For daily syncs, save generated_at and pass it as since next time.
  • Remove anything listed in removed from your copy.
  • Nectar.Pay is the source of truth. Please don’t edit stores in your copy; ask merchants to update their info in Nectar.Pay.
Try it
curl https://app.nectar-pay.com/api/public/v1/directory
Example response
{
  "generated_at": "2026-10-27T11:00:00Z",
  "since": null,
  "count": 1,
  "stores": [{
    "id": "dedc6db9-c547-45ed-920a-a5c325d49507",   // permanent, never changes
    "slug": "blakes-bbq",
    "listing": "full",            // "full" | "online" | "city_only"
    "status": "live",
    "name": "Blake's BBQ",
    "category": "Restaurant",
    "description": "Texas BBQ, family-run since 2014.",
    "logo_url": "https://app.nectar-pay.com/api/public/store-logo/…/logo.png",
    "presence": "physical",       // "physical" | "online" | "both"
    "address": { "street": "123 Main St", "city": "Odessa", "region": "TX",
                 "postal_code": "79761", "country": "United States" },
    "location": { "lat": 31.84, "lng": -102.36, "precision": "exact" },
    "phone": "(432) 555-0100",
    "website": "https://example.com",
    "hours": "Mon–Sat 11am–9pm",
    "accepts": ["BTC", "TSD", "TXC", "USDC"],
    "egiving": { "accepted": true, "discount_pct": 10 },
    "updated_at": "2026-10-27T10:57:57Z"
  }],
  "removed": [
    { "id": "…", "status": "closed", "removed_at": "2026-10-27T09:00:00Z" }
  ]
}

Payments API — full reference

For stores and plugins: invoices, payment links, rates, store info and signed webhooks. Step-by-step guides: API keys, wallet setup, address rotation, tap to pay (Tangem).

Developer documentation

A non-custodial gateway. You hold the keys; we derive deposit addresses, watch the chain, and notify your store. This page is the complete public API reference — everything a third-party site needs to integrate without ever talking to us.

1. Create an account & store

Sign up at /auth, create a store, and add at least one chain config.

New to xpubs? Step-by-step wallet setup guide for every supported chain → Wondering whether each invoice gets its own address? Address derivation & rotation policy →

2. Get your credentials

Each store has its own secret API key (sk_live_…), created from Store → API keys. It's shown once — store it securely.

For webhooks, set your endpoint URL and copy the signing secret from Store → Webhooks. The secret is per-store and is used to verify every event we send you (see §6).

3. Base URL & authentication

All API calls go to https://app.nectar-pay.com. Keys are issued and validated on this host only — sending a valid key to nectar-pay.com (the marketing site) returns 401 Invalid API key.

Every request carries the key as a Bearer token. Keys are scoped to a single store: you can only ever see and create that store's invoices.

NECTARPAY_API_URL=https://app.nectar-pay.com

# sanity-check your key before wiring anything else:
curl -s https://app.nectar-pay.com/api/public/v1/me \
  -H "Authorization: Bearer sk_live_..."

A 200 from /api/public/v1/me confirms the key, the store it belongs to, and the webhook URL currently configured. All errors across the API look like { "error": "human readable message" } with an appropriate 4xx/5xx status.

4. Create an invoice

POST https://app.nectar-pay.com/api/public/v1/invoices
Authorization: Bearer sk_live_...
Content-Type: application/json

{
  "chain": "btc",                    // optional — omit to let the customer pick
  "amount": 49.00,                   // required, fiat amount
  "currency": "USD",                 // required, ISO fiat code
  "order_id": "ORDER_1234",          // optional, echoed back + sent in webhooks
  "description": "T-shirt, size M",  // optional
  "redirect_url": "https://store.example.com/thanks", // optional
  "buyer_email": "sam@example.com",  // optional, sends a receipt
  "expires_in_seconds": 900          // optional, 60–86400, default is store TTL
}

Supported chain values:

chainnetwork
btcBitcoin (on-chain)
lightningBitcoin via Lightning
txcTEXITcoin
ethEthereum (ETH + USDC/USDT/PYUSD/DAI)
baseBase (ETH + stablecoins)
tronTron (TRX + USDT)
solSolana (SOL + SPL tokens)
dogeDogecoin
ltcLitecoin
bchBitcoin Cash
dashDash
iskIskander Network
zcuZCU (Zero Chill)

If you omit chain, the invoice is created in a "customer picks the network" state and address/crypto_amount/rate are null until the customer chooses on the hosted checkout page.

Response (201):

{
  "id": "7c9e…-invoice-uuid",
  "address": "bc1q…",              // null if chain omitted
  "crypto_amount": 0.00041712,     // null if chain omitted
  "rate": 117500.00,               // fiat per coin, null if chain omitted
  "fiat_amount": 49.00,
  "currency": "USD",
  "chain": "btc",
  "status": "pending",
  "expires_at": "2026-09-14T10:15:00.000Z",
  "checkout_url": "https://app.nectar-pay.com/i/7c9e…"
}

Send the customer to checkout_url (QR + amount + copyable address) or render your own UI from address and crypto_amount.

5. Check invoice status (polling)

GET https://app.nectar-pay.com/api/public/v1/invoices/{id}
Authorization: Bearer sk_live_...

→ 200 {
  "id": "7c9e…",
  "status": "confirmed",           // see lifecycle below
  "chain": "btc",
  "fiat_amount": 49.00,
  "fiat_currency": "USD",
  "crypto_amount": 0.00041712,
  "rate": 117500.00,
  "address": "bc1q…",
  "order_id": "ORDER_1234",
  "description": "…",
  "redirect_url": "…",
  "buyer_email": "…",
  "expires_at": "…",
  "created_at": "…",
  "checkout_url": "https://app.nectar-pay.com/i/7c9e…"
}

A 404 means the invoice doesn't exist or belongs to a different store's key — we never leak across stores.

Invoice lifecycle:

statusmeaning
pendingCreated; waiting for payment (or for the customer to pick a chain).
underpaidA payment arrived but is short of the requested amount.
confirmedPaid and confirmed on-chain. Treat this as final.
expiredTTL elapsed with no (sufficient) payment.

6. Receive webhooks

We POST a signed JSON event to your configured webhook URL on every status change. Headers:

POST /your/webhook HTTP/1.1
Content-Type: application/json
X-TXCPay-Signature: t=1729000000,v1=<hex-hmac-sha256>
X-TXCPay-Event: invoice.paid
X-TXCPay-Event-Id: 0f4c…-event-uuid
User-Agent: payHME-webhook/1

Events and when they're sent:

EventWhen / what to do
invoice.paidFull payment received (data.status = "confirmed"). Always followed by invoice.confirmed.
invoice.overpaidCustomer paid more than asked (data.status = "overpaid"). Also followed by invoice.confirmed.
invoice.confirmedFINAL — mark the order paid / ship. Sent for every settled invoice (status confirmed or overpaid). Settle on this one event.
invoice.underpaidPayment arrived but short beyond the 1% cushion (data.status = "underpaid"). Don't ship.

A real, complete paid notification — this is the exact JSON body, no other fields are sent:

POST https://your-site.com/your/webhook
Content-Type: application/json
X-TXCPay-Signature: t=1791019331,v1=5d2c0e9f6b1a…(64 hex chars)
X-TXCPay-Event: invoice.confirmed
X-TXCPay-Event-Id: 3b8f2a61-9d4e-4c1a-8f0b-2e7d5c9a1f44
User-Agent: payHME-webhook/1

{
  "id": "3b8f2a61-9d4e-4c1a-8f0b-2e7d5c9a1f44",
  "type": "invoice.confirmed",
  "created_at": "2026-10-03T09:22:11.482Z",
  "data": {
    "invoice_id": "7c9e6f1a-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
    "store_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "status": "confirmed",
    "chain": "txc",
    "address": "TXc9yQ…(the address the customer paid)",
    "fiat_amount": 49.00,
    "fiat_currency": "USD",
    "paid_amount_usd": 49.00,
    "order_id": "ORDER_1234"
  }
}
FieldMeaning
idUnique event id (uuid). Dedupe on this — same as the X-TXCPay-Event-Id header.
typeOne of the four events above. Same as the X-TXCPay-Event header.
created_atWhen we sent it (ISO 8601, UTC).
data.invoice_idNectarPay invoice id — the id returned when you created the invoice.
data.store_idYour store's id.
data.statusconfirmed | overpaid | underpaid.
data.chainNetwork paid on: btc, txc, eth, base, tron, sol, ltc, doge, bch, dash, isk, zcu.
data.addressReceiving address in your wallet.
data.fiat_amountInvoice amount you asked for (number).
data.fiat_currencyCurrency of fiat_amount, e.g. USD.
data.paid_amount_usdUSD value we actually received (can be slightly under within 1%, or over).
data.order_idThe order_id you sent when creating the invoice, or null. Use it to find your order.

Want to double-check? Fetch GET /api/public/v1/invoices/{invoice_id} and confirm status is confirmed or overpaid before shipping.

Always verify the signature against the raw request body before trusting anything:

// node:
const sig = req.headers["x-txcpay-signature"];
const [, t]  = sig.match(/t=(\d+)/);
const [, v1] = sig.match(/v1=([a-f0-9]+)/);
const expected = crypto
  .createHmac("sha256", WEBHOOK_SECRET)   // Store → Webhooks
  .update(`${t}.${rawBody}`)
  .digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) reject();

// php:
[$t, $v1] = sscanf($_SERVER['HTTP_X_TXCPAY_SIGNATURE'], 't=%d,v1=%s');
$expected = hash_hmac('sha256', "$t." . file_get_contents('php://input'), $WEBHOOK_SECRET);
if (!hash_equals($expected, $v1)) { http_response_code(401); exit; }

Reject timestamps more than ~5 minutes old to stop replay. Events carry a unique id — dedupe on it, because retries can repeat a delivery. Reply 2xx quickly and process asynchronously.

Missed one? Trigger a redelivery for a single invoice:

curl -X POST "https://app.nectar-pay.com/api/public/v1/invoices/{id}?action=redeliver-webhook" \
  -H "Authorization: Bearer sk_live_..."

7. Drop-in JS button (no backend code)

One <script> tag and a button — crypto checkout opens in a modal, no redirect, works on any site.

<script src="https://app.nectar-pay.com/sdk/payhme.js" defer></script>

<button
  data-payhme
  data-api-key="sk_live_..."
  data-chain="btc"
  data-amount="49.00"
  data-currency="USD"
  data-order-id="ORDER_1234"
>Pay $49 with Bitcoin</button>

Or call it programmatically:

PayHME.checkout({
  apiKey: "sk_live_...",
  chain: "base", amount: 49, currency: "USD",
  orderId: "ORDER_1234",
}).then(result => {
  // result.status: "paid" | "closed" | "expired"
  if (result.status === "paid") console.log("Got it:", result.tx);
});

The button's result event is a UX signal, not a settlement guarantee — still confirm payment via webhook or the status endpoint before shipping goods.

Live demo

Click below — this is the actual SDK, opening a real (test-mode) checkout in a modal. No payment will be processed; close the modal at any time.

Tip: open your browser console to see the payhme:result event payload when the modal closes.

8. Email an invoice to the customer

Have us send the branded payment request by email. Uses the invoice's buyer_email unless you pass to. Sending an expiring invoice pushes its expiry out 7 days so the emailed link stays payable.

curl -X POST "https://app.nectar-pay.com/api/public/v1/invoices/{id}/email" \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"to":"customer@example.com"}'

# 200
{ "ok": true, "to": "customer@example.com", "sent_at": "...",
  "send_count": 1, "expires_at": "...", "checkout_url": "https://app.nectar-pay.com/i/..." }

9. List invoices (reconciliation)

Paged, newest first, scoped to your key's store. All filters are optional and combinable; status and chain accept comma-separated lists.

curl "https://app.nectar-pay.com/api/public/v1/invoices?status=confirmed,underpaid&since=2026-09-01T00:00:00Z&limit=100" \
  -H "Authorization: Bearer sk_live_..."

# 200
{ "total": 214, "limit": 100, "offset": 0, "invoices": [ { "id": "...", "status": "confirmed",
  "chain": "btc", "token": null, "fiat_amount": 49, "fiat_currency": "USD",
  "crypto_amount": 0.00042, "order_id": "ORDER_1234", "created_at": "...",
  "checkout_url": "..." } ] }
Query paramMeaning
statuspending, detected, underpaid, confirmed, overpaid, expired, cancelled, failed
chainbtc, txc, eth, base, sol, tron, ltc, doge, bch, dash, isk, zcu, lightning
order_idExact match on your own order reference
since / untilISO-8601 timestamps filtering created_at
limit / offsetlimit 1–200 (default 50), offset for paging

10. Webhook delivery history

Every attempt we made for an invoice — status code, retry schedule, and the first 500 bytes of your response. Handy when a webhook silently failed in production.

curl "https://app.nectar-pay.com/api/public/v1/invoices/{id}/webhooks" \
  -H "Authorization: Bearer sk_live_..."

# 200
{ "invoice_id": "...", "deliveries": [ { "id": "...", "url": "https://you/hook",
  "attempt": 1, "status_code": 200, "ok": true, "delivered_at": "...",
  "next_retry_at": null, "event_type": "invoice.paid", "event_id": "..." } ] }

11. Rates

The live USD rates we price invoices with. Pass ?amount= to get the converted crypto amount per network — useful for showing a price before creating an invoice. Stablecoins (USDC, USDT, TSD) are pegged at $1.

curl "https://app.nectar-pay.com/api/public/v1/rates?amount=25" \
  -H "Authorization: Bearer sk_live_..."

# 200
{ "fiat": "USD", "amount": 25,
  "rates": [ { "chain": "btc", "rate": 118422.5, "crypto_amount": 0.00021, "fetched_at": "..." } ],
  "stablecoins": [ { "token": "USDC", "rate": 1, "crypto_amount": 25 } ] }

12. Store info

What your key can see about the store it belongs to: currency, tax settings, default invoice lifetime, which networks and tokens are enabled, whether a webhook is configured, and the store's hosted payment links.

curl "https://app.nectar-pay.com/api/public/v1/store" \
  -H "Authorization: Bearer sk_live_..."

# 200
{ "id": "...", "name": "Acme Coffee", "currency": "USD", "invoice_ttl_seconds": 900,
  "tax_bps": 825, "webhook_configured": true,
  "chains": [ { "chain": "btc", "network": "mainnet", "tokens": [] },
              { "chain": "base", "network": "mainnet", "tokens": ["USDC"] } ],
  "payment_links": [ { "slug": "acme", "url": "https://app.nectar-pay.com/t/acme" } ] }

13. Hosted payment links (no code at all)

A payment link is a reusable hosted page at https://app.nectar-pay.com/t/your-slug — put it in an email, a QR code, a bio link, a donation button. No API key needed by the payer. Create and manage them over the API:

# create
curl -X POST "https://app.nectar-pay.com/api/public/v1/links" \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"slug":"acme","title":"Acme Coffee","preset_amounts":[5,10,25],
       "allow_custom_amount":true,"min_amount":1,"max_amount":500}'
# 201 -> { "slug": "acme", "url": "https://app.nectar-pay.com/t/acme", ... }

# list
curl "https://app.nectar-pay.com/api/public/v1/links" -H "Authorization: Bearer sk_live_..."

# update / deactivate
curl -X PATCH  "https://app.nectar-pay.com/api/public/v1/links/acme" \
  -H "Authorization: Bearer sk_live_..." -H "Content-Type: application/json" \
  -d '{"title":"Acme Coffee — Tips"}'
curl -X DELETE "https://app.nectar-pay.com/api/public/v1/links/acme" \
  -H "Authorization: Bearer sk_live_..."

Omit slug and we derive one from the title (with a short suffix if it's taken). Deleting deactivates the link — the URL stops accepting payments but past invoices stay intact. Set is_donation: true for a donate-style page.

E-commerce plugins

Prefer a ready-made plugin? WooCommerce, PrestaShop, OpenCart, Magento, WHMCS and more → Every plugin is a thin wrapper around the exact API documented above.

Don't trust us? Good.

We get it. The whole point of crypto is don't trust, verify. A payment processor asking you to take its word for it is the exact thing we built Nectar.Pay to replace. We don't hold your keys — and you don't have to take our word for that either.

Read the source. Run it yourself. Or fork the whole thing and run a competing gateway — we honestly don't mind. The bees do better when the hive is bigger.

Prefer to self-host the GitHub source? Go for it — no license fee, no phone-home. The $999 remix is just the shortcut.

Partner APIs

GET/api/public/v1/egiving/causes

Signed public list of approved eGiving churches. Optional ?q= (search) and ?code=. Every response is TXC-message-signed; wallets should pin the signer address.

POST/api/public/v1/pop/claim

For approved wallet partners: claim the POP reward for a paid sale (HMAC-signed). Each sale can be claimed once.