Complete REST API specification with role-based access control for all 80+ endpoints.
| Method | Route | Description | Auth |
|---|---|---|---|
| GET | /health | Health check | None |
| GET | /api/v1/tenants/discover | Search tenants by name/city/state | None |
| GET | /api/v1/tenants/:slug/public | Tenant public profile + branding | None |
| GET | /api/v1/tenants/:slug/causes | Public donation catalog | None |
| GET | /api/v1/tenants/:slug/trust-profile | Public trust info (visibility-gated) | None |
| GET | /api/v1/tenants/:slug/events | Public events list | None |
| GET | /api/v1/tenants/:slug/gallery | Public gallery albums | None |
| GET | /api/v1/tenants/:slug/gau-mahima | Public gau mahima content | None |
| GET | /api/v1/tenants/:slug/trustees | Public trustees list | None |
| GET | /api/v1/tenants/qr/:qrToken | QR / deeplink tenant resolution | None |
| GET | /api/v1/platform/plans | List public SaaS plans | None |
| Method | Route | Description |
|---|---|---|
| POST | /api/v1/auth/app/request-otp | Send OTP to phone/email |
| POST | /api/v1/auth/app/verify-otp | Verify OTP โ JWT tokens |
| POST | /api/v1/auth/app/google | Google OAuth login |
| POST | /api/v1/auth/app/apple | Apple Sign-In |
| POST | /api/v1/auth/app/refresh | Refresh access token |
| POST | /api/v1/auth/app/logout | Invalidate tokens |
| GET | /api/v1/auth/app/me | Get current user profile |
| PUT | /api/v1/auth/app/me | Update profile (name/email/PAN/addr) |
| POST | /api/v1/auth/app/fcm-token | Register/update FCM token |
| Method | Route | Description |
|---|---|---|
| POST | /api/v1/auth/admin/login | Email/password login |
| POST | /api/v1/auth/admin/verify-mfa | MFA verification (TOTP) |
| POST | /api/v1/auth/admin/refresh | Refresh token |
| POST | /api/v1/auth/admin/logout | Logout |
| POST | /api/v1/auth/admin/forgot-password | Password reset request |
| POST | /api/v1/auth/admin/reset-password | Reset password with token |
| GET | /api/v1/auth/admin/me | Current admin user + permissions |
| POST | /api/v1/auth/admin/change-password | Change password |
| Method | Route | Description |
|---|---|---|
| POST | /api/v1/auth/platform/login | Platform admin login |
| POST | /api/v1/auth/platform/verify-mfa | MFA verification |
| POST | /api/v1/auth/platform/refresh | Refresh token |
| GET | /api/v1/auth/platform/me | Current platform admin |
| POST | /api/v1/auth/platform/impersonate/:tenantAdminId | Impersonate tenant admin (audited) |
| DELETE | /api/v1/auth/platform/impersonate | End impersonation session |
| Method | Route | Description | Auth |
|---|---|---|---|
| GET | /api/v1/t/causes | List active causes for tenant | App User |
| GET | /api/v1/t/causes/:id | Cause detail + options | App User |
| POST | /api/v1/t/donations/initiate | Create donation order (idempotency key) | App User |
| POST | /api/v1/t/donations/:id/verify | Verify payment signature | App User |
| GET | /api/v1/t/donations | Donor's donation history | App User |
| GET | /api/v1/t/donations/:id/receipt | Download PDF receipt / 80G cert | App User |
| POST | /api/v1/t/recurring/subscribe | Create recurring mandate (eNACH/UPI) | App User |
| GET | /api/v1/t/recurring | List active recurring plans | App User |
| PATCH | /api/v1/t/recurring/:id/pause | Pause recurring plan | App User |
| DELETE | /api/v1/t/recurring/:id | Cancel recurring plan | App User |
| GET | /api/v1/t/gau-coins/balance | Donor's coin balance | App User |
| GET | /api/v1/t/gau-coins/transactions | Coin earn/redeem history | App User |
| POST | /api/v1/t/gau-coins/redeem | Apply coins to donation | App User |
| GET | /api/v1/t/cows | List available/adopted cows |
| GET | /api/v1/t/cows/:id | Cow detail + sponsor info |
| POST | /api/v1/t/cows/:id/adopt | Initiate cow adoption payment |
| GET | /api/v1/t/adoptions | Donor's adoption list |
| GET | /api/v1/t/adoptions/:id/updates | Cow health/photo updates |
| GET | /api/v1/t/membership/plans | List membership tiers |
| POST | /api/v1/t/membership/subscribe | Purchase membership |
| GET | /api/v1/t/membership/my | Current membership + card |
| POST | /api/v1/t/membership/renew | Renew membership |
| POST | /api/v1/t/volunteers/register | Volunteer registration |
| GET | /api/v1/t/volunteers/shifts | Available volunteer shifts |
| POST | /api/v1/t/volunteers/shifts/:id/book | Book a shift |
| GET | /api/v1/t/volunteers/my-hours | Volunteer hours log |
| GET | /api/v1/t/gallery | List gallery albums |
| GET | /api/v1/t/gallery/:albumId/media | Album media items |
| GET | /api/v1/t/gau-mahima | Gau Mahima articles/videos |
| GET | /api/v1/t/gau-mahima/:id | Single content item |
| Method | Route | Description |
|---|---|---|
| GET | /api/v1/platform/tenants | List all tenants (paginated, filterable) |
| POST | /api/v1/platform/tenants | Onboard new tenant |
| GET | /api/v1/platform/tenants/:id | Tenant detail with full config |
| PUT | /api/v1/platform/tenants/:id | Update tenant settings |
| POST | /api/v1/platform/tenants/:id/suspend | Suspend tenant |
| POST | /api/v1/platform/tenants/:id/activate | Activate / unsuspend tenant |
| GET | /api/v1/platform/subscriptions | List all SaaS subscriptions |
| POST | /api/v1/platform/subscriptions | Create subscription for tenant |
| POST | /api/v1/platform/subscriptions/:id/upgrade | Upgrade plan (prorated) |
| GET | /api/v1/platform/invoices | Platform invoices list |
| POST | /api/v1/platform/invoices/:id/retry | Retry failed invoice payment |
| GET | /api/v1/platform/analytics | Cross-tenant analytics |
| GET | /api/v1/platform/audit-logs | Platform audit log trail |
| GET | /api/v1/platform/plans | List/manage SaaS plans |
| PUT | /api/v1/platform/plans/:id | Update plan limits & pricing |
/webhooks/razorpay/:tenantSlug
Razorpay
/webhooks/payu/:tenantSlug
PayU
/webhooks/platform-billing
Platform
โ Full Access ๐ Read Only โ๏ธ Own Records โ No Access
| Resource / Action | Platform Admin | Tenant Admin | Finance Mgr | Content Mgr | App User |
|---|---|---|---|---|---|
| Tenant CRUD | โ | โ | โ | โ | โ |
| SaaS Plans | โ | ๐ | โ | โ | โ |
| Platform Billing | โ | ๐ | โ | โ | โ |
| Impersonation | โ | โ | โ | โ | โ |
| Platform Audit Logs | โ | โ | โ | โ | โ |
| Donation Causes CRUD | โ | โ | โ | โ | โ |
| View Transactions | โ | โ | โ | โ | โ๏ธ |
| Export Reports | โ | โ | โ | โ | โ |
| Payment Gateway Config | โ | โ | โ | โ | โ |
| Refund Donations | โ | โ | โ | โ | โ |
| Gau Coins Config | โ | โ | โ | โ | โ |
| Cow Adoptions CRUD | โ | โ | โ | โ | โ |
| Membership Plans | โ | โ | โ | โ | โ |
| Events CRUD | โ | โ | โ | โ | โ |
| Gallery Upload | โ | โ | โ | โ | โ |
| CMS Content | โ | โ | โ | โ | โ |
| Trust & Compliance | โ | โ | ๐ | โ | โ |
| User & RBAC Mgmt | โ | โ | โ | โ | โ |
| Notifications Send | โ | โ | โ | โ | โ |
| Tenant Audit Logs | โ | โ | ๐ | ๐ | โ |
| Own Donations | โ | โ | โ | โ | โ๏ธ |
| Own Profile | โ | โ | โ | โ | โ๏ธ |
| PDF Receipts | โ | โ | โ | โ | โ๏ธ |
| GauCoins Balance | โ | โ | โ | โ | โ๏ธ |
import { Request, Response, NextFunction } from 'express';
import { redis } from '../config/redis';
import { db } from '../config/database';
export async function tenantResolverMiddleware(
req: Request & { tenant?: any },
res: Response,
next: NextFunction
) {
try {
// 1. Extract tenant identifier
const subdomain = req.hostname.split('.')[0]; // trust1.gaudaan.in
const headerTenantId = req.headers['x-tenant-id'] as string;
const identifier = headerTenantId || subdomain;
if (!identifier || identifier === 'api' || identifier === 'admin') {
return next(); // Platform-level route
}
// 2. Check Redis cache (TTL: 5 minutes)
const cacheKey = `tenant:${identifier}`;
const cached = await redis.get(cacheKey);
if (cached) {
req.tenant = JSON.parse(cached);
return next();
}
// 3. Database lookup
const tenant = await db.query(
`SELECT id, slug, name, status, plan_id, settings
FROM tenants WHERE slug = $1 OR id::text = $1 LIMIT 1`,
[identifier]
);
if (!tenant.rows[0]) {
return res.status(404).json({ error: 'Tenant not found' });
}
if (tenant.rows[0].status === 'SUSPENDED') {
return res.status(403).json({ error: 'Tenant suspended' });
}
// 4. Cache and attach
await redis.setex(cacheKey, 300, JSON.stringify(tenant.rows[0]));
req.tenant = tenant.rows[0];
next();
} catch (err) {
next(err);
}
}
import { Request, Response, NextFunction } from 'express';
type Role = 'PLATFORM_ADMIN' | 'TENANT_ADMIN' | 'FINANCE_MANAGER' | 'CONTENT_MANAGER' | 'APP_USER';
const PERMISSIONS: Record<string, Role[]> = {
'tenant:create': ['PLATFORM_ADMIN'],
'tenant:update': ['PLATFORM_ADMIN', 'TENANT_ADMIN'],
'donation:read': ['PLATFORM_ADMIN', 'TENANT_ADMIN', 'FINANCE_MANAGER', 'APP_USER'],
'donation:export': ['PLATFORM_ADMIN', 'TENANT_ADMIN', 'FINANCE_MANAGER'],
'gateway:config': ['PLATFORM_ADMIN', 'TENANT_ADMIN'],
'event:create': ['PLATFORM_ADMIN', 'TENANT_ADMIN', 'CONTENT_MANAGER'],
'audit:read': ['PLATFORM_ADMIN', 'TENANT_ADMIN'],
};
export function requirePermission(permission: string) {
return (req: Request & { user?: any }, res: Response, next: NextFunction) => {
const userRole: Role = req.user?.role;
const allowed = PERMISSIONS[permission];
if (!userRole || !allowed?.includes(userRole)) {
return res.status(403).json({
error: 'Insufficient permissions',
required: permission
});
}
next();
};
}
export interface PaymentProvider {
name: string;
createOrder(params: CreateOrderParams): Promise<OrderResult>;
verifySignature(params: VerifyParams): boolean;
initiateRefund(params: RefundParams): Promise<RefundResult>;
createRecurringMandate(params: MandateParams): Promise<MandateResult>;
cancelMandate(mandateId: string): Promise<void>;
processWebhook(payload: unknown, signature: string): Promise<WebhookEvent>;
}
// Razorpay implementation
export class RazorpayProvider implements PaymentProvider {
name = 'razorpay';
private keyId: string;
private keySecret: string;
constructor(private config: GatewayConfig) {
// Keys are decrypted from DB on instantiation
this.keyId = decrypt(config.key_id_encrypted);
this.keySecret = decrypt(config.key_secret_encrypted);
}
async createOrder(params: CreateOrderParams): Promise<OrderResult> {
const razorpay = new Razorpay({ key_id: this.keyId, key_secret: this.keySecret });
const order = await razorpay.orders.create({
amount: params.amount * 100, // paise
currency: 'INR',
receipt: params.idempotencyKey,
});
return { orderId: order.id, amount: params.amount };
}
verifySignature(params: VerifyParams): boolean {
const expectedSig = crypto
.createHmac('sha256', this.keySecret)
.update(`${params.orderId}|${params.paymentId}`)
.digest('hex');
return expectedSig === params.signature;
}
}