API Reference
Base URL
http://<merchant-host>:<port>/api/v1 — self-hosted, so the host/port is whatever you set when deploying (see Quickstart).
Authentication
Single-tenant: every endpoint uses one shared secret (FIBERGATE_INTERNAL_SECRET, set as an environment variable at deploy time), compared in constant time. There's no per-client API key system — whatever app you give this secret to can call the full API on your behalf.
Authorization: Bearer <FIBERGATE_INTERNAL_SECRET>Response Format
Every response is JSON, in one of these two shapes:
// Success
{ data: T, error: null, meta?: { ... } }
// Error
{ data: null, error: { code: string, message: string } }Endpoints
POST /invoices
Create a new invoice.
Request:
{
"amount": 1.5,
"asset": "CKB",
"description": "Order #123",
"expires_in": 3600,
"metadata": {}
}amount— number of CKB or RUSD (float)asset—"CKB"or"RUSD"description— optionalexpires_in— seconds, default3600, max86400metadata— optional, stores any object you want alongside the invoice
Response 201:
{
"data": {
"id": "inv_uuid",
"invoice_address": "fibt1...",
"payment_hash": "0xabc...",
"amount": 1.5,
"asset": "CKB",
"status": "pending",
"expires_at": "2026-07-01T12:00:00Z",
"created_at": "2026-07-01T11:00:00Z"
},
"error": null
}Errors:
400 INVALID_AMOUNT— amount is<= 0or unreasonably large400 UNSUPPORTED_ASSET— asset isn'tCKBorRUSD401 UNAUTHORIZED— token doesn't matchFIBERGATE_INTERNAL_SECRET429 RATE_LIMITED— exceeded 100 invoices/minute across the whole deployment503 NODE_UNAVAILABLE— the Fiber node isn't responding503 ASSET_NOT_CONFIGURED— the asset is valid (CKB/RUSD) but the node hasn't whitelisted this UDT yet — unlikeNODE_UNAVAILABLE, retrying won't help; the merchant needs to fix the node's config
GET /invoices/:id
Get an invoice's current status.
Response 200:
{
"data": {
"id": "inv_uuid",
"invoice_address": "fibt1...",
"payment_hash": "0xabc...",
"amount": 1.5,
"asset": "CKB",
"status": "paid",
"paid_at": "2026-07-01T11:05:00Z",
"expires_at": "2026-07-01T12:00:00Z",
"created_at": "2026-07-01T11:00:00Z"
},
"error": null
}status is one of pending, paid, expired, failed — see Architecture's "Invoice status lifecycle" for how these transitions work.
Errors:
401 UNAUTHORIZED— token doesn't matchFIBERGATE_INTERNAL_SECRET404 NOT_FOUND— no invoice with thatid
GET /invoices
List invoices, paginated.
Query params: status, asset, limit (default 20, max 100), cursor
GET /node/info
Current Fiber node status (this endpoint is public — no auth required).
Response 200:
{
"data": {
"pubkey": "02...",
"active_channels": 3,
"inbound_capacity_ckb": 800,
"outbound_capacity_ckb": 400,
"status": "online"
},
"error": null
}status is "online" (every channel active) or "degraded" (the RPC call succeeded, but some channels are disabled). There's no "offline" value here — if the node truly isn't responding, this endpoint returns 503 NODE_UNAVAILABLE instead of a 200 with a status field saying so.
POST /api/cron/poll-invoices
Not under /api/v1 — the full path is http://<merchant-host>:<port>/api/cron/poll-invoices. An optional endpoint to manually trigger one invoice-poll cycle (the primary mechanism is an in-process background job that already runs continuously — this endpoint is only useful when you want to force a check right now instead of waiting).
Auth: Authorization: Bearer <CRON_SECRET> — a separate secret, notFIBERGATE_INTERNAL_SECRET. If CRON_SECRET isn't configured, this endpoint is disabled entirely and returns 503 before even checking the token.
Response 200:
{
"data": { "triggered": true },
"error": null
}Errors:
401 UNAUTHORIZED— token doesn't matchCRON_SECRET503 CRON_NOT_CONFIGURED—CRON_SECRETisn't set, endpoint is disabled500 INTERNAL_ERROR— the poll cycle hit an unexpected error (a single slow/failed Fiber node call for one invoice doesn't fail the whole request — only unexpected errors do)
Webhook Payload
When an invoice is paid, FiberGate POSTs this to your registered endpoint:
{
"event": "payment.paid",
"created_at": "2026-07-01T11:05:00Z",
"data": {
"invoice_id": "inv_uuid",
"payment_hash": "0xabc...",
"amount": 1.5,
"asset": "CKB",
"paid_at": "2026-07-01T11:05:00Z",
"metadata": {}
}
}When an invoice expires (same data shape, paid_at is always null since it was never paid):
{
"event": "invoice.expired",
"created_at": "2026-07-01T13:00:00Z",
"data": {
"invoice_id": "inv_uuid",
"payment_hash": "0xabc...",
"amount": 1.5,
"asset": "CKB",
"paid_at": null,
"metadata": {}
}
}Every request includes an X-Fiber-Signature: sha256=<hex> header — an HMAC-SHA256 of the raw request body, signed with the secret you were given when you registered the webhook endpoint. Verify it before trusting the payload (the @fibergate/sdk package provides a verifyWebhookSignature() helper for this).