One row per submission (as specified by client).
Client says: For each submission, get the submission id.
Source: Submission.id
Status: No issues.
Client says: For each submission, get the createdAt (e.g. 2026-02-20T18:47:12.544Z).
Source: Submission.createdAt
Status: No issues.
Client says: For each submission, get the appointment id → go to Appointment table → get the slotId → go to AppointmentSlot table → get date + startTime → combine them.
Source: Submission → Appointment.slotId → AppointmentSlot.date + AppointmentSlot.startTime
Implementation: Combine date (AWSDate, e.g. 2026-03-15) and startTime (AWSTime, e.g. 14:00:00.000Z) into a single timestamp string. All appointment times are in America/New_York in the existing codebase.
ISSUE — Multiple appointments per submission: A submission can have more than one appointment (e.g. a canceled first appointment and a rebooked second one). The client said "for each submission" (one row) but did not specify which appointment to use when there are multiple.
Recommendation: Use the latest appointment by
bookedAt. Or alternatively use the latest non-CANCELED appointment. Needs client confirmation.
Client says: For each submission, get submissionId → go to UserMessageVariant table → get variantTrack → go to AppointmentMessageVariantTemplate table → get mrt.
Source: UserMessageVariant.variantTrack → AppointmentMessageVariantTemplate.mrt
Implementation:
- Query
UserMessageVariantbysubmissionId(GSI:BySubmissionId) - Get the
variantTrackvalue - Query
AppointmentMessageVariantTemplatebyvariantTrack(GSI:ByVariantTrack) - Return the
mrtfield
NOTE: The
AppointmentMessageVariantTemplatetable has multiple rows pervariantTrack(one permessageType:confirmation,reminder_24hr,reminder_1hr). Themrtvalue should be the same across all message types for a given track. We will use any row for that track (e.g. theconfirmationrow).
Status: No issues. This resolves the earlier HAPA terminology question — mrt is the correct field.
Client says: For each submission, get submissionId → go to UserMessageVariant table → get variantTrack. That is the Reminder_Variant_ID.
Source: UserMessageVariant.variantTrack
Status: No issues. Confirmed this is the track number (e.g. 1, 2, 3, 4), not a template row id.
Client says: (left blank)
Status: Client did not define this column. We will leave it blank in the export for now, or omit it entirely. Needs client decision on whether to keep or drop this column.
Client says: Appt_Scheduled_TimeStamp - 1 hr
Source: Calculated: subtract 1 hour from the appointment slot date+time.
Implementation: Take the combined AppointmentSlot.date + AppointmentSlot.startTime, subtract 1 hour.
ISSUE — This is a calculated estimate, not the actual send time. The system schedules reminders via EventBridge Scheduler (
SendSMSApptinmha-lambda-backend). The actual scheduling logic is:
- If appointment is > 24hr away: schedules a 24hr and a 1hr reminder
- If appointment is 1hr–24hr away: schedules only a 1hr reminder
- If appointment is < 1hr away: no reminders are scheduled
So
slot_time - 1hrmay not match reality:
- If the appointment was booked < 1hr before the slot, no 1hr reminder was sent
- If the appointment was canceled before the reminder fired, it was never sent
- The actual send time may vary slightly from the scheduled time
The client appears to accept this as a calculated approximation. No actual reminder send log exists in the database today.
Client says: Using the submission id, go to Appointment table, get the status. If the status is Assessment Completed, then 1, otherwise 0.
ISSUE —
Assessment Completedis a Submission status, NOT an Appointment status.The system has two separate status fields:
Field Possible values Appointment.status (enum) SCHEDULED,COMPLETED,CANCELED,NO_SHOWSubmission.Status (string) Submitted,Active Engagement,Do Not Contact,Assessment Completed,Assessment InProcess,Assessment Scheduled,Accepted Referral,Attended Intake,Closed,Archive
Assessment Completeddoes not exist as an Appointment status. The client likely means one of:Option A: Check
Appointment.status === 'COMPLETED'— this is when staff marks the appointment itself as completed in the admin portal.Option B: Check
Submission.Status === 'Assessment Completed'— this is a broader submission-level status that may be set independently of the appointment status.Recommendation: Clarify with client. Option A (
Appointment.status === 'COMPLETED') is the most direct indicator that the appointment was attended. Option B is a downstream workflow status that staff sets manually and may not always align with appointment attendance.
Client says: Using the submission id, go to Appointment table, get updatedAt. That is the Attendance_Timestamp.
Source: Appointment.updatedAt
NOTE — Fragility risk:
updatedAtis automatically updated on any edit to the Appointment record, not just when marking attendance. If a staff member edits thenotesfield or changes status multiple times,updatedAtwill reflect the last edit, not the attendance moment.The client has explicitly chosen this field, so we will use it. There is no
attendanceAtfield in the current schema.
Implementation: Return Appointment.updatedAt when Attended_Appointment = 1, otherwise blank.
Client says: Check if there is any status change after status Assessment Scheduled.
Source: SubmissionStatusHistory
Implementation:
- Query
SubmissionStatusHistorybysubmissionId(GSI:BySubmissionId), sorted bychangedAt - Find the row where
newStatus = 'Assessment Scheduled' - Check if any
SubmissionStatusHistoryrows exist withchangedAtafter that row'schangedAt - If yes →
1, if no →0
NOTE: The status
Assessment Scheduledis set automatically by the system when an appointment is booked (inappointments.service.js). This column checks whether anything changed AFTER that automatic status was set.
Status: No issues. Logic is clear.
Client says: Get all the status changes after status Assessment Completed, colon-separated string.
ISSUE — Did the client mean "after Assessment Scheduled" instead of "after Assessment Completed"?
The typical submission status flow is:
Submitted → Active Engagement → Assessment Scheduled → Assessment InProcess → Assessment Completed → Accepted Referral → Attended Intake → ...If we collect statuses after
Assessment Completed, we would only capture very late-stage transitions (e.g.Accepted Referral,Attended Intake). This seems unlikely to be what the client wants, especially since:
- The previous column (
Status_Change_Post_Appt_Scheduled) checks changes afterAssessment Scheduled- The column name says "Post Appt Status" — i.e. after the appointment was scheduled, not after it was completed
Possibility 1 — Client meant "after Assessment Scheduled": Collect all
newStatusvalues from SubmissionStatusHistory wherechangedAtis after theAssessment Scheduledrow. This would show the full journey:Assessment InProcess,Assessment Completed,Accepted Referral, etc.Possibility 2 — Client actually wants "after Assessment Completed": Collect only statuses that happen after
Assessment Completed. This would show justAccepted Referral,Attended Intake, etc.Recommendation: Clarify with client. "After Assessment Scheduled" makes more sense given the column name and the context of the previous column.
Implementation (once clarified):
- Query
SubmissionStatusHistorybysubmissionId, sorted ascending bychangedAt - Find the anchor row (where
newStatus= the clarified status) - Collect all subsequent
newStatusvalues - Join with
:separator
| # | Issue | Question |
|---|---|---|
| 1 | Multiple appointments per submission | When a submission has more than one appointment, which appointment should we use? Latest by bookedAt? Latest non-canceled? All of them? |
| 2 | Attended_Appointment — wrong status field |
Assessment Completed is a Submission status. The Appointment table uses COMPLETED. Should we check Appointment.status = 'COMPLETED' or Submission.Status = 'Assessment Completed'? |
| 3 | Reminder_ID — undefined |
This column was left blank. Should we omit it from the export, or does the client have a definition? |
| 4 | Post_Appt_Status — "after Assessment Completed" or "after Assessment Scheduled"? |
Collecting statuses after Assessment Completed only shows very late-stage changes. Did the client mean after Assessment Scheduled? |