Skip to content

Platform

Licensing and editions

How the community and enterprise editions differ, loading the license token, offline operation, token states and a price summary.

On this page

O3O Office Online comes in two editions on the same installation: a free community edition and an enterprise edition priced per connection. Upgrading needs no reinstall: just load a license token.

How the editions differ#

ItemCommunityEnterprise
PriceFreePer connection, see the price table below
License tokennoyes
Concurrent editing connections50Exactly conns from the token, even when below 50; orders are at least 50
Editor embedding and save callbacksyesyes
DocBuilder endpoints10 endpoints, no template fillingAll 11 endpoints (token with api_full)
DocBuilder rate10/minute, 200/day60–3,000/minute by connection count, no daily limit
Maximum DocBuilder file size10 MB300 MB
Job completion callbacksnoyes (token with callback)
White labelnoComing soon, source-built O3O image only
Soft overagenoComing soon

Every limit in detail: Plan limits. How connections are counted: How connections are counted.

Loading the license token#

The gate and DocBuilder read the token in this order: the O3O_LICENSE_TOKEN variable when not empty, then the O3O_LICENSE_FILE file (default /etc/o3o/license.token) when it exists, otherwise no token (community edition). Leading and trailing whitespace is trimmed. The file is re-read every 60 seconds, so replacing it needs no restart; changing the environment variable requires recreating the container.

  1. Put the token in place

    Either paste it into O3O_LICENSE_TOKEN in online/.env, or save it as a file mounted into both o3o-gate and o3o-docbuilder at /etc/o3o/license.token.
  2. Load it

    With the environment variable: docker compose ... up -d. With the file: wait up to 60 seconds.
  3. Check

    GET /o3o/status must show license.state = "valid", edition = "enterprise" and connections.limit equal to the purchased connections; GET /v1/limits must show edition = "enterprise".
Two ways to load the token
# Option 1: environment variable in online/.env (token shortened for readability)
O3O_LICENSE_TOKEN=eyJ2IjoxLCJrZXkiOiJPT1NZLTdLMk05LVE0WEJDLUQ4SEpOLVIzVFdaIiwicGxhbiI6InllYXIiLCJwcm9kIjoibzNvLW9ubGluZSIs...base64...==.W2m7...base64...==

# Option 2: a file; leave O3O_LICENSE_TOKEN empty
O3O_LICENSE_TOKEN=
O3O_LICENSE_FILE=/etc/o3o/license.token

# Check after loading
curl -s http://localhost:8080/o3o/status | jq '{edition, license: .license.state, limit: .connections.limit}'
curl -s http://localhost:8080/v1/limits -H "Authorization: Bearer O3O_DEMO_KEY" | jq .edition
# docker/compose.license.yml, use with: -f docker/compose.dev.yml -f docker/compose.license.yml
# ./license.token is relative to the first compose file's folder (online/docker/)
services:
  o3o-gate:
    volumes:
      - ./license.token:/etc/o3o/license.token:ro
  o3o-docbuilder:
    volumes:
      - ./license.token:/etc/o3o/license.token:ro

Token format#

A token is base64(payload_json) + "." + base64(signature): standard Base64 with = padding (alphabet +/), exactly one dot, and an Ed25519 signature over the exact payload bytes. The gate and DocBuilder verify it with the O3O license server's public key (O3O_LICENSE_PUBKEY). Unknown payload fields are ignored.

JSONSample payload (Base64-decoded)
{
  "v": 1,
  "key": "OOSY-7K2M9-Q4XBC-D8HJN-R3TWZ",
  "plan": "year",
  "prod": "o3o-online",
  "mid": "",
  "cust": "Công ty TNHH An Phát",
  "exp": "2027-09-30",
  "upd": "2027-10-07",
  "iat": 1790000000,
  "lid": 123,
  "kind": "enterprise",
  "conns": 200,
  "feat": [
    "api_full",
    "callback"
  ]
}

