User Guide · v1.1

Arsi CRM — User Guide

A step-by-step manual for signing in, managing companies and contacts, running leads through to won deals, tracking tasks, and reading the reports — for every role on the Arsi CRM platform, sales rep through admin.

Publisher
Arsi India Info
Web App
https://demo.arsiindiainfo.com
API Base URL
https://demo.arsiindiainfo.com/api/v1
Interactive Docs
Local dev only — see §3
Audience
Sales Reps · Managers · Admins
3
User Roles
9
Core Modules
5
Deal Stages
5
Lead Statuses

1. Welcome

Arsi CRM is a small-business CRM built for a fictional company, Brightfield Business Solutions — companies and contacts, a lead pipeline with real conversion logic, a deal pipeline board, tasks, an activity timeline, and dashboards/reports. This guide walks through the platform exactly as a sales rep, manager, or admin would use it: the web app first, and the underlying API for anyone integrating or testing programmatically.

Who this guide is for Anyone using or evaluating Arsi CRM — no prior knowledge of the backend architecture is required. Most people should just use the web app (§2); developers calling the API directly can skip ahead to §3.

2. Two Ways to Use This CRM

There's a real, working web application in front of this platform — you don't need to write any code or run any HTTP requests by hand to try it out.

Web App Recommended

Open https://demo.arsiindiainfo.com in a browser, sign in with a demo account (§4), and click through the Dashboard, Companies, Leads, Deals, Tasks, and Reports screens — plus the Team and Audit Log screens if you're an admin. §5 breaks down exactly which screens each role sees.

Raw API / Swagger

Everything the web app does, it does by calling the same JSON API documented in §3 onward. Use this if you're integrating programmatically, testing with curl/Postman, or want to see exact request/response shapes via the interactive Swagger UI.

This guide covers both Every workflow section from §9 onward gives you the web-app steps first ("Via the Web App") and the equivalent raw HTTP call underneath ("Via the API") — use whichever matches how you're exploring the CRM.

3. Web App & API Reference

Everything is reachable from one public API base URL — there is exactly one entry point into the platform.

ResourceURL
Web apphttps://demo.arsiindiainfo.com
API base URLhttps://demo.arsiindiainfo.com/api/v1
Interactive Swagger docsGET/api/docs (non-production environments only)

The fastest way to explore the API is the Swagger UI — it lets you authenticate once and try every endpoint from the browser. This guide covers the same endpoints with raw HTTP examples for anyone integrating programmatically.

Response envelope

Every response — success or failure — is wrapped the same way, so client code only ever needs one shape to check:

// success
{
  "success": true,
  "data": { /* the actual result */ }
}

// paginated list responses also include a "meta" block
{
  "success": true,
  "data": [ /* rows */ ],
  "meta": { "page": 1, "limit": 20, "total": 15, "totalPages": 1 }
}

// failure
{
  "success": false,
  "error": { "code": "VALIDATION_ERROR", "message": "..." }
}

4. Demo Accounts

The DemoSeeder seeds a full Brightfield sales org — one admin, two managers, four reps — so you can try every role immediately without inviting your own users. Every account shares the same password:

RoleNameEmailPassword
AdminAva Adminadmin@brightfield.testPassw0rd!
Sales ManagerPriya Managerpriya.manager@brightfield.test
Sales ManagerRohan Vermarohan.manager@brightfield.test
Sales RepArjun Rep (reports to Priya)arjun.rep@brightfield.test
Sales RepMeera Rep (no manager)meera.rep@brightfield.test
Sales RepKaran Malhotra (reports to Rohan)karan.rep@brightfield.test
Sales RepSana Iyer (reports to Rohan)sana.rep@brightfield.test
Demo data notice This is a local demo environment seeded with synthetic data — no real customer, contact, or financial information. Meera Rep is deliberately seeded with no manager, to demonstrate the ownership guardrail in §18.

5. Which Screen Is For Which Role

The web app shows the same core navigation to every signed-in user. Everything a Sales Rep can do, a Sales Manager and Admin can also do — admins additionally see two extra screens under "Admin" in the sidebar.

