Skip to main content

User Personas & Permissions

The Four Roles

SalesArck has four user roles. Every authenticated user occupies exactly one role at the platform level, though consumers and clients live in different tenant contexts.

1. Consumer

The end customer who shops at a merchants that use SalesArck. Consumers earn points passively (via POS transactions) and can actively redeem them through the consumer app.

Characteristics:

  • May not even know SalesArck exists — the experience is branded per merchant
  • Can have wallet relationships with multiple tenants simultaneously
  • Authenticates globally but transacts within tenant scope
  • Primary identity: Supabase user account with verified phone or email contact method

2. Client (Merchant)

A team member of a merchant business. May be the owner or an employee with delegated access. Manages their loyalty program: connects POS, configures rules, views analytics.

Characteristics:

  • Operates within a single tenant (their business)
  • Invited by an existing tenant owner or self-registered
  • Cannot see other tenants' data — ever
  • Manages POS OAuth tokens and reward rules for their tenant
  • New client accounts start with status: pending_approval and require admin approval before access is granted

3. POS Operator

A cashier or front-line employee at a merchant location. Has limited access scoped to a single task: verifying and confirming customer redemption codes at the register. Does not have access to analytics, reward configuration, POS settings, or any other merchant portal feature.

Characteristics:

  • Operates within a single tenant (their employer's business)
  • Invited by a Client (merchant owner/manager) — cannot self-register
  • Access is restricted to the Redemption Verification Page only
  • Cannot view transaction history, analytics, reward rules, or POS settings
  • Designed for high employee turnover — easy to provision and revoke

Redemption Verification Page:

The POS Operator sees a single-purpose interface:

┌──────────────────────────────────────────┐
│ SalesArck — Verify Redemption │
│ │
│ Enter code: [ AXK47R ] [Search] │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ Customer: Jane D. │ │
│ │ Discount: $1.00 (100 points) │ │
│ │ Expires: 3 min 22 sec remaining │ │
│ │ │ │
│ │ [ ✅ Confirm Redemption ] │ │
│ └──────────────────────────────────────┘ │
│ │
│ ⓘ Apply $1.00 discount at the register │
│ before confirming. │
└──────────────────────────────────────────┘
  • Search box: Operator enters the 6-character code from the customer
  • Result displays: Customer first name + last initial, discount dollar amount, time remaining
  • Confirm button: Grayed out / disabled if the code is expired or otherwise invalid
  • Estimated verification time: ~30 seconds per transaction (acceptable for counter service)

4. Admin

A SalesArck internal employee with platform-wide access. Performs support actions, fraud interventions, and platform operations. Every admin action is immutably logged.

Characteristics:

  • Global access across all tenants
  • Can perform actions consumers and clients cannot (manual wallet adjustments, freezing, etc.)
  • Every action creates an admin_audit_log with before/after state and reason
  • Admin bootstrap seeded in Phase 0 via environment config
  • Admin signup restricted to @doshidhruv.com email addresses only (enforced server-side)
  • Admins can approve/reject client accounts and assign roles via PATCH /api/v1/admin/users/:id/approve

Permission Matrix

CapabilityConsumerClientPOS OperatorAdmin
View own profile
Update own profile
View own wallet balance
View own transaction history
Redeem points
Verify redemption code✅ (own tenant)✅ (own tenant)
Confirm redemption✅ (own tenant)✅ (own tenant)
View tenant transactions✅ (own tenant)✅ (all)
View tenant analytics✅ (own tenant)✅ (all)
Configure reward rules✅ (own tenant)
Connect/disconnect POS✅ (own tenant)
Revoke POS token✅ (own tenant)
Invite team members✅ (own tenant, owner role)
Invite POS operators✅ (own tenant)
Manual wallet adjustments❌ (post-MVP)
Freeze/suspend wallets
View all tenants
Change tenant status
View admin audit logs
View fraud alerts

Identity Constraints

Key rules baked into the data model
  • mobile is globally unique — it is the primary login identifier
  • email is optional but when present must be globally unique
  • A consumer can exist in multiple tenant contexts (has a separate wallet per tenant)
  • Client users are restricted to the tenants they're explicitly members of — enforced by tenant_users and verified server-side on every request
  • Admin users have no tenant_id claim — they access all tenants via elevated permission checks

JWT Token Claims

The Supabase session JWT identifies the user, and SalesArck resolves the rest of the authorization context server-side:

{
"sub": "supabase-user-uuid",
"role": "consumer | client | pos_operator | admin",
"tenant_ids": ["tenant-uuid-1"]
}

Notes:

  • sub comes from Supabase and is the stable identity key for SalesArck
  • tenant_ids is resolved by SalesArck after verifying the Supabase JWT
  • Role and tenant access are enforced by SalesArck middleware, not by the client
  • Session refresh and token rotation are handled by Supabase Auth

Multi-Tenancy Enforcement Pattern

Request arrives

JWT decoded → userId, role, tenant_ids

tenantMiddleware() runs:
if role = client:
assert requestedTenantId ∈ jwt.tenant_ids
if role = pos_operator:
assert requestedTenantId ∈ jwt.tenant_ids
assert route ∈ OPERATOR_ALLOWED_ROUTES
if role = consumer:
assert consumer owns wallet for requestedTenantId
if role = admin:
allow any tenantId

Handler receives validated (userId, tenantId) context

All DB queries include WHERE tenant_id = :tenantId
Never skip this check

It is not sufficient to validate the tenant only at the route level. Every database query that operates on tenant-scoped data must include a tenant_id filter, even if the route already has a middleware check. Defense in depth.

Written byDhruv Doshi