For developers

Connect your system to Wamy

Wamy delivers your system’s messages to the user’s phone as a notification and brings their replies back. The API is JSON over HTTPS and works from any programming language.

  1. Open your businessIn the business panel, and take your key.
  2. Link the userA “Get notifications on Wamy” button in their account on your side.
  3. SendOne request on every event.
  4. Receive repliesOptional: into your system or to your team in Wamy.
Open your business and get the key

Address and key

Every request goes to this address, with the key in a header:

https://wamyapp.com/api/v1/

Authorization: Bearer wmy_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

The key is secret and stays on your server only. Never put it in a web page or a mobile app. Errors come back as JSON with error and message.

Your first message in 5 minutes

  1. Open the business panel and create your business. The key is shown once: put it in an environment variable named WAMY_KEY.
  2. In the panel press “Send me a test message”: it reaches you in Wamy, so you see how a customer sees it, and you turn notifications on for your phone.
  3. Ask for a link for your own account (the next section) and open it, so your id on your side is tied to your Wamy account.
  4. Send with the example on the side, and replace 42 with your id.

Send

curl -X POST https://wamyapp.com/api/v1/send.php \
  -H "Authorization: Bearer $WAMY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": {"external_id": "42"},
       "title": "Order confirmed",
       "text": {"ar": "تم تأكيد طلبك رقم *5521*", "en": "Order *5521* is confirmed"},
       "ref": "order-5521-confirmed"}'

Rules for self-registered businesses

Anyone can open a business in Wamy, so these rules protect people from unwanted messages and protect their accounts. They are built into the API and cannot be bypassed.

  • You message only people who agreedSomeone who linked their account through your link, or started a conversation with you from the contact link. Sending to an e-mail that neither linked nor wrote to you returns 404 recipient_not_found.
  • A link never signs anyone in for themWhoever opens it signs in to Wamy with their own e-mail (a code or Google), and then their account is linked. So sending email in the link request is pointless: it is ignored.
  • No e-mail from our addressemail_fallback and create are not available to self-registered businesses.
  • Request limitUp to 120 requests a minute per business.
  • The account starts unverifiedAsk for the green tick in the business panel once your profile is complete.
  • Whoever blocks you stops receiving409 blocked_by_user

2. Send a message

/api/v1/send.phpPOST
FieldMeaning
to
required
The recipient: {"external_id": "42"}
text
required
The text, or {"ar": "…", "en": "…"} so each user sees their language. Up to 4096 characters. Supports *bold*, _italic_ and links.
titleA bold line above the text (optional).
image_urlAn image by https link (optional).
buttonsUp to 3 buttons, label up to 30 characters: a link {"type":"url","label":"…","url":"https://…"} or a quick reply {"type":"reply","label":"…","payload":"…"}.
refYour reference. The same ref twice = the same message, so retrying is safe.
fileAn attached file (see “Attachments”).
senderSend as one of your senders (see “Many senders”).

pushed_devices: 0 means the user has not turned notifications on: the message is stored for them and shows when they open Wamy, but it will not reach them as an alert.

curl -X POST https://wamyapp.com/api/v1/send.php \
  -H "Authorization: Bearer $WAMY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": {"external_id": "42"},
       "title": "Order confirmed",
       "text": {"ar": "تم تأكيد طلبك رقم *5521*", "en": "Order *5521* is confirmed"},
       "ref": "order-5521-confirmed"}'
Response
201
{"id": 812, "status": "sent", "pushed_devices": 2, "emailed": false,
 "created_at": "2026-10-26T09:41:00+00:00", "delivered_at": null, "read_at": null,
 "ref": "order-5521-confirmed", "duplicate": false}
A richer example
{"to": {"external_id": "42"},
 "title": "Appointment tomorrow",
 "text": {"ar": "موعدك غدًا الساعة 10:00", "en": "Your appointment is tomorrow at 10:00"},
 "image_url": "https://example.com/map.png",
 "buttons": [
   {"type": "url",   "label": {"ar": "الموقع", "en": "Location"}, "url": "https://maps.example.com/clinic"},
   {"type": "reply", "label": {"ar": "سأحضر", "en": "I will come"}, "payload": "confirm_1042"}
 ],
 "ref": "appt-1042-reminder"}

3. Message and user state

Before an important message (a sign-in code, a payment confirmation) ask: will an alert reach them? And afterwards: did it arrive? If it did not, use another channel.

/api/v1/user.php?external_id=42GET

linked · joined · notifications (devices that receive alerts) · blocked · muted · last_seen_at

/api/v1/message.php?ref=order-5521-confirmedGET
  • sent — stored
  • delivered — reached a device
  • read — the user opened it

Accepts id or ref.

cURL
curl "https://wamyapp.com/api/v1/user.php?external_id=42" -H "Authorization: Bearer $WAMY_KEY"
# {"linked": true, "joined": true, "notifications": 2, "blocked": false, "muted": false, ...}

curl "https://wamyapp.com/api/v1/message.php?ref=order-5521-confirmed" -H "Authorization: Bearer $WAMY_KEY"
# {"id": 812, "status": "delivered", "pushed_devices": 2, ...}

4. Receive replies

Set a receiving address (a webhook) in the business panel and turn on “Customers can write to us”. Every reply, or tap on a quick-reply button, reaches your address like this:

POST https://your-app.example.com/wamy
X-Wamy-Timestamp: 1792800000
X-Wamy-Signature: sha256=…

