Skip to content

Platform & admin ​

Ascendly platform features for super admins (access level 5), HR, and engineering — sign-in policy, operational health, broadcast email, employee onboarding, and access-level reference.


Sign-in & domains ​

Google OAuth ​

All clients (web and desktop) use Continue with Google. After Google returns a profile, the API validates in order:

  1. Email domain ∈ ALLOWED_EMAIL_DOMAINS (server env) — else 403
  2. Email verified with Google — else 403
  3. Employee record exists in tbl_employees (match google_sub or personal_data.email_address) — else 403

Initial domains: outdoorequipped.com, channelprecision.com. HR must create the employee profile before first sign-in.

Work email on employee create/update ​

When admins create or edit an employee's work email, the API validates the address domain against the same allowlist (ensure_email_domain_allowed).

RoutePurpose
/privacyPrivacy policy
/termsTerms of service

Available without signing in. Desktop opens these in the system browser.

Session length ​

After Google sign-in, Ascendly issues an access token that lasts 7 days (from that sign-in, not from last activity). Closing the tab or going idle does not extend it.

ClientAccess tokenStay signed in?When it expires
Web7 daysNo refresh tokenThe signed-in app sends you back to Continue with Google, even if you are only looking at a page.
Desktop7 days, renewed in the backgroundRefresh token: 365 daysYou stay signed in across restarts until you sign out, or until the refresh token hits one year. If renewal fails, the app asks you to sign in again.

Using the app (heartbeats, Scorecard, Dashboard) does not reset the 7-day clock on web. Sign in again to get a new 7 days.

Operators: JWT_EXPIRE_MINUTES (default 10080 = 7 days) and JWT_DESKTOP_REFRESH_EXPIRE_DAYS (default 365). Production and staging currently use those defaults.

Desktop refresh tokens ​

Desktop sign-in (POST /v1/auth/google with client: "desktop") returns a refresh token in addition to the access token. The desktop app stores both in OS-encrypted storage and silently renews via POST /v1/auth/refresh. Sign-out calls POST /v1/auth/logout with the refresh token to revoke it.

Web uses the 7-day access token only (no refresh token). See Session length.


Access levels & permissions ​

Ascendly uses numeric access levels (1–5) on tbl_employees. Level 5 is super admin; level 1 is a standard employee.

Permissions page (/permissions) ​

Read-only matrix showing what each level can do across:

ResourceActions
Employeesview, create, request_create, approve_requests, edit, delete
Scorecardview, submit, edit, delete
Organizationview_tree, edit_departments, assign_managers, create_cluster_or_department
Tasksview, create_edit, delete
Configurationsview, edit
Dashboardmanual_sync
Activity logsview

API: GET /v1/auth/permissions (current user) · GET /v1/auth/permissions/matrix (full matrix, level 5).

Level 5 users can impersonate another employee for support — impersonation is scoped in audit logs and activity tracking.


System status (/system) ​

Super admins see operational health at /system.

API: GET /v1/system/status

CheckWhat it reports
MongoDBConnection ping
BigQueryConfigured, enabled, SELECT 1 probe
S3Configured, bucket connectivity (ATW proof images)
WorkerHeartbeat from tbl_worker_status (keyed by API_ENV); stale after 120s
BSC rolling syncCurrent period, last rolling sync timestamp, interval

Overall status is ok or degraded. Worker heartbeats are environment-scoped — local dev (API_ENV=development) and Railway staging/production each show their own worker.

Related pages: System → Notifications (/system/notifications), System → Email (/system/email), System → Teams (/system/teams), System → Visibility (/system/visibility), System → Product feedback (/system/feedback).


Product feedback ​

Any signed-in employee can send a bug, suggestion, or this is confusing note from the web account menu (Send feedback). Desktop does not include this form.

PieceDetail
APIPOST /v1/feedback, POST /v1/feedback/{id}/attachments
ScreenshotsOptional. JPEG, PNG, WebP, GIF. Up to 5 images, 5 MB each. Paste, drop, or choose files. Stored in S3 (ascendly/product-feedback/).
Auto-attachedSigned-in name, employee id, current page path, web vs desktop, impersonation flag
Admin queue/system/feedback (level 5). Status: new → reviewing → resolved / won't fix, plus a reviewer note
Admin APIGET /v1/feedback/admin, PATCH /v1/feedback/admin/{id}, screenshot URLs under /attachments/{id}/url

This is not Nella thumbs-up/down. Those rate a chat reply and stay on /system/nella.


Page visibility ​

Super admins control which nav areas and pages appear for everyone via System → Visibility (/system/visibility).

