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

Changelog

Notable changes by milestone. ScaiControl uses migration numbers (001022+) as its versioning anchor; release tags follow the migration that bumped the schema.

v1.x (current)#

Outbound webhooks (migration 022)#

Two-table durable event-outbox pattern. 14 lifecycle topics ship in v1.0:

  • tenant.billing_linked.v1, tenant.billing_updated.v1
  • partner.billing_linked.v1, partner.billing_updated.v1
  • subscription.activated.v1, subscription.changed.v1, subscription.cancelled.v1, subscription.suspended.v1, subscription.resumed.v1, subscription.trial_ending.v1, subscription.payment_failed.v1
  • pack_subscription.activated.v1, pack_subscription.changed.v1 (reserved), pack_subscription.cancelled.v1

HMAC-SHA256 signing, 1m/5m/30m/2h/12h/24h backoff schedule, (subscription_id, idempotency_key) uniqueness, admin UI at /admin/webhook-subscriptions. See Concepts: webhooks.

Email template system (migration 020)#

Operator-customisable email templates per (name, document_type, language) triple. GrapesJS designer reused from invoices; Jinja syntax encoded as HTML comments (<!--JS:if-->) so the designer doesn't choke on {% %} tags. Variables differ per document_type (invoice-sent vs trial-ending vs payment-failed). Validated by scaicontrol admin templates validate.

Service packs (migration 014)#

Bundled subscriptions modeled as a parent PackSubscription linked to child Subscription rows. Pricing can be fixed_price (override the sum) or percentage (discount off the catalog sum). Lifecycle propagates parent → children atomically. Pack-level events fire instead of per-child events. See Concepts: service packs.

Identity sync (migration 013)#

scaicontrol sync pulls users + groups from ScaiKey, merging with local role assignments. Resolved the "super-admin downgrade after JWT refresh" issue: ScaiControl now re-fetches the canonical permission set from /auth/me on each token refresh.

Template designer (migration 012)#

GrapesJS-based WYSIWYG editor for invoice + credit-note templates, including a live preview pane that renders the same WeasyPrint pipeline used for production PDFs. Templates support partner-specific overrides AND language fallbacks. Blogger Sans is embedded as base64 so WeasyPrint doesn't need to fetch fonts at render time.

Accounting integrations (migration 011)#

Plugin architecture for outbound accounting sync. First implementation: Visma e-Accounting (Sweden/Norway/Finland). Hooks fire on invoice finalize → mirrored to the external accounting system. Failures don't roll back the invoice — they queue for retry.

EU-compliant invoicing (migration 008)#

The biggest milestone. Adds:

  • tenant_billing_profiles — buyer EU address + VAT.
  • partner_configuration extensions — seller EU address + VAT + bank.
  • invoice_templates table with partner-specific and language-specific overrides.
  • invoices.document_type (invoice | credit_note) and invoices.referenced_invoice_id.
  • Buyer + seller + VAT-details snapshots frozen on finalize.
  • VAT determination (services/billing/vat.py): 4 rules covering domestic, intra-EU B2B reverse-charge per Art. 196 EU VAT Directive, intra-EU B2C, and non-EU export.
  • WeasyPrint-rendered PDFs in S3 keyed by invoice ID; template_html_snapshot for byte-stable reprints.
  • CreditNoteSequence for separate gap-free SCAI-CN-YYYY-NNNNNN numbering.
  • E-invoicing in migration 009 (UBL / XRechnung / CII / ZUGFeRD / Factur-X).
  • Per-line tax in migration 010 (replacing single invoice-level rate).

See Concepts: invoice lifecycle, Concepts: VAT & reverse charge.

Cost-based metering (migration 007)#

Cost-derived plans where the unit price is computed from a per-unit cost + margin instead of fixed. Subscriptions inherit a billable rate updated on plan refresh; usage records reference the rate at ingestion time.

Service registry (migration 006)#

Parent-slug relationships for service hierarchy (e.g. scaicontrol-billing is a sub-component of scaicontrol). Health monitoring split from approval state — health_status is now derived continuously from heartbeats independent of registration_status.

Users + groups (migrations 003–005)#

Identity reflection from ScaiKey: users, groups, and user_groups populated by scaicontrol sync. Role bundles defined locally on groups; effective permissions = union of (user.role scopes ∪ groups.role scopes).

Marketplace (migration 002)#

Service catalog: services, plans, foundational subscription tables.

Initial schema (migration 001)#

Tenancy hierarchy (partners, tenants, users), audit log, basic ORM-layer tenant filter via do_orm_execute.

Forward-looking#

Topics deferred past v1.x — these features exist conceptually but have no producer wired up yet:

  • Invoice eventsinvoice.issued, invoice.paid, invoice.overdue. Will land when ScaiCRM needs them for AR automation.
  • Dunning escalation events — surfacing the past_due → suspended path to subscribers.
  • Usage aggregate events — daily and monthly usage roll-ups as topic streams.
  • Catalog product events — when a plan price changes, when a service is added/retired.
  • Provisioning rejection events — explicit signal when a DAG step's compensating rollback completes.
  • Plan-change service tokens — service-to-service tokens with a scaicontrol:admin scope for CRM-driven plan changes.

Versioning policy#

  • ScaiControl's external API is versioned at the URL (/api/v1/). Within v1, only additive changes ship — no field removals, no semantics shifts.
  • Outbound event topics are versioned at the topic name (subscription.activated.v1). Breaking shape changes ship a new major (v2); both versions coexist for at least one full quarter before v1 retires.
  • Migrations are numbered sequentially and never re-numbered. Once a migration is in main, it's append-only — corrections ship as later migrations.
Updated 2026-05-18 01:48:39 View source (.md) rev 2