Admin GuidesPlatform Admin

Platform Admin Layer

Who this guide is for: Embay Consulting internal staff with a platform user account. The platform is not accessible to tenant admins or learners.

Overview

app.embaylms.com is the Embay staff platform administration domain. It provides cross-tenant operations — tenant provisioning, status management, migration monitoring, billing oversight, and support tooling — against the global schema. Platform users are a separate identity from tenant users; they are stored in embaylms_global.platform_users.

The platform does not surface tenant-scoped features (courses, enrollments, catalog, SCORM, reports, learning paths, etc.). Those are always accessed at the tenant’s own subdomain (e.g. acme.embaylms.com).


Platform User Roles

RoleWhoWhat they can do
super_adminEngineering leads, opsEverything: create/delete/suspend tenants, manage platform users and roles, override tenant config, export audit logs, impersonate any tenant user
support_agentCustomer support staffRead all tenant records (status, config, migration state). Impersonate a tenant user for debugging (1-hour session, audit-logged). Cannot create/delete tenants or manage platform users.
billing_adminFinance / accountsRead billing and subscription data across all tenants. Export invoices. No access to tenant user data or migration tools.

Getting Access

Platform accounts are created by a super_admin via /platform/users. There is no self-signup.

  1. Ask a super_admin to invite your Embay email address
  2. You will receive an email with a one-time setup link
  3. Set your password and enable TOTP MFA (required for all platform users)
  4. Navigate to https://app.embaylms.com and sign in

Bootstrap note: The very first super_admin must be created via the seed script:

pnpm tsx infra/scripts/seed-platform-admin.ts

This is a one-time operation. See the runbook in docs/enablement/runbooks/ for production bootstrap steps.

Forgot Your Password? (LMS-366)

