Ema 2.0 API Documentation

Gold-standard integration for health experiences.

Everything you need to integrate Ema into your platform. Stream responses, manage user sessions, and deliver clinical-grade conversational experiences to your users.

Quick Start Integration

Get up and running with the Ema API in minutes. Follow these steps to start delivering empathetic, clinically accurate conversations to your users.

Base URL
https://api.emaapi.com
1

Get Suggested Prompts

Optionally display curated prompts to help users get started with empathetic, health-focused conversations.

2

Create User Session

Call the Create User endpoint to get a unique userId for managing personalized conversation context.

3

Start Streaming

Use the Live Talk endpoint to send messages and receive real-time streaming responses from Ema.

Common Headers
Content-Type: application/json Authorization: Bearer YOUR_API_KEY
POST /ema/user

Create User Session

Creates a new user session that serves as the unique identifier for conversations. This endpoint must be called before sending any messages.

📝

Request Body

JSON Request
{ "tenant": "TENANT_NAME" }

Success Response

200 OK
{ "_id": "68542067e43c0200023f3b1b", "tenant": "TENANT_NAME" }

Error Responses

400 Bad Request
{ "code": "MISSING_FIELD", "errors": [ "'tenant' is required." ] }

Error Responses

400 Bad Request
{ "code": "BAD_REQUEST", "errors": [ "tenant is required." ] }
403 Forbidden
{ "code": "FORBIDDEN", "errors": [ "API Key not valid" ] }
400 Invalid Tenant
{ "code": "BAD_TENANT", "errors": [ "Tenant not onboarded." ] }
POST /ema/live-talk

Send Message with Streaming Response

Send a message to Ema 2.0 and receive a live streaming response using Server-Sent Events (SSE). This endpoint supports optional user context for personalized responses.

📝

Request Body

JSON Request
{ "tenant": "TENANT_NAME", "message": "MESSAGE_TEXT", "userId": "USER_ID", "context": "USER_PROFILE_INFORMATION (optional)" }
💡

How to Use the context Field

🔄

Streamed Response (SSE Format)

Streaming Response
data: {"content":[{"index":0,"type":"text","text":{"value":"Hey","annotations":[]}}]} data: {"content":[{"index":0,"type":"text","text":{"value":" again"}}]} data: {"content":[{"index":0,"type":"text","text":{"value":"!"}}]} data: {"content":[{"index":0,"type":"text","text":{"value":" 🤗"}}]} data: {"messageId":"685abef2e5cb107b22b14394"} data: [DONE]

Note: The messageId indicates the id of the message created once the stream finishes.

Note: The [DONE] message indicates the end of the stream.

💡 Important: This endpoint uses Server-Sent Events (SSE) to stream responses. Each streamed message is prefixed with data: (e.g., data: {"content": [...]}) per the SSE protocol. Some tools like Postman automatically hide the data: prefix when displaying responses in the UI but preserve it when saving to file. This is expected behavior and not an error.

Streamed Error Responses

Error Response Format
data: {"error":true,"message":"UserId required","statusCode":400}

🎮 Streaming Response Demo

This is a visual simulation showing how the streaming response looks and feels:

Click the button above to see a simulation of how streaming text appears...
GET /ema/messages

Get Chat Message History

Retrieves the chat history for a specific user with pagination support.

🔍

Query Parameters

Required Parameters
userId: required - User session identifier page: required - Page number for pagination per_page: required - Number of messages per page tenant: required - Tenant identifier

Success Response

200 OK
{ "messages": [ { "_id": "msg1", "userId": "user123", "sender": "assistant", "message": "Hello!", "timestamp": "2025-03-27T21:48:02.000Z", "quizQuestion": null, "flag": {} }, { "_id": "msg2", "userId": "user123", "sender": "user", "message": "Hi", "timestamp": "2025-03-27T21:47:51.000Z", "quizQuestion": { "answerChoices": [] }, "flag": {} } ], "pagination": { "total": 10, "per_page": 10, "page": 1 }, "conversation": { "status": "conversation" } }
POST /ema/messages/flag

Flag Ema's Response

Flags a specific message from Ema as “good” or “bad", accepting optional reason and description fields to provide feedback context.

📝

Request Body

JSON Request
{ "userId": "USER_ID", "messageId": "MESSAGE_ID", "tenant": "TENANT_NAME", "flag": { "value": "good" | "bad", "reason": string | null, "description": string | null } }

Success Response

200 OK
{ "message": "Message flagged successfully", "messageUpdated": { "quizQuestion": null, "flag": { "value": "bad", "reason": "Inaccurate", "description": "This message lacks accuracy" }, "createdAt": 1743090337, "updatedAt": 1743090337, "messageType": "text", "stickers": [], "_id": "abc123", "client": "cba321", "from": "assistant", "message": "Hey! 😊 How's it going?", "tenantId": "your-tenant-name", "__v": 0 } }
GET /ema/suggestions

Get Suggested Prompts

Returns a list of suggested prompts to help users get started with their conversations, along with customizable tenant-specific messages for busy and limit states.

🔍

Query Parameters

Required Parameters
tenant: required - Tenant identifier
💡 Important: This endpoint only accepts query parameters. Do not include a request body as it will cause the request to fail.

Success Response

200 OK
{ "tenant": "TENANT_NAME", "prompts": [ "What can my 6mo eat?", "Help me make my hospital bag", "I have a headache" ], "busyMessage": "Custom message to display when the system is busy", "limitMessage": "Custom message to display when user reaches usage limits, if enabled for your tenant" }

Error Responses

400 Bad Request
{ "code": "BAD_REQUEST", "errors": [ "tenant is required." ] }
403 Forbidden
{ "code": "FORBIDDEN", "errors": [ "API Key not valid" ] }
400 Invalid Tenant
{ "code": "BAD_TENANT", "errors": [ "Tenant not onboarded." ] }

Integration Examples

Production-ready code snippets to accelerate your integration.

JavaScript Integration Example
// Create user session const createUser = async () => { const response = await fetch('/ema/user', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_API_KEY' }, body: JSON.stringify({ tenant: 'your-tenant' }) }); const user = await response.json(); return user._id; }; // Send message with streaming const sendMessage = async (userId, message) => { const response = await fetch('/ema/live-talk', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_API_KEY' }, body: JSON.stringify({ tenant: 'your-tenant', message: message, userId: userId }) }); const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const data = line.slice(6); if (data === '[DONE]') return; try { const parsed = JSON.parse(data); console.log(parsed.content[0].text.value); } catch (e) { // Handle parsing errors } } } } };

Quick Navigation

❓ Frequently Asked Questions

How should I format the context field for Ema?
Always send human-readable, relevant information in the context field. For example, use User goal: Improve sleep quality; User condition: Asthma; Preferred language: Spanish instead of coded or raw data. Only include data Ema will use to customize her response.
Should I send only new user data, or all user data, in the context field each time?
Always send the full set of relevant user information in the context field with every request. Ema does not store user profiles, so any information you want Ema to use for the conversation must be included each time—even if only some data has changed.
What text formatting does the API support?
Our API supports text formatting using standard inline Markdown syntax, including:
  • Bold**bold**
  • Italic*italic*
  • Bold + Italic**_text_** or ***text***
  • Blockquotes → > text
  • Unordered lists → - item
  • Ordered lists → 1. item
  • Links → [text](https://example.com)
  • Headings → # through ######
  • Horizontal rules → ---
  • Tables → using pipes | and dashes -