Client lifecycle
Pipeline, brands, proposals, bookings, portals, approvals, invoices, and communication history.
Engineering a multi-tenant agency CRM from authenticated interface to tenant-scoped data boundary.

01 — Engineering scope
The codebase connects client acquisition, project execution, communication, finance, people operations, reporting, portals, and integrations without splitting tenant identity across separate products.
Pipeline, brands, proposals, bookings, portals, approvals, invoices, and communication history.
Projects, task boards, custom statuses, timers, workload, schedules, reporting, and activity history.
Expenses, accounting, profitability, payroll, allocations, attendance, leave, and employee records.
Portfolio boundary
This case documents engineering decisions. Product planning, interface design, manual QA, and scheduled automation are presented as separate cases so the implementation evidence is not confused with adjacent roles.
02 — System architecture
Authenticated pages, public client surfaces, machine consumers, server routes, and Postgres policies each carry a different level of authority.

03 — Data authorization
The application does not treat hidden buttons as access control. Postgres policies resolve the signed-in organization, assigned brands, deletion state, and view/manage permission for every operation.
Row-level update policy
create policy "tasks_update" on public.tasks for update
using (
organization_id = public.my_org_id()
and (brand_id is null
or brand_id in (select public.my_brand_ids()))
and deleted_at is null
and public.can_manage('tasks')
)
with check (
organization_id = public.my_org_id()
and public.can_manage('tasks')
);Organization-scoped machine route
const auth = await authApiKey(req)
if (!auth) return unauthorized()
if (!(await apiRateAllowed(auth))) return tooManyRequests()
const { data: brand } = await auth.supabase
.from('brands')
.select('id')
.eq('id', body.brand_id)
.eq('organization_id', auth.orgId)
.maybeSingle()
const row = {
organization_id: auth.orgId,
title: body.title.trim(),
}04 — API boundary
An API key identifies one organization. Referenced records are checked inside that organization before a new row is written with an explicit tenant identifier.
05 — Integration architecture
Invoices use a provider-neutral interface for hosted links and verified webhooks. Stripe, YooKassa, and LiqPay are implemented; Netopia and Paynet remain explicit scaffolds until merchant credentials are available.
Provider contract
export interface PaymentProvider {
id: ProviderId
label: string
region: string
currencies: string[] | null
implemented: boolean
enabled(): boolean
createLink(
invoice: InvoiceForPay,
options: CreateLinkOpts
): Promise<PaymentLink>
parseWebhook(
raw: string,
headers: Headers
): Promise<WebhookResult>
}06 — Engineering decisions
The implementation uses a small set of repeated constraints rather than one-off security and delivery rules per feature.
Row policies combine organization scope, brand membership, deletion state, and view/manage capabilities. Hidden controls are never treated as the security boundary.
API-key routes authenticate, throttle, validate referenced entities inside the same organization, and write organization_id explicitly.
Booking, proposal, portal, invoice, and tracking endpoints use purpose-specific slugs or tokens instead of opening the wider authenticated data surface.
Payments and integrations expose stable server-side interfaces so core invoice and CRM flows do not depend on one regional provider.
Tenant isolation, permissions, billing, privacy, reporting, automation, and hardening ship as reviewable, versioned migrations.
The application, public pages, money, dates, email, notifications, reports, and generated output resolve RU/RO/EN through shared language services.
07 — Implemented product
The product surface consumes the same organization, role, finance, localization, and activity contracts documented above.

52
product/public pages
62
API routes
113
SQL migrations
2,356
localized keys
08 — Outcome
The outcome is documented through implemented routes, policies, provider contracts, migrations, interface surfaces, and production delivery — not unsupported revenue or adoption claims.