API ReferenceBilling

Transactions

Record what money actually moved — payments, refunds, chargebacks and their reversals.

Transactions

PUT https://api.faurya.com/api/v1/billing/transactions/{external_id}
GET https://api.faurya.com/api/v1/billing/transactions/{external_id}

Requires Bearer Token authentication with your Faurya API key.

An invoice says what was billed. A transaction says what money moved against it. It is what makes an invoice paid, what makes your revenue reports show cash — and, for a subscription's first period, what proves service actually commenced.

Every transaction belongs to an invoice, so send the invoice first.

Record a transaction

const response = await fetch(
  "https://api.faurya.com/api/v1/billing/transactions/txn_7781",
  {
    method: "PUT",
    headers: {
      Authorization: `Bearer ${process.env.FAURYA_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      invoice_external_id: "inv_1042",
      type: "payment",
      result: "succeeded",
      amount: 49,
      occurred_at: "2026-03-01T09:14:00Z",
      fee: 1.72,
      metadata: {
        provider: "razorpay",
        method: "upi",
      },
    }),
  },
);

Request body

Required

  • invoice_external_id (string): The invoice this money moved against. Must already exist.
  • type (string): payment, refund, chargeback or chargeback_reversal.
  • amount (number): Always positive. The type carries the direction, so a refund is type: "refund" with amount: 10, never a payment of -10.

Optional

  • result (string): succeeded (the default), failed or pending.
  • currency (string): The invoice's currency. You may send it to be checked; a mismatch is refused with CURRENCY_MISMATCH. Omit it and the invoice's currency is used.
  • occurred_at (string): When the money moved, ISO 8601. Defaults to the time Faurya receives the request. Use it when reporting past transactions; it may not be more than 5 minutes in the future.
  • original_transaction_external_id (string): The payment this reverses. Required on refund, chargeback and chargeback_reversal; not allowed on a payment.
  • fee (number): The processing fee you paid. Defaults to 0.
  • fee_currency (string): The currency of fee, if it differs from the invoice's.
  • settlement_amount and settlement_currency: What actually landed in your account, when your processor settles in another currency. The two are one fact — send both or neither.
  • metadata (object): Up to 10 entries. Keys 1–64 characters; values a string (≤500 characters), number, boolean or null. Objects and arrays are refused.

Decimal places are judged against the invoice's currency, not a guess: 12.50 is fine on a USD invoice and two decimals too many on a JPY one.

In a bulk import a transaction's kind is sent as transaction_type, because type there names the item kind. It is the only field whose name differs between the two doors.

Reversals

The three reversal types must name the payment they act on:

TypeWhat it meansNames
refundYou gave money backthe payment being refunded
chargebackThe bank pulled money backthe payment that was pulled back
chargeback_reversalYou won the disputethe payment whose chargeback was undone

The payment they name must exist, have succeeded, and belong to the same invoice — otherwise 422 TRANSACTION_NOT_FOUND.

await fetch("https://api.faurya.com/api/v1/billing/transactions/txn_7782", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.FAURYA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    invoice_external_id: "inv_1042",
    type: "refund",
    amount: 20,
    original_transaction_external_id: "txn_7781",
    occurred_at: "2026-03-09T11:00:00Z",
  }),
});

Faurya marks a refund for less than the full payment as is_partial_refund: true in the response.

Money cannot exceed what it acts on

RuleCode when broken
Payments on an invoice may not exceed the invoice total422 AMOUNT_EXCEEDS_INVOICE
Refunds and chargebacks may not exceed the payment they reverse422 AMOUNT_EXCEEDS_PAYMENT
Reversals may not give back more than was taken422 AMOUNT_EXCEEDS_PAYMENT

Only succeeded money is bounded. A failed or pending transaction moved nothing, so it is accepted whatever its size — and is checked again if it later succeeds.

A transaction is almost entirely immutable

Money that moved is a fact about the past. Re-sending the same transaction is a replay and writes nothing; changing it is refused.

FieldCan it change?
type, amount, currency, invoice_external_id, original_transaction_external_idNo — 409 TRANSACTION_IMMUTABLE
resultOnly off pending — to succeeded or failed
occurred_atKeeps the stored instant unless you state a new one
fee, fee_currency, settlement_amount, settlement_currency, metadataYes, freely — they describe the same movement

So the normal lifecycle of a pending charge is: record it as pending, then PUT the same id again with result: "succeeded" and the fee your processor reported.

A transaction id and a payments API transaction_id share one space. Reusing an id already recorded through /payments is refused with 409 TRANSACTION_EXISTS_IN_PAYMENTS_API.

Read a transaction

const response = await fetch(
  "https://api.faurya.com/api/v1/billing/transactions/txn_7781",
  { headers: { Authorization: `Bearer ${process.env.FAURYA_API_KEY}` } },
);
{
  "object": "transaction",
  "external_id": "txn_7781",
  "data": {
    "external_id": "txn_7781",
    "invoice_external_id": "inv_1042",
    "type": "payment",
    "result": "succeeded",
    "amount": 49,
    "currency": "USD",
    "occurred_at": "2026-03-01T09:14:00.000Z",
    "original_transaction_external_id": null,
    "fee": 1.72,
    "fee_currency": "USD",
    "settlement_amount": null,
    "settlement_currency": null,
    "is_partial_refund": null,
    "metadata": { "provider": "razorpay", "method": "upi" }
  },
  "request_id": "req_01JQ8Z0Y5N6W3K2M4P7R9T1V3X"
}

amount always reads back positive. is_partial_refund is null on anything that is not a refund. result reads back null if the invoice was voided — voiding the document voids its cash with it.

Response codes

  • 200 OK — transaction updated, or the request matched what was stored.
  • 201 Created — the transaction did not exist before.
  • 400 Bad Request — invalid fields, an unknown field, or a field not allowed for the type.
  • 401 Unauthorized — missing or invalid API key.
  • 404 Not Found — (GET only) no transaction with that id.
  • 409 Conflict — TRANSACTION_IMMUTABLE, INVOICE_VOIDED, TRANSACTION_EXISTS_IN_PAYMENTS_API, or a concurrent write.
  • 422 Unprocessable Entity — INVOICE_NOT_FOUND, TRANSACTION_NOT_FOUND, CURRENCY_MISMATCH, AMOUNT_EXCEEDS_INVOICE or AMOUNT_EXCEEDS_PAYMENT.
  • 429 Too Many Requests — over the rate limit.