ScreenRepManagerAdminWhat it's for
DashboardKPI cards and monthly revenue trend (§8); managers/admins also see a leaderboard.
Companies✅ own only✅ own + reports'✅ allAccounts, with tabbed Overview/Contacts/Deals/Activity/Notes (§9).
Contacts✅ own only✅ own + reports'✅ allPeople at each company (§9).
Leads✅ own only✅ own + reports'✅ allNew/Contacted/Qualified/Disqualified board (§10).
Deals✅ own only✅ own + reports'✅ allPipeline board through to Won/Lost (§12).
Tasks✅ own only✅ own only✅ own onlyYour own open tasks, due soonest first (§13).
Reports✅ no leaderboardPipeline funnel, revenue trend, salesperson leaderboard (§15).
TeamInvite, edit role/status, and disable users (§16).
Audit LogEvery sensitive action, platform-wide (§17).
Why this split There is no self-service way to become an Admin or Manager (§16) — a role is only ever granted by seeding (§4) or by an admin updating the account directly. §18 explains exactly how the Rep/Manager/Admin visibility rule works under the hood.

6. Signing In

The sign-in page is the first thing you see. It lists every demo account (§4) with a one-click fill so you don't need to retype credentials while exploring.

Via the Web App

  1. Open https://demo.arsiindiainfo.com — you land on the sign-in page.
  2. Click any row under "Demo project — sign in as" to auto-fill that account's email and password, or type your own.
  3. Tick the reCAPTCHA checkbox — see the callout below.
  4. Click Sign in. You land on the Dashboard (§8).
Arsi CRM sign-in page, showing the demo-account quick-fill list
The sign-in page — every demo account from §4 is one click away, with the shared password shown right on the screen.
Why there's a reCAPTCHA checkbox POST /auth/login is the only endpoint anyone can call without already being logged in, which makes it the obvious target for scripted/credential-stuffing bot traffic. The login form requires a Google reCAPTCHA v2 check before the request is accepted — RecaptchaFilter verifies the token server-side against Google on every login call, so a scripted client can't just skip the widget and post directly to the API either.

Via the API

The same reCAPTCHA check applies here too — recaptchaToken is a required field. In the web app it's filled in automatically once you complete the checkbox; calling the API directly means solving the widget yourself (e.g. via Swagger, which renders the same checkbox) and passing along the token you receive.

POST https://demo.arsiindiainfo.com/api/v1/auth/login
Content-Type: application/json

{
  "email": "admin@brightfield.test",
  "password": "Passw0rd!",
  "recaptchaToken": "03AGdBq27..."
}

A successful login returns both tokens, ready to use:

{
  "success": true,
  "data": {
    "accessToken": "eyJhbGciOi...",
    "refreshToken": "9ba3b6cf3c...",
    "user": {
      "id": 5,
      "name": "Ava Admin",
      "email": "admin@brightfield.test",
      "role": "ADMIN",
      "status": "ACTIVE"
    }
  }
}

Copy the accessToken and send it on every subsequent request as a bearer token:

Authorization: Bearer eyJhbGciOi...

7. Your Session & Signing Out

Access tokens are short-lived (15 minutes). The web app quietly exchanges the refreshToken for a new pair in the background whenever a request comes back 401 — you won't see a token expire while you're using the app.

Refreshing manually (API)

POST https://demo.arsiindiainfo.com/api/v1/auth/refresh
Content-Type: application/json

{ "refreshToken": "9ba3b6cf3c..." }

Via the Web App: click Sign out in the top-right corner of the header. Via the API: POST/auth/logout with { "refreshToken": "..." } and your current access token — this revokes the refresh token so it can no longer be used.

Via the Web App: click your name in the top-right corner, or use GET/users/me via the API, to see your own profile — id, name, email, role, and status.

8. The Dashboard

The Dashboard is the landing page after sign-in — a snapshot of the numbers that matter, scoped to what you're allowed to see (§18).

Via the Web App: five KPI cards (Customers, New Leads, Conversion Rate, Open Deals, Revenue MTD), a 12-month revenue trend bar chart, and — for Managers and Admins only — a Top Salespeople leaderboard.

