This case study examines a real-world integration between PeopleSoft Campus Solutions and D2L Brightspace (a Learning Management System) using the Valence APIs. This is an outbound integration where PeopleSoft acts as the HTTP client, driving data into D2L. The patterns and lessons here apply to any outbound integration with a modern REST API.
PeopleSoft was the system of record for student and faculty data. D2L Brightspace was the LMS. The integration needed to keep D2L in sync with PeopleSoft for:
PeopleSoft was the driver for all of these interactions. D2L was the consumer.
Before writing a single line of PeopleCode, every D2L API transaction was tested and documented using HTTP test tools (HTTPYac/Postman). This is critical:
Document each API call in HTTP syntax. This becomes your technical specification.
Think through every interaction between PeopleSoft and D2L:
The key design principle was separating API complexity from business logic.
The integration used a layered architecture:
┌─────────────────────────┐
│ Batch Process / Page │ ← Knows WHAT data to send
│ (Driver) │
├─────────────────────────┤
│ PeopleCode SDK │ ← Knows HOW to talk to D2L
│ (Application Package) │
├─────────────────────────┤
│ D2L Valence API │ ← HTTP/REST/JSON
└─────────────────────────┘
This separation means:
The user service class handled creating and updating user accounts in D2L.
import MY_D2L_INTEGRATION:user:baseUser;
/* Create an instance for each EMPLID */
Local MY_D2L_INTEGRATION:user:baseUser &d2lUser;
&d2lUser = create MY_D2L_INTEGRATION:user:baseUser(&INSTITUTION, &EMPLID);
If &d2lUser.CreateOrUpdateUser() Then
/* Success */
Else
/* Error: check &d2lUser.errorInfo */
End-If;
The batch process does not know anything about D2L URLs, JSON payloads, or authentication. The class handles:
import MY_D2L_INTEGRATION:class:classInstance;
Local MY_D2L_INTEGRATION:class:classInstance &c;
&c = create MY_D2L_INTEGRATION:class:classInstance(&INSTITUTION, &STRM, &CLASS_NBR);
If &c.CreateOrUpdate() Then
/* Course offering created/updated in D2L */
Else
/* Error: check &c.errorInfo */
End-If;
Enrollment was handled as a reconciliation pattern rather than individual add/drop events:
Local MY_D2L_INTEGRATION:class:classInstance &c;
&c = create MY_D2L_INTEGRATION:class:classInstance(&INSTITUTION, &STRM, &CLASS_NBR);
If &c.existsInCache Then
/* Reconcile students - compares PeopleSoft enrollments with D2L */
&c.reconcileStudentEnrollments(&bReportOnly);
/* Reconcile instructors */
&c.reconcileInstructorEnrollments(&bReportOnly);
End-If;
The &bReportOnly flag allowed running the reconciliation in “dry run” mode to preview changes before committing them. This was very useful during initial rollout.
Different data types had different update frequencies:
| Integration | Frequency | Notes |
|---|---|---|
| User provisioning | Frequent / near-real-time | New users need accounts before classes start |
| Bio/demo updates | Near-real-time | Name changes, email updates |
| Term creation | Once per semester | Run when term setup is complete |
| Class creation | Frequent during registration window | Only when D2L admin setup is ready |
| Enrollments | Frequent during registration | Must handle last-minute adds/drops (class starts at 5pm, student enrolled at 4:45pm) |
A configuration table stored environment-specific settings, keyed by database name (or institution):
This ensures a database refresh from PRD to DEV does not accidentally point DEV at production D2L.
Every outbound API call was logged to a custom PeopleSoft table:
This proved invaluable for:
The integration used PeopleCode’s JsonObject and JsonArray classes for all JSON work. Messages and Document classes were explicitly avoided for outbound integrations because:
JsonObject provides direct, flexible access to JSON propertiesSee the JSON Encoding and JSON Decoding sections for examples.
Test the API thoroughly before writing PeopleCode. Use Postman or HTTPYac to understand every endpoint, error response, and edge case. This saves enormous time.
Separate API logic from business logic. Batch processes should not contain HTTP or JSON code. Put that in an Application Package SDK.
Log everything. You will need the API interaction logs when something goes wrong in production. The time you invest in logging infrastructure pays for itself quickly.
Design for environment isolation. Use configuration tables keyed by database name so DEV never accidentally talks to production APIs.
Plan for certificate maintenance. TLS certificates expire. Set up monitoring and calendar reminders. See the TLS Certificates section for details.
Use %IntBroker.ProvideRestTest for modern REST integrations. The ability to capture HTTP status codes is critical for proper error handling. See PeopleSoft as HTTP Client for the comparison.
Support a “report only” mode. Being able to preview what the integration would do without actually making changes is extremely helpful during rollout and troubleshooting.
Cross-reference IDs between systems. Store the external system’s ID (D2L User ID, D2L Org Unit ID) in PeopleSoft so you can look up records without making extra API calls.
Chris Malek s a PeopleTools® Technical Consultant with over two decades of experience working on PeopleSoft enterprise software projects. He is available for consulting engagements.
Work with Chris