You are a senior Python/FastAPI engineer working on the LIA backend. /api/lia/outbound is wired up but is not aligned with the behaviour spec from the CPO. Right now, we are seeing responses like: { "message_text": "Error: No template found for stage TEST_12_PCP and asset_type PCP", "conversation_id": "", "to_number": "", "stage": "TEST_12_PCP" } From the CPO spec and LIA system brief, the outbound engine should behave as follows: - stage: one of "18", "12", "9", "6", "3" (derived from payments_remaining) - asset_type: "new" or "used" - campaign: a safety gate (dp_campaigns.type / name), but NOT used as the stage value Design rules: - For payments_remaining = 12, stage must be "12" - asset_type must be "new" or "used" (PCP/HP is a finance product type, not an asset type) - Canonical 12-month templates are: - EOT_12_NEW - EOT_12_USED The error above shows that outbound is currently trying to resolve a template for: - stage = "TEST_12_PCP" - asset_type = "PCP" That combination will never exist. It means: - The campaign key is leaking into the stage field. - The finance product ("PCP") is being used as asset_type instead of "new"/"used". We need to realign outbound with the behaviour spec. ──────────────────────── TASK 1 – Fix stage derivation and response shape 1. Open app/lia_outbound.py and find the code path that: - Accepts the outbound payload for /api/lia/outbound - Calls into the rule/template engine - Constructs the final JSON response with keys like: - message_text - conversation_id - to_number - stage 2. Ensure stage is derived from payments_remaining, **not** from campaign: - There should be (or you should create) a function that maps payments_remaining → stage string: - e.g. "18", "12", "9", "6", "3" - For our current use case (payments_remaining = 12), stage must be "12". Concretely: - Wherever the outbound response currently sets `"stage": payload.campaign` or similar, change it to use the computed stage value from payments_remaining (or the same stage variable the rule engine uses internally). 3. Ensure the field names in the outbound response match the CPO contract: Successful /api/lia/outbound response MUST be: ```json { "message_text": "", "conversation_id": "", "to_number": "whatsapp:+44...", "stage": "12" } outbound: use message_text as the response field containing the WhatsApp body. inbound (/api/lia/inbound) and nudge (/api/lia/nudge) should continue to use reply as already implemented; do not change those. Do NOT change URE/LLM logic; only align how outbound uses payments_remaining to derive stage, and how it builds the response JSON. ──────────────────────── TASK 2 – Fix asset_type usage for template selection In app/lia_outbound.py (or wherever you pick the template key), check how asset_type is passed into the rules/template selection. Current incorrect behaviour: It is using asset_type "PCP" from the payload, leading to lookups like (stage="TEST_12_PCP", asset_type="PCP"). Align with the behaviour spec: asset_type should be "new" or "used". PCP/HP are not asset_type; they are product types/campaign types. Expectation from the CPO: The Make/HTTP payload will now send asset_type as "new" or "used". Outbound should pass this asset_type through unchanged into the rules engine and use it to pick EOT_XX_NEW vs EOT_XX_USED. Do NOT try to create templates for asset_type "PCP". Instead, treat "PCP" as a campaign/type concept in dp_campaigns, and ensure outbound still routes to the standard EOT_12_NEW / EOT_12_USED templates based on asset_type "new"/"used" and stage "12". ──────────────────────── TASK 3 – Confirm with a sandbox test payload After making the code changes above, this payload should work in sandbox_mode without template errors: json Copy code { "customer_id": "TEST-123", "payments_remaining": 12, "campaign": "TEST_12_PCP", // campaign/key for dp_campaigns; used as a gate, not as stage "asset_type": "new", // or "used" "channel": "whatsapp", "sandbox_mode": true, "from_number": "whatsapp:+44YOUR_SANDBOX_OR_BUSINESS_NUMBER" } Expected behaviour in sandbox: payments_remaining = 12 → internal stage = "12" asset_type = "new" → engine picks EOT_12_NEW branch Response: json Copy code { "message_text": "Hi ...", // final opener text "conversation_id": "", "to_number": "whatsapp:+44", "stage": "12" } No “No template found for stage TEST_12_PCP and asset_type PCP” errors should appear after this alignment. ──────────────────────── CONSTRAINTS Do NOT modify the LIA system prompt or URE logic. Do NOT change dp_campaigns schema. Only: Fix how outbound derives and returns stage. Fix how outbound uses asset_type (expects "new"/"used", not "PCP"). Ensure /api/lia/outbound response shape matches the contract above. ──────────────────────── WHEN DONE Reply with: The final outbound response-building code snippet (showing message_text, conversation_id, to_number, stage). A brief note of how stage is now derived from payments_remaining. Confirmation that the sandbox test payload above returns 200 with a valid message_text and stage="12". yaml Copy code --- You can paste that straight to the Replit agent. If you then get their summary back, send it to me and I’ll sanity-check that outbound is now truly aligned.