Skip to content

EIAS API Reference

Complete documentation for all EIAS API endpoints.

Base URL

Development: http://localhost:3000/api
Production: https://your-domain.com/api

Authentication

Most endpoints require authentication. The current MVP uses a mock user for development. Production deployments should implement NextAuth.js.

Interview endpoints (/api/interview/*) use token-based access and do not require authentication.


Table of Contents

  1. Health Check
  2. Dashboard
  3. Projects
  4. Uncertainty Areas
  5. Interview Agents
  6. Invitations
  7. Interview (Public)
  8. Sessions
  9. Insights
  10. VOI System

Health Check

GET /api/health

Check if the API is running.

Response:

{
  "status": "healthy",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Dashboard

GET /api/dashboard/stats

Get dashboard statistics for the current user.

Response:

{
  "activeProjects": 3,
  "pendingInsights": 12,
  "activeInvitations": 5
}

Projects

GET /api/projects

List all projects for the current user.

Query Parameters:

Parameter Type Description
status string Filter by status: active, paused, completed, archived

Response:

{
  "projects": [
    {
      "id": "uuid",
      "name": "EHR Integration Study",
      "description": "Understanding clinical EHR workflows",
      "goals": ["Document workflows", "Identify friction points"],
      "status": "active",
      "createdAt": "2024-01-10T10:00:00.000Z",
      "updatedAt": "2024-01-15T14:30:00.000Z",
      "_count": {
        "uncertaintyAreas": 3,
        "agents": 2
      }
    }
  ]
}

POST /api/projects

Create a new project.

Request Body:

{
  "name": "EHR Integration Study",
  "description": "Understanding clinical EHR workflows",
  "goals": ["Document workflows", "Identify friction points"]
}

Response: 201 Created

{
  "id": "uuid",
  "name": "EHR Integration Study",
  "description": "Understanding clinical EHR workflows",
  "goals": ["Document workflows", "Identify friction points"],
  "status": "active",
  "createdAt": "2024-01-15T10:00:00.000Z",
  "updatedAt": "2024-01-15T10:00:00.000Z"
}

GET /api/projects/[id]

Get a single project with details.

Response:

{
  "id": "uuid",
  "name": "EHR Integration Study",
  "description": "Understanding clinical EHR workflows",
  "goals": ["Document workflows", "Identify friction points"],
  "status": "active",
  "createdAt": "2024-01-10T10:00:00.000Z",
  "updatedAt": "2024-01-15T14:30:00.000Z",
  "uncertaintyAreas": [...],
  "agents": [...],
  "owner": {
    "id": "uuid",
    "name": "Researcher Name"
  }
}

PATCH /api/projects/[id]

Update a project.

Request Body:

{
  "name": "Updated Name",
  "description": "Updated description",
  "goals": ["Updated goals"],
  "status": "paused"
}

Response: Updated project object

DELETE /api/projects/[id]

Delete a project and all related data.

Response: 204 No Content


Uncertainty Areas

GET /api/projects/[id]/areas

List uncertainty areas for a project.

Response:

{
  "areas": [
    {
      "id": "uuid",
      "title": "Documentation Time Burden",
      "description": "How much time clinicians spend on documentation",
      "priority": "high",
      "status": "open",
      "questions": [
        "What percentage of your day is documentation?",
        "Which tasks take the most time?"
      ],
      "createdAt": "2024-01-10T10:00:00.000Z"
    }
  ]
}

POST /api/projects/[id]/areas

Create an uncertainty area.

Request Body:

{
  "title": "Documentation Time Burden",
  "description": "How much time clinicians spend on documentation",
  "priority": "high",
  "questions": ["What percentage of your day is documentation?"]
}

Response: 201 Created with area object

GET /api/projects/[id]/areas/[areaId]

Get a single uncertainty area.

PATCH /api/projects/[id]/areas/[areaId]

Update an uncertainty area.

Request Body:

{
  "title": "Updated Title",
  "description": "Updated description",
  "priority": "medium",
  "status": "partially_addressed",
  "questions": ["Updated questions"]
}

DELETE /api/projects/[id]/areas/[areaId]

Delete an uncertainty area.

Response: 204 No Content


Interview Agents

GET /api/projects/[id]/agents

List interview agents for a project.

Response:

{
  "agents": [
    {
      "id": "uuid",
      "name": "Clinical Workflow Interview",
      "style": "exploratory",
      "timeEstimateMinutes": 20,
      "status": "active",
      "createdAt": "2024-01-12T10:00:00.000Z",
      "focusAreas": [
        {
          "uncertaintyArea": {
            "id": "uuid",
            "title": "Documentation Time Burden"
          }
        }
      ],
      "_count": {
        "invitations": 3
      }
    }
  ]
}

POST /api/projects/[id]/agents

Create an interview agent.

Request Body:

{
  "name": "Clinical Workflow Interview",
  "focusAreaIds": ["area-uuid-1", "area-uuid-2"],
  "style": "exploratory",
  "timeEstimateMinutes": 20
}

The system automatically generates a systemPrompt based on the project context and selected focus areas.

Response: 201 Created with agent object including generated system prompt

POST /api/projects/[id]/agents/generate-prompt

Preview the system prompt that would be generated for an agent configuration.

Request Body:

{
  "name": "Test Agent",
  "focusAreaIds": ["area-uuid-1"],
  "style": "focused",
  "timeEstimateMinutes": 15
}

Response:

{
  "systemPrompt": "You are an expert interviewer conducting a focused interview..."
}

GET /api/projects/[id]/agents/[agentId]

Get a single agent with details.

PATCH /api/projects/[id]/agents/[agentId]

Update an agent.

Request Body:

{
  "name": "Updated Name",
  "status": "paused",
  "focusAreaIds": ["area-uuid-1"],
  "style": "validating",
  "timeEstimateMinutes": 30
}

DELETE /api/projects/[id]/agents/[agentId]

Delete an agent.

Response: 204 No Content


Invitations

GET /api/projects/[id]/agents/[agentId]/invitations

List invitations for an agent.

Response:

{
  "invitations": [
    {
      "id": "uuid",
      "expertName": "Dr. Jane Smith",
      "expertRole": "Physician",
      "expertEmail": "jane@example.com",
      "token": "secure-token-string",
      "expiresAt": "2024-01-22T10:00:00.000Z",
      "status": "pending",
      "createdAt": "2024-01-15T10:00:00.000Z",
      "session": null
    }
  ]
}

POST /api/projects/[id]/agents/[agentId]/invitations

Create an invitation.

Request Body:

{
  "expertName": "Dr. Jane Smith",
  "expertRole": "Physician",
  "expertEmail": "jane@example.com",
  "expiresInDays": 7
}

Response: 201 Created

{
  "id": "uuid",
  "expertName": "Dr. Jane Smith",
  "expertRole": "Physician",
  "token": "secure-token-string",
  "expiresAt": "2024-01-22T10:00:00.000Z",
  "status": "pending",
  "interviewUrl": "http://localhost:3000/interview/secure-token-string"
}

GET /api/projects/[id]/agents/[agentId]/invitations/[invitationId]

Get a single invitation.

PATCH /api/projects/[id]/agents/[agentId]/invitations/[invitationId]

Update an invitation (e.g., extend expiration).

Request Body:

{
  "expiresAt": "2024-01-29T10:00:00.000Z",
  "status": "expired"
}

DELETE /api/projects/[id]/agents/[agentId]/invitations/[invitationId]

Delete an invitation.

Response: 204 No Content


Interview (Public)

These endpoints are accessed by experts via their invitation token. No authentication required.

GET /api/interview/[token]

Validate token and get interview context.

Response (valid token):

{
  "valid": true,
  "invitation": {
    "expertName": "Dr. Jane Smith",
    "expertRole": "Physician",
    "status": "pending"
  },
  "agent": {
    "name": "Clinical Workflow Interview",
    "timeEstimateMinutes": 20
  },
  "project": {
    "name": "EHR Integration Study",
    "description": "Understanding clinical EHR workflows"
  },
  "session": null
}

Response (invalid/expired token):

{
  "valid": false,
  "error": "Invitation has expired"
}

POST /api/interview/[token]/start

Start a new interview session.

Request Body: None required

Response: 201 Created

{
  "sessionId": "uuid",
  "welcomeMessage": "Hello Dr. Smith! I'm here to learn about your experience with clinical workflows..."
}

POST /api/interview/[token]/chat

Send a message and get agent response.

Request Body:

{
  "message": "I typically spend about 3 hours on documentation per day."
}

Response:

{
  "response": "That's a significant amount of time. Could you tell me which specific documentation tasks consume the most of those 3 hours?",
  "sessionId": "uuid",
  "turnCount": 4
}

POST /api/interview/[token]/complete

Complete the interview session.

Request Body:

{
  "corrections": "One clarification: I mentioned 3 hours but that includes both clinical notes and administrative paperwork.",
  "rating": 5
}

Response:

{
  "success": true,
  "message": "Thank you for completing this interview!"
}

Sessions

GET /api/projects/[id]/agents/[agentId]/sessions

List sessions for an agent.

Response:

{
  "sessions": [
    {
      "id": "uuid",
      "status": "completed",
      "startedAt": "2024-01-15T14:00:00.000Z",
      "completedAt": "2024-01-15T14:25:00.000Z",
      "turnCount": 12,
      "expertRating": 5,
      "invitation": {
        "expertName": "Dr. Jane Smith",
        "expertRole": "Physician"
      },
      "_count": {
        "insights": 5
      }
    }
  ]
}

GET /api/projects/[id]/agents/[agentId]/sessions/[sessionId]

Get a session with full transcript.

Response:

{
  "id": "uuid",
  "status": "completed",
  "startedAt": "2024-01-15T14:00:00.000Z",
  "completedAt": "2024-01-15T14:25:00.000Z",
  "turnCount": 12,
  "expertCorrections": "Clarification about documentation time",
  "expertRating": 5,
  "invitation": {
    "expertName": "Dr. Jane Smith",
    "expertRole": "Physician"
  },
  "messages": [
    {
      "id": "uuid",
      "role": "assistant",
      "content": "Hello Dr. Smith! I'm here to learn about...",
      "timestamp": "2024-01-15T14:00:00.000Z"
    },
    {
      "id": "uuid",
      "role": "user",
      "content": "I typically spend about 3 hours...",
      "timestamp": "2024-01-15T14:01:30.000Z"
    }
  ],
  "insights": [...]
}

Insights

POST /api/projects/[id]/agents/[agentId]/sessions/[sessionId]/insights

Extract insights from a completed session.

Request Body: None required

Response: 201 Created

{
  "insights": [
    {
      "id": "uuid",
      "content": "Clinicians spend approximately 3 hours daily on documentation tasks",
      "category": "factual_claim",
      "confidence": "high",
      "status": "pending_review",
      "sourceMessages": [
        {
          "messageId": "uuid",
          "message": {
            "content": "I typically spend about 3 hours on documentation per day."
          }
        }
      ]
    }
  ],
  "count": 5
}

GET /api/projects/[id]/agents/[agentId]/sessions/[sessionId]/insights

List insights for a session.

Response:

{
  "insights": [
    {
      "id": "uuid",
      "content": "Clinicians spend approximately 3 hours daily on documentation tasks",
      "category": "factual_claim",
      "confidence": "high",
      "status": "pending_review",
      "reviewerNotes": null,
      "createdAt": "2024-01-15T15:00:00.000Z",
      "sourceMessages": [...]
    }
  ]
}

PATCH /api/projects/[id]/agents/[agentId]/sessions/[sessionId]/insights/[insightId]

Update an insight (review action).

Request Body:

{
  "status": "integrated",
  "reviewerNotes": "Verified against multiple sources"
}

Status options: - pending_review — Default, awaiting review - integrated — Approved and added to project knowledge - discarded — Rejected, not useful - review_later — Deferred for later review

Response: Updated insight object

GET /api/projects/[id]/insights

Get all insights for a project (aggregated view).

Query Parameters:

Parameter Type Description
status string Filter by status
category string Filter by category
confidence string Filter by confidence

Response:

{
  "insights": [...],
  "stats": {
    "total": 25,
    "pending": 10,
    "integrated": 12,
    "discarded": 3,
    "highConfidence": 15
  }
}

Error Responses

All endpoints return consistent error responses:

{
  "error": "Error message description",
  "code": "ERROR_CODE"
}

Common Error Codes

Code HTTP Status Description
NOT_FOUND 404 Resource does not exist
UNAUTHORIZED 401 Authentication required
FORBIDDEN 403 Insufficient permissions
VALIDATION_ERROR 400 Invalid request body
TOKEN_EXPIRED 400 Invitation token has expired
TOKEN_INVALID 400 Invitation token is invalid
SESSION_ALREADY_EXISTS 400 Interview already started
SESSION_COMPLETED 400 Cannot modify completed session
RATE_LIMITED 429 Too many requests
INTERNAL_ERROR 500 Server error

Rate Limiting

Endpoint Limit
/api/interview/[token]/chat 20 requests/minute per session
All other endpoints 100 requests/minute per user

Rate limit headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1705320000

Webhooks (Future)

Webhooks for integration with external systems are planned for a future release:

  • interview.completed — When an expert completes an interview
  • insights.extracted — When insights are extracted from a session
  • insight.integrated — When an insight is integrated

SDK Examples

JavaScript/TypeScript

// Fetch projects
const response = await fetch('/api/projects');
const { projects } = await response.json();

// Create invitation
const inviteResponse = await fetch(`/api/projects/${projectId}/agents/${agentId}/invitations`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    expertName: 'Dr. Jane Smith',
    expertRole: 'Physician',
    expiresInDays: 7
  })
});
const invitation = await inviteResponse.json();
console.log('Interview URL:', invitation.interviewUrl);

Python

import requests

# Fetch projects
response = requests.get('http://localhost:3000/api/projects')
projects = response.json()['projects']

# Create invitation
invite_response = requests.post(
    f'http://localhost:3000/api/projects/{project_id}/agents/{agent_id}/invitations',
    json={
        'expertName': 'Dr. Jane Smith',
        'expertRole': 'Physician',
        'expiresInDays': 7
    }
)
invitation = invite_response.json()
print(f"Interview URL: {invitation['interviewUrl']}")

VOI System

The VOI (Value of Information) system provides intelligent interview orchestration through research decomposition, adaptive questioning, and cross-interview synthesis.

Research Decomposition

POST /api/projects/[id]/decompose

Decompose a research question into sub-questions with dependency tracking.

Request Body:

{
  "researchQuestion": "How do clinicians experience EHR documentation burden?",
  "maxSubQuestions": 8,
  "expertDomains": ["clinical informatics", "health IT"]
}

Response: 201 Created

{
  "subQuestions": [
    {
      "id": "uuid",
      "title": "Time allocation for documentation",
      "description": "How much time do clinicians spend on documentation vs patient care?",
      "priority": "high",
      "status": "uncovered",
      "confidence": 0,
      "dependencies": []
    },
    {
      "id": "uuid",
      "title": "Documentation workflow pain points",
      "description": "What specific steps in the documentation process cause the most friction?",
      "priority": "high",
      "status": "uncovered",
      "confidence": 0,
      "dependencies": ["uuid-of-time-allocation"]
    }
  ],
  "decompositionId": "uuid",
  "metadata": {
    "totalSubQuestions": 6,
    "highPriority": 3,
    "mediumPriority": 2,
    "lowPriority": 1
  }
}

Sub-Questions

GET /api/projects/[id]/subquestions/[subquestionId]

Get a sub-question with its findings and status.

Response:

{
  "id": "uuid",
  "title": "Time allocation for documentation",
  "description": "How much time do clinicians spend on documentation vs patient care?",
  "priority": "high",
  "status": "partial",
  "confidence": 0.65,
  "currentAnswer": "Clinicians report spending 2-4 hours daily on documentation...",
  "dependencies": [],
  "findings": [
    {
      "id": "uuid",
      "summary": "Physicians spend average 3 hours/day on documentation",
      "confidence": "high",
      "expertId": "uuid",
      "turnNumber": 4
    }
  ],
  "createdAt": "2024-01-15T10:00:00.000Z",
  "updatedAt": "2024-01-16T14:30:00.000Z"
}

PATCH /api/projects/[id]/subquestions/[subquestionId]

Update a sub-question (refine, change priority, or manually update status).

Request Body:

{
  "title": "Updated title",
  "description": "Refined description",
  "priority": "medium",
  "status": "answered",
  "confidence": 0.85
}

Response: Updated sub-question object


VOI Calculation

POST /api/projects/[id]/voi

Calculate Value of Information for candidate questions.

Request Body:

{
  "expertId": "uuid",
  "sessionId": "uuid",
  "candidateQuestions": [
    "What percentage of your workday involves documentation?",
    "Which EHR features do you find most time-consuming?",
    "How does documentation affect your patient interactions?"
  ]
}

Response:

{
  "rankings": [
    {
      "question": "Which EHR features do you find most time-consuming?",
      "voi": 0.42,
      "expectedUtilityGain": 0.15,
      "targetSubQuestions": ["uuid-1", "uuid-2"],
      "rationale": "High coverage potential for uncovered sub-questions"
    },
    {
      "question": "What percentage of your workday involves documentation?",
      "voi": 0.38,
      "expectedUtilityGain": 0.12,
      "targetSubQuestions": ["uuid-1"],
      "rationale": "Directly addresses high-priority sub-question"
    },
    {
      "question": "How does documentation affect your patient interactions?",
      "voi": 0.25,
      "expectedUtilityGain": 0.08,
      "targetSubQuestions": ["uuid-3"],
      "rationale": "Exploratory question with moderate coverage"
    }
  ],
  "currentUtility": {
    "coverage": 0.45,
    "confidence": 0.52,
    "coherence": 0.78,
    "actionability": 0.35,
    "total": 0.51
  },
  "recommendedQuestion": "Which EHR features do you find most time-consuming?"
}

Adaptive Interviews

POST /api/interviews

Start a new adaptive interview session with VOI-driven question selection.

Request Body:

{
  "invitationId": "uuid",
  "useAdaptive": true
}

Response: 201 Created

{
  "sessionId": "uuid",
  "welcomeMessage": "Hello Dr. Smith! I'm conducting research on clinical documentation...",
  "phase": "greeting",
  "knowledgeState": {
    "subQuestionsCovered": 0,
    "totalSubQuestions": 6,
    "coverage": 0
  }
}

GET /api/interviews

List interview sessions with optional filtering.

Query Parameters:

Parameter Type Description
projectId string Filter by project
status string Filter by status: active, completed, abandoned, paused
limit number Results per page (default: 20)
offset number Pagination offset

Response:

{
  "sessions": [
    {
      "id": "uuid",
      "status": "active",
      "phase": "interviewing",
      "turnNumber": 5,
      "startedAt": "2024-01-15T14:00:00.000Z",
      "invitation": {
        "expertName": "Dr. Jane Smith",
        "expertRole": "Physician"
      },
      "analytics": {
        "findingsCount": 3,
        "subQuestionsAddressed": 2,
        "totalSubQuestions": 6,
        "utilityScore": 0.58
      }
    }
  ],
  "total": 15,
  "limit": 20,
  "offset": 0
}

GET /api/interviews/[sessionId]

Get detailed interview state including knowledge progress.

Response:

{
  "id": "uuid",
  "status": "active",
  "phase": "interviewing",
  "turnNumber": 8,
  "startedAt": "2024-01-15T14:00:00.000Z",
  "invitation": {
    "expertName": "Dr. Jane Smith",
    "expertRole": "Physician"
  },
  "messages": [
    {
      "role": "assistant",
      "content": "Hello Dr. Smith!...",
      "timestamp": "2024-01-15T14:00:00.000Z"
    }
  ],
  "knowledgeState": {
    "subQuestions": [
      {
        "id": "uuid",
        "title": "Time allocation",
        "status": "partial",
        "confidence": 0.65
      }
    ],
    "findings": [
      {
        "id": "uuid",
        "summary": "3 hours/day on documentation",
        "confidence": "high",
        "turnNumber": 4
      }
    ],
    "contradictions": []
  },
  "analytics": {
    "turnCount": 8,
    "findingsCount": 4,
    "subQuestionsAddressed": 3,
    "totalSubQuestions": 6,
    "utilityScore": 0.62,
    "phase": "interviewing"
  }
}

POST /api/interviews/[sessionId]

Continue an interview with expert response.

Request Body:

{
  "message": "I typically spend about 3 hours on documentation each day, mostly on progress notes and medication reconciliation."
}

Response:

{
  "response": "That's helpful context. You mentioned medication reconciliation specifically - could you walk me through that process?",
  "turnNumber": 9,
  "phase": "interviewing",
  "voiAnalysis": {
    "selectedQuestion": "medication reconciliation process",
    "voi": 0.38,
    "rationale": "Follow-up addresses partially covered sub-question"
  },
  "findingsExtracted": [
    {
      "id": "uuid",
      "summary": "3 hours daily on documentation, primarily progress notes and med rec",
      "confidence": "high",
      "subQuestionIds": ["uuid-1", "uuid-2"]
    }
  ]
}

DELETE /api/interviews/[sessionId]

Abort an interview session.

Response:

{
  "success": true,
  "message": "Interview session aborted",
  "endReason": "user_aborted"
}

Synthesis

POST /api/projects/[id]/synthesis

Generate a synthesis report across all completed interviews.

Request Body:

{
  "includeContradictions": true,
  "generateExecutiveSummary": true
}

Response:

{
  "projectId": "uuid",
  "generatedAt": "2024-01-20T10:00:00.000Z",
  "subQuestionSummaries": [
    {
      "subQuestionId": "uuid",
      "title": "Time allocation for documentation",
      "status": "answered",
      "confidence": 0.85,
      "synthesizedAnswer": "Clinicians consistently report spending 2-4 hours daily on documentation, with progress notes and medication reconciliation consuming the majority of time.",
      "supportingFindings": [
        {
          "findingId": "uuid",
          "summary": "3 hours/day on documentation",
          "expertWeight": 0.35,
          "confidence": "high"
        }
      ],
      "expertCount": 4
    }
  ],
  "contradictions": [
    {
      "id": "uuid",
      "description": "Conflicting reports on documentation time",
      "nature": "quantitative_disagreement",
      "status": "unresolved",
      "findingA": {
        "id": "uuid",
        "summary": "2 hours/day",
        "expertId": "uuid"
      },
      "findingB": {
        "id": "uuid",
        "summary": "5 hours/day",
        "expertId": "uuid"
      }
    }
  ],
  "overallProgress": {
    "subQuestionsCovered": 5,
    "totalSubQuestions": 6,
    "coverage": 0.83,
    "averageConfidence": 0.72
  },
  "executiveSummary": "Based on 4 expert interviews, this synthesis reveals..."
}

GET /api/projects/[id]/synthesis/stats

Get synthesis statistics for a project.

Response:

{
  "totalInterviews": 6,
  "completedInterviews": 4,
  "totalFindings": 28,
  "subQuestionsCovered": 5,
  "subQuestionsTotal": 6,
  "unresolvedContradictions": 2,
  "totalContradictions": 3,
  "overallCoverage": 0.83
}

GET /api/projects/[id]/contradictions

List contradictions for a project.

Query Parameters:

Parameter Type Description
status string Filter by status: unresolved, resolved, acknowledged

Response:

{
  "contradictions": [
    {
      "id": "uuid",
      "description": "Conflicting reports on documentation time",
      "nature": "quantitative_disagreement",
      "status": "unresolved",
      "resolution": null,
      "findingA": {
        "id": "uuid",
        "summary": "2 hours/day",
        "expertId": "uuid"
      },
      "findingB": {
        "id": "uuid",
        "summary": "5 hours/day",
        "expertId": "uuid"
      }
    }
  ]
}

POST /api/projects/[id]/contradictions/[contradictionId]/resolve

Resolve a contradiction.

Request Body:

{
  "resolution": "The discrepancy likely reflects different clinical settings - the 2-hour estimate came from an outpatient physician while the 5-hour estimate came from an inpatient hospitalist.",
  "status": "resolved"
}

Response:

{
  "id": "uuid",
  "status": "resolved",
  "resolution": "The discrepancy likely reflects different clinical settings...",
  "resolvedAt": "2024-01-20T15:30:00.000Z"
}