{"openapi":"3.0.0","paths":{"/v1/auth/phone/start":{"post":{"operationId":"auth.phone.start","summary":"Send a code to add / change the sign-in phone number","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/phone/confirm":{"post":{"operationId":"auth.phone.confirm","summary":"Confirm the code; the number can then be used to sign in","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/phone/remove":{"post":{"operationId":"auth.phone.remove","summary":"Stop signing in with the phone number","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/forgot-password/phone":{"post":{"operationId":"auth.forgotPassword.phone","summary":"Forgot password: send a reset code to a verified phone number","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/reset-password/phone":{"post":{"operationId":"auth.resetPassword.phone","summary":"Reset the password with the SMS code","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/register":{"post":{"operationId":"auth.register","summary":"Create a new account","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDto"}}}},"responses":{"201":{"description":"Account created"}},"tags":["Authentication"]}},"/v1/auth/signup/phone/start":{"post":{"operationId":"auth.signup.phoneStart","summary":"Sign-up: text a code to the phone number (refused if the number is already on an account)","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/login":{"post":{"operationId":"auth.login","summary":"Login and receive JWT + refresh token","description":"Returns a short-lived access token (1h) and a long-lived refresh token (30 days). Store the refresh token securely and use POST /v1/auth/refresh to rotate it.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginDto"}}}},"responses":{"200":{"description":"JWT access token + refresh token"}},"tags":["Authentication"]}},"/v1/auth/mfa/login":{"post":{"operationId":"auth.mfa.login","summary":"Finish signing in with a two-factor code","description":"When login (or Google sign-in) answers mfaRequired, send the mfaTicket with the 6-digit code from the authenticator app to receive tokens.","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/check-provider":{"post":{"operationId":"auth.checkProvider","summary":"Check which auth provider an email uses","parameters":[],"responses":{"201":{"description":""}},"tags":["Authentication"]}},"/v1/auth/refresh":{"post":{"operationId":"auth.refresh","summary":"Rotate refresh token","description":"Exchange a valid refresh token for a new access + refresh token pair. The old refresh token is invalidated immediately (rotation).","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"type":"string"}}}}}},"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/logout":{"post":{"operationId":"auth.logout","summary":"Revoke refresh token","description":"Invalidates the given refresh token. The access token remains valid until its 1h expiry — clients should discard it locally.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"type":"string"}}}}}},"responses":{"200":{"description":""}},"tags":["Authentication"],"security":[{"BearerJWT":[]}]}},"/v1/auth/me":{"get":{"operationId":"auth.me","summary":"Get current account profile","description":"Returns the authenticated account details, current wallet balance, and active API key count.","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"],"security":[{"BearerJWT":[]}]},"patch":{"operationId":"auth.updateProfile","summary":"Update profile","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"],"security":[{"BearerJWT":[]}]}},"/v1/auth/forgot-password":{"post":{"operationId":"auth.forgotPassword","summary":"Request a password reset link","description":"Sends a password reset token to the registered email address. Always returns 200 to prevent email enumeration. In development mode the token is returned directly in the response body (_devToken).","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/reset-password":{"post":{"operationId":"auth.resetPassword","summary":"Reset password using a reset token","description":"Validates the reset token (valid 15 minutes) and updates the account password. All existing refresh tokens are revoked on success.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token","newPassword"],"properties":{"token":{"type":"string","description":"Reset token from the password-reset email"},"newPassword":{"type":"string","minLength":8}}}}}},"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/mfa/enroll":{"post":{"operationId":"auth.mfa.enroll","summary":"Enroll TOTP MFA — returns QR code URI","parameters":[],"responses":{"201":{"description":""}},"tags":["Authentication"]}},"/v1/auth/mfa/verify":{"post":{"operationId":"auth.mfa.verify","summary":"Verify TOTP code and activate MFA","parameters":[],"responses":{"201":{"description":""}},"tags":["Authentication"]}},"/v1/auth/mfa/disable":{"post":{"operationId":"auth.mfa.disable","summary":"Disable MFA (requires current TOTP)","parameters":[],"responses":{"201":{"description":""}},"tags":["Authentication"]}},"/v1/auth/sessions":{"get":{"operationId":"auth.sessions.list","summary":"List active sessions","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/change-password":{"post":{"operationId":"auth.changePassword","summary":"Change password","parameters":[],"responses":{"201":{"description":""}},"tags":["Authentication"]}},"/v1/auth/set-password":{"post":{"operationId":"auth.setPassword","summary":"Set password for first time (Google OAuth accounts)","parameters":[],"responses":{"201":{"description":""}},"tags":["Authentication"],"security":[{"bearer":[]}]}},"/v1/auth/me/avatar":{"patch":{"operationId":"auth.updateAvatar","summary":"Update account avatar","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"],"security":[{"bearer":[]}]}},"/v1/auth/me/location":{"patch":{"operationId":"auth.me.updateLocation","summary":"Save region and district for the authenticated account","description":"Called after Google OAuth signup to capture the tenant location that was skipped during the OAuth flow. Stores with confidence 0.95 (self-reported) matching the email/password signup behaviour.","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"],"security":[{"bearer":[]}]}},"/v1/auth/me/account-type":{"post":{"operationId":"auth.me.chooseAccountType","summary":"Choose account type (business / individual / nonprofit) — once per account","parameters":[],"responses":{"201":{"description":""}},"tags":["Authentication"],"security":[{"BearerJWT":[]}]}},"/v1/auth/send-verification":{"post":{"operationId":"auth.sendVerification","summary":"Send email verification link to authenticated user","parameters":[],"responses":{"201":{"description":""}},"tags":["Authentication"],"security":[{"BearerJWT":[]}]}},"/v1/auth/resend-verification":{"post":{"operationId":"auth.resendVerification","summary":"Resend verification email (public, rate-limited)","description":"Public endpoint for users who did not receive or lost their original verification email. Always returns 200 to prevent account enumeration — response does not reveal whether the email is registered.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","example":"user@example.com"}},"required":["email"]}}}},"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/verify-email":{"get":{"operationId":"auth.verifyEmail","summary":"Verify email address with token from link","description":"Call with ?token=... from the verification link. Marks account as verified.","parameters":[{"name":"token","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/google":{"get":{"operationId":"auth.google.redirect","summary":"Redirect to Google OAuth consent screen","parameters":[{"name":"redirect","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/auth/google/callback":{"get":{"operationId":"auth.google.callback","summary":"Google OAuth callback — exchange code for HOVA tokens","parameters":[],"responses":{"200":{"description":""}},"tags":["Authentication"]}},"/v1/admin/phone-flags":{"get":{"operationId":"admin.phoneFlags.list","summary":"Duplicate phone attempts (open first)","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]}},"/v1/admin/phone-flags/{id}/review":{"post":{"operationId":"admin.phoneFlags.review","summary":"Mark a duplicate-phone flag reviewed","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"]}},"/v1/accounts/me":{"get":{"operationId":"accounts.me","summary":"Get own account from JWT","parameters":[],"responses":{"200":{"description":""}},"tags":["Accounts"],"security":[{"BearerJWT":[]}]}},"/v1/accounts/{accountId}":{"get":{"operationId":"accounts.get","summary":"Get account details (own account only)","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Accounts"],"security":[{"BearerJWT":[]}]}},"/v1/accounts/{accountId}/billing":{"patch":{"operationId":"accounts.updateBilling","summary":"Update billing details","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Accounts"],"security":[{"BearerJWT":[]}]}},"/v1/accounts/{accountId}/api-keys":{"post":{"operationId":"apiKeys.create","summary":"Create API key","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyDto"}}}},"responses":{"201":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]},"get":{"operationId":"apiKeys.list","summary":"List API keys","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/accounts/{accountId}/api-keys/{keyId}":{"delete":{"operationId":"apiKeys.revoke","summary":"Revoke API key","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}},{"name":"keyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/accounts/{accountId}/api-keys/{keyId}/rotate":{"post":{"operationId":"apiKeys.rotate","summary":"Rotate (regenerate) API key","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}},{"name":"keyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/me/api-keys":{"get":{"operationId":"apiKeys.listMe","summary":"List my API keys","parameters":[],"responses":{"200":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"apiKeys.createMe","summary":"Create API key","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyDto"}}}},"responses":{"201":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/me/api-keys/{keyId}":{"delete":{"operationId":"apiKeys.revokeMe","summary":"Revoke API key","parameters":[{"name":"keyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/me/api-keys/{keyId}/rotate":{"post":{"operationId":"apiKeys.rotateMe","summary":"Rotate (regenerate) API key","parameters":[{"name":"keyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/api-keys":{"get":{"operationId":"apiKeys.listAlias","summary":"List my API keys (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"apiKeys.createAlias","summary":"Create API key (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyDto"}}}},"responses":{"201":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/api-keys/{keyId}":{"delete":{"operationId":"apiKeys.revokeAlias","summary":"Revoke API key (alias)","parameters":[{"name":"keyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/api-keys/{keyId}/rotate":{"post":{"operationId":"apiKeys.rotateAlias","summary":"Rotate (regenerate) API key (alias)","parameters":[{"name":"keyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["API Keys"],"security":[{"BearerJWT":[]}]}},"/v1/accounts/{accountId}/sub-accounts":{"post":{"operationId":"subAccounts.create","summary":"Create sub-account","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sub-Accounts"],"security":[{"BearerJWT":[]}]},"get":{"operationId":"subAccounts.list","summary":"List sub-accounts","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Sub-Accounts"],"security":[{"BearerJWT":[]}]}},"/v1/accounts/{accountId}/sub-accounts/{subAccountId}/billing":{"patch":{"operationId":"subAccounts.configureBilling","summary":"Configure sub-account billing mode","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}},{"name":"subAccountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Sub-Accounts"],"security":[{"BearerJWT":[]}]}},"/v1/wallets":{"get":{"operationId":"wallet.getMaster","summary":"Get master wallet balance","parameters":[],"responses":{"200":{"description":""}},"tags":["Wallets"]}},"/v1/wallets/{walletId}":{"get":{"operationId":"wallet.get","summary":"Get wallet by ID","parameters":[{"name":"walletId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Wallets"]}},"/v1/wallets/{walletId}/transactions":{"get":{"operationId":"wallet.transactions","summary":"List ledger transactions","parameters":[{"name":"walletId","required":true,"in":"path","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}},{"name":"category","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Wallets"]}},"/v1/account/wallet":{"get":{"operationId":"wallet.getForAccount","summary":"Get wallet for logged-in account","parameters":[],"responses":{"200":{"description":""}},"tags":["Wallets"]}},"/v1/account/wallet/auto-recharge/config":{"get":{"operationId":"wallet.autoRecharge.get","summary":"Get auto-recharge config","parameters":[],"responses":{"200":{"description":""}},"tags":["Wallets"]},"post":{"operationId":"wallet.autoRecharge.save","summary":"Save auto-recharge config","parameters":[],"responses":{"201":{"description":""}},"tags":["Wallets"]}},"/v1/webhooks/paystack":{"post":{"operationId":"webhooks.paystack","summary":"Paystack event receiver","parameters":[{"name":"x-paystack-signature","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/wallets/{walletId}/topup/initiate":{"post":{"operationId":"topup.initiate","summary":"Initiate top-up (card via Paystack, MoMo via MTN direct)","parameters":[{"name":"walletId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Top-Up"],"security":[{"BearerJWT":[]}]}},"/v1/wallets/{walletId}/topup/{topupId}/momo-status":{"get":{"operationId":"topup.momoStatus","summary":"Poll MoMo payment status","parameters":[{"name":"topupId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Top-Up"],"security":[{"BearerJWT":[]}]}},"/v1/wallets/{walletId}/topup/{topupId}":{"get":{"operationId":"topup.get","summary":"Get top-up status","parameters":[{"name":"walletId","required":true,"in":"path","schema":{"type":"string"}},{"name":"topupId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Top-Up"],"security":[{"BearerJWT":[]}]}},"/v1/wallets/{walletId}/topup":{"get":{"operationId":"topup.list","summary":"List top-ups","parameters":[{"name":"walletId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Top-Up"],"security":[{"BearerJWT":[]}]}},"/v1/billing/invoices":{"get":{"operationId":"billing.invoices.list","summary":"List invoices","parameters":[],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/invoices/{id}":{"get":{"operationId":"billing.invoices.get","summary":"Get invoice detail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/invoices/{id}/pdf":{"get":{"operationId":"billing.invoices.pdf","summary":"Download invoice PDF","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/invoices/generate":{"post":{"operationId":"billing.invoices.generate","summary":"Generate invoice for current account","parameters":[],"responses":{"201":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/billing/generate":{"post":{"operationId":"admin.billing.generate","summary":"Generate invoice (admin)","parameters":[],"responses":{"201":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/billing/{id}/void":{"post":{"operationId":"admin.billing.void","summary":"Void invoice (admin)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/usage":{"get":{"operationId":"billing.usage.summary","summary":"Usage summary for billing period","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/usage/plan":{"get":{"operationId":"billing.plan.get","summary":"Get current plan with limits","parameters":[],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]},"put":{"operationId":"billing.plan.update","summary":"Upgrade or downgrade billing plan","parameters":[],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/usage/plans":{"get":{"operationId":"billing.plans.list","summary":"List all available plans (for upgrade/downgrade page)","description":"Returns every row from plan_definitions plus the caller's current plan key. Used by the in-dashboard /dashboard/billing/plans page.","parameters":[],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/usage/auto-recharge":{"get":{"operationId":"billing.autoRecharge.get","summary":"Get auto-recharge settings","parameters":[],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]},"put":{"operationId":"billing.autoRecharge.set","summary":"Set auto-recharge threshold and amount","parameters":[],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/usage/estimate":{"post":{"operationId":"billing.estimate","summary":"Estimate campaign cost before launch","parameters":[],"responses":{"201":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/usage/payment-methods":{"get":{"operationId":"billing.paymentMethods.list","summary":"List saved payment methods","parameters":[],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"billing.paymentMethods.add","summary":"Add a payment method","parameters":[],"responses":{"201":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/billing/usage/payment-methods/{id}":{"delete":{"operationId":"billing.paymentMethods.remove","summary":"Remove a payment method","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/accounts/{id}/plan":{"post":{"operationId":"admin.accounts.assignPlan","summary":"Assign a plan to an account","description":"charge=false (default): contract billed outside the wallet — no fee, no renewal, the customer cannot change it. charge=true: the monthly price is taken from the wallet now and renews monthly.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"]}},"/v1/admin/accounts/{id}/plan/release":{"post":{"operationId":"admin.accounts.releasePlan","summary":"Let the customer change their plan again","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"]}},"/v1/public/plans":{"get":{"operationId":"public.plans","summary":"Plans shown on the website, with prices, rates and limits","parameters":[],"responses":{"200":{"description":""}},"tags":["Public"]}},"/v1/billing/export/ledger":{"get":{"operationId":"billing.export.ledger","summary":"Export ledger (CSV or XLSX)","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}},{"name":"format","required":false,"in":"query","schema":{"enum":["csv","xlsx"],"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Billing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/webhooks":{"get":{"operationId":"webhookEvents.list","summary":"List webhook events","parameters":[{"name":"source","required":true,"in":"query","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Webhook Events"],"security":[{"BearerJWT":[]}]}},"/v1/admin/webhooks/{eventId}/replay":{"post":{"operationId":"webhookEvents.replay","summary":"Replay failed webhook event","parameters":[{"name":"eventId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Webhook Events"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing/sms-floor":{"get":{"operationId":"admin.pricing.smsFloor","summary":"SMSOnlineGH cost, minimum margin and lowest allowed SMS price","parameters":[],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing":{"get":{"operationId":"admin.pricing.list","summary":"List all pricing tiers","parameters":[{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"admin.pricing.create","summary":"Create pricing tier","parameters":[],"responses":{"201":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]},"put":{"operationId":"admin.pricing.supersede","summary":"Supersede (change) a pricing tier for a prefix","description":"Retires the current active tier for the (account scope, destination prefix) and inserts the replacement in one transaction. Use this to CHANGE a rate cleanly instead of stacking duplicate active tiers.","parameters":[],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing/voice":{"put":{"operationId":"admin.pricing.voice","summary":"Change the global voice rate","parameters":[],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing/whatsapp":{"put":{"operationId":"admin.pricing.whatsapp","summary":"Change a global WhatsApp rate (plan + category)","parameters":[],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing/ussd":{"put":{"operationId":"admin.pricing.ussd","summary":"Change the global USSD rate","parameters":[],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing/email":{"put":{"operationId":"admin.pricing.email","summary":"Change the global email rate","parameters":[],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing/plans":{"get":{"operationId":"admin.pricing.plans.list","summary":"List plan definitions (packages)","parameters":[],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"admin.pricing.plans.create","summary":"Create a plan (copied from basedOn; starts hidden and not self-serve)","parameters":[],"responses":{"201":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing/plans/{plan}/rate-limit":{"put":{"operationId":"admin.pricing.plans.rateLimit","summary":"Change a plan's API rate limit","parameters":[{"name":"plan","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/admin/pricing/plans/{plan}":{"put":{"operationId":"admin.pricing.plans.update","summary":"Update a plan definition (price, credit, rates, limits)","parameters":[{"name":"plan","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/pricing/rates":{"get":{"operationId":"pricing.rates","summary":"Current customer-facing rate card","parameters":[],"responses":{"200":{"description":""}},"tags":["Pricing"],"security":[{"BearerJWT":[]}]}},"/v1/sender-ids/loa/generate":{"post":{"operationId":"senderIds.generateLoa","summary":"Generate a carrier Letter of Authorisation for download","parameters":[],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/sender-ids/check":{"get":{"operationId":"senderIds.check","summary":"Check sender ID availability","parameters":[{"name":"name","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/sender-ids/check-id-document":{"post":{"operationId":"senderIds.checkIdDocument","summary":"Check if a Ghana Card is available for this account","parameters":[],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/sender-ids":{"post":{"operationId":"senderIds.create","summary":"Register a sender ID","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSenderIdDto"}}}},"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]},"get":{"operationId":"senderIds.list","summary":"List own sender IDs","parameters":[],"responses":{"200":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/sender-ids/default":{"post":{"operationId":"senderIds.setDefault","summary":"Set the sender used when a send has no \"from\" (null clears)","parameters":[],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/sender-ids/{id}/use-case":{"patch":{"operationId":"senderIds.updateUseCase","summary":"Change the message type of an approved sender ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/sender-ids/{id}/id-document":{"post":{"operationId":"senderIds.attachIdDocument","summary":"Attach ID document photos to a pending sender ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/sender-ids/{id}/business-document":{"post":{"operationId":"senderIds.attachBusinessDocument","summary":"Attach a business document (LoA / registration) to a pending sender ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/pending":{"get":{"operationId":"admin.senderIds.listPending","summary":"List pending sender IDs (conflict-flagged)","parameters":[{"name":"conflict","required":true,"in":"query","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/{id}/approve":{"post":{"operationId":"admin.senderIds.approve","summary":"Approve sender ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/{id}/request-documents":{"post":{"operationId":"admin.senderIds.requestDocuments","summary":"Request documents from the customer for a sender ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/{id}/reject":{"post":{"operationId":"admin.senderIds.reject","summary":"Reject sender ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/{id}/retry-carrier-submit":{"post":{"operationId":"admin.senderIds.retrySubmission","summary":"Carrier registration instructions (manual via SMSOnlineGH)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/{id}/carrier-status":{"post":{"operationId":"admin.senderIds.setCarrierStatus","summary":"Manually set a carrier approval status","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/protected":{"get":{"operationId":"admin.senderIds.protected.list","summary":"List protected sender names","parameters":[],"responses":{"200":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"admin.senderIds.protected.add","summary":"Add protected name","parameters":[],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/protected/{id}/delete":{"post":{"operationId":"admin.senderIds.protected.remove","summary":"Remove protected name","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids":{"get":{"operationId":"admin.senderIds.listAll","summary":"List ALL sender IDs across all accounts (admin)","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sender-ids/{id}/id-document/{side}":{"get":{"operationId":"admin.senderIds.getIdDocument","summary":"Stream a submitted sender-ID document for admin review/download","description":"Returns a document attached to a sender ID for admin download (to forward to SMSOnlineGH for carrier approval). Authenticated admin only. Audit-logged. The :side parameter selects the slot: \"front\" or \"back\" (Ghana Card sides), \"loa\" (Letter of Authorisation), or \"registration\" (business registration / trademark certificate).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"side","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Sender IDs"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/agent":{"get":{"operationId":"hovaAi.agent.get","summary":"Get this account agent","parameters":[],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]},"put":{"operationId":"hovaAi.agent.update","summary":"Update agent config","parameters":[],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/conversations":{"get":{"operationId":"hovaAi.conversations.list","summary":"List agent conversations","parameters":[{"name":"channel","required":true,"in":"query","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/conversations/{id}":{"get":{"operationId":"hovaAi.conversations.get","summary":"Get one conversation with messages","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/conversations/{id}/take-over":{"post":{"operationId":"hovaAi.conversations.takeOver","summary":"Human claims this conversation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/tools":{"get":{"operationId":"hovaAi.tools.list","summary":"List configured tools","parameters":[],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"hovaAi.tools.create","summary":"Add a tool (platform or custom)","parameters":[],"responses":{"201":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/tools/platform-catalog":{"get":{"operationId":"hovaAi.tools.catalog","summary":"List available platform tools","parameters":[],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/tools/{id}":{"delete":{"operationId":"hovaAi.tools.remove","summary":"Remove a tool","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/tools/{id}/test":{"post":{"operationId":"hovaAi.tools.test","summary":"Sandbox-invoke a custom tool","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/queue":{"get":{"operationId":"hovaAi.queue.list","summary":"List pending escalations","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/queue/{id}/claim":{"post":{"operationId":"hovaAi.queue.claim","summary":"Claim a queue item","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/queue/{id}/resolve":{"post":{"operationId":"hovaAi.queue.resolve","summary":"Resolve a queue item","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/usage":{"get":{"operationId":"hovaAi.usage","summary":"Agent usage stats (cost, runs)","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/hova-ai/budget":{"get":{"operationId":"hovaAi.budget","summary":"Today's spend vs daily cap","description":"Returns this account's daily AI spend status (UTC day). Includes the cap, spend so far, remaining USD, and where the cap came from (per-agent override, env default, or unlimited). Used by the dashboard budget widget.","parameters":[],"responses":{"200":{"description":""}},"tags":["HOVA AI"],"security":[{"BearerJWT":[]}]}},"/v1/messages/sms/try":{"post":{"operationId":"sms.try","summary":"Try a send in test mode from the dashboard (free, nothing sent)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendSmsDto"}}}},"responses":{"201":{"description":""}},"tags":["SMS"]}},"/v1/messages/sms":{"post":{"operationId":"sms.send","summary":"Send SMS","description":"Send a single SMS to one recipient. Use POST /v1/messages/sms/bulk for multi-recipient sends.","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Client-supplied idempotency key. If the same key is sent again, the original response is returned and no second charge is made. Recommended for retries.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendSmsDto"}}}},"responses":{"201":{"description":""}},"tags":["SMS"]},"get":{"operationId":"sms.list","summary":"List SMS messages","parameters":[],"responses":{"200":{"description":""}},"tags":["SMS"]}},"/v1/messages/sms/bulk":{"post":{"operationId":"sms.bulk","summary":"Send SMS to multiple recipients","description":"Send the same (or personalised) SMS body to up to 500 recipients in one call. Each recipient is charged independently. Partial failures are returned in the errors array.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendBulkSmsDto"}}}},"responses":{"201":{"description":""}},"tags":["SMS"]}},"/v1/messages/sms/templates":{"get":{"operationId":"sms.templates.list","summary":"List SMS templates","parameters":[],"responses":{"200":{"description":""}},"tags":["SMS"]},"post":{"operationId":"sms.templates.create","summary":"Create SMS template","parameters":[],"responses":{"201":{"description":""}},"tags":["SMS"]}},"/v1/messages/sms/templates/{id}":{"put":{"operationId":"sms.templates.update","summary":"Update SMS template","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SMS"]},"delete":{"operationId":"sms.templates.delete","summary":"Delete SMS template","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SMS"]}},"/v1/messages/sms/{messageId}":{"get":{"operationId":"sms.get","summary":"Get a single SMS message","description":"Fetch full detail for one SMS message including delivery report and timestamps.","parameters":[{"name":"messageId","required":true,"in":"path","description":"UUID of the SMS message","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SMS"]}},"/v1/messages/whatsapp":{"post":{"operationId":"whatsapp.send","summary":"Send a WhatsApp message","description":"Send text, template, image, document, audio, or video messages via WhatsApp Business. Template messages must be pre-approved by Meta before sending. Free-form text messages can only be sent within 24 hours of the last inbound message from the contact.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendWhatsAppDto"}}}},"responses":{"201":{"description":""}},"tags":["WhatsApp"]},"get":{"operationId":"whatsapp.messages.list","summary":"List WhatsApp messages","parameters":[{"name":"direction","required":true,"in":"query","schema":{"type":"string"}},{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]}},"/v1/messages/whatsapp/conversations":{"get":{"operationId":"whatsapp.conversations.list","summary":"List WhatsApp conversations","parameters":[{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]}},"/v1/messages/whatsapp/conversations/{phone}":{"get":{"operationId":"whatsapp.conversations.get","summary":"Get conversation thread with a contact","parameters":[{"name":"phone","required":true,"in":"path","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]}},"/v1/messages/whatsapp/{messageId}":{"get":{"operationId":"whatsapp.messages.get","summary":"Get a single WhatsApp message","parameters":[{"name":"messageId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]}},"/v1/whatsapp/templates":{"get":{"operationId":"whatsapp.templates.list","summary":"List message templates","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"whatsapp.templates.create","summary":"Submit a new message template for Meta approval","description":"Templates must be approved by Meta (24–48 hours) before they can be used in send calls. Use {{1}}, {{2}}, … as variable placeholders. Provide the actual values in templateVariables when sending.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateDto"}}}},"responses":{"201":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]}},"/v1/whatsapp/opt-ins/{phone}":{"post":{"operationId":"whatsapp.optins.register","summary":"Register opt-in for a contact","parameters":[{"name":"phone","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["WhatsApp"]}},"/v1/whatsapp/opt-outs/{phone}":{"post":{"operationId":"whatsapp.optouts.register","summary":"Register opt-out for a contact","parameters":[{"name":"phone","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["WhatsApp"]}},"/v1/whatsapp/opt-ins":{"get":{"operationId":"whatsapp.optins.list","summary":"List opt-in status","parameters":[{"name":"optedIn","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]}},"/v1/whatsapp/bindings":{"post":{"operationId":"whatsapp.bindings.register","summary":"Register a WABA phone-number binding","description":"Maps a Meta phone_number_id to this account so inbound webhooks are routed correctly. Required before sending or receiving WhatsApp messages.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterWaBindingDto"}}}},"responses":{"201":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]},"get":{"operationId":"whatsapp.bindings.list","summary":"List WABA bindings for this account","parameters":[],"responses":{"200":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]}},"/v1/whatsapp/bindings/{id}":{"delete":{"operationId":"whatsapp.bindings.disable","summary":"Disable a WABA binding","description":"Marks a binding as disabled. Outbound sends from this account will be rejected (BadRequest with \"WhatsApp not configured\") until another binding is registered. We do NOT hard-delete the row so the audit trail is preserved (we know this account once owned this phone_number_id).","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]}},"/v1/whatsapp/connect":{"post":{"operationId":"whatsapp.connect.token","summary":"Connect WhatsApp with your own Meta token","description":"Checks the token against Meta, subscribes Hova to the WABA's webhooks, binds the number and stores the token encrypted. The token is never shown again.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectWaTokenDto"}}}},"responses":{"201":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]}},"/v1/whatsapp/embedded-signup":{"get":{"operationId":"whatsapp.embeddedSignup.config","summary":"Embedded Signup settings for the dashboard","parameters":[],"responses":{"200":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"whatsapp.embeddedSignup.complete","summary":"Finish Meta Embedded Signup","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddedSignupDto"}}}},"responses":{"201":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]}},"/v1/whatsapp/bindings/{id}/token":{"delete":{"operationId":"whatsapp.bindings.removeToken","summary":"Stop using your own token (fall back to Hova's)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]}},"/v1/whatsapp/webhook/c/{key}":{"get":{"operationId":"whatsapp.webhook.customerVerify","summary":"Meta verification for a business's own webhook URL","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}},{"name":"hub.mode","required":true,"in":"query","schema":{"type":"string"}},{"name":"hub.verify_token","required":true,"in":"query","schema":{"type":"string"}},{"name":"hub.challenge","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]},"post":{"operationId":"whatsapp.webhook.customerReceive","summary":"Events from a business's own Meta app","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-hub-signature-256","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]}},"/v1/whatsapp/webhook":{"get":{"operationId":"whatsapp.webhook.verify","summary":"Meta webhook verification challenge","parameters":[{"name":"hub.mode","required":true,"in":"query","schema":{"type":"string"}},{"name":"hub.verify_token","required":true,"in":"query","schema":{"type":"string"}},{"name":"hub.challenge","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]},"post":{"operationId":"whatsapp.webhook.receive","summary":"Receive Meta webhook events (status updates + inbound messages)","description":"Single webhook URL for all tenants. The owning account is derived from the inbound payload's phone_number_id (or waba_business_id for template-status events) via wa_account_bindings; do NOT pass account information in the URL.","parameters":[{"name":"x-hub-signature-256","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]}},"/v1/messages/templates":{"get":{"operationId":"whatsapp.alias.templates.list","summary":"List WhatsApp templates (alias)","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"whatsapp.alias.templates.create","summary":"Create WhatsApp template (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateDto"}}}},"responses":{"201":{"description":""}},"tags":["WhatsApp"],"security":[{"BearerJWT":[]}]}},"/v1/messages/opt-ins/{phone}":{"post":{"operationId":"whatsapp.alias.optins.register","summary":"Register opt-in (alias)","parameters":[{"name":"phone","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["WhatsApp"]}},"/v1/messages/opt-outs/{phone}":{"post":{"operationId":"whatsapp.alias.optouts.register","summary":"Register opt-out (alias)","parameters":[{"name":"phone","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["WhatsApp"]}},"/v1/messages/opt-ins":{"get":{"operationId":"whatsapp.alias.optins.list","summary":"List opt-in status (alias)","parameters":[{"name":"optedIn","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["WhatsApp"]}},"/v1/messages/inbound":{"get":{"operationId":"inbound.list","summary":"List inbound messages (polling fallback)","parameters":[],"responses":{"200":{"description":""}},"tags":["Inbound Messages"]}},"/v1/rcs/messages":{"post":{"operationId":"rcs.send","summary":"Send an RCS message","description":"Send a Rich Communication Services (RCS) message to a Ghana mobile number. Supports text, rich_card, carousel, and suggested_replies message types. Automatically falls back to SMS when RCS is unavailable on the destination unless smsFallback=false. Pass an `Idempotency-Key` header to make retries safe — the same key returns the original message rather than re-sending.","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendRcsDto"}}}},"responses":{"201":{"description":""}},"tags":["RCS"]},"get":{"operationId":"rcs.list","summary":"List RCS messages","description":"Returns a paginated list of RCS messages sent by this account, newest first.","parameters":[{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["RCS"]}},"/v1/rcs/messages/{messageId}":{"get":{"operationId":"rcs.get","summary":"Get RCS message status","description":"Returns the delivery status and metadata for a specific RCS message.","parameters":[{"name":"messageId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["RCS"]}},"/v1/rcs/capability":{"post":{"operationId":"rcs.capability","summary":"Check if a number is RCS capable","description":"Queries the network to determine whether a Ghana mobile number can receive RCS messages. Returns rcsCapable: true/false and the detected carrier.","parameters":[],"responses":{"201":{"description":""}},"tags":["RCS"]}},"/v1/rcs/campaigns":{"post":{"operationId":"rcs.campaign.launch","summary":"Launch an RCS campaign","description":"Sends an RCS message to a contact group. Non-RCS numbers automatically receive an SMS fallback unless smsFallback=false.","parameters":[],"responses":{"201":{"description":""}},"tags":["RCS"]}},"/v1/rcs/analytics":{"get":{"operationId":"rcs.analytics","summary":"RCS analytics — CTR, read rate, fallback breakdown","parameters":[],"responses":{"200":{"description":""}},"tags":["RCS"]}},"/v1/webhooks/infobip/rcs/delivery":{"post":{"operationId":"webhooks.infobip.rcs.delivery","summary":"Infobip RCS delivery / read report","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/numbers/search":{"get":{"operationId":"numbers.search","summary":"Search available numbers","parameters":[{"name":"prefix","required":true,"in":"query","schema":{"type":"string"}},{"name":"type","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Numbers"]}},"/v1/numbers/provision":{"post":{"operationId":"numbers.provision","summary":"Provision a number","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionNumberDto"}}}},"responses":{"201":{"description":""}},"tags":["Numbers"]}},"/v1/numbers":{"get":{"operationId":"numbers.list","summary":"List provisioned numbers","parameters":[],"responses":{"200":{"description":""}},"tags":["Numbers"]}},"/v1/numbers/{numberId}/enable-voice":{"post":{"operationId":"numbers.enableVoice","summary":"Enable voice on a number (admin action)","parameters":[{"name":"numberId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Numbers"]}},"/v1/numbers/{numberId}":{"delete":{"operationId":"numbers.release","summary":"Release a number","parameters":[{"name":"numberId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Numbers"]}},"/v1/numbers/port":{"post":{"operationId":"numbers.port.create","summary":"Submit a number porting request","description":"Initiate a number portability request (LNP). Submit the current carrier, account number, and authorisation details. Typical porting time: 5–10 business days.","parameters":[],"responses":{"201":{"description":""}},"tags":["Numbers"]},"get":{"operationId":"numbers.port.list","summary":"List number porting requests","parameters":[],"responses":{"200":{"description":""}},"tags":["Numbers"]}},"/v1/numbers/port/{requestId}":{"get":{"operationId":"numbers.port.get","summary":"Get porting request status","parameters":[{"name":"requestId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Numbers"]}},"/v1/webhook-endpoints":{"post":{"operationId":"webhookEndpoints.create","summary":"Register webhook endpoint","description":"Creates a webhook endpoint and returns a one-time signing secret. Verify every inbound request using the X-CPaaS-Signature header (HMAC-SHA256).","parameters":[],"responses":{"201":{"description":""}},"tags":["Webhook Endpoints"],"security":[{"BearerJWT":[]}]},"get":{"operationId":"webhookEndpoints.list","summary":"List webhook endpoints","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhook Endpoints"],"security":[{"BearerJWT":[]}]}},"/v1/webhook-endpoints/{id}/attempts":{"get":{"operationId":"webhookEndpoints.attempts","summary":"List delivery attempts for an endpoint","description":"Returns the last N delivery attempts including event type, HTTP status, latency, and success flag. Use this to diagnose failed or missed webhook deliveries.","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint UUID","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max attempts to return (default 50)","schema":{"example":50,"type":"number"}}],"responses":{"200":{"description":""}},"tags":["Webhook Endpoints"],"security":[{"BearerJWT":[]}]}},"/v1/webhook-endpoints/{id}/test":{"post":{"operationId":"webhookEndpoints.test","summary":"Send a test event to the endpoint","description":"Fires a synthetic test event at the registered URL and returns the HTTP response status. Use this to verify your endpoint is reachable and your signature validation works correctly.","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint UUID","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Webhook Endpoints"],"security":[{"BearerJWT":[]}]}},"/v1/webhook-endpoints/{id}":{"delete":{"operationId":"webhookEndpoints.delete","summary":"Delete webhook endpoint","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint UUID","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Webhook Endpoints"],"security":[{"BearerJWT":[]}]}},"/v1/airtime":{"post":{"operationId":"airtime.send","summary":"Send airtime to a Ghana number, paid from your wallet","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Recommended. The same key returns the original transaction instead of sending again.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendAirtimeDto"}}}},"responses":{"201":{"description":""}},"tags":["Airtime & Data"]},"get":{"operationId":"airtime.list","summary":"Recent airtime and data transactions","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Airtime & Data"]}},"/v1/airtime/{id}":{"get":{"operationId":"airtime.get","summary":"One airtime or data transaction","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Airtime & Data"]}},"/v1/data-bundles":{"get":{"operationId":"data.catalog","summary":"Data bundles on sale, with prices","parameters":[{"name":"network","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Airtime & Data"]},"post":{"operationId":"data.send","summary":"Send a mobile data bundle, paid from your wallet","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Recommended. The same key returns the original transaction instead of sending again.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendDataDto"}}}},"responses":{"201":{"description":""}},"tags":["Airtime & Data"]}},"/v1/admin/data-bundles":{"get":{"operationId":"admin.dataBundles.list","summary":"All data bundles, active or not","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]},"post":{"operationId":"admin.dataBundles.upsert","summary":"Add or update a data bundle (by network + size + validity)","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin"]}},"/v1/healthcare":{"get":{"operationId":"healthcare.overview","summary":"Packages, my application, my allowance and templates","parameters":[],"responses":{"200":{"description":""}},"tags":["Healthcare"]}},"/v1/healthcare/applications":{"post":{"operationId":"healthcare.apply","summary":"Apply for a healthcare package","parameters":[],"responses":{"201":{"description":""}},"tags":["Healthcare"]}},"/v1/healthcare/applications/cancel":{"post":{"operationId":"healthcare.cancel","summary":"Withdraw my pending application","parameters":[],"responses":{"201":{"description":""}},"tags":["Healthcare"]}},"/v1/healthcare/templates/install":{"post":{"operationId":"healthcare.templates.install","summary":"Add the healthcare SMS templates to my templates","parameters":[],"responses":{"201":{"description":""}},"tags":["Healthcare"]}},"/v1/healthcare/allowance":{"get":{"operationId":"healthcare.allowance","summary":"SMS used this period against the plan allowance","parameters":[],"responses":{"200":{"description":""}},"tags":["Healthcare"]}},"/v1/public/healthcare-plans":{"get":{"operationId":"public.healthcarePlans","summary":"Healthcare SMS packages","parameters":[],"responses":{"200":{"description":""}},"tags":["Public"]}},"/v1/admin/healthcare/applications":{"get":{"operationId":"admin.healthcare.list","summary":"Healthcare applications","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]}},"/v1/admin/healthcare/plans":{"get":{"operationId":"admin.healthcare.plans","summary":"Healthcare packages including hidden ones","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]}},"/v1/admin/healthcare/applications/{id}/approve":{"post":{"operationId":"admin.healthcare.approve","summary":"Approve and assign a package (charge=true takes the fee from the wallet)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"]}},"/v1/admin/healthcare/applications/{id}/reject":{"post":{"operationId":"admin.healthcare.reject","summary":"Reject with a reason the facility sees","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"]}},"/v1/public/platform":{"get":{"operationId":"public.platform","summary":"What HOVA sells right now (enabled channels)","parameters":[],"responses":{"200":{"description":""}},"tags":["Public"]}},"/v1/admin/platform/channels":{"put":{"operationId":"admin.platform.channels","summary":"Turn channels on or off platform-wide (SMS always on)","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]}},"/metrics":{"get":{"operationId":"MetricsController_scrape","parameters":[],"responses":{"200":{"description":""}}}},"/v1/ussd/sessions":{"get":{"operationId":"ussd.sessions.list","summary":"List active USSD sessions","description":"Returns currently-active USSD sessions for your account. Sessions expire after 300s of inactivity and disappear from this list.","parameters":[],"responses":{"200":{"description":""}},"tags":["USSD"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/africastalking/ussd":{"post":{"operationId":"webhooks.atUssd","summary":"Africa's Talking USSD callback","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/ussd/shortcodes":{"post":{"operationId":"ussd.shortcodes.create","summary":"Register USSD shortcode","parameters":[],"responses":{"201":{"description":""}},"tags":["USSD"],"security":[{"BearerJWT":[]}]},"get":{"operationId":"ussd.shortcodes.list","summary":"List shortcodes","parameters":[],"responses":{"200":{"description":""}},"tags":["USSD"],"security":[{"BearerJWT":[]}]}},"/v1/ussd/shortcodes/{shortcodeId}/menu-nodes":{"post":{"operationId":"ussd.menuNodes.create","summary":"Create menu node","parameters":[{"name":"shortcodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["USSD"],"security":[{"BearerJWT":[]}]},"get":{"operationId":"ussd.menuNodes.list","summary":"List menu nodes","parameters":[{"name":"shortcodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["USSD"],"security":[{"BearerJWT":[]}]}},"/v1/ussd/shortcodes/{shortcodeId}/set-root":{"post":{"operationId":"ussd.shortcodes.setRoot","summary":"Set root menu node","parameters":[{"name":"shortcodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["USSD"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/revenue":{"get":{"operationId":"admin.reports.revenue","summary":"Revenue summary","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/revenue/top-accounts":{"get":{"operationId":"admin.reports.topAccounts","summary":"Top accounts by revenue","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/delivery":{"get":{"operationId":"admin.reports.delivery","summary":"SMS delivery","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/webhooks":{"get":{"operationId":"admin.reports.webhooks","summary":"Webhook health","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/tenants/health":{"get":{"operationId":"admin.tenants.health","summary":"Tenant health","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/metrics/refresh":{"post":{"operationId":"admin.metrics.refresh","summary":"Refresh views","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/audit":{"get":{"operationId":"admin.audit.list","summary":"Audit log","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/suspend":{"post":{"operationId":"admin.accounts.suspend","summary":"Suspend account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/unsuspend":{"post":{"operationId":"admin.accounts.unsuspend","summary":"Unsuspend account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/topup":{"post":{"operationId":"admin.accounts.topup","summary":"Manual wallet credit","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/spend-cap":{"post":{"operationId":"admin.accounts.spendCap","summary":"Set spend cap","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}":{"get":{"operationId":"admin.accounts.detail","summary":"Tenant detail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/fraud/monitor":{"get":{"operationId":"admin.fraud.monitor","summary":"Fraud monitor","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/platform/health":{"get":{"operationId":"admin.platform.health","summary":"Platform health","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/errors":{"get":{"operationId":"admin.errors.list","summary":"Error log","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/suppressions":{"get":{"operationId":"admin.suppressions.list","summary":"List platform-wide opt-out suppression numbers","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"admin.suppressions.add","summary":"Add number to platform suppression list","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/suppressions/delete":{"post":{"operationId":"admin.suppressions.remove","summary":"Remove number from suppression list","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/tier":{"post":{"operationId":"admin.accounts.changeTier","summary":"Change account tier","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/account-type":{"post":{"operationId":"admin.accounts.changeAccountType","summary":"Change account type (business/individual/nonprofit)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/pricing-markup":{"post":{"operationId":"admin.accounts.setPricingMarkup","summary":"Override per-account pricing markup (basis points)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/force-reonboarding":{"post":{"operationId":"admin.accounts.forceReonboarding","summary":"Force account-type re-choice on next login","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/grant-admin":{"post":{"operationId":"admin.accounts.grantAdmin","summary":"Grant platform admin role","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/revoke-admin":{"post":{"operationId":"admin.accounts.revokeAdmin","summary":"Revoke platform admin role","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/admins":{"get":{"operationId":"admin.admins.list","summary":"List all platform admins","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/rate-limit":{"get":{"operationId":"admin.accounts.getRateLimit","summary":"Get rate limit config for account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"admin.accounts.setRateLimit","summary":"Set rate limit override for account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/rate-limit-tiers":{"get":{"operationId":"admin.rateLimitTiers.list","summary":"List all rate limit tiers","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/regions":{"get":{"operationId":"admin.geo.regions","summary":"Account counts by Ghana region with revenue and contacts","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/districts":{"get":{"operationId":"admin.geo.districts","summary":"District breakdown within a region","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/accounts":{"get":{"operationId":"admin.geo.accounts","summary":"Accounts with geo data, filterable by region and district","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/regions-list":{"get":{"operationId":"admin.geo.regionsList","summary":"Ghana regions reference list","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/export":{"get":{"operationId":"admin.geo.export","summary":"Export accounts with contact counts for a region/district","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/contacts-export":{"get":{"operationId":"admin.geo.contactsExport","summary":"Export end-customer contacts from tenants in a region/district (Scenario B)","description":"Returns phone numbers and names of contacts uploaded by tenants registered in the specified region/district. Excludes opted-out contacts. Use page/pageSize for pagination — large regions may have millions of contacts.","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/suppressions":{"get":{"operationId":"admin.geo.suppressions.list","summary":"List global broadcast suppression list","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"admin.geo.suppressions.add","summary":"Add phone to global broadcast suppression list","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/suppressions/{phone}":{"delete":{"operationId":"admin.geo.suppressions.remove","summary":"Remove phone from global broadcast suppression list","parameters":[{"name":"phone","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/geo/accounts/{id}/marketing-optout":{"post":{"operationId":"admin.geo.marketingOptout","summary":"Toggle marketing opt-out for a tenant (Scenario A)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/alerts":{"get":{"operationId":"admin.accounts.getAlerts","summary":"Get alert config for account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"admin.accounts.setAlerts","summary":"Set alert config for account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/accounts/{id}/msg-limit":{"post":{"operationId":"admin.accounts.setMsgLimit","summary":"Set daily/monthly message count limit","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/alerts/events":{"get":{"operationId":"admin.alerts.events","summary":"Platform-wide alert trigger log","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/announcements":{"get":{"operationId":"admin.announcements.list","summary":"List platform announcements","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"admin.announcements.create","summary":"Create platform announcement","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/announcements/{id}/deactivate":{"post":{"operationId":"admin.announcements.deactivate","summary":"Deactivate announcement","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/export/revenue":{"get":{"operationId":"admin.export.revenue","summary":"Export revenue data as JSON for CSV","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/export/delivery":{"get":{"operationId":"admin.export.delivery","summary":"Export delivery stats as JSON for CSV","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/export/errors":{"get":{"operationId":"admin.export.errors","summary":"Export error log as JSON for CSV","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/finance/geo":{"get":{"operationId":"admin.finance.geo","summary":"Revenue + cost + margin by region & district","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/finance/cohorts":{"get":{"operationId":"admin.finance.cohorts","summary":"Signup-month cohort retention by revenue","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/finance/by-channel":{"get":{"operationId":"admin.finance.byChannel","summary":"Revenue + cost + margin by channel category","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/integrations":{"get":{"operationId":"admin.reports.integrations","summary":"List connected social integrations across all tenants","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reports/integrations/{provider}/{id}":{"delete":{"operationId":"admin.reports.integrations.disconnect","summary":"Force-disconnect a tenant social integration","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/reconciliation/wallets/run":{"post":{"operationId":"admin.reconcile.wallets.run","summary":"Force-run the wallet balance reconciler","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin/Reconciliation"],"security":[{"bearer":[]}]}},"/v1/admin/reconciliation/wallets/{walletId}/run":{"post":{"operationId":"admin.reconcile.wallet.one","summary":"Force-reconcile a single wallet","parameters":[{"name":"walletId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Admin/Reconciliation"],"security":[{"bearer":[]}]}},"/v1/admin/reconciliation/stale-messages/run":{"post":{"operationId":"admin.reconcile.stale.run","summary":"Force-run stale message detector","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin/Reconciliation"],"security":[{"bearer":[]}]}},"/v1/admin/reconciliation/orphans/run":{"post":{"operationId":"admin.reconcile.orphans.run","summary":"Force-run orphan ledger detector","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin/Reconciliation"],"security":[{"bearer":[]}]}},"/v1/admin/reconciliation/stuck-topups/run":{"post":{"operationId":"admin.reconcile.topups.run","summary":"Force-run stuck top-up alerter","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin/Reconciliation"],"security":[{"bearer":[]}]}},"/v1/compliance/exports":{"post":{"operationId":"compliance.exports.create","summary":"Start an export of your account data, or of one contact's data (kind=contact, subject=phone or email)","parameters":[],"responses":{"201":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]},"get":{"operationId":"compliance.exports.list","summary":"Your recent data exports and their status","parameters":[],"responses":{"200":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/compliance/erasure":{"post":{"operationId":"compliance.erasure.create","summary":"Erase one person's data from your account (Data Protection Act 2012)","description":"Scrubs the number or address from messages, calls, conversations and contacts, clears message text and deletes call recordings. Billing rows stay without the person in them. The suppression entry stays so they are never messaged again. Cannot be undone: send confirm equal to subject. Dashboard sign-in only, not API keys.","parameters":[],"responses":{"201":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]},"get":{"operationId":"compliance.erasure.list","summary":"Erasures made on this account (no numbers or addresses shown)","parameters":[],"responses":{"200":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/compliance/exports/{id}/download":{"get":{"operationId":"compliance.exports.download","summary":"Download a ready export (gzipped JSON)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/compliance/my-data":{"get":{"operationId":"compliance.myData","summary":"Download all data we hold about your account (DSAR self-service)","parameters":[],"responses":{"200":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/compliance/consent":{"post":{"operationId":"compliance.recordConsent","summary":"Record an opt-in (consent) for a recipient","parameters":[],"responses":{"201":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/compliance/opt-out":{"post":{"operationId":"compliance.recordOptOut","summary":"Record an opt-out for a recipient","parameters":[],"responses":{"201":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/compliance/consent/check":{"get":{"operationId":"compliance.checkConsent","summary":"Check whether you have active consent for a recipient","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"channel","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/admin/compliance/accounts/{id}/export":{"get":{"operationId":"admin.compliance.exportAccount","summary":"Admin: export any account's data (DSAR-on-behalf)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/admin/compliance/accounts/{id}/contacts/{phone}/export":{"get":{"operationId":"admin.compliance.exportContact","summary":"Admin: export data held about a specific contact under an account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"phone","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Compliance"],"security":[{"bearer":[]}]}},"/v1/opt-out/{code}":{"get":{"operationId":"optOut.describe","summary":"Which business and (masked) number an opt-out link is for","parameters":[{"name":"code","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Compliance"]},"post":{"operationId":"optOut.confirm","summary":"Stop this business sending SMS to this number","parameters":[{"name":"code","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Compliance"]}},"/v1/admin/test/seed-topup":{"post":{"operationId":"admin.loadtest.seedTopup","summary":"Seed a pending top-up (load test only — env-gated)","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin/LoadTest"],"security":[{"bearer":[]}]}},"/v1/admin/test/cleanup":{"post":{"operationId":"admin.loadtest.cleanup","summary":"Clean up load-test seed data (load test only — env-gated)","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin/LoadTest"],"security":[{"bearer":[]}]}},"/v1/admin/test/ping":{"get":{"operationId":"admin.loadtest.ping","summary":"Verify load test endpoints are enabled","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin/LoadTest"],"security":[{"bearer":[]}]}},"/v1/contacts":{"get":{"operationId":"contacts.list","summary":"List contacts","parameters":[],"responses":{"200":{"description":""}},"tags":["Contacts"]},"post":{"operationId":"contacts.create","summary":"Create contact","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContactDto"}}}},"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/stats":{"get":{"operationId":"contacts.stats","summary":"Contact data-completeness stats","parameters":[],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/lead-stages":{"get":{"operationId":"contacts.leadStages","summary":"Lead pipeline counts by stage","parameters":[],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/export.csv":{"get":{"operationId":"contacts.exportCsv","summary":"Export all contacts as CSV","parameters":[],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/import":{"post":{"operationId":"contacts.import","summary":"Import contacts from CSV/XLSX file upload","description":"Uploads a CSV or XLSX file and imports rows. Files < 5MB are processed synchronously. Files >= 5MB are processed in the background — the response contains a jobId you can poll via GET /v1/contacts/import/:jobId.","parameters":[],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/import/{jobId}":{"get":{"operationId":"contacts.importStatus","summary":"Get import job status","description":"Returns the current progress of a background import job.","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/imports":{"get":{"operationId":"contacts.listImports","summary":"List import jobs","parameters":[],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/bulk":{"post":{"operationId":"contacts.bulk","summary":"Bulk import contacts from JSON","description":"Same dedup + mode behavior as POST /import, but accepts pre-parsed JSON rows. Use this when integrating from a CRM or external system that already has the data in memory.","parameters":[],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/fields":{"get":{"operationId":"contacts.fields.list","summary":"List custom contact fields","parameters":[],"responses":{"200":{"description":""}},"tags":["Contacts"]},"post":{"operationId":"contacts.fields.create","summary":"Create a custom contact field","parameters":[],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/fields/{fieldId}":{"delete":{"operationId":"contacts.fields.delete","summary":"Delete a custom contact field","parameters":[{"name":"fieldId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/bulk-delete":{"post":{"operationId":"contacts.bulkDelete","summary":"Delete multiple contacts by ID","description":"Soft-deletes contacts by ID array. Returns the count of contacts deleted.","parameters":[],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/bulk-stage":{"post":{"operationId":"contacts.bulkSetLeadStage","summary":"Set the lead stage on multiple contacts","description":"Assigns a reserved \"lead:*\" stage tag to many contacts at once (or clears it when stage is null). A contact ends in exactly one stage.","parameters":[],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/delete-all":{"post":{"operationId":"contacts.deleteAll","summary":"Delete all contacts","description":"Soft-deletes ALL contacts for the account. This is irreversible from the dashboard (contacts are recoverable from the database for 30 days). Requires the body to contain { confirm: true } as a safety check.","parameters":[],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/suppress":{"get":{"operationId":"contacts.suppress.list","summary":"List suppressed contacts","parameters":[],"responses":{"200":{"description":""}},"tags":["Contacts"]},"post":{"operationId":"contacts.suppress.add","summary":"Suppress a contact (add to DNC list)","parameters":[],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/suppress/{phone}":{"delete":{"operationId":"contacts.suppress.remove","summary":"Remove a contact from the suppression list","parameters":[{"name":"phone","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/{id}/tags":{"post":{"operationId":"contacts.tags.add","summary":"Add tags to a contact","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/{id}/tags/{tag}":{"delete":{"operationId":"contacts.tags.remove","summary":"Remove a tag from a contact","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"tag","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/{id}/export":{"get":{"operationId":"contacts.export","summary":"Export a single contact (GDPR)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/{id}/messages":{"get":{"operationId":"contacts.messages","summary":"List recent messages sent to a contact","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contacts/{id}":{"put":{"operationId":"contacts.update","summary":"Update contact","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]},"delete":{"operationId":"contacts.delete","summary":"Delete contact","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/contact-groups":{"get":{"operationId":"contactGroups.list","summary":"List contact groups","parameters":[],"responses":{"200":{"description":""}},"tags":["Contacts"]},"post":{"operationId":"contactGroups.create","summary":"Create contact group","parameters":[],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contact-groups/{id}/members":{"post":{"operationId":"contactGroups.addMembers","summary":"Add contacts to group","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Contacts"]}},"/v1/contact-groups/{id}":{"delete":{"operationId":"contactGroups.delete","summary":"Delete contact group","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Contacts"]}},"/v1/campaigns":{"get":{"operationId":"campaigns.list","summary":"List campaigns","parameters":[],"responses":{"200":{"description":""}},"tags":["Campaigns"]},"post":{"operationId":"campaigns.create","summary":"Create campaign","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCampaignDto"}}}},"responses":{"201":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/preview-draft":{"post":{"operationId":"campaigns.previewDraft","summary":"Preview personalised messages for first 3 contacts of a group (no saved campaign)","description":"Returns the personalised body for the first 3 contacts of the given group, for verifying {{variable}} substitution in the builder before saving/launching.","parameters":[],"responses":{"201":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/estimate-cost":{"post":{"operationId":"campaigns.estimateCost","summary":"Estimate campaign cost vs wallet balance","description":"Returns recipient count, per-segment rate, total estimated cost, wallet balance, and whether the balance covers the campaign. Advisory pre-launch check.","parameters":[],"responses":{"201":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/analyse-audience":{"post":{"operationId":"campaigns.analyseAudience","summary":"Pre-send number hygiene report for a group or pasted numbers","description":"Returns total vs sendable counts, filtered breakdown (invalid/duplicate/opted-out/suppressed), and a per-carrier split. Lets users clean their list before spending.","parameters":[],"responses":{"201":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/{id}":{"get":{"operationId":"campaigns.get","summary":"Get campaign","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Campaigns"]},"delete":{"operationId":"campaigns.cancel","summary":"Cancel campaign","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/{id}/preview":{"get":{"operationId":"campaigns.preview","summary":"Preview personalised messages for first 3 contacts","description":"Returns the personalised message body as each of the first 3 contacts in the group will receive it. Useful for verifying {{variable}} substitution before launching.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/{id}/launch":{"post":{"operationId":"campaigns.launch","summary":"Launch campaign","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/{id}/pause":{"post":{"operationId":"campaigns.pause","summary":"Pause a running campaign","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/{id}/resume":{"post":{"operationId":"campaigns.resume","summary":"Resume a paused campaign","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/{id}/stats":{"get":{"operationId":"campaigns.stats","summary":"Campaign delivery + engagement analytics","description":"Returns sent, delivered, failed, opened, clicked counts and rates. Updates in near-real-time during active campaigns.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/{id}/replies":{"get":{"operationId":"campaigns.replies","summary":"List inbound replies captured for a campaign","description":"Returns replies attributed to this campaign with contact, matched keyword, applied tag, and a keyword tally. For two-way conversational broadcasts.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Campaigns"]}},"/v1/campaigns/{id}/ab":{"get":{"operationId":"campaigns.ab","summary":"A/B test results for a campaign","description":"Returns per-variant delivery, open, and click metrics. Only available when the campaign was created with abVariants.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Campaigns"]}},"/v1/reports/channels":{"get":{"operationId":"reports.channels","summary":"Message volume by channel","parameters":[],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/reports/summary":{"get":{"operationId":"reports.summary","summary":"Cross-channel usage summary","description":"Aggregated metrics for all channels (SMS, WhatsApp, Voice, RCS) plus campaigns, contacts, and wallet balance. Defaults to last 30 days.","parameters":[{"name":"from","required":false,"in":"query","schema":{"example":"2025-01-01","type":"string"}},{"name":"to","required":false,"in":"query","schema":{"example":"2025-01-31","type":"string"}}],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/reports/chart":{"get":{"operationId":"reports.chart","summary":"Daily chart data (all channels)","description":"Per-day time series for SMS, WhatsApp, and Voice. Defaults to last 7 days.","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/reports/sms-delivery":{"get":{"operationId":"reports.smsDelivery","summary":"SMS delivery by network, day and route","description":"Delivery rate = delivered ÷ messages with a final status; messages still awaiting a delivery report are counted separately. Includes median seconds from hand-off to delivery.","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/reports/delivery":{"get":{"operationId":"reports.delivery","summary":"Delivery breakdown by channel","description":"Sent / delivered / failed counts and delivery rate per channel (SMS, WhatsApp, Voice, RCS).","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/reports/failures":{"get":{"operationId":"reports.failures","summary":"SMS failure-reason breakdown","description":"Aggregates failed SMS by provider failure reason (UNDELIV, REJECTD, EXPIRED, etc.) with humanised labels, counts, and percentages.","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/reports/voice":{"get":{"operationId":"reports.voice","summary":"Voice call analytics","description":"Call counts by direction and status, with duration and spend per bucket.","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/reports/whatsapp":{"get":{"operationId":"reports.whatsapp","summary":"WhatsApp message analytics","description":"Breakdown by message type with sent / delivered / read / received counts.","parameters":[{"name":"from","required":true,"in":"query","schema":{"type":"string"}},{"name":"to","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/reports/export":{"post":{"operationId":"reports.export","summary":"Export report as CSV or PDF","description":"Generates a downloadable report. Returns a pre-signed URL to the generated file.","parameters":[],"responses":{"201":{"description":""}},"tags":["Reports"]}},"/v1/reports/ai-usage":{"get":{"operationId":"reports.aiUsage","summary":"AI generation usage and cost breakdown","parameters":[],"responses":{"200":{"description":""}},"tags":["Reports"]}},"/v1/notifications":{"get":{"operationId":"notifications.list","summary":"List notifications","parameters":[{"name":"unreadOnly","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Notifications"],"security":[{"BearerJWT":[]}]},"delete":{"operationId":"notifications.clearAll","summary":"Clear all notifications","description":"Permanently deletes all notifications for the authenticated account.","parameters":[],"responses":{"200":{"description":""}},"tags":["Notifications"],"security":[{"BearerJWT":[]}]}},"/v1/notifications/{id}/read":{"post":{"operationId":"notifications.markRead","summary":"Mark notification read","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Notifications"],"security":[{"BearerJWT":[]}]}},"/v1/notifications/read-all":{"post":{"operationId":"notifications.markAllRead","summary":"Mark all notifications read","parameters":[],"responses":{"201":{"description":""}},"tags":["Notifications"],"security":[{"BearerJWT":[]}]}},"/v1/notifications/preferences":{"get":{"operationId":"notifications.prefs.get","summary":"Get notification preferences","parameters":[],"responses":{"200":{"description":""}},"tags":["Notifications"],"security":[{"BearerJWT":[]}]},"put":{"operationId":"notifications.prefs.update","summary":"Update notification preferences","parameters":[],"responses":{"200":{"description":""}},"tags":["Notifications"],"security":[{"BearerJWT":[]}]}},"/v1/team":{"get":{"operationId":"team.list","summary":"List team members","parameters":[],"responses":{"200":{"description":""}},"tags":["Team"],"security":[{"BearerJWT":[]}]}},"/v1/team/invite":{"post":{"operationId":"team.invite","summary":"Invite team member","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteTeamMemberDto"}}}},"responses":{"201":{"description":""}},"tags":["Team"],"security":[{"BearerJWT":[]}]}},"/v1/team/{id}/role":{"put":{"operationId":"team.updateRole","summary":"Update member role","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Team"],"security":[{"BearerJWT":[]}]}},"/v1/team/{id}":{"delete":{"operationId":"team.remove","summary":"Remove team member","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Team"],"security":[{"BearerJWT":[]}]}},"/v1/team/audit":{"get":{"operationId":"team.audit","summary":"Team activity audit log","parameters":[],"responses":{"200":{"description":""}},"tags":["Team"],"security":[{"BearerJWT":[]}]}},"/v1/ai/generate":{"post":{"operationId":"ai.generate","summary":"Generate AI text from a prompt template","description":"Generate personalised or translated text using LLM. Output is guardrail-checked and length-enforced. Billed per generation (GHS 0.005 default).","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateDto"}}}},"responses":{"201":{"description":""}},"tags":["AI"]}},"/v1/ai/sms/send":{"post":{"operationId":"ai.sms.send","summary":"Send AI-personalised SMS","description":"Generate personalised SMS body with AI, then dispatch immediately. Billed for both AI generation and SMS segment.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmsSendWithAiDto"}}}},"responses":{"201":{"description":""}},"tags":["AI"]}},"/v1/ai/generate/{generationId}/mark-used":{"post":{"operationId":"ai.markUsed","summary":"Mark a generation as used","parameters":[{"name":"generationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["AI"]}},"/v1/ai/generations":{"get":{"operationId":"ai.generations.list","summary":"List AI generation history","parameters":[{"name":"useCase","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AI"]}},"/v1/ai/generations/{generationId}":{"get":{"operationId":"ai.generations.get","summary":"Get generation detail","parameters":[{"name":"generationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AI"]}},"/v1/ai/agents":{"get":{"operationId":"aiAgents.list","summary":"List AI agents","parameters":[],"responses":{"200":{"description":""}},"tags":["AI Agents"]},"post":{"operationId":"aiAgents.create","summary":"Create an AI agent","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentDto"}}}},"responses":{"201":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/agents/{agentId}":{"get":{"operationId":"aiAgents.get","summary":"Get AI agent details","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AI Agents"]},"put":{"operationId":"aiAgents.update","summary":"Update an AI agent","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAgentDto"}}}},"responses":{"200":{"description":""}},"tags":["AI Agents"]},"delete":{"operationId":"aiAgents.delete","summary":"Delete (archive) an AI agent","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/agents/{agentId}/activate":{"post":{"operationId":"aiAgents.activate","summary":"Activate an agent","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/agents/{agentId}/pause":{"post":{"operationId":"aiAgents.pause","summary":"Pause an agent","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/templates":{"get":{"operationId":"ai.templates.list","summary":"List AI prompt templates","parameters":[],"responses":{"200":{"description":""}},"tags":["AI"],"security":[{"BearerJWT":[]}]},"post":{"operationId":"ai.templates.create","summary":"Create custom prompt template","parameters":[],"responses":{"201":{"description":""}},"tags":["AI"],"security":[{"BearerJWT":[]}]}},"/v1/ai/agents/{agentId}/train":{"post":{"operationId":"aiAgents.train","summary":"Train (compile) an agent","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/agents/{agentId}/knowledge":{"get":{"operationId":"aiAgents.knowledge.list","summary":"List agent knowledge entries","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AI Agents"]},"post":{"operationId":"aiAgents.knowledge.add","summary":"Add a knowledge entry (text only in Phase 1)","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddKnowledgeDto"}}}},"responses":{"201":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/agents/{agentId}/knowledge/{knowledgeId}":{"delete":{"operationId":"aiAgents.knowledge.delete","summary":"Delete a knowledge entry","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}},{"name":"knowledgeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/agents/{agentId}/sessions":{"get":{"operationId":"aiAgents.sessions.list","summary":"List agent sessions","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/agents/{agentId}/sessions/{sessionId}":{"get":{"operationId":"aiAgents.sessions.get","summary":"Get a session with full transcript","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}},{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AI Agents"]}},"/v1/ai/agents/{agentId}/chat":{"post":{"operationId":"aiAgents.chat","summary":"Send a message to the agent and get a reply (test/webchat)","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatDto"}}}},"responses":{"201":{"description":""}},"tags":["AI Agents"]}},"/v1/calls/flows":{"get":{"operationId":"ivr.alias.flows.list","summary":"List IVR flows (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["Voice"]},"post":{"operationId":"ivr.alias.flows.create","summary":"Create IVR flow (alias)","parameters":[],"responses":{"201":{"description":""}},"tags":["Voice"]}},"/v1/calls/flows/{flowId}":{"get":{"operationId":"ivr.alias.flows.get","summary":"Get IVR flow with nodes (alias)","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]},"patch":{"operationId":"ivr.alias.flows.update","summary":"Update IVR flow (alias)","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]},"delete":{"operationId":"ivr.alias.flows.delete","summary":"Delete IVR flow (alias)","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/flows/{flowId}/set-root":{"post":{"operationId":"ivr.alias.flows.setRoot","summary":"Set IVR flow root (alias)","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Voice"]}},"/v1/calls/flows/{flowId}/nodes":{"post":{"operationId":"ivr.alias.nodes.create","summary":"Add node (alias)","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Voice"]}},"/v1/calls/flows/{flowId}/nodes/{nodeId}":{"patch":{"operationId":"ivr.alias.nodes.update","summary":"Update node (alias)","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}},{"name":"nodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]},"delete":{"operationId":"ivr.alias.nodes.delete","summary":"Delete node (alias)","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}},{"name":"nodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/flows/bind/{phoneNumberId}":{"patch":{"operationId":"ivr.alias.numbers.bind","summary":"Bind flow to number (alias)","parameters":[{"name":"phoneNumberId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/ivr/flows":{"post":{"operationId":"ivr.flows.create","summary":"Create IVR flow","parameters":[],"responses":{"201":{"description":""}},"tags":["Voice"]},"get":{"operationId":"ivr.flows.list","summary":"List IVR flows","parameters":[],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/ivr/flows/{flowId}":{"get":{"operationId":"ivr.flows.get","summary":"Get IVR flow with nodes","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]},"patch":{"operationId":"ivr.flows.update","summary":"Update IVR flow metadata","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]},"delete":{"operationId":"ivr.flows.delete","summary":"Delete IVR flow","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/ivr/flows/{flowId}/set-root":{"post":{"operationId":"ivr.flows.setRoot","summary":"Set IVR flow entry node","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Voice"]}},"/v1/calls/ivr/flows/{flowId}/nodes":{"post":{"operationId":"ivr.nodes.create","summary":"Add node to IVR flow","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Voice"]},"get":{"operationId":"ivr.nodes.list","summary":"List IVR flow nodes","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/ivr/flows/{flowId}/nodes/{nodeId}":{"patch":{"operationId":"ivr.nodes.update","summary":"Update IVR node","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}},{"name":"nodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]},"delete":{"operationId":"ivr.nodes.delete","summary":"Delete IVR node","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}},{"name":"nodeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/ivr/numbers/{phoneNumberId}/bind":{"patch":{"operationId":"ivr.numbers.bind","summary":"Bind IVR flow to phone number","parameters":[{"name":"phoneNumberId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/ivr/{flowId}/twiml":{"get":{"operationId":"ivr.twiml","summary":"Render IVR flow as action JSON (preview)","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/ivr/{flowId}/digit":{"post":{"operationId":"ivr.digit","summary":"Process DTMF digit mid-call","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Voice"]}},"/v1/calls":{"post":{"operationId":"voice.call","summary":"Initiate an outbound call","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakeCallDto"}}}},"responses":{"201":{"description":""}},"tags":["Voice"]},"get":{"operationId":"voice.list","summary":"List calls","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"direction","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/recordings":{"get":{"operationId":"voice.recordings.list","summary":"List call recordings","parameters":[],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/recordings/{recordingId}/audio":{"get":{"operationId":"voice.recordings.audio","summary":"Download a call recording","parameters":[{"name":"recordingId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/recordings/{recordingId}":{"delete":{"operationId":"voice.recordings.delete","summary":"Delete a call recording (removes the audio from Hova storage)","parameters":[{"name":"recordingId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/cdr/summary":{"get":{"operationId":"voice.cdr.summary","summary":"CDR summary (30d)","parameters":[],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/calls/{callId}":{"get":{"operationId":"voice.get","summary":"Get call detail","parameters":[{"name":"callId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]},"delete":{"operationId":"voice.hangup","summary":"Hang up an active call","parameters":[{"name":"callId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Voice"]}},"/v1/webhooks/at/voice/inbound/{e164}":{"post":{"operationId":"webhooks.at.voiceInbound","summary":"AT inbound call XML callback","parameters":[{"name":"e164","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/webhooks/at/voice/status/{callId}":{"post":{"operationId":"webhooks.at.voiceStatus","summary":"AT call status callback","parameters":[{"name":"callId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/webhooks/at/voice/digit/{flowId}":{"post":{"operationId":"webhooks.at.voiceDigit","summary":"AT IVR digit event","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/webhooks/at/voice/dial-result/inbound":{"post":{"operationId":"webhooks.at.dialResult","summary":"AT dial-result handler (chains voicemail on no-answer)","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/webhooks/at/voice/recording/{callId}":{"post":{"operationId":"webhooks.at.voiceRecording","summary":"AT voicemail recording callback (outbound or attached call)","parameters":[{"name":"callId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/webhooks/at/voice/recording/inbound":{"post":{"operationId":"webhooks.at.voicemailInbound","summary":"AT inbound-call voicemail recording","parameters":[],"responses":{"200":{"description":""}},"tags":["Webhooks"]}},"/v1/ucaas/calls":{"get":{"operationId":"ucaas.calls.list","summary":"Inbound call history: outcome, who answered, cost, recording","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"period","required":true,"in":"query","schema":{"type":"string"}},{"name":"search","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"before","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Business Phone"]}},"/v1/ucaas/calls/summary":{"get":{"operationId":"ucaas.calls.summary","summary":"Answered / missed / voicemail counts, answer time, spend","parameters":[{"name":"period","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Business Phone"]}},"/v1/ucaas/calls/{id}":{"patch":{"operationId":"ucaas.calls.update","summary":"Link a call to an order / booking reference, add notes","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCallDto"}}}},"responses":{"200":{"description":""}},"tags":["Business Phone"]}},"/v1/ucaas/calls/{id}/recording":{"get":{"operationId":"ucaas.calls.recording","summary":"Play a call recording (logged)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Business Phone"]},"delete":{"operationId":"ucaas.calls.recording.delete","summary":"Delete a call recording (logged)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Business Phone"]}},"/v1/ucaas/voicemails/{id}/audio":{"get":{"operationId":"ucaas.voicemail.audio","summary":"Play a voicemail (stored by Hova, logged)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Business Phone"]}},"/v1/ucaas/recording-access":{"get":{"operationId":"ucaas.recordings.accessLog","summary":"Who played or deleted which recording","parameters":[{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Business Phone"]}},"/v1/ucaas/settings":{"get":{"operationId":"ucaas.settings.get","summary":"Recording and call settings","parameters":[],"responses":{"200":{"description":""}},"tags":["Business Phone"]},"put":{"operationId":"ucaas.settings.update","summary":"Turn recording on/off, set the spoken notice, retention and call cap","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessPhoneSettingsDto"}}}},"responses":{"200":{"description":""}},"tags":["Business Phone"]}},"/v1/admin/voice/inbound-rates":{"get":{"operationId":"admin.voice.inboundRates.list","summary":"Inbound per-minute rates","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]},"put":{"operationId":"admin.voice.inboundRates.set","summary":"Set the default (no accountId) or an account's inbound rate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundRateDto"}}}},"responses":{"200":{"description":""}},"tags":["Admin"]}},"/v1/verify/otp":{"post":{"operationId":"verify.otp.send","summary":"Send an OTP to a phone number","description":"Generates a one-time passcode and delivers it to the destination number via SMS or WhatsApp. Returns a requestId that must be passed to POST /v1/verify/otp/check. Rate-limited to 3 sends per destination per 10 minutes. Use clientReference for idempotency — identical keys within the OTP TTL return the same request.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendOtpDto"}}}},"responses":{"201":{"description":""}},"tags":["Verify"]}},"/v1/verify/otp/check":{"post":{"operationId":"verify.otp.check","summary":"Validate an OTP code","description":"Validates the code entered by the user against the stored OTP. Returns status: verified | pending | expired | max_attempts_reached. After max_attempts (default 5), further attempts are rejected regardless of code correctness.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckOtpDto"}}}},"responses":{"201":{"description":""}},"tags":["Verify"]}},"/v1/verify/otp/{requestId}":{"get":{"operationId":"verify.otp.get","summary":"Get OTP request status","description":"Returns the current status and metadata of a verify request. The OTP code itself is never returned.","parameters":[{"name":"requestId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Verify"]}},"/v1/conversations":{"get":{"operationId":"conversations.list","summary":"List omnichannel conversations","description":"Returns unified conversation threads across SMS and WhatsApp, sorted by most recent message. Each conversation represents a unique contact number regardless of channel.","parameters":[{"name":"channel","required":true,"in":"query","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Conversations"]}},"/v1/conversations/unread-count":{"get":{"operationId":"conversations.unreadCount","summary":"Get total unread message count across all conversations","description":"Fast aggregate count used by the dashboard sidebar badge. Returns { count, conversations } where count = SUM(unread_count) across all open conversations for this tenant.","parameters":[],"responses":{"200":{"description":""}},"tags":["Conversations"]}},"/v1/conversations/{conversationId}":{"get":{"operationId":"conversations.get","summary":"Get conversation with message thread","description":"Returns conversation metadata and its message history (newest first, then reversed). Marks unread count as 0.","parameters":[{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Conversations"]}},"/v1/conversations/{conversationId}/messages":{"get":{"operationId":"conversations.messages","summary":"List messages in a conversation","description":"Returns just the message list for a conversation without touching the unread counter. Use for pagination or polling for new messages.","parameters":[{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}},{"name":"before","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Conversations"]}},"/v1/conversations/{conversationId}/reply":{"post":{"operationId":"conversations.reply","summary":"Send a reply in a conversation","description":"Sends a message to the contact on the conversation's last active channel (or override with `channel`). Billing flows through the underlying SMS or WhatsApp send pipeline.","parameters":[{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyDto"}}}},"responses":{"201":{"description":""}},"tags":["Conversations"]}},"/v1/conversations/{conversationId}/assign":{"post":{"operationId":"conversations.assign","summary":"Assign conversation to an agent","parameters":[{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Conversations"]}},"/v1/conversations/{conversationId}/status":{"put":{"operationId":"conversations.status","summary":"Update conversation status (open/resolved/snoozed)","parameters":[{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Conversations"]}},"/v1/conversations/{conversationId}/tags":{"post":{"operationId":"conversations.addTags","summary":"Add tags to a conversation","parameters":[{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Conversations"]}},"/v1/conversations/{conversationId}/csat":{"post":{"operationId":"conversations.csat","summary":"Submit CSAT score for a resolved conversation","parameters":[{"name":"conversationId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Conversations"]}},"/v1/sip/trunks":{"post":{"operationId":"sip.trunk.create","summary":"Create a SIP trunk","description":"Register a new SIP trunk by providing your PABX or SBC IP address. Hova authenticates inbound/outbound SIP traffic by IP address — no passwords required. Up to 10 trunks per account. Each trunk can carry up to 500 concurrent channels.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSipTrunkDto"}}}},"responses":{"201":{"description":""}},"tags":["SIP Trunking"]},"get":{"operationId":"sip.trunk.list","summary":"List SIP trunks","description":"Returns all active SIP trunks configured for this account.","parameters":[],"responses":{"200":{"description":""}},"tags":["SIP Trunking"]}},"/v1/sip/trunks/{trunkId}":{"get":{"operationId":"sip.trunk.get","summary":"Get SIP trunk","parameters":[{"name":"trunkId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SIP Trunking"]},"put":{"operationId":"sip.trunk.update","summary":"Update SIP trunk","description":"Update trunk configuration. Changes take effect within 30 seconds.","parameters":[{"name":"trunkId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSipTrunkDto"}}}},"responses":{"200":{"description":""}},"tags":["SIP Trunking"]},"delete":{"operationId":"sip.trunk.delete","summary":"Delete SIP trunk","description":"Soft-deletes the trunk. Active calls are not interrupted.","parameters":[{"name":"trunkId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SIP Trunking"]}},"/v1/sip/trunks/{trunkId}/stats":{"get":{"operationId":"sip.trunk.stats","summary":"Get trunk usage stats (last 30 days)","parameters":[{"name":"trunkId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SIP Trunking"]}},"/v1/sip/dids":{"get":{"operationId":"sip.dids.list","summary":"List DID numbers on SIP trunks","parameters":[],"responses":{"200":{"description":""}},"tags":["SIP Trunking"]}},"/v1/sip/active-calls":{"get":{"operationId":"sip.calls.active","summary":"List active SIP calls","parameters":[],"responses":{"200":{"description":""}},"tags":["SIP Trunking"]}},"/v1/video/sessions":{"post":{"operationId":"video.session.create","summary":"Create a video session","description":"Create a new video session. Choose mode: `meet` (up to 15,000 interactive participants), `broadcast` (unlimited LL-HLS viewers + RTMPS), or `webinar` (hybrid). Enable `llHls` for 4-second latency broadcast. Add `rtmpsTargets` to simultaneously push to YouTube Live, Facebook, Twitch, etc.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVideoSessionDto"}}}},"responses":{"201":{"description":""}},"tags":["Video"]},"get":{"operationId":"video.session.list","summary":"List video sessions","parameters":[{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Video"]}},"/v1/video/sessions/{sessionId}":{"get":{"operationId":"video.session.get","summary":"Get video session","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Video"]},"delete":{"operationId":"video.session.end","summary":"End a video session","description":"Terminates the session and disconnects all participants.","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Video"]}},"/v1/video/sessions/{sessionId}/tokens":{"post":{"operationId":"video.session.token","summary":"Generate a participant token","description":"Generate a signed participant token. Pass this token to your client SDK to join the session. Roles: `publisher` (can send video/audio), `subscriber` (receive only), `moderator` (full control including kick/mute).","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVideoTokenDto"}}}},"responses":{"201":{"description":""}},"tags":["Video"]}},"/v1/video/sessions/{sessionId}/analytics":{"get":{"operationId":"video.session.analytics","summary":"Get session analytics","description":"Returns participant counts, publisher/subscriber split, and average watch time.","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Video"]}},"/v1/fraud/score":{"post":{"operationId":"fraud.score","summary":"Score a message for fraud risk","description":"Returns a fraud risk score (0–100) and verdict (clear / flagged / blocked) for a message before it is dispatched. Scoring takes <50ms. A score ≥80 indicates the message should be blocked. Signals: high_risk_prefix, sim_farm_device_cluster, ait_rate_spike, ait_elevated_rate, high_volume_ip, firewall_rule_match.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreMessageDto"}}}},"responses":{"201":{"description":""}},"tags":["Fraud Defender"]}},"/v1/fraud/firewall/rules":{"post":{"operationId":"fraud.firewall.create","summary":"Create an SMS firewall rule","description":"Create a rule to block or rate-limit traffic matching a prefix or country code. Rules are evaluated in real-time during fraud scoring.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFirewallRuleDto"}}}},"responses":{"201":{"description":""}},"tags":["Fraud Defender"]},"get":{"operationId":"fraud.firewall.list","summary":"List SMS firewall rules","parameters":[],"responses":{"200":{"description":""}},"tags":["Fraud Defender"]}},"/v1/fraud/firewall/rules/{ruleId}":{"delete":{"operationId":"fraud.firewall.delete","summary":"Delete a firewall rule","parameters":[{"name":"ruleId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Fraud Defender"]}},"/v1/fraud/spend-alert":{"get":{"operationId":"fraud.spend.get","summary":"Get spend alert configuration","parameters":[],"responses":{"200":{"description":""}},"tags":["Fraud Defender"]},"put":{"operationId":"fraud.spend.update","summary":"Update spend alert thresholds","description":"Configure spend alerts that fire when hourly or daily spend exceeds the set threshold. Alerts are delivered via SMS and email.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSpendAlertDto"}}}},"responses":{"200":{"description":""}},"tags":["Fraud Defender"]}},"/v1/fraud/stats":{"get":{"operationId":"fraud.stats","summary":"Get fraud detection stats","parameters":[],"responses":{"200":{"description":""}},"tags":["Fraud Defender"]}},"/v1/fraud/events":{"get":{"operationId":"fraud.events","summary":"List fraud events","description":"Paginated list of all scored messages, newest first. Filter by verdict.","parameters":[{"name":"verdict","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Fraud Defender"]}},"/v1/network/hlr":{"post":{"operationId":"network.hlr","summary":"HLR — Home Location Register lookup","description":"Query the Ghana carrier HLR to determine whether a number is valid, currently reachable, and which operator it belongs to. Useful for cleaning contact lists before campaign sends — avoid paying for undeliverable messages. Results are cached for 1 hour. GHS 0.05 per lookup.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HlrLookupDto"}}}},"responses":{"201":{"description":""}},"tags":["Network APIs"]}},"/v1/network/number-portability":{"post":{"operationId":"network.np","summary":"Number portability lookup","description":"Check whether a Ghana mobile number has been ported to a different operator. Ensures correct routing for ported numbers and prevents wasted delivery attempts. GHS 0.03 per lookup.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NumberPortabilityDto"}}}},"responses":{"201":{"description":""}},"tags":["Network APIs"]}},"/v1/network/silent-auth/init":{"post":{"operationId":"network.sna.init","summary":"Initiate Silent Network Authentication","description":"Starts a Silent Network Authentication (SNA) flow. The user's device must visit the returned authUrl over mobile data (not Wi-Fi). The carrier network authenticates the SIM transparently — no OTP required. Session expires after 5 minutes. GHS 0.15 per successful verification.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SilentAuthInitDto"}}}},"responses":{"201":{"description":""}},"tags":["Network APIs"]}},"/v1/network/silent-auth/verify":{"post":{"operationId":"network.sna.verify","summary":"Verify Silent Network Authentication result","description":"Exchange the code returned by the SNA redirect for a verification result.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SilentAuthVerifyDto"}}}},"responses":{"201":{"description":""}},"tags":["Network APIs"]}},"/v1/network/carrier-status":{"get":{"operationId":"network.carrier.status","summary":"Get real-time Ghana carrier status","description":"Returns operational status and latency for each direct carrier interconnect (MTN, Telecel, AirtelTigo).","parameters":[],"responses":{"200":{"description":""}},"tags":["Network APIs"]}},"/v1/network/lookups":{"get":{"operationId":"network.lookups.list","summary":"List network lookup history","parameters":[{"name":"type","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Network APIs"]}},"/v1/masking/sessions":{"post":{"operationId":"masking.sessions.create","summary":"Create a masked number session","description":"Allocates a virtual number that bridges two real numbers. Both parties call the virtual number and are connected without either seeing the other's real number. Optionally set a TTL in minutes after which the session auto-expires.","parameters":[],"responses":{"201":{"description":""}},"tags":["Number Masking"]},"get":{"operationId":"masking.sessions.list","summary":"List masking sessions","description":"Returns all masking sessions for the account. Filter by status (active|expired).","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Number Masking"]}},"/v1/masking/sessions/{sessionId}":{"get":{"operationId":"masking.sessions.get","summary":"Get a masking session","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Number Masking"]},"delete":{"operationId":"masking.sessions.delete","summary":"End a masking session","description":"Immediately expires the session and releases the virtual number back to the pool.","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Number Masking"]}},"/v1/masking/sessions/{sessionId}/log":{"get":{"operationId":"masking.sessions.log","summary":"Call log for a masking session","description":"Returns all calls routed through this virtual number pair.","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Number Masking"]}},"/v1/ucaas/extensions":{"post":{"operationId":"ucaas.extension.create","summary":"Create an extension","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExtensionDto"}}}},"responses":{"201":{"description":""}},"tags":["UCaaS"]},"get":{"operationId":"ucaas.extension.list","summary":"List extensions","parameters":[],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/extensions/{extensionId}":{"get":{"operationId":"ucaas.extension.get","summary":"Get extension","parameters":[{"name":"extensionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]},"delete":{"operationId":"ucaas.extension.delete","summary":"Delete extension","parameters":[{"name":"extensionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/call-groups":{"post":{"operationId":"ucaas.group.create","summary":"Create a hunt group","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCallGroupDto"}}}},"responses":{"201":{"description":""}},"tags":["UCaaS"]},"get":{"operationId":"ucaas.group.list","summary":"List call groups","parameters":[],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/call-groups/{groupId}":{"delete":{"operationId":"ucaas.group.delete","summary":"Delete call group","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/routing":{"get":{"operationId":"ucaas.routing.get","summary":"Get routing config + IVR menus + dial plan","parameters":[],"responses":{"200":{"description":""}},"tags":["UCaaS"]},"put":{"operationId":"ucaas.routing.upsert","summary":"Create or update IVR auto-attendant menus","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertRoutingDto"}}}},"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/routing/test":{"post":{"operationId":"ucaas.routing.test","summary":"Simulate a test call through the dial plan","parameters":[],"responses":{"201":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/hours":{"get":{"operationId":"ucaas.hours.get","summary":"Get business hours schedule","parameters":[],"responses":{"200":{"description":""}},"tags":["UCaaS"]},"put":{"operationId":"ucaas.hours.update","summary":"Update business hours schedule","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertHoursDto"}}}},"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/holidays":{"get":{"operationId":"ucaas.holidays.list","summary":"List holiday overrides","parameters":[],"responses":{"200":{"description":""}},"tags":["UCaaS"]},"post":{"operationId":"ucaas.holidays.create","summary":"Add a holiday override","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHolidayDto"}}}},"responses":{"201":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/holidays/{holidayId}":{"delete":{"operationId":"ucaas.holidays.delete","summary":"Remove a holiday override","parameters":[{"name":"holidayId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/calllog":{"get":{"operationId":"ucaas.calllog.list","summary":"Call log with filters","description":"Returns call records. Filter by type (inbound|outbound|missed|voicemail), extensionId, and period (today|yesterday|7d|30d).","parameters":[{"name":"type","required":true,"in":"query","schema":{"type":"string"}},{"name":"extensionId","required":true,"in":"query","schema":{"type":"string"}},{"name":"period","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/presence":{"get":{"operationId":"ucaas.presence.get","summary":"Live agent presence snapshot","description":"Returns current availability status for all extensions. Poll at your desired interval or subscribe via WebSocket at /ws/ucaas/presence.","parameters":[],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/presence/{extensionId}":{"put":{"operationId":"ucaas.presence.set","summary":"Set own presence status","parameters":[{"name":"extensionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/voicemails":{"get":{"operationId":"ucaas.voicemail.list","summary":"List voicemails","parameters":[{"name":"extensionId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/voicemails/{voicemailId}/listened":{"patch":{"operationId":"ucaas.voicemail.listened","summary":"Mark voicemail as listened","parameters":[{"name":"voicemailId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/voicemails/{voicemailId}":{"delete":{"operationId":"ucaas.voicemail.delete","summary":"Delete voicemail","parameters":[{"name":"voicemailId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/voicemails/{voicemailId}/transcript":{"get":{"operationId":"ucaas.voicemail.transcript","summary":"Get AI transcript of a voicemail","description":"Returns the AI-generated transcription. If not yet available, triggers async transcription and returns status=pending.","parameters":[{"name":"voicemailId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ucaas/stats":{"get":{"operationId":"ucaas.stats","summary":"UCaaS analytics dashboard stats","parameters":[],"responses":{"200":{"description":""}},"tags":["UCaaS"]}},"/v1/ccaas/queues":{"post":{"operationId":"ccaas.queue.create","summary":"Create a call queue","description":"Create an ACD call queue with configurable routing strategy (longest_waiting, round_robin, skills_based). Assign a DID so inbound callers reach this queue directly. Enable call recording for QA and compliance.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateQueueDto"}}}},"responses":{"201":{"description":""}},"tags":["CCaaS"]},"get":{"operationId":"ccaas.queue.list","summary":"List call queues","parameters":[],"responses":{"200":{"description":""}},"tags":["CCaaS"]}},"/v1/ccaas/queues/{queueId}":{"get":{"operationId":"ccaas.queue.get","summary":"Get call queue","parameters":[{"name":"queueId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["CCaaS"]},"delete":{"operationId":"ccaas.queue.delete","summary":"Delete call queue","parameters":[{"name":"queueId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["CCaaS"]}},"/v1/ccaas/queues/{queueId}/stats":{"get":{"operationId":"ccaas.queue.stats","summary":"Get queue performance stats (last 24h)","description":"Returns answered/abandoned/overflow counts, average wait, and average handle time.","parameters":[{"name":"queueId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["CCaaS"]}},"/v1/ccaas/agents":{"post":{"operationId":"ccaas.agent.create","summary":"Create a contact centre agent","description":"Register a new agent by name and mobile number. Assign skills for skills-based routing and enrol them in one or more queues.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentDto"}}}},"responses":{"201":{"description":""}},"tags":["CCaaS"]},"get":{"operationId":"ccaas.agent.list","summary":"List agents","parameters":[],"responses":{"200":{"description":""}},"tags":["CCaaS"]}},"/v1/ccaas/agents/{agentId}/status":{"patch":{"operationId":"ccaas.agent.status","summary":"Update agent availability status","description":"Set agent status to available, busy, break, or offline. Routing decisions use this in real-time.","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentStatusDto"}}}},"responses":{"200":{"description":""}},"tags":["CCaaS"]}},"/v1/ccaas/agents/{agentId}":{"delete":{"operationId":"ccaas.agent.delete","summary":"Remove agent","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["CCaaS"]}},"/v1/ccaas/agents/{agentId}/coach":{"post":{"operationId":"ccaas.agent.coach","summary":"Whisper a coaching note to an agent","description":"Sends a coaching message to the agent over the real-time channel. The customer does not hear the note. Optionally link it to a specific interaction via interactionId.","parameters":[{"name":"agentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["CCaaS"]}},"/v1/ccaas/wallboard":{"get":{"operationId":"ccaas.wallboard","summary":"Real-time contact centre wallboard","description":"Returns live agent availability counts (available / busy / on-break) per queue. Designed for polling every 10–30 seconds from a supervisor dashboard.","parameters":[],"responses":{"200":{"description":""}},"tags":["CCaaS"]}},"/v1/ccaas/sla":{"get":{"operationId":"ccaas.sla","summary":"SLA compliance report","description":"Returns first-contact resolution, average handle time, CSAT, and SLA breach log for the account.","parameters":[],"responses":{"200":{"description":""}},"tags":["CCaaS"]}},"/v1/brand/status":{"get":{"operationId":"brand.status","summary":"Brand verification status across all channels","description":"Returns verification status for each messaging channel (WhatsApp green tick, SMS alphanumeric sender ID, RCS verified agent). Includes any pending review notes.","parameters":[],"responses":{"200":{"description":""}},"tags":["Brand Verification"]}},"/v1/brand/assets":{"post":{"operationId":"brand.assets.upload","summary":"Upload brand assets","description":"Upload a brand logo, business registration certificate, or other verification documents. Accepted formats: PNG, JPG, PDF. Max 5 MB. Assets are used for channel verification applications.","parameters":[],"responses":{"201":{"description":""}},"tags":["Brand Verification"]},"get":{"operationId":"brand.assets.list","summary":"List uploaded brand assets","parameters":[],"responses":{"200":{"description":""}},"tags":["Brand Verification"]}},"/v1/brand/greentick":{"get":{"operationId":"brand.greentick","summary":"Green tick / blue badge verification status per channel","description":"Returns the Meta WhatsApp green tick status, RCS verified business agent status, and SMS brand registration status with next action steps.","parameters":[],"responses":{"200":{"description":""}},"tags":["Brand Verification"]}},"/v1/brand/apply":{"post":{"operationId":"brand.apply","summary":"Submit brand for channel verification","description":"Initiates a verification application for the specified channel (whatsapp, rcs, sms). Requires brand assets to have been uploaded first. Returns an application reference.","parameters":[],"responses":{"201":{"description":""}},"tags":["Brand Verification"]}},"/v1/changelog":{"get":{"operationId":"changelog.list","summary":"API changelog","parameters":[{"name":"type","required":false,"in":"query","schema":{"enum":["breaking","non_breaking","deprecation","security","bugfix"],"type":"string"}},{"name":"since","required":false,"in":"query","schema":{"example":"2025-01-01","type":"string"}}],"responses":{"200":{"description":""}},"tags":["Changelog"]}},"/v1/webhooks/instagram":{"get":{"operationId":"instagram.webhook.verify","summary":"Meta webhook verification challenge","parameters":[{"name":"hub.mode","required":true,"in":"query","schema":{"type":"string"}},{"name":"hub.verify_token","required":true,"in":"query","schema":{"type":"string"}},{"name":"hub.challenge","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]},"post":{"operationId":"instagram.webhook.receive","summary":"Instagram Messenger API webhook receiver","parameters":[{"name":"x-hub-signature-256","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/messages":{"post":{"operationId":"instagram.messages.send","summary":"Send an Instagram DM","description":"Send a text, image, audio, video, or sticker message to an Instagram user. The recipient must have previously messaged your connected Instagram Business account (24-hour window). recipientId is the Instagram-scoped user ID (IGSID) from the inbound webhook event. Pass an `Idempotency-Key` header to make retries safe — the same key returns the original message rather than re-sending.","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInstagramDto"}}}},"responses":{"201":{"description":""}},"tags":["Instagram"]},"get":{"operationId":"instagram.messages.list","summary":"List Instagram DM history","description":"Returns paginated Instagram DM history across all connected accounts. Filter by recipientId to get a thread.","parameters":[{"name":"recipientId","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/messages/thread/{igsid}":{"get":{"operationId":"instagram.messages.thread","summary":"Get full DM thread with a contact","parameters":[{"name":"igsid","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/story-mentions":{"get":{"operationId":"instagram.story_mentions.list","summary":"List story mentions","description":"Returns users who mentioned your connected Instagram account in their Stories.","parameters":[],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/story-mentions/reply":{"post":{"operationId":"instagram.story_mentions.reply","summary":"Reply to a story mention","description":"Send a DM reply to a user who mentioned your account in their Story.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyToStoryDto"}}}},"responses":{"201":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/analytics":{"get":{"operationId":"instagram.analytics","summary":"Instagram DM analytics","description":"Returns inbound/outbound message counts, unique contacts, seen rate, and story mention count.","parameters":[],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/automations":{"get":{"operationId":"instagram.automations.list","summary":"List IG automation rules","parameters":[],"responses":{"200":{"description":""}},"tags":["Instagram"]},"post":{"operationId":"instagram.automations.create","summary":"Create an Instagram automation rule","description":"Create keyword, story_mention, or first_message triggers that auto-reply via DM. Use {{name}} in replyText as the IGSID placeholder.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIgAutomationDto"}}}},"responses":{"201":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/automations/{id}/toggle":{"patch":{"operationId":"instagram.automations.toggle","summary":"Toggle automation active/inactive","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/automations/{id}":{"delete":{"operationId":"instagram.automations.delete","summary":"Delete automation rule","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/instagram/accounts":{"get":{"operationId":"instagram.accounts.list","summary":"List connected Instagram accounts","parameters":[],"responses":{"200":{"description":""}},"tags":["Instagram"],"security":[{"BearerJWT":[]}]}},"/v1/instagram/accounts/connect":{"post":{"operationId":"instagram.accounts.connect","summary":"Connect an Instagram Business account via Meta OAuth","description":"Exchange a short-lived Meta user access token (from your OAuth callback) for a long-lived page token. Automatically subscribes webhook events for messages, seen receipts, and story mentions on the connected Page.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectInstagramAccountDto"}}}},"responses":{"201":{"description":""}},"tags":["Instagram"],"security":[{"BearerJWT":[]}]}},"/v1/instagram/accounts/{id}":{"delete":{"operationId":"instagram.accounts.disconnect","summary":"Disconnect Instagram account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/instagram/messages":{"post":{"operationId":"instagram.alias.messages.send","summary":"Send Instagram DM (alias)","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInstagramDto"}}}},"responses":{"201":{"description":""}},"tags":["Instagram"]},"get":{"operationId":"instagram.alias.messages.list","summary":"List Instagram DMs (alias)","parameters":[{"name":"recipientId","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/webhooks/instagram/messages/thread/{igsid}":{"get":{"operationId":"instagram.alias.messages.thread","summary":"Get IG DM thread (alias)","parameters":[{"name":"igsid","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/webhooks/instagram/story-mentions":{"get":{"operationId":"instagram.alias.story_mentions.list","summary":"List story mentions (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/webhooks/instagram/story-mentions/reply":{"post":{"operationId":"instagram.alias.story_mentions.reply","summary":"Reply to story mention (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyToStoryDto"}}}},"responses":{"201":{"description":""}},"tags":["Instagram"]}},"/v1/webhooks/instagram/analytics":{"get":{"operationId":"instagram.alias.analytics","summary":"IG analytics (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/webhooks/instagram/automations":{"get":{"operationId":"instagram.alias.automations.list","summary":"List IG automations (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["Instagram"]},"post":{"operationId":"instagram.alias.automations.create","summary":"Create IG automation (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIgAutomationDto"}}}},"responses":{"201":{"description":""}},"tags":["Instagram"]}},"/v1/webhooks/instagram/automations/{id}/toggle":{"patch":{"operationId":"instagram.alias.automations.toggle","summary":"Toggle IG automation (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/webhooks/instagram/automations/{id}":{"delete":{"operationId":"instagram.alias.automations.delete","summary":"Delete IG automation (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"]}},"/v1/webhooks/instagram/accounts":{"get":{"operationId":"instagram.alias.accounts.list","summary":"List connected IG accounts (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["Instagram"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/instagram/accounts/connect":{"post":{"operationId":"instagram.alias.accounts.connect","summary":"Connect IG account (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectInstagramAccountDto"}}}},"responses":{"201":{"description":""}},"tags":["Instagram"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/instagram/accounts/{id}":{"delete":{"operationId":"instagram.alias.accounts.disconnect","summary":"Disconnect IG account (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Instagram"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/tiktok":{"post":{"operationId":"tiktok.webhook.receive","summary":"TikTok Business API webhook receiver","description":"Receives DM events, comment events, and LIVE stream events from TikTok. Verified with HMAC-SHA256 using the webhook secret per TikTok's standard TikTok-Signature header (`t=<unix>,s=<hex>`). Events fan into the unified conversations inbox and trigger automation rules.","parameters":[{"name":"tiktok-signature","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]},"get":{"operationId":"tiktok.alias.accounts.list","summary":"List TikTok accounts (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/tiktok/oauth/callback":{"get":{"operationId":"tiktok.oauth.callback","summary":"TikTok OAuth callback","description":"Receives the authorization code from TikTok after the user approves consent. Exchanges the code for tokens, persists the connected account, and redirects the user back to the HOVA dashboard.","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/messages":{"post":{"operationId":"tiktok.messages.send","summary":"Send a TikTok DM","description":"Send a text or image DM to a TikTok user. The recipient must have previously messaged your connected TikTok Business account. recipientOpenId is obtained from inbound DM webhook events. Pass an `Idempotency-Key` header to make retries safe — the same key returns the original message rather than re-sending.","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendTikTokDmDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"]},"get":{"operationId":"tiktok.messages.list","summary":"List TikTok DM history","parameters":[{"name":"recipientOpenId","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/messages/thread/{openId}":{"get":{"operationId":"tiktok.messages.thread","summary":"Get DM thread with a TikTok user","parameters":[{"name":"openId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/shop/reply":{"post":{"operationId":"tiktok.shop.reply","summary":"Send TikTok Shop order DM","description":"Send an order confirmation, shipping update, or support reply to a TikTok Shop buyer.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TikTokShopReplyDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/live/auto-reply":{"post":{"operationId":"tiktok.live.autoReply","summary":"Configure TikTok LIVE DM auto-reply","description":"Set an auto-reply message sent to viewers who DM your account during a TikTok LIVE stream. Captures high-intent purchase queries in real time.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigureLiveAutoReplyDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"]},"get":{"operationId":"tiktok.live.list","summary":"List TikTok LIVE auto-reply configs","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/live/auto-reply/{id}":{"delete":{"operationId":"tiktok.live.delete","summary":"Delete a TikTok LIVE auto-reply config","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/seed-demo":{"post":{"operationId":"tiktok.seedDemo","summary":"Seed sample TikTok engagement data","parameters":[],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/agent":{"get":{"operationId":"tiktok.agent.get","summary":"Get the workspace TikTok DM agent","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]},"post":{"operationId":"tiktok.agent.upsert","summary":"Create or update the workspace TikTok DM agent","parameters":[],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/comments":{"get":{"operationId":"tiktok.comments.list","summary":"List TikTok video comments","description":"Returns comments received on your TikTok videos. Filter by videoId.","parameters":[{"name":"videoId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/analytics":{"get":{"operationId":"tiktok.analytics","summary":"TikTok engagement analytics","description":"Returns inbound/outbound DM counts, unique contacts, comment volume, and automation fire counts.","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/automations":{"get":{"operationId":"tiktok.automations.list","summary":"List TikTok automation rules","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]},"post":{"operationId":"tiktok.automations.create","summary":"Create a TikTok automation rule","description":"Create keyword, comment, live_viewer, first_dm, or shop_inquiry triggers. Keyword rules match DMs and comments. live_viewer rules fire when a LIVE viewer DMs. shop_inquiry rules match common purchase-intent words automatically.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTikTokAutomationDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/automations/{id}/toggle":{"patch":{"operationId":"tiktok.automations.toggle","summary":"Toggle automation active/inactive","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/automations/{id}":{"delete":{"operationId":"tiktok.automations.delete","summary":"Delete automation rule","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/comment-rules":{"get":{"operationId":"tiktok.commentRules.list","summary":"List TikTok comment auto-reply rules","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]},"post":{"operationId":"tiktok.commentRules.create","summary":"Create a comment auto-reply rule","parameters":[],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/comment-rules/{id}":{"patch":{"operationId":"tiktok.commentRules.update","summary":"Update a comment auto-reply rule","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]},"delete":{"operationId":"tiktok.commentRules.delete","summary":"Delete a comment auto-reply rule","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/comment-rules/try/{commentId}":{"post":{"operationId":"tiktok.commentRules.try","summary":"Run rules engine against an existing comment (demo / preview)","parameters":[{"name":"commentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/comment-rules/try-all":{"post":{"operationId":"tiktok.commentRules.tryAll","summary":"Replay all rules against every comment (demo)","parameters":[],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/tiktok/accounts":{"get":{"operationId":"tiktok.accounts.list","summary":"List connected TikTok accounts","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"],"security":[{"BearerJWT":[]}]}},"/v1/tiktok/accounts/oauth-url":{"get":{"operationId":"tiktok.accounts.oauthUrl","summary":"Get TikTok OAuth authorization URL","description":"Returns the TikTok for Business OAuth URL. Redirect the user to this URL to authorize access.","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"],"security":[{"BearerJWT":[]}]}},"/v1/tiktok/accounts/connect":{"post":{"operationId":"tiktok.accounts.connect","summary":"Connect a TikTok Business account via OAuth","description":"Exchange an authorization code (from TikTok OAuth redirect) for long-lived access and refresh tokens. The access token is refreshed automatically before it expires.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectTikTokAccountDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"],"security":[{"BearerJWT":[]}]}},"/v1/tiktok/accounts/{id}":{"delete":{"operationId":"tiktok.accounts.disconnect","summary":"Disconnect TikTok account","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/tiktok/messages":{"post":{"operationId":"tiktok.alias.messages.send","summary":"Send TikTok DM (alias)","parameters":[{"name":"idempotency-key","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendTikTokDmDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"]},"get":{"operationId":"tiktok.alias.messages.list","summary":"List TikTok DMs (alias)","parameters":[{"name":"recipientOpenId","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/messages/thread/{openId}":{"get":{"operationId":"tiktok.alias.messages.thread","summary":"Get TikTok DM thread (alias)","parameters":[{"name":"openId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/shop/reply":{"post":{"operationId":"tiktok.alias.shop.reply","summary":"TikTok Shop reply (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TikTokShopReplyDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/live/auto-reply":{"post":{"operationId":"tiktok.alias.live.autoReply","summary":"Configure LIVE auto-reply (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigureLiveAutoReplyDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"]},"get":{"operationId":"tiktok.alias.live.list","summary":"List LIVE auto-reply configs (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/live/auto-reply/{id}":{"delete":{"operationId":"tiktok.alias.live.delete","summary":"Delete LIVE auto-reply (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/seed-demo":{"post":{"operationId":"tiktok.alias.seedDemo","summary":"Seed sample TikTok engagement data (alias)","parameters":[],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/agent":{"get":{"operationId":"tiktok.alias.agent.get","summary":"Get TikTok DM agent (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]},"post":{"operationId":"tiktok.alias.agent.upsert","summary":"Upsert TikTok DM agent (alias)","parameters":[],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/comments":{"get":{"operationId":"tiktok.alias.comments.list","summary":"List TikTok comments (alias)","parameters":[{"name":"videoId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/analytics":{"get":{"operationId":"tiktok.alias.analytics","summary":"TikTok analytics (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/automations":{"get":{"operationId":"tiktok.alias.automations.list","summary":"List TikTok automations (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]},"post":{"operationId":"tiktok.alias.automations.create","summary":"Create TikTok automation (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTikTokAutomationDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/automations/{id}/toggle":{"patch":{"operationId":"tiktok.alias.automations.toggle","summary":"Toggle TikTok automation (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/automations/{id}":{"delete":{"operationId":"tiktok.alias.automations.delete","summary":"Delete TikTok automation (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/comment-rules":{"get":{"operationId":"tiktok.alias.commentRules.list","summary":"List comment auto-reply rules (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"]},"post":{"operationId":"tiktok.alias.commentRules.create","summary":"Create a comment auto-reply rule (alias)","parameters":[],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/comment-rules/{id}":{"patch":{"operationId":"tiktok.alias.commentRules.update","summary":"Update a comment auto-reply rule (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]},"delete":{"operationId":"tiktok.alias.commentRules.delete","summary":"Delete a comment auto-reply rule (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/comment-rules/try/{commentId}":{"post":{"operationId":"tiktok.alias.commentRules.try","summary":"Try rules engine on existing comment (alias)","parameters":[{"name":"commentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/comment-rules/try-all":{"post":{"operationId":"tiktok.alias.commentRules.tryAll","summary":"Replay all rules against all comments (alias)","parameters":[],"responses":{"201":{"description":""}},"tags":["TikTok"]}},"/v1/webhooks/tiktok/oauth-url":{"get":{"operationId":"tiktok.alias.accounts.oauthUrl","summary":"TikTok OAuth URL (alias)","parameters":[],"responses":{"200":{"description":""}},"tags":["TikTok"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/tiktok/connect":{"post":{"operationId":"tiktok.alias.accounts.connect","summary":"Connect TikTok account (alias)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectTikTokAccountDto"}}}},"responses":{"201":{"description":""}},"tags":["TikTok"],"security":[{"BearerJWT":[]}]}},"/v1/webhooks/tiktok/{id}":{"delete":{"operationId":"tiktok.alias.accounts.disconnect","summary":"Disconnect TikTok account (alias)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["TikTok"],"security":[{"BearerJWT":[]}]}},"/v1/email/send":{"post":{"operationId":"email.send","summary":"Send a transactional email","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailDto"}}}},"responses":{"201":{"description":""}},"tags":["Email"]}},"/v1/email/bulk":{"post":{"operationId":"email.bulk","summary":"Bulk send to a contact group","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkEmailDto"}}}},"responses":{"201":{"description":""}},"tags":["Email"]}},"/v1/email/messages":{"get":{"operationId":"email.messages.list","summary":"List email history","parameters":[{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"bulkSendId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/email/messages/{messageId}":{"get":{"operationId":"email.messages.get","summary":"Get a single message (with body)","parameters":[{"name":"messageId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/email/bulk-sends":{"get":{"operationId":"email.bulk.list","summary":"List bulk email sends","parameters":[{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/email/bulk-sends/{bulkSendId}":{"get":{"operationId":"email.bulk.get","summary":"Get bulk send detail with aggregate stats","parameters":[{"name":"bulkSendId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/email/bulk-sends/{bulkSendId}/cancel":{"post":{"operationId":"email.bulk.cancel","summary":"Cancel a bulk send and refund undispatched messages","description":"Drains queued/delayed jobs from BullMQ and refunds the per-message price for each drained recipient. Active jobs (mid-flight to Resend) are left alone — they may still deliver. Returns the count of jobs drained and the refunded amount.","parameters":[{"name":"bulkSendId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Email"]}},"/v1/email/analytics":{"get":{"operationId":"email.analytics","summary":"Email analytics","parameters":[],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/email/templates":{"get":{"operationId":"email.templates.list","summary":"List email templates","parameters":[{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Email"]},"post":{"operationId":"email.templates.create","summary":"Create email template","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmailTemplateDto"}}}},"responses":{"201":{"description":""}},"tags":["Email"]}},"/v1/email/templates/{templateId}":{"delete":{"operationId":"email.templates.delete","summary":"Delete email template","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/email/domains":{"get":{"operationId":"email.domains.list","summary":"List sending domains","parameters":[],"responses":{"200":{"description":""}},"tags":["Email"]},"post":{"operationId":"email.domains.register","summary":"Register a sending domain","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDomainDto"}}}},"responses":{"201":{"description":""}},"tags":["Email"]}},"/v1/email/domains/{domainId}/verify":{"post":{"operationId":"email.domains.verify","summary":"Verify domain DNS records","parameters":[{"name":"domainId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Email"]}},"/v1/email/suppress":{"get":{"operationId":"email.suppress.list","summary":"List suppressed emails","parameters":[{"name":"cursor","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Email"]},"post":{"operationId":"email.suppress.add","summary":"Suppress an email address","parameters":[],"responses":{"201":{"description":""}},"tags":["Email"]}},"/v1/email/suppress/{email}":{"delete":{"operationId":"email.suppress.remove","summary":"Remove from suppression list","parameters":[{"name":"email","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/webhooks/email/resend":{"post":{"operationId":"email.webhook.resend","summary":"Resend event webhook","parameters":[],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/webhooks/email/unsubscribe":{"post":{"operationId":"email.webhook.unsubscribe","summary":"One-click unsubscribe handler","parameters":[],"responses":{"200":{"description":""}},"tags":["Email"]},"get":{"operationId":"email.webhook.unsubscribeGet","summary":"Unsubscribe link (footer link click)","parameters":[],"responses":{"200":{"description":""}},"tags":["Email"]}},"/v1/webchat/widgets":{"get":{"operationId":"webchat.widgets.list","summary":"List chat widgets","parameters":[],"responses":{"200":{"description":""}},"tags":["Web Chat"]},"post":{"operationId":"webchat.widgets.create","summary":"Create a chat widget","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWidgetDto"}}}},"responses":{"201":{"description":""}},"tags":["Web Chat"]}},"/v1/webchat/widgets/{widgetId}":{"get":{"operationId":"webchat.widgets.get","summary":"Get widget + embed code","parameters":[{"name":"widgetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Web Chat"]},"delete":{"operationId":"webchat.widgets.delete","summary":"Delete a widget","parameters":[{"name":"widgetId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Web Chat"]}},"/v1/webchat/sessions":{"get":{"operationId":"webchat.sessions.list","summary":"List chat sessions","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"widgetId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Web Chat"]}},"/v1/webchat/sessions/{sessionId}/messages":{"get":{"operationId":"webchat.messages.list","summary":"Get session messages","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Web Chat"]}},"/v1/webchat/sessions/{sessionId}/reply":{"post":{"operationId":"webchat.sessions.reply","summary":"Send operator reply to visitor","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Web Chat"]}},"/v1/webchat/analytics":{"get":{"operationId":"webchat.analytics","summary":"Chat analytics","parameters":[],"responses":{"200":{"description":""}},"tags":["Web Chat"]}},"/v1/flows":{"get":{"operationId":"flows.list","summary":"List automation flows","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Flows (Automation)"]},"post":{"operationId":"flows.create","summary":"Create an automation flow","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFlowDto"}}}},"responses":{"201":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/flows/{flowId}":{"get":{"operationId":"flows.get","summary":"Get flow with steps","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Flows (Automation)"]},"put":{"operationId":"flows.update","summary":"Update flow config","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFlowDto"}}}},"responses":{"200":{"description":""}},"tags":["Flows (Automation)"]},"delete":{"operationId":"flows.delete","summary":"Delete a flow","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/flows/{flowId}/steps":{"post":{"operationId":"flows.steps.add","summary":"Add step to flow","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFlowStepDto"}}}},"responses":{"201":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/flows/{flowId}/steps/{stepId}":{"delete":{"operationId":"flows.steps.delete","summary":"Remove step","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}},{"name":"stepId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/flows/{flowId}/activate":{"post":{"operationId":"flows.activate","summary":"Activate a flow","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/flows/{flowId}/pause":{"post":{"operationId":"flows.pause","summary":"Pause a flow","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/flows/{flowId}/trigger":{"post":{"operationId":"flows.trigger","summary":"Manually trigger a flow for a contact","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/flows/{flowId}/runs":{"get":{"operationId":"flows.runs","summary":"Flow run history","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/flows/{flowId}/stats":{"get":{"operationId":"flows.stats","summary":"Flow analytics","parameters":[{"name":"flowId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Flows (Automation)"]}},"/v1/integrations":{"get":{"operationId":"integrations.list","summary":"List all integrations (connected + available)","parameters":[{"name":"category","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Integrations"]}},"/v1/integrations/connected":{"get":{"operationId":"integrations.connected","summary":"List connected integrations","parameters":[],"responses":{"200":{"description":""}},"tags":["Integrations"]}},"/v1/integrations/{provider}/oauth-url":{"get":{"operationId":"integrations.oauthUrl","summary":"Get OAuth authorization URL","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}},{"name":"shop","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Integrations"]}},"/v1/integrations/{provider}/connect":{"post":{"operationId":"integrations.connect","summary":"Connect via API key","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Integrations"]}},"/v1/integrations/{provider}":{"delete":{"operationId":"integrations.disconnect","summary":"Disconnect integration","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Integrations"]}},"/v1/integrations/{provider}/sync":{"post":{"operationId":"integrations.sync","summary":"Trigger a manual sync","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Integrations"]}},"/v1/integrations/{provider}/log":{"get":{"operationId":"integrations.log","summary":"Integration sync log","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Integrations"]}},"/v1/webhooks/integrations/{provider}/callback":{"get":{"operationId":"integrations.oauthCallback","summary":"OAuth callback","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}},{"name":"code","required":true,"in":"query","schema":{"type":"string"}},{"name":"state","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Integrations"]}},"/v1/webhooks/integrations/{provider}":{"post":{"operationId":"integrations.webhook","summary":"Signed integration event (Shopify, Stripe, WooCommerce, Zendesk)","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Integrations"]}},"/v1/partners/connections":{"get":{"operationId":"partners.connections","summary":"Apps connected to this HOVA account","parameters":[],"responses":{"200":{"description":""}},"tags":["Connected apps"]}},"/v1/partners/connections/{id}":{"delete":{"operationId":"partners.disconnect","summary":"Disconnect an app (revokes its key and webhook)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Connected apps"]}},"/v1/partners/{partnerId}/authorize":{"get":{"operationId":"partners.describe","summary":"Consent screen details","parameters":[{"name":"partnerId","required":true,"in":"path","schema":{"type":"string"}},{"name":"redirect_uri","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Connected apps"]},"post":{"operationId":"partners.approve","summary":"Approve the connection; returns where to send the browser","parameters":[{"name":"partnerId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveDto"}}}},"responses":{"200":{"description":""}},"tags":["Connected apps"]}},"/v1/partners/{partnerId}/deny":{"post":{"operationId":"partners.deny","summary":"Cancel the connection","parameters":[{"name":"partnerId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DenyDto"}}}},"responses":{"200":{"description":""}},"tags":["Connected apps"]}},"/v1/partners/{partnerId}/token":{"post":{"operationId":"partners.token","summary":"Exchange a one-time code for the connection credentials","parameters":[{"name":"partnerId","required":true,"in":"path","schema":{"type":"string"}},{"name":"authorization","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Connected apps"]}},"/v1/partners/{partnerId}/connections/{id}/revoke":{"post":{"operationId":"partners.partnerRevoke","summary":"Partner-initiated disconnect","parameters":[{"name":"partnerId","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Connected apps"]}},"/v1/connected-systems/providers":{"get":{"operationId":"connectedSystems.providers","summary":"Gateways HOVA can stand in for, with their HOVA addresses","parameters":[],"responses":{"200":{"description":""}},"tags":["Connected systems"]}},"/v1/connected-systems/senders":{"get":{"operationId":"connectedSystems.senders","summary":"Sender IDs a connected system can send as","parameters":[],"responses":{"200":{"description":""}},"tags":["Connected systems"]}},"/v1/connected-systems":{"get":{"operationId":"connectedSystems.list","summary":"Connected systems on this account","parameters":[],"responses":{"200":{"description":""}},"tags":["Connected systems"]},"post":{"operationId":"connectedSystems.create","summary":"Connect a system; returns its key once and what to paste where","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSystemDto"}}}},"responses":{"201":{"description":""}},"tags":["Connected systems"]}},"/v1/connected-systems/{id}":{"get":{"operationId":"connectedSystems.get","summary":"One connected system with usage and recent problems","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Connected systems"]},"patch":{"operationId":"connectedSystems.update","summary":"Rename or change the sender","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSystemDto"}}}},"responses":{"200":{"description":""}},"tags":["Connected systems"]},"delete":{"operationId":"connectedSystems.revoke","summary":"Disconnect (revokes its key)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Connected systems"]}},"/v1/connected-systems/{id}/new-key":{"post":{"operationId":"connectedSystems.newKey","summary":"Replace the key (the old one stops working at once)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Connected systems"]}},"/v1/bots":{"get":{"operationId":"bots.list","summary":"List bots","parameters":[],"responses":{"200":{"description":""}},"tags":["Bot Builder"]},"post":{"operationId":"bots.create","summary":"Create a bot","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBotDto"}}}},"responses":{"201":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}":{"get":{"operationId":"bots.get","summary":"Get a bot","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Bot Builder"]},"put":{"operationId":"bots.update","summary":"Update a bot","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBotDto"}}}},"responses":{"200":{"description":""}},"tags":["Bot Builder"]},"delete":{"operationId":"bots.delete","summary":"Delete a bot","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}/intents":{"get":{"operationId":"bots.intents.list","summary":"List intents","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Bot Builder"]},"post":{"operationId":"bots.intents.upsert","summary":"Create or update an intent","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIntentDto"}}}},"responses":{"201":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}/intents/{intentId}":{"delete":{"operationId":"bots.intents.delete","summary":"Delete an intent","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}},{"name":"intentId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}/canvas":{"get":{"operationId":"bots.canvas.get","summary":"Get bot canvas nodes","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}/canvas/nodes":{"post":{"operationId":"bots.canvas.upsertNode","summary":"Create or update canvas node","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertNodeDto"}}}},"responses":{"201":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}/deploy":{"post":{"operationId":"bots.deploy","summary":"Deploy bot to channels","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}/pause":{"post":{"operationId":"bots.pause","summary":"Pause a live bot","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}/test":{"post":{"operationId":"bots.test","summary":"Test a bot with a message","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Bot Builder"]}},"/v1/bots/{botId}/stats":{"get":{"operationId":"bots.stats","summary":"Bot analytics","parameters":[{"name":"botId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Bot Builder"]}},"/v1/runtime/functions":{"get":{"operationId":"runtime.functions.list","summary":"List serverless functions","parameters":[],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]},"post":{"operationId":"runtime.functions.create","summary":"Create a function","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFunctionDto"}}}},"responses":{"201":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/functions/{id}":{"get":{"operationId":"runtime.functions.get","summary":"Get function with code","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]},"put":{"operationId":"runtime.functions.update","summary":"Update function code","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFunctionDto"}}}},"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]},"delete":{"operationId":"runtime.functions.delete","summary":"Delete function","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/functions/{id}/deploy":{"post":{"operationId":"runtime.functions.deploy","summary":"Deploy function","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/functions/{id}/logs":{"get":{"operationId":"runtime.functions.logs","summary":"Function execution logs","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/functions/{id}/deployments":{"get":{"operationId":"runtime.functions.deployments","summary":"Deployment history","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/logs":{"get":{"operationId":"runtime.logs.all","summary":"All function logs","parameters":[],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/assets":{"get":{"operationId":"runtime.assets.list","summary":"List static assets","parameters":[],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]},"post":{"operationId":"runtime.assets.upload","summary":"Upload a static asset","parameters":[],"responses":{"201":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/assets/{id}":{"delete":{"operationId":"runtime.assets.delete","summary":"Delete an asset","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/env":{"get":{"operationId":"runtime.env.list","summary":"List environment variables","parameters":[],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]},"put":{"operationId":"runtime.env.set","summary":"Set environment variable","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetEnvVarDto"}}}},"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/runtime/env/{key}":{"delete":{"operationId":"runtime.env.delete","summary":"Delete environment variable","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Cloud Runtime"]}},"/v1/team-chat/members":{"get":{"operationId":"teamChat.members","summary":"List team members for DM","parameters":[],"responses":{"200":{"description":""}},"tags":["Team Chat"]}},"/v1/team-chat/channels":{"get":{"operationId":"teamChat.channels.list","summary":"List team chat channels","parameters":[],"responses":{"200":{"description":""}},"tags":["Team Chat"]},"post":{"operationId":"teamChat.channels.create","summary":"Create a channel","parameters":[],"responses":{"201":{"description":""}},"tags":["Team Chat"]}},"/v1/team-chat/channels/{channelId}/messages":{"get":{"operationId":"teamChat.messages.list","summary":"List messages in a channel","parameters":[{"name":"channelId","required":true,"in":"path","schema":{"type":"string"}},{"name":"before","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Team Chat"]},"post":{"operationId":"teamChat.messages.send","summary":"Send a message","parameters":[{"name":"channelId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Team Chat"]}},"/v1/team-chat/messages/{messageId}/react":{"post":{"operationId":"teamChat.messages.react","summary":"Add a reaction","parameters":[{"name":"messageId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Team Chat"]}},"/v1/team-chat/messages/{messageId}":{"delete":{"operationId":"teamChat.messages.delete","summary":"Delete a message","parameters":[{"name":"messageId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Team Chat"]}},"/v1/journeys":{"get":{"operationId":"journeys.list","summary":"List journeys","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Conversational Marketing"]},"post":{"operationId":"journeys.create","summary":"Create a journey","parameters":[],"responses":{"201":{"description":""}},"tags":["Conversational Marketing"]}},"/v1/journeys/{journeyId}":{"get":{"operationId":"journeys.get","summary":"Get journey with steps","parameters":[{"name":"journeyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Conversational Marketing"]},"delete":{"operationId":"journeys.delete","summary":"Delete journey","parameters":[{"name":"journeyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Conversational Marketing"]}},"/v1/journeys/{journeyId}/steps":{"post":{"operationId":"journeys.steps.add","summary":"Add a step to a journey","parameters":[{"name":"journeyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Conversational Marketing"]}},"/v1/journeys/{journeyId}/launch":{"post":{"operationId":"journeys.launch","summary":"Launch journey","parameters":[{"name":"journeyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Conversational Marketing"]}},"/v1/journeys/{journeyId}/pause":{"post":{"operationId":"journeys.pause","summary":"Pause journey","parameters":[{"name":"journeyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Conversational Marketing"]}},"/v1/journeys/{journeyId}/analytics":{"get":{"operationId":"journeys.analytics","summary":"Journey funnel analytics","parameters":[{"name":"journeyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Conversational Marketing"]}},"/v1/status":{"get":{"operationId":"status.summary","summary":"Current status, 30-day uptime per component, and incidents","parameters":[],"responses":{"200":{"description":""}},"tags":["Status"]}},"/v1/admin/status/incidents":{"post":{"operationId":"admin.status.createIncident","summary":"Open an incident on the status page","parameters":[],"responses":{"201":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/status/incidents/{id}":{"patch":{"operationId":"admin.status.updateIncident","summary":"Post an update to an incident (status + message)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sms-routes":{"get":{"operationId":"admin.smsRoutes.list","summary":"SMS routes in the order they are tried, with health and cost","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sms-routes/senders":{"get":{"operationId":"admin.smsRoutes.senders","summary":"Sender IDs registered on fallback routes","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/admin/sms-routes/senders/{senderName}/{route}":{"put":{"operationId":"admin.smsRoutes.setSender","summary":"Record that a sender ID is registered (or pending/rejected) on a route","parameters":[{"name":"senderName","required":true,"in":"path","schema":{"type":"string"}},{"name":"route","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]},"delete":{"operationId":"admin.smsRoutes.removeSender","summary":"Remove a sender ID from a fallback route","parameters":[{"name":"senderName","required":true,"in":"path","schema":{"type":"string"}},{"name":"route","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"],"security":[{"BearerJWT":[]}]}},"/v1/smpp/accounts":{"get":{"operationId":"smpp.accounts.list","summary":"Your SMPP logins and the connection details","parameters":[],"responses":{"200":{"description":""}},"tags":["SMPP"],"security":[{"bearer":[]}]},"post":{"operationId":"smpp.accounts.create","summary":"Create an SMPP login (password shown once)","parameters":[],"responses":{"201":{"description":""}},"tags":["SMPP"],"security":[{"bearer":[]}]}},"/v1/smpp/accounts/{id}/rotate":{"post":{"operationId":"smpp.accounts.rotate","summary":"Set a new password for an SMPP login","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["SMPP"],"security":[{"bearer":[]}]}},"/v1/smpp/accounts/{id}":{"patch":{"operationId":"smpp.accounts.update","summary":"Change allowed IPs or switch a login off","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SMPP"],"security":[{"bearer":[]}]},"delete":{"operationId":"smpp.accounts.remove","summary":"Delete an SMPP login","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["SMPP"],"security":[{"bearer":[]}]}},"/health/live":{"get":{"operationId":"health.live","summary":"Liveness probe (always 200 if alive)","parameters":[],"responses":{"200":{"description":""}},"tags":["Health"]}},"/health":{"get":{"operationId":"health.alias","summary":"Alias for /health/live","parameters":[],"responses":{"200":{"description":""}},"tags":["Health"]}},"/health/ready":{"get":{"operationId":"health.ready","summary":"Readiness probe (200 if deps healthy)","parameters":[],"responses":{"200":{"description":""}},"tags":["Health"]}},"/health/deep":{"get":{"operationId":"health.deep","summary":"Deep health check (all deps + memory)","parameters":[],"responses":{"200":{"description":""}},"tags":["Health"]}},"/smoke":{"get":{"operationId":"HealthController_smokeTest","parameters":[],"responses":{"200":{"description":""}},"tags":["Health"]}}},"info":{"title":"CPaaS Ghana API","description":"\nGhana-first CPaaS. Send SMS, manage numbers, run USSD, top up wallets via MTN MoMo and Paystack.\n\n## Authentication\nProgrammatic API calls use **HTTP Basic Auth**: `Authorization: Basic base64(key_prefix:raw_key)`\nManagement calls use **JWT Bearer** from `POST /v1/auth/login`.\n\n## Rate Limiting\nEvery response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.\n\n## Currency\nAll amounts in **GHS** as decimal strings (e.g. `\"12.50\"`). Internally stored as integer pesewas.\n    ","version":"1.0.0","contact":{"name":"CPaaS Ghana Support","url":"https://docs.yourcpaas.gh","email":"support@yourcpaas.gh"}},"tags":[{"name":"Authentication","description":"Account registration and JWT login"},{"name":"API Keys","description":"Manage programmatic API keys"},{"name":"Sub-Accounts","description":"Isolated billing units"},{"name":"Wallets","description":"Prepaid balance management"},{"name":"Top-Up","description":"Fund wallets via MoMo and card"},{"name":"SMS","description":"Send and receive SMS messages"},{"name":"Sender IDs","description":"Register SMS sender identifiers"},{"name":"Numbers","description":"Virtual number provisioning"},{"name":"Inbound Messages","description":"Inbound SMS via webhook or polling"},{"name":"USSD","description":"USSD shortcode and session management"},{"name":"Webhook Endpoints","description":"Configure event delivery endpoints"},{"name":"Billing","description":"Invoices, usage, and exports"},{"name":"Reports","description":"Revenue and delivery analytics (admin)"},{"name":"AI","description":"AI text generation, personalisation and translation"},{"name":"Voice","description":"Outbound calls and IVR flows"},{"name":"Changelog","description":"API change history"},{"name":"Health","description":"Service health checks"}],"servers":[{"url":"https://api.yourcpaas.gh","description":"Production"},{"url":"https://sandbox.yourcpaas.gh","description":"Sandbox"},{"url":"http://localhost:3000","description":"Local Development"}],"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"basic","description":"API Key: base64(key_prefix:raw_key)"},"BearerJWT":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"RegisterDto":{"type":"object","properties":{"name":{"type":"string","example":"Kojo Telecom Ltd"},"email":{"type":"string","example":"kojo@kojotelecom.gh"},"password":{"type":"string","example":"Str0ngP@ss12","minLength":10,"description":"Minimum 10 characters, must contain letters and (numbers or symbols)."},"accountType":{"type":"string","description":"Account type; drives sender-ID approval rules and pricing.","enum":["business","individual","nonprofit"],"default":"business"},"regionId":{"type":"string","description":"Ghana region ID from gh_regions table","example":"ashanti"},"district":{"type":"string","description":"District / town within the region","example":"Kumasi"},"phone":{"type":"string","description":"Ghana mobile number. One account per number.","example":"0244123456"},"phoneCode":{"type":"string","description":"6-digit code sent by POST /v1/auth/signup/phone/start","example":"123456"},"organisation":{"type":"string","description":"Organisation / company name","example":"Acme Telecom Ltd"},"useCase":{"type":"string","description":"Primary use case","example":"otp"},"industry":{"type":"string","description":"Industry / sector","example":"fintech"},"monthlyVolume":{"type":"string","description":"Estimated monthly message volume range","example":"10k_50k"}},"required":["name","email","password"]},"LoginDto":{"type":"object","properties":{"email":{"type":"string","example":"kojo@kojotelecom.gh","description":"Email address, or a verified Ghana mobile number (024 412 3456 / +233244123456)."},"password":{"type":"string","example":"Str0ngP@ss!"}},"required":["email","password"]},"CreateApiKeyDto":{"type":"object","properties":{"name":{"type":"string","example":"Production SMS Key"},"scopes":{"example":["sms:send","sms:read"],"type":"array","items":{"type":"string"}},"subAccountId":{"type":"string","format":"uuid"},"environment":{"type":"string","enum":["live","test"],"default":"live","description":"test keys (sk_test_…) send nothing to carriers and charge nothing; receipts are simulated."},"expiresAt":{"type":"string","format":"date-time","example":"2027-01-01T00:00:00Z"}},"required":["name"]},"CreateSenderIdDto":{"type":"object","properties":{"senderName":{"type":"string","example":"HOVA","description":"Up to 11 alphanumeric characters. This is the name recipients see on SMS."},"useCase":{"type":"string","example":"marketing","enum":["marketing","promotional","transactional","otp","alerts"],"description":"Use case category — the backend maps \"marketing\" to \"promotional\"."},"supportingDocs":{"description":"Business-registration documents supporting the sender ID.","type":"array","items":{"type":"string"}},"ownershipDocs":{"description":"Ownership / brand-rights documents (required for names that conflict with existing approved sender IDs).","type":"array","items":{"type":"string"}},"idDocumentType":{"type":"string","description":"ID document type. Required for individual accounts. Ghana Card preferred.","enum":["ghana_card","passport","voter_id"]},"idDocumentNumber":{"type":"string","description":"Ghana Card / passport / voter ID number. Required for individual accounts."},"idDocumentPath":{"type":"string","description":"Storage path of the uploaded ID document image. Required for individual accounts."},"legalNameOnId":{"type":"string","description":"Legal name exactly as it appears on the ID. Required for individual accounts."}},"required":["senderName","useCase"]},"SendSmsDto":{"type":"object","properties":{"to":{"type":"string","example":"+233244123456","description":"Recipient in E.164 format. Ghanaian local format auto-normalised."},"from":{"type":"string","example":"MyBrand","description":"Sender ID. Optional: defaults to the API key's sender (Connect to HOVA), then your default sender ID, then your only approved sender, then HOVA-INFO."},"body":{"type":"string","example":"Akwaaba! Your OTP is 482910.","maxLength":1600},"clientReference":{"type":"string","example":"order-8821-otp"},"subAccountId":{"type":"string","format":"uuid"},"scheduledAt":{"type":"string","description":"ISO-8601 datetime to schedule the send. Leave blank to send immediately."},"templateId":{"type":"string","description":"SMS template ID to track usage","format":"uuid"}},"required":["to","body"]},"BulkRecipientDto":{"type":"object","properties":{"to":{"type":"string","example":"+233244123456","description":"Recipient in E.164 or Ghanaian local format"},"body":{"type":"string","example":"Hello Kwame, your order is ready.","description":"Per-recipient body. Overrides the top-level body when provided.","maxLength":1600}},"required":["to"]},"SendBulkSmsDto":{"type":"object","properties":{"recipients":{"description":"List of recipients (1–500). Each entry must have a \"to\" field and may optionally override \"body\".","minItems":1,"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/BulkRecipientDto"}},"from":{"type":"string","example":"MyBrand","description":"Sender ID. Optional: defaults to the API key's sender (Connect to HOVA), then your default sender ID, then your only approved sender, then HOVA-INFO."},"body":{"type":"string","example":"Akwaaba! Your account has been activated.","maxLength":1600,"description":"Default body sent to recipients that do not have a per-recipient body."},"clientReference":{"type":"string","example":"campaign-jan-2025","description":"Idempotency prefix. Each recipient gets key \"<clientReference>:<to>\"."},"subAccountId":{"type":"string","format":"uuid"}},"required":["recipients"]},"SendWhatsAppDto":{"type":"object","properties":{"to":{"type":"string","example":"+233244123456"},"type":{"type":"string","enum":["text","template","image","document","audio","video"],"example":"text"},"body":{"type":"string","example":"Hello! Your appointment is confirmed."},"templateName":{"type":"string","example":"appointment_reminder"},"templateLanguage":{"type":"string","example":"en","default":"en"},"templateVariables":{"example":["Kofi","Monday 9am","Ridge Hospital"],"type":"array","items":{"type":"string"}},"mediaUrl":{"type":"string","example":"https://example.com/brochure.pdf"},"caption":{"type":"string"},"filename":{"type":"string"},"clientReference":{"type":"string","example":"appt-kofi-001"},"subAccountId":{"type":"string"}},"required":["to","type"]},"CreateTemplateDto":{"type":"object","properties":{"name":{"type":"string","example":"appointment_reminder"},"language":{"type":"string","example":"en","default":"en"},"category":{"type":"string","enum":["UTILITY","MARKETING","AUTHENTICATION"],"example":"UTILITY"},"body":{"type":"string","example":"Hi {{1}}, your appointment is confirmed for {{2}} at {{3}}."},"footer":{"type":"string"},"headerType":{"type":"string","example":"TEXT"},"headerContent":{"type":"string"},"buttons":{"example":[],"type":"array","items":{"type":"string"}}},"required":["name","category","body"]},"RegisterWaBindingDto":{"type":"object","properties":{"phoneNumberId":{"type":"string","example":"108234567890123","description":"Meta phone_number_id (WABA phone object id)"},"displayPhoneNumber":{"type":"string","example":"+233244123456"},"wabaBusinessId":{"type":"string","example":"102345678901234","description":"WABA business id (for template management)"}},"required":["phoneNumberId"]},"ConnectWaTokenDto":{"type":"object","properties":{"phoneNumberId":{"type":"string","example":"108234567890123","description":"Meta phone_number_id"},"wabaBusinessId":{"type":"string","example":"102345678901234","description":"WhatsApp Business Account id"},"accessToken":{"type":"string","description":"A permanent System User access token from Meta Business Manager with whatsapp_business_messaging and whatsapp_business_management."},"appSecret":{"type":"string","description":"Your Meta app's App Secret. Give it to receive messages through your own app; Hova then returns a webhook URL and verify token for that app."}},"required":["phoneNumberId","wabaBusinessId","accessToken"]},"EmbeddedSignupDto":{"type":"object","properties":{"code":{"type":"string","description":"The code returned by Meta Embedded Signup (FB.login response)."},"phoneNumberId":{"type":"string"},"wabaBusinessId":{"type":"string"},"pin":{"type":"string","description":"6-digit two-step verification PIN, to register the number for Cloud API."}},"required":["code","phoneNumberId","wabaBusinessId"]},"RcsButtonDto":{"type":"object","properties":{"text":{"type":"string","example":"Shop Now"},"url":{"type":"string","example":"https://example.com/shop"},"dialNumber":{"type":"string","example":"+233244000000"}},"required":["text"]},"RcsCardDto":{"type":"object","properties":{"title":{"type":"string","example":"Kente Sale — 30% off this weekend"},"description":{"type":"string","example":"Visit us at AkomaShop for the best deals."},"imageUrl":{"type":"string","example":"https://cdn.akoma.gh/kente-banner.jpg"},"buttons":{"type":"array","items":{"$ref":"#/components/schemas/RcsButtonDto"}}}},"SendRcsDto":{"type":"object","properties":{"to":{"type":"string","example":"+233244000000"},"type":{"type":"string","enum":["text","rich_card","carousel","suggested_replies"]},"text":{"type":"string","example":"Hello from Hova RCS!"},"richCard":{"$ref":"#/components/schemas/RcsCardDto"},"carousel":{"type":"array","items":{"$ref":"#/components/schemas/RcsCardDto"}},"suggestedReplies":{"type":"array","items":{"$ref":"#/components/schemas/RcsButtonDto"}},"clientReference":{"type":"string","example":"my-ref-001"},"smsFallback":{"type":"boolean","default":true}},"required":["to","type"]},"ProvisionNumberDto":{"type":"object","properties":{"e164":{"type":"string","example":"+233302000001"},"inboundWebhookUrl":{"type":"string"}},"required":["e164"]},"SendAirtimeDto":{"type":"object","properties":{"phone":{"type":"string","example":"+233241234567","description":"Ghana mobile number (any common format)."},"amount":{"type":"string","example":"5.00","description":"Airtime value in GHS (up to 2 decimals). Limits: AIRTIME_MIN_GHS–AIRTIME_MAX_GHS."},"clientReference":{"type":"string","example":"reward-1042","description":"Your reference, echoed in webhooks."}},"required":["phone","amount"]},"SendDataDto":{"type":"object","properties":{"phone":{"type":"string","example":"+233241234567"},"bundleId":{"type":"string","description":"A bundleId from GET /v1/data-bundles."},"clientReference":{"type":"string","example":"staff-data-oct"}},"required":["phone","bundleId"]},"CreateContactDto":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"phone":{"type":"string","example":"+233244123456"},"email":{"type":"string"},"groupId":{"type":"string"},"extraFields":{"type":"object"}}},"CreateCampaignDto":{"type":"object","properties":{"name":{"type":"string","example":"June Fee Reminder"},"channel":{"type":"string","enum":["sms","whatsapp","rcs","email","voice"]},"body":{"type":"string","example":"Hi {{first_name}}, your fee is due."},"senderName":{"type":"string"},"subject":{"type":"string"},"contactGroupId":{"type":"string"},"scheduledAt":{"type":"string","description":"ISO-8601 datetime for scheduled send"},"expectsReply":{"type":"boolean","description":"Capture inbound replies to this campaign (two-way broadcast)"},"replyKeywordTags":{"type":"object","description":"Keyword→tag rules, e.g. {\"YES\":\"confirmed\",\"NO\":\"declined\"}"},"abTest":{"type":"boolean","description":"Enable A/B test split"},"bodyB":{"type":"string","description":"Variant B message body for A/B test"}},"required":["name","channel","body"]},"InviteTeamMemberDto":{"type":"object","properties":{"email":{"type":"string","example":"ama@example.com"},"role":{"type":"string","enum":["admin","agent","viewer"]},"name":{"type":"string"}},"required":["email","role"]},"GenerateDto":{"type":"object","properties":{"templateSlug":{"type":"string","example":"sms-personalise-en","description":"Named template slug. Either this or useCase required."},"useCase":{"type":"string","enum":["sms_personalise","sms_generate","ivr_script","ussd_copy","email_body","translation","template_fill","custom","agent_system_prompt","tiktok_reply","tiktok_triage","tiktok_rule","tiktok_live","tiktok_insights"]},"targetChannel":{"type":"string","enum":["sms","voice","ussd","email","whatsapp","tiktok","instagram","custom"],"default":"sms"},"targetLanguage":{"type":"string","enum":["en","tw","ga","ee","pcm"],"default":"en","description":"en=English, tw=Twi, ga=Ga, ee=Ewe, pcm=Pidgin"},"context":{"type":"object","description":"Variables injected into {{placeholders}} in the prompt","example":{"recipient_name":"Kofi","purpose":"appointment reminder","date":"Monday 9 AM"}},"referenceType":{"type":"string","description":"Reference this generation to a downstream entity (for tracing)"},"referenceId":{"type":"string"}},"required":["context"]},"SmsSendWithAiDto":{"type":"object","properties":{"to":{"type":"string","example":"+233244123456"},"from":{"type":"string","example":"MyBrand"},"body":{"type":"string","description":"Original body to personalise. Required for sms_personalise use case."},"ai":{"type":"object","description":"AI configuration"},"clientReference":{"type":"string"},"subAccountId":{"type":"string"}},"required":["to","from","ai"]},"CreateAgentDto":{"type":"object","properties":{"name":{"type":"string","example":"Kwame Asante"},"phoneNumber":{"type":"string","example":"+233244000000","description":"Agent's mobile number"},"email":{"type":"string","example":"agent@acme.gh"},"skills":{"example":["billing","technical"],"type":"array","items":{"type":"string"}},"queueIds":{"example":["queue-id-1","queue-id-2"],"type":"array","items":{"type":"string"}}},"required":["name","phoneNumber"]},"UpdateAgentDto":{"type":"object","properties":{"name":{"type":"string","example":"Ama — Customer Support"},"emoji":{"type":"string","example":"🤖"},"description":{"type":"string","example":"Handles customer inquiries about orders, refunds, and account issues."},"channels":{"example":["whatsapp","sms"],"type":"array","items":{"type":"string"}},"language":{"type":"string","example":"en"},"model":{"type":"string","example":"HOVA-1"},"tone":{"type":"string","example":"professional"},"systemPrompt":{"type":"string","description":"System prompt — instructions sent to the LLM on every turn."}},"required":["name"]},"AddKnowledgeDto":{"type":"object","properties":{"sourceType":{"type":"string","enum":["text","faq"],"default":"text"},"title":{"type":"string","example":"Office hours"},"content":{"type":"string","example":"We are open Monday-Friday 8am-5pm. Saturday 9am-1pm. Closed Sunday."}},"required":["content"]},"ChatDto":{"type":"object","properties":{"message":{"type":"string","example":"What are your office hours?"},"sessionId":{"type":"string","description":"Continue an existing session. If omitted a new session is created."},"channel":{"type":"string","default":"test","description":"Channel: test (default), webchat, etc."}},"required":["message"]},"MakeCallDto":{"type":"object","properties":{"to":{"type":"string","example":"+233244123456","description":"Destination number in E.164 format"},"from":{"type":"string","example":"+233200000042","description":"Voice-enabled rented number on your account"},"ivrFlowId":{"type":"string","format":"uuid","description":"IVR flow to run when the call is answered"},"statusCallbackUrl":{"type":"string","example":"https://myapp.com/voice/status","description":"HTTPS URL to receive call status updates"},"record":{"type":"boolean","example":false,"description":"Enable call recording"},"clientReference":{"type":"string","example":"order-001-call","maxLength":255},"subAccountId":{"type":"string","format":"uuid"},"maxDurationSeconds":{"type":"number","example":180,"minimum":1,"maximum":3600,"description":"Maximum expected call duration in seconds (default 180, max 3600)"},"ttsMessage":{"type":"string","example":"Your code is 1, 2, 3, 4, 5, 6.","maxLength":600,"description":"Inline TTS message spoken when the call is answered"}},"required":["to","from"]},"UpdateCallDto":{"type":"object","properties":{}},"BusinessPhoneSettingsDto":{"type":"object","properties":{}},"InboundRateDto":{"type":"object","properties":{}},"SendOtpDto":{"type":"object","properties":{"to":{"type":"string","example":"+233201234567","description":"Destination phone number in E.164 format"},"channel":{"type":"string","example":"sms","enum":["sms","whatsapp","voice"],"description":"Channel to deliver the OTP. Defaults to sms. Use voice for a text-to-speech call-based OTP (useful when SMS is unreachable)."},"codeLength":{"type":"number","example":6,"minimum":4,"maximum":8,"description":"OTP code length. Defaults to 6."},"expirySeconds":{"type":"number","example":300,"description":"OTP validity in seconds. Defaults to 300 (5 min)."},"template":{"type":"string","example":"Your Hova verification code is {{code}}. Valid for {{expiry}} minutes."},"brand":{"type":"string","example":"MyApp","description":"Brand name shown in the OTP message."},"clientReference":{"type":"string","example":"order-checkout-user-123","description":"Idempotency key — same key returns the existing request."}},"required":["to"]},"CheckOtpDto":{"type":"object","properties":{"requestId":{"type":"string","example":"vrf_01JABCXYZ","description":"Verify request ID returned by POST /v1/verify/otp"},"code":{"type":"string","example":"483921","description":"OTP code entered by the user"}},"required":["requestId","code"]},"ReplyDto":{"type":"object","properties":{"body":{"type":"string","example":"Hello, how can I help you today?"},"text":{"type":"string","description":"Alias for `body` — same content"},"channel":{"type":"string","enum":["sms","whatsapp","rcs","instagram","tiktok"],"description":"Channel to reply on. Defaults to the channel of the most recent inbound message."}},"required":["body"]},"CreateSipTrunkDto":{"type":"object","properties":{"name":{"type":"string","example":"Head Office PABX"},"ipAddress":{"type":"string","example":"196.46.10.22","description":"IP address of your PABX / SBC"},"port":{"type":"number","example":5060,"default":5060},"transport":{"type":"string","enum":["UDP","TCP","TLS"],"default":"UDP"},"maxConcurrentCalls":{"type":"number","example":10,"description":"Max concurrent channels"},"defaultCallerId":{"type":"string","example":"+233302000000","description":"Default caller ID for outbound calls"}},"required":["name","ipAddress"]},"UpdateSipTrunkDto":{"type":"object","properties":{"name":{"type":"string"},"ipAddress":{"type":"string"},"port":{"type":"number"},"transport":{"type":"string"},"maxConcurrentCalls":{"type":"number"},"defaultCallerId":{"type":"string"}}},"CreateVideoSessionDto":{"type":"object","properties":{"name":{"type":"string","example":"Product Launch Live Stream"},"mode":{"type":"string","enum":["meet","broadcast","webinar"],"description":"Session mode"},"maxDurationMinutes":{"type":"number","example":60,"description":"Max session duration in minutes (default 60)"},"maxParticipants":{"type":"number","example":15000,"description":"Max interactive participants (meet/webinar mode)"},"recording":{"type":"boolean","default":false,"description":"Enable DVR / replay recording"},"llHls":{"type":"boolean","default":false,"description":"Enable LL-HLS broadcast output"},"rtmpsTargets":{"example":["rtmps://a.rtmp.youtube.com/live2/xxxx"],"description":"RTMPS endpoints to simulcast to (YouTube, Facebook, etc.)","type":"array","items":{"type":"string"}},"clientReference":{"type":"string","example":"my-session-ref"}},"required":["name","mode"]},"CreateVideoTokenDto":{"type":"object","properties":{"participantName":{"type":"string","example":"Alice"},"role":{"type":"string","enum":["publisher","subscriber","moderator"]},"ttlSeconds":{"type":"number","example":3600,"description":"Token TTL in seconds (default 3600)"},"metadata":{"type":"object","example":{"userId":"usr_123"},"description":"Custom metadata stored with the participant"}},"required":["participantName","role"]},"ScoreMessageDto":{"type":"object","properties":{"to":{"type":"string","example":"+233244000000"},"from":{"type":"string","example":"+233244111000","description":"Originating number / sender ID"},"channel":{"type":"string","enum":["sms","voice","ussd"]},"body":{"type":"string","example":"Your OTP is 123456","description":"Message body (used for AIT pattern detection)"},"sourceIp":{"type":"string","example":"196.46.10.22"},"deviceId":{"type":"string","example":"device_abc123"}},"required":["to","from","channel"]},"CreateFirewallRuleDto":{"type":"object","properties":{"type":{"type":"string","enum":["block_prefix","block_country","rate_limit","require_hlr"]},"pattern":{"type":"string","example":"+23350","description":"Pattern to match (prefix, country code, etc.)"},"reason":{"type":"string","example":"Block suspicious Sierra Leone traffic"},"limit":{"type":"number","example":100,"description":"For rate_limit rules: max messages per hour"}},"required":["type","pattern"]},"UpdateSpendAlertDto":{"type":"object","properties":{"enabled":{"type":"boolean","example":true},"hourlyThresholdGhs":{"type":"number","example":500,"description":"Alert when hourly spend exceeds this GHS amount"},"dailyThresholdGhs":{"type":"number","example":2000},"alertPhones":{"example":["+233244000000"],"type":"array","items":{"type":"string"}},"alertEmails":{"example":["alerts@acme.gh"],"type":"array","items":{"type":"string"}}}},"HlrLookupDto":{"type":"object","properties":{"number":{"type":"string","example":"+233244000000","description":"E.164 number to look up"},"clientReference":{"type":"string","example":"my-ref-001"}},"required":["number"]},"NumberPortabilityDto":{"type":"object","properties":{"number":{"type":"string","example":"+233244000000"}},"required":["number"]},"SilentAuthInitDto":{"type":"object","properties":{"phoneNumber":{"type":"string","example":"+233244000000"},"redirectUrl":{"type":"string","example":"https://app.acme.gh/callback/auth"},"state":{"type":"string","example":"state_xyz"}},"required":["phoneNumber"]},"SilentAuthVerifyDto":{"type":"object","properties":{"authId":{"type":"string","example":"sna_auth_xxxx"},"code":{"type":"string","example":"code_from_redirect"}},"required":["authId","code"]},"CreateExtensionDto":{"type":"object","properties":{"extension":{"type":"string","example":"101"},"displayName":{"type":"string","example":"Alice Mensah"},"mobileNumber":{"type":"string","example":"+233244000000"},"voicemailEnabled":{"type":"boolean","default":true},"ringDuration":{"type":"number","example":20}},"required":["extension","displayName"]},"CreateCallGroupDto":{"type":"object","properties":{"name":{"type":"string","example":"Sales Team"},"strategy":{"type":"string","enum":["simultaneous","round_robin","sequential"]},"extensions":{"example":["101","102"],"type":"array","items":{"type":"string"}},"ringDuration":{"type":"number","example":30},"overflowExtension":{"type":"string","example":"101"}},"required":["name","strategy","extensions"]},"IvrMenuKeyDto":{"type":"object","properties":{"key":{"type":"string","example":"1","description":"DTMF key (0-9, *, #)"},"destination":{"type":"string","example":"extension:201","description":"Destination: extension:<ext>, group:<id>, voicemail, hangup"},"label":{"type":"string","example":"Sales Queue"}},"required":["key","destination","label"]},"IvrMenuDto":{"type":"object","properties":{"id":{"type":"string","description":"Menu UUID — omit to create new"},"name":{"type":"string","example":"Main Menu"},"greeting":{"type":"string","example":"Thank you for calling. Press 1 for Sales, 2 for Support, 0 for Operator."},"keys":{"type":"array","items":{"$ref":"#/components/schemas/IvrMenuKeyDto"}}},"required":["name","greeting","keys"]},"UpsertRoutingDto":{"type":"object","properties":{"menus":{"type":"array","items":{"$ref":"#/components/schemas/IvrMenuDto"}},"mainDid":{"type":"string","example":"+233307650000","description":"Main DID for the dial plan"}},"required":["menus"]},"DayHoursDto":{"type":"object","properties":{"open":{"type":"string","example":"08:00"},"close":{"type":"string","example":"17:00"},"closed":{"type":"boolean","example":false}},"required":["closed"]},"UpsertHoursDto":{"type":"object","properties":{"monday":{"$ref":"#/components/schemas/DayHoursDto"},"tuesday":{"$ref":"#/components/schemas/DayHoursDto"},"wednesday":{"$ref":"#/components/schemas/DayHoursDto"},"thursday":{"$ref":"#/components/schemas/DayHoursDto"},"friday":{"$ref":"#/components/schemas/DayHoursDto"},"saturday":{"$ref":"#/components/schemas/DayHoursDto"},"sunday":{"$ref":"#/components/schemas/DayHoursDto"},"timezone":{"type":"string","example":"Africa/Accra"}},"required":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]},"CreateHolidayDto":{"type":"object","properties":{"name":{"type":"string","example":"Ghana Independence Day"},"date":{"type":"string","example":"2026-03-06","description":"ISO date (YYYY-MM-DD)"},"routingTarget":{"type":"string","example":"after_hours","description":"Routing target during this holiday"}},"required":["name","date"]},"CreateQueueDto":{"type":"object","properties":{"name":{"type":"string","example":"Customer Support"},"welcomeMessage":{"type":"string","example":"Thank you for calling, please hold..."},"maxWaitSeconds":{"type":"number","example":120,"description":"Max queue wait time in seconds before overflow"},"routingStrategy":{"type":"string","enum":["longest_waiting","round_robin","skills_based"],"default":"longest_waiting"},"recordCalls":{"type":"boolean","default":true},"did":{"type":"string","example":"+233302000000","description":"Number callers reach this queue on"}},"required":["name"]},"AgentStatusDto":{"type":"object","properties":{"status":{"type":"string","enum":["available","busy","break","offline"]}},"required":["status"]},"SendInstagramDto":{"type":"object","properties":{"recipientId":{"type":"string","example":"17841400455323","description":"Instagram-scoped user ID (IGSID) of the recipient. Obtain from inbound webhook events."},"type":{"type":"string","enum":["text","image","audio","video","sticker"],"example":"text"},"text":{"type":"string","example":"Thanks for reaching out! How can I help?"},"mediaUrl":{"type":"string","example":"https://cdn.example.com/product.jpg"},"quickReplies":{"example":[{"title":"Track order"},{"title":"Return item"},{"title":"Speak to agent"}],"type":"array","items":{"type":"string"}},"clientReference":{"type":"string"}},"required":["recipientId","type"]},"ReplyToStoryDto":{"type":"object","properties":{"storyId":{"type":"string","example":"17846368219941196","description":"Story ID from the story_mention webhook event."},"text":{"type":"string","example":"Love your story! Here is what we have for you 👇"},"recipientId":{"type":"string","example":"17841400455323"}},"required":["storyId","text","recipientId"]},"CreateIgAutomationDto":{"type":"object","properties":{"trigger":{"type":"string","enum":["keyword","story_mention","comment","first_message"],"example":"keyword"},"keyword":{"type":"string","example":"price","description":"Keyword to match (when trigger=keyword)."},"replyText":{"type":"string","example":"Hi {{name}}! Our pricing starts at GHS 50. Would you like a full quote?"},"active":{"type":"boolean","default":true}},"required":["trigger","replyText"]},"ConnectInstagramAccountDto":{"type":"object","properties":{"shortLivedToken":{"type":"string","example":"EAAB...XYZ","description":"Short-lived user access token from Meta OAuth redirect."},"pageId":{"type":"string","example":"106385768223786","description":"Meta Page ID to connect."}},"required":["shortLivedToken"]},"SendTikTokDmDto":{"type":"object","properties":{"recipientOpenId":{"type":"string","example":"7012345678901234567","description":"TikTok open_id of the recipient. Obtain from inbound DM webhook events."},"type":{"type":"string","enum":["text","image","video"],"example":"text"},"text":{"type":"string","example":"Thanks for your interest! Here are the details 👇"},"mediaUrl":{"type":"string","example":"https://cdn.example.com/product-demo.mp4"},"clientReference":{"type":"string"}},"required":["recipientOpenId","type"]},"TikTokShopReplyDto":{"type":"object","properties":{"recipientOpenId":{"type":"string","example":"7012345678901234567"},"orderId":{"type":"string","example":"ORD-20240611-001","description":"Order ID to include in the reply."},"text":{"type":"string","example":"Your order ORD-20240611-001 is confirmed and ships in 2 days! 📦"}},"required":["recipientOpenId","orderId","text"]},"ConfigureLiveAutoReplyDto":{"type":"object","properties":{"tiktokAccountId":{"type":"string","description":"TikTok account ID to configure LIVE auto-replies for."},"autoReplyText":{"type":"string","example":"Thanks for watching LIVE! DM us for our price list 🎁","description":"Auto-reply sent to viewers who DM during a LIVE stream."},"enabled":{"type":"boolean","default":true,"description":"Enable or disable LIVE auto-reply."}},"required":["tiktokAccountId","autoReplyText"]},"CreateTikTokAutomationDto":{"type":"object","properties":{"trigger":{"type":"string","enum":["keyword","comment","live_viewer","first_dm","shop_inquiry"],"example":"keyword"},"keyword":{"type":"string","example":"price","description":"Keyword to match in DMs or comments."},"replyText":{"type":"string","example":"Hi! Our TikTok Shop price list: https://shop.example.com 🛒"},"active":{"type":"boolean","default":true}},"required":["trigger","replyText"]},"ConnectTikTokAccountDto":{"type":"object","properties":{"code":{"type":"string","example":"act.example12345","description":"Authorization code from TikTok for Business OAuth redirect."},"state":{"type":"string","example":"state_xyz","description":"OAuth state parameter for CSRF protection."}},"required":["code"]},"SendEmailDto":{"type":"object","properties":{"to":{"type":"string","example":"ama@example.com"},"toName":{"type":"string","example":"Ama Owusu"},"subject":{"type":"string","example":"Your order is confirmed!"},"html":{"type":"string","example":"<h1>Hello!</h1><p>Your order #1234 is confirmed.</p>"},"text":{"type":"string","example":"Hello! Your order #1234 is confirmed."},"templateId":{"type":"string","example":"template-uuid","description":"Use a saved email template"},"variables":{"type":"object","description":"Variables injected into {{placeholder}} in template"},"clientReference":{"type":"string"},"from":{"type":"string","example":"noreply@hovacorp.com"},"fromName":{"type":"string","example":"HOVA"},"replyTo":{"type":"string","example":"support@hovacorp.com"}},"required":["to","subject"]},"BulkEmailDto":{"type":"object","properties":{"groupId":{"type":"string","example":"group-uuid","description":"Contact group ID to send to"},"subject":{"type":"string"},"html":{"type":"string"},"text":{"type":"string"},"templateId":{"type":"string"},"variables":{"type":"object","deprecated":true},"personalize":{"type":"boolean","description":"Substitute {{placeholders}} per recipient (first_name, last_name, email, extra fields).","default":false},"from":{"type":"string","example":"noreply@hovacorp.com"},"fromName":{"type":"string","example":"HOVA Marketing"}},"required":["groupId","subject"]},"CreateEmailTemplateDto":{"type":"object","properties":{"name":{"type":"string","example":"Order Confirmation"},"subject":{"type":"string","example":"Your order {{order_id}} is confirmed!"},"html":{"type":"string","example":"<h1>Hi {{first_name}}!</h1>"},"text":{"type":"string"},"category":{"type":"string","example":"transactional"}},"required":["name","subject","html"]},"RegisterDomainDto":{"type":"object","properties":{"domain":{"type":"string","example":"mg.hovacorp.com"}},"required":["domain"]},"CreateWidgetDto":{"type":"object","properties":{"name":{"type":"string","example":"Support Chat"},"welcomeMessage":{"type":"string","example":"Hi there! How can we help?"},"accentColor":{"type":"string","example":"#6B4FBB"},"agentName":{"type":"string","example":"HOVA Support"},"allowedDomains":{"example":["example.com","app.example.com"],"type":"array","items":{"type":"string"}}},"required":["name"]},"CreateFlowDto":{"type":"object","properties":{"name":{"type":"string","example":"Abandoned Cart Recovery"},"description":{"type":"string"},"triggerType":{"type":"string","enum":["contact_added","cart_abandoned","order_delivered","message_received","inbound_dm","inactive_days","webhook","manual"],"example":"cart_abandoned"},"triggerConfig":{"type":"object","description":"Trigger config — e.g. {inactiveDays: 90} for inactive_days trigger"}},"required":["name","triggerType"]},"CreateFlowStepDto":{"type":"object","properties":{"type":{"type":"string","enum":["wait","message","condition","tag","webhook","end"]},"delaySeconds":{"type":"number","example":3600,"description":"Delay in seconds (for wait steps)"},"delayLabel":{"type":"string","description":"Human-readable delay label, e.g. \"Wait 1 hour\""},"channel":{"type":"string","example":"whatsapp","description":"Channel for message step"},"messageBody":{"type":"string","example":"Hi {{first_name}}, you left something in your cart!"},"conditionExpr":{"type":"object","description":"Condition expression for branch steps"},"nextIfTrue":{"type":"string","description":"Next step key if condition is true"},"nextIfFalse":{"type":"string","description":"Next step key if condition is false"},"tags":{"description":"Tags to apply to contact","type":"array","items":{"type":"string"}},"webhookUrl":{"type":"string","description":"Webhook URL for webhook steps"}},"required":["type"]},"ApproveDto":{"type":"object","properties":{}},"DenyDto":{"type":"object","properties":{}},"CreateSystemDto":{"type":"object","properties":{}},"UpdateSystemDto":{"type":"object","properties":{}},"CreateBotDto":{"type":"object","properties":{"name":{"type":"string","example":"Payment Support Bot"},"description":{"type":"string","example":"Handles payment FAQs, order tracking, and account queries."},"channels":{"example":["whatsapp","sms"],"type":"array","items":{"type":"string"}},"language":{"type":"string","example":"en","default":"en"}},"required":["name"]},"CreateIntentDto":{"type":"object","properties":{"name":{"type":"string","example":"payment_failed"},"examples":{"example":["my payment failed","payment not working","card declined"],"type":"array","items":{"type":"string"}},"threshold":{"type":"number","example":0.82,"minimum":0.5,"maximum":1}},"required":["name","examples"]},"UpsertNodeDto":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["message","condition","api_call","handoff","intent"]},"text":{"type":"string"},"config":{"type":"object"},"children":{"type":"array","items":{"type":"string"}}},"required":["type"]},"CreateFunctionDto":{"type":"object","properties":{"name":{"type":"string","example":"sms-auto-reply.js"},"code":{"type":"string","example":"exports.handler = async (ctx, event, callback) => { callback(null); };"},"description":{"type":"string","example":"Keyword-based SMS auto-responder"},"dependencies":{"example":["axios@^1.6.0"],"type":"array","items":{"type":"string"}},"wiredTo":{"type":"string","example":"+233301234567","description":"Phone number or service to wire this function to"}},"required":["name","code"]},"SetEnvVarDto":{"type":"object","properties":{"key":{"type":"string","example":"BACKEND_URL"},"value":{"type":"string","example":"https://api.example.com"},"masked":{"type":"boolean","default":false}},"required":["key","value"]}}}}