Skip to content

title: Regnify Tool: draft_cpd_reminder

Regnify Tool: draft_cpd_reminder

Back to Regnify Product Docs

Previous Next


Purpose

draft_cpd_reminder drafts (but does NOT send) a polite CPD-reminder email for a specific representative. The tool fetches the rep's user profile (name + email), current-year CPD hour totals, and remaining gap against the annual CPD floor, then asks the LLM to compose a 2-paragraph professional reminder. The returned draft is meant for a UI approval step -- a COMP or HR_ADMIN user must review and approve before the email is dispatched.

Critical: This tool NEVER auto-sends email. The return payload has a {PORTAL_URL} placeholder for the frontend to substitute before actual dispatch.

Signature

draft_cpd_reminder(ctx, rep: RepProfileRef, confirm_write: bool = False) -> dict

Parameters

Parameter Type Required Description
rep {"kind": "rep_profile", "id": <int>} Yes Tagged rep-profile reference. id must be the numeric rep_profile_id.
confirm_write bool No (default False) Safety gate. False returns an abort_and_redirect bounce. Set True ONLY when the user's current message uses an explicit issuance verb (e.g., "draft a CPD reminder for rep X", "send a reminder", "draft and send").

confirm_write Safety Gate

The confirm_write parameter prevents the tool from being called when the user is merely asking about CPD reminders rather than commanding one:

User Intent confirm_write Tool Behaviour
"draft a CPD reminder for rep 42" True Drafts and returns the reminder
"send rep 12 a CPD nudge" True Drafts and returns the reminder
"what is the CPD requirement?" False or omitted Returns abort_and_redirect bounce
"how do CPD reminders work?" False or omitted Returns abort_and_redirect bounce
"should I send a CPD reminder?" False or omitted Returns abort_and_redirect bounce

abort_and_redirect Bounce Shape

When confirm_write is False, the tool returns:

{
  "status": "abort_and_redirect",
  "tool_unavailable": true,
  "next_action": "answer_from_search_mas_knowledge",
  "internal_only_note": "..."
}

Do NOT relay this dict to the user. Instead, call search_mas_knowledge to answer the user's actual question about CPD or compliance. The internal_only_note is a system-level instruction for the LLM, not user-facing text.

Internal Data Gathering

When confirm_write=True, the tool gathers three data pieces:

  1. Rep profile -> user_id: repProfile(id) GraphQL query resolves the userId foreign key.
  2. User profile (name + email): user(id) GraphQL query returns name, emailAddress, username.
  3. CPD totals: Internal call to get_cpd_status(ctx, rep, cycle_year) where cycle_year is the current calendar year (datetime.now(UTC).year). Returns hours_ethics, hours_product, hours_other, total, records.

LLM Drafting

The tool passes the gathered data to ctx.llm_client.generate() with the model tier standard (Qwen 3.6 35B, medium reasoning effort, 4096 thinking tokens):

System prompt instructs: output strict JSON only -- {"subject": "<short subject>", "body": "<email body, exactly 2 paragraphs separated by a blank line>"}. The body must greet the recipient by name, state their current CPD hours and the annual deadline (31 December of the cycle year), and include the literal placeholder {PORTAL_URL} as the link the recipient should follow.

User prompt provides: recipient name, cycle year, hours completed, hours remaining (vs 9-hour annual floor).

Annual CPD Floor

The tool uses 9 hours as the Singapore CPD annual requirement for the reminder calculation:

hours_remaining = max(0.0, 9.0 - cpd["total"])

This 9-hour floor is the basic regulatory minimum. It is used for the reminder phrasing only. The server-side health badge computation uses the full FAA-N26 thresholds (30h total: ≥6h Core CPD [ethics/rules, IBF/SCI-accredited] + ≥24h Supplementary CPD), applied per calendar year.

Return Shape (Success)

{
  "rep_profile_id": <int>,
  "draft": {
    "subject": "<short subject line>",
    "body": "<2 paragraphs separated by a blank line, with {PORTAL_URL} placeholder>"
  },
  "hours_remaining": <float>
}
  • draft.subject: Short email subject (e.g., "Your CPD Hours Update -- Action Required by 31 Dec 2026").
  • draft.body: Exactly two paragraphs. Greets the rep by name, states current hours, remaining gap, annual deadline (31 December), and includes {PORTAL_URL}. The caller (frontend or user) substitutes {PORTAL_URL} with the actual Rep Portal CPD page URL before sending.
  • hours_remaining: Non-negative float. 0.0 means the rep has met or exceeded the 9-hour annual floor (but may still fall below FAA-N26 30h total / 6h Core CPD / 24h Supplementary CPD thresholds).

Error Conditions

Condition Behaviour
rep_profile_id not found LookupError: "draft_cpd_reminder: rep_profile_id=... not found"
user_id (from rep) not found LookupError: "draft_cpd_reminder: user_id=... (from rep ...) not found"
LLM returns malformed JSON (missing subject/body) RuntimeError: "draft_cpd_reminder: LLM returned malformed draft (missing subject or body)"
GraphQL transport error RuntimeError -- surfaces verbatim

Draft-Then-Approve Workflow

The full lifecycle of a CPD reminder in Regnify:

  1. COMP/HR_ADMIN requests draft -- chatbot calls draft_cpd_reminder with confirm_write=True.
  2. Chatbot returns draft -- subject + body displayed in the chat UI for review.
  3. User reviews and edits -- the draft is editable before dispatch.
  4. User approves -- the frontend sends the finalized email via the notification/email system (separate from this tool).
  5. Email dispatched -- the {PORTAL_URL} placeholder is substituted with the actual Rep Portal CPD Log URL (/rep/cpd).

NOT automatic: The tool only drafts. Compliance staff (COMP or HR_ADMIN) must actively trigger and approve. Representatives cannot trigger their own reminders.

Rep Self-Service Alternative

Representatives can view their own CPD hours without needing a reminder. Direct them to the Rep Portal CPD Log page at /rep/cpd which displays: - Current year CPD hours (ethics, product, other, total) - Historical CPD records - Gap against FAA-N26 thresholds

When a rep asks "how many CPD hours do I have?", use get_cpd_status to fetch their totals -- do NOT call draft_cpd_reminder.

Use in Attestation Context

The tool can be used to nudge reps who haven't completed attestation responses: 1. Call get_rep_register to identify reps with AMBER or RED flags. 2. For each non-compliant rep, call draft_cpd_reminder to generate a personalized nudge. 3. The nudge can mention both CPD gaps and pending attestation responses.

Important Rules

  1. confirm_write must be True -- Do not call without it when drafting. If the user hasn't explicitly commanded a draft, answer from knowledge instead.
  2. Do not relay the abort_and_redirect dict -- it contains internal instructions. Pivot to search_mas_knowledge instead.
  3. Never claim the email was sent -- the tool only drafts. Tell the user "I've drafted a reminder for your review" not "I've sent a reminder."
  4. Annual floor is 9 hours for reminder calculation. This is the basic MAS annual CPD minimum. Do not conflate with FAA-N26 health badge thresholds.
  5. {PORTAL_URL} is literal -- the body contains this exact placeholder string. Do not substitute it; the frontend handles substitution.

Previous Next

Back to Regnify Product Docs