{
  "name": "Subscription billing",
  "nodes": [
    {
      "id": "n_organisation",
      "name": "Organisation",
      "labels": [
        "Organisation",
        "Party"
      ],
      "shape": "circle",
      "description": "The legal entity that owes the money. Kept above Customer because one organisation can hold several billing relationships — different products, different currencies, different subsidiaries — and consolidating them is a question that cannot even be asked if the organisation is not modelled.",
      "properties": [
        {
          "name": "organisationId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the organisation."
        },
        {
          "name": "legalName",
          "type": "string",
          "required": true,
          "indexed": true,
          "description": "Registered legal name, which is what must appear on an invoice and is frequently not the trading name."
        },
        {
          "name": "tradingName",
          "type": "string",
          "description": "Name the organisation is known by commercially."
        },
        {
          "name": "countryCode",
          "type": "string",
          "required": true,
          "description": "ISO 3166-1 alpha-2 code of incorporation. Drives which tax rules apply before any address is considered."
        },
        {
          "name": "isTaxExempt",
          "type": "boolean",
          "default": "false",
          "description": "True where the organisation has established an exemption. An exemption is a claim by the customer that the seller relies on, so the evidence matters as much as the flag."
        }
      ],
      "x": 960,
      "y": 170
    },
    {
      "id": "n_customer",
      "name": "Customer",
      "labels": [
        "Customer",
        "Party"
      ],
      "shape": "circle",
      "description": "The billing relationship itself: an account that can hold subscriptions, receive invoices and store payment methods. Distinct from the organisation that owns it and from any person who logs in.",
      "properties": [
        {
          "name": "customerId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the billing account."
        },
        {
          "name": "email",
          "type": "string",
          "required": true,
          "indexed": true,
          "description": "Address invoices and dunning notices are sent to. Not an identity — the same person may be the billing contact for several customers."
        },
        {
          "name": "createdAt",
          "type": "datetime",
          "required": true,
          "description": "When the billing relationship opened."
        },
        {
          "name": "balanceMinorUnits",
          "type": "integer",
          "default": "0",
          "description": "Running credit or debit carried on the account, in the smallest unit of its currency. Negative means the customer is in credit, which is the sign convention most billing systems use and almost every integration gets backwards at least once."
        },
        {
          "name": "delinquentSince",
          "type": "date",
          "description": "When the most recent unpaid invoice passed its due date. Blank while in good standing, and the field dunning runs from."
        },
        {
          "name": "metadata",
          "type": "map",
          "description": "Free-form key/value pairs the integrating system attaches to the record. Deliberately untyped and deliberately not queried by the billing logic itself — it is the customer's data, carried along and handed back."
        }
      ],
      "x": 1200,
      "y": 170
    },
    {
      "id": "n_address",
      "name": "Address",
      "labels": [
        "Address"
      ],
      "shape": "circle",
      "description": "A billing or shipping address. Its own node because tax is decided by where the customer is, so the address is an input to the calculation rather than a decoration on the invoice.",
      "properties": [
        {
          "name": "addressId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the address."
        },
        {
          "name": "line1",
          "type": "string",
          "required": true,
          "description": "First line of the address."
        },
        {
          "name": "city",
          "type": "string",
          "description": "City or locality."
        },
        {
          "name": "postalCode",
          "type": "string",
          "indexed": true,
          "description": "Postal code, which in several jurisdictions determines the tax rate on its own."
        },
        {
          "name": "countryCode",
          "type": "string",
          "required": true,
          "description": "ISO 3166-1 alpha-2 code. The single most important field on the record for tax purposes."
        },
        {
          "name": "addressType",
          "type": "enum",
          "required": true,
          "default": "billing",
          "description": "What the address is used for: billing, shipping or registered. Billing and shipping can attract different tax treatment for the same sale."
        }
      ],
      "x": 1440,
      "y": 170
    },
    {
      "id": "n_tax_registration",
      "name": "TaxRegistration",
      "labels": [
        "TaxRegistration"
      ],
      "shape": "circle",
      "description": "A customer's registration for a tax regime — a VAT number, a GST registration. Held because a valid registration shifts who accounts for the tax, which changes what the invoice must say and how much is charged.",
      "properties": [
        {
          "name": "registrationId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the registration."
        },
        {
          "name": "taxIdentifier",
          "type": "string",
          "required": true,
          "indexed": true,
          "description": "The registration number as issued."
        },
        {
          "name": "regime",
          "type": "enum",
          "required": true,
          "default": "vat",
          "description": "Which regime the registration is under: vat, gst, sales_tax or other."
        },
        {
          "name": "countryCode",
          "type": "string",
          "required": true,
          "description": "ISO 3166-1 alpha-2 code of the issuing authority."
        },
        {
          "name": "verificationStatus",
          "type": "enum",
          "required": true,
          "default": "unverified",
          "description": "Whether the number has been checked against the issuing authority: unverified, verified or invalid. An unverified number is not a defence if it turns out to be wrong."
        },
        {
          "name": "verifiedOn",
          "type": "date",
          "description": "When verification last succeeded. Registrations are withdrawn, so a check has a shelf life."
        }
      ],
      "x": 1680,
      "y": 170
    },
    {
      "id": "n_product",
      "name": "Product",
      "labels": [
        "Product",
        "Catalogue"
      ],
      "shape": "square",
      "description": "Something sold, as a catalogue entry. Reference data: it changes when the commercial offer changes, not when any customer does anything.",
      "properties": [
        {
          "name": "productCode",
          "type": "string",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Stable code for the product, used in the catalogue and on invoices."
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Name as it appears to customers and on invoices."
        },
        {
          "name": "isActive",
          "type": "boolean",
          "default": "true",
          "description": "False once the product is withdrawn from sale. Withdrawn products stay because existing subscriptions still reference them."
        },
        {
          "name": "taxCategory",
          "type": "string",
          "description": "Classification used to decide the tax treatment of the sale, such as a digital-services category."
        }
      ],
      "x": 190,
      "y": 210
    },
    {
      "id": "n_plan",
      "name": "Plan",
      "labels": [
        "Plan",
        "Catalogue"
      ],
      "shape": "square",
      "description": "A named tier of a product — Basic, Pro, Enterprise. Sits between product and price because a tier is the thing customers talk about, while the price is the thing that varies by currency and billing interval.",
      "properties": [
        {
          "name": "planCode",
          "type": "string",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Stable code for the tier."
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Tier name as marketed."
        },
        {
          "name": "isPubliclyListed",
          "type": "boolean",
          "default": "true",
          "description": "False for a tier sold only by negotiation, which still needs to exist in the catalogue."
        },
        {
          "name": "trialDays",
          "type": "integer",
          "default": "0",
          "description": "Length of any free trial the tier grants by default, in days. Zero where there is none."
        }
      ],
      "x": 190,
      "y": 365
    },
    {
      "id": "n_price",
      "name": "Price",
      "labels": [
        "Price",
        "Catalogue"
      ],
      "shape": "square",
      "description": "A specific amount, in one currency, charged on one interval, for one plan. Immutable by convention: changing what a customer pays means pointing them at a different price, never editing the one they are on, because the old figure has to stay true for the invoices already issued against it.",
      "properties": [
        {
          "name": "priceId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the price."
        },
        {
          "name": "unitAmountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Amount per unit in the smallest unit of the currency — pence, cents. Integers throughout, because floating-point money accumulates error the moment anything is prorated."
        },
        {
          "name": "billingInterval",
          "type": "enum",
          "required": true,
          "default": "month",
          "description": "How often it recurs: day, week, month, year or one_time. The interval is part of the price's identity, not a setting on the subscription."
        },
        {
          "name": "intervalCount",
          "type": "integer",
          "required": true,
          "default": "1",
          "description": "Number of intervals between charges, so that a three-monthly price is month with a count of three."
        },
        {
          "name": "pricingModel",
          "type": "enum",
          "required": true,
          "default": "per_unit",
          "description": "How the amount is computed: flat_rate, per_unit, tiered or usage_based. Determines whether quantity or metered usage is even consulted."
        },
        {
          "name": "isActive",
          "type": "boolean",
          "default": "true",
          "description": "False once withdrawn. Withdrawn prices are never deleted — subscriptions and invoices point at them permanently."
        }
      ],
      "x": 190,
      "y": 520
    },
    {
      "id": "n_metered_feature",
      "name": "MeteredFeature",
      "labels": [
        "MeteredFeature",
        "Catalogue"
      ],
      "shape": "square",
      "description": "Something whose consumption is counted and charged for, or granted as an allowance — API calls, seats, gigabytes. Named separately from price so that the same meter can back a usage charge in one plan and a hard limit in another.",
      "properties": [
        {
          "name": "featureCode",
          "type": "string",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Stable code for the meter."
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Name shown to customers."
        },
        {
          "name": "unitName",
          "type": "string",
          "required": true,
          "description": "What one unit is — a call, a seat, a gigabyte. Appears on the invoice line and must be unambiguous."
        },
        {
          "name": "aggregation",
          "type": "enum",
          "required": true,
          "default": "sum",
          "description": "How usage over a period is reduced to a billable number: sum, maximum, last_value or unique_count. Two customers with identical raw usage owe different amounts under different aggregations."
        }
      ],
      "x": 190,
      "y": 675
    },
    {
      "id": "n_coupon",
      "name": "Coupon",
      "labels": [
        "Coupon",
        "Catalogue"
      ],
      "shape": "square",
      "description": "A reusable discount offer. Reference data — the offer exists independently of anyone redeeming it, which is why the redemption is a separate thing entirely.",
      "properties": [
        {
          "name": "couponCode",
          "type": "string",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Code as published or as entered at checkout."
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Internal name for the offer."
        },
        {
          "name": "percentOff",
          "type": "float",
          "description": "Percentage reduction, where the offer is proportional. Exactly one of this and amountOff is set."
        },
        {
          "name": "amountOffMinorUnits",
          "type": "integer",
          "description": "Fixed reduction in minor units, where the offer is absolute. Only meaningful together with a currency."
        },
        {
          "name": "durationMonths",
          "type": "integer",
          "description": "How many billing periods the discount survives once applied. Blank means forever, which is a commitment worth making deliberately."
        },
        {
          "name": "maximumRedemptions",
          "type": "integer",
          "description": "Cap on total redemptions across all customers. Blank for uncapped."
        }
      ],
      "x": 190,
      "y": 830
    },
    {
      "id": "n_tax_rate",
      "name": "TaxRate",
      "labels": [
        "TaxRate",
        "Catalogue"
      ],
      "shape": "square",
      "description": "A rate of tax applicable in a jurisdiction, with the dates it is in force. Reference data, and versioned rather than edited: an invoice issued last year must still show the rate that applied then.",
      "properties": [
        {
          "name": "taxRateId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the rate."
        },
        {
          "name": "displayName",
          "type": "string",
          "required": true,
          "description": "Name as it must appear on the invoice, such as VAT."
        },
        {
          "name": "percentage",
          "type": "float",
          "required": true,
          "description": "The rate itself, as a percentage."
        },
        {
          "name": "jurisdictionCode",
          "type": "string",
          "required": true,
          "indexed": true,
          "description": "Where the rate applies, which can be a country, a state or a smaller unit."
        },
        {
          "name": "isInclusive",
          "type": "boolean",
          "required": true,
          "default": "false",
          "description": "True where the tax is already inside the displayed price rather than added to it. Getting this backwards misstates every total on the invoice."
        },
        {
          "name": "effectiveFrom",
          "type": "date",
          "required": true,
          "description": "First date the rate applies."
        },
        {
          "name": "effectiveTo",
          "type": "date",
          "description": "Last date it applies. Blank while current."
        }
      ],
      "x": 190,
      "y": 985
    },
    {
      "id": "n_currency",
      "name": "Currency",
      "labels": [
        "Currency",
        "Catalogue"
      ],
      "shape": "square",
      "description": "A currency, with the precision needed to interpret every minor-unit amount in the model. Reference data, and load-bearing: an integer amount is meaningless without knowing how many decimal places its currency has.",
      "properties": [
        {
          "name": "currencyCode",
          "type": "string",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "ISO 4217 three-letter code."
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Name of the currency."
        },
        {
          "name": "minorUnitDigits",
          "type": "integer",
          "required": true,
          "default": "2",
          "description": "Decimal places the currency uses. Two for most, zero for the yen, three for the dinar — which is why amounts are stored as integers and formatted late."
        },
        {
          "name": "symbol",
          "type": "string",
          "description": "Symbol used when displaying amounts."
        }
      ],
      "x": 190,
      "y": 1140
    },
    {
      "id": "n_subscription",
      "name": "Subscription",
      "labels": [
        "Subscription"
      ],
      "shape": "circle",
      "description": "An ongoing agreement to be billed for something. Holds the relationship and its state, but deliberately not the plan: what the customer is paying for lives on the items below, because that is what changes.",
      "properties": [
        {
          "name": "subscriptionId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the agreement."
        },
        {
          "name": "status",
          "type": "enum",
          "required": true,
          "default": "active",
          "description": "Where the agreement stands: trialing, active, past_due, paused, canceled or incomplete. Distinct from whether anything is currently billable."
        },
        {
          "name": "startedAt",
          "type": "datetime",
          "required": true,
          "description": "When the agreement began."
        },
        {
          "name": "canceledAt",
          "type": "datetime",
          "description": "When cancellation was requested. Not the same as when billing stops, which is usually the end of the paid period."
        },
        {
          "name": "endedAt",
          "type": "datetime",
          "description": "When the agreement actually ended and billing ceased. Blank while live."
        },
        {
          "name": "collectionMethod",
          "type": "enum",
          "required": true,
          "default": "charge_automatically",
          "description": "How payment is taken: charge_automatically or send_invoice. Changes the entire downstream flow — one produces payment attempts, the other produces due dates and chasing."
        },
        {
          "name": "metadata",
          "type": "map",
          "description": "Free-form key/value pairs the integrating system attaches to the record. Deliberately untyped and deliberately not queried by the billing logic itself — it is the customer's data, carried along and handed back."
        }
      ],
      "x": 480,
      "y": 470
    },
    {
      "id": "n_subscription_item",
      "name": "SubscriptionItem",
      "labels": [
        "SubscriptionItem"
      ],
      "shape": "diamond",
      "description": "One priced line of a subscription, held for a stated span of time. This is the node the model exists to argue for. The instinct is to put the plan on the customer as a property; do that and the moment anyone upgrades, the old value is overwritten and the question 'what plan were they on in March?' becomes permanently unanswerable. Give the pairing a start and an end instead and the history is simply there.",
      "properties": [
        {
          "name": "subscriptionItemId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the line."
        },
        {
          "name": "quantity",
          "type": "integer",
          "required": true,
          "default": "1",
          "description": "How many units of the price are being billed — seats, licences. Belongs here rather than on the price, because the price is shared across every customer and the quantity is not."
        },
        {
          "name": "startedAt",
          "type": "datetime",
          "required": true,
          "description": "When this line became effective. The other half of the answer to what someone was paying for on a given date."
        },
        {
          "name": "endedAt",
          "type": "datetime",
          "description": "When it stopped being effective. Blank for the line currently in force, and set rather than deleted when a plan changes — the superseded line is the historical record."
        },
        {
          "name": "isTrialing",
          "type": "boolean",
          "default": "false",
          "description": "True while the line is within a trial and generating no charge. A trialing line still occupies the customer's plan history."
        },
        {
          "name": "metadata",
          "type": "map",
          "description": "Free-form key/value pairs the integrating system attaches to the record. Deliberately untyped and deliberately not queried by the billing logic itself — it is the customer's data, carried along and handed back."
        }
      ],
      "x": 720,
      "y": 470
    },
    {
      "id": "n_subscription_schedule",
      "name": "SubscriptionSchedule",
      "labels": [
        "SubscriptionSchedule"
      ],
      "shape": "circle",
      "description": "A planned sequence of future changes to a subscription — a promotional rate for three months, then the standard price. Exists so that an agreed future change is recorded when it is agreed, rather than relying on somebody remembering to make it.",
      "properties": [
        {
          "name": "scheduleId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the schedule."
        },
        {
          "name": "status",
          "type": "enum",
          "required": true,
          "default": "active",
          "description": "Where the schedule stands: not_started, active, completed, released or canceled."
        },
        {
          "name": "startsOn",
          "type": "date",
          "required": true,
          "description": "When the first phase takes effect."
        },
        {
          "name": "endBehaviour",
          "type": "enum",
          "required": true,
          "default": "release",
          "description": "What happens when the last phase finishes: release, which hands control back to the subscription, or cancel."
        }
      ],
      "x": 960,
      "y": 470
    },
    {
      "id": "n_billing_period",
      "name": "BillingPeriod",
      "labels": [
        "BillingPeriod"
      ],
      "shape": "circle",
      "description": "One span a subscription is billed for. Its own node because almost every question in billing is scoped to a period — what was owed, what was used, what was invoiced — and a pair of dates on the subscription can only ever describe the current one.",
      "properties": [
        {
          "name": "billingPeriodId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the period."
        },
        {
          "name": "startsAt",
          "type": "datetime",
          "required": true,
          "description": "First moment of the period."
        },
        {
          "name": "endsAt",
          "type": "datetime",
          "required": true,
          "description": "First moment after the period. Half-open, so consecutive periods share a boundary without overlapping — which is what stops a day being billed twice or not at all."
        },
        {
          "name": "sequenceNumber",
          "type": "integer",
          "required": true,
          "description": "Position in the subscription's run of periods, counting from one."
        },
        {
          "name": "isClosed",
          "type": "boolean",
          "default": "false",
          "description": "True once the period has been invoiced and no further usage may be attributed to it."
        }
      ],
      "x": 1200,
      "y": 470
    },
    {
      "id": "n_proration",
      "name": "Proration",
      "labels": [
        "Proration"
      ],
      "shape": "diamond",
      "description": "The adjustment produced when something changes mid-period — an upgrade partway through a month, a seat added on the eleventh day. Reified because it is neither the old line nor the new one: it is the arithmetic connecting them, and it has to appear on an invoice in its own right.",
      "properties": [
        {
          "name": "prorationId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the adjustment."
        },
        {
          "name": "amountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "The adjustment, positive for an additional charge and negative for a credit."
        },
        {
          "name": "effectiveAt",
          "type": "datetime",
          "required": true,
          "description": "The instant the change took effect, from which the remaining fraction of the period is measured."
        },
        {
          "name": "reason",
          "type": "enum",
          "required": true,
          "default": "plan_change",
          "description": "What caused it: quantity_change, price_change, plan_change or cancellation."
        },
        {
          "name": "calculationBasis",
          "type": "string",
          "description": "How the fraction was derived — by day, by second, or by a fixed convention. Two systems using different bases produce different numbers from identical inputs, which is why it is recorded rather than assumed."
        }
      ],
      "x": 1440,
      "y": 470
    },
    {
      "id": "n_usage_record",
      "name": "UsageRecord",
      "labels": [
        "UsageRecord"
      ],
      "shape": "circle",
      "description": "A reported quantity of consumption at a point in time. Kept as reported rather than as a running total, so that a late or duplicated report can be reconciled instead of silently corrupting a balance.",
      "properties": [
        {
          "name": "usageRecordId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the report."
        },
        {
          "name": "quantity",
          "type": "float",
          "required": true,
          "description": "Amount consumed, as reported."
        },
        {
          "name": "occurredAt",
          "type": "datetime",
          "required": true,
          "indexed": true,
          "description": "When the consumption happened, which decides the period it falls in."
        },
        {
          "name": "reportedAt",
          "type": "datetime",
          "required": true,
          "description": "When it reached the billing system. Later than occurredAt, sometimes by enough to arrive after the period closed."
        },
        {
          "name": "idempotencyKey",
          "type": "string",
          "unique": true,
          "indexed": true,
          "description": "Client-supplied key that makes a repeated report harmless. Without it, a retried request bills twice."
        }
      ],
      "x": 1680,
      "y": 470
    },
    {
      "id": "n_entitlement",
      "name": "Entitlement",
      "labels": [
        "Entitlement"
      ],
      "shape": "diamond",
      "description": "What a subscription actually grants access to, resolved for one customer. Reified because entitlement is a conclusion drawn from plans, add-ons and overrides rather than a fact stored on any one of them — and the application asking 'may this customer do this?' needs an answer, not a derivation.",
      "properties": [
        {
          "name": "entitlementId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the grant."
        },
        {
          "name": "includedQuantity",
          "type": "integer",
          "description": "Allowance included before any usage charge applies. Zero for a feature that is billed from the first unit."
        },
        {
          "name": "isUnlimited",
          "type": "boolean",
          "default": "false",
          "description": "True where the feature is granted without a cap, which is a different statement from a very large allowance."
        },
        {
          "name": "effectiveFrom",
          "type": "datetime",
          "required": true,
          "description": "When access begins."
        },
        {
          "name": "effectiveTo",
          "type": "datetime",
          "description": "When it ends. Blank while current."
        }
      ],
      "x": 1920,
      "y": 470
    },
    {
      "id": "n_discount",
      "name": "Discount",
      "labels": [
        "Discount"
      ],
      "shape": "diamond",
      "description": "One customer's redemption of a coupon, for a period of time. Reified for the same reason as the subscription item: the coupon is a shared offer, but when this customer redeemed it and when it runs out are facts about the pairing alone.",
      "properties": [
        {
          "name": "discountId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the redemption."
        },
        {
          "name": "appliedAt",
          "type": "datetime",
          "required": true,
          "description": "When the coupon was redeemed."
        },
        {
          "name": "expiresAt",
          "type": "datetime",
          "description": "When it stops reducing invoices. Blank for a discount that runs for the life of the subscription."
        },
        {
          "name": "remainingPeriods",
          "type": "integer",
          "description": "Billing periods still to be discounted, where the offer is limited by duration."
        }
      ],
      "x": 2160,
      "y": 470
    },
    {
      "id": "n_invoice",
      "name": "Invoice",
      "labels": [
        "Invoice"
      ],
      "shape": "circle",
      "description": "A demand for payment covering one period. Immutable once issued — corrections are made by credit note, never by editing — because an invoice is a legal document and somebody else's accounting depends on it not changing.",
      "properties": [
        {
          "name": "invoiceId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the invoice."
        },
        {
          "name": "invoiceNumber",
          "type": "string",
          "unique": true,
          "indexed": true,
          "description": "Sequential number as issued. Must be gapless in many jurisdictions, which is why it is assigned at issue and never at creation."
        },
        {
          "name": "status",
          "type": "enum",
          "required": true,
          "default": "draft",
          "description": "Where it stands: draft, open, paid, uncollectible or void. A draft may be changed; nothing else may."
        },
        {
          "name": "issuedOn",
          "type": "date",
          "description": "Date the invoice was finalised and became a demand."
        },
        {
          "name": "dueOn",
          "type": "date",
          "description": "Date payment is due, from which arrears are counted."
        },
        {
          "name": "subtotalMinorUnits",
          "type": "integer",
          "required": true,
          "default": "0",
          "description": "Total before tax and after discounts, in minor units."
        },
        {
          "name": "taxMinorUnits",
          "type": "integer",
          "required": true,
          "default": "0",
          "description": "Total tax, in minor units."
        },
        {
          "name": "totalMinorUnits",
          "type": "integer",
          "required": true,
          "default": "0",
          "description": "Amount demanded, in minor units. Stored rather than derived, because it must not change if a tax rate or a price is later corrected."
        },
        {
          "name": "customFields",
          "type": "list",
          "description": "Additional fields the customer requires on their invoices — a purchase order number, a cost centre. A list because the set differs per customer and none of it is meaningful to the billing logic."
        },
        {
          "name": "metadata",
          "type": "map",
          "description": "Free-form key/value pairs the integrating system attaches to the record. Deliberately untyped and deliberately not queried by the billing logic itself — it is the customer's data, carried along and handed back."
        }
      ],
      "x": 840,
      "y": 700
    },
    {
      "id": "n_invoice_line",
      "name": "InvoiceLine",
      "labels": [
        "InvoiceLine"
      ],
      "shape": "circle",
      "description": "One charge on an invoice. Carries its own copy of the amount and description rather than pointing at a price for them, because the invoice has to remain true even after the catalogue moves on.",
      "properties": [
        {
          "name": "invoiceLineId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the line."
        },
        {
          "name": "description",
          "type": "string",
          "required": true,
          "description": "Text as printed. Copied at issue rather than looked up, so a renamed product does not rewrite history."
        },
        {
          "name": "quantity",
          "type": "float",
          "required": true,
          "default": "1",
          "description": "Units billed on this line."
        },
        {
          "name": "unitAmountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Price per unit at the moment of issue, in minor units."
        },
        {
          "name": "amountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Line total before tax, in minor units."
        },
        {
          "name": "periodStartsAt",
          "type": "datetime",
          "description": "Start of the span this line covers, which for a proration is not the invoice's own period."
        },
        {
          "name": "periodEndsAt",
          "type": "datetime",
          "description": "End of the span this line covers."
        }
      ],
      "x": 1080,
      "y": 700
    },
    {
      "id": "n_tax_line",
      "name": "TaxLine",
      "labels": [
        "TaxLine"
      ],
      "shape": "diamond",
      "description": "Tax applied to one invoice line at one rate. Reified because a single line can attract several taxes at once — a federal and a state rate, or a reverse charge alongside a local one — so the pairing of line and rate needs somewhere to record its own amount.",
      "properties": [
        {
          "name": "taxLineId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the applied tax."
        },
        {
          "name": "amountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Tax charged on this line at this rate, in minor units."
        },
        {
          "name": "taxableAmountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "The base the rate was applied to, which is not always the line total once other taxes are involved."
        },
        {
          "name": "appliedPercentage",
          "type": "float",
          "required": true,
          "description": "Rate actually applied, copied at issue so that a later change to the tax rate cannot alter a historic invoice."
        },
        {
          "name": "isReverseCharge",
          "type": "boolean",
          "default": "false",
          "description": "True where the liability shifts to the customer and no tax is collected, which still has to appear on the invoice with an explanation."
        }
      ],
      "x": 1320,
      "y": 700
    },
    {
      "id": "n_credit_note",
      "name": "CreditNote",
      "labels": [
        "CreditNote"
      ],
      "shape": "circle",
      "description": "A document reducing what was demanded on an invoice. The only lawful way to correct an issued invoice, and the reason invoices can be immutable at all.",
      "properties": [
        {
          "name": "creditNoteId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the credit note."
        },
        {
          "name": "creditNoteNumber",
          "type": "string",
          "unique": true,
          "indexed": true,
          "description": "Sequential number as issued, in its own series."
        },
        {
          "name": "issuedOn",
          "type": "date",
          "required": true,
          "description": "Date the credit note was issued."
        },
        {
          "name": "totalMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Amount credited, in minor units."
        },
        {
          "name": "reason",
          "type": "enum",
          "required": true,
          "default": "billing_error",
          "description": "Why it was issued: duplicate, fraudulent, order_change, product_unsatisfactory or billing_error."
        },
        {
          "name": "refundedToSource",
          "type": "boolean",
          "default": "false",
          "description": "True where money went back to the payment method, false where the credit stayed on the account balance. Two very different outcomes from one document."
        }
      ],
      "x": 1560,
      "y": 700
    },
    {
      "id": "n_balance_transaction",
      "name": "BalanceTransaction",
      "labels": [
        "BalanceTransaction"
      ],
      "shape": "circle",
      "description": "A movement in the customer's running credit or debit. Every change to the balance is an entry rather than an edit, so the balance can always be explained by what produced it.",
      "properties": [
        {
          "name": "balanceTransactionId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the entry."
        },
        {
          "name": "amountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Movement in minor units, signed. Negative increases the customer's credit."
        },
        {
          "name": "occurredAt",
          "type": "datetime",
          "required": true,
          "description": "When the entry was made."
        },
        {
          "name": "reason",
          "type": "enum",
          "required": true,
          "default": "adjustment",
          "description": "What produced it: credit_note, adjustment, unapplied_payment, migration or write_off."
        },
        {
          "name": "runningBalanceMinorUnits",
          "type": "integer",
          "description": "Balance after this entry, in minor units. Denormalised so the balance can be read without replaying every entry, and reconciled rather than trusted."
        }
      ],
      "x": 1800,
      "y": 700
    },
    {
      "id": "n_payment_method",
      "name": "PaymentMethod",
      "labels": [
        "PaymentMethod"
      ],
      "shape": "circle",
      "description": "A stored instrument the customer can be charged against. Holds only what is safe to keep: the real details live with the payment processor and never here.",
      "properties": [
        {
          "name": "paymentMethodId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the instrument."
        },
        {
          "name": "methodType",
          "type": "enum",
          "required": true,
          "default": "card",
          "description": "Kind of instrument: card, bank_debit, bank_transfer or wallet. Determines how long a failure takes to surface — a card fails in seconds, a direct debit can fail a week later."
        },
        {
          "name": "lastFourDigits",
          "type": "string",
          "description": "Final four digits, kept for the customer to recognise it by. The only part of the number that may be stored."
        },
        {
          "name": "expiresOn",
          "type": "date",
          "description": "Expiry, where the instrument has one. A large share of involuntary churn is nothing more than this date passing unnoticed."
        },
        {
          "name": "isDefault",
          "type": "boolean",
          "default": "false",
          "description": "True for the instrument charged unless another is specified."
        },
        {
          "name": "addedAt",
          "type": "datetime",
          "required": true,
          "description": "When the instrument was stored."
        }
      ],
      "x": 600,
      "y": 930
    },
    {
      "id": "n_mandate",
      "name": "Mandate",
      "labels": [
        "Mandate"
      ],
      "shape": "circle",
      "description": "The customer's standing authorisation to be debited. Separate from the instrument because the permission has its own lifecycle: it is granted, evidenced, and can be withdrawn while the instrument remains perfectly valid.",
      "properties": [
        {
          "name": "mandateId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the authorisation."
        },
        {
          "name": "reference",
          "type": "string",
          "required": true,
          "unique": true,
          "description": "Mandate reference as given to the customer and to the bank."
        },
        {
          "name": "scheme",
          "type": "enum",
          "required": true,
          "default": "card_on_file",
          "description": "Rules the mandate operates under: sepa_direct_debit, bacs, ach or card_on_file. Each carries different notice periods and different dispute rights."
        },
        {
          "name": "acceptedAt",
          "type": "datetime",
          "required": true,
          "description": "When the customer granted it, which must be evidenced to defend a disputed debit."
        },
        {
          "name": "status",
          "type": "enum",
          "required": true,
          "default": "active",
          "description": "Whether it may still be used: pending, active, inactive or revoked."
        }
      ],
      "x": 840,
      "y": 930
    },
    {
      "id": "n_payment_intent",
      "name": "PaymentIntent",
      "labels": [
        "PaymentIntent"
      ],
      "shape": "circle",
      "description": "One attempt to collect a specific amount, tracked from initiation to outcome. Modelled apart from the charge because an attempt can require authentication, be abandoned, or fail without any money moving — and all of that is worth knowing.",
      "properties": [
        {
          "name": "paymentIntentId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the attempt."
        },
        {
          "name": "amountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Amount being collected, in minor units."
        },
        {
          "name": "status",
          "type": "enum",
          "required": true,
          "default": "processing",
          "description": "Where the attempt stands: requires_action, processing, succeeded or failed."
        },
        {
          "name": "initiatedAt",
          "type": "datetime",
          "required": true,
          "description": "When collection was started."
        },
        {
          "name": "failureCode",
          "type": "string",
          "description": "Processor's reason for a failure. A soft decline is worth retrying and a hard one is not, and the difference is only in this field."
        },
        {
          "name": "requiresAuthentication",
          "type": "boolean",
          "default": "false",
          "description": "True where the customer had to complete a challenge. Attempts needing one cannot be made while the customer is absent, which is why renewals are set up differently from first payments."
        }
      ],
      "x": 1080,
      "y": 930
    },
    {
      "id": "n_charge",
      "name": "Charge",
      "labels": [
        "Charge"
      ],
      "shape": "circle",
      "description": "Money actually taken. The point at which an attempt becomes a fact, and the anchor for everything that can happen afterwards.",
      "properties": [
        {
          "name": "chargeId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the charge."
        },
        {
          "name": "amountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Amount captured, in minor units. Can be less than the intent's amount where a partial capture was taken."
        },
        {
          "name": "capturedAt",
          "type": "datetime",
          "required": true,
          "description": "When the funds were captured."
        },
        {
          "name": "processorReference",
          "type": "string",
          "indexed": true,
          "description": "The processor's own identifier, which is what any investigation will be conducted in terms of."
        },
        {
          "name": "isRefundable",
          "type": "boolean",
          "default": "true",
          "description": "Whether the scheme still permits a refund. Refund windows close, and after that a credit note is the only remedy."
        }
      ],
      "x": 1320,
      "y": 930
    },
    {
      "id": "n_refund",
      "name": "Refund",
      "labels": [
        "Refund"
      ],
      "shape": "circle",
      "description": "Money returned to the customer against a charge. Distinct from a credit note: one moves funds, the other adjusts what was owed, and a correction frequently needs both.",
      "properties": [
        {
          "name": "refundId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the refund."
        },
        {
          "name": "amountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Amount returned, in minor units. May be partial, and several refunds can be taken against one charge."
        },
        {
          "name": "initiatedAt",
          "type": "datetime",
          "required": true,
          "description": "When the refund was requested."
        },
        {
          "name": "settledAt",
          "type": "datetime",
          "description": "When the funds reached the customer, which can be days later and is what the customer actually experiences."
        },
        {
          "name": "reason",
          "type": "enum",
          "required": true,
          "default": "requested_by_customer",
          "description": "Why it was made: requested_by_customer, duplicate, fraudulent or service_failure."
        }
      ],
      "x": 1560,
      "y": 930
    },
    {
      "id": "n_dispute",
      "name": "Dispute",
      "labels": [
        "Dispute"
      ],
      "shape": "circle",
      "description": "A customer's challenge to a charge through their bank. Its own node because it runs on the scheme's timetable rather than the business's, carries evidence and deadlines, and can reverse a payment long after everyone considered it settled.",
      "properties": [
        {
          "name": "disputeId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the dispute."
        },
        {
          "name": "amountMinorUnits",
          "type": "integer",
          "required": true,
          "description": "Amount under challenge, in minor units."
        },
        {
          "name": "raisedOn",
          "type": "date",
          "required": true,
          "description": "Date the challenge was received."
        },
        {
          "name": "evidenceDueBy",
          "type": "date",
          "description": "Deadline for responding. Missing it concedes the dispute regardless of the merits, which is why it is a first-class field."
        },
        {
          "name": "status",
          "type": "enum",
          "required": true,
          "default": "needs_response",
          "description": "Where it stands: needs_response, under_review, won or lost."
        },
        {
          "name": "reasonCode",
          "type": "string",
          "description": "The scheme's own code for the challenge, which dictates what evidence will be accepted."
        }
      ],
      "x": 1800,
      "y": 930
    },
    {
      "id": "n_dunning_attempt",
      "name": "DunningAttempt",
      "labels": [
        "DunningAttempt"
      ],
      "shape": "diamond",
      "description": "One try at collecting an overdue invoice, with its outcome. Reified because a single invoice is retried on a schedule, and the sequence — how many tries, how far apart, what was said to the customer each time — is the process rather than an attribute of the invoice.",
      "properties": [
        {
          "name": "dunningAttemptId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the attempt."
        },
        {
          "name": "attemptNumber",
          "type": "integer",
          "required": true,
          "description": "Position in the retry sequence, counting from one."
        },
        {
          "name": "scheduledFor",
          "type": "datetime",
          "required": true,
          "description": "When the attempt was due to run. Retry timing is tuned around when balances are topped up, and moving it changes recovery rates more than most other levers."
        },
        {
          "name": "outcome",
          "type": "enum",
          "required": true,
          "default": "pending",
          "description": "What happened: pending, succeeded, failed or abandoned."
        },
        {
          "name": "notificationSent",
          "type": "boolean",
          "default": "false",
          "description": "Whether the customer was told. A silent retry that fails teaches nobody anything."
        }
      ],
      "x": 2040,
      "y": 930
    },
    {
      "id": "n_event",
      "name": "Event",
      "labels": [
        "Event"
      ],
      "shape": "circle",
      "description": "A record that something happened, kept so that downstream systems can be told. Separate from the change itself because the notification has its own delivery problem, and losing a webhook must not mean losing the fact.",
      "properties": [
        {
          "name": "eventId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the event."
        },
        {
          "name": "eventType",
          "type": "string",
          "required": true,
          "indexed": true,
          "description": "What occurred, such as invoice.payment_failed. The string integrators subscribe to, so it is part of the public contract and cannot be renamed casually."
        },
        {
          "name": "occurredAt",
          "type": "datetime",
          "required": true,
          "description": "When the underlying change happened, as opposed to when anyone was told."
        },
        {
          "name": "objectType",
          "type": "string",
          "description": "Kind of record the event concerns. A deliberate simplification: a fully general model would link the event to any node, which no schema can express directly."
        },
        {
          "name": "payloadVersion",
          "type": "string",
          "description": "Version of the payload shape sent to subscribers, because the contract evolves and old subscribers must keep working."
        }
      ],
      "x": 1080,
      "y": 1160
    },
    {
      "id": "n_webhook_endpoint",
      "name": "WebhookEndpoint",
      "labels": [
        "WebhookEndpoint"
      ],
      "shape": "circle",
      "description": "A destination an integrator has registered to receive events. Its own node because endpoints have their own health: one failing endpoint should not hold up anyone else's delivery.",
      "properties": [
        {
          "name": "endpointId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the endpoint."
        },
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "Where deliveries are posted."
        },
        {
          "name": "subscribedEventTypes",
          "type": "list",
          "description": "Event types this endpoint wants. A list because it is a set the integrator chooses, and matching is the only thing done with it."
        },
        {
          "name": "isEnabled",
          "type": "boolean",
          "default": "true",
          "description": "False once the endpoint has been disabled, automatically after sustained failure or by hand."
        },
        {
          "name": "signingSecret",
          "type": "string",
          "description": "Shared secret used to sign deliveries so the receiver can verify they are genuine. Stored as a reference to a secret, never as the secret itself."
        }
      ],
      "x": 1320,
      "y": 1160
    },
    {
      "id": "n_webhook_delivery",
      "name": "WebhookDelivery",
      "labels": [
        "WebhookDelivery"
      ],
      "shape": "diamond",
      "description": "One attempt to deliver one event to one endpoint. Reified because the same event goes to many endpoints and each is retried independently — the pairing has its own outcome, its own retry count, and its own eventual failure.",
      "properties": [
        {
          "name": "deliveryId",
          "type": "uuid",
          "key": true,
          "required": true,
          "unique": true,
          "indexed": true,
          "description": "Surrogate identifier for the attempt."
        },
        {
          "name": "attemptNumber",
          "type": "integer",
          "required": true,
          "default": "1",
          "description": "Which try this is, counting from one."
        },
        {
          "name": "attemptedAt",
          "type": "datetime",
          "required": true,
          "description": "When the request was sent."
        },
        {
          "name": "responseStatusCode",
          "type": "integer",
          "description": "HTTP status returned, where a response arrived at all."
        },
        {
          "name": "outcome",
          "type": "enum",
          "required": true,
          "default": "pending",
          "description": "How it ended: pending, delivered, failed or exhausted. Exhausted means retries stopped, which is when a human needs to know."
        },
        {
          "name": "durationMs",
          "type": "integer",
          "description": "How long the receiver took. A consistently slow endpoint is throttled, so this drives behaviour rather than merely being observed."
        }
      ],
      "x": 1560,
      "y": 1160
    }
  ],
  "relationships": [
    {
      "type": "BELONGS_TO",
      "from": "n_customer",
      "to": "n_organisation",
      "cardinality": "n-1",
      "description": "The organisation legally responsible for a billing account. Many-to-one, because one company routinely holds several accounts and asking what it owes in total is a question the model should be able to answer."
    },
    {
      "type": "BILLED_AT",
      "from": "n_customer",
      "to": "n_address",
      "cardinality": "n-1",
      "description": "The address that decides the customer's tax treatment and appears on their invoices."
    },
    {
      "type": "HOLDS",
      "from": "n_organisation",
      "to": "n_tax_registration",
      "cardinality": "1-n",
      "description": "Tax registrations the organisation has declared. One-to-many because a company trading across borders registers in each place it must."
    },
    {
      "type": "FOR_PRODUCT",
      "from": "n_plan",
      "to": "n_product",
      "cardinality": "n-1",
      "description": "The product a tier belongs to."
    },
    {
      "type": "FOR_PLAN",
      "from": "n_price",
      "to": "n_plan",
      "cardinality": "n-1",
      "description": "The tier a price implements. Many-to-one, and heavily so: one tier carries a price per currency and per billing interval."
    },
    {
      "type": "DENOMINATED_IN",
      "from": "n_price",
      "to": "n_currency",
      "cardinality": "n-1",
      "description": "The currency a price is expressed in, and therefore how its minor-unit amount must be read."
    },
    {
      "type": "METERS",
      "from": "n_price",
      "to": "n_metered_feature",
      "cardinality": "n-1",
      "description": "The feature a usage-based price charges for. Absent on flat-rate prices, which is the usual case."
    },
    {
      "type": "FOR_CUSTOMER",
      "from": "n_subscription",
      "to": "n_customer",
      "cardinality": "n-1",
      "description": "The account a subscription bills. One customer can hold several concurrently."
    },
    {
      "type": "CONTAINS",
      "from": "n_subscription",
      "to": "n_subscription_item",
      "cardinality": "1-n",
      "description": "The priced lines making up a subscription, past and present. One-to-many over time as well as at any instant — a subscription that has changed plan twice has three lines, only one of them current, and that is the whole mechanism by which its history is preserved."
    },
    {
      "type": "PRICED_AT",
      "from": "n_subscription_item",
      "to": "n_price",
      "cardinality": "n-1",
      "description": "The price a line is billed at. Many-to-one and never edited: moving a customer to a different price means ending this line and starting another, which is what keeps the old invoices explicable."
    },
    {
      "type": "HAS_PERIOD",
      "from": "n_subscription",
      "to": "n_billing_period",
      "cardinality": "1-n",
      "description": "Every period the subscription has been billed for, in sequence."
    },
    {
      "type": "CURRENT_PERIOD",
      "from": "n_subscription",
      "to": "n_billing_period",
      "cardinality": "1-1",
      "description": "The period the subscription is in right now. Genuinely one-to-one — a subscription has exactly one current period and a period is current for exactly one subscription — and it exists alongside HAS_PERIOD because 'now' is asked for constantly and should not require scanning the history."
    },
    {
      "type": "GOVERNS",
      "from": "n_subscription_schedule",
      "to": "n_subscription",
      "cardinality": "1-1",
      "description": "The subscription a schedule controls. One-to-one: a schedule drives a single subscription, and a subscription is driven by at most one."
    },
    {
      "type": "TRANSITIONS_TO",
      "from": "n_subscription_schedule",
      "to": "n_price",
      "cardinality": "n-n",
      "description": "Prices a schedule will move the subscription onto, in its phases. Many-to-many because a schedule has several phases and a price appears in many schedules."
    },
    {
      "type": "FOR_ITEM",
      "from": "n_usage_record",
      "to": "n_subscription_item",
      "cardinality": "n-1",
      "description": "The billed line a usage report is attributed to."
    },
    {
      "type": "MEASURES",
      "from": "n_usage_record",
      "to": "n_metered_feature",
      "cardinality": "n-1",
      "description": "The meter a report counts against."
    },
    {
      "type": "GRANTED_BY",
      "from": "n_entitlement",
      "to": "n_subscription",
      "cardinality": "n-1",
      "description": "The subscription an entitlement derives from. Many-to-one, since one subscription grants access to several features at once."
    },
    {
      "type": "FOR_FEATURE",
      "from": "n_entitlement",
      "to": "n_metered_feature",
      "cardinality": "n-1",
      "description": "The feature access is granted to."
    },
    {
      "type": "REDEEMS",
      "from": "n_discount",
      "to": "n_coupon",
      "cardinality": "n-1",
      "description": "The offer a discount is a redemption of. Many-to-one: one coupon is redeemed by many customers, and the shared offer must not carry any one customer's dates."
    },
    {
      "type": "REDUCES",
      "from": "n_discount",
      "to": "n_subscription",
      "cardinality": "n-1",
      "description": "The subscription a redemption reduces the invoices of."
    },
    {
      "type": "ARISES_FROM",
      "from": "n_proration",
      "to": "n_subscription_item",
      "cardinality": "n-1",
      "description": "The line whose change produced the adjustment. A single change usually yields two prorations — a credit against the old line and a charge for the new — and both point at their own line."
    },
    {
      "type": "BILLED_ON",
      "from": "n_proration",
      "to": "n_invoice",
      "cardinality": "n-1",
      "description": "The invoice an adjustment was settled on, which is normally the next one issued rather than a new one."
    },
    {
      "type": "FOR_CUSTOMER",
      "from": "n_invoice",
      "to": "n_customer",
      "cardinality": "n-1",
      "description": "The account an invoice is addressed to."
    },
    {
      "type": "FOR_SUBSCRIPTION",
      "from": "n_invoice",
      "to": "n_subscription",
      "cardinality": "n-1",
      "description": "The subscription an invoice bills, where it bills one. Not every invoice does — one-off charges exist."
    },
    {
      "type": "COVERS",
      "from": "n_invoice",
      "to": "n_billing_period",
      "cardinality": "n-1",
      "description": "The period an invoice bills for. Distinct from when it was issued, and the pairing that makes 'what did we charge for March' answerable."
    },
    {
      "type": "HAS_LINE",
      "from": "n_invoice",
      "to": "n_invoice_line",
      "cardinality": "1-n",
      "description": "The charges making up an invoice."
    },
    {
      "type": "DENOMINATED_IN",
      "from": "n_invoice",
      "to": "n_currency",
      "cardinality": "n-1",
      "description": "The currency an invoice is issued in. Fixed at issue, because a later exchange-rate change must not restate a document somebody has already paid."
    },
    {
      "type": "FOR_ITEM",
      "from": "n_invoice_line",
      "to": "n_subscription_item",
      "cardinality": "n-1",
      "description": "The subscription line a charge came from, where it came from one. Blank for a proration line or a one-off charge."
    },
    {
      "type": "TAXED_BY",
      "from": "n_invoice_line",
      "to": "n_tax_line",
      "cardinality": "1-n",
      "description": "Taxes applied to a line. One-to-many because a single charge can attract more than one tax at once, which is why the applied tax needs its own record."
    },
    {
      "type": "AT_RATE",
      "from": "n_tax_line",
      "to": "n_tax_rate",
      "cardinality": "n-1",
      "description": "The rate an applied tax was calculated from. The percentage is copied onto the tax line as well, so a later correction to the rate cannot alter a historic invoice."
    },
    {
      "type": "ADJUSTS",
      "from": "n_credit_note",
      "to": "n_invoice",
      "cardinality": "n-1",
      "description": "The invoice a credit note corrects. Many-to-one, since an invoice can be credited more than once and in part."
    },
    {
      "type": "POSTS",
      "from": "n_credit_note",
      "to": "n_balance_transaction",
      "cardinality": "1-n",
      "description": "Balance entries a credit note produced, where the credit stayed on account rather than being refunded to source."
    },
    {
      "type": "FOR_CUSTOMER",
      "from": "n_balance_transaction",
      "to": "n_customer",
      "cardinality": "n-1",
      "description": "The account whose balance moved."
    },
    {
      "type": "BELONGS_TO",
      "from": "n_payment_method",
      "to": "n_customer",
      "cardinality": "n-1",
      "description": "The account an instrument is stored against."
    },
    {
      "type": "AUTHORISED_BY",
      "from": "n_payment_method",
      "to": "n_mandate",
      "cardinality": "n-1",
      "description": "The standing authorisation permitting the instrument to be debited. Without a live mandate an instrument may be perfectly valid and still unusable."
    },
    {
      "type": "SETTLES",
      "from": "n_payment_intent",
      "to": "n_invoice",
      "cardinality": "n-1",
      "description": "The invoice a collection attempt is for. Many-to-one, because a failed attempt is followed by another and both belong to the same invoice."
    },
    {
      "type": "USES",
      "from": "n_payment_intent",
      "to": "n_payment_method",
      "cardinality": "n-1",
      "description": "The instrument an attempt was made against."
    },
    {
      "type": "FULFILS",
      "from": "n_charge",
      "to": "n_payment_intent",
      "cardinality": "n-1",
      "description": "The attempt a charge resulted from. The boundary between trying to collect and having collected."
    },
    {
      "type": "REVERSES",
      "from": "n_refund",
      "to": "n_charge",
      "cardinality": "n-1",
      "description": "The charge money was returned against. Many-to-one, since a charge can be refunded in several parts."
    },
    {
      "type": "CONTESTS",
      "from": "n_dispute",
      "to": "n_charge",
      "cardinality": "n-1",
      "description": "The charge a customer has challenged through their bank."
    },
    {
      "type": "RETRIES",
      "from": "n_dunning_attempt",
      "to": "n_invoice",
      "cardinality": "n-1",
      "description": "The overdue invoice an attempt is chasing. Many-to-one by design: the sequence of attempts against one invoice is the dunning process."
    },
    {
      "type": "CREATED",
      "from": "n_dunning_attempt",
      "to": "n_payment_intent",
      "cardinality": "n-1",
      "description": "The collection attempt a dunning run initiated, where it got that far."
    },
    {
      "type": "CONCERNS",
      "from": "n_event",
      "to": "n_customer",
      "cardinality": "n-1",
      "description": "The account an event relates to. A simplification, and flagged as one: events genuinely concern many kinds of record, and a schema cannot express a relationship to any node."
    },
    {
      "type": "DELIVERS",
      "from": "n_webhook_delivery",
      "to": "n_event",
      "cardinality": "n-1",
      "description": "The event a delivery attempt is carrying."
    },
    {
      "type": "TO_ENDPOINT",
      "from": "n_webhook_delivery",
      "to": "n_webhook_endpoint",
      "cardinality": "n-1",
      "description": "The destination a delivery was addressed to."
    },
    {
      "type": "REGISTERED_BY",
      "from": "n_webhook_endpoint",
      "to": "n_organisation",
      "cardinality": "n-1",
      "description": "The organisation that registered an endpoint and is accountable for it."
    }
  ]
}
