You are a senior Python/FastAPI engineer working in my LIA backend. Problem: - /api/lia/outbound is returning 400 {"detail":"Campaign not active for this request"} even after we added a TEST_12_PCP row in dp_campaigns. - The SQL row is present and looks correct, so the failure is in the Python validation logic, not the DB. Goal: 1) Make sandbox outbound calls more forgiving: - If `sandbox_mode == true` and a campaign row with that name exists, treat it as active for this request, even if it would fail some of the stricter production checks. 2) Add a small debug endpoint that returns the campaigns the system considers “active” for a given site and (optionally) asset_type, so we can introspect valid keys without reading code. Do NOT change any business behaviour for non-sandbox calls. ──────────────────────── TASK 1 – Adjust outbound campaign validation for sandbox 1. Open app/lia_outbound.py and locate the logic that: - reads the outbound payload - looks up dp_campaigns based on the `campaign` field - raises `HTTPException(status_code=400, detail="Campaign not active for this request")` 2. Refactor that validation into something like this (pseudocode; use the existing db helper / error type / models): - After parsing the request body, ensure we have: - campaign_name (string) - asset_type (string, e.g. "PCP") - sandbox_mode (bool) - site_id or whatever context you already use (likely "lincoln_audi") - Query the campaign row once: SELECT * FROM dp_campaigns WHERE name = $1 AND site_id = $2 LIMIT 1; - If no row is found: raise HTTPException(400, "Campaign not found for this site") - If sandbox_mode is TRUE: - For sandbox requests, accept the campaign as "active" as long as: - the row exists, and - status != 'archived' (or whatever terminal status you use, if any) - In other words: DO NOT require date range or strict status checks in sandbox_mode. - Continue processing the outbound flow using this campaign row. - If sandbox_mode is FALSE (normal production behaviour): - Keep the existing strict checks exactly as they are now, for example: - status == 'active' - date_from <= today <= date_to - type matches asset_type - any existing channel/site conditions - If any of these fail, still raise HTTPException(400, "Campaign not active for this request") as before. 3. Make sure the rest of process_outbound_message / outbound pipeline stays unchanged: - We are only relaxing the "active" criteria when sandbox_mode is true. - We do NOT change how Twilio or Make.com is called. - We do NOT change the payload model for /api/lia/outbound. ──────────────────────── TASK 2 – Add a debug endpoint for active campaigns Create a small debug endpoint under the same router that handles /api/lia/outbound, for example in main.py or app/lia_outbound.py: - GET /api/lia/campaigns/active Behaviour: - Query dp_campaigns for campaigns considered “active” by the outbound logic, using the same criteria you apply for **non-sandbox** calls. - Accept optional query parameters: - site_id (default to 'lincoln_audi' if not provided) - asset_type (optional filter matching dp_campaigns.type) Example implementation sketch (adapt to existing router/db helpers): @router.get("/campaigns/active") async def list_active_campaigns( site_id: str = "lincoln_audi", asset_type: str | None = None, ): db = await get_db() if asset_type: rows = await db.fetch( """ SELECT name, type, status, date_from, date_to, target_view, initial_template_key FROM dp_campaigns WHERE site_id = $1 AND type = $2 AND status = 'active' AND date_from <= CURRENT_DATE AND date_to >= CURRENT_DATE ORDER BY name """, site_id, asset_type, ) else: rows = await db.fetch( """ SELECT name, type, status, date_from, date_to, target_view, initial_template_key FROM dp_campaigns WHERE site_id = $1 AND status = 'active' AND date_from <= CURRENT_DATE AND date_to >= CURRENT_DATE ORDER BY name """, site_id, ) return [ { "campaign": r["name"], "type": r["type"], "status": r["status"], "date_from": r["date_from"], "date_to": r["date_to"], "target_view": r["target_view"], "initial_template_key": r["initial_template_key"], } for r in rows ] Important: - This endpoint is for debugging/ops. No auth changes; reuse whatever auth you currently use for admin endpoints. - Do not use sandbox_mode in this endpoint; it should reflect the strict production criteria. ──────────────────────── CONSTRAINTS - Do NOT change any table schema. - Do NOT change any existing outbound business rules for non-sandbox calls. - Only: - Relax the "Campaign not active" criteria when sandbox_mode == true and the campaign row exists. - Add the new /api/lia/campaigns/active endpoint. ──────────────────────── WHEN DONE Reply in your summary with: - The final campaign validation logic for /api/lia/outbound, showing the sandbox_mode branch and the normal branch. - The full FastAPI code for the new /api/lia/campaigns/active endpoint.