Sign in →

Billing Entities

Configure the legal entities that issue your invoices — base currency, tax registration, address, billing timezone, and invoice numbering — and designate a default.

Updated 2026-06-15Suggest edits

Billing Entities

Billing Entities (Settings → Billing Entities) manages the legal entities that issue your invoices. Multi-entity support lets a parent organization operate several subsidiaries — Acme US Inc., Acme EU GmbH, Acme Japan K.K. — each with its own currency, tax jurisdiction, billing timezone, address, and invoice numbering sequence.

Every subscription is bound to exactly one billing entity. That entity determines:

  • Which currency the invoice is issued in
  • Which tax jurisdiction applies
  • What "midnight" means for billing-period boundaries (per-entity timezone, added in V46)
  • What the invoice number looks like (prefix + format + sequence)
  • Which branding and footer text the customer sees on the rendered invoice

When you need multiple entities

You only need one entity if you operate as a single legal organization. Spin up a second (or third) entity when any of these apply:

  • You've incorporated a foreign subsidiary that issues invoices in its local currency
  • You file taxes in more than one jurisdiction
  • You want different invoice numbering streams per region (e.g. US-2026-0001 vs EU-2026-0001)
  • Customer time-of-day matters for your billing cycle (a Tokyo customer expects their month to close at midnight JST, not 09:00 JST)

Entity fields

FieldNotes
Entity code (immutable)Short uppercase identifier (e.g. US, EU, JP). Anchors the invoice numbering sequence — cannot be changed after creation.
Display nameOperator-facing label (e.g. "Acme US Inc.").
Legal nameGoes on every invoice's "From" block (e.g. "Acme United States Incorporated").
Base currency (immutable)ISO 4217 (USD, EUR, GBP, JPY, …). Anchors the GL ledger — cannot be changed after creation.
Reporting currencyOptional second currency for consolidated reporting.
Country codeISO 3166 alpha-2 (US, DE, JP).
Billing timezoneIANA timezone id (e.g. America/Los_Angeles, Asia/Tokyo, Europe/Berlin). See below.
Tax registration numberVAT, EIN, GST — whichever your tax authority assigns.
Tax jurisdictionFree-form (e.g. US-CA, IE-VAT, JP-CIT).
AddressLine 1 (optional line 2), city, region/state, postal code. Rendered as the "From" block on every invoice issued by this entity.
Invoice number prefixDefault INV. Combined with the format string to build invoice numbers.
Invoice number formatTokens: {prefix}, {seq:0000}, {YYYY}, {MM} (e.g. {prefix}-{YYYY}-{seq:0000}INV-2026-0001).
Logo URLOptional per-entity logo for invoice branding. Falls back to the org-level logo when blank.
Footer textOptional invoice footer (e.g. "Thank you for your business — questions? billing@acme.com").
Is defaultThe entity used when an offering doesn't pin one explicitly. Exactly one entity per tenant is default.

Billing timezone

Added in V46 (2026-06-14). Every entity has an IANA timezone that defines what "midnight" means for that entity's subscriptions.

Why per-entity, not per-tenant? Because a multi-entity tenant likely has customers in multiple regions. A Tokyo-issuing entity expects its monthly billing periods to close at 00:00 JST — not at 00:00 UTC (which is 09:00 JST). Closing a period 9 hours early surprises the customer and creates messy proration.

Common timezonesWhen to pick
UTCDefault. Use if you want billing in coordinated universal time.
America/Los_AngelesUS West Coast subsidiary (PT, observes DST).
America/New_YorkUS East Coast subsidiary (ET, observes DST).
Europe/BerlinCentral European subsidiary (CET/CEST).
Asia/TokyoJapan subsidiary (JST, no DST).
Asia/KolkataIndia subsidiary (IST, no DST).
Australia/SydneyAustralia subsidiary (AEST/AEDT).

