Skip to content

Sahaayak AI documentation

Setup, architecture and reference for Sahaayak AI, the AI back-office for Indian businesses. The public demo workspace uses seeded sample data, so don't upload confidential data there.

Questions about your deployment? Email [email protected].

Quickstart

Runs fully local with zero AWS. Flip USE_AWS=1 for the real cloud path.

cd backend && pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000

cd frontend && npm install && npm run dev    # → http://localhost:5173

cd backend && pytest tests -v                # 51 green
python simulation/simulate_demo.py           # 18/18 — the demo as a test
USE_AWS=1 python simulation/check_aws.py     # verify AWS handoff: 5/5

Demo tenant: ramesh_auto — Ramesh Auto Components, Faridabad.

Architecture

Browser ──▶ FastAPI ──▶ Sahaayak (orchestrator, Nova Pro)
   │            │            ├─ Vasool    → invoices tools
   │            │            ├─ Sourcer   → supplier tools
   │            │            ├─ Khata     → cashflow tools
   │            │            └─ Nirmata   → factory (previews + hires agents)
   │            │
   │            ├─ Store    ── USE_AWS=0: LocalStore (JSON)
   │            │              USE_AWS=1: DynamoStore  (11 tables)
   │            ├─ Notifier ── ConsoleNotifier → SESNotifier (SES)
   │            ├─ Sessions ── FileSessionManager → S3SessionManager
   │            └─ Models   ── MockModel (deterministic) → Bedrock Nova

One env var swaps every seam. Local mode is the dev/test path — the product is the AWS path. The contract between frontend and backend is frontend/mocks/contract.json.

Agents

sahayak
Orchestrator @sahayak

Routes every message to the right specialist — or to Nirmata.

vasool
Receivables @vasool

Overdue invoices, aging report, payment reminders (draft-only).

sourcer
Procurement @sourcer

Supplier search, price compare, trust scores, MOQ pooling.

khata
Cash flow @khata

Receivables vs payables timeline, 90-day-terms gap analysis.

nirmata
Agent factory @nirmata

Interviews the owner → previews a spec → hires on confirmation. Generated agents get tool allowlists + draft-only guardrails.

API reference

Rendered live from contract.json — the single source of truth.

