Communications

Communications

This page documents the current outbound communication behavior used by the API.

Invoice Email Provider

Invoice emails and patient import invitation emails are sent through Resend.

Runtime wiring:

  • cmd/main.go creates services.NewResendEmailService(cfg).
  • The router stores it as EmailSender.
  • Invoice send flows call rctx.EmailSender.SendInvoice(...).
  • Patient import invitation flows call SendPatientInvitation(...) when the configured email sender supports it.
  • services/resend.go sends a POST request to the Resend /emails API.

Configuration:

Environment VariableRequiredDefaultPurpose
RESEND_API_KEYYes-Bearer token used for Resend API calls
RESEND_FROM_EMAILYes-Sender email. Plain addresses are normalized to Aivida <email>
RESEND_API_BASENohttps://api.resend.comResend API base URL

Invoice Email Flow

Invoice emails are generated with a PDF attachment and sent through Resend.

Manual invoice email flow:

  1. Call invoices/send-email.
  2. Backend loads the invoice, organization, and patient.
  3. Backend renders the invoice PDF.
  4. Backend calls EmailSender.SendInvoice.
  5. Resend sends the email with the PDF attachment.
  6. Backend updates emailedAt on the invoice after a successful send.

Automatic invoice email flow after successful payment:

  1. The payment success endpoint creates or updates the payment.
  2. Backend calls payments/common.SendInvoiceForPayment.
  3. Backend creates a paid invoice for the payment source when needed.
  4. Backend calls invoices/send-email.
  5. The invoice is delivered through Resend.

Automatic invoice sending is used by these current success flows:

FlowTrigger
Appointment payment successappointments/mark-payment-success
Appointment create with completed paymentappointments/create
Provider order payment successproviderOrders/markPaymentSuccess unless skipInvoice is true
Cart checkout payment successcart/payment-success unless skipInvoice is true
Existing invoice paymentPayment source type invoice

Patient Import Invitation Flow

Patient import invitation emails are sent after patientProfiles/importRecords successfully creates or updates an imported patient profile with an email address.

Flow:

  1. Backend validates that the doctorId belongs to the organizationId.
  2. Backend creates or updates each imported patient profile.
  3. For each successfully created or updated patient with an email address, backend attempts one invitation email per unique email in the import batch.
  4. The email subject says that the importing doctor is available on Aivida.
  5. The email body tells the patient that the doctor added their details to Aivida and is available there for healthcare needs.

This email is best-effort. If the email sender is not configured, does not support patient invitations, or Resend returns an error, the import response is still returned and the failure is logged.

Module-Based Notifications

Use the notifications namespace for in-app notification records and module-based communication routing. The frontend can send module to group notifications by product area, for example appointments, providerOrders, labOrders, billing, or hrmPayroll.

module is a frontend-friendly alias for relatedEntityType. If both module and relatedEntityType are sent, relatedEntityType wins.

Supported Notification Channels

ChannelPurposeNotes
pushMobile push notificationStore the notification with channels: ["push"]. Devices are managed through pushDevices using Expo push tokens.
smsSMS communicationStore the notification with channels: ["sms"]. Use deliveryState, deliveryError, and related entity fields for provider tracking/audit.
emailEmail communicationFor invoice and invitation emails, use the dedicated email flows documented above.
in_appApp notification centerStore the row for app inbox/list views.

The notifications.create API records the communication intent and delivery metadata. Push/SMS delivery workers or provider integrations can use channels, recipientStackUserId, module, relatedEntityId, and dedupeKey to process and update delivery state.

Create Appointment Push Notification

curl -X POST https://api.aivida.in/api \
  -H "Content-Type: application/json" \
  -H "x-stack-access-token: <access_token_optional>" \
  -d '{
  "namespace": "notifications",
  "apiName": "create",
  "data": {
    "organizationId": "org_123",
    "recipientStackUserId": "patient_stack_user_123",
    "recipientRole": "patient",
    "type": "appointment_reminder",
    "title": "Appointment reminder",
    "message": "Your appointment starts in 15 minutes.",
    "module": "appointments",
    "relatedEntityId": "appointment_123",
    "channels": ["push", "in_app"],
    "navigationTarget": "appointment_details",
    "navigationParams": {
      "appointmentId": "appointment_123"
    },
    "deliveryState": "PENDING",
    "dedupeKey": "appointment_123:reminder_15m"
  }
}'

Create Appointment SMS Notification