Platform accounts have self-service password reset:

  1. On the sign-in page (https://app.embaylms.com/platform/login), click Forgot password?
  2. Enter your platform account email and click Send reset link
  3. Check your inbox for an email titled “Password reset — EmbayLMS platform” and click Reset password
  4. Choose a new password (minimum 8 characters) and confirm it
  5. Sign in with the new password — the old one stops working immediately
BehaviourDetail
Link lifetime1 hour — request a new link if it expires
Email languageMatches the locale of the page you requested from (EN / FR-CA)
Unknown emailsThe page always shows the same success message — it never reveals whether an account exists
Rate limit5 requests per minute per IP address
AuditBoth the request and the completed reset are written to the audit log
MFAResetting the password does not reset TOTP MFA. If you also lost your authenticator, a super_admin must clear your MFA enrollment

Troubleshooting

  • No email arrives: check spam, confirm you used the exact email on your platform account, and verify with a super_admin that the account is active — deactivated accounts never receive reset emails.
  • “This reset link is invalid or has expired”: the link is older than 1 hour, was already used after a newer one was issued, or was generated for a tenant account. Request a fresh link from /platform/forgot-password.

Dashboard (/platform/dashboard)

Shows aggregate platform health. Visible to all platform roles.

MetricDescription
Total TenantsAll tenants in the global registry
Active TenantsTenants with status = active
Migrations DoneCount of completed migration jobs
Migrations FailedCount of failed jobs (highlighted in red if > 0)

Tenant Management (/platform/tenants)

Access: super_admin for mutations. All roles can view.

Viewing Tenants

Lists all tenants with display name, subdomain, status, region, latest migration state, and creation date.

Tenant Detail (/platform/tenants/{slug})

Access: all platform roles can view.

Opening a tenant shows a read-only health overview: identity (region, created, custom domain, billing), usage metrics (users, courses, enrollments, completions), a configuration snapshot (SSO/SCIM/custom domain/branding), and a recent-activity audit preview.

Administrators (LMS-440). The detail page lists the tenant’s administrators — every tenant user with the admin role — with their name, email (click to compose), last login, and active status. Soft-deleted users are excluded. Use this to find the right person to contact about a tenant without signing in to it. The list is read from the tenant schema live; if it is briefly unavailable the section shows an empty state rather than failing the page.

Feature Entitlements Panel (super_admin for changes) — LMS-373

The tenant detail page includes a Feature entitlements panel showing the tenant’s plan tier, seat/course/storage limits, API monthly cap, and each gated feature flag (SSO/SAML, SCIM, API access, custom branding, certificates, advanced automations) with its current On/Off state and whether entitlements are enforced for the tenant.

  • Per-flag overrides. A super_admin can toggle an individual flag on or off outside the tier default — e.g. enable SSO temporarily for a security review. Every override requires a reason, and the change is audit-logged (actor, before/after, reason).
  • Overrides persist until the tier changes — using Change plan tier resets the flags to the new tier’s defaults.
  • Tenants with entitlements not enforced (grandfathered) show all features available regardless of flags.

Data Export (super_admin only) — LMS-379

The Data Export panel on the tenant detail page produces a full export of a tenant’s data (users, courses, enrollment/completion records) — for contract-end offboarding or a support investigation (§6.3.1).

  1. Click Export tenant data. A job is queued and an audit entry (tenant_export_requested) records who requested it.
  2. The worker gathers the data, writes a JSON document to the exports bucket, and emails you when it’s ready.
  3. Click Refresh to update the list, then Download on a completed job. The link is a short-lived presigned URL (≤15 minutes, CLAUDE.md §4.4); each download is audit-logged (tenant_export_downloaded). Re-click to get a fresh link after it expires.
StateMeaning
queued / runningThe export is in progress.
completedReady — a Download button appears (shows file size).
failedSomething went wrong (reason shown), e.g. exports storage not configured.

Signing in to a Tenant (super_admin only) — LMS-437

A super_admin can sign in to any active tenant as a full administrator without waiting for a customer access request. On the tenant detail page click Log in to tenant.

  1. The action records an audit entry (platform_tenant_login) tying your platform identity to the target tenant — this is the trace of which super_admin entered which tenant and when.
  2. You are redirected to the tenant (e.g. acme.embaylms.com) and signed in as a synthetic platform-support administrator. A navy banner across the top reads “You are signed in to {tenant} as Embay platform support”.
  3. You have the full tenant-admin permission set — you can do anything a tenant admin can.
  4. Click Return to platform in the banner to sign out of the tenant and go back to the platform console (your platform session is preserved).
PropertyDetail
Whosuper_admin only. Support agents use consent-based access (future) instead.
Tenant visibilityYou do not appear in the tenant’s user table — the platform-support session has no tenant user record.
AttributionActions inside the tenant are audit-logged with your platform user id as the actor.
AvailabilityOnly active tenants — reactivate a suspended tenant first.
Exit”Return to platform”, or simply signing out of the tenant.

This is distinct from user impersonation (logging in as a specific tenant user); platform-support login acts as a generic tenant administrator.

Creating a Tenant (super_admin only)

  1. Click New Tenant
  2. Fill in the form:
FieldRequiredDescription
Display NameYesHuman-readable name (e.g. “Acme Corp”)
Subdomain SlugYesLowercase, alphanumeric + hyphens. Becomes {slug}.embaylms.com
RegionYesDefault: ca-central-1
Custom DomainNoe.g. learn.acme.com
  1. Click Create Tenant

The system creates the tenant record (status: provisioning) and queues a worker job to:

  • Create the embaylms_tenant_{slug} PostgreSQL schema
  • Register {slug}.embaylms.com in Vercel

If Redis is unavailable, run provisioning manually:

pnpm tsx infra/scripts/provision-tenant.ts --tenant {slug}

Suspending a Tenant (super_admin only)

Click Suspend. Sets status: suspended — all logins at {slug}.embaylms.com are blocked. No data is deleted.

Activating a Tenant (super_admin only)

Click Activate on a suspended tenant to restore access.

Deleting a Tenant (super_admin only)

Soft-deletes the tenant record (status: deleted). The PostgreSQL schema and S3 data are not immediately purged — a separate manual purge step is required. Deletion is audit-logged and irreversible via the UI.


Support: Tenant Impersonation (/platform/tenants/{slug}/impersonate)

Access: super_admin and support_agent

Allows an Embay staff member to log in to a tenant’s context as a specific user, for debugging purposes.

  1. Find the tenant in the tenant list
  2. Click Impersonate
  3. Select the target tenant user (by email)
  4. Confirm — you are redirected to {slug}.embaylms.com with a 1-hour impersonation session

Audit trail: Every impersonation login is written to audit_log with:

  • actor_id: platform user ID
  • is_impersonation: true
  • impersonated_user_id: target user
  • impersonated_by_role: your platform role

Constraints:

  • Impersonation sessions expire after 1 hour and cannot be extended
  • An impersonation session cannot itself perform impersonation
  • support_agent impersonation sessions are read-only within the tenant (no mutations)

Platform User Management (/platform/users)

Access: super_admin only

  • Invite a new platform user by email — sends a one-time set-password link.
  • Add a new platform user directly (LMS-438) — generates a temporary password shown once for you to hand off securely. The new user is forced to change it on first sign-in (a full-screen prompt blocks the console until they do). Use this when email delivery is unavailable or you need to bootstrap an account immediately.
  • Assign or change roles
  • Deactivate a platform user (immediately revokes access)
  • View login history per user

Impersonating a Platform User (LMS-439)

Access: super_admin only

For debugging another Embay staff member’s experience, hover a row in /platform/users and click Impersonate. Your session is swapped to that user and an amber banner appears at the top of the console; click Exit impersonation to return to your own account. You cannot impersonate yourself or a deactivated user. Every impersonation start is written to the audit log (platform_impersonation_started) with your id as impersonatedBy.

This impersonates a platform user on app.embaylms.com — it is distinct from entering a tenant (see Tenant access above).


Errors Console & Platform Notifications (/platform/errors) — LMS-600/601

Access: super_admin and support_agent can view; status changes and identity resolution are super_admin only.

The Errors console surfaces the self-healing error pipeline (ADR-018): user-facing tenant errors are captured first-party, deduplicated into error groups, ticketed at runtime in Linear, and triaged by an automated coding agent. The console is the human window into that loop.

The Errors list

/platform/errors lists error groups (not individual occurrences), filterable by status and tenant slug. Each row shows the tenant slug, environment, occurrence and affected-user counts, first/last seen, and the linked Linear bug.

Group statuses: New, Triaging, Fix in progress, Awaiting decision (the agent escalated — a human call is needed), Resolved, Ignored, and Regressed (a resolved error came back).

Status actions (super_admin, audit-logged): Resolve an open group, Ignore noise, or Reopen a resolved/ignored group.

Identity resolution (audit-logged)

Error rows are pseudonymous — they carry the user’s UUID, tenant slug, and browser/OS only, never email, name, or IP. On a group’s detail page a super_admin can explicitly resolve a captured UUID to a person (name, email, role) when needed for support follow-up. Every resolution is written to the audit log.

The notifications bell

The platform console header shows a notifications bell — the first in-app notification path for Embay platform users. It signals pipeline events that need attention: an error group awaiting a decision, a fix deployed, or a regression. Clicking a notification deep-links to the relevant error group.

For pipeline operations (enabling capture, agent caps, the safety envelope, callback secrets, and incident handling), see the runbook: docs/runbooks/self-healing-pipeline.md.


What the Platform Does NOT Include

The following are not available at app.embaylms.com. Always access them at the tenant’s subdomain:

  • Course catalog and course editor
  • Enrollments and completions
  • Learning paths and skills
  • SCORM content upload and launch
  • ILT sessions and quizzes
  • Tenant user management (inviting learners, setting tenant roles)
  • Reports and certificates

Troubleshooting

SymptomLikely CauseResolution
Redirected to login page on app.embaylms.comNo platform user account, or session expiredSign in, or ask a super_admin to create your account
Signed in but immediately redirected awayAccount exists in a tenant schema, not in platform_usersA super_admin must create you a platform account via /platform/users
Tenant shows provisioning indefinitelyRedis/BullMQ not runningRun pnpm tsx infra/scripts/provision-tenant.ts --tenant {slug} manually
app.embaylms.com/admin/courses/... returns redirectCorrect — tenant routes are blocked on the platform domainAccess at {tenant}.embaylms.com instead
”Platform API only accessible from platform domain” error/api/platform called from a non-platform domainSecurity guard — do not call the platform API from tenant subdomains
Impersonation session expired1-hour limit reachedStart a new impersonation session from /platform/tenants