TriportRPC

Pay invoice from balance

POSThttps://api.triport.io/v1/billing/invoices/inv_8f2c1a/pay-from-balance

Settle a pending invoice instantly by debiting the signed-in user's prepaid balance.

Closes a pending invoice by drawing down the authenticated user's prepaid balance instead of an on-chain or in-app crypto payment. The debit and the invoice activation happen atomically inside a single database transaction, so the call either fully settles the invoice or changes nothing.

On success the endpoint returns the new balance synchronously — there is no need to poll the balance snapshot afterwards. The same number is also pushed as a balance_debit event on /v1/balance/events, so any other open UI tab updates live.

The call is idempotent: POSTing again for an invoice that is already paid returns 200 with the same response shape (the current state), not an error. This lets the UI treat a double-click or a retried request exactly like the first request.

Use this when the user has enough prepaid balance and wants to pay without broadcasting a blockchain transaction. If the balance is insufficient, locked, or the invoice is not yours / not pending, the call fails with a named error code (see Errors).

Parameters

Path parameters

idstringrequired
The invoice ID to settle. Must be a pending invoice owned by the authenticated user.

Response

Response fields

FieldTypeDescription
invoice_idstringThe invoice that was settled.
statusstringAlways "paid" on a successful (or idempotent) call.
new_balance_microintegerThe user's balance after the debit, in micro-units (1 unit = 1,000,000 micro).

Errors

All errors use the shared error envelope { "error": "<code>" } — see errors.md for the full contract. The error value is one of the named codes below; surface it to the user verbatim.

CodeHTTPMeaningWhen it happens
auth required401Not authenticatedNo valid session cookie on the request.
invoice not found404Unknown invoiceNo invoice exists for the given id.
not_owner409Invoice belongs to another userThe invoice's owner is not the authenticated user.
invoice_not_pending409Invoice not payableInvoice is expired, cancelled, or lost a race to another settlement.
invoice_is_topup409Wrong invoice kindTop-up invoices credit the balance rather than debit it; they cannot be paid from balance.
insufficient_balance409Balance too lowThe balance is less than the invoice amount.
balance_locked409Balance lockedThe user's balance is temporarily locked and cannot be debited.
already_debited409Concurrent debitA parallel settlement already consumed this invoice's debit slot.

A request for an invoice that is already paid is not an error — it returns 200 with the standard response body (idempotent replay).

Examples

JavaScript (fetch)

const res = await fetch(
  `https://api.triport.io/v1/billing/invoices/${invoiceId}/pay-from-balance`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    credentials: "include", // send the session cookie
  },
);


if (!res.ok) {
  const { error } = await res.json();
  throw new Error(error); // e.g. "insufficient_balance"
}


const { invoice_id, status, new_balance_micro } = await res.json();
console.log(`${invoice_id}${status}, balance now ${new_balance_micro} micro`);

TypeScript SDK (@triport/sdk)

import { Triport } from "@triport/sdk";


const triport = new Triport(); // uses the browser session cookie


const result = await triport.billing.payFromBalance(invoiceId);
// result: { invoice_id: string; status: "paid"; new_balance_micro: number }
console.log(result.new_balance_micro);

Python (triport-sdk)

from triport import Triport


triport = Triport(session=session_cookie)


result = triport.billing.pay_from_balance(invoice_id)
# {"invoice_id": "inv_8f2c1a", "status": "paid", "new_balance_micro": 500000}
print(result["new_balance_micro"])

Notes

  • Live updates. On success a balance_debit event is broadcast on /v1/balance/events with the shape { "kind": "balance_debit", "user_id", "delta_micro", "new_balance", "ref_invoice_id", "at" }, where delta_micro is negative and ref_invoice_id is the settled invoice. An invoice_paid event is also emitted on that invoice's own event stream.
  • Idempotency. Retrying after a paid response is safe — you receive the same 200 body. Build retries assuming at-most-once settlement even under concurrent requests; the duplicate is collapsed server-side.
  • Related endpoints. To pay on-chain or in-app instead, see the in-app transaction endpoints; to cancel an unpaid invoice, see cancel invoice; to inspect available funds, see the balance snapshot and ledger entries.