curl -X POST https://api.aivida.in/api \
  -H "Content-Type: application/json" \
  -H "x-stack-access-token: <access_token_optional>" \
  -d '{
  "namespace": "notifications",
  "apiName": "create",
  "data": {
    "organizationId": "org_123",
    "recipientStackUserId": "patient_stack_user_123",
    "recipientRole": "patient",
    "type": "appointment_sms_reminder",
    "title": "Appointment SMS reminder",
    "message": "Reminder: your Aivida appointment is scheduled for 10:30 AM.",
    "module": "appointments",
    "relatedEntityId": "appointment_123",
    "channels": ["sms"],
    "deliveryState": "PENDING",
    "dedupeKey": "appointment_123:sms_reminder"
  }
}'

List Notifications by Module

Use module when the app needs a module-specific notification center, for example only appointment notifications.

curl -X POST https://api.aivida.in/api \
  -H "Content-Type: application/json" \
  -H "x-stack-access-token: <access_token_optional>" \
  -d '{
  "namespace": "notifications",
  "apiName": "listByOrganization",
  "data": {
    "organizationId": "org_123",
    "module": "appointments",
    "page": 1,
    "pageSize": 20
  }
}'

Without module, notifications.listByOrganization returns all organization notifications.

Appointment Module Communication

Appointment communications are sent from the appointment APIs and the scheduled reminder worker. All appointment push notification records use relatedEntityType: "appointment" and navigate to appointment_detail.

Current Appointment Scenarios

ScenarioTriggerPatient push / in-appPatient SMSPatient emailDoctor push / in-appDoctor email
Appointment created by appointments/createAppointment row is createdOnly when the booking is already paid, through the confirmation flowOnly when the booking is already paid, through the confirmation flowInvoice email only if payment/invoice flow succeedsSent immediately: Appointment createdSent if Resend is configured and doctor email exists
Appointment created as scheduled without paid bookingappointments/create with status scheduled and not paidA scheduled appointment_booked notification is queued for immediate processingA scheduled appointment_booked SMS is queued for immediate processingNo appointment emailSent immediately: Appointment createdSent if Resend is configured and doctor email exists
Pending appointment createdappointments/create-pendingNo current automatic pushNo current automatic SMSNo current automatic emailNo current automatic doctor notificationNo current automatic doctor email
Doctor-created appointmentappointments/create-by-doctorNo current automatic pushNo current automatic SMSNo current automatic emailNo current automatic notificationNo current automatic email
Payment marked successfulappointments/mark-payment-success, payment success handler, or paid appointments/createSent to patient: Appointment bookedSent to patient using appointment confirmation DLT templateInvoice email may be sent by invoice flow; no separate appointment confirmation emailNo extra doctor notification from this flowNo extra doctor email from this flow
Status changed to confirmedappointments/update and previous status was not confirmedNo current automatic pushSent to patient using appointment confirmation DLT templateNo current automatic emailNo current automatic doctor notificationNo current automatic doctor email
Status changed to cancelledappointments/update and previous status was not cancelledNo current automatic patient pushSent to patient using appointment cancellation DLT templateNo current automatic patient emailSent only when the actor is the patient/customer or matches the appointment patientSent to doctor if Resend is configured and doctor email exists, same actor rule as push
Appointment rescheduled with dedicated APIappointments/reschedule and date/time/duration changedSent to patient: Appointment rescheduledSent to patient using appointment rescheduled DLT templateSent to patient if Resend is configured and patient email existsSent to doctor: Appointment rescheduledSent to doctor if Resend is configured and doctor email exists
Appointment date/time changed through generic updateappointments/update with changed date/time/durationNo immediate reschedule push from this endpointNo immediate reschedule SMS from this endpointNo immediate reschedule email from this endpointNo immediate doctor reschedule notification from this endpointNo immediate doctor email from this endpoint
Consultation startedappointments/update status changes to in_progress, or appointments/trigger-call-join-notificationSent to patient: Consultation startedSent to patient using consultation-started templateSent to patient if Resend is configured and patient email existstrigger-call-join-notification also sends doctor Patient joined consultationSent to doctor if Resend is configured and doctor email exists
Patient joined callappointments/patient-joined-callNo patient notificationNo patient SMSNo patient emailSent to doctor: Patient joined consultationSent to doctor if Resend is configured and doctor email exists

Appointment Reminder Schedule

The reminder service schedules these jobs when an appointment has a valid patient stackUserId, valid date/time, and is not cancelled:

ReminderRecipientScheduled timeChannels
appointment_bookedPatientImmediately, only when ScheduleForAppointment is usedPush/in-app and SMS
appointment_reminder_24hPatient24 hours before appointment startPush/in-app and SMS
appointment_reminder_1hPatient1 hour before appointment startPush/in-app and SMS
appointment_reminder_15mPatient15 minutes before appointment startPush/in-app and SMS
doctor_appointment_starting_15mDoctor15 minutes before appointment startPush/in-app and email if Resend is configured
doctor_patient_waiting_10mDoctor10 minutes after patient waiting is scheduledPush/in-app and email if Resend is configured

Reminder timing uses APPOINTMENT_TIMEZONE; default is Asia/Kolkata. If a reminder time is already in the past when the job is created, it is scheduled for the current time. The scheduler polls once per minute, claims up to 100 due notifications per poll, and retries failed scheduled notifications up to 3 times.

When an appointment is cancelled, pending scheduled reminders for that appointment are cancelled. When appointment date, time, or duration changes, pending reminders are cancelled and recreated for the new appointment time.

Appointment SMS Templates

EventTemplate keyDefault DLT IDBody
Appointment confirmation / bookedappointment-confirmation1007098381514317785Dear {name}, your appointment is confirmed for {datetime}. Please arrive on time. - AIVIDA
Appointment cancellationappointment-cancellation1007514674555560874Dear {name}, your appointment scheduled for {datetime} has been cancelled. Please contact support for assistance. - AIVIDA
Appointment rescheduledappointment-rescheduled1007897657973836151Dear {name}, your appointment has been rescheduled to {datetime}. Please visit at the updated time. - AIVIDA
Appointment reminderappointment-reminder1007702540941982595Dear {name}, this is a reminder for your appointment scheduled on {datetime}. Please be available on time. - AIVIDA
Consultation startedconsultation_startedAIRTEL_CONSULTATION_STARTED_DLT_TEMPLATE_ID or AIRTEL_DLT_TEMPLATE_IDDear {name}, your consultation has been started by the doctor. Please join now. - AIVIDA

SMS is sent through Airtel when AIRTEL_CUSTOMER_ID, AIRTEL_ENTITY_ID, AIRTEL_SOURCE_ADDRESS, an applicable DLT template ID, and a valid Indian mobile number are available. AIRTEL_AUTHORIZATION or AIRTEL_AUTH_TOKEN is used when configured.

Appointment Push and Email Rules

Push delivery uses active push devices from pushDevices plus the patient’s saved Expo token where available. If no push token exists, the notification row is still created and delivery is marked skipped.

Push providers are Expo and Firebase through the default push provider. Email uses Resend and is best-effort; when Resend credentials or recipient email are missing, the backend skips email and logs the reason.

Current appointment email coverage is:

RecipientEmail is sent for
PatientInvoice after paid appointment, appointment rescheduled, consultation started
DoctorAppointment created, appointment rescheduled, patient cancelled appointment, appointment starting soon, patient joined consultation, patient waiting

There is currently no separate patient email for plain appointment created, confirmed status update, or cancelled status update.

Update Delivery State

After a push or SMS provider attempt, update the notification row with the delivery outcome.

curl -X POST https://api.aivida.in/api \
  -H "Content-Type: application/json" \
  -H "x-stack-access-token: <access_token_optional>" \
  -d '{
  "namespace": "notifications",
  "apiName": "update",
  "data": {
    "id": "notification_123",
    "organizationId": "org_123",
    "module": "appointments",
    "deliveryState": "SENT",
    "pushTicketIds": ["expo_ticket_123"]
  }
}'

For failed SMS or push attempts, send deliveryState: "FAILED" and put the provider error in deliveryError.

Failure Behavior

Payment confirmation is not rolled back only because invoice email delivery fails in the cart flow. Other payment paths log invoice delivery failures and continue according to the endpoint flow. Patient import invitation delivery failures are logged and do not fail the import. Resend configuration problems return errors such as missing API key, missing from email, empty recipient email, or empty PDF attachment.

  • invoices/send-email: sends an existing invoice email.
  • providerOrders/markPaymentSuccess: confirms provider-order payment and can trigger invoice email.
  • appointments/markPaymentSuccess: confirms appointment payment and can trigger invoice email.
  • cart/payment-success: confirms cart checkout payment and can trigger invoice email.
  • patientProfiles/importRecords: imports patient records and can trigger patient invitation email.
  • notifications/create: creates in-app, push, or SMS notification records with module grouping.
  • notifications/listByOrganization: lists all organization notifications or filters by module.
  • notifications/update: updates delivery state, push tickets, read/open state, or provider errors.
  • pushDevices/*: manages Expo push tokens used by push notification delivery.