POST/auth/login
request
{
  "provider": "google|phone|guest",
  "provider_id": "[email protected]",
  "name": "Ramesh Gupta",
  "business": "Ramesh Hardware & Electricals"
}
response
{
  "token": "<signed HMAC token>",
  "role": "owner",
  "base_role": "owner",
  "tenant_id": "ramesh_auto",
  "user": {
    "name": "Ramesh Gupta",
    "business": "Ramesh Hardware & Electricals",
    "city": "Faridabad",
    "line": "hardware"
  },
  "onboarded": true
}
GET/auth/me
response
{
  "tenant_id": "ramesh_auto",
  "role": "owner",
  "base_role": "owner",
  "perms": [
    "*"
  ],
  "exp": 1790000000
}
POST/auth/role
request
{
  "role": "viewer"
}
response
{
  "token": "<signed>",
  "role": "viewer",
  "base_role": "owner"
}
POST/auth/invite
request
{
  "role": "manager"
}
response
{
  "token": "<signed>",
  "role": "manager",
  "tenant_id": "ramesh_auto",
  "expires_in_days": 30
}
GET/dashboard/summary
response
{
  "receivables": {
    "total": 623900,
    "overdue_count": 4,
    "overdue_total": 190400,
    "due_soon_total": 180500
  },
  "capital_locked_long_terms": 88700,
  "payables_due_30d": 94400,
  "pending_approvals": 2,
  "agents": {
    "total": 4,
    "ai_hired": 1
  },
  "alerts_unread": 3,
  "recent_activity": [
    {
      "ts": "2026-09-19T02:10:00Z",
      "kind": "agent_created",
      "text": "Nirmata hired 'Logistics Agent' for you"
    },
    {
      "ts": "2026-09-19T01:44:00Z",
      "kind": "reminder_sent",
      "text": "Reminder sent to Sharma Motors (INV-0031)"
    },
    {
      "ts": "2026-09-18T22:30:00Z",
      "kind": "invoice_parsed",
      "text": "Invoice INV-0045 parsed from photo → ledger"
    }
  ]
}
POST/chat
request
{
  "tenant_id": "ramesh_auto",
  "text": "Show my overdue invoices",
  "agent_id": null,
  "chat_id": "chat-4f2a9c1d3e",
  "attachments": null
}
response
{
  "reply": "You have 4 overdue invoices totalling ₹1,90,400...",
  "agent_name": "vasool",
  "agent_tagline": "पैसा वसूलने वाला",
  "actions": [
    {
      "type": "invoices_listed",
      "data": {}
    }
  ],
  "trace": [
    "sahayak",
    "vasool"
  ],
  "usage": {
    "tools": [
      "vasool"
    ],
    "input_tokens": 0,
    "output_tokens": 0,
    "total_tokens": 0,
    "cost_usd": 0,
    "latency_ms": 12
  },
  "chat_id": "chat-4f2a9c1d3e"
}
GET/chats
response
[
  {
    "id": "chat-4f2a9c1d3e",
    "title": "Show my overdue invoices",
    "agent_id": null,
    "created_at": "2026-09-19T02:00:00Z",
    "updated_at": "2026-09-19T02:05:00Z",
    "message_count": 6,
    "preview": "You have 4 overdue invoices totalling ₹1,90,400..."
  }
]
POST/chats
request
{
  "tenant_id": "ramesh_auto",
  "agent_id": null,
  "title": ""
}
response
{
  "id": "chat-4f2a9c1d3e",
  "agent_id": null,
  "title": "Conversation",
  "created_at": "2026-09-19T02:00:00Z",
  "updated_at": "2026-09-19T02:00:00Z",
  "messages": []
}
GET/chats/{id}
response
{
  "id": "chat-4f2a9c1d3e",
  "agent_id": null,
  "title": "Show my overdue invoices",
  "created_at": "2026-09-19T02:00:00Z",
  "updated_at": "2026-09-19T02:05:00Z",
  "messages": [
    {
      "role": "user",
      "text": "Show my overdue invoices",
      "ts": "2026-09-19T02:00:00Z"
    },
    {
      "role": "agent",
      "agent": "vasool",
      "tagline": "पैसा वसूलने वाला",
      "text": "You have 4 overdue...",
      "trace": [
        "sahayak",
        "vasool"
      ],
      "actions": [],
      "usage": {},
      "mode": "chat",
      "ts": "2026-09-19T02:00:05Z"
    }
  ]
}
PATCH/chats/{id}
request
{
  "tenant_id": "ramesh_auto",
  "title": "Overdue chase",
  "agent_id": "vasool"
}
response
{
  "id": "chat-4f2a9c1d3e",
  "title": "Overdue chase",
  "agent_id": "vasool"
}
DELETE/chats/{id}
response
{
  "status": "deleted",
  "id": "chat-4f2a9c1d3e"
}
POST/chats/{id}/messages
request
{
  "tenant_id": "ramesh_auto",
  "messages": [
    {
      "role": "agent",
      "agent": "system",
      "text": "Scanned bill.jpg → INV-101"
    }
  ]
}
response
{
  "id": "chat-4f2a9c1d3e",
  "title": "Show my overdue invoices",
  "agent_id": null,
  "created_at": "2026-09-19T02:00:00Z",
  "updated_at": "2026-09-19T02:05:00Z",
  "message_count": 7,
  "preview": "Scanned bill.jpg → INV-101"
}
GET/agents
response
[
  {
    "id": "vasool",
    "name": "Vasool",
    "hindi_tagline": "पैसा वसूलने वाला",
    "description": "Tracks invoices and chases payments",
    "tools": [
      "list_overdue",
      "aging_report",
      "draft_reminder"
    ],
    "status": "active",
    "created_by": "builtin",
    "icon": "receipt",
    "stats": {
      "runs": 14,
      "last_active": "2026-09-19T02:00:00Z"
    }
  }
]
GET/agents/{id}
response
{
  "id": "agent-l7x2",
  "name": "Logistics Agent",
  "hindi_tagline": "सामान पहुँचाने वाला",
  "goal": "Find backup transport and book pickups when scheduled carriers fail",
  "persona_prompt": "...",
  "tools": [
    "list_carriers",
    "quote_pickup",
    "book_pickup"
  ],
  "created_by": "factory",
  "status": "active",
  "created_at": "2026-09-19T02:10:00Z",
  "stats": {
    "runs": 3,
    "actions_taken": 1,
    "last_active": "2026-09-19T02:15:00Z"
  }
}
GET/agents/{id}/context
response
{
  "invoice_summary": {
    "total_outstanding": 623900,
    "overdue_total": 190400,
    "oldest_overdue_days": 111
  },
  "capital_locked_90d": 88700,
  "top_defaulters": [
    {
      "buyer": "Om Sai Traders",
      "amount": 28700,
      "days": 111
    }
  ],
  "recent_invoices": []
}
POST/agents/preview
request
{
  "name": "Logistics Agent",
  "goal": "Find backup transport when pickups fail",
  "tools": [
    "list_carriers",
    "quote_pickup",
    "book_pickup"
  ]
}
response
{
  "valid": true,
  "spec": {},
  "warnings": []
}
POST/agents
request
{
  "tenant_id": "ramesh_auto",
  "name": "Logistics Agent",
  "goal": "...",
  "tools": [
    "list_carriers"
  ]
}
response
{
  "id": "agent-abc123",
  "status": "active",
  "created_by": "factory"
}
GET/tasks
response
[
  {
    "id": "task-1",
    "title": "Chase all invoices >30 days",
    "agent_id": "vasool",
    "status": "done",
    "due": "2026-09-20",
    "result": "Drafted 3 reminders",
    "created_at": "2026-09-19T01:00:00Z"
  }
]
POST/tasks
request
{
  "tenant_id": "ramesh_auto",
  "title": "Get steel quotes under ₹65",
  "agent_id": "sourcer",
  "due": "2026-09-21",
  "details": "raw steel coils"
}
response
{
  "id": "task-9",
  "status": "todo"
}
POST/tasks/{id}/run
response
{
  "task_id": "task-9",
  "status": "done",
  "agent_name": "sourcer",
  "result": "Balaji Steel at ₹62/unit...",
  "actions": []
}
PATCH/tasks/{id}
request
{
  "col": "done",
  "status": "done"
}
response
{
  "id": "task-9",
  "status": "done"
}
GET/calendar/events?from=&to=
response
[
  {
    "id": "ev-1",
    "date": "2026-09-24",
    "title": "INV-0041 due (₹56,000 — Khanna Industries)",
    "kind": "invoice_due",
    "ref_id": "inv-007"
  },
  {
    "id": "ev-2",
    "date": "2026-09-26",
    "title": "Reminder fires: INV-0035",
    "kind": "alert",
    "ref_id": "alert-1"
  },
  {
    "id": "ev-3",
    "date": "2026-09-21",
    "title": "Task: Get steel quotes",
    "kind": "task",
    "ref_id": "task-9"
  }
]
GET/notifications
response
[
  {
    "id": "n-1",
    "kind": "action_required",
    "title": "Reminder ready to send: INV-0031",
    "body": "Namaste Sharma ji...",
    "ref_id": "alert-x",
    "created_at": "2026-09-19T02:00:00Z",
    "status": "unread"
  },
  {
    "id": "n-2",
    "kind": "info",
    "title": "Logistics Agent hired",
    "body": "Nirmata created it after your transporter issue",
    "created_at": "2026-09-19T02:10:00Z",
    "status": "unread"
  },
  {
    "id": "n-3",
    "kind": "warning",
    "title": "₹58,000 cash gap next week",
    "body": "Payables due before receivables arrive",
    "created_at": "2026-09-18T06:00:00Z",
    "status": "read"
  }
]
POST/notifications/{id}/read
response
{
  "id": "n-1",
  "status": "read"
}
GET/connectors
response
[
  {
    "id": "google_calendar",
    "name": "Google Calendar",
    "icon": "calendar",
    "status": "available",
    "description": "Push invoice dues + reminders to your calendar",
    "items_synced": 0
  },
  {
    "id": "airtable",
    "name": "Airtable",
    "icon": "table",
    "status": "connected",
    "connected_at": "2026-09-18T10:00:00Z",
    "last_sync": "2026-09-19T01:00:00Z",
    "items_synced": 32,
    "description": "Sync ledger + suppliers to your base"
  },
  {
    "id": "whatsapp",
    "name": "WhatsApp",
    "icon": "message",
    "status": "available",
    "description": "Send payment reminders via wa.me deep links"
  },
  {
    "id": "tally",
    "name": "Tally",
    "icon": "ledger",
    "status": "available",
    "description": "Import existing invoices"
  },
  {
    "id": "razorpay",
    "name": "Razorpay",
    "icon": "rupee",
    "status": "available",
    "description": "Payment links in reminders"
  }
]
POST/connectors/{id}/connect
request
{
  "tenant_id": "ramesh_auto"
}
response
{
  "id": "google_calendar",
  "status": "connected",
  "connected_at": "2026-09-19T03:00:00Z"
}
POST/connectors/{id}/disconnect
response
{
  "id": "airtable",
  "status": "available"
}
GET/connectors/{id}/sync
response
{
  "id": "airtable",
  "last_sync": "2026-09-19T01:00:00Z",
  "items_synced": 32,
  "state": "ok"
}
GET/channels
response
[
  {
    "type": "telegram",
    "name": "Telegram",
    "status": "connected",
    "active": true,
    "running": true,
    "mode": "polling",
    "identity": "sahayak_bot",
    "agent_id": null,
    "approved_users": [],
    "pending_pairings": [],
    "stats": {
      "messages_in": 4,
      "messages_out": 4
    }
  },
  {
    "type": "whatsapp",
    "name": "WhatsApp Business",
    "status": "available",
    "active": false,
    "running": false,
    "mode": null,
    "identity": null,
    "agent_id": null,
    "approved_users": [],
    "pending_pairings": [],
    "stats": {}
  }
]
POST/channels/{type}/connect
request
{
  "tenant_id": "ramesh_auto",
  "bot_token": "123:abc",
  "agent_id": null,
  "webhook_url": null,
  "phone_number_id": "",
  "access_token": "",
  "app_secret": "",
  "verify_token": ""
}
response
{
  "success": true,
  "bot_username": "sahayak_bot",
  "mode": "polling"
}
POST/channels/{type}/disconnect
response
{
  "success": true,
  "status": "available"
}
GET/channels/{type}/status
response
{
  "type": "telegram",
  "status": "connected",
  "active": true,
  "running": true,
  "mode": "polling",
  "identity": "sahayak_bot",
  "approved_users": [
    {
      "chat_id": "123",
      "name": "Ramesh",
      "approved_at": "..."
    }
  ],
  "pending_pairings": [],
  "stats": {},
  "connected_at": "..."
}
GET/channels/{type}/pending-users
response
{
  "users": [
    {
      "chat_id": "123456",
      "name": "Suresh",
      "code": "482910",
      "ts": "..."
    }
  ]
}
GET/channels/{type}/approved-users
response
{
  "users": [
    {
      "chat_id": "123456",
      "name": "Suresh",
      "approved_at": "..."
    }
  ]
}
POST/channels/{type}/approve-user
request
{
  "tenant_id": "ramesh_auto",
  "chat_id": "123456"
}
response
{
  "success": true,
  "user": {
    "chat_id": "123456",
    "name": "Suresh",
    "code": "482910"
  }
}
POST/channels/{type}/deny-user
request
{
  "tenant_id": "ramesh_auto",
  "chat_id": "123456"
}
response
{
  "success": true
}
POST/channels/{type}/revoke-user
request
{
  "tenant_id": "ramesh_auto",
  "chat_id": "123456"
}
response
{
  "success": true
}
GET|POST /webhooks/{type}/{tenant_id} [EXISTS]
GET/settings
response
{
  "business": {
    "name": "Ramesh Auto Components",
    "owner": "Ramesh Gupta",
    "city": "Faridabad",
    "line": "auto components"
  },
  "onboarded": true,
  "prefs": {
    "reminder_cadence_days": 7,
    "auto_draft_reminders": true,
    "approval_mode": "manual",
    "language": "hinglish",
    "notify_email": "[email protected]",
    "disabled_tools": []
  },
  "mcp_servers": [
    {
      "id": "mcp-1",
      "name": "Tally MCP",
      "url": "http://localhost:9100/mcp",
      "status": "configured"
    }
  ]
}
PATCH/settings
request
{
  "prefs": {
    "reminder_cadence_days": 5
  }
}
response
{
  "status": "saved"
}
POST/upload
request
multipart/form-data: file=<image|pdf>, tenant_id
response
{
  "file_id": "f-001",
  "filename": "invoice_0042.jpg",
  "parsed": {
    "invoice_no": "INV-0042",
    "buyer": "Sharma Motors",
    "amount": 45200,
    "gst": 8136,
    "due_date": "2026-10-05",
    "items": "Brake pads x200",
    "confidence": 0.93
  },
  "low_confidence_fields": []
}
GET/invoices?tenant_id=&status=
response
[
  {
    "id": "inv-001",
    "invoice_no": "INV-0038",
    "buyer": "Sharma Motors",
    "amount": 45200,
    "due_date": "2026-09-10",
    "status": "overdue",
    "days_overdue": 9
  }
]
POST/invoices/{id}/reminder
response
{
  "draft_id": "draft-1",
  "status": "draft",
  "body": "Namaste Sharma ji...",
  "requires_approval": true
}
POST/alerts/{id}/approve
response
{
  "id": "alert-1",
  "status": "sent",
  "via": "console|ses"
}
GET/suppliers?tenant_id=&q=
response
[
  {
    "id": "sup-1",
    "name": "Balaji Steel",
    "category": "raw steel",
    "trust_score": 4.2,
    "moq": 500,
    "price_per_unit": 62,
    "stock": "in_stock",
    "lead_days": 4
  }
]
GET/carriers?tenant_id=&to=
response
[
  {
    "id": "car-1",
    "name": "VRL Logistics",
    "route": "Faridabad→Ludhiana",
    "rate_per_kg": 4.5,
    "next_slot": "tomorrow 7am",
    "reliability": 4.6
  }
]
GET/alerts?tenant_id=&status=
response
[
  {
    "id": "alert-1",
    "kind": "reminder",
    "title": "INV-0038 due in 3 days",
    "status": "pending_approval",
    "fires_at": "2026-09-19T06:00:00Z",
    "body": "..."
  }
]
GET/cashflow?tenant_id=
response
{
  "receivables": [
    {
      "buyer": "Sharma Motors",
      "amount": 45200,
      "expected": "2026-10-05"
    }
  ],
  "payables": [
    {
      "vendor": "Balaji Steel",
      "amount": 31000,
      "due": "2026-09-25"
    }
  ],
  "gaps": [
    {
      "period": "Oct wk1",
      "shortfall": 58000,
      "reason": "90-day buyer terms vs 30-day supplier terms"
    }
  ]
}
POST/import/excel
request
multipart/form-data: file=<.xlsx>, tenant_id
response
{
  "file_id": "f-imp-1",
  "filename": "ledger.xlsx",
  "collection": "invoices",
  "imported": 34,
  "skipped": 2,
  "sample": [
    {
      "invoice_no": "INV-0101",
      "buyer": "Kapil Auto",
      "amount": 12400,
      "due_date": "2026-10-02"
    }
  ]
}
GET/memories?tenant_id=
response
[
  {
    "id": "mem-1",
    "text": "Sharma Traders always pays around 45 days — don't push hard",
    "source": "owner",
    "created_at": "2026-09-19T03:00:00Z"
  }
]
POST/memories
request
{
  "tenant_id": "ramesh_auto",
  "text": "Big orders go through GST invoice only",
  "source": "owner|onboarding"
}
response
{
  "id": "mem-2",
  "status": "saved"
}
DELETE/memories/{id}
response
{
  "status": "deleted"
}
GET/search?q=&tenant_id=
response
{
  "q": "sharma",
  "results": {
    "invoices": [
      {
        "id": "inv-001",
        "title": "INV-0038 — Sharma Motors",
        "meta": "₹45,200 · overdue 9d",
        "ref": "#/app"
      }
    ],
    "suppliers": [
      {
        "id": "sup-1",
        "title": "Balaji Steel",
        "meta": "raw steel · trust 4.2",
        "ref": "#/app"
      }
    ],
    "carriers": [],
    "agents": [],
    "tasks": [],
    "memories": [],
    "documents": [
      {
        "id": "f-001",
        "title": "invoice_0042.jpg",
        "meta": "uploaded 18 Sep",
        "ref": "#/app"
      }
    ]
  }
}
GET/artifacts?tenant_id=
response
[
  {
    "id": "art-1",
    "title": "Tracking — ORD-1042",
    "template": "tracking_page",
    "visibility": "public",
    "created_by": "agent-l7x2",
    "created_at": "2026-09-19T03:00:00Z",
    "share_path": "/a/art-1"
  }
]
GET/artifacts/{id}
response
{
  "id": "art-1",
  "title": "Tracking — ORD-1042",
  "template": "tracking_page",
  "visibility": "public",
  "data": {
    "order_id": "ORD-1042",
    "carrier": "SafeRoad Carriers",
    "from": "Ludhiana",
    "to": "Faridabad",
    "eta": "tomorrow 11am",
    "status": "in_transit",
    "progress_pct": 62
  },
  "created_by": "agent-l7x2",
  "created_at": "2026-09-19T03:00:00Z",
  "share_path": "/a/art-1"
}
GET/public/artifacts/{id}
response
"same as GET /artifacts/{id}"
PATCH/artifacts/{id}
request
{
  "tenant_id": "ramesh_auto",
  "visibility": "public"
}
response
"the updated artifact row"
POST/artifacts
GET/people?tenant_id=
response
{
  "summary": {
    "customers": 5,
    "suppliers": 20,
    "carriers": 6,
    "outstanding": 645400
  },
  "customers": [
    {
      "id": "cust-sharma-motors",
      "name": "Sharma Motors",
      "invoices": 3,
      "billed": 152700,
      "outstanding": 152700,
      "worst_overdue_days": 37,
      "defaulter": true
    }
  ],
  "suppliers": [
    {
      "id": "sup-01",
      "name": "Balaji Steel Traders",
      "category": "raw steel coils",
      "trust_score": 4.2
    }
  ],
  "carriers": [
    {
      "id": "car-1",
      "name": "VRL Logistics",
      "route": "Faridabad→Ludhiana",
      "rate_per_kg": 4.5
    }
  ]
}
GET/logs?tenant_id=
response
[
  {
    "id": "act-1",
    "ts": "2026-09-19T02:10:00Z",
    "kind": "agent_created",
    "text": "Nirmata hired 'Logistics Agent'"
  }
]
GET/metrics?tenant_id=
response
{
  "runs": 12,
  "total_tokens": 48213,
  "est_cost_usd": 0.0412,
  "avg_latency_ms": 1830,
  "by_tool": {
    "vasool": 5,
    "sourcer": 2
  },
  "recent": []
}
GET/templates?tenant_id=
response
[
  {
    "id": "chase-overdue",
    "category": "money",
    "title": "Chase overdue payments",
    "prompt": "Show my overdue invoices and draft a polite reminder for the oldest one.",
    "runs_on": "vasool"
  },
  {
    "id": "digital-presence",
    "category": "presence",
    "title": "Digital presence agent",
    "prompt": "I want to sell online — build me an agent that lists on Facebook Marketplace + IndiaMART and keeps listings SEO-optimized.",
    "runs_on": "nirmata",
    "agent_spec": {
      "name": "Digital Presence Agent",
      "goal": "Publish products to online marketplaces and keep listings SEO-optimized",
      "tools": []
    }
  }
]
POST/templates/{id}/install
request
{
  "tenant_id": "ramesh_auto"
}
response
{
  "id": "agent-abc123",
  "status": "active",
  "created_by": "factory"
}
POST/context/upload
request
multipart/form-data: file=<any> OR text=<note>, tenant_id
response
{
  "id": "doc-1",
  "filename": "GSTR-3B_FY25.xlsx",
  "kind": "spreadsheet",
  "tags": [
    "tax",
    "finance"
  ],
  "summary": "GSTR-3B filing data for FY25, 18 rows",
  "status": "fed_to_agents",
  "created_at": "2026-09-20T04:00:00Z"
}
GET/context?tenant_id=
response
[
  {
    "id": "doc-1",
    "filename": "GSTR-3B_FY25.xlsx",
    "kind": "spreadsheet",
    "tags": [
      "tax",
      "finance"
    ],
    "summary": "GSTR-3B filing data for FY25",
    "status": "fed_to_agents",
    "created_at": "2026-09-20T04:00:00Z"
  }
]
DELETE/context/{id}
response
{
  "status": "deleted"
}
POST/onboarding
request
{
  "tenant_id": "ramesh_auto",
  "business": {
    "name": "Ramesh Auto Components",
    "owner": "Ramesh Gupta",
    "city": "Faridabad",
    "phone": "",
    "type": "manufacturer",
    "line": "auto components",
    "products": "brake pads",
    "materials": "steel coils",
    "years": 12
  },
  "prefs": {
    "language": "hinglish",
    "reminder_tone": "polite",
    "approval_mode": "manual",
    "notify_email": "[email protected]",
    "credit_terms_days": 45,
    "uses_gst": true
  },
  "pains": [
    "late_payments",
    "gst",
    "no_online_presence"
  ],
  "slow_payers": [
    "Sharma Motors"
  ],
  "key_buyers": [
    "Khanna Industries"
  ],
  "key_suppliers": [
    "Balaji Steel"
  ],
  "rules": [
    "Big orders go through GST invoice only"
  ],
  "tools_today": [
    "excel",
    "whatsapp"
  ],
  "auto_hire": true
}
response
{
  "tenant_id": "ramesh_auto",
  "onboarded": true,
  "memories_created": 4,
  "agents_installed": [
    {
      "id": "agent-abc123",
      "name": "Collections Agent"
    }
  ],
  "suggested_agents": [
    {
      "template_id": "digital-presence",
      "title": "Digital presence agent",
      "reason": "no online presence"
    }
  ],
  "next_steps": [
    "Import your existing ledger from Excel"
  ]
}
GET/approvals?tenant_id=&status=
response
[
  {
    "id": "apr-1",
    "tool": "create_invoice",
    "title": "Add invoice for Kapil Auto (₹12,400)",
    "summary": "Invoice ₹12,400 to Kapil Auto, due 2026-10-19",
    "agent": "vasool",
    "risk": "ask",
    "status": "pending|executed|denied|failed",
    "args": {},
    "created_at": "2026-09-26T04:00:00Z"
  }
]
POST/approvals/{id}/approve
response
{
  "id": "apr-1",
  "status": "executed",
  "result": {},
  "result_ref": {
    "kind": "invoice",
    "id": "inv-x",
    "label": "INV-2302"
  }
}
POST/approvals/{id}/deny
response
{
  "id": "apr-1",
  "status": "denied"
}
POST/approvals/grant
request
{
  "tenant_id": "ramesh_auto",
  "tool": "create_invoice",
  "on": true
}
response
{
  "tool": "create_invoice",
  "granted": true,
  "grants": [
    "create_invoice"
  ]
}
GET/approvals/grants
response
{
  "grants": [
    "sync_catalog"
  ],
  "gated_tools": [
    "create_artifact",
    "create_invoice",
    "publish_listing",
    "schedule_alert",
    "sync_catalog"
  ]
}
POST/alerts/{id}/dismiss
response
{
  "id": "alert-1",
  "status": "dismissed"
}
GET/roles
response
[
  {
    "id": "compliance",
    "name": "Compliance Agent",
    "hindi_tagline": "कानून का ख्याल",
    "category": "compliance",
    "icon": "shield",
    "description": "Watches GST/TDS deadlines",
    "goal": "Track GST/TDS filing deadlines",
    "default_tools": [
      "list_alerts",
      "schedule_alert"
    ],
    "allowed_tools": [
      "list_alerts",
      "schedule_alert",
      "aging_report"
    ],
    "tool_limit": 4,
    "max_action": "draft_only",
    "extras": [
      "artifact",
      "memory"
    ],
    "intent_keywords": [
      "gst"
    ]
  }
]
POST/agents/batch
request
{
  "tenant_id": "ramesh_auto",
  "roles": [
    {
      "role_id": "compliance",
      "tools": [
        "list_alerts"
      ]
    },
    {
      "role_id": "digital_presence"
    }
  ]
}
response
{
  "hired": [
    {
      "id": "agent-ab12cd",
      "name": "Compliance Agent",
      "role_id": "compliance",
      "tools": [
        "list_alerts"
      ],
      "refused_tools": []
    }
  ],
  "errors": [
    {
      "role_id": "ghost",
      "error": "unknown role"
    }
  ]
}
POST/integrations/mcp/{id}/test
response
{
  "id": "mcp-1",
  "status": "connected",
  "tools": [
    {
      "name": "list_overdue_invoices",
      "read_only": true,
      "description": "…"
    }
  ],
  "last_checked": "2026-09-26T10:00:00Z"
}
POST/integrations/mcp/token
response
{
  "token": "<signed, scope=mcp>",
  "endpoint_path": "/mcp",
  "expires_in_days": 90,
  "tools": [
    "aging_report",
    "create_invoice",
    "list_overdue_invoices"
  ]
}
GET/marketplace
response
{
  "mcp": [
    {
      "id": "custom",
      "name": "Any MCP server",
      "icon": "mcp",
      "category": "custom",
      "desc": "…",
      "url_hint": "https://…/mcp"
    }
  ],
  "installed": [],
  "sahayak_mcp_tools": [
    "list_overdue_invoices"
  ]
}
POST/scheduler/run
response
{
  "moved": 1
}
GET/sent
response
[
  {
    "id": "msg-1",
    "to": "[email protected]",
    "subject": "...",
    "via": "console",
    "sent_at": "..."
  }
]
POST/demo/reset
response
{
  "status": "reseeded",
  "tenants": [
    "ramesh_auto"
  ]
}
POST/context/reembed
response
{
  "model": "amazon.titan-embed-text-v2:0",
  "reembedded": 10,
  "skipped": 0
}
GET/health
response
{
  "status": "ok",
  "use_aws": false,
  "model": "mock|bedrock|anthropic|openai|ollama|litellm",
  "provider": "bedrock",
  "orchestrator_model": "apac.amazon.nova-pro-v1:0",
  "worker_model": "apac.amazon.nova-lite-v1:0",
  "embed_provider": "titan|openai|ollama|none",
  "embed_model": "amazon.titan-embed-text-v2:0"
}

