Data import & export
CSV templates, bulk endpoints, validation rules, and migration playbooks for moving data into - or out of - Lectern.
Overview
Lectern accepts bulk data in two ways: through the Settings → Imports wizard in the dashboard (with column mapping and a live preview), or through the bulk API. Both run the same validators and produce identical results - the wizard is just the API with a UI on top.
CSV templates
Start by downloading the canonical template for each resource. Column order doesn’t matter; column names do. Empty optional columns are fine.
name, grade, class, dob, guardian_email, …name, email, role, qualifications, …learner_id, name, relationship, email, phonename, grade_level, subject, teacher_emailgrade_level, term, item, amount, currencyUploading via the API
Bulk uploads are two-step: first POST the file, then poll for validation results.
$ curl https://api.lectern.school/v1/imports \
-H "Authorization: Bearer sk_live_lectern_…" \
-F "resource=learners" \
-F "file=@./learners.csv"{
"id": "imp_5f3c0e2a",
"status": "validating",
"resource": "learners",
"rows": 418,
"created_at": "2026-05-07T11:42:08Z"
}Poll GET /v1/imports/{id} until status is ready_for_review. The response includes a per-row validation summary - rows that failed validation won’t be applied until you fix them and re-upload, or override in the dashboard.
Validation rules
Lectern runs three layers of validation before any write:
- Schema - required columns present, types correct, dates parseable.
- Reference - foreign keys resolve.
guardians.learner_idmust match an existing learner;classes.teacher_emailmust match a staff member. - Business rules - no duplicate enrollments per term; class sizes within tier limits; finance rows balance.
Failed rows surface in the import response with a row_errors array containing the row number, the column at fault, and a stable error code:
{
"id": "imp_5f3c0e2a",
"status": "ready_for_review",
"rows_total": 418,
"rows_valid": 412,
"rows_invalid": 6,
"row_errors": [
{
"row": 42,
"column": "guardian_email",
"code": "invalid_email"
}
]
}Applying the import
Once you’re happy with the preview, apply the import:
$ curl -X POST https://api.lectern.school/v1/imports/imp_5f3c0e2a/apply \
-H "Authorization: Bearer sk_live_lectern_…"Apply runs in the background. Lectern fires a import.applied webhook when finished, and every row creates a per-resource webhook (e.g. learner.created) for downstream consumers.
Exports
Every list endpoint accepts ?format=csv for flat downloads, or you can request a full-tenant export from Settings → Exports. Tenant exports are zipped CSVs delivered to a signed URL; the URL is valid for 24 hours.
Limits
- File size: 25 MB per import (~50,000 rows for most resources)
- Rate: 5 imports running concurrently per tenant
- Retention: uploaded files are stored for 7 days after apply, then permanently deleted