Payload fields

  • vnumberrequired
    Format version: 1 or 2.
  • prodstringrequired
    Must be o3o-online.
  • kindstringrequired
    enterprise or community; only enterprise upgrades the edition.
  • connsintegerrequired
    Purchased connections, 1 or more. With a valid enterprise token the connection cap is exactly this number, even when it is below the community edition's 50.
  • planstringrequired
    year (annual) or life (perpetual).
  • expstringoptional
    YYYY-MM-DD, required for year; valid until the END of that day, UTC+7.
  • updstringoptional
    YYYY-MM-DD: end of update rights. Images built after this date reject the token. For annual plans the license server sets upd = exp + 7 days (the grace period, matching the gate).
  • featarrayoptionalDefault: []
    Features: whitelabel, api_full (full DocBuilder), callback (DocBuilder callbacks).
  • midstringoptional
    Bound instance id; empty, absent or * means unbound.
  • keystringrequired
    License code, shaped OOSY-XXXXX-XXXXX-XXXXX-XXXXX (annual) or OOSL-… (perpetual). The gate does not check the code's shape; /o3o/status only shows the last 5 characters (key_hint).
  • custstringoptional
    Customer name.
  • iatnumberrequired
    Signing time, Unix seconds.
  • lidnumberoptional
    License server internal id; ignored by the gate and DocBuilder.
  • aidnumberoptional
    Internal activation id, present only when the token belongs to an activation; ignored by the gate and DocBuilder.

Token states#

license.stateMeaningEffective edition
noneNo token.community
validValid signature, not expired.enterprise (when kind = enterprise)
graceAnnual plan up to 7 days past exp; grace_days_left is the days remaining.enterprise
expiredMore than 7 days past exp.community
invalidBroken format, bad signature, bad mandatory fields, or a TEST key outside DEV mode.community
wrong_productprod is not o3o-online.community
update_expiredThe image was built after upd.community
instance_mismatchThe token is bound to another instance (mid).community

Working offline#

  • The token is verified entirely OFFLINE with the Ed25519 public key in the configuration; your O3O server does not call office.o3o.vn and sends usage data nowhere.
  • A server on an isolated network gets the full enterprise edition until exp, plus 7 days of grace for annual plans.
  • Perpetual licenses never expire; update rights end at upd: images built before that date keep accepting the token forever.
  • To bind a token to exactly one server, request a token whose mid equals the instance_id from /o3o/status. When you use an instance-bound token, set O3O_GATE_INSTANCE_ID explicitly in .env so the gate and DocBuilder share the same id; scripts/gen-secrets.sh (or gen-secrets.ps1) generates it when it creates a new .env.
Coming soon

Online activation with a license code, heartbeat and revocation through the office.o3o.vn server. v1 only verifies offline.

Renewing and adding connections#

O3O signs and delivers a new token once the order is complete. Replace the token the same way you loaded it; the gate picks it up within 60 seconds (file) or after the container is recreated (environment variable). Open sessions are not affected.

Price summary#

Prices are listed in USD. The unit price applies to ALL connections of the order (not progressive by tier). Minimum 50 connections per order. Perpetual licenses include 3 years of updates.

ConnectionsAnnual (USD/connection/year)Perpetual with 3 years of updates (USD/connection)
50 – 2005 USD10 USD
201 – 5004.5 USD9 USD
501 and above3.5 USD8 USD

Never-more-expensive rule#

charged(n) = min(m × unit_price(m)) over every m ≥ n and m ≥ 50. When the cheapest price falls on m > n, the customer pays for m and IS GRANTED m connections. Buying more therefore never costs more at a tier boundary.

The invoice states, and the token grants, exactly the connection count of the level that is billed, not the number entered. Annual example: choosing 190 connections bills 201 connections × 4.5 USD = 904.5 USD, and the token carries conns = 201.

PlanConnection rangeAmount chargedBilled and granted as
Annual181 – 200904.5 USD201
Annual390 – 5001,753.5 USD501
Perpetual181 – 2001,809 USD201
Perpetual446 – 5004,008 USD501
ConnectionsAnnual: listAnnual: chargedAnnual: grantedPerpetual: listPerpetual: chargedPerpetual: granted
50250 USD250 USD50500 USD500 USD50
120600 USD600 USD1201,200 USD1,200 USD120
190950 USD904.5 USD2011,900 USD1,809 USD201
2001,000 USD904.5 USD2012,000 USD1,809 USD201
201904.5 USD904.5 USD2011,809 USD1,809 USD201
3001,350 USD1,350 USD3002,700 USD2,700 USD300
4502,025 USD1,753.5 USD5014,050 USD4,008 USD501
5002,250 USD1,753.5 USD5014,500 USD4,008 USD501
5011,753.5 USD1,753.5 USD5014,008 USD4,008 USD501
8002,800 USD2,800 USD8006,400 USD6,400 USD800

Paying in VND: the USD to VND exchange rate is fixed and stored when the order is CREATED; the payment QR code keeps that exact amount. If an order expires unpaid, create a new order, priced at the exchange rate when the new order is created.