Documentation

Kovarra API (v1)

Base URL: https://<your-domain>/api/v1 · Auth: Authorization: Bearer <api key> (create keys in the admin under API keys; they are stored hashed and shown once).

Amount conventions: fiat amounts are decimal strings ("100", "99.50"). Crypto amounts appear both human-readable (crypto_amount: "100.129181") and in 6-decimal base units (crypto_amount_base_units: "100129181"). All money math server-side is integer-only.

Create an invoice

POST /api/v1/invoices
{
  "fiat_amount": "100",          // required
  "fiat_currency": "USD",        // optional (default from settings)
  "external_id": "order-1234",   // optional — your order reference, echoed in webhooks
  "metadata": { "any": "json" }, // optional
  "expires_in_sec": 1200,        // optional, 60–86400 (default 1200 = 20 min)
  "assets": ["USDT"]             // optional — restrict which assets checkout offers
}
→ 201
{
  "id": "01J…",
  "checkout_url": "https://…/pay/01J…",
  "status": "created",
  "fiat_amount": "100",
  "fiat_currency": "USD",
  "expires_at": "2026-07-08T12:20:00.000Z"
}

The invoice is fiat-denominated; the payer chooses USDT (Tron) or USDC (Base) on the checkout page. Each choice pegs its own exchange rate at selection time.

Read invoices

GET /api/v1/invoices/:id            → full invoice incl. options[] and payments[]
GET /api/v1/invoices?status=paid&limit=20&cursor=<last id>

Statuses: created → detected → confirming → paid, or expired / underpaid. Overpayments mark the invoice paid and record the surplus in metadata.overpaid_amount.

Payment links

POST /api/v1/payment-links  { "fiat_amount": "25", "description": "Consulting" }
→ 201 { "id": "01J…", "url": "https://…/link/01J…" }

A link is reusable: every visitor gets a fresh invoice for the configured amount.

Health

GET /health (no auth) → { "ok": true }

Webhooks

Configure endpoints in the admin under Webhooks. Every event is delivered as:

POST <your endpoint>
X-PayServer-Event: invoice.paid
X-PayServer-Signature: <hex hmac-sha256 of the raw body, keyed with the endpoint secret>

{ "event": "invoice.paid", "created_at": "…", "data": { "invoice_id": "01J…", "external_id": "order-1234", … } }

Events: invoice.detected, invoice.confirming, invoice.paid, invoice.overpaid, invoice.expired, invoice.underpaid, payment.late.

Failed deliveries retry after 1m, 5m, 30m, 2h, 12h, then dead-letter (visible + redeliverable in the admin).

Verify signatures with a constant-time compare. Node example:

import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(secret, rawBody, signature) {
  const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
  return expected.length === signature.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

PHP: hash_equals(hash_hmac('sha256', $rawBody, $secret), $signature).

The header names X-PayServer-Signature / X-PayServer-Event are stable technical identifiers in the code and are intentionally left unchanged across releases.

Public endpoints (used by the checkout page — no auth)

GET  /public/invoices/:id            → summary + branding
POST /public/invoices/:id/options    { "chain": "tron" | "base" }   → address + pegged amount
GET  /public/invoices/:id/status     → { status, confirmations, amount_received, … }  (poll ≤ every 3s)
Install guide → Wallet setup → ← Back to home