Skip to main content

CSV Upload API

Upload a CSV for automated patient intake or case enrichment. The endpoint accepts a CSV, stores it, and processes each row through the pipeline for its type.

Upload CSV

Submit a CSV for processing.

POST /api/v1/csv

Purpose

Accepts a multipart CSV upload, validates the file, and processes each row through the pipeline selected by type. Processing is synchronous (typically 5-15s for 1-500 rows).

Request Body

Content-Type: multipart/form-data

Your HTTP client sets Content-Type automatically when you attach a file — no need to set it manually.

FieldTypeRequiredValues
typestringYesappointments, medications, consult-notes
filefileYesUTF-8 .csv; max 10MB; header row must contain at least one comma

Response

{
"success": true,
"data": {
"file_name": "appointments_20260413.csv",
"type": "appointments",
"size": 3939,
"uploaded_at": "2026-04-13T17:52:51.825Z"
},
"message": "CSV uploaded and processed successfully."
}

Notes

  • Uploads are deduplicated by (organization_id, file_name, type) — re-uploading the same filename is a no-op
  • Use filenames with a unique timestamp component (e.g. appointments_20260413_093000.csv)
  • Row-level validation errors are recorded internally but not returned in the response
  • The NewConsult field determines which voice agent handles each row's intake call

Error Responses

  • 400 Bad Request: File missing, empty, not .csv, or invalid CSV format
  • 400 Bad Request: type missing or not one of appointments, medications, consult-notes
  • 413 Payload Too Large: File exceeds 10MB
  • 422 Unprocessable Entity: No agent routing rules configured for your organization (appointments only)

Example

curl -X POST "https://orchestrator.helloblair.com/api/v1/csv" \
-H "X-API-Key: YOUR_API_KEY" \
-F "type=appointments" \
-F "file=@appointments_20260413.csv"

CSV Schemas

Header rows are case-sensitive. Column order does not matter. Extra columns are silently ignored.

Appointments (type=appointments)

Required and optional columns

Required Columns

ColumnTypeNotes
AppointmentIdstringUnique appointment identifier, used as the case key
PatientRecordIdstringStable patient identifier across uploads
AppointmentStatusenumCommon values: Scheduled, Cancelled, Waitlist, Triage. Cancelled, Waitlist, Triage auto-cancel the appointment
AppointmentDoctorstringDoctor's last name, must match an active Blair clinician
VirtualbooleanMust be true to proceed; any other value auto-cancels
AppointmentDatedateYYYY-MM-DD
EarliestAppointmentTimetimeHH:MM or HH:MM:SS (24-hour, Eastern Time)
PatientFirstNamestring
PatientLastNamestring
DOBdateYYYY-MM-DD
PhonestringAny North American format, normalized to E.164
NewConsultbooleanDetermines per-row voice agent routing
TestStatusenumPending, Removed; Removed auto-cancels the appointment
IntakeChartedbooleantrue auto-cancels (intake already completed externally)

Re-uploading an existing AppointmentId triggers reprocessing only when one of these fields changes: AppointmentDate, AppointmentStatus, AppointmentDoctor, Virtual, IntakeCharted, TestStatus.

Optional Columns

AppointmentTestId, OfficeId, Office, AppointmentStatusId, TestStatusId, TestId, TestCode, TestName, TestStartTime, email, PhoneType, HealthCardStatus, PharmacyName, PharmacyAddress, PharmacyCity, PharmacyPostalCode, PharmacyFax, LastModifiedDate

Medications (type=medications)

Enriches existing cases with medication data. Rows are grouped by PatientRecordId. Max 5000 rows per upload.

Required and optional columns

Required Columns

ColumnTypeNotes
PatientRecordIdstringPatient identifier; rows are grouped by this
MedicationNamestringRows with an empty MedicationName are silently dropped
DosestringFeeds dosage; Strength used as fallback if Dose is empty
StrengthstringFallback for dosage when Dose is empty
SIGstringFeeds frequency
DateStartedstringFeeds duration

Missing fields fall back to "unknown" in the enriched record.

Optional Columns

PatientMedicationId, MedicationClass, Form, Route, Ingredients, Instructions, HasDIN, DIN, DateCreated

Consult Notes (type=consult-notes)

Extracts medications and allergies from consult-note text via LLM. Rows are grouped by AppointmentId. Max 500 rows per upload.

Required and optional columns

Required Columns

ColumnTypeNotes
AppointmentIdstringRows are grouped by this appointment
VPTextstringConsult-note text; rows with no VPText contribute no context
CategoryNameReportstringCategory label; CategoryNameTemplate used as fallback if empty
CategoryNameTemplatestringFallback for category label when CategoryNameReport is empty

Missing category falls back to "Unknown" in the extraction context.

Optional Columns

VPTextId, AppointmentTestId, PatientRecordId, wRootCategoryId, ReportingDoctorId, DoctorRootCategoryId, CapturedDate

Consumer Checklist

  1. Register your doctors with Blair. Every AppointmentDoctor value must match an active clinician in Blair's system.
  2. Lock in your filename convention. Timestamp-based names are safest. Never reuse a filename for different content.
  3. Include all required columns in every CSV header row.