{"event": "message", "id": 913, "app": "your-code",
 "from": {"external_id": "42", "email": null, "name": "Salem"},
 "text": "Great", "payload": "confirm_1042",
 "reply_to": {"id": 905, "ref": "appt-1042-reminder"},
 "file": null, "created_at": "2026-10-26T09:50:11+00:00"}
  • Answer with a success code from 200 to 299 within 6 seconds, otherwise we retry 5 times over two hours.
  • The address must be public https reachable from the internet. We never call addresses on internal networks.
  • Someone who has not linked (came from the contact link) reaches you with "external_id": null and their e-mail.
  • After you read a reply call POST /api/v1/read.php {"id": 913} so the user sees the two blue ticks.

Always verify the signature

The signature is HMAC-SHA256 of timestamp + "." + raw body with the signing secret from the business panel. Reject the request if the timestamp is more than 5 minutes old.

<?php
$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_WAMY_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_WAMY_SIGNATURE'] ?? '';
$mine = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, getenv('WAMY_WEBHOOK_SECRET'));
if (!ctype_digit($ts) || abs(time() - (int) $ts) > 300 || !hash_equals($mine, $sig)) { http_response_code(401); exit; }
$event = json_decode($raw, true);   // {"event": "message", "id": 913, "from": {...}, "text": "..."}
http_response_code(204);            // any 2xx = received

Contact link: people start the conversation

Every business has a contact link. Put it behind a “Message us on Wamy” button on your site, or print it as a QR code. Whoever opens it signs in with their e-mail if needed, and a conversation with you opens. No key and no code needed: it is ready in the business panel.

/api/v1/contact.phpPOST

To get it from code (or for each sender you send as): {"sender": {"id": "school-12", "name": "…"}}

  • open is true when replies are on and the team has someone who sees visitors. Without it the link does not work.
  • Flood guard: until you answer for the first time, a person may send at most 5 messages an hour.
cURL
curl -X POST https://wamyapp.com/api/v1/contact.php -H "Authorization: Bearer $WAMY_KEY"
# {"url": "https://wamyapp.com/start.php?to=your-code", "open": true}
HTML
<a href="https://wamyapp.com/start.php?to=your-code" target="_blank" rel="noopener">
  Message us on Wamy
</a>

Reply team

Who answers for your business inside Wamy: a receptionist, a teacher, a sales team. They open Wamy and find a “Replies” tab with everyone who wrote to you. The easy way: add them by e-mail in the business panel. From code:

/api/v1/agent.phpPOST
FieldMeaning
external_id
required
The person on your side. They must have linked their account first.
titleThe name people see on their replies (default: their name).
peopleA list of ids: they see only these people’s conversations (a teacher and the parents of their class). Without it they see everyone.
scope"guests": they see visitors only, people who are not your users yet.
activefalse removes them from the team.
cURL
curl -X POST https://wamyapp.com/api/v1/agent.php \
  -H "Authorization: Bearer $WAMY_KEY" -H "Content-Type: application/json" \
  -d '{"external_id": "u-15", "title": "Ms Huda, Grade 5 teacher", "people": ["p-101", "p-102"]}'

Attachments: photos and documents

A message carries one file: a photo (jpg, png, webp) or a document (pdf, doc/docx, xls/xlsx, ppt/pptx, txt, csv), up to 10 MB. The type is found from the content, not the name, and photos are redrawn so camera and location data drop out.

A file the user sends reaches your receiving address in the field "file": {"url", "name", "mime", "size"}. The link is signed and valid for 7 days, so download the file if you need it.

JSON
{"to": {"external_id": "42"}, "text": "Your invoice",
 "file": {"name": "invoice.pdf", "data": "<base64 of the file>"}}

Many senders with one key

A platform that serves many schools, branches or clinics? Add the field sender when you send and when you ask for a link, and each one shows to the user as its own chat with its own name and logo, created on first use. Users are linked to your business: their id on your side is the same across all senders.

  • Replies reach your address with "sender": {"id": "school-12"}.
  • The user’s state in one sender: user.php?external_id=42&sender_id=school-12.
  • Each sender has its own contact link: your system asks for it from contact.php.
JSON
"sender": {"id": "school-12",
           "name": {"ar": "مدرسة النور", "en": "Al Noor School"},
           "logo_url": "https://example.com/logo.png", "color": "#017672"}

Errors and limits

CodeMeaning
400 bad_jsonThe body is not valid JSON.
401 unauthorizedThe key is wrong, or the business is suspended.
404 recipient_not_foundThe user has not linked their account yet, or has not written to you.
409 blocked_by_userThe user blocked your business.
413 file_too_bigThe file is larger than 10 MB.
422 text_requiredNo text, title, image or file.
422 text_too_longThe text is longer than 4096 characters.
422 buttons_invalidMore than 3 buttons, a label longer than 30 characters, or an invalid link.
422 image_must_be_httpsThe image link must start with https.
429 rate_limitedYou went over the per-minute limit. The answer states the limit; retry shortly.

Tips

  • Send ref with every message, so retrying after a network failure is safe.
  • Do not stop your system when sending fails: log the error and carry on.
  • For the very important (a sign-in code) check the user’s state first and keep a backup channel.
  • Message only people who linked their account or wrote to you: that is what we enforce, and what people want.

Downloads

You need no library: everything above works with a plain HTTP request. For PHP there is one file that wraps the requests and verifies reply signatures.

WamyClient.php
PHP
require 'WamyClient.php';
$wamy = new WamyClient('https://wamyapp.com', getenv('WAMY_KEY'));
$url = $wamy->linkUrl((string) $user->id);                       // 1. link
$wamy->send(['external_id' => (string) $user->id],               // 2. send
    ['ar' => 'تم تأكيد طلبك', 'en' => 'Your order is confirmed'],
    ['ref' => 'order-5521-confirmed']);
$ok = WamyClient::verifyWebhook($raw, $ts, $sig, $secret);       // 4. verify a reply