Tadhkir Partner API — Reference (V1, Enterprise-gated)
This is handed directly to each enterprise partner's engineer at onboarding — there is no public docs site yet (that's V2, see
docs/roadmap-v2.md).
Overview
Your software sends events to Tadhkir when something happens for your customers (an appointment is booked, an order is ready, rent or a payment is due). Tadhkir picks the right approved WhatsApp template, fills in the details, and sends or schedules the reminder. You never touch WhatsApp, templates, or Meta approval directly.
Base URL: https://tadhkirapp.com/api/v1
Direction: your system → Tadhkir. Not the reverse.
Who is billed
Every event is sent on behalf of one of your clients, and that client is a Tadhkir business with its own plan. The message is counted against their allowance, not yours — you are never metered on message volume. You earn commission on your clients' subscriptions instead.
This is why client_ref is required on every request: it tells us whose plan to
draw from. See Identifying the client.
Authentication
Authorization: Bearer ta_live_xxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
You're issued two keys at onboarding:
ta_live_*— sends real WhatsApp messages.ta_test_*— same endpoint, same validation, but routes to a sandbox that never actually sends a WhatsApp message. Use this while integrating.
Keys are shown to you once, at creation — Tadhkir only stores a hash, so if you lose a key, ask for a new one to be issued (the old one is deactivated).
Identifying the client
Every request must name which of your clients it is for, using your own id for them — whatever primary key they have in your system. You do not need to know anything about Tadhkir's internal ids.
"client_ref": "CLINIC-4471"
That id is mapped to a Tadhkir business when the client is connected in your partner portal. The mapping is what lets us bill the right plan, so:
- The client must have finished onboarding with Tadhkir and be connected in your portal before you can send for them.
- If
client_refdoes not resolve, the request is rejected rather than sent. We will not deliver a message we cannot attribute to a plan.
Three distinct errors tell you which step is missing — see Errors.
The endpoint
POST /v1/events
Event types
| Event | Behavior |
|---|---|
appointment.booked |
Schedules 3 reminders: 72h, 24h, and 1h before data.appointment_date + data.appointment_time |
appointment.completed |
Sends immediately (e.g. a thank-you / follow-up) |
order.ready |
Sends immediately |
renewal.due |
Schedules 3 reminders: 7 days, 3 days, and 1 day before data.due_date |
payment.due |
Schedules 3 reminders: 7 days, 3 days, and 1 day before data.due_date |
Example request
POST https://tadhkirapp.com/api/v1/events
Authorization: Bearer ta_live_xxxx
{
"event": "appointment.booked",
"idempotency_key": "cloudpital_appt_9847362",
"client_ref": "CLINIC-4471",
"vertical": "optical",
"customer": {
"name": "Ahmed Al-Rashidi",
"phone": "+966501234567",
"language": "ar"
},
"data": {
"appointment_date": "2026-06-10",
"appointment_time": "10:00",
"link": "https://cloudpital.example.com/booking/9847362"
}
}
client_refis required — your id for the client this event belongs to. Without it we cannot tell whose plan to bill, so the request is rejected.idempotency_keyis required. If you send the same key again, Tadhkir returnsDUPLICATE_EVENTand does not send a second message — this protects you from network-retry double-sends.data.linkis optional and is used as the "more info / manage your booking" link in the message, if your template includes one.
Vertical + market
Your account has a fixed market (gulf / uk / usa / africa), set at onboarding.
If you also have a partner portal account, the market and company name on that
record are authoritative — they are the single source of truth, so the two can
never disagree. vertical is per-event, so one account can send events across multiple industries. Not every vertical is mapped in every market yet — if you get UNSUPPORTED_VERTICAL, ask us to add the mapping (this is expected to grow over time, not something we pre-built exhaustively).
Supported vertical values (not all resolve in every market yet): dental, optical, physiotherapy, general_clinic, dermatology, cardiology, orthopedics, womens_health, pediatrics, pharmacy, mental_health, nutrition, salon, spa, nail, barbershop, aesthetics, gym, pilates, driving_school, school, tutoring, university, elearning, property, real_estate, auto, hotel, restaurant, legal, accounting, insurance, vet, pet_grooming, events, home_services.
Languages
en, ar, fr, sw, de, es, hi, ur, bn, fil. Defaults to en if omitted. If no template exists for your vertical + language combination, Tadhkir falls back to English automatically and logs the gap internally — your request still succeeds.
Errors
Every error follows the same shape:
{
"error": "UNSUPPORTED_VERTICAL",
"message": "Vertical 'pharmacy_chain' is not supported for market 'gulf'.",
"field": "vertical",
"docs": "https://docs.tadhkirapp.com/errors/UNSUPPORTED_VERTICAL"
}
| Code | Meaning |
|---|---|
INVALID_API_KEY |
Missing or wrong API key |
INVALID_PHONE |
customer.phone isn't in E.164 format (+ then country code then number) |
UNSUPPORTED_VERTICAL |
That vertical isn't mapped for your market yet |
UNSUPPORTED_LANGUAGE |
Language code not recognized |
TEMPLATE_NOT_FOUND |
No approved template exists, even after falling back to English |
CUSTOMER_OPTED_OUT |
Defined for spec parity — not yet enforced in V1 (no opt-out tracking wired up for partner-sourced customers yet) |
DUPLICATE_EVENT |
Same idempotency_key already processed — not an error, no action needed |
RATE_LIMIT_EXCEEDED |
Too many requests — check retry_after (seconds) and back off |
INVALID_EVENT_TYPE |
event isn't one of the 5 supported types |
MISSING_FIELD |
A required field is missing — check field |
When `client_ref` does not resolve
All three return MISSING_FIELD with field: "client_ref"; the message
distinguishes them, because each needs a different person to act:
| Message says | What it means | Who fixes it |
|---|---|---|
| not linked to a partner account | Your API key exists but is not connected to your partner portal account | Tadhkir support — ask us to link them |
| does not match any of your connected clients | We have no mapping for that client_ref |
You — connect the client in your partner portal |
| has not finished onboarding | The client is mapped but never completed Tadhkir signup, so has no plan | Your client — they finish signup and choose a plan |
No message is sent in any of these cases. A send we cannot attribute to a plan is refused rather than delivered and billed to nobody.
Message allowance
Messages count against your client's plan, not yours. If a client reaches their monthly limit, sends for that client stop until they buy credits or enable overage billing — other clients are unaffected. Rate limits (below) are separate and apply per API key.
Rate limits
Standard tier: 100 events/minute. Enterprise tier: 1,000 events/minute. Limits are enforced per API key over a rolling 60-second window. Implement standard exponential backoff on 429.
What's not in V1
- No outbound webhook confirmations back to your system yet (delivery status, customer replies) — most partners don't need this on day one; ask if you do.
- No SDKs yet — raw HTTP only. A ~30-line wrapper function is enough in any language (see example below).
- No self-serve signup or public docs site — onboarding is manual (this doc + your two keys, sent directly).
Minimal integration example (Python)
import requests
def notify_tadhkir(event_type, customer, data, vertical, idempotency_key, language="en"):
return requests.post(
"https://tadhkirapp.com/api/v1/events",
headers={"Authorization": "Bearer ta_live_xxxx", "Content-Type": "application/json"},
json={
"event": event_type,
"idempotency_key": idempotency_key,
"vertical": vertical,
"customer": {**customer, "language": language},
"data": data,
},
).json()