AWS setup

ServiceUsed for
Bedrock — Nova Pro / LiteOrchestrator + specialists (Strands BedrockModel); Nova Lite parses invoice photos
Strands Agents SDKAgent runtime, agents-as-tools orchestration, session managers
DynamoDBAll state — 11 tables, PAY_PER_REQUEST, PK tenant_id + SK id
S3Agent session persistence + invoice uploads
SESPayment reminders & alerts (sandbox = verified recipients only)
Lambda + MangumServerless API (handler already wired in main.py)
EventBridgeScheduled alert promotion (replaces the local scheduler thread)

Environment

USE_AWS=1
AWS_REGION=ap-south-1
ORCHESTRATOR_MODEL=apac.amazon.nova-pro-v1:0
WORKER_MODEL=apac.amazon.nova-lite-v1:0
S3_BUCKET=…               # sessions + uploads
SES_SENDER=…              # must be a verified SES identity
DDB_TABLE_*=sahayak-*     # 11 tables, created by Ayush
# credentials: ~/.aws profile OR AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY

Verify a handoff in one command: USE_AWS=1 python simulation/check_aws.py → creds, tables, bucket, SES sender, Bedrock converse.

Demo script

This is literally simulate_demo.py — if the sim is green, the demo is green.

  1. 1Reset demo → roster shows Vasool, Sourcer, Khata
  2. 2"show my overdue invoices" → Sahaayak routes to Vasool, real ₹ amounts
  3. 3Upload invoice photo → parsed into the ledger
  4. 4Draft a reminder → approve → sent (SES / console)
  5. 5"mera transporter nahi aaya" → Nirmata previews a Logistics Agent
  6. 6"haan, create it" → agent materializes in the roster — HIRED BY AI badge
  7. 7"cheapest pickup to ludhiana?" → the new agent answers with real carriers
  8. 8Alerts + cashflow views → "it works while you sleep"

Team & ownership

fahmin
Fahmin
frontend · product · demo

All UI — landing, dashboard, agent views, chat, calendar, connectors. Builds against contract.json, never waits on backend.

ayush
Ayush
backend · AWS

FastAPI surface, agents, store/notifier seams, DynamoDB/SES/S3/Bedrock, deploy. Sees AYUSH.md first.

Merge gate for both: pytest + simulate_demo.py green.