Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Billing profiles

Two distinct objects hold the billing-side identity of every invoice:

  • tenant_billing_profiles — the buyer. One row per tenant. Carries company name, address, EU VAT number, contact email, preferred language, and the names of the templates this tenant gets.
  • partner_configuration — the seller. One row per partner. Carries legal name, address, VAT, bank details (IBAN/BIC), and the default templates the partner's tenants inherit.

At invoice finalisation time, both rows are snapshotted into the invoice itself (buyer_snapshot and seller_snapshot JSON columns) so the invoice stays immutable even if either profile is edited later.

Tenant billing profile#

The full schema:

carbon
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
tenant_billing_profiles
  tenant_id (PK in spirit, unique),
  company_name,
  address_line1, address_line2,
  postal_code, city, state, country_code,
  vat_number,
  tax_exempt (bool),
  contact_email, phone,
  is_business (bool),
  peppol_id, peppol_scheme,           -- for Peppol e-invoicing
  leitweg_id,                          -- for German XRechnung
  preferred_einvoice_format,           -- ubl | xrechnung | cii | zugferd | facturx
  preferred_language,                  -- en | nl | de | fr | es | it
  invoice_template_name,               -- which designer template (see Templates)
  email_template_name,                 -- which email template
  buyer_reference,                     -- customer-side PO/reference for invoices
  created_at, updated_at

A tenant can perform any commerce action (subscribe, get an invoice) only if this profile exists. Triggering its first creation emits the tenant.billing_linked.v1 event; subsequent edits emit tenant.billing_updated.v1 with changed_fields[].

Endpoints:

  • Self-service (the tenant edits its own): GET/PUT /billing/billing-profile — see Billing (tenant).
  • Admin: PUT /admin/tenants/{tenant_id}/billing-profile — see Admin — tenants.

Partner configuration (seller side)#

carbon
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
partner_configuration
  partner_id,
  partner_name,
  billing_model,                       -- pass_through | wholesale | self_billed
  commission_rate,                     -- decimal % for reseller revenue split
  allowed_services, allowed_plans,
  branding,                            -- JSON: theme, logo URL, footer text, etc.
  default_plan_slug,
  provider_config,
  invoice_template_name, email_template_name,
  legal_name,                          -- "ScaiLabs B.V."
  seller_address_line1, seller_address_line2,
  seller_postal_code, seller_city, seller_country_code,
  seller_vat_number,
  seller_email, seller_phone,
  invoice_logo_url, invoice_footer_text,
  seller_peppol_id, seller_peppol_scheme,
  ...

A partner is considered billable once legal_name AND seller_country_code are both set. The first time those fields cross from null → set, partner.billing_linked.v1 fires. Subsequent changes to any seller field emit partner.billing_updated.v1 with changed_fields[].

Endpoint: PATCH /admin/partners/{partner_id} — see Admin — partners.

Snapshots at finalisation#

When an invoice is finalised:

  1. The tenant's billing profile is copied into invoices.buyer_snapshot (JSON).
  2. The partner's relevant seller fields are copied into invoices.seller_snapshot.
  3. The VAT determination is recomputed and frozen into invoices.vat_details.

After that point, edits to the tenant profile do NOT propagate. Generating a credit note from the same invoice will reuse the snapshots, ensuring downstream documents reference the original buyer/seller exactly as they were at the time.

If the tenant moves countries, gets a new VAT number, etc., the next invoice picks up the new state, but the existing ones stay correct for audit purposes.

Template assignment#

Both invoice templates and email templates resolve through a two-step chain at render time:

  1. Try the tenant's assigned template (tenant_billing_profiles.invoice_template_name / .email_template_name).
  2. Fall back to the partner's assigned template (partner_configuration.invoice_template_name / .email_template_name).
  3. Fall back to the built-in filesystem default.

Each step also tries the tenant's preferred_language first, falling back to the any-language template variant. See Templates.

E-invoicing identifiers#

Several optional fields on the buyer side cover European e-invoicing requirements:

  • Peppol — used in the Netherlands, Nordics, Belgium. peppol_id + peppol_scheme together identify the receiver in the Peppol network.
  • Leitweg-ID — required for German XRechnung-format invoices to public-sector buyers.
  • preferred_einvoice_formatubl, xrechnung, cii, zugferd, facturx, or null for PDF-only. Drives the format of the XML attachment shipped alongside the PDF.

The seller side mirrors these so the operator (or reseller) can act as an e-invoicing sender.

Editing a billing profile mid-stream#

Profile edits are non-blocking — any in-flight subscription continues, and the next invoice picks up the new buyer info. The only special case is country_code, which changes VAT treatment (domestic ↔ intra-EU ↔ export). The next finalisation will recompute. If you need to correct a previously-issued invoice for a buyer-side error, issue a credit note and a fresh invoice — never edit a finalised one.

Updated 2026-05-18 01:48:39 View source (.md) rev 2