Arsi CRM Dashboard — KPI cards, monthly revenue chart, and Top Salespeople leaderboard
The Dashboard, signed in as Admin — hovering a bar on the revenue chart shows that month's exact figure.

Via the API: GET/dashboard/summary returns the same numbers as one payload:

{
  "success": true,
  "data": {
    "totals": {
      "totalCompanies": 8,
      "newLeadsThisMonth": 10,
      "conversionRate": 0.0,
      "openDealsCount": 9,
      "openDealsValue": 295500,
      "revenueThisMonth": 42000
    },
    "monthlyTrend": [ { "month": "2026-08", "revenue": 61050 } /* ...11 more */ ]
  }
}

9. Companies & Contacts

Companies are Arsi CRM's accounts. Each has a status — PROSPECT, CUSTOMER, or CHURNED — that flips to CUSTOMER automatically the first time one of its deals is won (§12).

Via the Web App

  1. Open Companies. Search by name, sort by name/status/created date, and page through results.
  2. Click + New Company to add one directly (name, industry, website, phone).
  3. Click any row to open its detail page — tabs for Overview, Contacts, Deals, Activity, and Notes (§14).
  4. From the Contacts tab, click + Add Contact to add a person at that company.
Companies list — searchable, sortable table of accounts
The Companies list — status pills show Prospect/Customer/Churned at a glance.
A company's detail page with Overview/Contacts/Deals/Activity/Notes tabs
A company's detail page — the same five-tab layout every company shares.
The New Company dialog, with Name/Industry/Website/Phone fields
The "+ New Company" dialog — only Name is required.
Deletion guard A company can't be deleted while it has a deal that isn't Won or Lost — the API returns 409 INVALID_TRANSITION instead, so an active deal never loses its home company mid-pipeline.

Via the API

GET/companies?page=1&limit=20&search=&sort=createdAt&direction=desc — the same paginated list contract every list screen in Arsi CRM shares (Companies, Contacts, Leads, Deals, Tasks all use it).

POST/companies{ name, industry?, website?, phone? }. A duplicate name returns 409 DUPLICATE_NAME.

GET/companies/:id · PUT/companies/:id · DELETE/companies/:id

POST/contacts{ companyId, firstName, lastName, email?, phone?, jobTitle? }. Creating a contact under a company you can't see, or that doesn't exist, returns 404.

10. The Leads Board

Leads are unqualified interest — a name and (usually) a company name, not yet an account in the system. The board has four columns: New, Contacted, Qualified, Disqualified.

Process flow: a lead's life

1
New
Just captured — a name, maybe a company
2
Contacted
Reached out at least once
3
Qualified
Real interest, budget, fit
4
Converted
§11 — becomes a Company + Contact + Deal
Disqualified
Reachable from New or Contacted, with a required reason — a dead end, not a stage on the way to Converted

Via the Web App

  1. Open Leads and click + New Lead — first name, last name, email, phone, company name, and source are required.
  2. Drag a card from New into Contacted, then into Qualified, as you work it. A dropped card that isn't ready to qualify can be dragged into Disqualified instead — a reason is required.
  3. Once a lead reaches Qualified, a green Convert button appears on its card — that's §11.
The Leads kanban board with New/Contacted/Qualified/Disqualified columns
The Leads board — Qualified cards show the green Convert button described in §11.

Via the API

POST/leads{ firstName, lastName, email?, phone?, companyName?, source }, where source is one of WEBSITE, REFERRAL, COLD_CALL, EVENT, OTHER.

PUT/leads/:id — moves a card between the three pre-conversion columns via { status } (NEW/CONTACTED/QUALIFIED only — the two terminal states below are reached through their own endpoints).

POST/leads/:id/disqualify{ reason } (required). Disqualifying an already-converted lead returns 409 ALREADY_CONVERTED.

11. Converting a Lead into a Deal

This is Arsi CRM's flagship flow — converting a Qualified lead atomically creates a Company, a Contact, and a Deal, all linked back to the originating lead, in one transaction.

Via the Web App

  1. On a Qualified lead's card, click Convert.
  2. Give the new deal a name and, optionally, an estimated value — or link it to an existing company instead of creating a new one, if the lead turned out to belong to an account you already track.
  3. Submit. You're taken straight to the new deal's page, and the lead's status flips to CONVERTED.
