Agentech Assistant

Developer API — send WhatsApp from your own system

The Developer API lets your own software — your online shop, CRM, booking system — send WhatsApp messages through the number you connected to Agentech, and tells your server when a customer writes to you or when a message you sent is delivered, read or fails. It's for your developer: it needs someone who can make HTTPS requests and run a web server. You don't need it to use Agentech; the dashboard does everything the API does.

Everything is set up on the Developer API page in your dashboard.


Who does what

WhoWhat they do
YouCreate a secret key, give it to your developer, and set the webhook URL where your server should receive events. Keep the key secret and revoke it if it leaks.
Your developerCalls the API with the key, and runs the webhook endpoint that receives events and checks their signature.
MetaDelivers each message, and can refuse one (number not on WhatsApp, template problem, payment problem on your WhatsApp Business account). Meta bills your WhatsApp Business account for what it delivers, exactly as for campaigns.
AgentechChecks the key, picks the number, checks the template and the 24-hour window, sends to Meta, counts the message against your allowance, keeps each message's status as Meta reports it, and forwards events to your webhook with retries.

What you need first

  • WhatsApp connected — see Connect WhatsApp.
  • API messages on your account. They open the API and let you create a secret key; without them, creating a key is refused with "API keys need API messages on your plan. Contact us to add them." Campaign messages don't open the API: the two are separate allowances.
  • For template messages: at least one approved template (see WhatsApp templates).

Everything your system sends through the API uses your API messages, counted by Meta's category:

  • A marketing template counts 5.
  • A utility or authentication (verification code) template counts 1.
  • A free-text (service) message inside the 24-hour window counts 1.

So 1,000 API messages are 1,000 verification codes or order updates, or 200 marketing messages, or any mix. If Meta counts a message under another category than its template's, the difference moves inside your API messages (a marketing message Meta counted as utility gives 4 back). A message Meta accepts and then fails is given back. Meta's own fee for each message is billed to your WhatsApp Business account, apart from this. API messages never borrow from your Campaign messages, or the other way round; both show on Usage.


The top of the page

  • Which number: the number picker at the top (shown with one number too) decides which number the Message log shows and Test send sends from. Your own system names the number with numberId; secret keys and the webhook belong to the whole workspace, not to one number.
  • "No WhatsApp number connected." shows when no number is connected. Fix: connect one under WhatsApp → Connect; until then Test send is off and every API send is refused.
  • API messages: a meter at the top, "API messages: 4,120 of 5,000 used", and under it the rule: "A marketing message counts as 5; utility, authentication and service count as 1. Meta's own fees are billed to your WhatsApp Business account."
  • Running low, used up or ending soon: a warning at the top names the allowance ("Only 150 API messages left. Top up before they run out.", "Your API messages are used up for this month…", "…end in 3 days.") with a Top up or Renew button that opens Support → Contact us.
  • Without API messages the page says "The Developer API sends through API messages: everything your own system sends. Without them on your plan, you can’t create keys or send.", with a Request access button; creating keys and Test send are off.

Secret keys

On Developer API → Secret keys, press Create key and give it a name (for example "Shop"). The full key, starting sk_, is shown once — copy it then. After that the page only shows its first characters, when it was created and when it was last used.

  • Keep it secret. Anyone with the key can send WhatsApp messages from your number. Never put it in a website's page or a mobile app — only on your server.
  • This is not the widget key on the Keys page. The widget key is public by design and can't send WhatsApp; the secret key can't run the widget.
  • Revoke a key the moment you think it leaked. It stops working immediately; create a new one for your developer.

Sending a message

Every request sends the key in the Authorization header:

Authorization: Bearer sk_your_key

Base URL: https://api.assistant.agentech.tech.

Template message (any time)

