OdontoX Mobile API — Complete Specification
Base URL:https://api.odontox.io/api/v1/protected
Auth:Authorization: Bearer {JWT_TOKEN}
Last Updated: 2026-05-10
Table of Contents
Appointments API
Shared Schema
All appointment objects contain these fields:GET /appointments
Query Parameters:startDate(optional, YYYY-MM-DD) — filter appointments on or after this dateendDate(optional, YYYY-MM-DD) — filter appointments on or before this datelimit(optional, 1-100, default 50) — pagination limitoffset(optional, default 0) — pagination offsetdoctorId(optional, UUID) — filter by doctor
GET /appointments/available-slots
Required Query Parameters:date(YYYY-MM-DD) — the date to check availability for
doctorId(UUID) — filter available slots for a specific doctor
- 30-minute buffer is applied on both
/appointmentsand/available-slotsendpoints (deployed 2026-05-09) - Past time slots are filtered out automatically
- Respects clinic operating hours from
clinics.operating_hoursJSON - Returns unavailable slots with reasons
POST /appointments
Request Body:- Patient role users automatically force
status: 'requested'(staff approval required) - Staff (doctor/receptionist) can create
status: 'scheduled'directly - Applies 30-minute buffer validation with existing appointments
- Checks clinic operating hours and business day
- Detects doctor + time conflicts
- Detects operatory/room conflicts
- Triggers email + WhatsApp confirmation (for scheduled status)
- Creates notifications for patient, doctor, and receptionists
- Records activity in audit trail
GET /appointments/:id
Path Parameters:id(UUID) — appointment ID
PUT /appointments/:id
Full update endpoint — replace all mutable fields. Request Body:- Patients cannot use PUT (403 error) — use PATCH /appointments/:id/status instead
- Validates all same checks as POST (no past dates, conflicts, business hours)
- Sends email + WhatsApp rescheduling notifications
- Records activity as ‘rescheduled’ if date or time changed
- Does NOT support partial updates — provide all required fields
PATCH /appointments/:id/status
Status-change-only endpoint with optional invoice generation. Request Body:requested→scheduled,confirmed,cancelledscheduled→confirmed,in_progress,cancelledconfirmed→in_progress,cancelledin_progress→completed,cancelledcompleted→ (immutable, but can be audited)cancelled→ (immutable)no_show→cancelled(reschedule with new appointment)
- Invoice auto-generation if
invoiceobject provided - Sends email notification to patient
- Sends WhatsApp notification if configured
- Records activity as ‘status_changed’
- Creates clinic notifications for admins/receptionists
- Updates audit trail
DELETE /appointments/:id
Soft-delete: marks appointment as ‘cancelled’. Response:Messages API
Shared Message Schema
GET /messages
List all messages with rich filtering and pagination. Query Parameters:patientId(optional, UUID) — filter by patientstatus(optional, ‘unread’ | ‘read’) — filter by read statustype(optional, ‘sms’ | ‘email’ | ‘portal’) — filter by message channeldirection(optional, ‘inbound’ | ‘outbound’) — filter by directionpage(optional, default 1) — pagination pagelimit(optional, 1-100, default 50) — results per page
GET /messages/conversations
Get list of all conversations grouped by participant. Query Parameters:type(optional, ‘patient’ | ‘staff’) — filter conversation type
- Conversations are grouped by participant (not individual messages)
- Separate conversations for Doctor vs Clinic staff (for patient view)
unreadCountis from the logged-in user’s perspectivehasWhatsAppandhasPortalindicate available channels- Sorted by most recent message time
GET /messages/conversations/:id
Get all messages in a conversation. Path Parameters:id(string) — conversation ID (format:patient-{uuid}orstaff-{uuid})
POST /messages
Send a message to a patient or staff member. Request Body:- Direction inferred from user role (patient = ‘inbound’, staff = ‘outbound’)
- Patient users automatically set
patientIdto their own patient record - Conversation initialization rules:
- Patients → Staff: always allowed
- Receptionists → Patients: always allowed
- Doctors → Patients: only allowed once per conversation (can reply to existing)
- Admins/Superadmins: always allowed
- Email notification sent if:
- First message ever in conversation, OR
- Last message > 30 minutes ago (new session)
- Real-time SSE event published to clinic subscribers
- Creates clinic notifications for relevant staff
GET /messages/contacts
Get list of contacts the user can message. Response (Patient):GET /messages/receptionists
Get list of receptionists in user’s clinic (for patients to contact). Response:PUT /messages/:id
Update message metadata (star/unstar, mark read). Request Body:DELETE /messages/:id
Soft-delete a message (marks as deleted, not physically removed). Response:POST /messages/conversations/:id/read
Mark all messages in a conversation as read. Path Parameters:id(string) — conversation ID (format:patient-{uuid}orstaff-{uuid})
POST /messages/conversations/:id/send
Send a message to a conversation (alternative to POST /messages). Path Parameters:id(string) — conversation ID
POST /messages/whatsapp/send
Send a WhatsApp message via Zepto SMS gateway. Request Body:Mobile Permissions API
GET /mobile/permissions
Get modules enabled for current user’s role. Response:| Role | Modules |
|---|---|
| patient | appointments, medical_history, messages, prescriptions, invoices, notifications |
| doctor | appointments, patients, clinical_notes, dental_charts, prescriptions, messages, invoices, treatment_plans, notifications |
| receptionist | appointments, patients, messages, invoices, notifications |
| admin | All modules |
GET /mobile/permissions/clinic/:clinicId/:role
Get modules for a specific role at a clinic (admin endpoint). Response:PATCH /mobile/permissions/clinic/:clinicId/:role/:module
Toggle a module for a role at a clinic (admin endpoint). Request Body:Common Patterns
Authentication
All requests require theAuthorization header:
Error Responses
Standard error format:| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad request (validation error) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not found |
| 409 | Conflict (e.g., appointment time conflict) |
| 500 | Server error |
Pagination
Responses with pagination include:Date/Time Format
- Dates:
YYYY-MM-DD(e.g.,2026-05-15) - Times:
HH:MM24-hour format (e.g.,14:30) - Timestamps: ISO 8601 (e.g.,
2026-05-10T10:30:00.000Z)
Clinic Context
Multi-clinic users can specify clinic with header:Summary of Query Contracts
Message Query Contracts
| Endpoint | Query Support | Purpose |
|---|---|---|
| GET /messages | patientId, status, type, direction, page, limit | List all messages with filters |
| GET /messages/conversations | type (‘patient’ | ‘staff’) | Group conversations by participant |
| GET /messages/conversations/:id | None (uses path param) | Get messages in a conversation |
| GET /messages/receptionists | None | List clinic receptionists |
| GET /messages/contacts | None | List messageable contacts |
| GET /messages/:id | None (uses path param) | Get single message |
Answer to User Questions
Q: Does GET /messages support conversationId/threadId?A: No. Queries use
patientId only. Use GET /messages/conversations to get conversation groupings, then GET /messages/conversations/:id to get thread messages.
Q: Is /mobile/permissions still needed?A: Yes. It’s used to determine which modules are available in the mobile app per role. Current response:
{ modules: string[] } listing enabled features.
Q: Should I use PATCH /appointments/:id or PUT /appointments/:id?A:
- PATCH /appointments/:id/status — for status changes only (preferred for mobile)
- PUT /appointments/:id — for full appointment updates (requires all fields, not mobile-friendly)
Deployment Notes
Last Deployed Features:- ✅ 30-minute buffer on
/appointments+/available-slots(2026-05-09) - ✅ Message direction inference from user role (2026-05-09)
- ✅ Doctor schedule integration for slot availability
- ✅ WhatsApp + Email notifications on appointment changes
- ✅ Real-time SSE events for messages
- ✅ Conversation grouping with separate Doctor/Clinic threads for patients
Updated: 2026-05-10
Maintainer: Development Team

