API and webhooks
Connect Ijtimaa to your own systems: CRM, HR system or website.
Keys
A workspace admin makes keys in Admin → Developers. Each key is shown once. Read-only keys can list; write keys can also book and cancel. Send it with every request:
curl https://ijtimaa.chat/api/v1/meetings \ -H "Authorization: Bearer mj_your_key"
Answers are JSON: { "data": … }, or { "error": "…" } with status 400, 401, 403, 404, 409 or 429. Each key may make 600 requests per hour.
Endpoints
| Method | Path | What it does |
|---|---|---|
GET | /api/v1 | Which workspace the key belongs to. |
GET | /api/v1/people | Everyone in the workspace, with their booking page. |
GET | /api/v1/meeting-types?person=<slug> | Active meeting types (lengths, seats, questions, location). |
GET | /api/v1/slots?type=<id>&from=YYYY-MM-DD&to=YYYY-MM-DD&duration=<min> | Free start times (UTC), in the host’s dates. |
GET | /api/v1/meetings?from=<ISO>&to=<ISO>&status=confirmed|requested|cancelled&person=<slug> | Bookings, oldest first (max 500). |
GET | /api/v1/meetings/<id> | One booking. |
POST | /api/v1/bookings | Book a meeting type for someone. Body: typeId, start, name, email, timezone, duration, note, answers, guests. |
POST | /api/v1/meetings/<id>/cancel | Cancel as the host (or decline a request). Body: reason. |
POST | /api/v1/meetings/<id>/no-show | Mark that the guest didn’t come. Body: no_show (true/false). |
Booking example
curl -X POST https://ijtimaa.chat/api/v1/bookings \
-H "Authorization: Bearer mj_your_key" -H "Content-Type: application/json" \
-d '{"typeId":"…","start":"2026-11-08T07:00:00Z","name":"Sara Ali","email":"sara@example.com","timezone":"Asia/Riyadh"}'Webhooks
Add an https address in Admin → Developers and pick events. We POST JSON like { "event": "booking.created", "created_at": "…", "data": { …the booking… } }. Events:
booking.createdbooking.requestedbooking.rescheduledbooking.cancelledbooking.declinedmeeting.no_show
Every request carries X-Ijtimaa-Signature: t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256 of"<t>.<raw body>" with your webhook secret. Check it, and reject old timestamps:
const [t, v1] = sig.split(',').map((p) => p.split('=')[1]);
const ok = crypto.timingSafeEqual(
Buffer.from(v1, 'hex'),
crypto.createHmac('sha256', SECRET).update(t + '.' + rawBody).digest());Answer with any 2xx status within 10 seconds. Otherwise we try again after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. After 20 failures in a row the webhook is switched off; switch it back on in Admin.