1
Qualified Lead
Click Convert
2
Company
New, or an existing one you pick
+
3
Contact
The lead's person, now attached to that company
+
4
Deal
Named, with an optional value, in Prospecting
All-or-nothing All three records (or two, if you linked an existing company) are created in a single database transaction — there's no state where the lead converted but only the Deal exists without its Company/Contact.

Via the API

POST/leads/:id/convert{ dealName, dealValue?, existingCompanyId? }

{
  "success": true,
  "data": { "dealId": 23, "companyId": 14, "contactId": 14 }
}
Only a Qualified lead can convert Converting a lead that isn't QUALIFIED returns 409 NOT_QUALIFIED; converting one that's already been converted returns 409 ALREADY_CONVERTED. Under the hood, the conversion is guarded by a row lock — click Convert twice in quick succession (e.g. two browser tabs) and only one request wins; the other gets the already-converted error instead of a duplicate deal.

12. The Deals Pipeline

The Deals board has five columns matching a deal's stage: Prospecting Proposal Negotiation Won Lost.

1
Prospecting
2
Proposal
3
Negotiation
4
Won
Company → Customer
Lost
Reachable from any open stage, with a required reason — also terminal

Via the Web App

  1. Open Deals — each column header shows the count and total value of the deals in it.
  2. Drag a card forward through Prospecting → Proposal → Negotiation as it progresses.
  3. Drag into Won — a confirmation dialog asks "Mark {company} as a customer?"; confirming flips that company's status to CUSTOMER (§9) if it wasn't already.
  4. Drag into Lost — the confirm dialog requires you to type a reason before it will let you confirm.
The Deals kanban board with Prospecting/Proposal/Negotiation/Won/Lost columns
The Deals pipeline — each column header totals both the count and dollar value of what's in it.
Closed deals don't reopen Once a deal reaches WON or LOST it's closed for good — any further stage-change attempt returns 409 INVALID_TRANSITION, in the web app and the API alike.

Via the API

POST/deals{ companyId, contactId?, name, valueAmount?, expectedCloseDate? }

POST/deals/:id/change-stage{ stage, lostReason? }. Moving to LOST without lostReason returns 400 LOST_REASON_REQUIRED.

13. Tasks

A simple to-do list, optionally linked to a Company, Contact, Lead, or Deal.

Via the Web App: open Tasks — defaults to your own open tasks, due soonest first. Click + New Task (subject, due date, priority, optionally linked to a record), and click Mark done to complete one. An empty list shows "Nothing due — nice work" instead of a blank table.

My Tasks list, showing subject/due date/priority and a Mark done button
Your own open tasks, due soonest first.
The New Task dialog, with Subject/Due date/Priority fields
The "+ New Task" dialog.

Via the API: POST/tasks{ subject, dueDate?, priority, relatedToType?, relatedToId?, assignedTo? }, where priority is LOW/MEDIUM/HIGH. POST/tasks/:id/complete marks it done — calling it twice is harmless (idempotent).

14. Activity Timeline & Notes

Every Company, Contact, Lead, and Deal detail page has an Activity tab — a shared timeline component logging calls, emails, meetings, and notes against that record.

Via the Web App: open the Activity tab on any record's detail page and click + Log Activity — pick a type, an optional subject, the body, and when it happened. The Notes tab on a Company page is the same timeline, filtered to NOTE-type entries only.

Via the API: POST/activities{ type, subject?, body, occurredAt?, relatedToType, relatedToId }, where type is NOTE/CALL/EMAIL/MEETING and relatedToType is COMPANY/CONTACT/LEAD/DEAL. Logging against a record you can't see returns 404. GET/activities?relatedToType=&relatedToId= lists the timeline for one record.

15. Reports

Via the Web App: open Reports for a pipeline-by-stage funnel chart, the same monthly revenue trend as the Dashboard, and — signed in as a Manager or Admin — a Salesperson Performance leaderboard (deals won and revenue, per rep).