How the timezone propagates

  1. When you create or update an entity, the timezone is validated against the JDK's IANA registry. Unknown IDs reject with HTTP 400 + a friendly message (Unknown timezone 'Asia/MarsCity'. Expected an IANA zone id (e.g. 'America/Los_Angeles', 'Asia/Tokyo', 'Europe/Berlin').).
  2. When a subscription is created against the entity, the timezone is pinned onto the subscription row (denormalized so the billing-period math doesn't need a cross-service RPC on every read).
  3. Pricing-service uses the pinned timezone for every period-boundary computation: MONTHLY → next month at 00:00 in the entity's local clock, WEEKLY → next week, same, DAILY → next day, same.
  4. The customer-facing invoice renders dates in the entity's local clock with an explicit suffix (e.g. Jul 31, 2026 (JST)) so there's no ambiguity.

Changes to an entity's timezone are NOT retroactive. Existing subscriptions keep the timezone they were pinned with at create-time. Only NEW subscriptions issued by the entity after the change pick up the new timezone. This is intentional — shifting an active customer's billing period mid-cycle would surprise them and create awkward proration.

Lifecycle

StatusBehavior
ACTIVECan issue new invoices; can be set as default
SUSPENDEDCannot issue new invoices; existing customer bindings unchanged; can be reactivated
ARCHIVEDSoft-deleted; invoice history preserved; cannot be reactivated or set as default

Actions on each row: Edit, Set Default, Suspend, Reactivate, Archive.

At least one entity must be the default — invoice creation fails if none is designated. Setting a new default automatically clears the previous one (an atomic flip in the same transaction). The default entity also cannot be suspended or archived; demote it first.

Editing an entity

Click Edit on any active or suspended row. The dialog opens with all current values prefilled.

Immutable fields (shown disabled with a hint):

  • Entity code — anchors the invoice numbering sequence
  • Base currency — anchors the GL ledger

If you genuinely need to change either, archive the entity and create a new one. Existing invoices keep their original entity binding — there's no in-place migration.

Editable fields include everything else: display name, legal name, tax registration, jurisdiction, reporting currency, billing timezone, full address, invoice prefix and format, logo, and footer text.

Quick start

If you're operating from a single jurisdiction, you don't need to do anything — a default entity (DEFAULT) is auto-provisioned for you at tenant signup with sensible defaults (USD, US, UTC, INV-{seq:0000}).

To add a second entity:

  1. Click New Entity (the + button in the toolbar).
  2. Pick an Entity code that's stable and short (EU, JP, UK). You cannot rename it later.
  3. Pick the Base currency for this entity (immutable too — pick carefully).
  4. Pick the Billing timezone from the dropdown (or type a custom IANA zone for a region not in the list — backend will validate).
  5. Fill in the legal name, tax registration, and address. These render verbatim on every invoice the entity issues.
  6. Set the invoice number prefix and format (e.g. EU-{YYYY}-{seq:0000}).
  7. Optionally tick Make this the default to switch the default entity to the new one in the same save.

API reference

# List all billing entities (paginated)
GET /api/v1/billing-entities?status=ACTIVE&page=0&size=20
X-Tenant-Id: <tenant id>

# Get one
GET /api/v1/billing-entities/{entityId}
X-Tenant-Id: <tenant id>

# Create
POST /api/v1/billing-entities
X-Tenant-Id: <tenant id>
Content-Type: application/json

{
  "entityCode": "JP",
  "displayName": "Acme Japan K.K.",
  "legalName": "Acme Japan Kabushiki Kaisha",
  "baseCurrency": "JPY",
  "countryCode": "JP",
  "billingTimezone": "Asia/Tokyo",
  "address": {
    "line1": "1-1 Marunouchi",
    "city": "Chiyoda-ku",
    "region": "Tokyo",
    "postalCode": "100-0005",
    "country": "JP"
  },
  "invoiceNumberPrefix": "JP",
  "invoiceNumberFormat": "{prefix}-{YYYY}-{seq:0000}"
}

# Update (PATCH semantics — only send fields you want to change)
PUT /api/v1/billing-entities/{entityId}
X-Tenant-Id: <tenant id>
Content-Type: application/json

{
  "billingTimezone": "Asia/Tokyo",
  "footerText": "ご利用ありがとうございます — お問い合わせ: billing@acme.co.jp"
}

# Lifecycle actions
POST /api/v1/billing-entities/{entityId}/set-default
POST /api/v1/billing-entities/{entityId}/suspend
POST /api/v1/billing-entities/{entityId}/reactivate
POST /api/v1/billing-entities/{entityId}/archive

The internal cross-service endpoint that pricing-service uses to pin the timezone onto subscriptions:

# Read full entity from another service (used by pricing-service)
GET /internal/v1/billing-entities/{entityId}
X-Tenant-Id: <tenant id>

Cross-tenant probes return 404 Not Found (the existence-leak-prevention rule) — there's no oracle that lets one tenant discover whether another tenant's entity ID is in use.

Common questions

My team operates from a single jurisdiction. Do I need to think about this? No. Aforo auto-provisions a default entity at tenant signup. Stick with UTC and the org-level defaults unless you have an active reason to change.

I changed my entity's timezone but my existing customer's invoices still look the same. Bug? Working as designed. Timezone is pinned at subscription create-time so an active customer doesn't get a surprise period shift mid-cycle. The new timezone applies to NEW subscriptions issued by the entity after the change.

Can I migrate an active subscription to a new timezone? Today, no — the architectural design intentionally avoids retroactive shifts. A "Migrate Subscription Timezone" admin operation is on the post-launch roadmap and will let you re-anchor a subscription's current period boundaries in a new zone, with an audit event so the customer can be notified.

Why are entity code and base currency immutable? Both anchor downstream state that can't be cleanly migrated:

  • Entity code is the seed for the invoice numbering sequence. Changing it mid-stream would create gaps or duplicates in the invoice number space.
  • Base currency anchors the GL ledger. Changing it would require re-stating every prior journal entry — not something Aforo can do safely.

If you need to change either, archive the entity and create a new one. The old entity's invoices and subscriptions stay where they are.

What happens when I archive an entity? The entity is marked ARCHIVED. New subscriptions can't be bound to it. Existing subscriptions keep their binding — historical invoices remain queryable and customer-portal viewable. Archived entities cannot be reactivated, set as default, or suspended.