Skip to main content

Erghi API Reference

Gateway Base URL (live today): https://api.staging.erghi.aistaging. is currently the only live prefix platform-wide, not a separate sandbox; this is the real, production gateway. api.erghi.ai does not resolve yet. Gateway Base URL (local dev): http://localhost:5080
Conversation API (direct, dev only): http://localhost:5002
Content-Type: application/json

All production API traffic routes through the Gateway API (localhost:5080), which validates JWTs, enforces rate limits, and proxies requests to downstream services.

Authentication

Endpoints marked Auth Required need a Bearer JWT token issued by GoTrue or generated via the Client Credentials flow.

Authorization: Bearer <your-jwt-token>

User Authentication (GoTrue)

Obtain a token for a user:

POST /auth/v1/token?grant_type=password
Content-Type: application/json

{
"email": "agent@company.com",
"password": "your-password"
}

Machine-to-Machine Authentication (M2M)

Obtain a token for a backend service or SDK using Client Credentials:

POST /api/v1/auth/token
Content-Type: application/json

{
"grant_type": "client_credentials",
"client_id": "your-client-id",
"client_secret": "your-client-secret"
}

Response 200 OK

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}

Client Credentials Management

Admins can manage M2M keys via the API:

  • POST /api/v1/auth/keys - Generate a new Client ID and Client Secret pair.
  • GET /api/v1/auth/keys - List active client credentials for a workspace.
  • DELETE /api/v1/auth/keys/{id} - Revoke a credential pair.

Widget-initiated requests (creating conversations, sending messages) do not require a JWT — they authenticate with a widgetId.

Rate Limiting

ScopeLimit
Authenticated users100 requests / minute
Widget endpoints60 requests / minute per IP

When exceeded: HTTP 429 Too Many Requests with Retry-After header.


Conversations

POST /api/conversations — Widget Auth

Start a new conversation. Called by the embedded widget when a visitor sends their first message.

Request body

{
"widgetId": "widget-uuid",
"visitorId": "visitor-uuid-or-null",
"metadata": {
"page": "/pricing",
"referrer": "google.com",
"userId": "optional-authenticated-user-id"
}
}

Response 201 Created

{
"id": "conv-uuid",
"workspaceId": "ws-uuid",
"widgetId": "widget-uuid",
"visitorId": "visitor-uuid",
"assignedAgentId": null,
"status": "open",
"channel": "widget",
"startedAt": "2025-01-15T10:30:00Z",
"closedAt": null,
"metadata": {
"page": "/pricing"
}
}

GET /api/conversations — Auth Required

List all conversations in the authenticated workspace (agents and admins).

Query parameters

ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Results per page
statusstringFilter: open, closed, pending
searchstringSearch by visitor ID

Response 200 OK — Paginated list of conversation objects


GET /api/conversations/{id} — Public

Get a conversation by ID. Used by the widget to resume a session.

Response 200 OK — Full conversation object


POST /api/conversations/{id}/close — Auth Required

Close a conversation and mark it as resolved.

Response 200 OK — Updated conversation with status: "closed" and closedAt timestamp


POST /api/conversations/{id}/assign — Auth Required

Assign a conversation to a specific agent.

Request body

{
"agentId": "agent-user-uuid"
}

Response 200 OK — Updated conversation with assignedAgentId set


Messages

POST /api/conversations/{id}/messages — Public / Auth Required

Send a message in a conversation. Called by both visitors (no auth) and agents (auth required).

Request body

{
"content": "Hello, I have a question about your pricing.",
"senderType": "visitor",
"senderId": "visitor-uuid-or-agent-uuid"
}

senderType values: visitor, agent, ai

Response 201 Created

{
"id": "msg-uuid",
"conversationId": "conv-uuid",
"content": "Hello, I have a question about your pricing.",
"senderType": "visitor",
"senderId": "visitor-uuid",
"isRead": false,
"createdAt": "2025-01-15T10:30:05Z"
}

GET /api/conversations/{id}/messages — Public

Get all messages in a conversation (paginated, chronological).

Query parameters

ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger50Messages per page

Response 200 OK

{
"data": [
{
"id": "msg-uuid",
"conversationId": "conv-uuid",
"content": "Hello!",
"senderType": "visitor",
"senderId": "visitor-uuid",
"isRead": true,
"createdAt": "2025-01-15T10:30:05Z"
}
],
"total": 12,
"page": 1,
"limit": 50
}

POST /api/conversations/{conversationId}/messages/{messageId}/read — Auth Required

Mark a specific message as read (for read receipt tracking).

Response 200 OK — Updated message with isRead: true


Widgets

POST /api/widgets — Auth Required (Admin)

Create a new chat widget for the workspace.

Request body

{
"name": "Homepage Widget",
"welcomeMessage": "Hi! How can we help you today?",
"aiEnabled": true,
"themeColor": "#6366f1",
"autoAssign": true,
"offlineMessage": "We're offline right now. Leave a message and we'll respond ASAP."
}

Response 201 Created

{
"id": "widget-uuid",
"workspaceId": "ws-uuid",
"name": "Homepage Widget",
"welcomeMessage": "Hi! How can we help you today?",
"aiEnabled": true,
"themeColor": "#6366f1",
"autoAssign": true,
"offlineMessage": "We're offline right now...",
"embedCode": "<script src='https://chat.staging.erghi.ai/widget.js' data-widget-id='widget-uuid' async></script>",
"createdAt": "2025-01-15T09:00:00Z"
}

