Appearance
CMS SendGrid Template System
Overview
The CMS email system uses SendGrid Dynamic Templates.
For each email send, we call the SendGrid Tools plugin and pass payload data through dynamic_template_data. That means your SendGrid template can directly use placeholder values such as:
text
{{user.firstname}}
{{siteDomain}}
{{course.name}}The final payload always includes:
user: the user instance passed to the send callsiteDomain:process.env.FRONTEND_DOMAIN- additional template-specific data (for example
course,credit,creditsSender,creditsRecipient, etc.)
Where Templates Are Connected In The CMS
To map each predefined CMS email type to a concrete SendGrid template ID:
- Go to Settings.
- Open Sendgrid tools plugin.
- Open Configuration.
- Select a SendGrid dynamic template for each CMS email type.
- Save configuration.
Predefined CMS Email Types
These are the predefined email types registered by CMS:
lms.courseEnrollSuccesslms.courseExpiredlms.courseReminderlms.courseCompletedlms.courseCreditsAwardedlms.courseCreditsReminderlms.courseCreditsTransferredSenderlms.courseCreditsTransferredRecipientlms.courseCompletedNoCertificatelms.courseCertificateUpdatelms.documentReviewNotificationlms.documentApprovedlms.documentDeniedlms.learningPathCompletedlms.learningPathStageCompletedlms.learningPathReviewNotificationlms.learningPathStageReviewNotificationlms.learningPathDenied
Base dynamic_template_data Shape
All CMS emails include this base:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online"
}Notes:
usercan contain additional first-level fields depending on where the send call is made.- SendGrid delivery is skipped when
user.dncis true. - Only first-level properties should be relied on for template design.
Template Data Per Email Type
lms.courseEnrollSuccess
When used: when a user is enrolled into a course.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"course": {
"documentId": "abc123",
"name": "Ethics 101",
"slug": "ethics-101",
"price": "29.00",
"coursePointsCompletion": "3.0",
"accessPeriod": 7,
"available": true,
"autoCertGeneration": true,
"url": "https://calpg.online/courses/ethics-101"
}
}lms.courseExpired
When used: hourly cron when enrolled user courses become expired.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"course": {
"documentId": "abc123",
"name": "Ethics 101",
"slug": "ethics-101",
"price": "29.00",
"coursePointsCompletion": "3.0",
"accessPeriod": 7,
"available": true,
"autoCertGeneration": true
}
}lms.courseReminder
When used: hourly cron for courses nearing expiration.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"course": {
"documentId": "abc123",
"name": "Ethics 101",
"slug": "ethics-101",
"price": "29.00",
"coursePointsCompletion": "3.0",
"accessPeriod": 7,
"available": true,
"autoCertGeneration": true,
"url": "https://calpg.online/courses/ethics-101"
}
}lms.courseCompleted
When used: when a course reaches completion (standard course progress and learning-path certificate flows).
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"course": {
"documentId": "abc123",
"name": "Ethics 101",
"slug": "ethics-101",
"price": "29.00",
"coursePointsCompletion": "3.0",
"accessPeriod": 7,
"available": true,
"autoCertGeneration": true
}
}lms.courseCreditsAwarded
When used: when course credits are granted and not created from a transfer.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"credit": {
"documentId": "crd_456",
"amount": "3",
"spent": "1",
"expiresAt": "2026-05-25T00:00:00.000Z",
"expired": false,
"fromTransfer": false,
"reminderSent": true,
"expiredAtFormatted": "5/25/2026"
}
}lms.courseCreditsReminder
When used: hourly cron for credits approaching expiration.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"credit": {
"documentId": "crd_456",
"amount": "3",
"spent": "1",
"expiresAt": "2026-05-25T00:00:00.000Z",
"expired": false,
"fromTransfer": false,
"reminderSent": true,
"expiredAtFormatted": "5/25/2026"
}
}lms.courseCreditsTransferredSender
When used: when a user transfers credits to another user (sender notification).
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"creditsSender": {
"documentId": "crd_sender",
"amount": "10",
"spent": "4",
"expiresAt": "2026-12-31T00:00:00.000Z",
"expired": false
},
"creditsRecipient": {
"documentId": "crd_recipient",
"amount": "2",
"spent": "0",
"expiresAt": "2026-12-31T00:00:00.000Z",
"fromTransfer": true,
"expired": false,
"expiresAtFormatted": "12/31/2026"
},
"amount": 2,
"dateFormatted": "5/11/2026"
}lms.courseCreditsTransferredRecipient
When used: when a user receives transferred credits (recipient notification).
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"creditsSender": {
"documentId": "crd_sender",
"amount": "10",
"spent": "4",
"expiresAt": "2026-12-31T00:00:00.000Z",
"expired": false
},
"creditsRecipient": {
"documentId": "crd_recipient",
"amount": "2",
"spent": "0",
"expiresAt": "2026-12-31T00:00:00.000Z",
"fromTransfer": true,
"expired": false,
"expiresAtFormatted": "12/31/2026"
},
"amount": 2,
"dateFormatted": "5/11/2026"
}lms.courseCompletedNoCertificate
When used: when a course reaches completion (standard course progress and learning-path certificate flows), but no certificate was generated.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"course": {
"documentId": "abc123",
"name": "Ethics 101",
"slug": "ethics-101",
"price": "29.00",
"coursePointsCompletion": "3.0",
"accessPeriod": 7,
"available": true,
"autoCertGeneration": true
}
}lms.courseCertificateUpdate
When used: when a certificate is generated and sent to the user. This will contain the generated certificate as an attachment. This can happen if a user completed a course, but the certificate for this was generated at a later date by the system.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"courses": [
{
"documentId": "abc123",
"name": "Ethics 101",
"slug": "ethics-101",
"price": "29.00",
"coursePointsCompletion": "3.0",
"accessPeriod": 7,
"available": true,
"autoCertGeneration": true
},
{
"documentId": "def456",
"name": "Compliance Basics",
"slug": "compliance-basics",
"price": "19.00",
"coursePointsCompletion": "2.0",
"accessPeriod": 14,
"available": true,
"autoCertGeneration": false
}
],
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
}
}lms.documentReviewNotification
When used: when a certificate is generated and sent to the user. This will contain the generated certificate as an attachment. This can happen if a user completed a course, but the certificate for this was generated at a later date by the system.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
},
"learningPathStage": {
"documentId": "rflrujoz7rv1hkozyyww5200",
"name": "Stage 1",
"description": "<p>This is a description</p>",
"autoApprove": true,
"genCertificates": false,
"optional": false,
"advanceButtonText": "Temp",
"alias": "Application",
"shortName": "App"
},
"document": {
"documentId": "mkmlb6t5pe6rprhmoidqt95w",
"name": "Pre-Qualification",
"description": "<p>Description of document.</p>",
"type": "auto"
},
"userDocument": {
"documentId": "lqu3jt2iczb9iid8gh0lofky",
"label": "Pre-Qualification - Pending",
"state": "pending",
"stateMessage": "This is a message used when user document was denied.",
"uploadedAt": "2026-06-22T22:45:56.751Z"
}
}lms.documentApproved
When used: when a document is approved.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
},
"learningPathStage": {
"documentId": "rflrujoz7rv1hkozyyww5200",
"name": "Stage 1",
"description": "<p>This is a description</p>",
"autoApprove": true,
"genCertificates": false,
"optional": false,
"advanceButtonText": "Temp",
"alias": "Application",
"shortName": "App"
},
"document": {
"documentId": "mkmlb6t5pe6rprhmoidqt95w",
"name": "Pre-Qualification",
"description": "<p>Description of document.</p>",
"type": "auto"
},
"userDocument": {
"documentId": "lqu3jt2iczb9iid8gh0lofky",
"label": "Pre-Qualification - Pending",
"state": "pending",
"stateMessage": "This is a message used when user document was denied.",
"uploadedAt": "2026-06-22T22:45:56.751Z"
}
}lms.documentDenied
When used: when a document is denied.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
},
"learningPathStage": {
"documentId": "rflrujoz7rv1hkozyyww5200",
"name": "Stage 1",
"description": "<p>This is a description</p>",
"autoApprove": true,
"genCertificates": false,
"optional": false,
"advanceButtonText": "Temp",
"alias": "Application",
"shortName": "App"
},
"document": {
"documentId": "mkmlb6t5pe6rprhmoidqt95w",
"name": "Pre-Qualification",
"description": "<p>Description of document.</p>",
"type": "auto"
},
"userDocument": {
"documentId": "lqu3jt2iczb9iid8gh0lofky",
"label": "Pre-Qualification - Pending",
"state": "pending",
"stateMessage": "This is a message used when user document was denied.",
"uploadedAt": "2026-06-22T22:45:56.751Z"
}
}lms.learningPathCompleted
When used: when a document is approved.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
},
"learningPathStage": {
"documentId": "rflrujoz7rv1hkozyyww5200",
"name": "Stage 1",
"description": "<p>This is a description</p>",
"autoApprove": true,
"genCertificates": false,
"optional": false,
"advanceButtonText": "Temp",
"alias": "Application",
"shortName": "App"
},
"document": {
"documentId": "mkmlb6t5pe6rprhmoidqt95w",
"name": "Pre-Qualification",
"description": "<p>Description of document.</p>",
"type": "auto"
},
"userDocument": {
"documentId": "lqu3jt2iczb9iid8gh0lofky",
"label": "Pre-Qualification - Pending",
"state": "pending",
"stateMessage": "This is a message used when user document was denied.",
"uploadedAt": "2026-06-22T22:45:56.751Z"
}
}lms.learningPathCompleted
When used: when a learning path is completed.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
}
}lms.learningPathStageCompleted
When used: when a learning path stage is completed.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
},
"learningPathStage": {
"documentId": "rflrujoz7rv1hkozyyww5200",
"name": "Stage 1",
"description": "<p>This is a description</p>",
"autoApprove": true,
"genCertificates": false,
"optional": false,
"advanceButtonText": "Temp",
"alias": "Application",
"shortName": "App"
}
}lms.learningPathReviewNotification
When used: when a learning path needs to be reviewed.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
},
"userLearningPath": {
"documentId": "wj2lzfxjcgeybgo7tysw03e6",
"label": "Phase I Online Training - [email protected] - Pending",
"isCompletable": false,
"state": "pending",
"stateMessage": "This is a message used when user learning path was denied."
}
}lms.learningPathStageReviewNotification
When used: when a learning path stage needs to be reviewed.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
},
"learningPathStage": {
"documentId": "rflrujoz7rv1hkozyyww5200",
"name": "Stage 1",
"description": "<p>This is a description</p>",
"autoApprove": true,
"genCertificates": false,
"optional": false,
"advanceButtonText": "Temp",
"alias": "Application",
"shortName": "App"
},
"userLearningPathStage": {
"documentId": "v75phhdxl1li2ugslzs4a1gx",
"label": "Stage 2 - Pending",
"state": "pending",
"isCompletable": false
}
}lms.learningPathDenied
When used: when a learning path is denied.
Example:
json
{
"user": {
"documentId": "usr_123",
"firstname": "Jane",
"lastname": "Doe",
"email": "[email protected]",
"dnc": false
},
"siteDomain": "calpg.org",
"cmsUrl": "https://cms.calpg.online",
"learningPath": {
"documentId": "b1tlwgkmf06ac2v24hrqao7o",
"header": "Phase I Online Training",
"content": "<p>Test Test</p>",
"slug": "phase-i-online-training",
"autoApprove": false,
"genCertificates": false,
"defaultUrl": "/account/lp-ccgc-certification",
"informationText": "What's next?",
"informationUrl": "/url-to-more-info"
},
"learningPathStage": {
"documentId": "rflrujoz7rv1hkozyyww5200",
"name": "Stage 1",
"description": "<p>This is a description</p>",
"autoApprove": true,
"genCertificates": false,
"optional": false,
"advanceButtonText": "Temp",
"alias": "Application",
"shortName": "App"
},
"userLearningPathStage": {
"documentId": "v75phhdxl1li2ugslzs4a1gx",
"label": "Stage 2 - Pending",
"state": "pending",
"isCompletable": false
}
}