Base URL:
http://localhost:3500/api/v1Local Port:3500(set in.env) API Version:v1(set in.env)
Create a Postman Environment with these variables:
| Variable | Initial Value | Description |
|---|---|---|
base_url |
http://localhost:3500/api/v1 |
Base API URL |
access_token |
(set after login) | PASETO access token (Bearer) |
admin_token |
(set after admin login) | PASETO access token for admin user |
refresh_token |
(auto-set via cookie) | Set as HttpOnly cookie by server |
org_id |
(set after createOrg) | MongoDB ObjectId of your org |
contract_id |
(set after upload) | MongoDB ObjectId of a contract |
analysis_id |
(set after requestAnalysis) | MongoDB ObjectId of an analysis |
user_id |
(set after login) | MongoDB ObjectId of your user |
notification_id |
(from GET /notifications) | MongoDB ObjectId of a notification |
otp |
(from email / dev response) | 6-digit OTP for email verification |
reset_token |
(from forgot-password email) | Hex token for password reset |
session_jti |
(from GET /auth/sessions) | UUID JTI of a session to revoke |
invite_token |
(from invitation email) | Invitation acceptance token |
comment_id |
(from POST /comments) | MongoDB ObjectId of a comment |
template_id |
(from POST /templates) | MongoDB ObjectId of a template |
share_link_id |
(from POST /shares) | MongoDB ObjectId of a share link |
bookmark_contract_id |
(from POST /bookmarks) | Contract ID of a bookmarked item |
🔒 Protected routes require:
Authorization: Bearer {{access_token}}🍪 Refresh token: automatically stored as HttpOnly cookie namedrefreshToken
No authentication required. Used by Docker / load balancers.
GET http://localhost:3500/health
Headers: (none)
Success Response (200):
{
"status": "ok",
"services": { "mongodb": "up", "redis": "up", "rabbitmq": "up" },
"timestamp": "2026-03-03T17:50:00.000Z",
"uptime": 3600
}Degraded Response (503):
{
"status": "degraded",
"services": { "mongodb": "up", "redis": "down", "rabbitmq": "up" },
"timestamp": "2026-03-03T17:50:00.000Z",
"uptime": 100
}Rate-limited. Public endpoints do NOT need a token.
POST {{base_url}}/auth/register
Content-Type: application/json
{
"name": "Vishal Sanam",
"email": "vishal@example.com",
"password": "SecurePass@123"
}Password rules: min 8 chars, uppercase + lowercase + digit + special char (@$!%*?&.,\-_#^()).
Success (201):
{
"success": true,
"message": "Registration successful. A 6-digit OTP has been sent to your email.",
"data": { "userId": "65f1a2b3c4d5e6f7a8b9c0d1", "email": "vishal@example.com", "otp": "482910" }
}
⚠️ otpis only in the response in development mode.
POST {{base_url}}/auth/verify-email
Content-Type: application/json
{ "email": "vishal@example.com", "otp": "{{otp}}" }OTP is 6 digits, expires in 10 minutes.
Success (200):
{ "success": true, "message": "Email verified successfully. You can now log in." }POST {{base_url}}/auth/resend-verification-email
Content-Type: application/json
{ "email": "vishal@example.com" }Success (200):
{ "success": true, "message": "If this email exists and is unverified, a new OTP has been sent." }POST {{base_url}}/auth/login
Content-Type: application/json
{ "email": "vishal@example.com", "password": "SecurePass@123" }Success (200):
{
"success": true,
"message": "Login successful.",
"data": {
"accessToken": "v3.local.abcdef1234567890...",
"user": { "id": "65f1a2b3c4d5e6f7a8b9c0d1", "name": "Vishal Sanam", "email": "vishal@example.com", "role": "admin" }
}
}🍪 Server sets
refreshTokenHttpOnly cookie automatically. Copydata.accessToken→access_token. Copydata.user.id→user_id.
POST {{base_url}}/auth/refresh-token
No body needed — reads refreshToken cookie automatically.
Success (200):
{ "success": true, "data": { "accessToken": "v3.local.abcdef1234567890..." } }POST {{base_url}}/auth/forgot-password
Content-Type: application/json
{ "email": "vishal@example.com" }Success (200):
{ "success": true, "message": "If this email is registered, a password reset link has been sent." }Copy the token from the reset email link →
reset_tokenenv var.
POST {{base_url}}/auth/reset-password
Content-Type: application/json
{ "token": "{{reset_token}}", "password": "NewSecurePass@456" }Token is a 64-char hex string, expires in 1 hour.
Success (200):
{ "success": true, "message": "Password reset successfully. You can now log in with your new password." }POST {{base_url}}/auth/logout
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "Logged out successfully." }Blacklists both access and refresh tokens in Redis. Cookie is cleared.
POST {{base_url}}/auth/change-password
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "currentPassword": "SecurePass@123", "newPassword": "NewSecurePass@456" }Success (200):
{ "success": true, "message": "Password changed successfully." }GET {{base_url}}/auth/sessions
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": { "sessions": [{ "jti": "a1b2c3d4-e5f6-...", "createdAt": "2026-03-01T10:00:00Z" }] }
}Copy a
jti→session_jtienv var.
DELETE {{base_url}}/auth/sessions/{{session_jti}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "Session revoked." }DELETE {{base_url}}/auth/sessions
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "All sessions revoked." }All routes require
Authorization: Bearer {{access_token}}
GET {{base_url}}/users/me
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": {
"user": { "_id": "65f1a2b3c4d5e6f7a8b9c0d1", "name": "Vishal Sanam", "email": "vishal@example.com", "role": "admin", "isVerified": true }
}
}PATCH {{base_url}}/users/me
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "name": "Vishal S." }Only name can be updated here. Email changes require a separate verification flow.
Success (200):
{
"success": true,
"data": { "user": { "_id": "65f1a2b3c4d5e6f7a8b9c0d1", "name": "Vishal S.", "email": "vishal@example.com" } }
}GET {{base_url}}/users/{{user_id}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "data": { "user": { ... } } }All routes require
Authorization: Bearer {{access_token}}
POST {{base_url}}/orgs
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "name": "LexAI Legal Ltd" }Success (201):
{
"success": true,
"data": {
"org": { "id": "65f1a2b3c4d5e6f7a8b9c0d2", "name": "LexAI Legal Ltd", "slug": "lexai-legal-ltd", "plan": "free", "memberCount": 1 }
}
}Copy
data.org.id→org_idenv var.
GET {{base_url}}/orgs/{{org_id}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "data": { "org": { ... } } }PATCH {{base_url}}/orgs/{{org_id}}
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "name": "LexAI Legal Group" }Success (200):
{ "success": true, "data": { "org": { ... } } }POST {{base_url}}/orgs/{{org_id}}/invite
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "email": "newmember@example.com", "role": "viewer" }Roles: "admin", "manager", "viewer" (default: "viewer").
Success (200):
{
"success": true,
"message": "Invitation sent to newmember@example.com",
"data": { "invitationId": "65f1a2b3c4d5e6f7a8b9c0d3", "expiresAt": "2026-03-10T17:00:00.000Z" }
}POST {{base_url}}/orgs/{{org_id}}/invite/accept
Content-Type: application/json
{ "token": "{{invite_token}}", "name": "New Member", "password": "Welcome@123" }name and password required only if the user has no existing account.
Success (200):
{ "success": true, "message": "Invitation accepted. Your account has been created.", "data": { ... } }PATCH {{base_url}}/orgs/{{org_id}}/members/{{user_id}}/role
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "role": "manager" }Success (200):
{ "success": true, "message": "Member role updated successfully." }DELETE {{base_url}}/orgs/{{org_id}}/members/{{user_id}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "Member removed from organization." }All routes require
Authorization: Bearer {{access_token}}.orgIdis resolved automatically from your token — you never need to send it.
Quickstart order: Create → List → Get → Update → Add Version → Compare → Audit → Delete
Use raw JSON — no file upload needed. Just paste text directly.
POST {{base_url}}/contracts
Authorization: Bearer {{access_token}}
Content-Type: application/json
Minimal body (only 2 required fields):
{
"title": "My NDA",
"content": "This Non-Disclosure Agreement is made between Party A and Party B. Both parties agree to keep all shared information strictly confidential and not disclose it to any third party."
}Full body (all optional fields):
{
"title": "My NDA",
"content": "This Non-Disclosure Agreement is made between Party A and Party B. Both parties agree to keep all shared information strictly confidential and not disclose it to any third party.",
"type": "NDA",
"tags": ["legal", "2026"],
"expiryDate": "2027-06-01",
"jurisdiction": "India"
}Field rules:
title— required, 3–300 chars, no HTML tagscontent— required, min 50 chars (just paste any contract text)type— optional, one of:NDAVendorEmploymentSaaSOther(default:Other)tags— optional array of stringsexpiryDate— optional, must be a future date inYYYY-MM-DDformatjurisdiction— optional, free text (e.g.India,United States)
Success (201):
{
"success": true,
"message": "Contract uploaded successfully",
"data": {
"contract": {
"id": "65f1a2b3c4d5e6f7a8b9c0d4",
"title": "My NDA",
"type": "NDA",
"version": 1,
"contentHash": "abc123def456..."
}
}
}Copy
data.contract.id→contract_idenv var.
GET {{base_url}}/contracts
Authorization: Bearer {{access_token}}
No body needed. All query params are optional — just hit send to get your contracts.
Optional query params:
| Param | Default | Options |
|---|---|---|
page |
1 | any number |
limit |
10 | 1–50 |
type |
(all) | NDA, Vendor, Employment, SaaS, Other |
sortBy |
createdAt |
createdAt, title, type, riskScore, expiryDate |
order |
desc |
asc, desc |
tag |
(none) | any tag string |
search |
(none) | search in title (max 100 chars) |
Examples:
GET {{base_url}}/contracts
GET {{base_url}}/contracts?type=NDA
GET {{base_url}}/contracts?search=confidential&limit=5
Success (200):
{
"success": true,
"data": {
"contracts": [{ "id": "...", "title": "My NDA", "type": "NDA", "version": 1 }],
"meta": { "total": 3, "page": 1, "limit": 10, "totalPages": 1 }
}
}GET {{base_url}}/contracts/{{contract_id}}
Authorization: Bearer {{access_token}}
No body. Just set contract_id in your env vars.
Success (200):
{ "success": true, "data": { "contract": { "id": "...", "title": "My NDA", "content": "...", "type": "NDA" } } }Send only the fields you want to change. At least one field required.
PATCH {{base_url}}/contracts/{{contract_id}}
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "title": "Updated NDA" }Other updatable fields (all optional, mix and match):
{
"title": "Updated NDA",
"type": "Vendor",
"tags": ["updated", "2026"],
"alertDays": [30, 7],
"expiryDate": "2027-12-01"
}alertDays— array of integers (days before expiry to send alerts, e.g.[90, 30, 7])
Success (200):
{ "success": true, "data": { "contract": { "id": "...", "title": "Updated NDA" } } }Upload revised contract text as a new version. Only content is required.
POST {{base_url}}/contracts/{{contract_id}}/versions
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"content": "This Non-Disclosure Agreement (revised) is made between Party A and Party B. Updated confidentiality terms apply from the date of signing.",
"changeNote": "Updated confidentiality clause"
}content— required, min 50 charschangeNote— optional, describe what changed (max 500 chars)
Success (201):
{ "success": true, "data": { "version": 2 } }GET {{base_url}}/contracts/{{contract_id}}/versions
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "data": { "versions": [{ "version": 1 }, { "version": 2 }] } }POST {{base_url}}/contracts/{{contract_id}}/compare
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "versionA": 1, "versionB": 2 }- Both values must be different integers ≥ 1
- AI explanation arrives via WebSocket (
diff:completeevent) — not in this response
Success (202):
{
"success": true,
"message": "Version comparison queued. You will be notified via WebSocket when complete.",
"data": { "diff": "..." }
}GET {{base_url}}/contracts/{{contract_id}}/audit
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": {
"logs": [{ "action": "contract.created", "userId": "...", "timestamp": "2026-03-01T10:00:00Z" }]
}
}DELETE {{base_url}}/contracts/{{contract_id}}
Authorization: Bearer {{access_token}}
No body needed.
Success (200):
{ "success": true, "message": "Contract deleted successfully." }Nested under
/contracts. All routes requireAuthorization: Bearer {{access_token}}+ org membership. Status transitions track the full lifecycle of a contract.
Valid statuses: draft → review → approved → signed → active → expired / terminated
GET {{base_url}}/contracts/{{contract_id}}/status
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": {
"status": {
"contractId": "65f1a2b3c4d5e6f7a8b9c0d4",
"currentStatus": "draft",
"updatedAt": "2026-04-08T10:00:00.000Z",
"updatedBy": "65f1a2b3c4d5e6f7a8b9c0d1"
}
}
}PATCH {{base_url}}/contracts/{{contract_id}}/status
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "status": "review", "note": "Sending to legal team for review" }Field rules:
status— required, one of:draft,review,approved,signed,active,expired,terminatednote— optional, max 500 chars (reason for the status change)
Success (200):
{
"success": true,
"message": "Contract status updated to \"review\".",
"data": {
"status": {
"currentStatus": "review",
"previousStatus": "draft",
"updatedAt": "2026-04-08T12:00:00.000Z"
}
}
}GET {{base_url}}/contracts/{{contract_id}}/status/history
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": {
"history": [
{ "from": null, "to": "draft", "userId": "...", "note": null, "timestamp": "2026-04-08T10:00:00Z" },
{ "from": "draft", "to": "review", "userId": "...", "note": "Sending to legal team for review", "timestamp": "2026-04-08T12:00:00Z" }
]
}
}Nested under
/contracts. All routes requireAuthorization: Bearer {{access_token}}+ org membership. Users can edit/delete their own comments. Admins can delete any comment.
POST {{base_url}}/contracts/{{contract_id}}/comments
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "content": "This clause in section 3.2 seems overly broad. We should narrow the scope." }Field rules:
content— required, 1–5000 chars, no control characters
Success (201):
{
"success": true,
"message": "Comment added.",
"data": {
"comment": {
"_id": "65f1a2b3c4d5e6f7a8b9c0e1",
"contractId": "65f1a2b3c4d5e6f7a8b9c0d4",
"userId": "65f1a2b3c4d5e6f7a8b9c0d1",
"content": "This clause in section 3.2 seems overly broad. We should narrow the scope.",
"createdAt": "2026-04-08T10:00:00.000Z"
}
}
}Copy
data.comment._id→comment_idenv var.
GET {{base_url}}/contracts/{{contract_id}}/comments
Authorization: Bearer {{access_token}}
Optional query params:
| Param | Default | Description |
|---|---|---|
page |
1 | 1–1000 |
limit |
20 | 1–50 |
Example: GET {{base_url}}/contracts/{{contract_id}}/comments?page=1&limit=10
Success (200):
{
"success": true,
"data": {
"comments": [
{ "_id": "...", "content": "...", "userId": "...", "createdAt": "..." }
],
"meta": { "total": 5, "page": 1, "limit": 20, "totalPages": 1 }
}
}PATCH {{base_url}}/contracts/{{contract_id}}/comments/{{comment_id}}
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "content": "Updated: This clause should be limited to a 2-year non-compete." }Success (200):
{
"success": true,
"message": "Comment updated.",
"data": { "comment": { "_id": "...", "content": "Updated: This clause should be limited to a 2-year non-compete.", "updatedAt": "..." } }
}DELETE {{base_url}}/contracts/{{contract_id}}/comments/{{comment_id}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "Comment deleted." }All routes require
Authorization: Bearer {{access_token}}+ org membership.
POST {{base_url}}/analyses
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "contractId": "{{contract_id}}", "version": 1 }version is optional — omit to analyse the latest version.
Success — Cached (200):
{ "success": true, "message": "Analysis result retrieved from cache.", "data": { "cached": true, "analysis": { ... } } }Success — Queued (202):
{
"success": true,
"message": "Analysis job queued. You will receive a WebSocket notification when complete.",
"data": { "analysisId": "65f1a2b3c4d5e6f7a8b9c0d5", "status": "pending", "estimatedSeconds": 30 }
}Copy
data.analysisId→analysis_idenv var.
GET {{base_url}}/analyses/{{analysis_id}}
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": {
"analysis": {
"_id": "65f1a2b3c4d5e6f7a8b9c0d5",
"contractId": "...",
"status": "completed",
"riskScore": 7.2,
"summary": "This NDA contains standard confidentiality clauses...",
"flags": ["missing_penalty_clause", "jurisdiction_mismatch"]
}
}
}GET {{base_url}}/analyses/contract/{{contract_id}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "data": { "analyses": [ { ... }, { ... } ] } }All routes require
Authorization: Bearer {{access_token}}+ org membership. Rate-limited to 10 requests/min per user (AI calls consume LLM tokens).
Get a quick plain-English explanation of a single contract clause.
POST {{base_url}}/ai/summarize-clause
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"clauseText": "The Receiving Party shall not disclose any Confidential Information to any third party without the prior written consent of the Disclosing Party.",
"contractType": "NDA"
}clauseText— required, 10–10,000 charscontractType— optional, provides context (e.g.NDA,SaaS Agreement)
Success (200):
{
"success": true,
"message": "Clause summarized successfully.",
"data": {
"summary": "This clause prevents the receiving party from sharing any confidential information with outside parties unless they get written approval first.",
"implications": ["You cannot share project details with subcontractors without written consent"],
"actionItems": ["Ensure all team members are aware of this restriction"],
"riskLevel": "low",
"isStandard": true,
"tokensUsed": 215,
"provider": "groq",
"latencyMs": 890
}
}"Chat with your contract" — ask any question and get answers grounded in the contract text.
POST {{base_url}}/ai/ask
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"contractId": "{{contract_id}}",
"question": "What happens if I breach the confidentiality clause?",
"chatHistory": []
}contractId— required, valid MongoDB ObjectIdquestion— required, 5–1,000 charschatHistory— optional, array of prior{ role, content }turns (max 20) for multi-turn conversations
Success (200):
{
"success": true,
"message": "Question answered successfully.",
"data": {
"answer": "According to Clause 7, a breach of confidentiality may result in injunctive relief and monetary damages as determined by arbitration.",
"confidence": "high",
"relatedClauses": ["Clause 7 - Remedies", "Clause 3 - Confidentiality"],
"caveat": "The contract does not specify a cap on damages.",
"tokensUsed": 540,
"provider": "groq",
"latencyMs": 1200
}
}Pull out all defined terms, parties, governing law, and contract metadata.
POST {{base_url}}/ai/extract-terms
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "contractId": "{{contract_id}}" }Success (200):
{
"success": true,
"message": "Key terms extracted successfully.",
"data": {
"terms": [
{ "term": "Confidential Information", "definition": "Any non-public information disclosed by either party...", "section": "Section 1 - Definitions" }
],
"parties": [
{ "name": "Acme Corp", "role": "Disclosing Party", "type": "company" },
{ "name": "John Doe", "role": "Receiving Party", "type": "individual" }
],
"contractType": "NDA",
"governingLaw": "State of Delaware, USA",
"currency": "",
"totalValue": "",
"duration": "2 years from effective date",
"tokensUsed": 620,
"provider": "groq",
"latencyMs": 1050
}
}Explain WHY a contract received its risk score and what to do about it.
POST {{base_url}}/ai/explain-risk
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "analysisId": "{{analysis_id}}" }Success (200):
{
"success": true,
"message": "Risk explanation generated successfully.",
"data": {
"explanation": "This contract received a risk score of 72/100 because it contains several one-sided clauses...",
"topRisks": [
{ "risk": "Unlimited Liability", "severity": "high", "description": "No cap on damages for the signing party" },
{ "risk": "Auto-Renewal", "severity": "medium", "description": "Contract auto-renews with only 7 days notice" }
],
"mitigations": ["Negotiate a liability cap", "Extend the notice period to 30 days"],
"overallRecommendation": "negotiate",
"tokensUsed": 780,
"provider": "groq",
"latencyMs": 1400
}
}Translate an analysis summary and key clauses into another language.
POST {{base_url}}/ai/translate
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "analysisId": "{{analysis_id}}", "targetLanguage": "Hindi" }analysisId— required, valid MongoDB ObjectIdtargetLanguage— required, e.g.Spanish,Hindi,French,German,Chinese
Success (200):
{
"success": true,
"message": "Analysis translated to Hindi successfully.",
"data": {
"translatedSummary": "यह अनुबंध दोनों पक्षों के बीच...",
"translatedClauses": [
{ "title": "गोपनीयता खंड", "explanation": "यह खंड..." }
],
"translatedObligations": {
"yourObligations": ["सभी जानकारी गोपनीय रखें"],
"otherPartyObligations": ["समय पर भुगतान करें"]
},
"targetLanguage": "Hindi",
"tokensUsed": 910,
"provider": "groq",
"latencyMs": 1600
}
}Check a contract against a specific regulatory framework (GDPR, HIPAA, SOX, etc.).
POST {{base_url}}/ai/compliance
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "contractId": "{{contract_id}}", "framework": "GDPR" }contractId— required, valid MongoDB ObjectIdframework— optional, one of:GDPR,HIPAA,SOX,PCI-DSS,CCPA,GENERAL(default:GDPR)
Success (200):
{
"success": true,
"message": "GDPR compliance check completed.",
"data": {
"framework": "GDPR",
"complianceScore": 65,
"compliant": false,
"issues": [
{
"requirement": "Article 28 - Data Processing Agreement",
"status": "non-compliant",
"finding": "No data processing agreement clause found",
"recommendation": "Add a DPA clause specifying data processing roles and responsibilities"
}
],
"missingClauses": ["Data Processing Agreement", "Right to Erasure"],
"recommendations": ["Add a dedicated GDPR compliance section"],
"disclaimer": "This is automated AI analysis and should not replace professional compliance review.",
"tokensUsed": 1100,
"provider": "groq",
"latencyMs": 2100
}
}Check which AI provider is active and configured.
GET {{base_url}}/ai/providers
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"message": "AI provider status retrieved.",
"data": {
"providers": {
"groq": { "configured": true, "isPrimary": true },
"openrouter": { "configured": false, "isPrimary": false }
}
}
}All routes require
Authorization: Bearer {{access_token}}
GET {{base_url}}/notifications
Authorization: Bearer {{access_token}}
Query Parameters (optional):
| Param | Default | Description |
|---|---|---|
page |
1 | |
limit |
20 | |
read |
(all) | true or false to filter |
Example: GET {{base_url}}/notifications?read=false&page=1&limit=20
Success (200):
{
"success": true,
"data": {
"notifications": [ { ... } ],
"meta": { "total": 5, "page": 1, "limit": 20, "totalPages": 1 }
}
}GET {{base_url}}/notifications/unread-count
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "data": { "unreadCount": 3 } }GET {{base_url}}/notifications/user
Authorization: Bearer {{access_token}}
Returns notifications scoped to the authenticated user (not org-scoped).
Query Parameters (optional):
| Param | Default | Description |
|---|---|---|
page |
1 | |
limit |
20 | |
read |
(all) | true or false |
type |
(all) | Filter by type (e.g. analysis_done) |
Success (200):
{
"success": true,
"data": {
"notifications": [{ "id": "...", "type": "analysis_done", "message": "...", "read": false, "createdAt": "..." }],
"meta": { "total": 5, "page": 1, "limit": 20, "totalPages": 1 }
}
}PATCH {{base_url}}/notifications/read-all
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "All notifications marked as read.", "data": { "modifiedCount": 3 } }PATCH {{base_url}}/notifications/{{notification_id}}/read
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "Notification marked as read.", "data": { "notification": { "_id": "...", "read": true, "readAt": "2026-03-03T17:00:00Z" } } }DELETE {{base_url}}/notifications/{{notification_id}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "Notification deleted." }All routes require
Authorization: Bearer {{access_token}}These are non-critical — they degrade gracefully if an external API is unavailable.
GET {{base_url}}/enrichment/country/:name
Authorization: Bearer {{access_token}}
Example: GET {{base_url}}/enrichment/country/India
Success (200):
{ "success": true, "data": { "country": { "name": "India", "capital": "New Delhi", "region": "Asia", "currencies": { ... }, "languages": { ... } } } }GET {{base_url}}/enrichment/time/:timezone
Authorization: Bearer {{access_token}}
Example: GET {{base_url}}/enrichment/time/Asia/Kolkata
Timezone names support slashes (e.g. America/New_York, Europe/London).
Success (200):
{ "success": true, "data": { "time": { "timezone": "Asia/Kolkata", "datetime": "2026-04-08T15:30:00+05:30", "utc_offset": "+05:30" } } }GET {{base_url}}/enrichment/holidays?country=IN&date=2026-01-26
Authorization: Bearer {{access_token}}
Query Parameters:
| Param | Required | Description |
|---|---|---|
country |
Yes | 2-letter ISO code (e.g. US, IN) |
date |
Yes | Date in YYYY-MM-DD format |
Success (200):
{ "success": true, "data": { "holiday": { "isHoliday": true, "holidays": [{ "name": "Republic Day", "date": "2026-01-26" }] } } }GET {{base_url}}/enrichment/holidays/:country/:year
Authorization: Bearer {{access_token}}
Example: GET {{base_url}}/enrichment/holidays/IN/2026
Success (200):
{ "success": true, "data": { "holidays": [ { "date": "2026-01-26", "name": "Republic Day" }, ... ], "country": "IN", "year": 2026 } }GET {{base_url}}/enrichment/ip/:ip
Authorization: Bearer {{access_token}}
Example: GET {{base_url}}/enrichment/ip/8.8.8.8
Useful for flagging logins from unexpected locations.
Success (200):
{ "success": true, "data": { "ipInfo": { "ip": "8.8.8.8", "city": "Mountain View", "region": "California", "country": "US", "org": "AS15169 Google LLC" } } }GET {{base_url}}/enrichment/email/validate?email=user@example.com
Authorization: Bearer {{access_token}}
Runs syntax + MX record check (EVA) and disposable email detection (Disify) in parallel.
Success (200):
{
"success": true,
"data": {
"email": "user@example.com",
"validation": { "valid": true, "disposable": false, "mx": true },
"disposable": { "isDisposable": false }
}
}GET {{base_url}}/enrichment/email/reputation?email=user@example.com
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "data": { "email": "user@example.com", "reputation": { "risk": "low", "suspicious": false } } }GET {{base_url}}/enrichment/email/breaches?email=user@example.com
Authorization: Bearer {{access_token}}
Requires HIBP_API_KEY in .env. Degrades gracefully if not configured.
Success (200):
{ "success": true, "data": { "email": "user@example.com", "breaches": { "count": 2, "breaches": [{ "Name": "Adobe", "BreachDate": "2013-10-04" }] } } }GET {{base_url}}/enrichment/currency/rate?from=USD&to=EUR
Authorization: Bearer {{access_token}}
Query Parameters:
| Param | Required | Description |
|---|---|---|
from |
Yes | 3-letter ISO code (USD) |
to |
Yes | 3-letter ISO code (EUR) |
Success (200):
{ "success": true, "data": { "exchange": { "base": "USD", "target": "EUR", "rate": 0.9215, "date": "2026-04-08" } } }GET {{base_url}}/enrichment/currency/rates?base=USD&targets=EUR,GBP,JPY
Authorization: Bearer {{access_token}}
Query Parameters:
| Param | Required | Description |
|---|---|---|
base |
Yes | 3-letter ISO base currency |
targets |
No | Comma-separated target currency list |
Success (200):
{ "success": true, "data": { "exchange": { "base": "USD", "date": "2026-04-08", "rates": { "EUR": 0.9215, "GBP": 0.7891, "JPY": 151.23 } } } }All routes require
Authorization: Bearer {{admin_token}}(admin role only). Rate limited: 5 requests / 15 min.
GET {{base_url}}/admin/stats
Authorization: Bearer {{admin_token}}
Success (200):
{
"success": true,
"data": {
"stats": {
"totalUsers": 120,
"totalOrgs": 35,
"totalContracts": 480,
"totalAnalyses": 310,
"analysesLast30Days": 45,
"averageRiskScore": 6.3,
"queueDepth": 2
}
}
}GET {{base_url}}/admin/queue/status
Authorization: Bearer {{admin_token}}
Success (200):
{
"success": true,
"data": {
"queue": {
"name": "lexai.analysis.queue",
"messageCount": 3,
"consumerCount": 1,
"dlxMessageCount": 0
}
}
}dlxMessageCount = jobs that failed all retries (dead letter queue).
GET {{base_url}}/admin/users
Authorization: Bearer {{admin_token}}
Query Parameters (optional):
| Param | Default | Description |
|---|---|---|
page |
1 | |
limit |
20 |
Success (200):
{
"success": true,
"data": {
"users": [ { "_id": "...", "name": "...", "email": "...", "role": "viewer", "isActive": true } ],
"meta": { "total": 120, "page": 1, "limit": 20, "totalPages": 6 }
}
}POST {{base_url}}/admin/users
Authorization: Bearer {{admin_token}}
Content-Type: application/json
{ "name": "New User", "email": "newuser@example.com", "password": "SecurePass@123", "role": "viewer" }Creates a pre-verified user — skips the OTP email flow. Roles: admin, manager, viewer.
Success (201):
{ "success": true, "message": "User created successfully.", "data": { "user": { ... } } }PATCH {{base_url}}/admin/users/{{user_id}}
Authorization: Bearer {{admin_token}}
Content-Type: application/json
{ "role": "manager", "isActive": true, "name": "Updated Name" }Allowed fields: name, role, isActive. At least one required.
Success (200):
{ "success": true, "message": "User updated successfully.", "data": { "user": { ... } } }DELETE {{base_url}}/admin/users/{{user_id}}
Authorization: Bearer {{admin_token}}
Soft-deactivates the user (sets isActive: false). Cannot deactivate your own account.
Success (200):
{ "success": true, "message": "User deactivated successfully." }DELETE {{base_url}}/admin/users/{{user_id}}/sessions
Authorization: Bearer {{admin_token}}
Immediately invalidates all active refresh token sessions for the target user across all devices. Use this when an account is compromised or needs to be force-logged out.
Success (200):
{ "success": true, "message": "All sessions revoked for user." }Error — User not found (404):
{ "success": false, "error": { "code": "NOT_FOUND", "message": "User not found." } }DELETE {{base_url}}/admin/contracts/{{contract_id}}
Authorization: Bearer {{admin_token}}
Permanently hard-deletes any contract platform-wide, bypassing org ownership checks. Also decrements the owning org's contractCount. Use for removing illegal, abusive, or test data.
Success (200):
{ "success": true, "message": "Contract permanently deleted." }Error — Contract not found (404):
{ "success": false, "error": { "code": "NOT_FOUND", "message": "Contract not found." } }DELETE {{base_url}}/admin/organizations/{{org_id}}
Authorization: Bearer {{admin_token}}
Deletes an organization and cascades cleanup in parallel:
- Soft-deletes all contracts belonging to the org (
isDeleted: true,deletedAtset) - Hard-deletes all analyses belonging to the org
- Clears
organizationfield and resetsroletovieweron all member users - Hard-deletes the organization document itself
Success (200):
{ "success": true, "message": "Organization deleted." }Error — Org not found (404):
{ "success": false, "error": { "code": "NOT_FOUND", "message": "Organization not found." } }DELETE {{base_url}}/admin/analyses/{{analysis_id}}
Authorization: Bearer {{admin_token}}
Permanently hard-deletes any analysis record platform-wide, bypassing org ownership checks. Use for removing corrupted, failed, or duplicate analysis documents.
Success (200):
{ "success": true, "message": "Analysis permanently deleted." }Error — Analysis not found (404):
{ "success": false, "error": { "code": "NOT_FOUND", "message": "Analysis not found." } }DELETE {{base_url}}/admin/templates/{{template_id}}
Authorization: Bearer {{admin_token}}
Soft-deletes any template including global (platform-wide) ones by setting isActive: false. Org-level users cannot delete global templates — only platform admins can. Already-inactive templates return 404.
Success (200):
{ "success": true, "message": "Template deleted." }Error — Template not found or already inactive (404):
{ "success": false, "error": { "code": "NOT_FOUND", "message": "Template not found." } }DELETE {{base_url}}/admin/comments/{{comment_id}}
Authorization: Bearer {{admin_token}}
Soft-deletes any comment platform-wide, bypassing org ownership and authorship checks. Use for removing abusive or policy-violating content across any org.
Success (200):
{ "success": true, "message": "Comment deleted." }Error — Comment not found (404):
{ "success": false, "error": { "code": "NOT_FOUND", "message": "Comment not found." } }GET {{base_url}}/admin/audit-logs
Authorization: Bearer {{admin_token}}
Query Parameters (optional):
| Param | Default | Description |
|---|---|---|
page |
1 | |
limit |
20 |
Success (200):
{
"success": true,
"data": {
"logs": [
{ "action": "contract.deleted", "userId": "...", "orgId": "...", "timestamp": "2026-04-08T10:00:00Z", "meta": { ... } }
],
"meta": { "total": 500, "page": 1, "limit": 20, "totalPages": 25 }
}
}All routes require
Authorization: Bearer {{access_token}}+ org membership. Read-only org-level analytics — no data mutation.
GET {{base_url}}/dashboard/stats
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": {
"stats": {
"totalContracts": 48,
"totalAnalyses": 31,
"averageRiskScore": 5.8,
"contractsByType": { "NDA": 12, "Vendor": 18, "Employment": 8, "SaaS": 6, "Other": 4 },
"contractsByStatus": { "draft": 5, "active": 30, "expired": 8, "terminated": 5 },
"analysisCoverage": 64.5
}
}
}GET {{base_url}}/dashboard/risk-distribution
Authorization: Bearer {{access_token}}
Returns contracts grouped by risk level (low / medium / high / critical).
Success (200):
{
"success": true,
"data": {
"distribution": {
"low": { "count": 15, "range": "0-3" },
"medium": { "count": 20, "range": "3.1-6" },
"high": { "count": 10, "range": "6.1-8" },
"critical": { "count": 3, "range": "8.1-10" }
}
}
}GET {{base_url}}/dashboard/expiry-timeline
Authorization: Bearer {{access_token}}
Returns contracts expiring in the next 30, 60, and 90 days.
Success (200):
{
"success": true,
"data": {
"timeline": {
"next30Days": [{ "id": "...", "title": "Vendor Agreement", "expiryDate": "2026-05-05" }],
"next60Days": [ ... ],
"next90Days": [ ... ]
}
}
}GET {{base_url}}/dashboard/recent-activity
Authorization: Bearer {{access_token}}
Optional query params:
| Param | Default | Description |
|---|---|---|
limit |
20 | Number of recent audit log entries |
Example: GET {{base_url}}/dashboard/recent-activity?limit=10
Success (200):
{
"success": true,
"data": {
"activity": [
{ "action": "contract.created", "userId": "...", "resourceType": "Contract", "timestamp": "2026-04-08T10:00:00Z" },
{ "action": "analysis.completed", "userId": "...", "resourceType": "Analysis", "timestamp": "2026-04-08T09:30:00Z" }
]
}
}All routes require
Authorization: Bearer {{access_token}}+ org membership. Rename and delete require admin or manager role.
GET {{base_url}}/tags
Authorization: Bearer {{access_token}}
Returns all unique tags used across the org's contracts, with usage counts.
Success (200):
{
"success": true,
"data": {
"tags": [
{ "tag": "legal", "count": 12 },
{ "tag": "2026", "count": 8 },
{ "tag": "confidential", "count": 5 }
]
}
}PATCH {{base_url}}/tags/rename
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "oldTag": "legal", "newTag": "legal-docs" }Field rules:
oldTag— required, 1–50 chars, auto-lowercasednewTag— required, 1–50 chars, auto-lowercased
Success (200):
{
"success": true,
"message": "Tag \"legal\" renamed to \"legal-docs\".",
"data": { "oldTag": "legal", "newTag": "legal-docs", "contractsAffected": 12 }
}DELETE {{base_url}}/tags/{{tag_name}}
Authorization: Bearer {{access_token}}
Example: DELETE {{base_url}}/tags/obsolete
Removes the tag from all contracts in the org.
Success (200):
{
"success": true,
"message": "Tag \"obsolete\" removed from 3 contracts.",
"data": { "tag": "obsolete", "contractsAffected": 3 }
}All routes require
Authorization: Bearer {{access_token}}+ org membership. Bookmarks are user-scoped — each user has their own bookmarks.
POST {{base_url}}/bookmarks
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "contractId": "{{contract_id}}", "note": "Review this NDA before Friday" }Field rules:
contractId— required, 24-char hex ObjectIdnote— optional, max 500 chars
Success (201):
{
"success": true,
"message": "Contract bookmarked.",
"data": {
"bookmark": {
"_id": "65f1a2b3c4d5e6f7a8b9c0e5",
"userId": "65f1a2b3c4d5e6f7a8b9c0d1",
"contractId": "65f1a2b3c4d5e6f7a8b9c0d4",
"note": "Review this NDA before Friday",
"createdAt": "2026-04-08T10:00:00.000Z"
}
}
}GET {{base_url}}/bookmarks
Authorization: Bearer {{access_token}}
Optional query params:
| Param | Default | Description |
|---|---|---|
page |
1 | 1–1000 |
limit |
20 | 1–50 |
Success (200):
{
"success": true,
"data": {
"bookmarks": [
{ "_id": "...", "contractId": "...", "note": "Review this NDA before Friday", "createdAt": "..." }
],
"meta": { "total": 3, "page": 1, "limit": 20, "totalPages": 1 }
}
}DELETE {{base_url}}/bookmarks/{{contract_id}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "Bookmark removed." }All routes require
Authorization: Bearer {{access_token}}+ org membership. Create, update, and delete require admin or manager role. Viewers can list, view, and clone templates.
POST {{base_url}}/templates
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"title": "Standard NDA Template",
"description": "A boilerplate NDA for general use with external vendors.",
"content": "This Non-Disclosure Agreement (\"Agreement\") is entered into between [Party A] and [Party B]. Both parties agree to maintain the confidentiality of all proprietary information shared during the term of this agreement.",
"type": "NDA",
"category": "Legal",
"tags": ["nda", "vendor", "standard"]
}Field rules:
title— required, 3–300 chars, no HTML tags or control charsdescription— optional, max 1000 charscontent— required, 10–500,000 charstype— optional, one of:NDA,Vendor,Employment,SaaS,Other(default:Other)category— optional, max 100 chars (default:General)tags— optional, array of up to 20 strings (each max 50 chars, auto-lowercased)
Success (201):
{
"success": true,
"message": "Template created.",
"data": {
"template": {
"_id": "65f1a2b3c4d5e6f7a8b9c0f1",
"title": "Standard NDA Template",
"type": "NDA",
"category": "Legal",
"createdAt": "2026-04-08T10:00:00.000Z"
}
}
}Copy
data.template._id→template_idenv var.
GET {{base_url}}/templates
Authorization: Bearer {{access_token}}
Returns both org-specific and global templates.
Optional query params:
| Param | Default | Options |
|---|---|---|
page |
1 | 1–1000 |
limit |
20 | 1–50 |
type |
(all) | NDA, Vendor, Employment, SaaS, Other |
category |
(all) | any string, max 100 chars |
search |
(none) | search in title, max 100 chars |
Example: GET {{base_url}}/templates?type=NDA&category=Legal
Success (200):
{
"success": true,
"data": {
"templates": [
{ "_id": "...", "title": "Standard NDA Template", "type": "NDA", "category": "Legal", "usageCount": 5 }
],
"meta": { "total": 8, "page": 1, "limit": 20, "totalPages": 1 }
}
}GET {{base_url}}/templates/{{template_id}}
Authorization: Bearer {{access_token}}
Returns full template details including content.
Success (200):
{
"success": true,
"data": {
"template": {
"_id": "65f1a2b3c4d5e6f7a8b9c0f1",
"title": "Standard NDA Template",
"description": "A boilerplate NDA for general use with external vendors.",
"content": "This Non-Disclosure Agreement...",
"type": "NDA",
"category": "Legal",
"tags": ["nda", "vendor", "standard"],
"usageCount": 5,
"createdAt": "2026-04-08T10:00:00.000Z"
}
}
}PATCH {{base_url}}/templates/{{template_id}}
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "title": "Updated NDA Template", "description": "Revised NDA with stricter penalties." }All fields are optional, but at least one must be provided. Same field rules as create.
Success (200):
{
"success": true,
"message": "Template updated.",
"data": { "template": { "_id": "...", "title": "Updated NDA Template", "description": "Revised NDA with stricter penalties." } }
}DELETE {{base_url}}/templates/{{template_id}}
Authorization: Bearer {{access_token}}
Soft-deletes the template. It will no longer appear in listings.
Success (200):
{ "success": true, "message": "Template deleted." }POST {{base_url}}/templates/{{template_id}}/clone
Authorization: Bearer {{access_token}}
Content-Type: application/json
{ "title": "My NDA from Template" }title— optional, overrides the template title (defaults to"<template title> (from template)")- Creates a new contract using the template's content, type, and tags
Success (201):
{
"success": true,
"message": "Contract created from template.",
"data": {
"contract": {
"id": "65f1a2b3c4d5e6f7a8b9c0f5",
"title": "My NDA from Template",
"type": "NDA"
}
}
}Create, list, and revoke require
Authorization: Bearer {{access_token}}+ org membership. The public access endpoint does NOT require authentication. Create and revoke require admin or manager role.
POST {{base_url}}/shares
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"contractId": "{{contract_id}}",
"permissions": "view_content",
"expiryHours": 72,
"password": "SecretLink@123",
"note": "Shared with external counsel for review"
}Field rules:
contractId— required, 24-char hex ObjectIdpermissions— optional, one of:view_metadata,view_content,view_analysis(default:view_metadata)expiryHours— optional, 1–720 hours (default: 72, max 30 days)password— optional, 4–128 chars (password-protect the link)note— optional, max 500 chars
Success (201):
{
"success": true,
"message": "Share link created.",
"data": {
"shareLink": {
"id": "65f1a2b3c4d5e6f7a8b9c0f8",
"token": "a1b2c3d4e5f6...(64 hex chars)",
"permissions": "view_content",
"expiresAt": "2026-04-11T10:00:00.000Z",
"hasPassword": true,
"note": "Shared with external counsel for review"
}
}
}Copy
data.shareLink.id→share_link_idenv var.
GET {{base_url}}/shares/contract/{{contract_id}}
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": {
"shareLinks": [
{ "id": "...", "permissions": "view_content", "expiresAt": "...", "hasPassword": true, "createdBy": "...", "createdAt": "..." }
]
}
}DELETE {{base_url}}/shares/{{share_link_id}}
Authorization: Bearer {{access_token}}
Success (200):
{ "success": true, "message": "Share link revoked." }All routes require
Authorization: Bearer {{access_token}}+ org membership. Returns JSON data with Content-Disposition header for download.
GET {{base_url}}/exports/contracts
Authorization: Bearer {{access_token}}
Exports all contracts for the org as a JSON download.
Optional query params: Same as List Contracts (type, search, sortBy, order).
Success (200):
{
"success": true,
"data": {
"contracts": [
{ "id": "...", "title": "My NDA", "type": "NDA", "status": "active", "riskScore": 7.2, "createdAt": "..." }
],
"exportedAt": "2026-04-08T10:00:00.000Z",
"totalCount": 48
}
}GET {{base_url}}/exports/contracts/{{contract_id}}/report
Authorization: Bearer {{access_token}}
Exports a full contract report including content, analysis, and audit trail.
Success (200):
{
"success": true,
"data": {
"report": {
"contract": { "id": "...", "title": "My NDA", "content": "...", "type": "NDA" },
"analysis": { "riskScore": 7.2, "summary": "...", "flags": [...] },
"auditLog": [ { "action": "contract.created", "timestamp": "..." } ],
"exportedAt": "2026-04-08T10:00:00.000Z"
}
}
}GET {{base_url}}/exports/analyses
Authorization: Bearer {{access_token}}
Exports all analyses for the org as a JSON download.
Success (200):
{
"success": true,
"data": {
"analyses": [
{ "analysisId": "...", "contractId": "...", "contractTitle": "My NDA", "riskScore": 7.2, "status": "completed", "completedAt": "..." }
],
"exportedAt": "2026-04-08T10:00:00.000Z",
"totalCount": 31
}
}All routes require
Authorization: Bearer {{access_token}}+ org membership + admin/manager role. All operations accept an array of contract IDs (max 100, except delete which allows max 50).
POST {{base_url}}/bulk/add-tags
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"contractIds": ["65f1a2b3c4d5e6f7a8b9c0d4", "65f1a2b3c4d5e6f7a8b9c0d5"],
"tags": ["urgent", "q2-2026"]
}Field rules:
contractIds— required, array of 1–100 hex ObjectIdstags— required, array of 1–20 strings (each max 50 chars, auto-lowercased)
Success (200):
{
"success": true,
"message": "Tags added to 2 contracts.",
"data": { "modifiedCount": 2 }
}POST {{base_url}}/bulk/remove-tags
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"contractIds": ["65f1a2b3c4d5e6f7a8b9c0d4", "65f1a2b3c4d5e6f7a8b9c0d5"],
"tags": ["obsolete"]
}Success (200):
{
"success": true,
"message": "Tags removed from 2 contracts.",
"data": { "modifiedCount": 2 }
}POST {{base_url}}/bulk/delete
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"contractIds": ["65f1a2b3c4d5e6f7a8b9c0d4", "65f1a2b3c4d5e6f7a8b9c0d5"]
}- Max 50 contract IDs per request (stricter limit for destructive operations)
- Performs soft-delete
Success (200):
{
"success": true,
"message": "2 contracts deleted.",
"data": { "deletedCount": 2 }
}POST {{base_url}}/bulk/update-type
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"contractIds": ["65f1a2b3c4d5e6f7a8b9c0d4", "65f1a2b3c4d5e6f7a8b9c0d5"],
"type": "Vendor"
}type— required, one of:NDA,Vendor,Employment,SaaS,Other
Success (200):
{
"success": true,
"message": "Type updated for 2 contracts.",
"data": { "modifiedCount": 2 }
}All routes require
Authorization: Bearer {{access_token}}. Preferences are user-scoped (no org required) — each user has their own settings.
GET {{base_url}}/preferences
Authorization: Bearer {{access_token}}
Success (200):
{
"success": true,
"data": {
"preferences": {
"notifications": {
"emailOnAnalysisComplete": true,
"emailOnContractExpiring": true,
"emailOnCommentAdded": false,
"emailOnInvitation": true,
"pushOnAnalysisComplete": true,
"pushOnContractExpiring": true,
"pushOnCommentAdded": false
},
"display": {
"contractsPerPage": 10,
"defaultSortBy": "createdAt",
"defaultSortOrder": "desc",
"showRiskBadges": true
},
"defaults": {
"contractType": "Other",
"alertDays": [30, 7]
},
"timezone": "Asia/Kolkata"
}
}
}PUT {{base_url}}/preferences
Authorization: Bearer {{access_token}}
Content-Type: application/json
Send only the sections/fields you want to change. At least one field required.
Example — update notifications only:
{
"notifications": {
"emailOnAnalysisComplete": false,
"emailOnCommentAdded": true
}
}Example — update display settings:
{
"display": {
"contractsPerPage": 25,
"defaultSortBy": "riskScore",
"defaultSortOrder": "desc",
"showRiskBadges": true
}
}Example — update defaults and timezone:
{
"defaults": {
"contractType": "NDA",
"alertDays": [90, 30, 7, 1]
},
"timezone": "America/New_York"
}Field rules:
notifications— optional object with boolean flagsdisplay.contractsPerPage— 5–50display.defaultSortBy— one of:createdAt,title,type,expiryDate,riskScoredisplay.defaultSortOrder—ascordescdefaults.contractType— one of:NDA,Vendor,Employment,SaaS,Otherdefaults.alertDays— array of up to 10 integers, each 1–365timezone— free text, max 50 chars (e.g.Asia/Kolkata,UTC)
Success (200):
{
"success": true,
"message": "Preferences updated.",
"data": { "preferences": { ... } }
}DELETE {{base_url}}/preferences
Authorization: Bearer {{access_token}}
Resets all preferences back to system defaults.
Success (200):
{
"success": true,
"message": "Preferences reset to defaults.",
"data": { "preferences": { ... } }
}All routes require
Authorization: Bearer {{access_token}}+ org membership. Reports are read-only aggregation endpoints — no data mutation.
GET {{base_url}}/reports/compliance
Authorization: Bearer {{access_token}}
Returns a compliance summary: analysis coverage, expired contracts, and risk breakdown.
Success (200):
{
"success": true,
"data": {
"report": {
"totalContracts": 48,
"analyzedContracts": 31,
"analysisCoverage": "64.6%",
"expiredContracts": 5,
"contractsWithoutExpiry": 12,
"highRiskContracts": 8,
"averageRiskScore": 5.8
}
}
}GET {{base_url}}/reports/risk-trend
Authorization: Bearer {{access_token}}
Returns risk score trends over the last 6 months.
Success (200):
{
"success": true,
"data": {
"report": {
"months": [
{ "month": "2025-11", "averageRiskScore": 6.1, "totalAnalyses": 5 },
{ "month": "2025-12", "averageRiskScore": 5.8, "totalAnalyses": 7 },
{ "month": "2026-01", "averageRiskScore": 5.5, "totalAnalyses": 6 },
{ "month": "2026-02", "averageRiskScore": 5.9, "totalAnalyses": 4 },
{ "month": "2026-03", "averageRiskScore": 5.2, "totalAnalyses": 8 },
{ "month": "2026-04", "averageRiskScore": 4.8, "totalAnalyses": 3 }
]
}
}
}GET {{base_url}}/reports/activity
Authorization: Bearer {{access_token}}
Returns org activity: total actions, active users, and daily action counts.
Optional query params:
| Param | Default | Description |
|---|---|---|
days |
30 | Number of days to look back |
Example: GET {{base_url}}/reports/activity?days=7
Success (200):
{
"success": true,
"data": {
"report": {
"totalActions": 245,
"activeUsers": 8,
"dailyCounts": [
{ "date": "2026-04-08", "count": 12 },
{ "date": "2026-04-07", "count": 18 },
{ "date": "2026-04-06", "count": 9 }
],
"topActions": [
{ "action": "contract.created", "count": 45 },
{ "action": "analysis.requested", "count": 31 },
{ "action": "comment.created", "count": 28 }
]
}
}
}Note: These APIs are called directly and are NOT yet proxied through the LexAI enrichment backend. If an API is already proxied through
/api/v1/enrichment/, it belongs in Section 10, not here.
These are free public APIs relevant to LexAI that can be called directly using the URLs below. No calendar APIs are included.
Check if an IP address is a known internet scanner, bot, or malicious actor. Useful for flagging suspicious login IPs.
GET https://api.greynoise.io/v3/community/{{ip}}
Key: none required for community tier
Example: GET https://api.greynoise.io/v3/community/8.8.8.8
Success (200):
{
"ip": "8.8.8.8",
"noise": false,
"riot": true,
"classification": "benign",
"name": "Google Public DNS",
"link": "https://viz.greynoise.io/ip/8.8.8.8",
"last_seen": "2026-04-08",
"message": "This IP is commonly included in blocklists."
}noise: true= actively scanning the internet (suspicious)riot: true= known benign service (Google, Cloudflare, etc.)- Auth: No key needed for community endpoint (1000 req/day)
- Docs: https://docs.greynoise.io
Search for internet-connected devices, open ports, and vulnerabilities by IP. Useful for security audits.
GET https://api.shodan.io/shodan/host/{{ip}}?key={{shodan_api_key}}
Example: GET https://api.shodan.io/shodan/host/8.8.8.8?key=YOUR_KEY
Success (200):
{
"ip_str": "8.8.8.8",
"org": "Google LLC",
"country_name": "United States",
"ports": [53, 443],
"vulns": [],
"last_update": "2026-04-08T00:00:00.000Z"
}- Auth:
apiKey— get free key at https://account.shodan.io - Free tier: 1 query credit/scan
- Add
SHODAN_API_KEYto your.env - Docs: https://developer.shodan.io/api
Find professional email addresses by domain or verify if an email is deliverable. Useful for org member invitations.
GET https://api.hunter.io/v2/email-verifier?email={{email}}&api_key={{hunter_api_key}}
Example: GET https://api.hunter.io/v2/email-verifier?email=user@example.com&api_key=YOUR_KEY
Success (200):
{
"data": {
"status": "valid",
"result": "deliverable",
"score": 92,
"email": "user@example.com",
"regexp": true,
"gibberish": false,
"disposable": false,
"webmail": false,
"mx_records": true,
"smtp_server": true,
"smtp_check": true
}
}- Auth:
apiKey— free tier: 25 verifications/month at https://hunter.io - Add
HUNTER_API_KEYto your.env - Docs: https://hunter.io/api-documentation
Validate email addresses for syntax, MX records, SMTP, and disposable detection.
GET https://apilayer.net/api/check?access_key={{mailboxlayer_key}}&email={{email}}
Example: GET https://apilayer.net/api/check?access_key=YOUR_KEY&email=user@example.com
Success (200):
{
"email": "user@example.com",
"did_you_mean": "",
"user": "user",
"domain": "example.com",
"format_valid": true,
"mx_found": true,
"smtp_check": true,
"catch_all": false,
"role": false,
"disposable": false,
"free": false,
"score": 0.96
}- Auth:
apiKey— free tier: 100 requests/month at https://mailboxlayer.com - Add
MAILBOXLAYER_API_KEYto your.env - Docs: https://mailboxlayer.com/documentation
Free currency conversion with 1500+ req/month on free plan. Alternative to Frankfurter (already in Section 10).
GET https://open.er-api.com/v6/latest/{{base_currency}}
Example: GET https://open.er-api.com/v6/latest/USD
Success (200):
{
"result": "success",
"base_code": "USD",
"time_last_update_utc": "Wed, 08 Apr 2026 00:00:00 +0000",
"rates": {
"EUR": 0.9215,
"GBP": 0.7891,
"INR": 83.45,
"JPY": 151.23
}
}- Auth: None required for free tier
- Free tier: 1500 requests/month
- Docs: https://www.exchangerate-api.com/docs/free
Detailed IP geolocation with timezone, currency, and security flags. Free plan: 30k requests/month.
GET https://api.ipgeolocation.io/ipgeo?apiKey={{ipgeo_key}}&ip={{ip}}
Example: GET https://api.ipgeolocation.io/ipgeo?apiKey=YOUR_KEY&ip=8.8.8.8
Success (200):
{
"ip": "8.8.8.8",
"country_name": "United States",
"country_code2": "US",
"city": "Mountain View",
"time_zone": { "name": "America/Los_Angeles", "offset": -7 },
"currency": { "code": "USD", "name": "US Dollar" },
"security": { "threat_score": 0, "is_tor": false, "is_proxy": false, "is_bot": false }
}- Auth:
apiKey— free tier: 30k req/month at https://ipgeolocation.io - Add
IPGEO_API_KEYto your.env - Docs: https://ipgeolocation.io/documentation
Sentiment analysis, entity extraction, and syntax analysis on contract text. Useful for automated contract summarization.
POST https://language.googleapis.com/v1/documents:analyzeEntities?key={{google_nlp_key}}
Content-Type: application/json
{
"document": {
"type": "PLAIN_TEXT",
"content": "This Non-Disclosure Agreement is between Party A and Party B, governed under the laws of India."
},
"encodingType": "UTF8"
}Success (200):
{
"entities": [
{ "name": "Party A", "type": "PERSON", "salience": 0.45 },
{ "name": "Party B", "type": "PERSON", "salience": 0.40 },
{ "name": "India", "type": "LOCATION", "salience": 0.15 }
],
"language": "en"
}- Auth:
apiKey— free tier: 5000 units/month at https://cloud.google.com/natural-language - Add
GOOGLE_NLP_API_KEYto your.env - Docs: https://cloud.google.com/natural-language/docs/reference/rest
Text analysis including sentiment, entity detection, and language identification. Free tier: 800 calls/month.
POST https://api.cloudmersive.com/nlp-v2/analytics/sentiment
Content-Type: application/json
Apikey: {{cloudmersive_key}}
{
"TextToAnalyze": "This contract contains unfair penalty clauses and ambiguous jurisdiction terms."
}Success (200):
{
"Successful": true,
"SentimentClassificationResult": "Negative",
"SentimentScoreResult": -0.72,
"SentenceCount": 1
}- Auth:
apiKey— free tier: 800 calls/month at https://cloudmersive.com - Add
CLOUDMERSIVE_API_KEYto your.env - Docs: https://cloudmersive.com/nlp-api
Validate EU VAT numbers for business contracts. Useful when contracts involve EU companies.
GET https://apilayer.net/api/validate?access_key={{vatlayer_key}}&vat_number={{vat_number}}
Example: GET https://apilayer.net/api/validate?access_key=YOUR_KEY&vat_number=GB123456789
Success (200):
{
"valid": true,
"country_code": "GB",
"vat_number": "123456789",
"company_name": "Example Ltd",
"company_address": "123 Business St, London, UK"
}- Auth:
apiKey— free tier: 100 requests/month at https://vatlayer.com - Add
VATLAYER_API_KEYto your.env - Docs: https://vatlayer.com/documentation
Check contract text for profanity or inappropriate content before storing. No API key needed.
GET https://www.purgomalum.com/service/json?text={{text_to_check}}
Example: GET https://www.purgomalum.com/service/json?text=This is a clean contract
Success (200):
{ "result": "This is a clean contract" }If profanity is found, the words are replaced with **** in the result.
- Auth: None required
- Free: Unlimited requests
- Docs: https://www.purgomalum.com
Add these to your .env as needed (all optional — features degrade gracefully if not set):
| Variable | API | Where to get it |
|---|---|---|
SHODAN_API_KEY |
Shodan | https://account.shodan.io |
HUNTER_API_KEY |
Hunter | https://hunter.io/users/sign_up |
MAILBOXLAYER_API_KEY |
mailboxlayer | https://mailboxlayer.com/product |
IPGEO_API_KEY |
ipgeolocation | https://ipgeolocation.io/signup.html |
GOOGLE_NLP_API_KEY |
Google Cloud NLP | https://console.cloud.google.com |
CLOUDMERSIVE_API_KEY |
Cloudmersive | https://account.cloudmersive.com/signup |
VATLAYER_API_KEY |
VATlayer | https://vatlayer.com/product |
These are the env vars used by this project. Set them in your .env file.
| Variable | Required | Description |
|---|---|---|
NODE_ENV |
Yes | development or production |
PORT |
Yes | Server port (default 3500) |
API_VERSION |
Yes | API version prefix (default v1) |
MONGO_URI |
Yes | MongoDB connection string |
REDIS_HOST |
Yes | Redis host |
REDIS_PORT |
Yes | Redis port (default 6379) |
REDIS_PASSWORD |
No | Redis password (leave empty for local) |
RABBITMQ_URL |
Yes | RabbitMQ AMQP URL |
ANALYSIS_QUEUE |
Yes | RabbitMQ queue name for analysis jobs |
ALERT_QUEUE |
Yes | RabbitMQ queue name for alert jobs |
DLX_EXCHANGE |
Yes | RabbitMQ dead letter exchange name |
PASETO_LOCAL_SECRET |
Yes | Min 32-char secret for PASETO token signing |
PASETO_ACCESS_EXPIRY |
Yes | Access token TTL (e.g. 15m) |
PASETO_REFRESH_EXPIRY |
Yes | Refresh token TTL (e.g. 7d) |
PASETO_REFRESH_COOKIE_MAX_AGE_MS |
Yes | Refresh cookie max-age in milliseconds (e.g. 604800000) |
AI_PROVIDER |
Yes | Primary AI provider: groq or openrouter |
AI_PRIMARY_MODEL |
Yes | Primary AI model (default llama-3.3-70b-versatile) |
AI_FALLBACK_MODEL |
Yes | Fallback AI model (default llama-3.1-8b-instant) |
AI_DIFF_MODEL |
No | Model for diff analysis (default llama-3.1-8b-instant) |
AI_REQUEST_TIMEOUT_MS |
No | AI request timeout in ms (default 60000) |
GROQ_API_KEY |
Yes | Groq API key (gsk_...) — get at console.groq.com |
GROQ_BASE_URL |
No | https://api.groq.com/openai/v1 |
OPENROUTER_API_KEY |
No | OpenRouter key (optional fallback) |
OPENROUTER_BASE_URL |
No | https://openrouter.ai/api/v1 |
RATE_LIMIT_WINDOW_MS |
No | Rate limit window in ms (default 60000) |
RATE_LIMIT_MAX |
No | Max requests per window (default 100) |
MAX_FILE_SIZE_MB |
Yes | Max contract upload size in MB (default 5) |
ALLOWED_MIME_TYPES |
Yes | Comma-separated allowed MIME types |
ALLOWED_ORIGINS |
Yes | Comma-separated CORS origins |
SMTP_HOST |
Yes | SMTP server host |
SMTP_PORT |
Yes | SMTP port (587 for TLS) |
SMTP_SECURE |
No | true for port 465, false for STARTTLS on 587 |
SMTP_USER |
Yes | SMTP username |
SMTP_PASS |
Yes | SMTP password |
EMAIL_FROM |
Yes | Sender address (e.g. noreply@lexai.io) |
REST_COUNTRIES_URL |
No | REST Countries API base URL |
WORLD_TIME_API_URL |
No | World Time API base URL |
EMAIL_VERIFICATION_EXPIRY |
No | OTP verification TTL in seconds (default 600) |
PASSWORD_RESET_EXPIRY |
No | Password reset token TTL in seconds (default 3600) |
OTP_EXPIRY |
No | OTP code TTL in seconds (default 600) |
IPINFO_TOKEN |
No | IPinfo token for higher rate limits (50k/mo free) |
HIBP_API_KEY |
No | HaveIBeenPwned API key for breach checks |
ADMIN_EMAIL |
No | Bootstrap admin email (seed script only) |
ADMIN_PASSWORD |
No | Bootstrap admin password (seed script only) |
| # | Module | Base Path | Endpoints | Auth Required |
|---|---|---|---|---|
| 1 | Health Check | /health |
1 | ❌ |
| 2 | Auth | /api/v1/auth |
12 | Mixed |
| 3 | Users | /api/v1/users |
3 | ✅ |
| 4 | Organizations | /api/v1/orgs |
7 | Mixed |
| 5 | Contracts | /api/v1/contracts |
9 | ✅ |
| 6 | Workflow Status | /api/v1/contracts/:id/status |
3 | ✅ |
| 7 | Comments | /api/v1/contracts/:contractId/comments |
4 | ✅ |
| 8 | Analyses | /api/v1/analyses |
3 | ✅ |
| 8b | AI Features | /api/v1/ai |
7 | ✅ |
| 9 | Notifications | /api/v1/notifications |
6 | ✅ |
| 10 | Enrichment | /api/v1/enrichment |
10 | ✅ |
| 11 | Admin | /api/v1/admin |
13 | ✅ Admin |
| 12 | Dashboard | /api/v1/dashboard |
4 | ✅ |
| 13 | Tags | /api/v1/tags |
3 | ✅ |
| 14 | Bookmarks | /api/v1/bookmarks |
3 | ✅ |
| 15 | Templates | /api/v1/templates |
6 | ✅ |
| 16 | Sharing | /api/v1/shares |
4 | Mixed |
| 17 | Exports | /api/v1/exports |
3 | ✅ |
| 18 | Bulk Operations | /api/v1/bulk |
4 | ✅ Admin/Mgr |
| 19 | Preferences | /api/v1/preferences |
3 | ✅ |
| 20 | Reports | /api/v1/reports |
3 | ✅ |
| 21 | Public APIs | External URLs | 10 | Varies |
| Total | 121 |