{
  "openapi": "3.1.0",
  "info": {
    "title": "Resnia API",
    "description": "Shared inbox API for email and WhatsApp. Multi-tenant, real-time, API-first.",
    "version": "1.0.0",
    "contact": { "name": "Resnia", "url": "https://resnia.com" }
  },
  "servers": [{ "url": "https://api.resnia.com", "description": "Production" }],
  "security": [{ "bearer": [] }],
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "API token obtained from POST /v1/auth/login. Prefix: rn_"
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "data": {},
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" }
            }
          }
        }
      },
      "Paginated": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "data": {
            "type": "object",
            "properties": {
              "items": { "type": "array" },
              "cursor": { "type": "string", "nullable": true }
            }
          }
        }
      }
    },
    "parameters": {
      "limit": { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 25, "maximum": 100 } },
      "after": { "name": "after", "in": "query", "schema": { "type": "string" } }
    }
  },
  "paths": {
    "/v1/health": {
      "get": {
        "tags": ["System"],
        "summary": "Health check",
        "security": [],
        "responses": { "200": { "description": "Service status with database and runtime info" } }
      }
    },
    "/v1/tenants": {
      "post": {
        "tags": ["Tenants"],
        "summary": "Create a new tenant with owner agent",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name", "slug", "agentEmail", "password"],
                "properties": {
                  "name": { "type": "string" },
                  "slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$" },
                  "agentName": { "type": "string" },
                  "agentEmail": { "type": "string", "format": "email" },
                  "password": { "type": "string", "minLength": 8 }
                }
              }
            }
          }
        },
        "responses": { "201": { "description": "Tenant and agent created" } }
      }
    },
    "/v1/tenant": {
      "get": { "tags": ["Tenants"], "summary": "Get current tenant info", "responses": { "200": { "description": "Tenant details" } } },
      "patch": { "tags": ["Tenants"], "summary": "Update tenant (admin)", "responses": { "200": { "description": "Updated tenant" } } }
    },
    "/v1/auth/login": {
      "post": {
        "tags": ["Auth"],
        "summary": "Login with email and password",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email", "password"],
                "properties": {
                  "email": { "type": "string", "format": "email" },
                  "password": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Bearer token and agent info" } }
      }
    },
    "/v1/auth/logout": {
      "post": { "tags": ["Auth"], "summary": "Revoke current token", "responses": { "200": { "description": "Token revoked" } } }
    },
    "/v1/auth/password": {
      "put": { "tags": ["Auth"], "summary": "Change password", "responses": { "200": { "description": "Password updated" } } }
    },
    "/v1/auth/refresh": {
      "post": { "tags": ["Auth"], "summary": "Refresh token (revokes current, issues new)", "responses": { "200": { "description": "New token and expiry" } } }
    },
    "/v1/auth/forgot": {
      "post": {
        "tags": ["Auth"],
        "summary": "Request password reset token",
        "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "format": "email" } } } } } },
        "responses": { "200": { "description": "Reset token issued (always returns sent:true)" } }
      }
    },
    "/v1/auth/reset": {
      "post": {
        "tags": ["Auth"],
        "summary": "Reset password with token",
        "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["token", "newPassword"], "properties": { "token": { "type": "string" }, "newPassword": { "type": "string", "minLength": 8 } } } } } },
        "responses": { "200": { "description": "Password reset and all sessions revoked" } }
      }
    },
    "/v1/me": {
      "get": { "tags": ["Agents"], "summary": "Get current agent profile", "responses": { "200": { "description": "Agent details" } } },
      "patch": { "tags": ["Agents"], "summary": "Update own profile (name, avatar)", "responses": { "200": { "description": "Updated agent" } } }
    },
    "/v1/me/availability": {
      "put": { "tags": ["Agents"], "summary": "Set availability (online, away, busy, offline)", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["availability"], "properties": { "availability": { "type": "string", "enum": ["online", "away", "busy", "offline"] } } } } } }, "responses": { "200": { "description": "Updated agent with new availability" } } }
    },
    "/v1/agents": {
      "get": { "tags": ["Agents"], "summary": "List agents", "responses": { "200": { "description": "Paginated agents" } } },
      "post": { "tags": ["Agents"], "summary": "Create agent (admin)", "responses": { "201": { "description": "Agent created" } } }
    },
    "/v1/agents/{id}": {
      "get": { "tags": ["Agents"], "summary": "Get agent by ID", "responses": { "200": { "description": "Agent details" } } },
      "patch": { "tags": ["Agents"], "summary": "Update agent", "responses": { "200": { "description": "Updated agent" } } },
      "delete": { "tags": ["Agents"], "summary": "Delete agent (admin)", "responses": { "200": { "description": "Agent deleted" } } }
    },
    "/v1/tokens": {
      "get": { "tags": ["Tokens"], "summary": "List API tokens (admin)", "responses": { "200": { "description": "Paginated tokens" } } },
      "post": { "tags": ["Tokens"], "summary": "Create API token (admin)", "responses": { "201": { "description": "Token created" } } }
    },
    "/v1/tokens/{id}": {
      "delete": { "tags": ["Tokens"], "summary": "Revoke API token (admin)", "responses": { "200": { "description": "Token revoked" } } }
    },
    "/v1/contacts": {
      "get": { "tags": ["Contacts"], "summary": "List contacts", "parameters": [{ "name": "q", "in": "query", "schema": { "type": "string" } }], "responses": { "200": { "description": "Paginated contacts" } } },
      "post": { "tags": ["Contacts"], "summary": "Create contact", "responses": { "201": { "description": "Contact created" } } }
    },
    "/v1/contacts/export": {
      "get": { "tags": ["Contacts"], "summary": "Export all contacts as CSV", "responses": { "200": { "description": "CSV file", "content": { "text/csv": {} } } } }
    },
    "/v1/contacts/import": {
      "post": { "tags": ["Contacts"], "summary": "Import contacts from CSV (admin)", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "file": { "type": "string", "format": "binary" } } } } } }, "responses": { "200": { "description": "Import result with created/updated/skipped counts" } } }
    },
    "/v1/contacts/{id}": {
      "get": { "tags": ["Contacts"], "summary": "Get contact", "responses": { "200": { "description": "Contact details" } } },
      "patch": { "tags": ["Contacts"], "summary": "Update contact", "responses": { "200": { "description": "Updated contact" } } },
      "delete": { "tags": ["Contacts"], "summary": "Delete contact (admin)", "responses": { "200": { "description": "Contact deleted" } } }
    },
    "/v1/contacts/{id}/merge": {
      "post": { "tags": ["Contacts"], "summary": "Merge source contact into target (admin)", "responses": { "200": { "description": "Contacts merged" } } }
    },
    "/v1/conversations": {
      "get": { "tags": ["Conversations"], "summary": "List conversations", "responses": { "200": { "description": "Paginated conversations" } } },
      "post": { "tags": ["Conversations"], "summary": "Create conversation", "responses": { "201": { "description": "Conversation created" } } }
    },
    "/v1/conversations/counts": {
      "get": { "tags": ["Conversations"], "summary": "Count conversations by status", "responses": { "200": { "description": "Status counts" } } }
    },
    "/v1/conversations/bulk": {
      "post": { "tags": ["Conversations"], "summary": "Bulk update conversations (max 100)", "responses": { "200": { "description": "Bulk result" } } }
    },
    "/v1/conversations/{id}": {
      "get": { "tags": ["Conversations"], "summary": "Get conversation", "responses": { "200": { "description": "Conversation details" } } },
      "patch": { "tags": ["Conversations"], "summary": "Update conversation", "responses": { "200": { "description": "Updated conversation" } } },
      "delete": { "tags": ["Conversations"], "summary": "Delete conversation (admin)", "responses": { "200": { "description": "Conversation deleted" } } }
    },
    "/v1/conversations/{id}/relations": {
      "get": { "tags": ["Conversations"], "summary": "Find related conversations by contact and labels", "responses": { "200": { "description": "Related conversations grouped by contact and label" } } }
    },
    "/v1/conversations/{id}/export": {
      "get": { "tags": ["Conversations"], "summary": "Export conversation transcript as plain text", "responses": { "200": { "description": "Text transcript", "content": { "text/plain": {} } } } }
    },
    "/v1/conversations/{id}/labels": {
      "post": { "tags": ["Conversations"], "summary": "Add label to conversation", "responses": { "201": { "description": "Label added" } } }
    },
    "/v1/conversations/{id}/labels/{labelId}": {
      "delete": { "tags": ["Conversations"], "summary": "Remove label from conversation", "responses": { "200": { "description": "Label removed" } } }
    },
    "/v1/conversations/{id}/messages": {
      "get": { "tags": ["Messages"], "summary": "List messages in conversation", "responses": { "200": { "description": "Paginated messages" } } },
      "post": { "tags": ["Messages"], "summary": "Send a message", "responses": { "201": { "description": "Message created" } } }
    },
    "/v1/messages/search": {
      "get": { "tags": ["Messages"], "summary": "Search messages across conversations", "parameters": [{ "name": "q", "in": "query", "required": true, "schema": { "type": "string", "minLength": 2 } }], "responses": { "200": { "description": "Matching messages" } } }
    },
    "/v1/conversations/{id}/scheduled": {
      "get": { "tags": ["Scheduled"], "summary": "List scheduled messages", "responses": { "200": { "description": "Paginated scheduled messages" } } },
      "post": { "tags": ["Scheduled"], "summary": "Schedule a message", "responses": { "201": { "description": "Scheduled message created" } } }
    },
    "/v1/conversations/{id}/scheduled/{schedId}": {
      "delete": { "tags": ["Scheduled"], "summary": "Cancel scheduled message", "responses": { "200": { "description": "Scheduled message cancelled" } } }
    },
    "/v1/inboxes": {
      "get": { "tags": ["Inboxes"], "summary": "List inboxes", "responses": { "200": { "description": "Paginated inboxes" } } },
      "post": { "tags": ["Inboxes"], "summary": "Create inbox (admin)", "responses": { "201": { "description": "Inbox created" } } }
    },
    "/v1/inboxes/{id}": {
      "get": { "tags": ["Inboxes"], "summary": "Get inbox", "responses": { "200": { "description": "Inbox details" } } },
      "patch": { "tags": ["Inboxes"], "summary": "Update inbox (admin)", "responses": { "200": { "description": "Updated inbox" } } },
      "delete": { "tags": ["Inboxes"], "summary": "Delete inbox (admin)", "responses": { "200": { "description": "Inbox deleted" } } }
    },
    "/v1/inboxes/{id}/connect": {
      "post": { "tags": ["Inboxes"], "summary": "Connect inbox channel (admin)", "responses": { "200": { "description": "Inbox connected" } } }
    },
    "/v1/inboxes/{id}/disconnect": {
      "post": { "tags": ["Inboxes"], "summary": "Disconnect inbox channel (admin)", "responses": { "200": { "description": "Inbox disconnected" } } }
    },
    "/v1/inboxes/{id}/status": {
      "get": { "tags": ["Inboxes"], "summary": "Get inbox connection status", "responses": { "200": { "description": "Database and live adapter status" } } }
    },
    "/v1/labels": {
      "get": { "tags": ["Labels"], "summary": "List labels", "responses": { "200": { "description": "Paginated labels" } } },
      "post": { "tags": ["Labels"], "summary": "Create label", "responses": { "201": { "description": "Label created" } } }
    },
    "/v1/labels/{id}": {
      "get": { "tags": ["Labels"], "summary": "Get label by ID", "responses": { "200": { "description": "Label details" } } },
      "patch": { "tags": ["Labels"], "summary": "Update label", "responses": { "200": { "description": "Updated label" } } },
      "delete": { "tags": ["Labels"], "summary": "Delete label (admin)", "responses": { "200": { "description": "Label deleted" } } }
    },
    "/v1/teams": {
      "get": { "tags": ["Teams"], "summary": "List teams", "responses": { "200": { "description": "Paginated teams" } } },
      "post": { "tags": ["Teams"], "summary": "Create team (admin)", "responses": { "201": { "description": "Team created" } } }
    },
    "/v1/teams/{id}": {
      "get": { "tags": ["Teams"], "summary": "Get team", "responses": { "200": { "description": "Team details" } } },
      "patch": { "tags": ["Teams"], "summary": "Update team (admin)", "responses": { "200": { "description": "Updated team" } } },
      "delete": { "tags": ["Teams"], "summary": "Delete team (admin)", "responses": { "200": { "description": "Team deleted" } } }
    },
    "/v1/teams/{id}/members": {
      "get": { "tags": ["Teams"], "summary": "List team members", "responses": { "200": { "description": "Team members with agent details" } } },
      "post": { "tags": ["Teams"], "summary": "Add member to team (admin)", "responses": { "201": { "description": "Member added" } } }
    },
    "/v1/teams/{id}/members/{agentId}": {
      "delete": { "tags": ["Teams"], "summary": "Remove member from team (admin)", "responses": { "200": { "description": "Member removed" } } }
    },
    "/v1/canned_responses": {
      "get": { "tags": ["Canned Responses"], "summary": "List canned responses", "responses": { "200": { "description": "Paginated canned responses" } } },
      "post": { "tags": ["Canned Responses"], "summary": "Create canned response", "responses": { "201": { "description": "Canned response created" } } }
    },
    "/v1/canned_responses/{id}": {
      "patch": { "tags": ["Canned Responses"], "summary": "Update canned response", "responses": { "200": { "description": "Updated" } } },
      "delete": { "tags": ["Canned Responses"], "summary": "Delete canned response (admin)", "responses": { "200": { "description": "Deleted" } } }
    },
    "/v1/webhooks": {
      "get": { "tags": ["Webhooks"], "summary": "List webhooks (admin)", "responses": { "200": { "description": "Paginated webhooks" } } },
      "post": { "tags": ["Webhooks"], "summary": "Create webhook (admin)", "responses": { "201": { "description": "Webhook created" } } }
    },
    "/v1/webhooks/{id}": {
      "get": { "tags": ["Webhooks"], "summary": "Get webhook (admin)", "responses": { "200": { "description": "Webhook details" } } },
      "patch": { "tags": ["Webhooks"], "summary": "Update webhook (admin)", "responses": { "200": { "description": "Updated" } } },
      "delete": { "tags": ["Webhooks"], "summary": "Delete webhook (admin)", "responses": { "200": { "description": "Deleted" } } }
    },
    "/v1/articles": {
      "get": { "tags": ["Knowledge Base"], "summary": "List articles", "responses": { "200": { "description": "Paginated articles" } } },
      "post": { "tags": ["Knowledge Base"], "summary": "Create article", "responses": { "201": { "description": "Article created" } } }
    },
    "/v1/articles/{id}": {
      "get": { "tags": ["Knowledge Base"], "summary": "Get article", "responses": { "200": { "description": "Article details" } } },
      "patch": { "tags": ["Knowledge Base"], "summary": "Update article", "responses": { "200": { "description": "Updated" } } },
      "delete": { "tags": ["Knowledge Base"], "summary": "Delete article (admin)", "responses": { "200": { "description": "Deleted" } } }
    },
    "/v1/media": {
      "post": { "tags": ["Media"], "summary": "Upload file (max 25 MB)", "responses": { "201": { "description": "File uploaded" } } }
    },
    "/v1/media/{id}": {
      "get": { "tags": ["Media"], "summary": "Get file metadata", "responses": { "200": { "description": "File info" } } },
      "delete": { "tags": ["Media"], "summary": "Delete file (admin)", "responses": { "200": { "description": "File deleted" } } }
    },
    "/v1/media/{id}/download": {
      "get": { "tags": ["Media"], "summary": "Download file", "responses": { "200": { "description": "File content" } } }
    },
    "/v1/conversations/{id}/csat": {
      "post": { "tags": ["CSAT"], "summary": "Submit CSAT rating for conversation", "responses": { "201": { "description": "Rating created" } } }
    },
    "/v1/conversations/{id}/csat/survey": {
      "post": { "tags": ["CSAT"], "summary": "Send CSAT survey to contact (generates public link)", "responses": { "200": { "description": "Survey URL and token" } } }
    },
    "/csat/{token}": {
      "get": { "tags": ["CSAT"], "summary": "Public CSAT survey page", "security": [], "responses": { "200": { "description": "HTML survey form" } } },
      "post": { "tags": ["CSAT"], "summary": "Submit public CSAT rating", "security": [], "responses": { "200": { "description": "Rating submitted" } } }
    },
    "/v1/csat/stats": {
      "get": { "tags": ["CSAT"], "summary": "Aggregate CSAT statistics", "responses": { "200": { "description": "Average, total, distribution" } } }
    },
    "/v1/analytics": {
      "get": { "tags": ["Analytics"], "summary": "Conversation analytics overview", "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "default": 30, "minimum": 1, "maximum": 365 } }], "responses": { "200": { "description": "Total conversations, resolved count, avg first reply time (ms), avg resolution time (ms), daily volume" } } }
    },
    "/v1/audit": {
      "get": { "tags": ["Audit"], "summary": "List audit entries (admin)", "responses": { "200": { "description": "Paginated audit entries" } } }
    },
    "/v1/jobs/{id}": {
      "get": { "tags": ["Jobs"], "summary": "Get async job status", "responses": { "200": { "description": "Job details" } } }
    },
    "/v1/ws": {
      "get": { "tags": ["WebSocket"], "summary": "WebSocket connection for real-time events", "description": "Upgrade to WebSocket. Receives events: message.created, typing, stop_typing, conversation.updated. Send {type: 'typing', conversationId: '...'} to broadcast typing." }
    },
    "/v1/openapi.json": {
      "get": { "tags": ["System"], "summary": "This OpenAPI specification", "security": [], "responses": { "200": { "description": "OpenAPI 3.1 JSON" } } }
    }
  },
  "tags": [
    { "name": "System" },
    { "name": "Auth" },
    { "name": "Tenants" },
    { "name": "Agents" },
    { "name": "Tokens" },
    { "name": "Contacts" },
    { "name": "Conversations" },
    { "name": "Messages" },
    { "name": "Scheduled" },
    { "name": "Inboxes" },
    { "name": "Labels" },
    { "name": "Teams" },
    { "name": "Canned Responses" },
    { "name": "Webhooks" },
    { "name": "Knowledge Base" },
    { "name": "Media" },
    { "name": "CSAT" },
    { "name": "Analytics" },
    { "name": "Audit" },
    { "name": "Jobs" },
    { "name": "WebSocket" }
  ]
}