Reports page — pipeline-by-stage funnel, monthly revenue trend, salesperson leaderboard
Reports, signed in as Admin — the Salesperson Performance leaderboard is hidden for a Sales Rep.
ReportEndpointWho sees it
Pipeline by StageGET/reports/pipeline-by-stageEveryone (scoped to their visibility, §18)
Monthly SalesGET/reports/monthly-salesEveryone (scoped to their visibility, §18)
Salesperson PerformanceGET/reports/salesperson-performanceManagers & Admins only — 403 FORBIDDEN_ROLE for a rep

16. Admin: Team Management

Log in as an admin account (§4) to manage the sales org. These endpoints return 403 FORBIDDEN_ROLE for anyone else.

Via the Web App

  1. Open Team from the Admin section of the sidebar (only visible when signed in as Admin).
  2. Click + Invite User, fill in name, email, and role — a Sales Rep also requires picking a manager.
  3. Click Disable next to any active user to deactivate their account (you can't disable your own).
Admin Team screen — every user with name, email, role, status, and a Disable action
Team management — the ADMIN sidebar section (Team, Audit Log) only appears for admins.

Via the API

POST/users{ name, email, role, managerId? }. A SALES_REP without managerId returns 400 VALIDATION_ERROR. A duplicate email returns 409 DUPLICATE_NAME.

GET/users — list every user. PUT/users/:id — update { role?, status?, managerId? }; an admin can't set their own status to DISABLED.

17. Admin: Audit Log

Every sensitive action — a company created, a lead converted, a deal's stage changed, a user invited, a lead disqualified — is written to an append-only audit trail.

Via the Web App: open Audit Log from the Admin section — each row shows when, what action, which entity, and who did it; click a row to expand its JSON details payload.

Audit Log — a table of When/Action/Entity/User rows, append-only
The Audit Log — every row here traces back to a real action elsewhere in this guide (§9–§13).

Via the API: GET/audit-logs (Admin only) — paginated, using the same list contract as every other module (§9).

18. Ownership & Visibility

Arsi CRM enforces row-level visibility on Companies, Contacts, Leads, and Deals, based on who's signed in:

RoleSees
Sales RepOnly records they own
Sales ManagerTheir own records, plus every direct report's
AdminEvery record, platform-wide
A hidden record looks exactly like a missing one This rule is enforced in the database query layer, not hidden by the UI — a rep who edits a URL to try to open a record outside their visibility (e.g. /companies/2 belonging to another rep) gets a plain "Not found" page, and the API returns 404, never a "you're not allowed" 403. A record that doesn't exist and a record you can't see are, deliberately, indistinguishable from the outside.

To see this yourself: sign in as Arjun Rep and note which companies appear on the Companies list. Sign out and sign in as Meera Rep (seeded with no manager, §4) — she sees a completely different, non-overlapping set. Now sign in as Priya Manager (Arjun's manager) — she can see everything Arjun can, plus her own.

19. Error Responses

Every error follows the same envelope shown in §3, with one of the following catalog codes:

HTTPCodeMeaning
400VALIDATION_ERRORThe request body failed validation.
400LOST_REASON_REQUIREDMoving a deal to Lost without a lostReason.
400RECAPTCHA_REQUIRED / RECAPTCHA_FAILEDThe reCAPTCHA check on login didn't pass — complete the checkbox again (§6).
401UNAUTHORIZEDMissing/expired token, invalid credentials, or a disabled account.
403FORBIDDEN_ROLEYour account's role can't perform this action.
404COMPANY_NOT_FOUND / CONTACT_NOT_FOUND / LEAD_NOT_FOUND / DEAL_NOT_FOUND / TASK_NOT_FOUND / USER_NOT_FOUNDThe record doesn't exist, or is outside your visibility (§18).
409DUPLICATE_NAMEA company or user with that name/email already exists.
409ALREADY_CONVERTEDThe lead has already been converted.
409NOT_QUALIFIEDOnly a QUALIFIED lead can be converted.
409INVALID_TRANSITIONThe stage/status change isn't allowed from the current state.
429RATE_LIMITEDToo many login attempts (10/min/IP) — slow down and retry.
503RECAPTCHA_UNAVAILABLECouldn't reach Google to verify the reCAPTCHA token — safe to retry.
500INTERNAL_ERRORAn unexpected server-side error.

20. Full Endpoint Reference

All paths are relative to https://demo.arsiindiainfo.com/api/v1. recaptchaToken is required on login (§6).

MethodPathAuthDescription
POST/auth/loginPublic + reCAPTCHALog in with email & password
POST/auth/refreshPublicExchange a refresh token for a new pair
POST/auth/logoutBearerRevoke a refresh token
GET/users/meBearerYour own profile
GET/usersAdminList every user
POST/usersAdminInvite a user
PUT/users/:idAdminUpdate role, status, or manager
GET/companiesBearerList (paginated, searchable, sortable)
POST/companiesBearerCreate a company
GET/companies/:idBearerGet one company
PUT/companies/:idBearerUpdate a company
DELETE/companies/:idBearerDelete a company (blocked by open deals)
GET/contactsBearerList (optionally filtered by companyId)
POST/contactsBearerCreate a contact under a company
GET/contacts/:idBearerGet one contact
PUT/contacts/:idBearerUpdate a contact
DELETE/contacts/:idBearerDelete a contact
GET/leadsBearerList the leads board
POST/leadsBearerCreate a lead
GET/leads/:idBearerGet one lead
PUT/leads/:idBearerUpdate / move between New-Contacted-Qualified
POST/leads/:id/convertBearerConvert to Company + Contact + Deal
POST/leads/:id/disqualifyBearerDisqualify with a reason
GET/dealsBearerList the deals pipeline
POST/dealsBearerCreate a deal
GET/deals/:idBearerGet one deal
PUT/deals/:idBearerUpdate a deal
POST/deals/:id/change-stageBearerMove to the next pipeline stage
GET/tasksBearerList your own tasks
POST/tasksBearerCreate a task
GET/tasks/:idBearerGet one task
PUT/tasks/:idBearerUpdate a task
POST/tasks/:id/completeBearerMark a task done (idempotent)
GET/activitiesBearerList a record's timeline
POST/activitiesBearerLog an activity/note
GET/dashboard/summaryBearerKPI totals + monthly trend
GET/reports/pipeline-by-stageBearerOpen deal count/value per stage
GET/reports/monthly-salesBearerWon-deal revenue, last 12 months
GET/reports/salesperson-performanceManager/AdminPer-rep deals-won + revenue leaderboard
GET/audit-logsAdminPlatform-wide audit trail

21. FAQ & Troubleshooting

The reCAPTCHA checkbox won't check, or my login is rejected

Make sure third-party content isn't blocked by a privacy extension or browser setting — the widget loads a script from google.com. If the checkbox itself works but submission still fails with RECAPTCHA_FAILED, the token likely expired (it's valid for about two minutes) — the form automatically resets the widget for you; just complete it again and resubmit.

I opened a link to a record and got "Not found" — is it deleted?

Not necessarily. §18 explains why: a record outside your ownership scope returns the exact same result as one that never existed. If you believe you should have access, ask an admin to check the record's owner, or view it from an account higher up that owner's reporting chain.

Why can't I drag a deal back out of Won or Lost?

Closed deals are final by design (§12) — create a new deal instead if the relationship continues.

I'm getting 401 UNAUTHORIZED on every request

Your access token has likely expired (15-minute lifetime). Use POST/auth/refresh (§7) to get a new one, or sign in again. The web app does this for you automatically and you should never see it happen there.

Can I make myself an Admin or Manager?

No — by design, there is no self-service path to the ADMIN or SALES_MANAGER role, in the web app or the API. Use one of the seeded demo accounts (§4), or have an existing admin update your role from the Team screen (§16).

Where do I see the full request/response schema for every field?

The interactive Swagger UI at /api/docs documents every field, validation rule, and example for every endpoint in this guide — it's disabled on the public demo (ENVIRONMENT === 'production' turns it off), so run the project locally (see the repo's README) to browse it.

Arsi India Info logo
Need more detail? This guide covers the day-to-day workflows. Schema, stored procedures, and API contract details are documented separately in the project's docs/ folder (ER diagram, demo script, OpenAPI notes).