Appearance
tenants
Platform instances belonging to the customer. Each customer typically owns 1+ tenants in a hierarchy (root + sub-platforms).
- ETL strategy:
merge(upsert byid) - PK:
id
Columns
| Column | Type | Description |
|---|---|---|
id | BIGINT | PK. |
id_parent | BIGINT | Logical FK → tenants.id. Parent tenant in the hierarchy. |
subdomain | TEXT | Tenant subdomain (e.g. customer.cademi.com.br). |
domain | TEXT | Custom domain when configured. |
key | TEXT | Internal slug for the tenant. |
user_limit | BIGINT | Contractual user cap. |
meta | JSONB | Tenant-level configuration. Domain-grouped (certificate, gamification). See structure below. |
created_at | TIMESTAMPTZ | Tenant creation. |
deleted_at | TIMESTAMPTZ | Soft delete (NULL = active). |
meta structure
Domain-grouped JSONB. Any key may be absent — empty branches are stripped.
json
{
"branding": {
"title",
"main_color",
"theme",
"login": { "logo" },
"email": { "logo", "sender": { "email", "name" } }
},
"certificate": {
"is_active",
"logo": { "light", "dark" }
},
"gamification": {
"is_active",
"show_users",
"show_full_name",
"comment_scoring_type",
"events": { "<event_name>": { "is_active", "points" }, … }
}
}meta.branding
Visual identity and email-sender configuration.
| Path | Type | Meaning |
|---|---|---|
title | string | Tenant display title. |
main_color | string | Primary brand color (hex). |
theme | string | Enum — see below. Defaults to light. |
login.logo | string | URL of the logo shown on the login page. |
email.logo | string | URL of the logo embedded in transactional emails. |
email.sender.email | string | "From" address used for transactional emails. |
email.sender.name | string | "From" display name used for transactional emails. |
Enum · meta.branding.theme
| Value | Meaning |
|---|---|
dark | Dark layout. |
light | Light layout. Default. |
meta.certificate
| Path | Type | Meaning |
|---|---|---|
is_active | boolean | Whether the tenant has certificates enabled. |
logo.light | string | URL of the light-theme certificate logo. |
logo.dark | string | URL of the dark-theme certificate logo. |
meta.gamification
| Path | Type | Meaning |
|---|---|---|
is_active | boolean | Gamification turned on for the tenant. |
show_users | boolean | Show learners in the gamification ranking. |
show_full_name | boolean | Display learners' full name in the ranking. |
comment_scoring_type | string | Enum — see below. |
events | object | Map of event name → { is_active, points }. See event names below. |
Enum · meta.gamification.comment_scoring_type
| Value | Meaning |
|---|---|
single | A user is awarded points only on their first comment. |
unlimited | Every comment scores. |
Event names in meta.gamification.events
Each value is { is_active: boolean, points: integer }. points defaults to 0 when not configured.
| Event | Awards points when… |
|---|---|
lesson_completed | a lesson is completed. |
course_started | the learner starts a course. |
course_progress_50 | the learner reaches 50%. |
course_progress_75 | the learner reaches 75%. |
course_progress_90 | the learner reaches 90%. |
course_completed | a course is fully completed. |
exam_passed | the learner passes an exam. |
certificate_issued | a certificate is issued. |
comment_posted | a comment is posted (subject to comment_scoring_type). |
question_asked | a question/inquiry is posted. |
The same event names show up in
scores.type, so you can matchscores.typeagainstmeta.gamification.events.
Relationships
- Self:
id_parent→ anothertenants.id. Use it to reconstruct the instance tree. - Children: almost every table references
tenants.idviaid_tenant.
Examples
sql
-- Full tenant tree for the customer
WITH RECURSIVE tree AS (
SELECT id, id_parent, subdomain, 0 AS level
FROM lms.tenants
WHERE id_parent IS NULL AND deleted_at IS NULL
UNION ALL
SELECT t.id, t.id_parent, t.subdomain, tree.level + 1
FROM lms.tenants t
JOIN tree ON tree.id = t.id_parent
WHERE t.deleted_at IS NULL
)
SELECT * FROM tree ORDER BY level, subdomain;
-- Tenants with gamification enabled and the points awarded for completing a lesson
SELECT id,
subdomain,
(meta->'gamification'->'events'->'lesson_completed'->>'points')::int AS lesson_points
FROM lms.tenants
WHERE (meta->'gamification'->>'is_active')::boolean = TRUE
AND deleted_at IS NULL;