{"openapi":"3.0.3","info":{"title":"Email SaaS Public API","version":"1.0.0","description":"Public API for transactional email, SMS, and WhatsApp Cloud: send, status, and timelines. Scopes: send:sms / read:sms, send:whatsapp / read:whatsapp. Tenant-managed opt-outs reject SMS and WhatsApp sends with SMS_RECIPIENT_OPTED_OUT / WHATSAPP_RECIPIENT_OPTED_OUT (HTTP 403). WhatsApp requires platform WHATSAPP_* configuration."},"servers":[{"url":"https://api.commtunnel.cloud"}],"tags":[{"name":"Public Email API"},{"name":"Public SMS API"},{"name":"Public WhatsApp API"}],"components":{"securitySchemes":{"ApiKeyBearer":{"type":"http","scheme":"bearer","bearerFormat":"API_KEY","description":"Use API key as Bearer token in Authorization header."}},"schemas":{"SendEmailRequest":{"type":"object","required":["from","to","subject"],"properties":{"from":{"type":"string","format":"email"},"to":{"type":"string","format":"email"},"subject":{"type":"string"},"html":{"type":"string"},"text":{"type":"string"},"templateId":{"type":"string","format":"uuid"},"variables":{"type":"object","additionalProperties":true},"scheduledFor":{"type":"string","format":"date-time"},"tracking":{"type":"boolean"},"sandboxMode":{"type":"boolean","description":"If true, persist and audit but do not deliver."},"headers":{"type":"object","additionalProperties":{"type":"string"}},"listUnsubscribeUrl":{"type":"string","format":"uri"},"listUnsubscribeMailto":{"type":"string","format":"email"},"listUnsubscribeOneClick":{"type":"boolean","description":"When true and listUnsubscribeUrl is set, adds List-Unsubscribe-Post one-click header."},"idempotencyKey":{"type":"string"}}},"PreviewEmailRequest":{"type":"object","properties":{"from":{"type":"string","format":"email"},"to":{"type":"string","format":"email"},"subject":{"type":"string"},"html":{"type":"string"},"text":{"type":"string"},"templateId":{"type":"string","format":"uuid"},"variables":{"type":"object","additionalProperties":true},"tracking":{"type":"boolean"}}},"PreviewEmailResponse":{"type":"object","properties":{"subject":{"type":"string"},"html":{"type":"string"},"text":{"type":"string"},"trackingEnabled":{"type":"boolean"},"estimatedSizeBytes":{"type":"integer"},"moderation":{"type":"object","properties":{"blocked":{"type":"boolean"},"token":{"type":"string","nullable":true}}}}},"SendEmailAccepted":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"duplicate":{"type":"boolean"},"sandboxMode":{"type":"boolean"}}},"SendSmsRequest":{"type":"object","required":["to","senderId","body"],"properties":{"to":{"type":"string","description":"E.164 recipient, e.g. +15551234567"},"senderId":{"type":"string","minLength":1,"maxLength":16,"pattern":"^[A-Za-z0-9]+$","description":"Must match an SMS-approved sender for the tenant **or** the platform default outbound sender (`SMS_HOLLATAGS_FROM`). Hollatags rule: alphanumeric <=11 chars; numeric <=16 chars."},"sendType":{"type":"integer","enum":[0,1,2],"description":"Provider send type: 0=Normal SMS, 1=Flash SMS, 2=Unicode SMS."},"body":{"type":"string","maxLength":1600,"description":"Message text. Maximum length depends on platform SMS configuration (commonly up to 1600 characters; lower when the outbound adapter enforces a smaller cap)."},"idempotencyKey":{"type":"string"}}},"SendSmsAccepted":{"type":"object","properties":{"id":{"type":"string"},"senderId":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"duplicate":{"type":"boolean"},"segments":{"type":"integer"},"encoding":{"type":"string","enum":["GSM7","UCS2"]}}},"SendSmsOtpRequest":{"type":"object","required":["to","brandName","otpCode"],"properties":{"to":{"type":"string","description":"E.164 recipient, e.g. +15551234567"},"purpose":{"type":"string","enum":["LOGIN","PHONE_VERIFICATION","PASSWORD_RESET"],"default":"PHONE_VERIFICATION"},"brandName":{"type":"string","minLength":3,"maxLength":20,"description":"OTP brand name; must match an OTP-approved sender for the tenant **or** a platform-configured OTP brand (e.g. `SMS_OTP_DEFAULT_BRAND_NAME` / `SMS_HOLLATAGS_FROM` / `SMS_OTP_BRAND_OPTIONS`)."},"otpCode":{"type":"string","pattern":"^\\d{4,8}$","description":"Numeric OTP digits (4–8)."}}},"SendSmsOtpAccepted":{"type":"object","properties":{"id":{"type":"string","description":"SMS external id for delivery tracking"},"status":{"type":"string","example":"QUEUED"},"cooldownSeconds":{"type":"integer"}}},"SendWhatsappRequest":{"type":"object","required":["to","body"],"properties":{"to":{"type":"string","description":"WhatsApp international number: digits with country code (optional + prefix stripped by API)."},"body":{"type":"string","maxLength":4096},"idempotencyKey":{"type":"string"}}},"SendWhatsappAccepted":{"type":"object","properties":{"id":{"type":"string","description":"External message id"},"status":{"type":"string"},"duplicate":{"type":"boolean"},"providerMessageId":{"type":"string","nullable":true}}},"PublicApiError":{"type":"object","description":"Standard error envelope from the public API","properties":{"error":{"type":"string"},"code":{"type":"string"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. validation)"}}}}},"security":[{"ApiKeyBearer":[]}],"paths":{"/v1/public/emails/send":{"post":{"tags":["Public Email API"],"summary":"Queue email for delivery","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailRequest"}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailAccepted"}}}}}}},"/v1/public/emails/preview":{"post":{"tags":["Public Email API"],"summary":"Preview rendered email content without delivery","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewEmailRequest"}}}},"responses":{"200":{"description":"Rendered preview response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewEmailResponse"}}}}}}},"/v1/public/emails/{externalId}":{"get":{"tags":["Public Email API"],"summary":"Get email status","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Email status payload"}}}},"/v1/public/emails/{externalId}/timeline":{"get":{"tags":["Public Email API"],"summary":"Get email event timeline","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Timeline events"}}}},"/v1/public/emails/stats":{"get":{"tags":["Public Email API"],"summary":"Get delivery stats for a window","parameters":[{"name":"start","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"end","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Status breakdown and total"}}}},"/v1/public/sms/send":{"post":{"tags":["Public SMS API"],"summary":"Queue SMS for delivery","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendSmsRequest"}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendSmsAccepted"}}}},"400":{"description":"Validation failed (code VALIDATION_ERROR) or bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}},"401":{"description":"Missing/invalid API key (e.g. API_KEY_MISSING, API_KEY_INVALID)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}},"403":{"description":"Forbidden: SMS_CHANNEL_DISABLED, SMS_RECIPIENT_OPTED_OUT, SMS_SENDER_ID_NOT_APPROVED (sender not approved for org and not the platform default), or insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}},"429":{"description":"SMS_QUOTA_EXCEEDED or SMS_RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}}}}},"/v1/public/sms/otp/send":{"post":{"tags":["Public SMS API"],"summary":"Queue OTP SMS for provider delivery","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendSmsOtpRequest"}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendSmsOtpAccepted"}}}},"400":{"description":"Validation failed or brand/code request invalid (e.g. SMS_OTP_CODE_INVALID, SMS_OTP_BRAND_NOT_ALLOWED, SMS_OTP_INVALID_CHALLENGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}},"403":{"description":"SMS_CHANNEL_DISABLED, SMS_RECIPIENT_OPTED_OUT, SMS_OTP_BRAND_NOT_ALLOWED, ORG_NOT_ACTIVE, or missing scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}},"429":{"description":"SMS_OTP_RATE_LIMITED, SMS_QUOTA_EXCEEDED, or SMS_RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}}}}},"/v1/public/sms/{externalId}":{"get":{"tags":["Public SMS API"],"summary":"Get SMS status","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"SMS status payload"}}}},"/v1/public/sms/{externalId}/timeline":{"get":{"tags":["Public SMS API"],"summary":"Get SMS event timeline","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Timeline events"}}}},"/v1/public/sms/{externalId}/retry":{"post":{"tags":["Public SMS API"],"summary":"Retry a failed SMS","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Retry accepted"},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}},"403":{"description":"SMS_CHANNEL_DISABLED, SMS_RECIPIENT_OPTED_OUT, or message not eligible to retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}},"429":{"description":"SMS_QUOTA_EXCEEDED or SMS_RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiError"}}}}}}},"/v1/public/whatsapp/send":{"post":{"tags":["Public WhatsApp API"],"summary":"Send WhatsApp Cloud API text message (synchronous Graph handoff)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendWhatsappRequest"}}}},"responses":{"200":{"description":"Duplicate idempotency key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendWhatsappAccepted"}}}},"201":{"description":"Accepted and handed to Meta Graph","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendWhatsappAccepted"}}}},"403":{"description":"WHATSAPP_CLOUD_DISABLED, WHATSAPP_ORG_NOT_CONFIGURED, WHATSAPP_RECIPIENT_OPTED_OUT, ORG_NOT_ACTIVE"},"429":{"description":"WHATSAPP_QUOTA_EXCEEDED or WHATSAPP_RATE_LIMITED"}}}},"/v1/public/whatsapp/{externalId}/timeline":{"get":{"tags":["Public WhatsApp API"],"summary":"Get synthetic WhatsApp timeline from stored timestamps","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Timeline events"}}}},"/v1/public/whatsapp/{externalId}/retry":{"post":{"tags":["Public WhatsApp API"],"summary":"Retry a failed WhatsApp message (synchronous Graph call)","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Retry succeeded; message sent to Graph"},"400":{"description":"WHATSAPP_NOT_RETRYABLE"},"403":{"description":"WHATSAPP_RECIPIENT_OPTED_OUT, WHATSAPP_CLOUD_DISABLED, etc."},"429":{"description":"WHATSAPP_QUOTA_EXCEEDED or WHATSAPP_RATE_LIMITED"}}}},"/v1/public/whatsapp/{externalId}":{"get":{"tags":["Public WhatsApp API"],"summary":"Get WhatsApp message status","parameters":[{"name":"externalId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"WhatsApp message payload"}}}}}}