GET /api/widgets — Auth Required

List all widgets in the workspace.

Response 200 OK — Array of widget objects


GET /api/widgets/{id} — Auth Required

Get a single widget's configuration.

Response 200 OK — Full widget object


PUT /api/widgets/{id} — Auth Required (Admin)

Update widget settings.

Request body — Same structure as POST; include only fields you want to change

Response 200 OK — Updated widget object


Admin Endpoints

All /api/admin/ endpoints require the Admin role.

GET /api/admin/conversations/stats

Workspace-wide conversation statistics.

Response 200 OK

{
"totalConversations": 524,
"activeConversations": 18,
"todayConversations": 42,
"avgConversationDuration": 8.3,
"conversationTrend": 12.5
}

GET /api/admin/messages/stats

Message statistics for the workspace.

Response 200 OK

{
"totalMessages": 3205,
"todayMessages": 142,
"avgResponseTime": 1.8,
"messageTrend": 8.3
}

GET /api/admin/conversations

List all workspace conversations with advanced filtering.

Query parameters

ParameterTypeDescription
pageintegerPage number
limitintegerResults per page
statusstringopen, closed, pending
searchstringSearch by visitor ID
agentIdUUIDFilter by assigned agent
widgetIdUUIDFilter by widget

Response 200 OK — Paginated conversation list


GET /api/admin/widgets/stats

Aggregated statistics per widget.

Response 200 OK

{
"totalWidgets": 4,
"activeWidgets": 3,
"widgets": [
{
"widgetId": "widget-uuid",
"name": "Homepage",
"conversationsToday": 18,
"avgResponseTime": 2.1
}
]
}

GET /api/admin/widgets

List all workspace widgets (admin view with usage stats).


POST /api/admin/widgets

Admin-level widget creation (same as POST /api/widgets with additional fields available).


Billing

GET /api/v1/billing/plans — Public

List available Erghi subscription plans.

Response 200 OK

[
{
"id": "plan-free",
"name": "Free",
"monthlyPrice": 0,
"annualPrice": 0,
"maxAgents": 1,
"maxConversations": 100,
"maxAiResponses": 0
},
{
"id": "plan-starter",
"name": "Starter",
"monthlyPrice": 29,
"annualPrice": 278,
"maxAgents": 3,
"maxConversations": 1000,
"maxAiResponses": 500
}
]

GET /api/v1/billing/current — Auth Required

Get the current workspace plan and usage.

Response 200 OK

{
"planId": "plan-starter",
"planName": "Starter",
"agentsUsed": 2,
"agentsLimit": 3,
"conversationsUsed": 412,
"conversationsLimit": 1000,
"aiResponsesUsed": 87,
"aiResponsesLimit": 500,
"billingPeriodStart": "2025-01-01",
"billingPeriodEnd": "2025-01-31"
}

POST /api/v1/billing/checkout — Auth Required

Create a Stripe checkout session.

Request body

{
"planId": "plan-growth",
"interval": "monthly"
}

Response 200 OK

{
"checkoutUrl": "https://checkout.stripe.com/pay/cs_..."
}

POST /api/v1/billing/portal — Auth Required

Create a Stripe customer portal session.

Response 200 OK

{
"portalUrl": "https://billing.stripe.com/session/..."
}

POST /api/v1/billing/add-agent — Auth Required (Admin)

Add an additional agent seat to the current plan (billed prorated).

Response 200 OK — Updated usage object


DELETE /api/v1/billing/remove-agent — Auth Required (Admin)

Remove an agent seat from the plan.


GET /api/v1/billing/invoices — Auth Required

Get invoice history for the workspace.

Response 200 OK — Array of invoice objects with amount, date, and PDF URL


POST /api/v1/billing/checkout — Auth Required (Admin)

Start a checkout for a paid plan, using the workspace's configured payment provider — currently always Stripe (see Payment methods).

Request body

{
"planKey": "growth_monthly",
"successUrl": "https://your-site.example/billing/success",
"cancelUrl": "https://your-site.example/billing/plans",
"startTrial": false
}

Response 200 OK — a checkout URL to redirect the user to (a real Stripe Checkout session).

Paymob and Fawry are planned but not yet implemented — do not build against provider-specific checkout endpoints; there aren't any.


Real-Time: WebSocket / SignalR

Erghi uses SignalR for real-time message delivery. Connect to the hub:

wss://api.staging.erghi.ai/hubs/chat?conversationId=<conv-id>&token=<jwt>

Events received from server:

EventPayloadDescription
ReceiveMessagemessage objectNew message in the conversation
AgentAssigned{ agentId, agentName }Conversation assigned to an agent
ConversationClosed{ conversationId }Conversation was closed
TypingStarted{ senderId, senderType }Sender is typing
TypingStopped{ senderId }Sender stopped typing

Events sent to server:

MethodParametersDescription
JoinConversationconversationIdSubscribe to a conversation's events
LeaveConversationconversationIdUnsubscribe
TypingStartconversationIdNotify others you're typing
TypingStopconversationIdNotify others you stopped

Errors

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
"title": "Bad Request",
"status": 400,
"detail": "widgetId is required",
"traceId": "00-abc123-def456-00"
}
StatusMeaning
400Validation error
401Missing or invalid token
403Insufficient role (e.g., agent accessing admin route)
404Resource not found
429Rate limit exceeded
500Internal error