POST /api/v1/whatsapp/messages
{
  "to": "+201001234567",
  "type": "template",
  "template": {
    "name": "order_update",
    "language": "en_US",
    "variables": { "1": "Sara", "2": "#1042" }
  }
}
  • name and language must be exactly as GET /api/v1/whatsapp/templates lists them (for example en_US, not en).
  • variables fills the template's {{1}}, {{2}}… in order — the same blanks as the campaign recipient sheet.
  • A template with a text header takes "header": { "text": "…" }; one with an image, video or document header takes "header": { "mediaUrl": "https://…" }.
  • A button that needs a value (for example a link ending in a tracking code) takes "buttons": { "0": "track/1042" }, keyed by the button's position starting at 0.

Free-text message (only within 24 hours)

POST /api/v1/whatsapp/messages
{ "to": "+201001234567", "type": "text", "text": { "body": "Your order has shipped." } }

WhatsApp only allows free text within 24 hours of the customer's last message to you. Outside that window, send a template. A free-text message sent through the API also appears in that customer's conversation in your Inbox, so your team sees it. It doesn't turn off your assistant.

Which number sends

With one WhatsApp number, you don't need to say. With more than one, add "numberId" — the list comes from GET /api/v1/whatsapp/numbers. The template must belong to that number's WhatsApp Business account.

What comes back

201 { "id": 881, "wamid": "wamid.HBg…", "status": "sent", "to": "+201001234567" }

"sent" means Meta accepted it, not that it arrived. Follow it with GET /api/v1/whatsapp/messages/881, or wait for the message.status event on your webhook.

Other calls

CallReturns
GET /api/v1/whatsapp/numbersYour connected numbers: id, phoneNumber, displayName
GET /api/v1/whatsapp/templates?numberId=12That number's approved templates: name, language, category, how many variables, which buttons need a value
GET /api/v1/whatsapp/messages/{id}One message you sent through the API: status (sent, delivered, read, failed), and Meta's error when it failed

The API allows up to 60 requests a minute per key — sends and lookups together.


Sending the same message twice by mistake

Add an Idempotency-Key header (any unique text up to 255 characters, for example your order id) to POST /api/v1/whatsapp/messages. If your system retries the same request with the same key within 24 hours, you get the first message's answer back: nothing is sent again and nothing is counted again. Reusing a key with a different request is refused with idempotency_key_reused.

Message log

The Developer API page shows a Message log: messages sent through the API and messages your customers sent to your numbers, with time, direction, number, customer phone, template or text, and status (and Meta's reason when one failed). Filter by number (with two or more), direction, status and dates. "Only the last 90 days are kept, and verification codes inside authentication templates are always masked."

Test send

The Test send box on the same page sends one message through the API from the dashboard, with no code: pick the number (with two or more), type a phone number, then write free text or pick an approved template and fill its variables. It uses your API messages like any other send. The messages it can show:

"This customer hasn't messaged your WhatsApp number in the last 24 hours, so a free-text message can't be sent. Ask them to message you first, or send an approved template instead."

Fix: message your WhatsApp number from that phone first, then try again, or use a template.

"This needs API messages on your plan. Contact us to add them."

Fix: on Usage, press Request access next to API messages, or email us.

"You've used up your API messages for this month. Contact us to top up."

Fix: a top-up, or wait for next month's allowance. A marketing template needs 5 left; a utility, authentication or free-text message needs 1.

"Could not send the message ([code])."

Fix: look the code up under Errors below.


Webhook — hearing back

On Developer API → Webhook, enter an https URL on your server, switch it on and save. We then send it:

EventWhen
message.receivedA customer writes to any of your WhatsApp numbers
message.statusA message you sent through the API is delivered, read or fails (campaign statuses stay in the dashboard)
number.disconnectedOne of your WhatsApp numbers is disconnected (you pressed Disconnect, or Meta removed our access). data: credentialId, number, reason, at
pingYou press Send test event

Each event is a POST with a JSON body:

{ "id": "3f1c…", "type": "message.received", "createdAt": "2026-09-24T10:15:00Z",
  "data": { "numberId": 12, "from": "+201001234567", "wamid": "wamid…",
            "messageType": "text", "text": "Is it in stock?", "timestamp": "…",
            "userId": "EG.2123983504836150",
            "customer": { "name": "Ahmed Hassan", "username": null } } }

messageType is WhatsApp's own (text, image, audio, video, document, sticker), and text is only filled for a text message. Locations, contact cards, reactions and button replies aren't sent yet. customer.name is the name the customer set in WhatsApp, as they wrote it; it is null when WhatsApp didn't send one.

from can be null. WhatsApp lets people use a username instead of showing their number. When such a customer writes to you for the first time, WhatsApp doesn't give us their number: from is null, userId (WhatsApp's own ID for this customer, the same every time they write to you) is filled, and customer.username is their WhatsApp @username. If your system keys customers on from, key them on userId when from is null. Customers who have written to you before keep arriving with their number.

