Sync / roster
Bidirectional real-time synchronization of student records, admissions data, and academic history between Jenzabar CX and Salesforce Education Data Architecture using event-driven webhooks and SCIM provisioning.
This integration connects Jenzabar CX — a comprehensive higher-education student information system — with Salesforce Education Data Architecture (EDA), enabling bidirectional synchronization of student records, admissions applications, and academic history. The sync is event-driven, with Jenzabar's outbound webhooks triggering transformations that map to EDA's data model, including Account, Contact, and Course Enrollment objects.
The primary technical challenge lies in reconciling Jenzabar's non-standard field names and legacy relationship structures with Salesforce EDA's hierarchical academic model. Mapping Jenzabar's program/enrollment concepts to EDA's Course Connection and Plan Requirement objects requires careful field-level transformation logic, especially for programs with complex curriculum structures.
A working piece from this integration — no sign-up. The full build handles the edge cases, safeguards, and cutover.
/* Jenzabar CX → Salesforce EDA Field Mapping Table */ /* Jenzabar Student Record → Contact + Program Enrollment */ Jenzabar CX Field Salesforce EDA Object.Field Transform ──────────────────────────────────────────────────────────────────────────── // Inbound webhook payload from Jenzabar (POST /api/v1/students) { "student_id": "STU-2024-00482", "first_name": "Morgan", "last_name": "Chen", "date_of_birth": "2002-03-15", "email": "[email protected]", "admit_term": "2024 Fall", "program_code": "BS-CS", "enrollment_status": "Matriculated", "cumulative_gpa": 3.72 } // Maps to Salesforce EDA upsert via External ID: Contact.External_Id__c = "JNZ:" + student_id // "JNZ:STU-2024-00482" Contact.FirstName = first_name Contact.LastName = last_name Contact.Birthdate = date_of_birth Contact.Email = email Program_Enrollment__c.Account__r.External_Id__c = "JNZ:PROG:" + program_code Program_Enrollment__c.Status__c = MAP[admit_status → Enrollment_Status__c] // SCIM user sync for provisioning: POST /scim/v2/Users { "userName": "[email protected]", "externalId": "STU-2024-00482", "name": { "givenName": "Morgan", "familyName": "Chen" }, "emails": [{ "value": "[email protected]", "primary": true }], "education": [{ "institution": "University", "major": "BS-CS" }] }
How we'd take this from discovery to a production-safe cutover — the phases, the canonical mapping, and the edge cases that bite.
This integration synchronizes student records, admissions data, and academic history from Jenzabar CX into Salesforce Education Data Architecture using event-driven webhooks and SCIM provisioning. The sync is bidirectional: changes in either system propagate to the other, with Jenzabar serving as the system of record for academic data and Salesforce EDA managing recruitment, enrollment, and relationship data.
Contact, Account, Application__c, Enrollment__c, CourseConnection__c, and AcademicRecord__c. Document existing integration points (Jenzabar's web services, flat-file exports) and identify which fields are writeable via Jenzabar's API vs. read-only. Validate OAuth2 or SAML configuration for authenticated access to both systems.entityType, sourceId, targetId (Salesforce record ID once provisioned), changedFields[], effectiveDate, and auditTimestamp. For SCIM provisioning, use User resources mapped to Contact records with the EDA-specific eduPerson and affiliation attributes.Application__c status and Enrollment__c term affiliation so those propagate back to Jenzabar via its admission or enrollment APIs.AcademicProgram__c, Term__c, and Plan__c so incoming records link to the correct EDA hierarchical structures. Date fields must normalize to UTC; avoid passing Jenzabar's legacy date formats unconverted.sourceId + entityType + attemptedAt for manual resolution. Implement a monitoring alert on dead-letter queue depth exceeding a threshold (e.g., 50 messages) and a reconciliation report run daily to detect events that were received but never applied.Student_ID__c (Jenzabar's primary key) against StudentId__c on the EDA Contact record. Reject payloads where sourceId is null or where changedFields[] is empty (no-op events). Enforce referential integrity: reject an Enrollment__c event if the corresponding Account or AcademicProgram__c does not exist in EDA.Application__c record appears in Salesforce with correct field values; change an enrollment status in EDA and confirm the update reaches Jenzabar; simulate a deactivate event and verify the contact's Primary_Language__c or affiliation is set correctly. Validate the SCIM provisioning flow by creating a user in Jenzabar and confirming the corresponding EDA Contact has Preferred_Enrollment_Status__c populated.| Jenzabar CX Field | Salesforce EDA Object | EDA Field | Transform / Notes |
|---|---|---|---|
STUDENT_ID |
Contact | StudentId__c |
Direct map; used as match key on upsert |
FIRST_NAME / LAST_NAME |
Contact | FirstName / LastName |
Direct map; enforce 40-char limit on LastName |
ACAD_PROG_CDE |
AcademicProgram__c | ProgramCode__c |
Lookup by program code; create if not found (verify) |
ENROLL_STATUS / ENR_TERM |
Enrollment__c | Status__c / EnrollmentTerm__c |
Map Jenzabar status codes to EDA picklist; reject if term missing |
APPLICATION_STATUS_CDE |
Application__c | Application_Status__c |
Transform to EDA status values; trigger SF→Jenzabar update on status change |
GPA / CUM_CREDITS |
AcademicRecord__c | GPA__c / CreditsEarned__c |
Validate numeric range; null if not yet calculated |
PERSON_UID (SCIM) |
Contact | UniqueIdentifier__c |
SCIM provisioning link; used for deprovisioning on student separation |
effectiveDate being in the past and suppress the event—or flag it for a separate review queue to avoid triggering unintended workflows in Salesforce based on outdated timestamps.Application__c records. The upsert must use StudentId__c exclusively as the match key; a name-based match is insufficient and will cause data fragmentation.STUDENT_ID. The integration must recognize this as a deprecation event for the absorbed ID and deactivate the corresponding EDA contact rather than leaving an orphaned record.Enrollment__c deactivate event leaves the student enrolled in Salesforce. The two event types must be emitted in sequence with a confirmation step to prevent dangling active enrollments.Cutover must be reversible and reconciled: before switching the integration to live, export a snapshot of both Jenzabar student records and Salesforce EDA contacts with their current StudentId__c values and store them as JenzabarCutoverSnapshot__c records in a backup org or custom object. During the first two weeks, run the reconciliation report nightly comparing record counts and field-level diffs on Application__c, Enrollment__c, and AcademicRecord__c; any discrepancy triggers a manual review workflow. To reverse, disable the webhooks in Jenzabar, stop the SCIM provisioning flow, and re-enable any legacy file-based feeds from the snapshot. Until the reconciliation report shows zero diffs across three consecutive days, the integration should remain in a monitored state with a rollback runbook ready. If Salesforce is unavailable for more than 30 minutes during cutover, Jenzabar must hold webhook events locally and replay them in order upon reconnection to prevent re-enrollment events from being processed out of sequence.
Reading the reference is free. Delivering it under liability — with the safeguards that keep production running through the cutover — is what we do.