API ReferenceBilling

Plans

Define a billing cadence. Plans carry no price — the price lives on each invoice line.

Plans

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

Requires Bearer Token authentication with your Faurya API key.

A plan is a name and a billing cadence: "Pro, billed every month", "Team, billed every year". That cadence is what turns a line amount into a monthly figure, so it is the piece Faurya needs to compute MRR.

A plan has no price. The price is stated on every invoice line that uses the plan. That is deliberate: it means grandfathered pricing, discounts, local currencies and per-seat amounts all work without a plan per price point.

Send your plans before the invoices whose lines name them — a line naming an unknown plan is refused with PLAN_NOT_FOUND.

Create or update a plan

const response = await fetch(
  "https://api.faurya.com/api/v1/billing/plans/plan_pro_monthly",
  {
    method: "PUT",
    headers: {
      Authorization: `Bearer ${process.env.FAURYA_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Pro",
      interval_unit: "month",
      interval_count: 1,
    }),
  },
);

Request body

All three fields are required.

  • name (string): What you call the plan. At most 255 characters.
  • interval_unit (string): day, week, month or year.
  • interval_count (integer): How many of those units one billing cycle is. Between 1 and 365.

So a quarterly plan is interval_unit: "month" with interval_count: 3; a two-weekly plan is "week" with 2.

Response

{
  "object": "plan",
  "external_id": "plan_pro_monthly",
  "created": true,
  "data": {
    "external_id": "plan_pro_monthly",
    "name": "Pro",
    "interval_unit": "month",
    "interval_count": 1
  },
  "warnings": [],
  "request_id": "req_01JQ8Z0Y5N6W3K2M4P7R9T1V3X"
}

The cadence locks once the plan is in use

A plan's name can always change. Its cadence can only change while nothing is priced on it.

Once any invoice line or subscription state uses the plan, changing interval_unit or interval_count is refused with 409 PLAN_CADENCE_LOCKED:

{
  "message": "This plan's billing interval cannot change because invoices already use it. Create a new plan instead.",
  "error": "PLAN_CADENCE_LOCKED",
  "request_id": "req_01JQ8Z0Y5N6W3K2M4P7R9T1V3X"
}

The reason is that the cadence is the only thing converting every historical line amount on that plan into a monthly figure. Flipping a plan from monthly to yearly would quietly divide every MRR figure it ever produced by twelve, retroactively.

If you genuinely move customers from monthly to yearly billing, create a second plan and bill the new periods against it. The move then shows up as a plan change, which is what it is.

Read a plan

const response = await fetch(
  "https://api.faurya.com/api/v1/billing/plans/plan_pro_monthly",
  { headers: { Authorization: `Bearer ${process.env.FAURYA_API_KEY}` } },
);

Answers 200 with the plan, or 404 if nothing was written under that id.

Response codes

  • 200 OK — plan updated, or the request matched what was stored.
  • 201 Created — the plan did not exist before.
  • 400 Bad Request — missing or invalid fields. errors names every one.
  • 401 Unauthorized — missing or invalid API key.
  • 404 Not Found — (GET only) no plan with that id.
  • 409 Conflict — PLAN_CADENCE_LOCKED, or another request is writing the same plan.
  • 429 Too Many Requests — over the rate limit.