Home Features Integrations Docs
Open dashboard
API Reference
 Base URL: https://api.doronpay.com/smart-invoicing
Token is stored in memory only
Smart Invoicing API

REST API for creating crypto invoices, managing merchant wallets, configuring live rates, handling payment detection and webhooks, and reading billing and reports.

Base URLhttps://api.doronpay.com/smart-invoicing
Prefix every path shown in this reference with this base URL.
Authentication
Dashboard / web app
Authorization: Bearer <token>
Content-Type: application/json
Server-to-server / API key
x-doronpay-api-key: <apiKey>
Content-Type: application/json
Response format
Success envelope
{ "success": true, "message": "...", "data": { ... } }
Error envelope
{ "success": false, "message": "Human readable error", "errorCode": "MACHINE_CODE" }
Authentication
POST/auth/registerPublicCreate merchant user, business, workspace, default subscription and trade pairs in one call.
Field Type Description
name string required Full name of the merchant user
email string required Email address — used for login
phone string required Phone number
password string required Minimum 8 characters
businessName string required Display name of the business
businessSlug string required URL-safe identifier e.g. demo-shop
environment "TEST"|"LIVE" required Starting environment
defaultCurrency string required e.g. GHS, USD, NGN
timezone string required IANA timezone e.g. Africa/Accra
planCode string optional FREE | BUSINESS | GROWTH | ENTERPRISE
200 OK
{ "success": true, "data": { "token": "eyJ...", "workspaceId": "6a...", "environment": "TEST" } }
Request body
Response
Response will appear here...
POST/auth/loginPublicAuthenticate and receive a fresh JWT token.
Field Type Description
email string required Registered email
password string required Account password
environment "TEST"|"LIVE" required Which workspace to authenticate into
200 OK
{ "data": { "token": "eyJ...", "environment": "LIVE" } }
Request body
Response
Response will appear here...
GET/workspace/setupBearerReturns onboarding checklist with progress %, readiness flags, and the next required action.
200 OK
{ "data": { "status": "INCOMPLETE", "progress": 56, "canCreateInvoice": false } }
Query string
Response
Response will appear here...
GET/workspace/configBearerFull runtime workspace config — enabled currencies, assets, wallet readiness flags.
200 OK
{ "data": { "workspace": { "environment": "TEST" }, "readiness": { "canCreateInvoice": true } } }
Query string
Response
Response will appear here...
Wallet profiles
Non-custodial: DoronX stores only your public receiving address. At least one wallet must be set as default before invoices can be created.
POST/wallet-profilesBearerConnect a business-owned external wallet address.
Field Type Description
asset string required USDT | BTC | USDC
network string required TRC20 | BEP20 | SOLANA | BTC
address string required Your business-owned public receiving address
label string required Human-readable label e.g. Main USDT TRC20
isDefault boolean optional Set as default receiving wallet
ownershipProofType string optional MANUAL
Request body
Response
Response will appear here...
GET/wallet-profilesBearerList all connected wallet profiles for the current workspace.
Query string
Response
Response will appear here...
PATCH/wallet-profiles/{walletProfileId}BearerUpdate a wallet profile — change label, set as default.
Field Type Description
isDefault boolean optional Promote this wallet to the default
label string optional Update the display label
walletProfileId + body
Response
Response will appear here...
Rate engine
POST/rates/trade-pairsBearerCreate or update a merchant trade pair. Rate = Kraken crypto/USD × Fixer USD/fiat × (1 + markup%).
Field Type Description
baseAsset string required USDT | BTC | USDC
quoteCurrency string required Fiat invoice currency e.g. GHS
allowedNetworks string[] required Networks customer can pay on
markupPercent number required Your profit margin. 2 = 2%
spreadPercent number optional Additional spread
pricingMode string optional AUTO (live) | MANUAL (fixed)
minInvoiceAmount number optional Minimum fiat invoice amount
maxInvoiceAmount number|null optional null = unlimited
rateTtlSeconds number optional Default 600
Request body
Response
Response will appear here...
GET/rates/trade-pairsBearerList all merchant-scoped trade pairs.
Query string
Response
Response will appear here...
POST/rates/quoteBearerGet the exact crypto amount for a fiat invoice value.
Field Type Description
amount number required Fiat invoice amount
invoiceCurrency string required e.g. GHS
paymentAsset string required e.g. USDT
forceRefresh boolean optional true to bypass cache
Request body
Response
Response will appear here...
POST/rates/refreshBearerForce-refresh the cached rate for a specific trade pair.
Request body
Response
Response will appear here...
Invoices
Amount fingerprinting: always display amountFingerprint.finalExpectedAmount — not the raw quoted amount. The customer must send the exact fingerprinted amount.
POST/invoicesBearer / API keyCreate a crypto invoice. Returns payment URL, exact crypto amount, and fingerprint data.
Field Type Description
payerName string required Customer full name
payerEmail string required Customer email
payerPhone string optional Customer phone
amount number required Fiat amount to invoice
currency string required GHS | USD | NGN | CAD | EUR
asset string required USDT | BTC | USDC
network string required TRC20 | BEP20 | SOLANA | BTC
description string optional Invoice note shown on payment page
forceRateRefresh boolean optional true to fetch live rate at creation time
200 OK
{
  "data": {
    "invoice": { "invoiceId": "6a...", "status": "AWAITING_PAYMENT" },
    "paymentIntent": {
      "expectedCryptoAmount": 65.123401,
      "amountFingerprint": { "finalExpectedAmount": 65.123401 },
      "receivingAddress": "TNoTbnm..."
    },
    "paymentUrl": "https://pay.doronpay.com/..."
  }
}
Request body
Response
Response will appear here...
GET/invoices/{invoiceId}Bearer / API keyFull invoice detail — payment intent, amount fingerprint, detection status.
invoiceId
Response
Response will appear here...
GET/invoices/reference/{referenceCode}Bearer / API keyLook up an invoice by its reference code.
referenceCode
Response
Response will appear here...
Payment stands
POST/payment-standsBearerCreate a reusable QR payment stand. Returns publicUrl, printUrl, and base64 QR image.
Request body
Response
Response will appear here...
GET/payment-standsBearerList all payment stands for this workspace.
Query string
Response
Response will appear here...
Webhooks
Deliveries are queued, retried with exponential backoff. Always respond 2xx immediately. Verify the X-DoronPay-Signature header using your webhook secret.
POST/webhooksBearerCreate a webhook endpoint and generate a signing secret. Secret is shown only once — save immediately.
Field Type Description
name string required Label for this endpoint
url string required HTTPS URL DoronX will POST events to
eventTypes string[] required invoice.created | invoice.payment_detected | invoice.paid | invoice.expired
200 OK
{ "data": { "webhookEndpointId": "6a...", "secret": "whsec_..." } }
Save the secret immediately. It is not stored and cannot be retrieved again.
Request body
Response
Response will appear here...
Node.js — signature verification
import crypto from 'crypto';
function verifyWebhook(rawBody, timestamp, signature, secret) {
  const expected = crypto.createHmac('sha256', secret)
    .update(timestamp + '.' + rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
app.post('/webhooks/doronpay', express.raw({type:'*/*'}), (req,res) => {
  const ok = verifyWebhook(req.body, req.headers['x-doronpay-timestamp'],
    req.headers['x-doronpay-signature'], process.env.WEBHOOK_SECRET);
  if (!ok) return res.status(401).end();
  res.sendStatus(200);
});
GET/webhooksBearerList all webhook endpoints for this workspace.
Query string
Response
Response will appear here...
DELETE/webhooks/{webhookEndpointId}BearerDisable and delete a webhook endpoint.
webhookEndpointId
Response
Response will appear here...
GET/webhooks/deliveriesBearerDelivery logs — attempt count, HTTP response codes, retry status.
Query string
Response
Response will appear here...
Billing
GET/billing/plansPublicList available SaaS plans with monthly fees and feature limits.
Query string
Response
Response will appear here...
GET/billing/subscriptionBearerGet current subscription details.
Query string
Response
Response will appear here...
GET/billing/usageBearerUsage counts for the current billing period.
Query string
Response
Response will appear here...
GET/billing/billsBearerList SaaS bills with amounts, due dates, and payment status.
Query string
Response
Response will appear here...
POST/billing/change-planBearerChange subscription plan.
Request body
Response
Response will appear here...
POST/billing/bills/{billId}/pay-cryptoBearerCreate a crypto payment intent for a SaaS bill.
billId + body
Response
Response will appear here...
Reports
GET/reports/summaryBearer / API keyAggregated summary — invoice totals, volume by currency, crypto received, webhook delivery stats.
Query string
Response
Response will appear here...
Invoice statuses
CREATED
Intent created, not yet monitoring
AWAITING_PAYMENT
Detection registered, waiting for customer
PAYMENT_DETECTED
Transaction seen on-chain, 0 confirmations
CONFIRMING
Confirmation blocks accumulating
PAID
Confirmed, amount within tolerance
UNDERPAID
Confirmed but below expected
OVERPAID
Confirmed but exceeds expected
EXPIRED
Timed out before payment detected
FAILED
Processing or detection error
CANCELLED
Manually cancelled
Amount fingerprinting

Because V1 merchants share a single wallet address, each invoice gets a tiny deterministic decimal suffix so DoronX can match an on-chain deposit to exactly one invoice.

Fiat invoiceGHS 1,000.00
Quoted crypto65.123000 USDT
Fingerprint suffix+ 0.000401
Customer must send EXACTLY65.123401 USDT
Always display amountFingerprint.finalExpectedAmount — never the raw quoted amount.
If a customer sends a rounded amount (e.g. 65.12 instead of 65.123401), the deposit may be flagged as AMBIGUOUS and require manual review.