APIPurpose
GET /v1/visibilityEffective visibility for the signed-in client
GET /v1/system/visibilityFull catalog + disabled keys (level 5)
PUT /v1/system/visibility/{key}Enable or disable a key (level 5)

Keys are hierarchical (e.g. disabling dashboard hides all dashboard children; disabling dashboard.news.birthdays hides only the Birthdays story). Catalog includes Dashboard (News stories + five BSC pages), Scorecard tabs (including Weekly Kudos), Learning sub-pages, Organization / Schedules, and Employees.

Hidden routes redirect according to remaining visible home destinations.


Employee create requests ​

Level 2+ users with request_create can submit employee create requests from the Employees page (Request new employee / My requests). Approvers with approve_requests open Requests to review pending items (approve / reject) and switch to previously approved or rejected requests. Exact matrix actions: Permissions above.


Maintenance mode ​

Use during deploys, data cutovers, or incidents when most users should not use Ascendly while engineers verify production.

Server configuration ​

Set on the API service (Railway env or local .env):

VariableDefaultPurpose
MAINTENANCE_MODEfalseWhen true, API returns 503 for most routes
MAINTENANCE_MESSAGE(built-in default)Optional custom text shown on the web maintenance page
MAINTENANCE_BYPASS_EMAILS(empty)Comma-separated work emails that may sign in and use the app normally

Example bypass list: admin@example.com,ops@example.com (matched case-insensitively against the employee profile email).

API behavior ​

Path / callerBehavior
GET /healthAlways allowed
GET /v1/public/app-statusAlways allowed — web/desktop poll this for maintenance state
POST /v1/auth/google, POST /v1/auth/refreshAllowed through middleware; handler rejects non-bypass emails with 503
Authenticated requestsAllowed when the JWT user’s email is in MAINTENANCE_BYPASS_EMAILS
Everything else503 with { maintenance: true, message: "…" }

The worker process is not blocked (API_ENV=worker skips maintenance middleware).

Web behavior ​

  • Polls GET /v1/public/app-status on load and after sign-in.
  • Non-bypass users are redirected to /maintenance (full-page message).
  • /login, /auth/callback, and /maintenance stay reachable so bypass users can sign in.
  • Failed sign-in during maintenance redirects to /maintenance instead of showing raw JSON.

Turn maintenance off (MAINTENANCE_MODE=false) and redeploy when verification is complete.


Admin email compose (/system/email) ​

Super admins (level 5) can send a broadcast message to selected employees.

FieldNotes
RecipientsMulti-select employees (max per send); quick-add by org group
SubjectRequired
BodyRich text (plain + HTML)
Action URLOptional deep link in email / in-app
DeliveryEmail (Gmail delegation), In-app notification, or both

API: POST /v1/system/email/send

Requires Gmail delegation configured on the API (NOTIFICATION_EMAIL_* env vars). Per-recipient results show email and in-app delivery status. Actions are audit-logged.

Use for company announcements — not for automated scorecard notifications (those use the notification type catalog).


Teams channel (/system/teams) ​

Super admins (level 5) connect one Teams channel and post operational notices there. Recipients are everyone in that channel, not a picked employee list — that is why this is separate from email compose.

FieldNotes
Webhook URLTeams Workflows “Post to a channel when a webhook request is received.” Secret; shown masked after save
Automated postsMaster switch for lock / missing-eval notices. Off by default. Test and compose still work
TestSends a sample Adaptive Card
ComposeSubject + plain body + optional Open in Ascendly link; footer “Posted by {admin}”
HistoryRecent posts with success/error

Allowlisted catalog types (lock approaching, missing evals) post once per event only when this switch is on and Also post to the Teams channel is on for that type. Missing-evals cards use department counts only.

API: GET/PUT/PATCH/DELETE /v1/system/teams/connection, POST /v1/system/teams/test, POST /v1/system/teams/compose, GET /v1/system/teams/posts

Optional env: TEAMS_WEBHOOK_URL (overrides the URL saved in the app).


Welcome email (Employees) ​

On Employees (/employees), admins with create/edit access can select one or more rows and Send Welcome Email.

API: POST /v1/employees/welcome-email with { employee_ids: string[] }

Sends a branded welcome message (email when configured) and can emit an in-app notification. Useful after HR creates a profile so the employee knows they can sign in with Google.


Activity tracking ​

Ascendly records sign-in time, last active (web) and last active (desktop), and active minutes per client. Managers see split columns on Employees; employees see their own summary on Profile (web) or Home (desktop).

Full rules — visibility, heartbeats, throttling, impersonation, and HR troubleshooting: User activity.

API: GET /v1/employees/activity-export for team CSV export (same scope as the employee list).