message.status data: id (the API message id), wamid, to, status, errorCode, errorTitle.

Check the signature. Every event carries X-Agentech-Signature: t=1727172900,v1=5e8f…. Compute HMAC-SHA256 of "<t>.<raw body>" with the signing secret shown on the page, and compare it to v1. Reject events that don't match or whose t is more than 5 minutes old. Rotate the secret if it leaks — the old one stops working at once.

Answer with any 2xx quickly. Anything else, or no answer within 10 seconds, is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then marked Failed. The page lists recent deliveries with the status your server returned. The same event can arrive twice — use its id to ignore repeats.


Errors

Every error comes back as { "error": { "code": "…", "message": "…" } }. Your developer should act on code; message is a hint in English.

unauthorized

The key is missing, mistyped, revoked, or it's the widget key. Fix: send Authorization: Bearer sk_… with an active secret key from Developer API.

module_not_active

Your account doesn't hold API messages, or they've ended. Every API send uses them, whatever the template's category. Fix: on Usage, press Request access next to API messages, or email us.

quota_exhausted

Your API messages for this month, and any top-up, are used up (a marketing template needs 5 left, anything else 1). They never borrow from your Campaign messages. Fix: a top-up or next month's allowance — see Usage and plans.

window_closed

A free-text message to a customer whose last message was more than 24 hours ago (or who never wrote to you). The response includes when the window closed. Fix: send a template instead.

message_not_found

No message with that id was sent through the API from your account. Fix: use the id from the send's response.

number_required

You have more than one WhatsApp number and didn't say which. Fix: add numberId.

number_not_found / number_disconnected

That numberId isn't one of yours, or it's disconnected. Fix: check GET /api/v1/whatsapp/numbers; reconnect the number on Channels if it dropped.

template_not_found / template_not_approved

No template with that name and language on that number, or Meta hasn't approved it (or paused it). Fix: check GET /api/v1/whatsapp/templates, or the Templates page and press Sync from Meta.

template_variables_mismatch

The variables, header or button values don't match the template. Fix: send exactly the template's variables, numbered from "1".

idempotency_key_reused

The Idempotency-Key you sent was already used in the last 24 hours with a different request. Fix: use a new key for a new message.

validation_failed

Something in the request is missing or malformed — usually to not in international format (+ and country code). Fix: the message says which field.

rate_limited

More than 60 requests in a minute on one key (checking a message's status counts too). Fix: slow down and retry after a minute.

meta_rejected

Meta refused the message. The error includes Meta's own metaCode and metaTitle. The common ones mean the same as for campaigns — see Why did a recipient fail?. You aren't charged for a message Meta refuses, and a template message Meta accepts and then fails is refunded automatically.

My webhook shows "Failed"

Your server didn't answer 2xx within 10 seconds, five retries in a row. Fix: check the HTTP status shown on the delivery, make sure the URL is reachable from the internet over https, then press Send test event. Events that already failed aren't resent.


When to email us

If a code above doesn't explain what you see, email admin@agentech.tech or use Support → Contact us in the dashboard. Include the message id or the time of the request, the number you sent to, and the full error body. Never send us your secret key — we don't need it.