Njiwa for WooCommerce
WhatsApp your customers when their order is paid, sent or cancelled, and get a message yourself when one comes in.
What changes for your business
Customers stop asking where their order is
Most of what a small shop answers all day is one question asked by people who have no way of knowing the answer. Every status your order passes through is one your customer now hears about the moment it happens, on the app already open on their phone. You are not writing those replies any more, and neither is whoever you pay to write them.
It reaches people your email does not
An order confirmation email lands in a promotions tab, behind a spam filter, or in an address somebody typed wrong at checkout. The number on the order is the one they gave you to be reached on. This is the same news, sent where they already read.
You know about a sale without watching a screen
One message to your own number the moment an order becomes real, with the customer, what they bought, the total, how they paid and a link that opens the order. It goes on the first status that means money is on the way, so an abandoned checkout never wakes you up at eleven at night.
A cancellation or a refund does not become an argument
The two moments a customer is most likely to ring you angry are the two they hear about first. A cancelled order says plainly that they have not been charged. A refund says the money is on its way and that banks take a few days, which is the sentence that stops the second phone call.
It sounds like your shop, and it works unedited
Every message arrives written, in plain language, with your shop name and the order details filled in. If the words are not yours, change them in WooCommerce settings like any other setting. Nobody needs a developer to change a sentence, and clearing a box is how you switch one message off and keep the rest.
What your customer receives
These are the messages this integration sends out of the box, written out in full. You can change every word of them, but you do not have to: they are written to be sent as they are.
Hi Mary, thank you. Your payment for order #1043 came through and we are getting it ready. 2 x Tyre 195/65 R15 1 x Wheel balancing Total KSh 18,400 Riverside Hardware
Hi Mary, order #1043 is done and on its way to you. Thank you for shopping with Riverside Hardware.
Hi Mary, we have your order #1043 for KSh 18,400. We will let you know the moment your payment comes through. Riverside Hardware
New order #1043 on Riverside Hardware. Mary Wanjiku 3 item(s), KSh 18,400 Paid by M-Pesa https://riverside.example/wp-admin/post.php?post=1043&action=edit
When each message goes out
Each of these is a separate switch, and every one of them is off until you turn it on. Installing this sends nothing to anybody.
| When it happens | Who hears about it |
|---|---|
| An order reaches On hold | The customer, told the order is in and you are waiting for the money. This is the one for bank transfer, cash on delivery and anything else paid after the order is placed. |
| An order reaches Processing | The customer, told the payment came through and you are getting the order ready. The default wording lists the items and the total. |
| An order reaches Completed | The customer, told it is done and on its way. WooCommerce sends its own email at the same moment; this arrives where people actually look. |
| An order reaches Cancelled | The customer, told it was cancelled and that they were not charged, and invited to reply if that was not them. |
| An order reaches Refunded | The customer, told the money is on its way back and that banks take a few days to show it. |
| The first time an order reaches On hold, Processing or Completed | You, once per order, on every number listed in Your WhatsApp numbers. Not when the order row appears, which happens the moment somebody reaches the payment page and usually means nothing. |
Why it is safe on a shop that is trading
Every event is off until you turn it on. Installing and activating the plugin sends nothing to anybody. There is no key saved, and all six events arrive switched off, so the first message leaves only after somebody has ticked a box on purpose.
Start on a test key. A key beginning sk_test_ validates and stores every message and delivers nothing to a phone. Every part of the plugin behaves exactly as it will on the live key, order notes and message ids included, so you can rehearse a real order before a customer is ever involved.
It cannot block a checkout. Messages are queued, not sent inline, and the code that arranges them catches its own errors. A customer completes an order at the same speed whether this plugin is installed, switched off, or pointed at an address that does not answer.
A customer is never messaged twice for the same thing. The marker on the order stops a second queue job being created, and the idempotency key stops a job that runs twice turning into two messages. The recipient is part of that key, so an alert going to three of your own numbers stays three messages rather than collapsing into one.
It cannot message a WhatsApp group. Everything typed into a number box and everything read off an order is reduced to digits before it is used, so a group address, which is not digits, cannot survive the trip and be messaged by accident.
No phone number is not an error. An order without a billing phone gets a note saying no message was sent, and nothing else happens. A customer who did not leave a number is normal, not a fault to be raised at the checkout.
The master switch keeps your work. Turning off Send WhatsApp messages stops every send while leaving your key, your numbers and all six templates exactly where they are. Deleting the plugin deletes the key and the settings, on the grounds that a live key nobody is looking after is worse than no key. The order notes stay, because they are a record of what was sent and they belong to the order.
Both check buttons are for administrators only. Test connection and Send me a test message require the permission to change WooCommerce settings and a valid one time token. The test message wording is fixed in the code and it goes to the first of your own numbers, never to a customer.
The words are yours
Every message is plain text you can edit in your own admin, with placeholders in braces that are filled in from the order. These are the ones it knows:
{first_name}{last_name}{customer_name}{order_number}{order_total}{order_date}{order_status}{payment_method}{items}{item_count}{shop_name}{order_url}{admin_url}
A placeholder it does not know is removed before sending rather than posted to a customer. Empty wording sends nothing, which is how you turn one message off while leaving the event switched on.
Questions
Does it slow down the checkout?
No. The order status hook queues a job and returns; Action Scheduler sends the message after the customer has been sent on their way. Njiwa being slow or unreachable cannot hold up an order, and an error while arranging a message is logged rather than raised at the checkout.
What happens if a customer has no phone number?
Nothing is sent and a note is added to the order saying there was no billing phone number. It is not an error and it does not stop the order.
Will a customer get two messages if I move an order back and forth?
No. The first time an order reaches a status the plugin writes a marker on the order, and it will not queue that event for that order again. Even if a queue job somehow runs twice, the idempotency key means Njiwa replays its first answer instead of sending a second message.
Can I try the whole thing without messaging a real customer?
Yes, and it is the recommended way in. Use the key that begins sk_test_. Every request is checked, every message is stored, order notes are written with real message ids, and nothing reaches a phone. Place a test order, walk it through the statuses, read the notes, then swap the key.
It says the message was sent but nothing arrived.
Read the order note first. If it ends with the line about a test key, the message was stored and deliberately not delivered. If it carries a message id, Njiwa accepted it and the console will show what happened next, including whether the number was reachable. If the note carries a reason instead, that is the reason, and WooCommerce, Status, Logs holds the same line with the error code next to it.
Do I need approval from Meta before I can use this?
Not for the Web rail, which works with a WhatsApp number you already own and needs no application. If you have been through the Cloud API route instead, that works too. The plugin does not know or care which rail a number is on: it posts to the same endpoint and Njiwa sends over whichever rail that number uses.
Does it work with the new WooCommerce order tables?
Yes. The plugin declares itself compatible with the high performance order tables and reads every order through WooCommerce's own methods, so it behaves the same on the new storage and the old one. Without that declaration WooCommerce would switch the new storage off for the whole shop, which is why it is there.
What happens if I delete the plugin?
The API key, the numbers and the six templates are deleted with it. The order notes remain, because they record what was actually sent to a customer and that belongs to the order rather than to us. Deactivating without deleting keeps everything and simply stops the sending.
What it does not do
Said plainly, so you are not surprised later
- It does not receive replies. Incoming WhatsApp arrives as a webhook, and verifying one needs that number's signing secret, which the console does not yet show. Until it does, a receiving feature could not prove a request really came from Njiwa, so there is not one.
- It does not keep its own copy of your messages. Njiwa already stores every message, its status and its failure reason, and a second copy inside WordPress is a second thing to keep in step. The order note and the WooCommerce log are the local trail.
- It sends plain text only. There is no attaching the invoice PDF to the message from this plugin; put {order_url} in the wording instead and the customer opens their own order. Njiwa's API sends files, so a developer can do it directly.
- The order note records that Njiwa accepted a message, not that a phone displayed it. Delivery and read status live in the Njiwa console.
- There is one template per event for the whole shop. No per product, per category, per country or per language wording, and no way to turn an event on for some orders and off for others.
- It does not run campaigns. Bulk sending to past customers is what the Njiwa console is for, on Business plans and above.
- A partial refund that leaves the order in its existing status sends nothing. Only an order that actually reaches the Refunded status triggers the refund message.
- There is no digest. Every order that is real gets its own alert to you, so a shop taking hundreds of orders a day should probably leave that particular event off.
From here on, it is the install
That is everything a person deciding whether to use this needs to know. What follows is for whoever puts it on your shop, and it takes about ten minutes. If that is not you, this is the point to send them the link.
What it needs
- WooCommerce 7.0 or newer. Tested up to WooCommerce 9.4.
- WordPress 6.0 or newer. Tested up to WordPress 6.7.
- PHP 7.4 or newer. The floor is low on purpose, because plenty of shops that would benefit from this are on hosting that has not moved.
- Either order storage. The plugin declares itself compatible with the high performance order tables (HPOS) and works the same on the older post based tables.
- No extra queue software. Messages go through Action Scheduler, which WooCommerce already ships and runs. Where it is unavailable the plugin falls back to WordPress cron, and failing that sends inline.
- A Njiwa account and an API key. A test key is enough to install, configure and rehearse the whole thing.
- Outbound HTTPS from your server to njiwa.upeo.ai. Requests go through WordPress's own HTTP layer, so a host that filters or proxies outbound traffic treats this plugin like every other one.
Install it
- Get the code from https://github.com/Upeosoft-Limited/njiwa-woocommerce. The green Code button offers Download ZIP, and that zip is what WordPress wants, so most people never open a terminal for this at all. If you would rather use SFTP, clone it straight into your plugins directory instead.
git clone https://github.com/Upeosoft-Limited/njiwa-woocommerce wp-content/plugins/njiwa-woocommerce - In WordPress, open Plugins, Add New, Upload Plugin, and choose the zip. Skip this if you copied the folder in over SFTP.
- Press Activate on Njiwa for WooCommerce. If WooCommerce itself is not active you get a notice saying so, and nothing is sent.
- Open WooCommerce, Settings, Njiwa. The plugin also adds a Settings link next to its own row on the Plugins page.
Set it up
- Sign in at console.upeo.ai, open API keys and create a key. Copy the test key, the one beginning sk_test_. The console shows a key once and keeps only its fingerprint, so a lost key is replaced rather than recovered.
- Paste it into API key on the Njiwa tab and press Save changes. Leave Njiwa address exactly as it is unless you were given an address of your own.
- Press Test connection. It asks Njiwa which WhatsApp numbers your account really has and lists each one with its state, and it says plainly when the key is a test key. It sends nothing. Both buttons read the saved settings rather than what is on screen, so save first.
- If your account has more than one number, put the one these messages should come from into Send from, digits only in full international form such as 254712345678. Leave it empty to use the number marked default. Test connection warns you if what you typed matches no number on the account, because every message would then be refused.
- Put your own WhatsApp number into Your WhatsApp numbers, near the bottom of the page, and save. Several numbers are allowed, separated by commas, and each one gets its own copy.
- Press Send me a test message. One fixed message goes to the first number on that list, never to a customer, and the answer gives you Njiwa's message id or the exact reason it failed.
- Tick the events you want and edit the wording underneath each one. Every event ships with wording that works unedited, so turning one on is one click rather than one click and a writing exercise.
- Place a test order and move it through the statuses you turned on. Open the order and read the notes: each one records what went where, with the message id, or why nothing went.
- When it all reads right, replace the sk_test_ key with your sk_live_ key and save. From that point the messages reach real phones and count against your plan.
How it works
The plugin listens to one WooCommerce hook per order status, woocommerce_order_status_processing and its five siblings, rather than a general "the order was saved". That matters twice over. A status hook fires at the moment the thing is actually true, so editing a delivery address never messages anybody. And it still fires for an order created directly into a status, which is what some payment gateways do and what every order you type in by hand does.
The hook itself sends nothing. It checks the master switch, the event's own switch and whether that order has already been handled, reads the billing phone against the order's country, writes a marker on the order and queues a job with Action Scheduler in a group called njiwa. If Action Scheduler is somehow missing the job goes to WordPress cron five seconds out, and if that is missing too the send happens there and then. Anything that goes wrong while arranging a message is caught and logged, because an order must never fail to change status over a WhatsApp message.
The queue worker renders the template against the order, trims it to 4000 characters so it fits what WhatsApp accepts, and posts it to /v1/messages with your key, an Idempotency-Key and a User-Agent naming the plugin and its version. Njiwa's answer becomes the order note. A refusal from Njiwa arrives with a stable machine readable code and a sentence a person can act on; both go to the log, the sentence goes on the order.
A network failure is not treated as a failed send. The message was never accepted, so nothing pretends it was delivered and nothing blocks a later attempt. The request has a 20 second timeout, so a stuck connection cannot hold a queue worker.
Get it
The code is public, at https://github.com/Upeosoft-Limited/njiwa-woocommerce.
Clone it, read it, and see every line that will run on your shop before you
install it. Inside is a README written for the person who runs the shop
rather than for a developer. It is developed at
packages/njiwa-woocommerce in the Njiwa repository and published
from there, so the two never drift apart. The API behind it is documented at
https://docs.njiwa.upeo.ai/.
You need a Njiwa account for the key. A trial gives you a live key straight away and a test key in one click, so you can install this, rehearse a whole order and only then decide.