Skip to content
Tadhkir
▾
Log inStart Free
Developers

Partner API Reference

Send events to Tadhkir from your own system — endpoints, verticals, errors

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_ref does 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_ref is 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_key is required. If you send the same key again, Tadhkir returns DUPLICATE_EVENT and does not send a second message — this protects you from network-retry double-sends.
  • data.link is 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()