# TuskMail full documentation --- --- > TuskMail (Tuskmail Africa) is Africa-first business email on your own domain from any registrar: branded user mailboxes, shared group mailboxes under one sign-in, Maria AI (an inbox assistant paid per use in KES), a developer API, and Tusk WhatsApp, a WhatsApp dashboard and REST API. Prices are in Kenyan shillings (KES); customers pay by M-Pesa or card. Web app: https://app.tuskmail.africa. Support: contact@tuskmail.africa. --- --- ## Key facts --- --- - Business email plans (yearly price, KES): Starter KES 2,470 for 2 mailboxes (KES 103 per mailbox per month); Business KES 3,950 for 5 mailboxes (KES 66 per mailbox per month); Team KES 6,320 for 10 mailboxes (KES 53 per mailbox per month). Each mailbox has 2 GB. Monthly and 3-month billing also exist. - Tusk WhatsApp plans (monthly price, KES): Starter KES 500 for 1 WhatsApp number; Growth KES 1,350 for 3 WhatsApp numbers; Business KES 2,400 for 6 WhatsApp numbers; Scale KES 3,500 for 10 WhatsApp numbers. - Maria AI is pay-as-you-go credit in KES, topped up from KES 100 to KES 10,000, with an optional monthly limit. - Works with domains from any registrar in any country; payment by M-Pesa or card, billed in KES. - Company: Loipi Plaza, Lunga Lunga Rd, Nairobi, Kenya. Phone +254726492249, +254799956916. --- --- ## Frequently asked questions: TuskMail ### What is TuskMail? TuskMail is africa-first business email. You add your own domain, create branded mailboxes for your people, and use them from the web app or the mobile apps. Mail is protected by advanced spam filtering and strong encryption. ### Can I use my own domain name? Yes. A business account adds one or more domains and then creates mailboxes such as sales@yourcompany.co.ke. You publish the MX, SPF, DKIM and DMARC records TuskMail shows you, and TuskMail checks them. Adding a domain is free. ### What is the difference between a user mailbox and a group mailbox? A user mailbox belongs to one person and has its own sign-in. A group mailbox, such as support@ or sales@, is shared: the team members you add open it from their own sign-in, so nobody has to share a password. ### What is Maria AI? Maria is the AI assistant inside TuskMail. In the inbox she suggests replies to messages that need one, and in the chat panel you can ask her to search your mail, summarise threads and draft emails. The business turns Maria on, then chooses which mailboxes can use her. ### How is Maria AI charged? Maria runs on pay-as-you-go credit in KES, separate from your plan. The business owner tops up between KES 100 and KES 10,000 and can set a monthly spending limit. A small free monthly allowance may apply. ### How do I pay, and in which currency? Plans and Maria credit are paid in Kenyan shillings (KES) by M-Pesa or card, inside the web app. You receive an invoice and a receipt for each payment. Other currencies are not charged yet. ### What happens if I forget to renew? When a paid period ends there is a 14-day grace period during which mail keeps working. If it is still unpaid after that, the subscription expires and the business's domains are blocked until payment. ### Can I read my TuskMail mail in Gmail, Thunderbird or another app? Yes. Create an app password in Settings, then connect any IMAP/SMTP client to mail.tuskmail.africa. The Settings page shows the exact server details to use. ### Is it secure? Mail is protected with spam and virus filtering, encryption in transit, and optional two-step sign-in where new devices must be approved from a trusted TuskMail mobile app. App passwords let you connect other apps without sharing your main password. --- ## Frequently asked questions: pricing ### How much does TuskMail cost? TuskMail is sold as plans in Kenyan shillings (KES). Each plan sets how many mailboxes and how much storage per mailbox you get, and can be paid monthly, every 3 months or yearly. The plans are Starter (2 mailboxes), Business (5) and Team (10), with custom plans for larger organisations; the current prices are listed on this page and confirmed in Billing at checkout. ### Is there a free trial? Yes. The Starter plan comes with a 7 days free trial. Adding a domain is also free, so you can set up and check DNS before you pay for mailboxes. ### What if I need more mailboxes than my plan includes? You can buy extra mailboxes on top of your plan for the rest of the current period, or change to a bigger plan. A plan change is prorated with credit for the unused time. ### What is the 14-day grace period? If a paid period ends without renewal, your subscription becomes past due and keeps working for 14 days. After that it expires and the business's domains are blocked until you pay. ### Can I use a coupon? Yes. Enter a coupon code at checkout. Coupons can take a percentage off, a fixed KES amount off, or add trial days. They can be limited to certain plans, have an expiry, and are used once per business. ### How does Maria AI pricing work? Maria AI is pay-as-you-go credit, not part of the plan price. Top up between KES 100 and KES 10,000 (quick amounts are 100, 250, 500, 1,000). Usage is shown in KES, never in tokens, and you can set a monthly limit. ### How do I pay? You can pay with M-Pesa or card in the web app. Each payment produces an invoice and a receipt you can print or save as PDF from your browser. --- ## Frequently asked questions: Tusk WhatsApp ### What is Tusk WhatsApp? Tusk WhatsApp lets you link a WhatsApp number to your TuskMail account and use it from a dashboard or from your own code over a REST API. You can send and receive messages, manage groups and templates, and receive webhooks. ### How do I link my number? Create a session, then scan a QR code from WhatsApp under Linked devices, or request a pairing code and enter it on the phone. You can do this in the web app or through the API. ### Do I need the WhatsApp Business app or a Meta approval? You link an existing WhatsApp number to a session, in the same way as WhatsApp Web. We do not require pre-approved message templates. Message people who expect to hear from you, because WhatsApp can restrict numbers that send unwanted messages. ### How do I authenticate API requests? With one API key, created on the API keys tab of the WhatsApp page in the web app. Send it in the X-API-Key header. Unlike the TuskMail email API there is no app password. ### How do I receive incoming messages? Register a webhook for a session and subscribe to message.received and the other events you need. Your server must be reachable on a public http or https URL. ### What happens when my plan or trial ends? The API is switched off at once and answers with 402 payment_required until you renew. Your sessions and keys are kept. ### Is it billed in KES? Yes. Plans are priced by number of sessions and billed in Kenyan shillings. --- ## Frequently asked questions: Tusk WhatsApp pricing ### How is Tusk WhatsApp priced? By the number of WhatsApp sessions, which is the number of WhatsApp numbers you can link: 1, 3, 6 or 10, with custom plans for more. Each extra session costs a little less, and 3-month and yearly plans cost less per month. Plans are billed in Kenyan shillings (KES), and the API and the dashboard are both included. ### Is there a free trial? Yes. Tusk WhatsApp comes with a 5 days free trial. Choose a plan, start the trial and add your first session. When the trial ends the plan must be paid for, or the dashboard and the API stop working. ### What happens when my plan ends? There is no grace period. When the paid time or the trial is over, the API answers with 402 payment_required and the dashboard asks you to renew. Your sessions and API keys are kept, so renewing restores access. ### What if I need more numbers than my plan includes? Creating a session beyond your plan's session count is refused. Delete a session you no longer need or upgrade to a plan with more sessions in the web app. ### How do I pay? Payments are made in KES by M-Pesa or card in the web app, the same way as TuskMail email plans. ### Can I try the API before paying? Yes, with the 5 days free trial. The API works only while a plan or trial is active. --- # Using TuskMail Source: https://tuskmail.africa/docs/tuskmail These guides cover setting up and running TuskMail for your business, from signing up to billing. ## Guides - StartGetting startedCreate a TuskMail business account, verify your email, add a domain and create your first mailbox. (https://tuskmail.africa/docs/tuskmail/getting-started) - StartAdding a domain and DNS recordsAdd your domain to TuskMail and publish the MX, SPF, DKIM and DMARC records so mail can be delivered and trusted. (https://tuskmail.africa/docs/tuskmail/domains-and-dns) - MailboxesGroup vs user accountsUnderstand TuskMail business accounts, user mailboxes and shared group mailboxes. (https://tuskmail.africa/docs/tuskmail/account-types) - MailboxesCreating mailboxesCreate user and group mailboxes on your domain and manage their members and passwords. (https://tuskmail.africa/docs/tuskmail/mailboxes) - Mail featuresConnect Gmail, Thunderbird and other appsUse TuskMail mail in Gmail, Thunderbird or any email app over IMAP and SMTP with an app password. (https://tuskmail.africa/docs/tuskmail/connect-other-apps) - Mail featuresApp passwordsCreate and revoke TuskMail app passwords for email apps and the TuskMail API. (https://tuskmail.africa/docs/tuskmail/app-passwords) - Mail featuresEmail footers and signaturesSet up a branded email footer with a live preview in TuskMail Settings. (https://tuskmail.africa/docs/tuskmail/footers) - Mail featuresGroup emails in SettingsSee the group addresses you belong to, and change the footer and profile picture used when anyone sends as them. (https://tuskmail.africa/docs/tuskmail/group-emails) - Mail featuresProfile picturesAdd a profile picture, crop it before saving, and see where other TuskMail users see it. (https://tuskmail.africa/docs/tuskmail/profile-pictures) - Mail featuresForwardingForward incoming TuskMail mail to other addresses and choose whether to keep a copy. (https://tuskmail.africa/docs/tuskmail/forwarding) - SecurityChanging and resetting passwordsChange your TuskMail password, reset a forgotten one, or reset a user mailbox password as the business owner. (https://tuskmail.africa/docs/tuskmail/passwords) - SecurityTwo-step sign-in and trusted devicesRequire new devices to be approved from a trusted TuskMail mobile app, and manage sessions and trusted devices. (https://tuskmail.africa/docs/tuskmail/two-step-sign-in) - Billing & MariaActivating Maria AITurn on Maria AI for your business and mailboxes, top up credit in KES and set monthly limits. (https://tuskmail.africa/docs/tuskmail/maria-ai) - Billing & MariaBilling, plans and renewalsHow TuskMail plans, mailbox limits, renewals, the 14-day grace period, coupons and M-Pesa and card payments work. (https://tuskmail.africa/docs/tuskmail/billing) --- # Getting started Source: https://tuskmail.africa/docs/tuskmail/getting-started To start, create a business account in the web app, verify your email with a code, then add your domain and create mailboxes. Adding a domain is free; mailboxes need a plan. ## How do I sign up? - Open the web app Go to https://app.tuskmail.africa/signup (https://app.tuskmail.africa/signup?product=mail). - Account Enter your work email and choose a password. - About you Enter your full name, optionally your company, your country and a phone number. The phone is used for billing receipts and account security. - Verify We send a verification code to your email. Enter it to finish. The email you sign up with becomes your business-owner login. It is separate from the mailboxes you create on your own domain. ## What do I do after signing up? - Add your domain and publish the DNS records. (https://tuskmail.africa/docs/tuskmail/domains-and-dns) - Pick a plan (https://tuskmail.africa/docs/tuskmail/billing) so you can create mailboxes. - Create user and group mailboxes. (https://tuskmail.africa/docs/tuskmail/mailboxes) - Open a mailbox in the inbox, or sign in on the mobile app. ## Where can I use TuskMail? In the browser at https://app.tuskmail.africa (https://app.tuskmail.africa), in the mobile apps, or in other email apps over IMAP and SMTP (see connect other apps (https://tuskmail.africa/docs/tuskmail/connect-other-apps)). The web app is light by default; you can switch to dark in Settings > Appearance. --- # Adding a domain and DNS records Source: https://tuskmail.africa/docs/tuskmail/domains-and-dns Add your domain in the web app, then publish four DNS records at your DNS host: MX, SPF, DKIM and DMARC. TuskMail shows the exact values to copy and checks them for you. ## How do I add a domain? - Open Domains In the web app, go to Domains and add your domain name, for example yourcompany.co.ke. - Copy the records The domain page lists every required record with its type, host and value. - Publish them at your DNS host Add each record where your domain's DNS is managed (your registrar or DNS provider). - Check DNS Use the Check DNS button. Each record is marked as OK or needing attention. DNS changes can take time to spread. ## Which DNS records are needed? Required DNS recordsName | Type | Description | MX | MX | Host: your domain. Value: mail.tuskmail.africa, priority 10. Routes incoming mail to TuskMail. | SPF | TXT | Host: your domain. Example: v=spf1 mx -all. Says which servers may send for your domain. | DKIM | TXT | A signing key record generated for your domain. Copy the exact name and value from the app; it is long. | DMARC | TXT | Host: _dmarc. Example: v=DMARC1; p=quarantine; rua=mailto:postmaster@yourcompany.co.ke. | ``` MX yourcompany.co.ke 10 mail.tuskmail.africa TXT yourcompany.co.ke v=spf1 mx -all TXT ._domainkey.yourcompany.co.ke TXT _dmarc.yourcompany.co.ke v=DMARC1; p=quarantine; rua=mailto:postmaster@yourcompany.co.ke ``` Use the values from your domain page These are examples. Always copy the exact records shown in the app, especially DKIM. ## What if my domain already has mail records? A domain should point to one mail provider. Replace old MX records with the TuskMail one, and merge your SPF into a single SPF record instead of publishing two. ## Why does a domain show as blocked? If the business subscription expires after the grace period, all its domains are blocked until payment. See billing (https://tuskmail.africa/docs/tuskmail/billing). --- # Group vs user accounts Source: https://tuskmail.africa/docs/tuskmail/account-types A business account owns domains and mailboxes. A user mailbox belongs to one person; a group mailbox is a shared address that team members open from their own sign-in. ## What is the difference? Business accountThe owner login. Manages domains, mailboxes, billing and Maria AI. User mailboxA personal address such as amina@yourcompany.co.ke, with its own sign-in and password. Group mailboxA shared address such as support@yourcompany.co.ke. It has no sign-in of its own; members open it from their own sign-in. ## How do group members work? Members of a group are user mailboxes on the same domain. Open the group in Email accounts or on the domain page to add or remove members at any time. Removing a member ends their access to the group without changing any password. ## Which should I choose? Use user mailboxes for people and group mailboxes for shared inboxes like sales@, info@ or support@, so no one has to share a password. --- # Creating mailboxes Source: https://tuskmail.africa/docs/tuskmail/mailboxes Create mailboxes from a domain's page or from Email accounts. You need an active plan, and the number of mailboxes is limited by your plan plus any extra mailboxes you bought. ## How do I create a mailbox? - Choose a domain Open Domains and select your domain. - Add a mailbox Choose whether it is a user or a group mailbox, enter the address name and a display name. For a user mailbox, set a password. - Add members (groups) For a group, select which user mailboxes are members. ## Why can't I create more mailboxes? If you have no plan, the plan has expired or you reached your limit, the app asks you to pick a plan or upgrade. You can buy extra mailboxes (https://tuskmail.africa/docs/tuskmail/billing) or change plan. Storage per mailbox follows your plan. ## How do I edit or remove a mailbox? Open the mailbox in Email accounts to edit its details, reset a user mailbox password, change group members or delete it. --- # Connect Gmail, Thunderbird and other apps Source: https://tuskmail.africa/docs/tuskmail/connect-other-apps Any app that supports IMAP and SMTP can use your TuskMail mailbox. Create an app password, then enter your full email address, that app password and the server mail.tuskmail.africa. ## How do I connect another app? - Create an app password In Settings > App passwords, create one for the mailbox. It is shown once, so copy it. - Add the account in your app In Gmail (Add another account), Thunderbird or Thundermail, choose a manual IMAP/SMTP setup. - Enter server details Use the values below, with your full address as the username and the app password as the password. ## Which server settings should I use? Incoming (IMAP)mail.tuskmail.africa, port 993 (SSL/TLS) Outgoing (SMTP)mail.tuskmail.africa, port 587 (STARTTLS) or 465 (SSL/TLS) UsernameYour full email address PasswordThe app password, not your sign-in password These are the standard ports. Check Settings in the web app for the current server details, which take priority. ## Can Gmail send as my TuskMail address? Yes, using Gmail’s option to check mail from another account and send as another address, with the same IMAP and SMTP details. Menu names change over time, so follow Gmail’s current instructions. --- # App passwords Source: https://tuskmail.africa/docs/tuskmail/app-passwords An app password is a separate password for one mailbox, used by other email apps and by the TuskMail API, so you never share your real password. ## How do I create an app password? - Open Settings > App passwords Pick the mailbox if you manage several. - Create Give it a name you will recognise, such as “Thunderbird laptop”. - Copy it now The password is shown only once. Store it in a password manager. ## Where can I use it? - IMAP and SMTP apps: see connect other apps (https://tuskmail.africa/docs/tuskmail/connect-other-apps). - The TuskMail API: send it as HTTP Basic credentials together with an API key from Settings > Developers. See the API docs (https://tuskmail.africa/docs/api). ## How do I revoke one? Delete it from the same list. Any app using it stops working immediately. Create one app password per app so you can revoke them separately. --- # Email footers and signatures Source: https://tuskmail.africa/docs/tuskmail/footers Go to Settings > Email footer, choose a design, fill in your details and watch the live preview. TuskMail adds the footer to mail you send, never to drafts. ## How do I set up a footer? - Switch it on Enable the footer for the mailbox. - Choose a design Simple, Classic, Card, Banner or Minimal. Or write your own, see below. - Add your details Name, job title, company, phone, website, address, tagline, disclaimer, social links and an accent colour. You can choose whether to show your email address. - Save Check the preview first. It is rendered the same way as on real emails. ## Can I design my own footer with HTML and CSS? Yes. At the bottom of Settings > Email footer, choose the quiet link Add custom footer design. Paste your HTML and your CSS in the two boxes and check the live preview. No JavaScript Scripts, event handlers such as onclick, forms, frames and similar tags are removed when your footer is saved and sent. Links must start with https://, mailto: or tel:, and images with https://. What to expect in a custom footerName | Type | Description | Layout | HTML | Use tables and simple tags (div, span, p, a, img, b, i, ul and so on). Tables are the most reliable in email apps. | Styling | CSS | Class, tag and id selectors work. We apply your CSS to the HTML as inline styles so it looks the same in Gmail, Outlook and others. | Not supported | CSS | Hover effects, animations, @media and @import rules, and fixed or absolute positioning are ignored. | ```
Jane Doe
Sales lead, Acme
``` ``` .name { font-weight: bold; color: #7a1f3d; font-size: 15px; } .role { color: #767676; } ``` To go back, choose Use a ready-made design instead. Your HTML and CSS stay saved until you change them. ## Does the footer appear on every email? It is added when you send. Drafts do not get it, and when you compose you can skip the footer for a single message. Footers are per mailbox. Group addresses have their own footer, see Group emails (https://tuskmail.africa/docs/tuskmail/group-emails). --- # Group emails in Settings Source: https://tuskmail.africa/docs/tuskmail/group-emails Go to Settings > Group emails to see every shared address you belong to, such as support@ or sales@. Open one to change its picture and its footer. ## Where are my group emails? Settings > Group emails lists the group addresses your mailbox is a member of, with their picture and whether their footer is on. If you are in no group, the page says so. Business owners pick which mailbox to look at first. ## How do I set a group picture? - Open the group Select the address in Settings > Group emails. - Pick the Picture tab Choose, drop or paste (Ctrl+V) an image, then crop it. - Save Other TuskMail users see this picture next to mail sent from the group address. ## How do I set a group footer? Open the Footer tab. It works like your own footer, including custom HTML and CSS designs (https://tuskmail.africa/docs/tuskmail/footers). The group footer is added when anyone sends as the group address, instead of their personal footer. ## Who can change these settings? Members of the group, the business owner and billing managers can change the picture and footer. Only the business owner and billing managers can change forwarding for a group. --- # Profile pictures Source: https://tuskmail.africa/docs/tuskmail/profile-pictures Add a picture in Settings > Profile. Other people using TuskMail see it next to your emails in their inbox. ## How do I add a picture? - Choose the image Select Upload picture, drop an image on the picture area, or copy an image and press Ctrl+V. - Crop it Drag to move, use the slider to zoom. The round outline shows how it will look. - Use picture Select Use picture to save. JPG, PNG, WebP and GIF up to 5 MB work. ## Who sees my picture? Other TuskMail users see it as the sender image on emails from you. People on other providers see their own mail app's image for you. ## How do I remove it? Select Remove next to the picture. Your initials are shown again. --- # Forwarding Source: https://tuskmail.africa/docs/tuskmail/forwarding Settings > Forwarding sends a copy of incoming mail to other addresses, up to 5, and lets you keep a copy in TuskMail. ## How do I turn on forwarding? - Open Settings > Forwarding Choose the mailbox. - Add addresses Enter up to 5 destination addresses. - Keep a copy Choose whether to keep the original in TuskMail. - Save Forwarding applies to new incoming mail. ## Why am I asked to unlock the mailbox? Changing forwarding needs the mailbox to be unlocked with its password, as a safety check. Business owners may see this prompt when managing a mailbox they do not sign in to directly. --- # Changing and resetting passwords Source: https://tuskmail.africa/docs/tuskmail/passwords Change your password in Settings > Security. If you forgot it, use Forgot password on the login page. Business owners can reset a user mailbox password from Email accounts. ## How do I change my password? Open Settings > Security, enter your current password and a new one of at least 8 characters. ## What if I forgot my password? Choose Forgot password on the login page and follow the emailed instructions to set a new one. ## How does an owner reset a user mailbox password? Open the user mailbox under Email accounts and set a new password. Group mailboxes have no password of their own. ## Do app passwords change too? No. App passwords are separate. Revoke any you no longer trust in App passwords (https://tuskmail.africa/docs/tuskmail/app-passwords). --- # Two-step sign-in and trusted devices Source: https://tuskmail.africa/docs/tuskmail/two-step-sign-in Turn on “Require approval from mobile app” in Settings > Security. New devices must then be approved on a trusted TuskMail mobile app before they can sign in. ## How do I enable it? - Sign in on the mobile app Make sure the phone is a trusted device. - Open Settings > Security Switch on Require approval from mobile app. - Sign in elsewhere A new device waits for you to approve or deny the request on your trusted phone. ## How do I manage trusted devices? Settings > Sessions & devices lists active sessions and known devices. You can sign out a session, trust or untrust a device, or remove it. Login history shows recent sign-in attempts. ## What if I lose my phone? Contact support at contact@tuskmail.africa (mailto:contact@tuskmail.africa). Trusted-device recovery steps are not described here. --- # Activating Maria AI Source: https://tuskmail.africa/docs/tuskmail/maria-ai Maria AI is turned on by the business, then enabled per mailbox. She runs on pay-as-you-go credit in KES, separate from your plan. ## How do I activate Maria? - Enable for the business The business owner, or a mailbox allowed to manage billing, switches Maria on under Billing > Maria AI. - Choose mailboxes For each mailbox, switch Inbox suggestions and Chat with Maria on or off. Users can also change these in Settings > Maria AI. - Add credit Top up so Maria can keep working. ## How does credit work? Top-ups are between KES 100 and KES 10,000. The quick amounts are 100, 250, 500, 1000. Usage is shown in KES, not tokens. A small free monthly allowance may apply, and credit is used after it. Pay with M-Pesa or card, the same way as plan payments. ## Can I limit spending? Yes. Set a monthly limit for the business, or remove it. When it is reached Maria pauses until the next month or until you raise it. When credit and allowance run out she also pauses. ## What does Maria do? Inbox suggestions offer short reply options when you open a message that needs a reply. Chat lets you ask Maria to search your mail and the group addresses you belong to, summarise and draft emails, and to find how to do things in TuskMail. You review everything before sending. --- # Billing, plans and renewals Source: https://tuskmail.africa/docs/tuskmail/billing TuskMail is billed in KES, and you can pay with M-Pesa or card. Adding a domain is free; you pay for a plan that sets your mailbox limit and storage per mailbox. ## How do plans work? Each plan has a mailbox limit, storage per mailbox, and prices for the periods it is sold for (for example monthly, 3 months or yearly). See current plans on the pricing page (https://tuskmail.africa/pricing); the live prices at checkout apply. ## How do I get more mailboxes? Buy extra mailboxes for the rest of the current period, or change to a bigger plan. A plan change is prorated with credit for unused time. You cannot move to a plan below your current usage. ## What happens if I don't renew? A late subscription becomes past due and keeps working for a 14-day grace period. After that it expires and your domains are blocked until payment. ## How do coupons work? Enter a code at checkout. A coupon may take a percentage off, a fixed KES amount off, or add trial days. It can be limited to some plans, expire, and be used once per business. The Starter plan has a 7 days free trial. ## How do I pay? Choose M-Pesa or card in the app. After payment the status updates automatically. Each payment gets an invoice and a receipt, which you can print or save as PDF from your browser. Only KES is charged for now. --- # TuskMail API Source: https://tuskmail.africa/docs/api The TuskMail API is a REST API for sending and reading email from code, without SMTP. It is built for businesses that want to automate order confirmations, notifications and inbox workflows from their own systems, using their own branded mailbox. ## What is the base URL? All requests go to one HTTPS base URL. Plain HTTP is not supported. ``` https://api.tuskmail.africa/v1 ``` Requests and responses are JSON, except attachment uploads (multipart/form-data) and attachment downloads (binary). ## What can I do with the API? - Send an email, or save it as a draft, with text, HTML, cc, bcc and attachments. - Reply to an existing message so it is threaded correctly. - List folders, list and search messages, and read a full message. - Mark messages read or starred, move them between folders, and delete them. - Upload attachments and download attachments from received mail. Each API key is bound to one user mailbox, so everything you do happens as that mailbox, with the same footer, folders and storage as in the TuskMail web app. ## How is the API secured? Every request carries two credentials: an API key in X-API-Key and the mailbox email plus an app password as HTTP Basic auth. Both are created by the mailbox owner in the web app. Read Authentication (https://tuskmail.africa/docs/api/authentication) for the details. Base URLhttps://api.tuskmail.africa/v1 AuthAPI key + HTTP Basic (mailbox email : app password) Rate limit60 requests per minute and 1,000 sent messages per day, per key ErrorsJSON with a code, a message and a request_id ## What does a request look like? This call returns the mailbox your credentials belong to. Replace the placeholders with your own values. Ready to build? Follow the quickstart (https://tuskmail.africa/docs/api/quickstart) to send your first message in a few minutes. --- # Quickstart Source: https://tuskmail.africa/docs/api/quickstart You need a TuskMail user mailbox, an API key and an app password. Then one request sends your first message. ## How do I get credentials? - Open Settings in the TuskMail web app Sign in as the mailbox you want to send from. API access is available for user mailboxes, not group mailboxes. - Create an app password Go to Settings, then App passwords, and create one. Copy it right away. You can give it an expiry and, optionally, restrict it to specific IP addresses. - Create an API key Go to Settings, then Developers, and create a key. It starts with tk_live_ and is shown once, so store it safely. Keep both secret Anyone holding the key and the app password can send as your mailbox. Keep them in environment variables or a secret manager, never in source control or client-side code. Set them in your shell: ``` export TK_API_KEY="tk_live_..." export MAILBOX_EMAIL="you@yourcompany.co.ke" export APP_PASSWORD="your-app-password" ``` ## How do I send my first message? Post a JSON body to /v1/messages. The response confirms the message was sent. ``` { "id": "Mabc123", "sent": true } ``` The sent message appears in the Sent folder of the mailbox. Use the messages reference (https://tuskmail.africa/docs/api/messages) to read, search and manage mail. ## What should I read next? - Authentication (https://tuskmail.africa/docs/api/authentication) for how the two credentials work together. - Rate limits (https://tuskmail.africa/docs/api/rate-limits) and errors (https://tuskmail.africa/docs/api/errors) to make your integration robust. - Idempotency (https://tuskmail.africa/docs/api/idempotency) to make retries safe. --- # Authentication Source: https://tuskmail.africa/docs/api/authentication Every request needs an API key in X-API-Key and an Authorization: Basic header holding the mailbox email and an app password. If either is missing or wrong, the API answers 401 invalid_credentials. ## Which two credentials are required? X-API-KeyYour key, starting with tk_live_. Created in Settings, then Developers. Shown once at creation. AuthorizationBasic base64(MAILBOX_EMAIL:APP_PASSWORD). The app password is created in Settings, then App passwords. The same app password also works for IMAP and SMTP clients such as Thunderbird or Gmail, so you can revoke access in one place. ``` GET /v1/me HTTP/1.1 Host: api.tuskmail.africa X-API-Key: tk_live_... Authorization: Basic bWFpbGJveEBleGFtcGxlLmNvLmtlOmFwcC1wYXNzd29yZA== ``` ## How is a key bound to a mailbox? Each API key is bound to a single user mailbox. The email in the Basic header must be that mailbox, otherwise the request is rejected with invalid_credentials. To work with several mailboxes, create a key and an app password for each one. ## What can I configure on an app password? When you create an app password you give it a description, and you can set an expiry in days and a list of allowed IP addresses. A request from an address that is not on the list gets 403 forbidden. Revoke an app password at any time in Settings, and it stops working immediately for the API and for mail clients. ## What happens on repeated failures? Repeated failed attempts from one IP address are throttled and answered with 429 rate_limited and a Retry-After header before credentials are even checked. A blocked domain (overdue subscription) returns 402 payment_required, and a disabled mailbox returns 403 forbidden. Basic auth over HTTPS is encrypted in transit. Never send credentials in a URL or log request headers. --- # Rate limits Source: https://tuskmail.africa/docs/api/rate-limits By default each API key can make 60 requests per minute and send 1,000 messages per day. Exceeding either returns 429 with a Retry-After header. ## What are the default limits? Requests60 per minute, per API key Sent messages1,000 per day, per API key (drafts do not count) Failed authenticationThrottled per client IP before credentials are checked Your key’s actual limits are returned by GET /v1/me (https://tuskmail.africa/docs/api/me) under limits. ## Which headers report my usage? Name | Type | Description | X-RateLimit-Limit | integer | Requests allowed in the current one-minute window. | X-RateLimit-Remaining | integer | Requests left in the current window. | X-RateLimit-Reset | unix timestamp | When the current window resets, in seconds since the epoch. | Retry-After | seconds | Only on 429 responses: how long to wait before retrying. | ``` HTTP/1.1 429 Too Many Requests Retry-After: 23 X-RateLimit-Limit: 60 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1767225600 X-Request-Id: req_8f2c1a9d03b44e7a1c5d { "error": { "code": "rate_limited", "message": "Rate limit of 60 requests per minute exceeded.", "request_id": "req_8f2c1a9d03b44e7a1c5d" } } ``` ## What happens at the daily sending limit? When a key reaches its daily send limit, POST /v1/messages returns 429 with the code daily_limit_exceeded and a Retry-After until the next day. Rejected attempts do not use up the allowance. Saving a draft (draft: true) does not count as a send. ## How should I retry? Wait for the number of seconds in Retry-After, then retry. For other transient failures such as 502 upstream_error, retry with exponential backoff and jitter, and always send an Idempotency-Key (https://tuskmail.africa/docs/api/idempotency) on sends so a retry can never deliver twice. Need higher limits? Contact support with your use case. --- # Errors Source: https://tuskmail.africa/docs/api/errors Failed requests return a non-2xx HTTP status and a JSON body of the form { "error": { "code", "message", "request_id" } }. Branch on code, show message to developers, and quote request_id to support. ## What does an error look like? ``` { "error": { "code": "validation_error", "message": "to: 'amina@' is not a valid email address", "request_id": "req_8f2c1a9d03b44e7a1c5d" } } ``` Every response, successful or not, carries an X-Request-Id header with the same identifier. ## Which error codes can I get? HTTP | code | Meaning | 400 | bad_request | The request could not be processed as sent. | 401 | invalid_credentials | Missing or wrong API key, mailbox email or app password, or the app password was revoked. | 402 | payment_required | The domain is blocked because the subscription is overdue. | 403 | forbidden | App password not allowed from this IP, or the mailbox is disabled. | 404 | not_found | Message, folder or attachment not found. | 413 | payload_too_large | An uploaded attachment is over 25 MB. | 422 | validation_error | A field is missing or invalid. The message names the field. | 429 | rate_limited | Too many requests. See Retry-After. | 429 | daily_limit_exceeded | The key's daily sending limit was reached. | 502 | upstream_error | The mail server could not complete the request. Safe to retry with backoff. | New error codes may be added over time. Treat unknown codes as a generic failure for their HTTP status. ## How should I handle errors? - 4xx (except 429): fix the request. Retrying will not help. - 429: wait for Retry-After, then retry. - 5xx: retry with exponential backoff, using an idempotency key for sends. --- # Idempotency Source: https://tuskmail.africa/docs/api/idempotency Add an Idempotency-Key header to POST /v1/messages and a retried request returns the original result instead of sending a second email. ## How does idempotency work? Choose a unique string per logical send, for example an order number plus the message purpose, or a random UUID. If the same key is sent again by the same API key, the API does not send again. It replays the first response with HTTP 200 and the header Idempotent-Replayed: true. ## What are the rules? - Keys can be up to 200 characters and are scoped to your API key. - Stored results are kept for 24 hours. - Only successful sends are remembered, so a request that failed can be retried with the same key. - The key identifies the request; it does not compare the body. Never reuse a key for a different message. ## When should I use it? Always, for sends. Network timeouts leave you unsure whether a message went out, and an idempotent retry resolves that safely. See best practices (https://tuskmail.africa/docs/api/best-practices). --- # Pagination Source: https://tuskmail.africa/docs/api/pagination GET /v1/messages is paginated with limit (1 to 100, default 25) and position (an offset, default 0). Follow next_position until it is null. ## What does a list response look like? ``` { "data": [ /* message objects */ ], "total": 140, "limit": 25, "position": 0, "next_position": 25 } ``` Name | Type | Description | data | array | The messages on this page. | total | integer | Total messages matching the filters. | limit / position | integer | Echo of the page size and offset you requested. | next_position | integer | null | Pass as position to get the next page. null on the last page. | ## How do I page through everything? ``` let position = 0; while (position !== null) { const res = await fetch(`https://api.tuskmail.africa/v1/messages?folder=inbox&limit=100&position=${position}`, { headers }); const page = await res.json(); for (const m of page.data) console.log(m.subject); position = page.next_position; } ``` Remember the rate limit (https://tuskmail.africa/docs/api/rate-limits): 60 requests per minute. --- # Account (me) Source: https://tuskmail.africa/docs/api/me Use GET /v1/me to verify credentials and read the limits that apply to your key. ## Retrieve the current mailbox GET/v1/me ``` { "email": "you@yourcompany.co.ke", "display_name": "Your Name", "domain": "yourcompany.co.ke", "api_key": { "id": 7, "name": "Order system", "prefix": "tk_live_a1b2c3" }, "limits": { "requests_per_minute": 60, "messages_per_day": 1000 } } ``` Name | Type | Description | email | string | The mailbox the key is bound to. | display_name | string | The mailbox display name. | domain | string | The domain the mailbox belongs to. | api_key | object | Key id, name and the non-secret prefix. The full key is never returned. | limits | object | requests_per_minute and messages_per_day for this key. | --- # Folders Source: https://tuskmail.africa/docs/api/folders Folders are the mailbox’s containers such as Inbox, Sent and Drafts. List them to get folder ids and counts. ## List folders GET/v1/folders ``` { "data": [ { "id": "b1", "name": "Inbox", "role": "inbox", "total": 140, "unread": 6 }, { "id": "b2", "name": "Sent", "role": "sent", "total": 312, "unread": 0 } ] } ``` Name | Type | Description | id | string | Folder id. Accepted anywhere a folder is requested. | name | string | Display name. | role | string | null | System role such as inbox, sent, drafts, junk or trash. Custom folders have no role. | total / unread | integer | Message counts. | Wherever the API takes a folder, you can pass a role name (inbox, sent, drafts, junk, trash) or a folder id. --- # List and get messages Source: https://tuskmail.africa/docs/api/messages List messages in a folder with filters and paging, or fetch one message to get its full body and bcc. ## The message object ``` { "id": "Mabc123", "thread_id": "Tabc123", "folder_ids": ["b1"], "from": [{ "name": "Amina Wanjiru", "email": "amina@example.com" }], "to": [{ "name": "", "email": "you@yourcompany.co.ke" }], "cc": [], "subject": "Quote request for Q4 deliveries", "received_at": "2025-01-15T18:45:00Z", "preview": "Hello, could you share your latest rates...", "is_read": false, "is_starred": false, "has_attachment": true, "size": 48213, "attachments": [ { "attachment_id": "B987", "name": "rates.pdf", "type": "application/pdf", "size": 40960 } ] } ``` Name | Type | Description | id | string | Message id. | thread_id | string | Conversation thread id. | folder_ids | string[] | Folders the message is in. | from / to / cc | address[] | Arrays of { "name", "email" }. | subject | string | Subject line. | received_at | string | ISO 8601 timestamp. | preview | string | Short plain-text preview. | is_read / is_starred | boolean | Flags. | has_attachment | boolean | Whether the message has attachments. | size | integer | Size in bytes. | attachments | object[] | attachment_id, name, type, size. | bcc, html, text, message_id | various | Only on the single-message GET. | ## List messages GET/v1/messages Name | Type | Description | folder | string | A role (inbox, sent, drafts, junk, trash) or a folder id. Omit to search all folders. | q | string | Search text. | unread | boolean | Only unread messages. | starred | boolean | Only starred messages. | limit | integer | 1 to 100. Default 25. | position | integer | Offset for paging. Default 0. | ``` { "data": [ { "id": "Mabc123", "thread_id": "Tabc123", "folder_ids": ["b1"], "from": [{ "name": "Amina Wanjiru", "email": "amina@example.com" }], "to": [{ "name": "", "email": "you@yourcompany.co.ke" }], "cc": [], "subject": "Quote request for Q4 deliveries", "received_at": "2025-01-15T18:45:00Z", "preview": "Hello, could you share your latest rates...", "is_read": false, "is_starred": false, "has_attachment": true, "size": 48213, "attachments": [ { "attachment_id": "B987", "name": "rates.pdf", "type": "application/pdf", "size": 40960 } ] } ], "total": 6, "limit": 10, "position": 0, "next_position": null } ``` See Pagination (https://tuskmail.africa/docs/api/pagination) for paging through large result sets. ## Retrieve a message GET/v1/messages/{id} Name | Type | Description | idrequired | string | Message id, in the path. | mark_read | boolean | If true, the message is also marked read. | ``` { "id": "Mabc123", "subject": "Quote request for Q4 deliveries", "bcc": [], "text": "Hello, could you share your latest rates...", "html": "

Hello, could you share your latest rates...

", "message_id": "", "...": "all other fields as in the message object" } ``` An unknown id returns 404 not_found. --- # Send a message Source: https://tuskmail.africa/docs/api/send-message Send an email from the mailbox with POST /v1/messages. It returns 201 with the new message id. Set draft: true to save without sending. ## Request POST/v1/messages Name | Type | Description | to | (string | {email, name?})[] | Recipients. Plain address strings or objects with an optional name. | cc, bcc | same as to | Optional. At most 100 recipients across to, cc and bcc. | subject | string | Up to 998 characters. | text | string | Plain-text body. | html | string | Optional HTML body. Send text as well for clients that do not render HTML. | reply_to_message_id | string | Reply to this message so it is threaded and the original is marked answered. | attachments | {attachment_id}[] | Up to 20 uploaded attachments. See Attachments (https://tuskmail.africa/docs/api/attachments). | draft | boolean | Save as a draft instead of sending. Recipients are optional for drafts. | A sent message needs at least one recipient and at least one of subject, text or html. Header: Idempotency-Key (optional but recommended, see Idempotency (https://tuskmail.africa/docs/api/idempotency)). ``` { "id": "Mnew456", "sent": true } ``` With draft: true the response is { "id": "...", "sent": false }. ## Reply to a message If your mailbox has an email footer enabled in Settings, it is applied to mail sent over the API in the same way as the web app. Drafts never get a footer. ## Limits and errors Sends count toward the daily sending limit (Rate limits (https://tuskmail.africa/docs/api/rate-limits)). Invalid addresses or missing fields return 422 validation_error naming the field. --- # Update and delete messages Source: https://tuskmail.africa/docs/api/update-message Change a message’s flags or folder with PATCH, and remove it with DELETE. ## Update a message PATCH/v1/messages/{id} Name | Type | Description | is_read | boolean | Mark read or unread. | is_starred | boolean | Star or unstar. | folder | string | Move to this folder (a role such as trash, or a folder id). | Send only the fields you want to change. The response is the updated message object. ## Delete a message DELETE/v1/messages/{id} Name | Type | Description | permanent | boolean | If true, delete permanently instead of moving to Trash. Default false. | Returns 204 No Content with no body. Permanent deletion cannot be undone. --- # Attachments Source: https://tuskmail.africa/docs/api/attachments To send a file, upload it first to get an attachment_id, then reference that id when you send. To read a file, download it from the message that carries it. ## Upload an attachment POST/v1/attachments Send a multipart/form-data request with the file in the file field. The maximum size is 25 MB, otherwise you get 413 payload_too_large. ``` { "attachment_id": "B987", "name": "invoice-2041.pdf", "type": "application/pdf", "size": 40960 } ``` ## Send a message with attachments Upload each file, then pass the ids in attachments on POST /v1/messages (https://tuskmail.africa/docs/api/send-message). Up to 20 attachments can be added to a message. Use the uploaded id soon after uploading: it stays usable for about 24 hours, and if it has expired you can still pass name and type next to the id. ``` { "to": ["amina@example.com"], "subject": "Invoice 2041", "text": "Attached.", "attachments": [{ "attachment_id": "B987" }] } ``` ## Download an attachment GET/v1/messages/{message_id}/attachments/{attachment_id} Get the attachment_id from the message’s attachments array, then request the file. The response body is the raw file, with the original name in Content-Disposition. An id that is not on that message returns 404 not_found. --- # Best practices and security Source: https://tuskmail.africa/docs/api/best-practices Treat the API key and app password like a password for your mailbox, call the API only from your server, and make every send idempotent. ## How do I protect my credentials? - Never expose keys client-side. Do not put the key or app password in browser code, mobile apps or public repositories. Call the API from your backend. - Store both in environment variables or a secret manager, and keep them out of logs. - Create a separate key and app password per system, with a clear name, so you can revoke one without affecting others. ## How do I rotate keys? Create a new key and app password, deploy them, confirm traffic works, then revoke the old ones in Settings. Revoked credentials stop working immediately. Set an expiry on app passwords where possible, and rotate on a schedule or whenever someone with access leaves. ## Can I restrict where requests come from? Yes. An app password can have an IP allow-list. Requests with that app password from any other address are rejected with 403 forbidden. Use it when your server has a fixed outbound IP. ## How do I make integrations reliable? - Send an Idempotency-Key (https://tuskmail.africa/docs/api/idempotency) on every send. - Honor Retry-After on 429, and back off with jitter on 5xx. - Log the request_id of failures so support can trace them. - Always send a plain-text body alongside HTML. - Set client timeouts (for example 30 seconds). --- # Changelog Source: https://tuskmail.africa/docs/api/changelog Notable changes to the TuskMail API. The current version is v1, served under /v1. ## v1.0: initial release - Authentication with X-API-Key plus HTTP Basic (mailbox email and app password). - Endpoints: /v1/me, /v1/folders, /v1/messages (list, get, send, update, delete) and /v1/attachments. - Rate limits, X-Request-Id, structured errors and idempotency keys for sends. --- # Tusk WhatsApp API Source: https://tuskmail.africa/docs/whatsapp-api The Tusk WhatsApp API is a REST API for sending and receiving WhatsApp messages from your own systems. You link a WhatsApp number to a session, then use one API key to send messages, read chats and receive events by webhook. ## What is the base URL? All requests go to one HTTPS base URL. Plain HTTP is not supported. ``` https://api.tuskmail.africa/v1/whatsapp ``` Requests and responses are JSON, except downloading a message’s media, which returns the file itself. ## What can I do with the API? - Create sessions, link a number by QR code or pairing code, and start, stop or log out a session. - Send text, images, video, audio, documents, locations, contact cards and polls, and reply, forward, react, edit or delete. - Send the same message to many people as an asynchronous bulk batch. - List chats, contacts and groups, read stored messages and fetch a chat’s history. - Create and use text templates with placeholders. - Receive incoming messages and other events at your own URL with webhooks. ## How does it fit together? Base URLhttps://api.tuskmail.africa/v1/whatsapp AuthOne API key in X-API-Key or as a Bearer token. No app password. BillingA Tusk WhatsApp plan priced by number of sessions, in KES. The API stops working when the plan ends. Rate limit120 requests per minute per key by default ErrorsJSON with a code, a message and a request_id Every message endpoint lives under a session, at /sessions/{sessionId}/..., and only works on sessions that belong to your account. Read Authentication (https://tuskmail.africa/docs/whatsapp-api/authentication) and Sessions (https://tuskmail.africa/docs/whatsapp-api/sessions) to learn more. ## What does a request look like? This call lists your sessions. Replace the key with your own. Ready to build? Follow the quickstart (https://tuskmail.africa/docs/whatsapp-api/quickstart) to send your first message. Not signed up yet? Create a Tusk WhatsApp account (https://app.tuskmail.africa/signup?product=whatsapp). --- # Quickstart Source: https://tuskmail.africa/docs/whatsapp-api/quickstart You need a Tusk WhatsApp plan (the 5 days free trial counts), an API key and a linked WhatsApp number. Then one request sends your first message. ## How do I get an API key? - Sign up and choose a plan Create an account at app.tuskmail.africa (https://app.tuskmail.africa/signup?product=whatsapp), then pick a plan on the WhatsApp page. You can pay, or start the 5 days free trial. - Open the API keys tab In the web app go to WhatsApp (https://app.tuskmail.africa/whatsapp), then the API keys tab, and create a key. It starts with tkw_live_ and is shown once, so copy it right away. Keep it secret Anyone with the key can send as your linked numbers. Keep it in an environment variable or a secret manager, never in source control or client-side code. ``` export TUSK_WA_KEY="tkw_live_..." ``` ## How do I create a session? A session is one WhatsApp number. Creating one is limited by your plan’s session count. ``` { "id": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "Sales", "phone": null, "status": "created", "status_label": "Created", "active": false, "created_at": "2026-01-15T09:30:00Z" } ``` Keep the id. It is the sessionId in every other path. ``` export SESSION_ID="9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10" ``` ## How do I link my number? Start the session, then link the number either by scanning a QR code or by entering a pairing code on the phone. Option 1: fetch the QR code and scan it from WhatsApp, under Settings, then Linked devices. Option 2: request a pairing code for the phone number, then enter it in WhatsApp under Linked devices, then Link with phone number. Poll GET /sessions/{sessionId} until status is ready. You can also link the number from the dashboard in the web app. See Sessions (https://tuskmail.africa/docs/whatsapp-api/sessions) for every status. ## How do I send my first message? Post to send-text with the recipient’s chat id, which is the phone number in international format without a plus, followed by @c.us. ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` The session must be ready. If it is not, the request fails. Check the status first, or handle the error and retry. WhatsApp can restrict numbers that send unwanted messages, so read Avoiding bans (https://tuskmail.africa/docs/whatsapp-api/avoiding-bans) before you send at volume. ## What should I read next? - Webhooks (https://tuskmail.africa/docs/whatsapp-api/webhooks) to receive incoming messages. - Plan and limits (https://tuskmail.africa/docs/whatsapp-api/plan-and-limits), rate limits (https://tuskmail.africa/docs/whatsapp-api/rate-limits) and errors (https://tuskmail.africa/docs/whatsapp-api/errors) to make your integration robust. - Send messages from a session (https://tuskmail.africa/docs/whatsapp-api/send-from-session) and the send messages reference (https://tuskmail.africa/docs/whatsapp-api/send-messages) for every message type. - Avoiding bans (https://tuskmail.africa/docs/whatsapp-api/avoiding-bans): how to warm up a number and send without getting it restricted. Read this before sending at volume. --- # Authentication Source: https://tuskmail.africa/docs/whatsapp-api/authentication Every request needs your API key in the X-API-Key header, or as Authorization: Bearer. Unlike the TuskMail email API, there is no app password. ## How do I send the key? X-API-KeyYour key, starting with tkw_live_. AuthorizationAlternatively, Bearer tkw_live_.... Use whichever your HTTP client prefers. ``` GET /v1/whatsapp/sessions HTTP/1.1 Host: api.tuskmail.africa X-API-Key: tkw_live_... ``` ## Where do I create keys? In the web app at app.tuskmail.africa/whatsapp (https://app.tuskmail.africa/whatsapp), on the API keys tab. The key is shown once at creation. You can have up to 10 active keys and revoke any of them at any time, and a revoked key stops working immediately. A key belongs to your account, so it can reach all of your sessions and none of anyone else’s. A session id that is not yours returns 404, exactly as if it did not exist. ## What happens when authentication fails? - A missing, wrong or revoked key returns 401 invalid_credentials. - A disabled account returns 403 forbidden. - A key whose WhatsApp plan is not active returns 402 payment_required. See plan and limits (https://tuskmail.africa/docs/whatsapp-api/plan-and-limits). - Repeated failed attempts from one IP address are throttled with 429 rate_limited before the key is checked. Always call the API over HTTPS and never log request headers. --- # Send messages from a session Source: https://tuskmail.africa/docs/whatsapp-api/send-from-session Once a session is ready, one authenticated POST sends a message from that number. This guide covers addressing, the response, the other message types, bulk sends and the errors you can expect. ## What do I need first? - A session with status ready. See Sessions and statuses (https://tuskmail.africa/docs/whatsapp-api/sessions). - An API key (tkw_live_...) sent in the X-API-Key header. See Authentication (https://tuskmail.africa/docs/whatsapp-api/authentication). - An active WhatsApp plan or trial. Expired or missing plans cannot send. Read this before you send at volume WhatsApp can restrict or ban numbers that send unwanted messages, and we cannot prevent it. Read Avoiding bans (https://tuskmail.africa/docs/whatsapp-api/avoiding-bans) first. ## How do I address the recipient? Use a JID in the chatId field. Person@c.us with digits only: country code first, no +, no spaces or dashes. For example 254700000001@c.us. Group@g.us, from the groups endpoints. As a convenience, a bare phone number in common formats (+254 700 000 001, 0700000001, 00254700000001) is also accepted and normalised to @c.us. A leading 0 is read as a Kenyan number, so prefer the full international form for anything else. See Chat ids (https://tuskmail.africa/docs/whatsapp-api/chat-ids). ## How do I send a text message? Post to send-text under the session. The base URL is the one on the introduction (https://tuskmail.africa/docs/whatsapp-api), and text may be 1 to 4096 characters. ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000001@c.us", "timestamp": 1768469400 } ``` Keep the message_id. You need it to reply to, react to, edit or delete the message, and to match later message.ack and message.failed webhook events. timestamp is Unix seconds and can be null. 201 means handed to WhatsApp, not delivered A number that is not on WhatsApp still returns 201. Check the number first (#check), and use webhooks for delivery status. Optional fields are linkPreview and quotedMessageId (send as a reply). ## Which other message types can I send? All of these live under /sessions/{sessionId}/messages/ and return the same 201 shape. Full parameters are in the send messages reference (https://tuskmail.africa/docs/whatsapp-api/send-messages). Endpoint | Body | send-text | chatId, text, linkPreview?, quotedMessageId? | send-image, send-video, send-document | chatId, url or base64 (exactly one), mimetype (required with base64), filename?, caption? (up to 1024), quotedMessageId? | send-audio | Same as media, plus ptt (true sends a voice note) | send-location | chatId, latitude, longitude, description?, address? | send-contact | chatId, contactName, contactNumber | send-poll | chatId, name, options (2 to 12 strings), allowMultipleAnswers? | react | chatId, messageId, emoji (empty string removes the reaction) | A media url must be a public http(s) address: localhost, private network addresses and URLs with credentials are rejected. ## How do I send to many people? Use send-bulk instead of looping over send-text. It accepts up to 100 messages, returns 202 Accepted straight away, and sends them one at a time with a pause between each. Set options.delayBetweenMessages, randomizeDelay and stopOnError, then poll the batch for progress. Bulk is a delivery convenience, not permission to blast. Send it only to people who opted in, keep the delay generous, and use stopOnError. See Avoiding bans (https://tuskmail.africa/docs/whatsapp-api/avoiding-bans). ## How do I check a number first? Call the contact check before the first message to a new number. It tells you whether the number is on WhatsApp and gives you the chat id to use. Checks are limited to 30 per minute per session. ``` { "number": "254700000001", "exists": true, "chat_id": "254700000001@c.us" } ``` When exists is false, chat_id is null. Do not send to it, and remove it from your list. ## Which errors can sending return? Errors use the usual { "error": { "code", "message", "request_id" } } envelope. HTTP | code | What to do | 402 | subscription_required | No WhatsApp plan yet. Pick a plan in the web app. | 402 | subscription_expired | The plan or trial ended. Renew to send. The API can also answer payment_required at authentication. | 409 | session_not_ready | The session is not connected. Check its status, relink or start it, then retry. | 429 | rate_limited | Over 60 messages per minute for this session. Wait and retry, and slow down well below it. | 422 | invalid_phone | The number could not be read. Use digits with the country code. | 422 | invalid_chat_id | The chat id is malformed. | 422 | validation_error | A field is missing or invalid, for example text over 4096 characters or fewer than 2 poll options. | 400 | bad_request | WhatsApp rejected the request. | 404 | not_found | The session does not exist or is not yours. | 413 | payload_too_large | The body or media is too large. Send a url instead of base64. | 502 | gateway_unavailable | Temporary problem on our side. Retry with backoff, and check history before resending. | The limit is 60 messages per minute per session. That is a ceiling on our side, not a safe sending rate for WhatsApp. --- # Avoiding bans Source: https://tuskmail.africa/docs/whatsapp-api/avoiding-bans WhatsApp decides who gets restricted, and it does not publish its rules or thresholds. Nothing here is a guarantee. These habits lower the risk, mostly by making your messaging look like what it should be: wanted conversations with real people. ## Can the API guarantee my number is safe? No. Tusk WhatsApp links your own number as a device. WhatsApp can limit, restrict or permanently ban any number, and a ban is usually not reversible by us. Treat the number as something you can lose, and do not link one your business cannot do without while you are testing. Do not use this for spam Unsolicited bulk messaging, cold lists and misleading content break WhatsApp’s terms and ours. Accounts used that way can be suspended. ## How do I start with a new number? - Warm it up. A brand-new number that suddenly sends many messages looks suspicious. Use it normally for a while first: chat with real contacts and let people reply. - Start small. Send a few dozen messages a day to people who know you, then raise volume gradually over weeks, not hours. - Avoid sudden spikes. A jump from 20 messages a day to 2,000 is a classic trigger. Grow in steps and keep the pattern steady. - Keep the profile complete. Set a name, a photo and a business description so the number looks like a real, identifiable sender. ## Who should I message? - Only people who opted in. They asked to hear from you, for example at checkout or by messaging you first. Keep a record of when and how. - No cold lists. Do not message bought, scraped or guessed numbers. People who do not know you will report or block you, and blocks and reports are the strongest signal WhatsApp uses. - Check numbers first. Use contacts/check (https://tuskmail.africa/docs/whatsapp-api/send-from-session#check) and skip numbers that are not on WhatsApp. Sending to many missing numbers is itself a bad signal. - Handle opt-outs. Tell people how to stop (for example “Reply STOP”), listen for message.received webhooks, and stop messaging anyone who asks, immediately and permanently. ## How should I send? - Vary your content. Hundreds of identical messages look automated. Personalise with the name or an order detail, and use templates (https://tuskmail.africa/docs/whatsapp-api/templates) with real variables rather than one fixed blast. - Pace sends. Our limit is 60 messages per minute per session, but that is a technical ceiling. Stay far below it. With bulk, use a long delayBetweenMessages and keep randomizeDelay on. - Prefer conversations. Messages people reply to build the number’s reputation. Messages that get ignored or blocked erode it. - Be clear who you are. Say who is writing and why in the first message. Avoid link-only messages and heavy use of shortened URLs. ## What should I monitor? - Delivery failures. Subscribe to message.failed and message.ack in webhooks (https://tuskmail.africa/docs/whatsapp-api/webhooks). A rising failure rate means stop and investigate. - Session warnings. Watch session.restriction, session.disconnected and session.reconnect_loop events. On a restriction, stop sending from that number. - API errors. Do not retry in a loop. Stop a campaign on repeated 400, 409 or session_not_ready errors and find the cause. For bulk, enable stopOnError. - Blocks and complaints. If people block you or ask why they are getting messages, your audience or content is wrong. Fix that before sending more. ## Quick checklist - Number warmed up, profile complete. - Every recipient opted in, with a record. - Numbers checked before first send. - Content varied and personalised, not identical. - Volume ramped gradually, delays generous, far below 60 per minute. - STOP requests honoured automatically. - Failures and restriction events monitored, with an automatic stop. Ready to send? Go to Send messages from a session (https://tuskmail.africa/docs/whatsapp-api/send-from-session). --- # Sessions and statuses Source: https://tuskmail.africa/docs/whatsapp-api/sessions A session is one WhatsApp number linked to your account. Everything else in the API happens inside a session, so you create one first, link a number to it, and keep it ready. ## What does a session look like? ``` { "id": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "Sales", "phone": "254700000001", "status": "ready", "status_label": "Ready", "active": true, "created_at": "2026-01-15T09:30:00Z" } ``` Name | Type | Description | id | string | The session id. Use it as sessionId in paths. | name | string | The label you gave the session, up to 80 characters. | phone | string | null | The linked number, or null until a number is linked. | status | string | Machine-readable status. See the table below. | status_label | string | Human-readable status for display. | active | boolean | Whether the session is currently connected and usable. | created_at | string | ISO 8601 creation time in UTC. | ## Which statuses can a session have? status | Meaning | created | The session exists but has not been started. | initializing | The session is starting up. | qr_ready | A QR code is available. Scan it, or request a pairing code instead. | authenticating | The number is being linked. | ready | Linked and connected. You can send and receive messages. | disconnected | The connection dropped. Start the session again. | action_required | The session needs attention before it can be used. Check the dashboard, and if it does not clear, log out and link the number again. | failed | The session could not start. Try force-kill and start again, and if it keeps failing, log out and link the number again. | Send messages only while the status is ready. Subscribe to the session.status webhook event to react to changes without polling. ## How do I link a number? After POST /sessions/{sessionId}/start, the session reaches qr_ready. Link it in one of two ways: - QR code. GET /sessions/{sessionId}/qr returns the code to scan from WhatsApp, under Linked devices. - Pairing code. POST /sessions/{sessionId}/pairing-code with the phone number returns an 8-character code you enter on the phone, under Linked devices, then Link with phone number. Handy when you cannot scan a screen. ## How do I manage a session over time? startStarts a stopped or new session. A previously linked number reconnects without a new QR code. stopDisconnects the session but keeps the linked number. logoutUnlinks the number. You need to link again to use the session. force-killTerminates a stuck session. Start it again afterwards. DELETEDeletes the session and frees a slot on your plan. The number of sessions you can create depends on your plan. See plan and limits (https://tuskmail.africa/docs/whatsapp-api/plan-and-limits). --- # Chat ids and phone numbers Source: https://tuskmail.africa/docs/whatsapp-api/chat-ids Messages are addressed with a chat id, not a plain phone number. For a person it is the number in international format, without a plus or spaces, followed by @c.us. ## What do chat ids look like? Person254700000002@c.us for +254 700 000 002. Group120363000000000000@g.us. Group ids come from the groups endpoints. The chat list endpoint reports a kind for every chat: individual, group, channel, status, broadcast or unknown. ## How do I check that a number is on WhatsApp? Call GET /sessions/{sessionId}/contacts/check/{number} with the number in international format, without a plus. The response includes exists and the chat_id to use (null when the number is not on WhatsApp). Checks are limited to 30 per minute per session. ``` { "number": "254700000002", "exists": true, "chat_id": "254700000002@c.us" } ``` Send only to people who expect it WhatsApp can restrict numbers that send unwanted messages. Message people who have opted in, keep a gap between bulk messages, and be ready to stop when asked. See Avoiding bans (https://tuskmail.africa/docs/whatsapp-api/avoiding-bans). --- # Plan and limits Source: https://tuskmail.africa/docs/whatsapp-api/plan-and-limits The API works while your Tusk WhatsApp plan is active, including during a free trial. The moment the plan or trial ends, every request is refused with 402 payment_required. ## How are plans priced? Plans are priced by the number of sessions you can link, for example one session or four, and billed in Kenyan shillings. Tusk WhatsApp comes with a 5 days free trial. Plans and prices are set by TuskMail and can change, so the pricing page (https://tuskmail.africa/pricing/whatsapp-api) and the plan picker in the web app show the current ones. ## What happens when the plan is not active? There is no grace period. Once paid time or a trial is over, the API answers every request with this error until you renew in the web app. ``` { "error": { "code": "payment_required", "message": "Your Tusk WhatsApp plan is not active. Renew it to use the API.", "request_id": "req_8f2c1a9d03b44e7a1c5d" } } ``` Your sessions and keys are kept, so renewing restores access. ## What happens at the session limit? Creating a session beyond your plan’s session count returns 402 with the code session_limit. Delete a session you no longer need, or upgrade your plan in the web app. ``` { "error": { "code": "session_limit", "message": "Your Starter plan includes 1 session. Upgrade to link more numbers.", "request_id": "req_8f2c1a9d03b44e7a1c5d" } } ``` Handle 402 by reading error.code, since payment_required and session_limit need different fixes. No plan yet? Create an account (https://app.tuskmail.africa/signup?product=whatsapp) and choose one. --- # Rate limits Source: https://tuskmail.africa/docs/whatsapp-api/rate-limits By default each API key can make 120 requests per minute. Exceeding it returns 429 rate_limited with a Retry-After header. ## Which headers report my usage? Name | Type | Description | X-RateLimit-Limit | integer | Requests allowed in the current one-minute window. | X-RateLimit-Remaining | integer | Requests left in the current window. | X-RateLimit-Reset | unix timestamp | When the current window resets, in seconds since the epoch. | Retry-After | seconds | Only on 429 responses: how long to wait before retrying. | ``` HTTP/1.1 429 Too Many Requests Retry-After: 23 X-RateLimit-Limit: 120 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1768469460 { "error": { "code": "rate_limited", "message": "Rate limit of 120 requests per minute exceeded.", "request_id": "req_8f2c1a9d03b44e7a1c5d" } } ``` ## Is this the limit on messages per minute? No. The rate limit counts API requests. Sending is also capped at 60 messages per minute per session (429 rate_limited), and WhatsApp has its own, unpublished limits on how fast a number can message people, so stay far below that cap. To send to many people, use bulk sending (https://tuskmail.africa/docs/whatsapp-api/send-messages#bulk), which spaces messages out for you, instead of looping over send-text. ## How should I retry? Wait for Retry-After seconds and retry. For 502 upstream_error, retry with exponential backoff and jitter. Do not blindly retry sends that may have gone through, since the API has no idempotency keys: check the chat history first. --- # Errors Source: https://tuskmail.africa/docs/whatsapp-api/errors Failed requests return a non-2xx status and a JSON body of the form { "error": { "code", "message", "request_id" } }. Branch on code, and quote request_id to support. ## What does an error look like? ``` { "error": { "code": "not_found", "message": "Session not found", "request_id": "req_8f2c1a9d03b44e7a1c5d" } } ``` ## Which error codes can I get? HTTP | code | Meaning | 400 | bad_request | The request could not be processed, for example the body is not valid JSON. | 401 | invalid_credentials | Missing, wrong or revoked API key. | 402 | payment_required | Your WhatsApp plan is not active. Renew it. | 402 | subscription_required | Sending or webhooks need a WhatsApp plan, and you have none. | 402 | subscription_expired | Your plan or trial ended. Sending and webhooks stop until you renew. | 403 | webhook_limit | You reached the number of webhooks your plan includes. | 409 | session_not_ready | The session is not connected, so it cannot send. | 422 | invalid_webhook_url | The webhook URL is not https on a public host. | 422 | invalid_phone | The phone number could not be read. | 422 | invalid_chat_id | The chat id is malformed. | 502 | gateway_unavailable | Temporary problem reaching WhatsApp. Retry with backoff. | 402 | session_limit | You reached the number of sessions your plan includes. | 403 | forbidden | The account is disabled. | 404 | not_found | Unknown session, chat, group, webhook, template or message. Also returned for sessions that belong to someone else. | 409 | conflict | The request conflicts with the current state of the resource. | 413 | payload_too_large | The request body is over 30 MB. | 422 | validation_error | A field is missing or invalid, including a webhook URL that is not public. | 429 | rate_limited | Too many requests. See Retry-After. | 501 | not_supported | The operation is not supported for this session. | 502 | upstream_error | WhatsApp could not complete the request. Safe to retry with backoff. | New error codes may be added over time. Treat unknown codes as a generic failure for their HTTP status. ## How should I handle errors? - 4xx (except 429): fix the request or the account state. Retrying will not help. - 429: wait for Retry-After, then retry. - 5xx: retry with exponential backoff. --- # Webhooks and events Source: https://tuskmail.africa/docs/whatsapp-api/webhooks A webhook sends an HTTP POST to your server when something happens, such as an incoming message. It is the best way to receive messages and delivery updates, so you do not have to poll. Webhooks need a running plan. ## How do I create a webhook? Register an HTTPS URL and the events you want. Use ["*"] for every event. A webhook covers all of your sessions unless you set session_id to one session id. You can choose your own secret (16 to 100 characters). If you leave it out, one is generated. The response includes the full secret once, and again only when you rotate it. Store it right away. ``` { "id": "5b0e9c3a-7d1f-4a62-9c1e-2f8a6d4b7e31", "url": "https://example.com/hooks/whatsapp", "events": ["message.received", "message.ack", "message.failed"], "active": true, "session_id": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "secret": "whsec_generated_once_16plus_chars", "consecutive_failures": 0 } ``` URL rules The URL must be https on a public host. Localhost, private and link-local addresses and user:pass@ URLs are rejected with 422 invalid_webhook_url. Redirects are not followed. To develop locally, expose your machine with an HTTPS tunnel. An unknown event name returns 422 validation_error. ## Which events can I subscribe to? Event | Fires when | message.received | A message arrives. | message.sent | A message is sent from the linked number. | message.ack | A message delivery or read status changes. | message.failed | A message could not be sent. | message.revoked | A message is deleted for everyone. | message.reaction | Someone reacts to a message. | message.edited | A message is edited. | status.received | A status update is received. | session.status | The session status changes. | session.qr | A new QR code is available. | session.authenticated | The number was linked. | session.disconnected | The session disconnected. | session.reconnect_loop | The session keeps failing to reconnect. | session.restriction | WhatsApp restricted the number. | presence.update | A contact's online or typing presence changes. | group.join | Someone joins a group. | group.leave | Someone leaves a group. | group.update | Group details change. | group.join_request | Someone asks to join a group. | call.received | An incoming call arrives. | call.accepted | A call is accepted. | call.rejected | A call is rejected. | call.missed | A call is missed. | webhook.test | You send a test ping. Always delivered to the tested webhook. | * | Every event above. | ## What does a delivery look like? Each delivery is a POST with a JSON body and these headers. X-Tusk-EventThe event name, such as message.received. X-Tusk-DeliveryThe event id. Same as id in the body. Use it to ignore duplicates. X-Tusk-TimestampUnix time in seconds when the delivery was signed. X-Tusk-Signaturesha256=, see below. ``` { "id": "evt_8f2c1a9d03b44e7a", "event": "message.received", "session_id": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "created_at": "2026-01-15T09:30:00Z", "data": { } } ``` data carries the event details from WhatsApp, and its fields vary by event. Use the webhook.test ping to see a real delivery. Respond with a 2xx within 8 seconds, and do heavy work afterwards. ## How do I verify the signature? The signature is HMAC-SHA256(secret, timestamp + "." + rawBody), hex encoded and prefixed with sha256=. Use the raw request bytes, before any JSON parsing, and compare in constant time. Also reject timestamps that are more than a few minutes old to stop replays. ## What about retries and failures? - A failed delivery is attempted up to 3 times. Responses in the 4xx range are not retried, except 408 and 429. - After 25 consecutive failures the webhook is switched off. Fix your endpoint, then call POST /webhooks/{id}/enable, or enable it in the web app. - Deliveries can repeat or arrive out of order, so make your handler idempotent using X-Tusk-Delivery. - POST /webhooks/{id}/test sends a signed webhook.test and returns success, status_code, error and duration_ms. GET /webhooks/{id}/deliveries shows the last 100 attempts. ## How many webhooks can I have? The number depends on your plan (3 by default, and plans can include more). Creating one over the limit returns 403 webhook_limit. Without a running plan the limit is 0, and creating or receiving webhooks returns 402 subscription_required or subscription_expired. Check the plan picker in the web app for the current numbers. See the webhooks reference (https://tuskmail.africa/docs/whatsapp-api/webhooks-reference) for every endpoint. --- # Sessions Source: https://tuskmail.africa/docs/whatsapp-api/sessions-reference Sessions are the numbers linked to your account. See Sessions and statuses (https://tuskmail.africa/docs/whatsapp-api/sessions) for the session object and the status values. ## List sessions GET/v1/whatsapp/sessions Returns every session on your account. ``` { "data": [ { "id": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "Sales", "phone": "254700000001", "status": "ready", "status_label": "Ready", "active": true, "created_at": "2026-01-15T09:30:00Z" } ] } ``` ## Create a session POST/v1/whatsapp/sessions Creates a session. It is limited by your plan’s session count, and beyond it the API returns 402 session_limit. BodyName | Type | Description | name | string | A label for the session, up to 80 characters. Optional. | ``` { "id": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "Sales", "phone": null, "status": "created", "status_label": "Created", "active": false, "created_at": "2026-01-15T09:30:00Z" } ``` ## Get a session GET/v1/whatsapp/sessions/{sessionId} Returns the session with its current status. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | ``` { "id": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "Sales", "phone": "254700000001", "status": "ready", "status_label": "Ready", "active": true, "created_at": "2026-01-15T09:30:00Z" } ``` ## Delete a session DELETE/v1/whatsapp/sessions/{sessionId} Deletes the session and frees a slot on your plan. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | ``` { "deleted": true } ``` ## Start a session POST/v1/whatsapp/sessions/{sessionId}/start Starts the session. Returns the session object. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | ``` { "id": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "Sales", "phone": null, "status": "initializing", "status_label": "Initializing", "active": false, "created_at": "2026-01-15T09:30:00Z" } ``` ## Stop a session POST/v1/whatsapp/sessions/{sessionId}/stop Stops the session and keeps the linked number. Returns the session object. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | ## Log out and unlink the number POST/v1/whatsapp/sessions/{sessionId}/logout Unlinks the number from the session. You must link a number again to use the session. Returns the session object. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | ## Force-kill a stuck session POST/v1/whatsapp/sessions/{sessionId}/force-kill Terminates a session that is stuck. Start it again afterwards. Returns the session object. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | ## Get the QR code GET/v1/whatsapp/sessions/{sessionId}/qr Returns the QR code to scan from WhatsApp, under Linked devices. It is available while the status is qr_ready, and the code refreshes, so fetch it again or watch the session.qr event if it expires. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | ``` { "qrCode": "2@AbCdEf...", "status": "qr_ready" } ``` The qrCode value is the data to encode as a QR image with any QR library. ## Get a pairing code POST/v1/whatsapp/sessions/{sessionId}/pairing-code Returns an 8-character code to link by phone number instead of scanning a QR code. Enter it on the phone in WhatsApp, under Linked devices, then Link with phone number. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | phoneNumberrequired | string | The number to link, in international format without a plus. | ``` { "pairingCode": "ABCD-EFGH", "status": "qr_ready" } ``` --- # Chats Source: https://tuskmail.africa/docs/whatsapp-api/chats Chats are your conversations with people and groups. A chat’s id is the chat id used everywhere else. See chat ids (https://tuskmail.africa/docs/whatsapp-api/chat-ids). ## List chats GET/v1/whatsapp/sessions/{sessionId}/chats Returns recent chats. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | limit | integer | Maximum chats to return, from 1 to 1000. Defaults to 1000. | offset | integer | Number of chats to skip, for paging. | ``` [ { "id": "254700000002@c.us", "name": "Amina", "isGroup": false, "kind": "individual", "unreadCount": 2, "timestamp": 1768469400, "lastMessage": "Thanks!", "archived": false, "pinned": false, "muted": false } ] ``` ## Mark a chat as read POST/v1/whatsapp/sessions/{sessionId}/chats/read Marks a chat, or specific messages in it, as read. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The chat to mark as read. | messageIds | string[] | Specific message ids. Omit to mark the whole chat. | ``` { "success": true } ``` ## Show typing or recording POST/v1/whatsapp/sessions/{sessionId}/chats/typing Shows a typing or recording indicator in a chat, or clears it with paused. Handy while a human or a bot prepares a reply. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The chat to show the indicator in. | staterequired | string | One of typing, recording or paused. | ``` { "success": true } ``` --- # Send messages Source: https://tuskmail.africa/docs/whatsapp-api/send-messages Every send returns 201 with a message_id, the normalised chat_id and a timestamp (Unix seconds, may be null). 201 means handed to WhatsApp, not delivered. Use the message_id to reply to, react to, edit or delete the message later. The session must be ready. New here? Read Send messages from a session (https://tuskmail.africa/docs/whatsapp-api/send-from-session) and Avoiding bans (https://tuskmail.africa/docs/whatsapp-api/avoiding-bans). ## Send a text message POST/v1/whatsapp/sessions/{sessionId}/messages/send-text Sends a text message. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | textrequired | string | The message text. | mentions | string[] | Chat ids to @mention. The text must also contain the @number token. | linkPreview | boolean | Whether to attach a preview for links in the text. | quotedMessageId | string | Send as a reply to this message id. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Send an image POST/v1/whatsapp/sessions/{sessionId}/messages/send-image Sends an image. Provide the file as a public url or as base64. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | url | string | A public URL of the image. Send either url or base64. | base64 | string | The image as a base64 string. Send either url or base64. | mimetype | string | The media type, for example image/jpeg. Recommended with base64. | filename | string | A file name. | caption | string | Text shown with the media. | mentions | string[] | Chat ids to @mention. The text must also contain the @number token. | quotedMessageId | string | Send as a reply to this message id. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Send a video POST/v1/whatsapp/sessions/{sessionId}/messages/send-video Sends a video. Provide the file as a public url or as base64. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | url | string | A public URL of the video. Send either url or base64. | base64 | string | The video as a base64 string. Send either url or base64. | mimetype | string | The media type, for example image/jpeg. Recommended with base64. | filename | string | A file name. | caption | string | Text shown with the media. | mentions | string[] | Chat ids to @mention. The text must also contain the @number token. | quotedMessageId | string | Send as a reply to this message id. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Send an audio or voice message POST/v1/whatsapp/sessions/{sessionId}/messages/send-audio Sends an audio file or a voice message. Provide the file as a public url or as base64. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | url | string | A public URL of the audio. Send either url or base64. | base64 | string | The audio as a base64 string. Send either url or base64. | mimetype | string | The media type, for example image/jpeg. Recommended with base64. | filename | string | A file name. | caption | string | Text shown with the media. | mentions | string[] | Chat ids to @mention. The text must also contain the @number token. | quotedMessageId | string | Send as a reply to this message id. | ptt | boolean | Send as a voice note (push to talk). | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Send a document POST/v1/whatsapp/sessions/{sessionId}/messages/send-document Sends a document or any file. Provide the file as a public url or as base64. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | url | string | A public URL of the file. Send either url or base64. | base64 | string | The file as a base64 string. Send either url or base64. | mimetype | string | The media type, for example image/jpeg. Recommended with base64. | filename | string | A file name. | caption | string | Text shown with the media. | mentions | string[] | Chat ids to @mention. The text must also contain the @number token. | quotedMessageId | string | Send as a reply to this message id. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Send a location POST/v1/whatsapp/sessions/{sessionId}/messages/send-location Sends a map location. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | latituderequired | number | Latitude in degrees. | longituderequired | number | Longitude in degrees. | description | string | A name for the place. | address | string | A street address. | quotedMessageId | string | Send as a reply to this message id. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Send a contact card POST/v1/whatsapp/sessions/{sessionId}/messages/send-contact Sends a contact card. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | contactNamerequired | string | The name on the card. | contactNumberrequired | string | The phone number on the card. | quotedMessageId | string | Send as a reply to this message id. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Send a poll POST/v1/whatsapp/sessions/{sessionId}/messages/send-poll Sends a native WhatsApp poll. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | namerequired | string | The poll question. | optionsrequired | string[] | The answer options. | allowMultipleAnswers | boolean | Let people pick more than one option. | quotedMessageId | string | Send as a reply to this message id. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Send a template POST/v1/whatsapp/sessions/{sessionId}/messages/send-template Renders one of your stored templates (https://tuskmail.africa/docs/whatsapp-api/templates) and sends it as a text message. Give templateId or templateName. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The recipient chat id. | templateId | string | The template id. Send this or templateName. | templateName | string | The template name. Send this or templateId. | vars | object | Values for the {{placeholder}} tokens in the template. | mentions | string[] | Chat ids to @mention. | linkPreview | boolean | Whether to attach a link preview. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Reply to a message POST/v1/whatsapp/sessions/{sessionId}/messages/reply Sends a text that quotes an earlier message. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The chat the message is in. | quotedMessageIdrequired | string | The id of the message to reply to. | textrequired | string | The reply text. | mentions | string[] | Chat ids to @mention. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Forward a message POST/v1/whatsapp/sessions/{sessionId}/messages/forward Forwards a message to another chat. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | fromChatIdrequired | string | The chat the message is in. | toChatIdrequired | string | The chat to forward it to. | messageIdrequired | string | The message to forward. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## React to a message POST/v1/whatsapp/sessions/{sessionId}/messages/react Adds a reaction to a message, or removes it. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The chat the message is in. | messageIdrequired | string | The message to react to. | emojirequired | string | The reaction emoji. Send an empty string to remove your reaction. | ``` { "success": true } ``` ## Edit a sent message POST/v1/whatsapp/sessions/{sessionId}/messages/edit Edits the text of a message your number sent. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The chat the message is in. | messageIdrequired | string | The message to edit. | bodyrequired | string | The new text. | mentions | string[] | Chat ids to @mention. | ``` { "message_id": "3EB0A1B2C3D4E5F60789", "chat_id": "254700000002@c.us", "timestamp": 1768469400 } ``` ## Delete a message POST/v1/whatsapp/sessions/{sessionId}/messages/delete Deletes a message. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | string | The chat the message is in. | messageIdrequired | string | The message to delete. | forEveryone | boolean | Delete for everyone in the chat, not just for you. | ``` { "success": true } ``` ## Send in bulk POST/v1/whatsapp/sessions/{sessionId}/messages/send-bulk Queues up to 100 messages to different recipients as one asynchronous batch and returns at once with 202 Accepted. Messages are sent one at a time with a pause between them. Poll the batch for progress, or cancel it. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | messagesrequired | object[] | Up to 100 items. Each has chatId, a type (text, image, video, audio or document), and content: text for text, or an object such as image with url or base64, plus an optional caption (up to 1024 characters). Text is up to 4096 characters. Optional variables are substituted into the text. Exact duplicate items are collapsed. | batchId | string | Your own batch id. Generated if omitted. | options.delayBetweenMessages | integer | Pause between messages in milliseconds, 1000 to 60000. Defaults to 3000. | options.randomizeDelay | boolean | Add a random 0 to 2 seconds to each pause. Defaults to true. | options.stopOnError | boolean | Stop the batch at the first failure. Defaults to false. | ``` { "batchId": "batch_01", "status": "pending", "totalMessages": 2, "estimatedCompletionTime": "2026-01-15T09:31:00Z" } ``` The response also includes a statusUrl. Ignore it and poll the batch endpoint below with the batchId. ## Get a batch's status GET/v1/whatsapp/sessions/{sessionId}/messages/batch/{batchId} Returns the progress of a bulk batch and the result for each recipient. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | batchIdrequired | path | The batch id returned by send-bulk. | ``` { "batchId": "batch_01", "status": "processing", "progress": { "total": 2, "sent": 1, "failed": 0, "pending": 1, "cancelled": 0 }, "results": [ { "chatId": "254700000002@c.us", "status": "sent", "messageId": "3EB0A1B2C3D4E5F60789", "sentAt": "2026-01-15T09:30:03Z" }, { "chatId": "254700000004@c.us", "status": "pending" } ], "startedAt": "2026-01-15T09:30:00Z" } ``` Batch status is pending, processing, completed, cancelled or failed. Each result is pending, sent, failed or cancelled. ## Cancel a batch POST/v1/whatsapp/sessions/{sessionId}/messages/batch/{batchId}/cancel Cancels a running batch. Messages already sent stay sent. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | batchIdrequired | path | The batch to cancel. | ``` { "batchId": "batch_01", "status": "cancelled", "progress": { "total": 2, "sent": 1, "failed": 0, "pending": 0, "cancelled": 1 } } ``` --- # Read messages Source: https://tuskmail.africa/docs/whatsapp-api/messages Incoming messages are best received with webhooks (https://tuskmail.africa/docs/whatsapp-api/webhooks). These endpoints let you look messages up afterwards. ## List stored messages GET/v1/whatsapp/sessions/{sessionId}/messages Returns messages stored for the session, newest first, with filters. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatId | string | Only messages in this chat. | from | string | Only messages from this sender. | limit | integer | Maximum messages to return. Defaults to 50. | offset | integer | Messages to skip. | after | string | Cursor for paging: the id of the last message of the previous page. | inlineMedia | boolean | Set false to leave out inline media data, and fetch it with the media endpoint instead. | ``` { "messages": [ { "id": "msg_1", "chatId": "254700000002@c.us", "from": "254700000002@c.us", "to": "254700000001@c.us", "body": "Hello", "type": "chat", "direction": "incoming", "timestamp": 1768469400, "status": "read", "createdAt": "2026-01-15T09:30:00Z" } ], "total": 1 } ``` ## Fetch a chat's history GET/v1/whatsapp/sessions/{sessionId}/messages/{chatId}/history Fetches recent messages of a chat live from WhatsApp. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | path | The chat id. | limit | integer | Maximum messages to return. Defaults to 50. | includeMedia | boolean | Include media as base64. Slower. Defaults to false. | deep | boolean | Raise the limit ceiling from 100 to 2000 to reach further back. Slower. | ``` [ { "id": "3EB0A1B2C3D4E5F60789", "from": "254700000002@c.us", "to": "254700000001@c.us", "chatId": "254700000002@c.us", "body": "Hello", "type": "text", "timestamp": 1768469400, "fromMe": false, "isGroup": false, "kind": "individual" } ] ``` ## Download a message's media GET/v1/whatsapp/sessions/{sessionId}/messages/{chatId}/{messageId}/media Downloads the stored media of a message. The response is the file itself with its content type, not JSON. It returns 404 when no media is stored for the message. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | chatIdrequired | path | The chat the message is in. | messageIdrequired | path | The message id. | ``` Binary file ``` --- # Contacts Source: https://tuskmail.africa/docs/whatsapp-api/contacts Contacts are the people known to the linked number. ## List contacts GET/v1/whatsapp/sessions/{sessionId}/contacts Returns the contacts of the session. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | limit | integer | Maximum contacts to return, from 1 to 1000. Defaults to 1000. | offset | integer | Number of contacts to skip, for paging. | ``` [ { "id": "254700000002@c.us", "name": "Amina Wanjiru", "pushName": "Amina", "number": "254700000002", "isMyContact": true, "isBlocked": false } ] ``` ## Check a number GET/v1/whatsapp/sessions/{sessionId}/contacts/check/{number} Checks whether a phone number is on WhatsApp. Limited to 30 checks per minute per session. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | numberrequired | path | The number in international format, without a plus. | ``` { "number": "254700000002", "exists": true, "chat_id": "254700000002@c.us" } ``` --- # Groups Source: https://tuskmail.africa/docs/whatsapp-api/groups Group chat ids end in @g.us. Send messages to a group by using its id as the chatId. ## List groups GET/v1/whatsapp/sessions/{sessionId}/groups Returns the groups the number is in. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | limit | integer | Maximum groups to return, from 1 to 1000. Defaults to 1000. | offset | integer | Number of groups to skip, for paging. | ``` [ { "id": "120363000000000000@g.us", "name": "Delivery team" } ] ``` ## Create a group POST/v1/whatsapp/sessions/{sessionId}/groups Creates a group with the given participants. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | namerequired | string | The group name. | participantsrequired | string[] | Chat ids of the people to add. | ``` { "id": "120363000000000000@g.us", "name": "Delivery team", "participantsCount": 3, "isAdmin": true } ``` ## Get a group GET/v1/whatsapp/sessions/{sessionId}/groups/{groupId} Returns group details and participants. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | groupIdrequired | path | The group id, ending in @g.us. | ``` { "id": "120363000000000000@g.us", "name": "Delivery team", "description": "Daily dispatch", "participants": [ { "id": "254700000002@c.us", "number": "254700000002", "isAdmin": true, "isSuperAdmin": true } ], "isAnnounce": false, "memberAddMode": "all" } ``` ## Add participants POST/v1/whatsapp/sessions/{sessionId}/groups/{groupId}/participants Adds people to a group. The response reports the outcome for each person. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | groupIdrequired | path | The group id. | participantsrequired | string[] | Chat ids to add. | ``` { "success": true, "message": "Participants updated", "results": [ { "id": "254700000004@c.us", "success": true, "status": 200 } ] } ``` ## Remove participants DELETE/v1/whatsapp/sessions/{sessionId}/groups/{groupId}/participants Removes people from a group. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | groupIdrequired | path | The group id. | participantsrequired | string[] | Chat ids to remove. | ``` { "success": true, "message": "Participants updated", "results": [ { "id": "254700000004@c.us", "success": true, "status": 200 } ] } ``` ## Leave a group POST/v1/whatsapp/sessions/{sessionId}/groups/{groupId}/leave Makes the number leave the group. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | groupIdrequired | path | The group id. | ``` { "success": true, "message": "Left group" } ``` ## Get the invite link GET/v1/whatsapp/sessions/{sessionId}/groups/{groupId}/invite-code Returns the group's invite code and link. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | groupIdrequired | path | The group id. | ``` { "inviteCode": "AbCdEfGhIjK", "inviteLink": "https://chat.whatsapp.com/AbCdEfGhIjK" } ``` --- # Webhooks Source: https://tuskmail.africa/docs/whatsapp-api/webhooks-reference Webhooks belong to your account and can be limited to one session. Read Webhooks and events (https://tuskmail.africa/docs/whatsapp-api/webhooks) for the event list, signature verification, retries and plan limits. URLs must be https on a public host, or the API returns 422 invalid_webhook_url. ## Create a webhook POST/v1/whatsapp/webhooks Registers a URL for events. Returns the full secret once. BodyName | Type | Description | urlrequired | string | Where to deliver events. Must be https on a public host. | events | string[] | Events to subscribe to, or ["*"] for all. See the event list (https://tuskmail.africa/docs/whatsapp-api/webhooks#events). | secret | string | 16 to 100 characters. Generated if omitted. Signs each delivery in X-Tusk-Signature. Shown in full only on create and rotate. | description | string | A note for yourself. | session_id | string | Only deliver events of this session. Omit for all sessions. | ``` { "id": "5b0e9c3a-7d1f-4a62-9c1e-2f8a6d4b7e31", "url": "https://example.com/hooks/whatsapp", "description": "Order bot", "events": [ "message.received", "message.ack" ], "active": true, "session_id": null, "session_ref": null, "secret_preview": "whsec_ab...", "consecutive_failures": 0, "last_delivery_at": "2026-01-15T10:00:00Z", "last_status_code": 200, "created_at": "2026-01-15T09:35:00Z", "secret": "whsec_generated_once" } ``` Over your plan’s webhook count you get 403 webhook_limit. ## List webhooks GET/v1/whatsapp/webhooks Returns your webhooks. ``` { "data": [ { "id": "5b0e9c3a-7d1f-4a62-9c1e-2f8a6d4b7e31", "url": "https://example.com/hooks/whatsapp", "description": "Order bot", "events": [ "message.received", "message.ack" ], "active": true, "session_id": null, "session_ref": null, "secret_preview": "whsec_ab...", "consecutive_failures": 0, "last_delivery_at": "2026-01-15T10:00:00Z", "last_status_code": 200, "created_at": "2026-01-15T09:35:00Z" } ] } ``` ## Get a webhook GET/v1/whatsapp/webhooks/{id} Returns one webhook. ParametersName | Type | Description | idrequired | path | The webhook id. | ``` { "id": "5b0e9c3a-7d1f-4a62-9c1e-2f8a6d4b7e31", "url": "https://example.com/hooks/whatsapp", "description": "Order bot", "events": [ "message.received", "message.ack" ], "active": true, "session_id": null, "session_ref": null, "secret_preview": "whsec_ab...", "consecutive_failures": 0, "last_delivery_at": "2026-01-15T10:00:00Z", "last_status_code": 200, "created_at": "2026-01-15T09:35:00Z" } ``` ## Update a webhook PUT/v1/whatsapp/webhooks/{id} Changes a webhook. Send only the fields to change. ParametersName | Type | Description | idrequired | path | The webhook id. | url | string | Where to deliver events. Must be https on a public host. | events | string[] | Events to subscribe to, or ["*"] for all. See the event list (https://tuskmail.africa/docs/whatsapp-api/webhooks#events). | secret | string | 16 to 100 characters. Generated if omitted. Signs each delivery in X-Tusk-Signature. Shown in full only on create and rotate. | description | string | A note for yourself. | session_id | string | Only deliver events of this session. Omit for all sessions. | active | boolean | Pause or resume deliveries. | ``` { "id": "5b0e9c3a-7d1f-4a62-9c1e-2f8a6d4b7e31", "url": "https://example.com/hooks/whatsapp", "description": "Order bot", "events": [ "*" ], "active": true, "session_id": null, "session_ref": null, "secret_preview": "whsec_ab...", "consecutive_failures": 0, "last_delivery_at": "2026-01-15T10:00:00Z", "last_status_code": 200, "created_at": "2026-01-15T09:35:00Z" } ``` ## Delete a webhook DELETE/v1/whatsapp/webhooks/{id} Deletes a webhook. ParametersName | Type | Description | idrequired | path | The webhook id. | ``` (empty body) ``` ## Send a test event POST/v1/whatsapp/webhooks/{id}/test Sends a signed webhook.test event and reports what your server answered. ParametersName | Type | Description | idrequired | path | The webhook id. | ``` { "success": true, "status_code": 200, "error": null, "duration_ms": 142 } ``` ## List deliveries GET/v1/whatsapp/webhooks/{id}/deliveries Returns recent delivery attempts. The last 100 are kept. ParametersName | Type | Description | idrequired | path | The webhook id. | ``` { "data": [ { "id": "d_1", "event": "message.received", "event_id": "evt_8f2c1a9d03b44e7a", "success": true, "status_code": 200, "attempts": 1, "duration_ms": 120, "error": null, "created_at": "2026-01-15T10:00:00Z" } ] } ``` --- # Templates Source: https://tuskmail.africa/docs/whatsapp-api/templates Templates are reusable texts with {{variable}} placeholders, stored per session. Send one with send-template (https://tuskmail.africa/docs/whatsapp-api/send-messages#template). These are your own text snippets, not pre-approved WhatsApp Business templates. ## Create a template POST/v1/whatsapp/sessions/{sessionId}/templates Creates a template. BodyName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | namerequired | string | A name, unique within the session. Up to 100 characters. | bodyrequired | string | The text, up to 4096 characters, with {{variable}} placeholders. | header | string | Optional text added before the body. Up to 1024 characters. | footer | string | Optional text added after the body. Up to 1024 characters. | ``` { "id": "b1c2d3e4-f5a6-7890-bcde-f01234567890", "sessionId": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "order-confirmation", "body": "Hi {{customer}}, your order {{orderId}} has shipped.", "footer": "Reply STOP to opt out.", "createdAt": "2026-01-15T09:40:00Z", "updatedAt": "2026-01-15T09:40:00Z" } ``` ## List templates GET/v1/whatsapp/sessions/{sessionId}/templates Returns the session's templates. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | ``` [ { "id": "b1c2d3e4-f5a6-7890-bcde-f01234567890", "sessionId": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "order-confirmation", "body": "Hi {{customer}}, your order {{orderId}} has shipped.", "footer": "Reply STOP to opt out.", "createdAt": "2026-01-15T09:40:00Z", "updatedAt": "2026-01-15T09:40:00Z" } ] ``` ## Get a template GET/v1/whatsapp/sessions/{sessionId}/templates/{id} Returns one template. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | idrequired | path | The template id. | ``` { "id": "b1c2d3e4-f5a6-7890-bcde-f01234567890", "sessionId": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "order-confirmation", "body": "Hi {{customer}}, your order {{orderId}} has shipped.", "footer": "Reply STOP to opt out.", "createdAt": "2026-01-15T09:40:00Z", "updatedAt": "2026-01-15T09:40:00Z" } ``` ## Update a template PUT/v1/whatsapp/sessions/{sessionId}/templates/{id} Changes a template. Send only the fields to change. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | idrequired | path | The template id. | name | string | A name, unique within the session. Up to 100 characters. | body | string | The text, up to 4096 characters, with {{variable}} placeholders. | header | string | Optional text added before the body. Up to 1024 characters. | footer | string | Optional text added after the body. Up to 1024 characters. | ``` { "id": "b1c2d3e4-f5a6-7890-bcde-f01234567890", "sessionId": "9c1f6a4e-2b7d-4e0a-8f53-6d2a1b7c9e10", "name": "order-confirmation", "body": "Hi {{customer}}, order {{orderId}} is on its way.", "footer": "Reply STOP to opt out.", "createdAt": "2026-01-15T09:40:00Z", "updatedAt": "2026-01-15T09:40:00Z" } ``` ## Delete a template DELETE/v1/whatsapp/sessions/{sessionId}/templates/{id} Deletes a template. ParametersName | Type | Description | sessionIdrequired | path | The session id from the sessions endpoints. | idrequired | path | The template id. | ``` (empty body) ``` --- # Best Email for Small Business in Kenya (2026): Free and Paid Options Compared Source: https://tuskmail.africa/blog/best-free-email-services-small-business-kenya Published: 2026-10-20 Free email gets you as far as @gmail.com or a web-only inbox. For a branded address like you@yourbusiness.co.ke that works on your phone, in a shared team inbox and with an AI assistant, TuskMail starts at KES 53 per mailbox a month on yearly plans, billed in KES by M-Pesa or card, with a 7 days free trial on the Starter plan. ## How did we compare these email services? We looked at what a Kenyan small business actually needs: a price in Kenyan shillings, an address on your own domain, mail that works on your phone and in other apps, shared inboxes for info@ and sales@, and an AI assistant that saves time. A free plan that cannot do the job is not cheap, so we note the limits too. Prices of other providers were taken from their public pricing pages and comparison pages in October 2026 and change often, so confirm them before you buy. We make TuskMail, and the 7 days free trial on the Starter plan lets you test it yourself. ## Which email service is best for a small business? The quick table Prices for other providers come from their public pages and are indicative. Check each provider before you buy. Service | Typical price | Your own domain | Billed in | Watch out for TuskMail | From KES 2,470 a year for 2 mailboxes, KES 3,950 for 5 | Yes, from any registrar, adding it is free | KES, by M-Pesa or card | Starter has a 7 days free trial, so you can test it first Zoho Mail Free | KES 0 (up to 5 users) | Yes, 1 domain | Free | Web only: no IMAP, POP or phone mail apps, and a sixth user forces a paid upgrade Gmail (free) | KES 0 | No, @gmail.com only | Free | No branded address Outlook.com (free) | KES 0 | No, @outlook.com only | Free | No branded address Truehost Workplace Starter | About KES 672 a year for 1 mailbox | Yes | KES | One mailbox only; check renewal pricing HostAfrica HMail Core | About KES 128 per user a month (promo) | Yes | KES | Promo price rises to the regular price Google Workspace Starter | About KES 900 per user a month, annual | Yes | US dollars, by card | Dollar pricing moves with the exchange rate ## 1. TuskMail: a branded inbox built for teams, from KES 53 per mailbox TuskMail is made for Kenyan and African small businesses that have outgrown a free address. Plans are priced in Kenyan shillings and paid by M-Pesa or card: Starter KES 2,470 a year for 2 mailboxes, Business KES 3,950 a year for 5 and Team KES 6,320 a year for 10. That is KES 66 per mailbox a month on Business. You get your domain from any registrar (adding a domain is free), user mailboxes and shared group mailboxes under one sign-in, a fast web and mobile app, Maria AI for reply suggestions and inbox search on pay-as-you-go KES credit, branded footers, and connection to Gmail, Thunderbird and other apps over IMAP and SMTP. Start with the 7 days free trial on the Starter plan, add your domain and see mail arrive before you pay anything. See pricing (https://tuskmail.africa/pricing). ## 2. Zoho Mail Free: branded, but web only Zoho Mail's free plan covers up to 5 users with 5 GB each and one custom domain. It is web-only: there is no IMAP, POP or ActiveSync, so you cannot connect Outlook or a phone's built-in mail app, and when you add a sixth person you must move the whole organisation to a paid plan. It works as a starting point for one to five people who are happy to live in a browser. Teams that want phone apps, shared inboxes and AI usually move to a plan like TuskMail Starter. ## 3. Gmail: free, but not a business address A free Gmail account ends in @gmail.com, so you cannot use you@yourcompany.co.ke. To get your own domain on Google's systems you need Google Workspace, which starts at about US$7 per user a month on an annual plan (roughly KES 900) and US$8.40 on monthly billing, charged in dollars by card. Many Kenyan businesses forward a domain address into Gmail and send as that address. It works, but replies can land in spam if SPF and DKIM are not set up correctly. With TuskMail the app shows the records and checks them for you. ## 4. Outlook.com and Microsoft 365 Outlook.com is free but, like Gmail, gives you an @outlook.com address. Microsoft 365 Business Basic adds your own domain from about US$6 per user a month on an annual plan and bundles Teams and web versions of Office. That is a lot of product to pay for if what you need is email. ## 5. Truehost and HostAfrica: email bundled with hosting Kenyan hosts sell email cheaply. Truehost lists a Workplace Starter plan at about KES 672 a year for one 3 GB mailbox, and HostAfrica lists its HMail Core at about KES 128 per user a month on promotion, with a higher regular price. Both accept local payments. Before you buy, ask for the renewal price, and ask how deliverability is protected when mailboxes sit on a shared server. TuskMail is dedicated business email with group mailboxes, Maria AI and a modern app, so you are not choosing between price and features. ## How do I choose the right business email? - Just you and a placeholder address: a free plan will do for a while, until you need your own domain. - A branded address on your phone and in any mail app: TuskMail Starter, with a 7 days free trial. - A team that shares info@, sales@ and support@ without sharing passwords: TuskMail group mailboxes. - Reply suggestions and inbox search without a dollar subscription: Maria AI on pay-as-you-go KES credit. - Finance that prefers M-Pesa and invoices in shillings: TuskMail. ### Is there free business email in Kenya with my own domain? Zoho Mail Free offers one custom domain for up to 5 users, but it is web only. If you want phone apps, IMAP and shared inboxes, TuskMail Starter has a 7 days free trial so you can try a full setup first. ### Can I use Gmail for my business email in Kenya? You can, but a free Gmail address ends in @gmail.com. A branded address needs Google Workspace (billed in US dollars) or a provider such as TuskMail, which is billed in KES. ### What is the best business email for a small team in Kenya? TuskMail is built for small teams: your own domain, user and group mailboxes under one sign-in, Maria AI, M-Pesa billing and a 7 days free trial on Starter. ### Can I pay for business email with M-Pesa? Yes, TuskMail accepts M-Pesa and card, and each payment produces an invoice and a receipt. --- # Why Kenyan Teams Choose TuskMail for Professional Business Email Source: https://tuskmail.africa/blog/why-use-tuskmail-professional-email Published: 2026-10-20 TuskMail gives your business email on your own domain, from any registrar, with a modern inbox, shared group mailboxes under one sign-in, Maria AI and prices in Kenyan shillings you can pay by M-Pesa. Mailboxes cost KES 53 to KES 103 a month each on yearly plans, and the Starter plan has a 7 days free trial. ## 1. Your domain, from any host Many email providers push you to buy the domain from them. TuskMail does not. Keep your domain at Truehost, HostAfrica, Namecheap, GoDaddy or wherever it is today, add it in the web app (adding a domain is free), and publish the MX, SPF, DKIM and DMARC records TuskMail shows you. It then checks them for you. ## 2. A modern inbox on web and mobile The web app is fast and clean, with a floating composer, contact suggestions and drafts saved as you write, plus a mobile app. If you prefer another client, connect Gmail, Thunderbird or Outlook over IMAP and SMTP with an app password. ## 3. Shared group mailboxes without shared passwords Addresses like sales@ and support@ are group mailboxes. You add team members and each opens the group from their own sign-in, so nobody shares a password and removing a person removes their access instantly. ## 4. Maria AI, paid for only when used Maria suggests replies to mail that needs an answer and answers questions about your inbox in a chat panel. She runs on pay-as-you-go credit in KES, topped up between KES 100 and KES 10,000, with an optional monthly limit. Usage is shown in shillings, not tokens, and the business owner chooses which mailboxes can use her. ## 5. Prices that make sense in shillings Starter is KES 2,470 a year for 2 mailboxes, Business is KES 3,950 for 5 and Team is KES 6,320 for 10. Longer periods cost less per month, and you pay by M-Pesa or card. Each payment produces an invoice and a receipt. ## 6. Branded footers with a live preview Pick a footer design or write custom HTML and CSS (no JavaScript), preview it live, and apply it to every outgoing message. Group mailboxes can have their own footer and profile picture. ## 7. Secure by default TuskMail is a modern, secure mail platform with spam and virus filtering, encryption in transit, optional two-step sign-in that requires approval from a trusted mobile device, and app passwords so other apps never see your main password. ## 8. Is TuskMail right for you? It fits small and growing teams in Kenya and across Africa that want a branded address, shared inboxes and an AI assistant without enterprise pricing. Start the 7 days free trial on Starter, add your domain, create a mailbox and send your first branded email today. Still comparing? Read our email comparison for small businesses in Kenya (https://tuskmail.africa/blog/best-free-email-services-small-business-kenya). ### Do I need to buy my domain from TuskMail? No. Keep your domain at any registrar and add it in the web app. Adding a domain is free. ### Is there a free trial? Yes. The Starter plan has a 7 days free trial. ### How much does TuskMail cost? Plans are in Kenyan shillings: Starter KES 2,470 a year for 2 mailboxes, Business KES 3,950 for 5 and Team KES 6,320 for 10. You can pay by M-Pesa or card. ### Is Maria AI included in the plan price? Maria AI runs on pay-as-you-go credit in KES, so you only pay when you use her, and you can set a monthly limit. ### Does TuskMail work with Gmail, Outlook and Thunderbird? Yes. Create an app password and connect over IMAP and SMTP. --- # Cheap WhatsApp API in Kenya and Africa (2026): Prices in KES and How to Start Source: https://tuskmail.africa/blog/cheap-whatsapp-api-kenya-africa Published: 2026-10-20 Tusk WhatsApp links the number you already use, like WhatsApp Web, and charges a flat monthly fee in KES from KES 500 a month per number, with no per-message fees and no template approvals. Start with the 5 days free trial and send your first message in minutes through the dashboard or REST API. ## What are the two ways to get a WhatsApp API? The official route is the WhatsApp Business Platform (Cloud API), directly or through a business solution provider. Since July 2025 Meta bills per delivered template message, with rates that depend on the message category and the destination country, and you must verify your business and have templates approved before you can start a conversation. The faster route is a session-based API such as Tusk WhatsApp. You link an existing WhatsApp number by scanning a QR code or entering a pairing code, exactly as with WhatsApp Web, and then send and receive through a dashboard or REST API. There are no per-message fees and no template approvals, and billing is a flat plan in KES. ## How do they compare? | Official Business Platform | Tusk WhatsApp (session API) Pricing model | Per delivered template message, plus any provider fee | Flat plan by number of sessions, in KES Starting price | Varies by country and category | KES 500 a month for 1 number Setup | Business verification, number registration, template approval | Scan a QR code or enter a pairing code Templates | Required to start conversations | Not required Existing number and chats | Number is registered to the API | Link the number you already use Official Meta support | Yes | No Best for | Large, compliant outbound campaigns | Support, sales and automation for small and mid-size teams ## How much does Tusk WhatsApp cost in Kenya? Plans are priced by the number of sessions, where one session is one WhatsApp number. Starter is KES 500 a month for 1 number, Growth is KES 1,350 for 3 (KES 450 per number), Business is KES 2,400 for 6 and Scale is KES 3,500 for 10. Three-month plans are about 10% cheaper and yearly plans about 25% cheaper. Payment is in KES by M-Pesa or card. Tusk WhatsApp comes with a 5 days free trial. Every plan includes the dashboard and the full REST API: text, images, video, audio, documents, locations, contacts and polls, group management, and webhooks for incoming messages. ## Does it work outside Kenya? Yes. Because you link a normal WhatsApp number, it works wherever that number works, including Nigeria, Ghana, Uganda, Tanzania, Rwanda, Ethiopia and South Africa. Plans are billed in Kenyan shillings, and card payment works from other countries. ## How do I get the best results from a session-based API? - Message people who expect to hear from you and keep volume sensible, as WhatsApp can restrict numbers that send unwanted messages. - For cold outreach use a separate number, and keep your main number for customers who already know you. - Tusk WhatsApp is not an official Meta product. If you need Meta's verified green tick, use the official platform. ## How do I get started? The docs have a quickstart and full reference (https://tuskmail.africa/docs/whatsapp-api). - Create a TuskMail account and choose a plan. - Add a session and scan the QR code from WhatsApp, under Linked devices. - Create an API key and send your first message with one request. - Register a webhook to receive incoming messages on your server. ### What is the cheapest WhatsApp API in Kenya? Tusk WhatsApp is a flat monthly plan from KES 500 for one number, billed in KES, with no per-message fees and a 5 days free trial. ### Do I need Meta approval for Tusk WhatsApp? No. You link an existing number by QR code or pairing code, and no pre-approved templates are required. ### Is Tusk WhatsApp the official WhatsApp Business API? No. It is a session-based API that links a number the way WhatsApp Web does. The official platform is run by Meta. ### Can I use it in Nigeria, Uganda or Tanzania? Yes. It works wherever your WhatsApp number works, and card payment works from other countries. --- # Affordable Business Email in Kenya (2026): Prices in KES and How to Choose Source: https://tuskmail.africa/blog/affordable-business-email-kenya-pricing-guide Published: 2026-10-20 Business email in Kenya costs from KES 0 to about KES 1,100 per mailbox a month, but the cheapest headline price rarely includes shared inboxes, phone apps, AI or KES billing. TuskMail Business works out to KES 66 per mailbox a month on a yearly plan and includes all of them, with a 7 days free trial on Starter. ## How much does business email cost in Kenya? Dollar prices are converted at roughly KES 129 to the dollar, so your card statement will differ. Provider prices change often, so verify them. Option | Per mailbox a month | What it means TuskMail Team | KES 53 | 10 mailboxes, yearly plan, billed in KES TuskMail Business | KES 66 | 5 mailboxes, yearly plan, billed in KES TuskMail Starter | KES 103 | 2 mailboxes, yearly plan, 7 days free trial Zoho Mail Free | KES 0 | Up to 5 users, web only Truehost Workplace Starter | About KES 56 | 1 mailbox, 3 GB, billed yearly HostAfrica HMail Core | About KES 128 to 257 | Promo to regular price Microsoft 365 Business Basic | About KES 775 | US$6 per user, annual, billed in dollars Google Workspace Starter | About KES 900 to 1,085 | US$7 annual, US$8.40 monthly, billed in dollars ## What hidden costs should I watch for? - Renewal jumps: introductory prices on some hosting-bundled plans roughly double at renewal. - Dollar pricing: global suites bill in USD, so the shilling cost moves with the exchange rate and card fees. - Per-user minimums: free tiers can force a full paid upgrade when you add one more person. - Add-ons: AI assistants, archiving and extra storage are often extra. - The domain itself: a .co.ke or .com is a separate yearly cost with any provider. ## What should I look for before choosing? - Your own domain from any registrar, so you are never locked in. - IMAP and SMTP support, so you can use any app. - SPF, DKIM and DMARC guidance, so your mail lands in the inbox, not spam. - Shared mailboxes for info@, sales@ and support@ without sharing passwords. - Billing in KES with M-Pesa if your finance team prefers it. - Mobile apps and two-step sign-in. - Clear storage per mailbox and a clear renewal price. ## How many mailboxes does my business need? Count the people who send mail to customers, then add the shared addresses you want, such as info@ and accounts@. A five-person shop with two shared addresses needs about seven mailboxes. In TuskMail, shared group mailboxes let several people open one address from their own sign-ins, so you often need fewer paid mailboxes than you expect. ## Why do teams choose TuskMail? TuskMail gives small teams a modern inbox, group mailboxes and Maria AI at a shilling-friendly price. Starter is KES 270 a month or KES 2,470 a year for 2 mailboxes, with a 7 days free trial. Business is KES 430 a month or KES 3,950 a year for 5. Team is KES 680 a month or KES 6,320 a year for 10. Pay by M-Pesa or card and get an invoice for every payment. See current pricing (https://tuskmail.africa/pricing) or start the 7 days free trial (https://tuskmail.africa/pricing). ### What is the best-value business email in Kenya? For teams, TuskMail: your own domain, group mailboxes, Maria AI and KES billing from 53 per mailbox a month on yearly plans, with a 7 days free trial on Starter. ### Is Google Workspace worth it in Kenya? It bills in US dollars by card, so the shilling cost moves with the exchange rate. TuskMail bills in KES and accepts M-Pesa. ### Do I pay monthly or yearly? Both work. Yearly is cheapest per mailbox, and TuskMail also offers 3-month plans. ### Can I move my existing email to a new provider? Yes. Add your domain to the new provider, publish the new MX records, and create the mailboxes. Keep the domain where it is. --- # Professional Business Email Hosting in Kenya (2026): Setup, DNS and Providers Source: https://tuskmail.africa/blog/professional-business-email-hosting-kenya-2026 Published: 2026-10-20 Professional business email hosting means mailboxes on your own domain, such as you@yourcompany.co.ke, with proper authentication (SPF, DKIM and DMARC), spam filtering, encryption and support. TuskMail sets this up in under an hour, checks your DNS for you and starts at KES 53 per mailbox a month, with a 7 days free trial on Starter. ## What is professional business email hosting? It is a service that stores and delivers mail for your domain. Unlike a free Gmail address, mail comes from your brand, you control the accounts, and the provider keeps your messages out of spam folders by authenticating them. ## How do I set it up in 5 steps? With TuskMail the app shows the exact values and checks them for you; see the domain and DNS guide (https://tuskmail.africa/docs/tuskmail/domains-and-dns). - Own a domain (.co.ke, .com, .africa and others all work). - Add the domain to your email provider. - Publish the MX, SPF, DKIM and DMARC records the provider shows you at your DNS host. - Create mailboxes: one per person plus shared ones such as info@ and sales@. - Sign in on web and mobile, or connect Outlook or Thunderbird with IMAP and SMTP. ## What do MX, SPF, DKIM and DMARC do? Gmail and Yahoo require SPF, DKIM and DMARC from bulk senders, and unauthenticated mail increasingly lands in spam even at low volume. Record | What it does MX | Tells the internet which servers receive mail for your domain SPF | Lists the servers allowed to send mail for your domain DKIM | Adds a cryptographic signature so receivers can verify a message was not altered DMARC | Tells receivers what to do with mail that fails SPF or DKIM, and sends you reports ## How do I compare hosting providers? - Deliverability: dedicated mail infrastructure beats a shared web host's server. - Security: spam and virus filtering, encryption in transit, two-step sign-in, app passwords. - Collaboration: shared mailboxes and calendars. - Apps: a usable web app and a mobile app, plus IMAP and SMTP. - Support and billing: local support, KES billing, invoices and receipts. - AI: reply suggestions and inbox search can save hours a week, but check how they are priced. ## What does TuskMail offer for Kenyan businesses? TuskMail is a modern, secure mail platform with user and group mailboxes on your own domain, web and mobile apps, branded footers, forwarding, Maria AI, and billing in KES by M-Pesa or card. It shows the DNS records and checks them for you, so setup stays under an hour. Plans start at KES 2,470 a year for 2 mailboxes, with a 7 days free trial on Starter. See pricing (https://tuskmail.africa/pricing) or how it works (https://tuskmail.africa/solutions/tuskmail). ### Do I need hosting to have business email? You need a mail service, not web hosting. Many providers sell email-only plans that are cheaper and more reliable for deliverability than a mailbox on shared web hosting. ### Can I keep my domain where it is? Yes. Keep the domain at any registrar and only change the MX records to point to your email provider. ### How long does setup take? Usually under an hour. DNS changes can take up to a few hours to be seen everywhere. ### What is a shared group mailbox? An address such as sales@ that several people open from their own sign-ins, so nobody shares a password. --- # Where to Get Cheap Business Email in Kenya (2026): 6 Options with Real Prices Source: https://tuskmail.africa/blog/where-to-get-cheap-emails-in-kenya Published: 2026-10-20 Free Gmail or Outlook gets you an address, but not a branded one. For a business address with team inboxes, phone apps and AI at a price made for Kenya, TuskMail Business is KES 66 a month per mailbox on a yearly plan and you can start the 7 days free trial on Starter. ## 1. Best value for teams: TuskMail From KES 2,470 a year for 2 mailboxes and KES 3,950 for 5, with shared group mailboxes, mobile apps and Maria AI. Pay in KES with M-Pesa or card, and start with the 7 days free trial on Starter. ## 2. Free personal email: Gmail or Outlook Create a free account in minutes. Fine for personal use, but customers see @gmail.com or @outlook.com, which looks less trustworthy on quotes and invoices. ## 3. Free branded email with limits: Zoho Mail Free Up to 5 users, one custom domain, web access only. There is no IMAP, so phone mail apps and Outlook cannot connect. ## 4. Single mailbox from a local host: Truehost Entry business mailboxes are listed at about KES 672 a year for 3 GB. Check renewal prices, and note it covers one mailbox. ## 5. Office-suite style: HostAfrica HMail Listed at about KES 128 per user a month on promotion, rising at the regular price. ## 6. Global suites: Google Workspace and Microsoft 365 Billed in US dollars by card at about KES 775 to 1,085 per user a month, so your cost moves with the exchange rate. ## How do I keep the cost down? - Choose yearly billing. - Use shared group mailboxes for info@ and sales@ instead of paying for extra users. - Buy the domain separately so you can move providers any time. - Ask for the renewal price before you pay. - Use a free trial first: TuskMail Starter has 7 days. ### What is the cheapest email for a business in Kenya? For a branded address you can use on your phone and share across a team, TuskMail starts at KES 53 per mailbox a month on yearly plans, with a 7 days free trial on Starter. ### Can I get a .co.ke email address for free? Free providers do not give you a .co.ke address unless you bring a domain, which costs a yearly registration fee. Adding that domain to TuskMail is free. ### Can I pay with M-Pesa? Yes. TuskMail accepts M-Pesa and card, in Kenyan shillings. --- # Affordable Business Email Across Africa (2026): Nigeria, Ghana, Uganda, Tanzania, Rwanda and South Africa Source: https://tuskmail.africa/blog/affordable-business-email-africa-nigeria-ghana-uganda-tanzania-south-africa Published: 2026-10-20 Business email works through your domain, so you can run professional mailboxes in any African country. Use your country domain (.ng, .com.gh, .co.ug, .co.tz, .rw, .co.za) or a .com, add it to TuskMail from any registrar, and pay by card or M-Pesa. Plans start at KES 2,470 a year for 2 mailboxes, with a 7 days free trial on Starter. ## Can I use Kenyan-built business email in other African countries? Yes. Email works through DNS records on your domain, not through your location. TuskMail is built in Nairobi but works for any domain, from any registrar, in any country. Plans are billed in Kenyan shillings, M-Pesa is available for Kenyan customers, and card payment works from elsewhere. ## Which domain endings do businesses use in each country? Some country registries have their own registration rules, so check what your registrar needs. A plain .com avoids most of that and works the same for email. Country | Common business domains Nigeria | .com.ng, .ng, .com Ghana | .com.gh, .gh, .com Uganda | .co.ug, .ug, .com Tanzania | .co.tz, .tz, .com Rwanda | .rw, .co.rw, .com Ethiopia | .com.et, .et, .com South Africa | .co.za, .com ## How do I get started? Read the step-by-step DNS guide (https://tuskmail.africa/docs/tuskmail/domains-and-dns). - Choose a domain you control. - Pick a provider that accepts domains from any registrar. - Publish MX, SPF, DKIM and DMARC. - Create a mailbox per person and shared addresses for roles. - Turn on two-step sign-in. ## What about currency and payment? Global suites bill in US dollars, which moves with your local currency. TuskMail bills in KES, with M-Pesa for Kenyan customers and card payment from anywhere. Choose yearly billing for the lowest rate: KES 53 to KES 103 per mailbox a month, and try Starter free for 7 days first. ### Do I need a provider in my own country? No. Email works through your domain's DNS records, so TuskMail works for a domain from any registrar in any country. ### Can I use a .ng or .co.za domain with TuskMail? Yes. Domains from any registrar can be added. ### Which currency is TuskMail billed in? Kenyan shillings (KES), by M-Pesa or card.