Engagement Reconciliation & Action Generation Skill
Purpose
Transform CRM opportunity data into a structured reconciliation report with derived accounting, collection, and event-action recommendations.
Input Data Schema
Read the payload JSON. It contains these top-level keys (all may be absent—treat missing as empty/null):
current_date: string "YYYY-MM-DD"
opportunity: object with opportunity_id, customer_id, customer_name, stage (WON | OPEN | LOST), amount (won amount), primary_contact
milestones: array of objects with milestone_id (MS1, MS2, MS3), amount, invoice_state, payment_state, paid_amount, due_date
payments: array of objects with payment_id, milestone_id, amount
revenue_journals: array of objects with journal_id, milestone_id, amount
event: object with event_id, status (SCHEDULED | ACTIVE | COMPLETED | CANCELLED), voucher object (voucher_code, status (ACTIVE | DRAFT | EXPIRED | DISABLED), discount_amount, max_uses)
Output Schema
Produce a JSON object matching answer_template.json with three top-level sections:
engagement_reconciliation
invoice_actions
event_actions
Section 1: Engagement Reconciliation
Top-Level Fields
| Field |
Derivation Rule |
as_of_date |
current_date |
opportunity_id |
From opportunity.opportunity_id |
customer_id |
From opportunity.customer_id |
customer_name |
From opportunity.customer_name |
stage |
From opportunity.stage |
won_amount |
opportunity.amount as number with 2 decimals |
phase_total_amount |
Sum of all milestone amount values |
opportunity_matches_phase_total |
won_amount == phase_total_amount |
total_paid_amount |
Sum of all payments.amount. If payments array is absent, use 0.00. |
outstanding_balance |
won_amount - total_paid_amount |
primary_contact.contact_name |
opportunity.primary_contact |
primary_contact.customer_id |
opportunity.customer_id |
Milestone Array Rules
Milestones must be ordered ascending by milestone_id (MS1, MS2, MS3).
For each milestone, derive invoice_state, payment_state, and paid_amount using this payments-override rule:
If the input contains a payments array (even if empty):
paid_amount = sum of all payments.amount where payment.milestone_id == milestone.milestone_id
- If
paid_amount > 0: output invoice_state = "PAID", payment_state = "PAID"
- If
paid_amount == 0: output invoice_state = "OPEN", payment_state = "UNPAID"
due_date = input milestone due_date (preserve null if null)
If the input does NOT contain a payments array:
- Preserve input
invoice_state, payment_state, paid_amount, and due_date exactly as given.
Milestone Recognition Status
Apply these rules in order for each milestone (use the derived invoice_state and payment_state from the payments-override rule):
- If derived
invoice_state is "VOID" -> "NOT_REQUIRED_UNPAID"
- If derived
invoice_state is "PAID" AND derived payment_state is "PAID" AND there exists a revenue_journal with journal.milestone_id == milestone.milestone_id AND journal.amount == milestone.amount -> "RECOGNIZED"
- If derived
invoice_state is "PAID" AND derived payment_state is "PAID" AND either no matching revenue_journal exists OR the matching journal's amount differs -> "MISSING_REVENUE_JOURNAL"
- If derived
invoice_state is "OPEN" AND derived payment_state is "UNPAID" AND due_date is in the future (>current_date) -> "NOT_REQUIRED_UNPAID"
- Otherwise ->
"UNKNOWN"
Important: When payments array is present, any milestone with at least one payment is treated as PAID/PAID for recognition purposes, even if the payment sum is less than the milestone amount. When payments array is absent, the input invoice/payment states are preserved and evaluated directly.
Section 2: Invoice Actions
Primary Accounting Action
| Condition |
Result |
opportunity.stage == "LOST" |
"NO_ACCOUNTING_ACTION" |
Any milestone has "MISSING_REVENUE_JOURNAL" |
"RECORD_REVENUE_MS2" |
| Otherwise |
"NO_ACCOUNTING_ACTION" |
LOST opportunities never trigger revenue recording, even if a milestone is missing its journal.
Accounting Action Detail Object
| Field |
When RECORD_REVENUE_MS2 |
When NO_ACCOUNTING_ACTION |
action |
"RECORD_REVENUE_MS2" |
"NO_ACCOUNTING_ACTION" |
milestone_id |
"MS2" |
"NONE" |
amount |
milestones[MS2].amount |
0.00 |
debit_account |
"DEFERRED_REVENUE" |
"NONE" |
credit_account |
"IMPLEMENTATION_SERVICES_REVENUE" |
"NONE" |
owner_queue |
"ACCOUNTING" |
"NONE" |
Collection Action
Identify "unpaid" milestones: those with derived invoice_state != "PAID" (or derived payment_state != "PAID") after applying the payments-override rule.
| Condition |
Result |
opportunity.stage == "LOST" AND at least one unpaid milestone exists |
"SEND_COLLECTION_NOTICE" |
opportunity.stage == "WON" AND at least one unpaid milestone has due_date < current_date (past due) |
"SEND_COLLECTION_NOTICE" |
opportunity.stage == "WON" AND at least one unpaid milestone exists but none are past due |
"MONITOR_UNPAID_NOT_DUE" |
| Otherwise |
"NO_COLLECTION_ACTION" |
Collection Task Detail Object
action = same as collection_action
milestone_id:
- For
SEND_COLLECTION_NOTICE:
- If stage is LOST: first unpaid milestone by milestone_id order (MS1, then MS2, then MS3)
- If stage is WON: first past-due unpaid milestone by milestone_id order
- For
MONITOR_UNPAID_NOT_DUE: first future-due unpaid milestone by milestone_id order
- For
NO_COLLECTION_ACTION: "NONE"
amount = the selected milestone's amount
due_date = the selected milestone's due_date (null if null)
owner_queue: "COLLECTIONS" for SEND_COLLECTION_NOTICE, "ACCOUNT_MANAGEMENT" for MONITOR_UNPAID_NOT_DUE, "NONE" for NO_COLLECTION_ACTION
contact_name = opportunity.primary_contact
Section 3: Event Actions
Event Fields
| Field |
Derivation |
event_id |
event.event_id if event exists, else null |
event_status |
event.status if event exists, else "UNKNOWN" |
voucher |
If event exists: copy event.voucher object fields (voucher_code, status as voucher_status, discount_amount, max_uses). If no event: null. |
Invite Action
| Condition |
Result |
event.status == "SCHEDULED" AND event.voucher.status == "ACTIVE" |
"SEND_BRIEFING_INVITE" |
event.status == "ACTIVE" AND event.voucher.status == "ACTIVE" |
"VERIFY_INVITE_SENT" |
| Otherwise (no event, COMPLETED/CANCELLED status, or non-ACTIVE voucher) |
"NO_INVITE_ACTION" |
Invite Task Detail Object
| Field |
When SEND_BRIEFING_INVITE |
When VERIFY_INVITE_SENT |
When NO_INVITE_ACTION |
action |
"SEND_BRIEFING_INVITE" |
"VERIFY_INVITE_SENT" |
"NO_INVITE_ACTION" |
event_id |
event.event_id |
event.event_id |
null |
voucher_code |
event.voucher.voucher_code |
event.voucher.voucher_code |
null |
owner_queue |
"ACCOUNT_MANAGEMENT" |
"EVENTS" |
"NONE" |
contact_name |
opportunity.primary_contact |
opportunity.primary_contact |
opportunity.primary_contact |
customer_id |
opportunity.customer_id |
opportunity.customer_id |
opportunity.customer_id |
Key Pitfalls
- Payments array presence changes behavior. When
payments exists, always derive milestone states from payments (presence = PAID/PAID, absence = OPEN/UNPAID). When payments is absent, preserve input states exactly.
- LOST stage blocks accounting. A LOST opportunity always gets
NO_ACCOUNTING_ACTION for the accounting action, even if MS2 has MISSING_REVENUE_JOURNAL.
- Collection on LOST is immediate. LOST stage with any unpaid milestone triggers
SEND_COLLECTION_NOTICE regardless of due date.
- Revenue journal amount must match milestone amount. For
RECOGNIZED status, the journal amount must equal the milestone amount exactly.
- Milestone order. Always output milestones sorted MS1, MS2, MS3. Collection and action targets pick the first qualifying milestone in this order.
- Numbers. All monetary values must be numbers with exactly 2 decimal places.
1---2name: fewshot-attempt-02-163description: Engagement Reconciliation & Action Generation Skill4---5# Engagement Reconciliation & Action Generation Skill67## Purpose8Transform CRM opportunity data into a structured reconciliation report with derived accounting, collection, and event-action recommendations.910## Input Data Schema11Read the payload JSON. It contains these top-level keys (all may be absent—treat missing as empty/null):12- `current_date`: string "YYYY-MM-DD"13- `opportunity`: object with `opportunity_id`, `customer_id`, `customer_name`, `stage` (WON | OPEN | LOST), `amount` (won amount), `primary_contact`14- `milestones`: array of objects with `milestone_id` (MS1, MS2, MS3), `amount`, `invoice_state`, `payment_state`, `paid_amount`, `due_date`15- `payments`: array of objects with `payment_id`, `milestone_id`, `amount`16- `revenue_journals`: array of objects with `journal_id`, `milestone_id`, `amount`17- `event`: object with `event_id`, `status` (SCHEDULED | ACTIVE | COMPLETED | CANCELLED), `voucher` object (`voucher_code`, `status` (ACTIVE | DRAFT | EXPIRED | DISABLED), `discount_amount`, `max_uses`)1819## Output Schema20Produce a JSON object matching `answer_template.json` with three top-level sections:211. `engagement_reconciliation`222. `invoice_actions`233. `event_actions`2425---2627## Section 1: Engagement Reconciliation2829### Top-Level Fields30| Field | Derivation Rule |31|-------|-----------------|32| `as_of_date` | `current_date` |33| `opportunity_id` | From `opportunity.opportunity_id` |34| `customer_id` | From `opportunity.customer_id` |35| `customer_name` | From `opportunity.customer_name` |36| `stage` | From `opportunity.stage` |37| `won_amount` | `opportunity.amount` as number with 2 decimals |38| `phase_total_amount` | Sum of all milestone `amount` values |39| `opportunity_matches_phase_total` | `won_amount == phase_total_amount` |40| `total_paid_amount` | Sum of all `payments.amount`. If `payments` array is absent, use `0.00`. |41| `outstanding_balance` | `won_amount - total_paid_amount` |42| `primary_contact.contact_name` | `opportunity.primary_contact` |43| `primary_contact.customer_id` | `opportunity.customer_id` |4445### Milestone Array Rules46Milestones must be ordered ascending by `milestone_id` (MS1, MS2, MS3).4748For each milestone, derive `invoice_state`, `payment_state`, and `paid_amount` using this **payments-override rule**:49501. **If the input contains a `payments` array (even if empty):**51 - `paid_amount` = sum of all `payments.amount` where `payment.milestone_id == milestone.milestone_id`52 - If `paid_amount > 0`: output `invoice_state` = `"PAID"`, `payment_state` = `"PAID"`53 - If `paid_amount == 0`: output `invoice_state` = `"OPEN"`, `payment_state` = `"UNPAID"`54 - `due_date` = input milestone `due_date` (preserve null if null)55562. **If the input does NOT contain a `payments` array:**57 - Preserve input `invoice_state`, `payment_state`, `paid_amount`, and `due_date` exactly as given.5859### Milestone Recognition Status60Apply these rules **in order** for each milestone (use the *derived* `invoice_state` and `payment_state` from the payments-override rule):61621. **If derived `invoice_state` is `"VOID"`** -> `"NOT_REQUIRED_UNPAID"`632. **If derived `invoice_state` is `"PAID"` AND derived `payment_state` is `"PAID"` AND there exists a `revenue_journal` with `journal.milestone_id == milestone.milestone_id` AND `journal.amount == milestone.amount`** -> `"RECOGNIZED"`643. **If derived `invoice_state` is `"PAID"` AND derived `payment_state` is `"PAID"` AND either no matching `revenue_journal` exists OR the matching journal's amount differs** -> `"MISSING_REVENUE_JOURNAL"`654. **If derived `invoice_state` is `"OPEN"` AND derived `payment_state` is `"UNPAID"` AND `due_date` is in the future (>`current_date`)** -> `"NOT_REQUIRED_UNPAID"`665. **Otherwise** -> `"UNKNOWN"`6768> **Important:** When `payments` array is present, any milestone with at least one payment is treated as PAID/PAID for recognition purposes, even if the payment sum is less than the milestone amount. When `payments` array is absent, the input invoice/payment states are preserved and evaluated directly.6970---7172## Section 2: Invoice Actions7374### Primary Accounting Action75| Condition | Result |76|-----------|--------|77| `opportunity.stage == "LOST"` | `"NO_ACCOUNTING_ACTION"` |78| Any milestone has `"MISSING_REVENUE_JOURNAL"` | `"RECORD_REVENUE_MS2"` |79| Otherwise | `"NO_ACCOUNTING_ACTION"` |8081> LOST opportunities never trigger revenue recording, even if a milestone is missing its journal.8283### Accounting Action Detail Object84| Field | When `RECORD_REVENUE_MS2` | When `NO_ACCOUNTING_ACTION` |85|-------|---------------------------|----------------------------|86| `action` | `"RECORD_REVENUE_MS2"` | `"NO_ACCOUNTING_ACTION"` |87| `milestone_id` | `"MS2"` | `"NONE"` |88| `amount` | `milestones[MS2].amount` | `0.00` |89| `debit_account` | `"DEFERRED_REVENUE"` | `"NONE"` |90| `credit_account` | `"IMPLEMENTATION_SERVICES_REVENUE"` | `"NONE"` |91| `owner_queue` | `"ACCOUNTING"` | `"NONE"` |9293### Collection Action94Identify "unpaid" milestones: those with derived `invoice_state != "PAID"` (or derived `payment_state != "PAID"`) after applying the payments-override rule.9596| Condition | Result |97|-----------|--------|98| `opportunity.stage == "LOST"` AND at least one unpaid milestone exists | `"SEND_COLLECTION_NOTICE"` |99| `opportunity.stage == "WON"` AND at least one unpaid milestone has `due_date < current_date` (past due) | `"SEND_COLLECTION_NOTICE"` |100| `opportunity.stage == "WON"` AND at least one unpaid milestone exists but none are past due | `"MONITOR_UNPAID_NOT_DUE"` |101| Otherwise | `"NO_COLLECTION_ACTION"` |102103### Collection Task Detail Object104- `action` = same as `collection_action`105- `milestone_id`:106 - For `SEND_COLLECTION_NOTICE`:107 - If stage is LOST: first unpaid milestone by milestone_id order (MS1, then MS2, then MS3)108 - If stage is WON: first past-due unpaid milestone by milestone_id order109 - For `MONITOR_UNPAID_NOT_DUE`: first future-due unpaid milestone by milestone_id order110 - For `NO_COLLECTION_ACTION`: `"NONE"`111- `amount` = the selected milestone's `amount`112- `due_date` = the selected milestone's `due_date` (null if null)113- `owner_queue`: `"COLLECTIONS"` for `SEND_COLLECTION_NOTICE`, `"ACCOUNT_MANAGEMENT"` for `MONITOR_UNPAID_NOT_DUE`, `"NONE"` for `NO_COLLECTION_ACTION`114- `contact_name` = `opportunity.primary_contact`115116---117118## Section 3: Event Actions119120### Event Fields121| Field | Derivation |122|-------|------------|123| `event_id` | `event.event_id` if event exists, else `null` |124| `event_status` | `event.status` if event exists, else `"UNKNOWN"` |125| `voucher` | If event exists: copy `event.voucher` object fields (`voucher_code`, `status` as `voucher_status`, `discount_amount`, `max_uses`). If no event: `null`. |126127### Invite Action128| Condition | Result |129|-----------|--------|130| `event.status == "SCHEDULED"` AND `event.voucher.status == "ACTIVE"` | `"SEND_BRIEFING_INVITE"` |131| `event.status == "ACTIVE"` AND `event.voucher.status == "ACTIVE"` | `"VERIFY_INVITE_SENT"` |132| Otherwise (no event, COMPLETED/CANCELLED status, or non-ACTIVE voucher) | `"NO_INVITE_ACTION"` |133134### Invite Task Detail Object135| Field | When `SEND_BRIEFING_INVITE` | When `VERIFY_INVITE_SENT` | When `NO_INVITE_ACTION` |136|-------|------------------------------|---------------------------|------------------------|137| `action` | `"SEND_BRIEFING_INVITE"` | `"VERIFY_INVITE_SENT"` | `"NO_INVITE_ACTION"` |138| `event_id` | `event.event_id` | `event.event_id` | `null` |139| `voucher_code` | `event.voucher.voucher_code` | `event.voucher.voucher_code` | `null` |140| `owner_queue` | `"ACCOUNT_MANAGEMENT"` | `"EVENTS"` | `"NONE"` |141| `contact_name` | `opportunity.primary_contact` | `opportunity.primary_contact` | `opportunity.primary_contact` |142| `customer_id` | `opportunity.customer_id` | `opportunity.customer_id` | `opportunity.customer_id` |143144---145146## Key Pitfalls1471. **Payments array presence changes behavior.** When `payments` exists, always derive milestone states from payments (presence = PAID/PAID, absence = OPEN/UNPAID). When `payments` is absent, preserve input states exactly.1482. **LOST stage blocks accounting.** A LOST opportunity always gets `NO_ACCOUNTING_ACTION` for the accounting action, even if MS2 has `MISSING_REVENUE_JOURNAL`.1493. **Collection on LOST is immediate.** LOST stage with any unpaid milestone triggers `SEND_COLLECTION_NOTICE` regardless of due date.1504. **Revenue journal amount must match milestone amount.** For `RECOGNIZED` status, the journal amount must equal the milestone amount exactly.1515. **Milestone order.** Always output milestones sorted MS1, MS2, MS3. Collection and action targets pick the first qualifying milestone in this order.1526. **Numbers.** All monetary values must be numbers with exactly 2 decimal places.