{"openapi":"3.0.3","info":{"title":"Jenefa Suite API","version":"1.0.0","description":"Canonical tenant API documentation. API keys are valid for messaging, package, usage, and package renewal routes."},"servers":[{"url":"/","description":"Current environment"}],"tags":[{"name":"Messaging"},{"name":"Logs"},{"name":"Metrics"},{"name":"Package"},{"name":"Package Renewal"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"apiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key"}},"parameters":{"idempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key per request to prevent duplicate sends.","schema":{"type":"string","minLength":8,"maxLength":128}}},"schemas":{"SendEmailRequest":{"type":"object","required":["from","to","subject"],"properties":{"from":{"type":"string","format":"email"},"to":{"type":"string","format":"email"},"cc":{"type":"array","items":{"type":"string","format":"email"},"maxItems":50,"description":"Carbon copy recipients"},"bcc":{"type":"array","items":{"type":"string","format":"email"},"maxItems":50,"description":"Blind carbon copy recipients"},"subject":{"type":"string","minLength":1,"maxLength":255},"html":{"type":"string"},"text":{"type":"string"},"attachments":{"type":"array","maxItems":10,"items":{"type":"object","required":["filename","contentBase64"],"properties":{"filename":{"type":"string","minLength":1,"maxLength":255},"contentBase64":{"type":"string","description":"Base64-encoded attachment content."},"contentType":{"type":"string","minLength":1,"maxLength":255,"example":"application/pdf"},"contentDisposition":{"type":"string","enum":["attachment","inline"],"default":"attachment"},"contentId":{"type":"string","description":"Optional CID for inline attachments."}}}},"provider":{"type":"string","enum":["SMTP","SES"],"default":"SMTP"},"configurationSet":{"type":"string"}}},"SendSmsRequest":{"type":"object","required":["mobile","message"],"properties":{"mobile":{"type":"string","pattern":"^[0-9]{10,15}$"},"message":{"type":"string","minLength":1,"maxLength":1000},"templateId":{"type":"string"},"senderId":{"type":"string"}}},"SendWhatsAppRequest":{"type":"object","required":["mobile","templateId"],"properties":{"mobile":{"type":"string","pattern":"^[0-9]{10,15}$"},"templateId":{"type":"string","minLength":1},"message":{"type":"string","minLength":1,"maxLength":2000},"variables":{"type":"object","additionalProperties":{"type":"string"}}}},"AcceptedSendResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"object","required":["requestAccepted","deduplicated"],"properties":{"requestAccepted":{"type":"boolean","example":true},"emailLogId":{"type":"string"},"smsLogId":{"type":"string"},"whatsappLogId":{"type":"string"},"deduplicated":{"type":"boolean","example":false}}},"requestId":{"type":"string"}}},"LimitBlock":{"type":"object","required":["limit","used","remaining","canSend"],"properties":{"limit":{"type":"integer","minimum":0},"used":{"type":"integer","minimum":0},"remaining":{"type":"integer","minimum":0},"canSend":{"type":"boolean"}}},"LimitsResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"object","required":["periodMonth","package","channels"],"properties":{"periodMonth":{"type":"string","example":"2026-03"},"package":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"channels":{"type":"object","properties":{"email":{"$ref":"#/components/schemas/LimitBlock"},"sms":{"$ref":"#/components/schemas/LimitBlock"},"whatsapp":{"$ref":"#/components/schemas/LimitBlock"},"aiChatbot":{"$ref":"#/components/schemas/LimitBlock"}}}}},"requestId":{"type":"string"}}},"UsageResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"array","items":{"type":"object","required":["metric","count","periodMonth"],"properties":{"metric":{"type":"string","example":"email.sent"},"count":{"type":"integer","minimum":0},"periodMonth":{"type":"string","example":"2026-03"}}}},"requestId":{"type":"string"}}},"PackageResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"object","required":["id","status","currentPeriodStart","currentPeriodEnd","gracePeriodEnd","canSend","renewalRequired","package"],"properties":{"id":{"type":"string","nullable":true},"status":{"type":"string","enum":["ACTIVE","GRACE","EXPIRED","MISSING"],"example":"ACTIVE"},"currentPeriodStart":{"type":"string","format":"date-time","nullable":true},"currentPeriodEnd":{"type":"string","format":"date-time","nullable":true},"gracePeriodEnd":{"type":"string","format":"date-time","nullable":true},"canSend":{"type":"boolean"},"renewalRequired":{"type":"boolean"},"package":{"nullable":true,"type":"object","required":["id","code","name","emailLimit","smsLimit","whatsappLimit","aiChatbotLimit"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"emailLimit":{"type":"integer","minimum":0},"smsLimit":{"type":"integer","minimum":0},"whatsappLimit":{"type":"integer","minimum":0},"aiChatbotLimit":{"type":"integer","minimum":0}}}},"example":{"id":"tp_123","status":"EXPIRED","currentPeriodStart":"2026-03-01T00:00:00.000Z","currentPeriodEnd":"2027-03-01T00:00:00.000Z","gracePeriodEnd":"2027-03-04T00:00:00.000Z","canSend":false,"renewalRequired":true,"package":{"id":"pkg_123","code":"pkg_123","name":"Base Package","emailLimit":10000,"smsLimit":1000,"whatsappLimit":500,"aiChatbotLimit":250}}},"requestId":{"type":"string"}}},"PackageRenewCheckoutResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"object","required":["transactionId","orderId","amount","currency","checkoutKey","package"],"properties":{"transactionId":{"type":"string"},"orderId":{"type":"string"},"amount":{"type":"number","minimum":0},"currency":{"type":"string","example":"INR"},"checkoutKey":{"type":"string"},"package":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}}}},"requestId":{"type":"string"}}},"PackageRenewConfirmRequest":{"type":"object","required":["transactionId","razorpayOrderId","razorpayPaymentId","razorpaySignature"],"properties":{"transactionId":{"type":"string"},"razorpayOrderId":{"type":"string"},"razorpayPaymentId":{"type":"string"},"razorpaySignature":{"type":"string"}}},"PackageTransactionResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"object","required":["id","status","amount","currency","durationDays","createdAt","updatedAt","package"],"properties":{"id":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number","minimum":0},"currency":{"type":"string"},"durationDays":{"type":"integer","minimum":1},"providerOrderId":{"type":"string","nullable":true},"providerPaymentId":{"type":"string","nullable":true},"failureReason":{"type":"string","nullable":true},"paidAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"package":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}}}},"requestId":{"type":"string"}}},"EmailLogItem":{"type":"object","required":["id","provider","from","to","subject","status","createdAt"],"properties":{"id":{"type":"string"},"provider":{"type":"string","enum":["SMTP","SES"]},"from":{"type":"string","format":"email"},"to":{"type":"string","format":"email"},"subject":{"type":"string"},"status":{"type":"string","enum":["QUEUED","SENT","DELIVERED","FAILED","BOUNCED","COMPLAINT"]},"failureReason":{"type":"string","nullable":true},"externalMessageId":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true,"additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}},"EmailLogsResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"object","required":["items","pagination"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/EmailLogItem"}},"pagination":{"type":"object","required":["page","pageSize","total","totalPages"],"properties":{"page":{"type":"integer","minimum":1},"pageSize":{"type":"integer","minimum":1},"total":{"type":"integer","minimum":0},"totalPages":{"type":"integer","minimum":1}}}}},"requestId":{"type":"string"}}},"SmsLogItem":{"type":"object","required":["id","provider","recipientNumber","message","status","createdAt"],"properties":{"id":{"type":"string"},"provider":{"type":"string","enum":["MSG91"]},"senderId":{"type":"string","nullable":true},"recipientNumber":{"type":"string"},"message":{"type":"string"},"templateId":{"type":"string","nullable":true},"status":{"type":"string","enum":["QUEUED","SENT","DELIVERED","FAILED","BOUNCED","COMPLAINT"]},"failureReason":{"type":"string","nullable":true},"externalMessageId":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true,"additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}},"SmsLogsResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"object","required":["items","pagination"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/SmsLogItem"}},"pagination":{"type":"object","required":["page","pageSize","total","totalPages"],"properties":{"page":{"type":"integer","minimum":1},"pageSize":{"type":"integer","minimum":1},"total":{"type":"integer","minimum":0},"totalPages":{"type":"integer","minimum":1}}}}},"requestId":{"type":"string"}}},"WhatsAppLogItem":{"type":"object","required":["id","provider","recipientNumber","status","createdAt"],"properties":{"id":{"type":"string"},"provider":{"type":"string","enum":["MSG91"]},"recipientNumber":{"type":"string"},"templateId":{"type":"string","nullable":true},"message":{"type":"string","nullable":true},"status":{"type":"string","enum":["QUEUED","SENT","DELIVERED","FAILED","BOUNCED","COMPLAINT"]},"failureReason":{"type":"string","nullable":true},"externalMessageId":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true,"additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}},"WhatsAppLogsResponse":{"type":"object","required":["data","requestId"],"properties":{"data":{"type":"object","required":["items","pagination"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppLogItem"}},"pagination":{"type":"object","required":["page","pageSize","total","totalPages"],"properties":{"page":{"type":"integer","minimum":1},"pageSize":{"type":"integer","minimum":1},"total":{"type":"integer","minimum":0},"totalPages":{"type":"integer","minimum":1}}}}},"requestId":{"type":"string"}}},"ErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","requestId"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"requestId":{"type":"string"},"details":{"type":"object","additionalProperties":true}}}}}}},"paths":{"/api/email/send":{"post":{"tags":["Messaging"],"summary":"Queue transactional email send","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailRequest"}}}},"responses":{"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptedSendResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Plan required or plan limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/email/logs":{"get":{"tags":["Logs"],"summary":"List email logs with pagination and search","description":"Returns paginated email logs for the authenticated tenant, ordered by most recent first. Supports full-text search across sender, recipient, and subject fields.","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1},"description":"Page number (1-indexed)."},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Number of items per page."},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search across from, to, and subject fields."},{"name":"status","in":"query","schema":{"type":"string","enum":["QUEUED","SENT","DELIVERED","FAILED","BOUNCED","COMPLAINT"]},"description":"Filter by email status."}],"responses":{"200":{"description":"Paginated email logs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailLogsResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/sms/send":{"post":{"tags":["Messaging"],"summary":"Queue SMS send","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendSmsRequest"}}}},"responses":{"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptedSendResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Plan required or plan limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/sms/logs":{"get":{"tags":["Logs"],"summary":"List SMS logs with pagination and search","description":"Returns paginated SMS logs for the authenticated tenant, ordered by most recent first. Supports search across recipient number, message, and sender ID.","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1},"description":"Page number (1-indexed)."},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Number of items per page."},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search across recipient number, message, and sender ID."},{"name":"status","in":"query","schema":{"type":"string","enum":["QUEUED","SENT","DELIVERED","FAILED","BOUNCED","COMPLAINT"]},"description":"Filter by SMS status."}],"responses":{"200":{"description":"Paginated SMS logs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmsLogsResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/whatsapp/send":{"post":{"tags":["Messaging"],"summary":"Queue WhatsApp template send","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendWhatsAppRequest"}}}},"responses":{"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptedSendResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Plan required or plan limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/whatsapp/logs":{"get":{"tags":["Logs"],"summary":"List WhatsApp logs with pagination and search","description":"Returns paginated WhatsApp logs for the authenticated tenant, ordered by most recent first. Supports search across recipient number, template ID, and message.","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1},"description":"Page number (1-indexed)."},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Number of items per page."},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search across recipient number, template ID, and message."},{"name":"status","in":"query","schema":{"type":"string","enum":["QUEUED","SENT","DELIVERED","FAILED","BOUNCED","COMPLAINT"]},"description":"Filter by WhatsApp status."}],"responses":{"200":{"description":"Paginated WhatsApp logs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhatsAppLogsResponse"}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/limits":{"get":{"tags":["Metrics"],"summary":"Get monthly limits and remaining quota","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Current limits and usage","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LimitsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/usage":{"get":{"tags":["Metrics"],"summary":"Get current month usage counters","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Usage summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/package":{"get":{"tags":["Package"],"summary":"Get package status","description":"Returns the tenant package status for ACTIVE, GRACE, EXPIRED, or MISSING states. Renewal-required states still return 200.","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"responses":{"200":{"description":"Package status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackageResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/tenant/package/renew/checkout":{"post":{"tags":["Package Renewal"],"summary":"Create package renewal Razorpay checkout","description":"Canonical renewal step 1. Supports x-api-key and bearer/session auth. Rate limited per tenant.","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"responses":{"201":{"description":"Checkout created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackageRenewCheckoutResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No active package","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/tenant/package/renew/confirm":{"post":{"tags":["Package Renewal"],"summary":"Confirm package renewal payment","description":"Canonical renewal step 2. Verifies payment signature and finalizes package renewal. Rate limited per tenant.","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackageRenewConfirmRequest"}}}},"responses":{"200":{"description":"Renewal captured and package updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackageTransactionResponse"}}}},"400":{"description":"Invalid payment details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Transaction not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}