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)