---
description: SPIF territory scanner — Same batch-of-5 BC pipeline as real-time-territory-usage, but filters for SPIF-eligible services and computes tier/payout eligibility per account
inclusion: manual
---

# SPIF Services Launch Scanner

Scan your territory's AWS spend to identify accounts with SPIF-eligible service usage. Uses the same batch-of-5, Python-first, Billing Central pipeline as the real-time territory usage analysis, but the output layer focuses exclusively on SPIF program qualification.

Accounts are processed in batches of 5 (sorted by ARR) so results are delivered incrementally and fit within context limits.

---

## SPIF Programs Reference (2026)

### 1. 🔵 Amazon Bedrock SPIF (Jan 1 – Dec 31, 2025/2026)
- **Service codes:** `AmazonBedrock`
- **Tiers & Payouts:**

| Tier | Monthly Usage | Payout |
|------|--------------|--------|
| 1 | $500+ | $1,000 |
| 2 | $2,000+ | $2,500 |
| 3 | $5,000+ | $5,000 |
| 4 | $15,000+ | $10,000 |
| 5 | $50,000+ | $15,000 |

- **Campaign Tag:** Gen-AI-SPIF-2025-Bedrock
- **Requirement:** Per-customer usage threshold

### 2. 🔵 Amazon Bedrock Acceleration SPIF (Oct 1 – Dec 31)
- **Service codes:** `AmazonBedrock`
- **Mechanic:** Territory-level Bedrock growth (not per-customer)
- **Payout rates:** 15%–30% of growth (sliding scale based on territory baseline)
- **Note:** This is computed at territory level after all accounts are scanned. Individual account Bedrock spend feeds into the territory aggregate.

### 3. 🟢 Amazon Q Business / QuickSight / QuickSuite SPIF (Jan 1 – Dec 31)
- **Service codes:** `AmazonQBusiness`, `AmazonQuickSight`, `AmazonQuickSuite`
- **Q Business Tiers & Payouts:**

| Tier | Monthly Usage | Payout |
|------|--------------|--------|
| 1 | $500+ | $1,000 |
| 2 | $2,000+ | $2,500 |
| 3 | $5,000+ | $5,000 |
| 4 | $8,000+ | $10,000 |

- **QuickSight & QuickSuite Tiers & Payouts:**

| Tier | Monthly Usage | Payout |
|------|--------------|--------|
| 1 | $1,000+ | $1,000 |
| 2 | $5,000+ | $2,500 |
| 3 | $10,000+ | $5,000 |
| 4 | $20,000+ | $10,000 |

- **Campaign Tag:** Gen-AI-SPIF-2025-AmazonQ

### 4. 🟠 Amazon Connect SPIF — 1H 2026 (Jan 1 – Jun 30, 2026)
- **Service codes:** `AmazonConnect`, `AmazonConnectCampaigns`, `ContactCenterTelecomm`
- **Covers ALL Amazon Connect products** (not just Connect AI)
- **Simplified 2-month consecutive usage requirement**
- **Tiers & Payouts (% of TVE):**

| Tier | Monthly Usage | Payout (% of TVE) |
|------|--------------|-------------------|
| 1 | $1,000+ | 2% of TVE |
| 2 | $5,000+ | 4% of TVE |
| 3 | $10,000+ | 8.5% of TVE |
| 4 | $25,000+ | 13% of TVE |

- **Eligibility:** Opportunities must launch by May 31, 2026
- **Deadline:** Program ends Jun 30, 2026

### 5. 🟡 Amazon SageMaker AI SPIF (Jan 1 – Dec 31)
- **Service codes:** `AmazonSageMaker`, `AmazonSageMakerRuntime`
- **Based on sustained usage growth over consecutive months**
- **Tiers & Payouts:**

| Tier | Monthly Usage | Payout |
|------|--------------|--------|
| 1 | $2,000+ | $1,000 |
| 2 | $5,000+ | $2,500 |
| 3 | $15,000+ | $5,000 |
| 4 | $50,000+ | $10,000 |
| 5 | $250,000+ | $15,000 |

- **Campaign Tag:** Gen-AI-SPIF-2025-Sagemaker

### 6. 🔴 Amazon GitLab Duo with Amazon Q SPIF (Apr 17 – Dec 31)
- **Not detectable via Billing Central** — requires AACV from opportunity data
- **AACV ≥ $100K → $3,000 payout; ≥ $1M → $5,000 payout**
- **Note:** Flag if account has any `AmazonCodeWhisperer` or `AmazonQDeveloper` spend as a potential lead, but payout is based on AACV not usage.

### 7. 🔴 Amazon Q Transformation SPIF (Jan 1 – Jun 30)
- **Not detectable via Billing Central** — requires published customer reference
- **For SMB/SUP customers: $3,000 per published customer reference**
- **Requires .NET, Mainframe, or VMware transformation**
- **Note:** Flag if account has `AWSMainframeModernization` or `AmazonQDeveloper` spend as a potential lead.

---

## SPIF Service Code Mapping

These are the BC service codes that map to SPIF-eligible services:

```python
SPIF_SERVICES = {
    # Bedrock SPIF (1 & 2)
    'AmazonBedrock': {'program': 'Bedrock', 'emoji': '🔵', 'tag': 'Gen-AI-SPIF-2025-Bedrock'},

    # Q Business / QuickSight / QuickSuite SPIF (3)
    'AmazonQBusiness': {'program': 'Q Business', 'emoji': '🟢', 'tag': 'Gen-AI-SPIF-2025-AmazonQ'},
    'AmazonQuickSight': {'program': 'QuickSight', 'emoji': '🟢', 'tag': 'Gen-AI-SPIF-2025-AmazonQ'},
    'AmazonQuickSuite': {'program': 'QuickSuite', 'emoji': '🟢', 'tag': 'Gen-AI-SPIF-2025-AmazonQ'},

    # Connect SPIF (4)
    'AmazonConnect': {'program': 'Connect', 'emoji': '🟠', 'tag': None},
    'AmazonConnectCampaigns': {'program': 'Connect', 'emoji': '🟠', 'tag': None},
    'ContactCenterTelecomm': {'program': 'Connect', 'emoji': '🟠', 'tag': None},

    # SageMaker SPIF (5)
    'AmazonSageMaker': {'program': 'SageMaker', 'emoji': '🟡', 'tag': 'Gen-AI-SPIF-2025-Sagemaker'},
    'AmazonSageMakerRuntime': {'program': 'SageMaker', 'emoji': '🟡', 'tag': 'Gen-AI-SPIF-2025-Sagemaker'},

    # Potential leads for GitLab Duo (6) and Q Transformation (7)
    'AmazonCodeWhisperer': {'program': 'Q Developer (lead)', 'emoji': '🔴', 'tag': None},
    'AmazonQDeveloper': {'program': 'Q Developer (lead)', 'emoji': '🔴', 'tag': None},
    'AWSMainframeModernization': {'program': 'Mainframe Mod (lead)', 'emoji': '🔴', 'tag': None},
}
```

---

## ⛔ PRESENTATION RULES

### NEVER show to the user:
- `service_count`, `bills_analyzed`, `grand_total`, `undecomposed_bills`, `undecomposed_total`
- `coverage_warning`, `duplicate_bills_removed`, `bill_id`, `charge_type`, `marketplace`
- Validation status (VALID, BROKEN, PASS), retry counts, retry attempts
- BC version detection results (v3.0.1 vs v3.0.2)
- Raw USD amounts with cents — use $K formatting
- "X services", "Y bills", "Z calls" — no counts of internal objects
- Any BC response field names or JSON structure
- Payer discovery method
- Non-SPIF services (unless user explicitly asks for full breakdown)

### ALWAYS show:
- Clean dollar amounts: $54.3K, $7.3K, $847, $0
- SPIF program name with emoji: 🔵 Bedrock, 🟢 Q Business, 🟠 Connect, 🟡 SageMaker
- Tier qualification: "Tier 3 ($5K+)" or "Below Tier 1" or "Near Tier 2 ($347 to go)"
- Estimated payout: "$5,000" or "—"
- Month-over-month trend for SPIF services: ↑ growing, ↓ declining, → flat, NEW
- Plain account names in tables (NO SFDC links)

### Dollar formatting rules:
- ≥ $100K → $882K (no decimal)
- $10K–$99K → $54.3K (one decimal)
- $1K–$9.9K → $7.3K (one decimal)
- $100–$999 → $847 (exact)
- < $100 → $42 (exact)
- Delta: +$4.6K, -$842

---

## Step 0: Gather User Input

Same as real-time-territory-usage — ask the user:
1. **How many accounts** do you want to scan?
2. **Which ones?** Accept any of:
   - A list of SFDC Account IDs with ARR
   - A territory name or user alias
   - A file path (Excel, CSV, JSON, markdown)
   - A pasted table
   - "All" or "top N" from their knowledge base

Then confirm: "I'll scan these for SPIF-eligible services in batches of 5, sorted by ARR. You'll see which accounts qualify for payouts per batch."

For each account, you need at minimum: **Account Name**, **SFDC Account ID**, **ARR**. Known AWS Account IDs / Payer IDs are optional but speed up the analysis.

**Default sort: ARR descending.**

---

## Execution Mode Decision

### 1 account → Manual MCP approach
Direct tool calls in conversation.

### >1 account → Python script from turn 1 (MANDATORY)

Same rationale as real-time-territory-usage — BC JSON is too large for context. Generate the Python script BEFORE making any MCP calls.

---

## ⛔ AWSentral BLOCK RULE

Identical to real-time-territory-usage:
- **ALL spend data MUST come from Billing Central.**
- AWSentral `search_aws_account_mappings` is ONLY allowed when an account has NO AWS ID at all.
- **Do NOT use `get_account_spend_summary` at all.**

---

## THE BATCH LOOP (Core Architecture)

Identical batch-of-5 pipeline as real-time-territory-usage:

```
FOR EACH BATCH OF 5 ACCOUNTS:
  STEP A: Discover & confirm payers (5 accts)
  STEP B: Pull 3 months of spend (5 × 3 = 15 calls)
  STEP C: Validate & retry (internal)
  STEP D: Feed data into Python script & run → SPIF-focused output
  STEP E: Ask user to continue
```

**The ONLY difference is Step D** — the Python script filters for SPIF services and computes tier/payout instead of showing all services.

Steps A, B, C are IDENTICAL to real-time-territory-usage. Refer to that steering file for:
- Payer discovery logic
- `view_service_usage` call pattern
- BC version detection
- Validation decision tree
- ARR sanity check
- Exclusion list
- Retry logic
- Known BC patterns

---

## Python Script Template (SPIF Version)

Generate this script at the START of the analysis. Populate `data` and `spif_data` dicts as you collect data per batch.

```python
import datetime

day = datetime.datetime.now().day
pf = 30 / day  # proration factor

# --- SPIF Program Definitions ---

BEDROCK_TIERS = [
    (50000, 15000, 'Tier 5'),
    (15000, 10000, 'Tier 4'),
    (5000, 5000, 'Tier 3'),
    (2000, 2500, 'Tier 2'),
    (500, 1000, 'Tier 1'),
]

Q_BUSINESS_TIERS = [
    (8000, 10000, 'Tier 4'),
    (5000, 5000, 'Tier 3'),
    (2000, 2500, 'Tier 2'),
    (500, 1000, 'Tier 1'),
]

QUICKSIGHT_TIERS = [
    (20000, 10000, 'Tier 4'),
    (10000, 5000, 'Tier 3'),
    (5000, 2500, 'Tier 2'),
    (1000, 1000, 'Tier 1'),
]

CONNECT_TIERS = [
    (25000, 0.13, 'Tier 4'),  # payout is % of TVE
    (10000, 0.085, 'Tier 3'),
    (5000, 0.04, 'Tier 2'),
    (1000, 0.02, 'Tier 1'),
]

SAGEMAKER_TIERS = [
    (250000, 15000, 'Tier 5'),
    (50000, 10000, 'Tier 4'),
    (15000, 5000, 'Tier 3'),
    (5000, 2500, 'Tier 2'),
    (2000, 1000, 'Tier 1'),
]

# SPIF service code → program mapping
SPIF_CODES = {
    'AmazonBedrock': 'Bedrock',
    'AmazonQBusiness': 'Q Business',
    'AmazonQuickSight': 'QuickSight',
    'AmazonQuickSuite': 'QuickSuite',
    'AmazonConnect': 'Connect',
    'AmazonConnectCampaigns': 'Connect',
    'ContactCenterTelecomm': 'Connect',
    'AmazonSageMaker': 'SageMaker',
    'AmazonSageMakerRuntime': 'SageMaker',
    'AmazonCodeWhisperer': 'Q Developer (lead)',
    'AmazonQDeveloper': 'Q Developer (lead)',
    'AWSMainframeModernization': 'Mainframe Mod (lead)',
}

SPIF_EMOJI = {
    'Bedrock': '🔵', 'Q Business': '🟢', 'QuickSight': '🟢',
    'QuickSuite': '🟢', 'Connect': '🟠', 'SageMaker': '🟡',
    'Q Developer (lead)': '🔴', 'Mainframe Mod (lead)': '🔴',
}

SPIF_TIER_MAP = {
    'Bedrock': BEDROCK_TIERS,
    'Q Business': Q_BUSINESS_TIERS,
    'QuickSight': QUICKSIGHT_TIERS,
    'QuickSuite': QUICKSIGHT_TIERS,  # same tiers as QuickSight
    'SageMaker': SAGEMAKER_TIERS,
    'Connect': CONNECT_TIERS,
}

EXCLUSIONS = {
    'ComputeSavingsPlans', 'DatabaseSavingsPlans',
    'AWSSupportBusiness', 'AWSSupportEssential', 'AWSDeveloperSupport',
    'AwsPremiumSupportSilver', 'AwsPremiumSupportGold', 'AWSSupportEnterprise',
}

# --- Data populated per batch ---
# {name: [m2_total, m1_total, m_mtd, arr]}
data = {}

# {name: {program: [m2_spend, m1_spend, m_mtd_spend]}}
# Programs are aggregated (e.g., all Connect codes → 'Connect')
spif_data = {}

def fk(v):
    if v == 0: return '$0'
    if abs(v) >= 100000: return f'${int(round(v/1000))}K'
    if abs(v) >= 1000: return f'${v/1000:.1f}K'
    if abs(v) >= 100: return f'${int(round(v))}'
    return f'${int(round(v))}'

def fd(v):
    s = '+' if v > 0 else ''
    if abs(v) >= 1000: return f'{s}${v/1000:.1f}K'
    return f'{s}${int(round(v))}'

def get_tier(program, monthly_spend):
    """Returns (tier_name, payout, threshold, gap_to_next) or None."""
    tiers = SPIF_TIER_MAP.get(program)
    if not tiers:
        return None
    matched = None
    for threshold, payout, name in tiers:
        if monthly_spend >= threshold:
            matched = (name, payout, threshold)
            break
    if matched:
        # Find next tier up
        tier_name, payout, threshold = matched
        idx = next(i for i, (t, p, n) in enumerate(tiers) if n == tier_name)
        if idx > 0:
            next_threshold = tiers[idx - 1][0]
            gap = next_threshold - monthly_spend
            return (tier_name, payout, threshold, gap)
        return (tier_name, payout, threshold, None)  # already at max
    else:
        # Below all tiers — show gap to Tier 1
        lowest_threshold = tiers[-1][0]
        gap = lowest_threshold - monthly_spend
        return ('Below Tier 1', 0, 0, gap)

def trend_arrow(m2, m1, m_eom):
    """Return trend indicator."""
    if m2 == 0 and m1 == 0 and m_eom > 0: return 'NEW'
    if m_eom > m1 > m2: return '↑↑'
    if m_eom > m1: return '↑'
    if m_eom < m1 < m2: return '↓↓'
    if m_eom < m1: return '↓'
    return '→'

# --- Per-account SPIF detail ---
for name in sorted(data, key=lambda n: -data[n][3]):
    m2, m1, m_mtd, arr = data[name]
    acct_spif = spif_data.get(name, {})

    if not acct_spif:
        continue  # skip accounts with zero SPIF service spend

    has_qualifying = False
    lines = []
    total_payout = 0

    for program in ['Bedrock', 'Q Business', 'QuickSight', 'QuickSuite', 'Connect', 'SageMaker', 'Q Developer (lead)', 'Mainframe Mod (lead)']:
        if program not in acct_spif:
            continue
        s_m2, s_m1, s_mtd = acct_spif[program]
        s_eom = s_mtd * pf
        emoji = SPIF_EMOJI[program]
        trend = trend_arrow(s_m2, s_m1, s_eom)

        if program in ('Q Developer (lead)', 'Mainframe Mod (lead)'):
            lines.append(f'| {emoji} {program} | {fk(s_m2)} | {fk(s_m1)} | {fk(s_eom)} | {trend} | 🔍 Check opps | — |')
            has_qualifying = True
            continue

        tier_info = get_tier(program, s_eom)
        if tier_info:
            tier_name, payout, threshold, gap = tier_info
            if program == 'Connect':
                payout_str = f'{payout*100:.1f}% TVE' if payout > 0 else '—'
            else:
                payout_str = fk(payout) if payout > 0 else '—'
                total_payout += payout

            if gap is not None and gap > 0:
                tier_str = f'{tier_name} ({fk(gap)} to next)'
            elif gap is None:
                tier_str = f'{tier_name} (MAX)'
            else:
                tier_str = tier_name

            lines.append(f'| {emoji} {program} | {fk(s_m2)} | {fk(s_m1)} | {fk(s_eom)} | {trend} | {tier_str} | {payout_str} |')
            has_qualifying = True

    if has_qualifying:
        payout_note = f' — Est. payout: {fk(total_payout)}' if total_payout > 0 else ''
        print(f'#### 💰 {name} (ARR {fk(arr)}){payout_note}')
        print()
        print('| Program | M-2 | M-1 | M EOM ↗ | Trend | Tier | Payout |')
        print('|---------|-----|-----|---------|-------|------|--------|')
        for line in lines:
            print(line)
        print()
        print('---')
        print()

# --- Summary table (SPIF accounts only) ---
print()
print('## 📊 SPIF Summary')
print()
print('| # | Account | ARR | Program | M EOM ↗ | Trend | Tier | Payout |')
print('|---|---------|-----|---------|---------|-------|------|--------|')
row = 0
territory_bedrock_total = [0, 0, 0]  # for Bedrock Acceleration SPIF
total_est_payout = 0

for name in sorted(data, key=lambda n: -data[n][3]):
    m2, m1, m_mtd, arr = data[name]
    acct_spif = spif_data.get(name, {})
    if not acct_spif:
        continue

    for program in ['Bedrock', 'Q Business', 'QuickSight', 'QuickSuite', 'Connect', 'SageMaker', 'Q Developer (lead)', 'Mainframe Mod (lead)']:
        if program not in acct_spif:
            continue
        s_m2, s_m1, s_mtd = acct_spif[program]
        s_eom = s_mtd * pf
        emoji = SPIF_EMOJI[program]
        trend = trend_arrow(s_m2, s_m1, s_eom)

        # Accumulate territory Bedrock totals
        if program == 'Bedrock':
            territory_bedrock_total[0] += s_m2
            territory_bedrock_total[1] += s_m1
            territory_bedrock_total[2] += s_eom

        if program in ('Q Developer (lead)', 'Mainframe Mod (lead)'):
            if s_eom > 0:
                row += 1
                print(f'| {row} | {name} | {fk(arr)} | {emoji} {program} | {fk(s_eom)} | {trend} | 🔍 | — |')
            continue

        tier_info = get_tier(program, s_eom)
        if tier_info:
            tier_name, payout, threshold, gap = tier_info
            if s_eom > 0 or payout > 0:
                row += 1
                if program == 'Connect':
                    payout_str = f'{payout*100:.1f}% TVE' if payout > 0 else '—'
                else:
                    payout_str = fk(payout) if payout > 0 else '—'
                    total_est_payout += payout
                print(f'| {row} | {name} | {fk(arr)} | {emoji} {program} | {fk(s_eom)} | {trend} | {tier_name} | {payout_str} |')

# --- Territory Bedrock Acceleration ---
print()
b_m2, b_m1, b_eom = territory_bedrock_total
if b_eom > 0:
    print(f'### 🔵 Territory Bedrock Acceleration (Oct–Dec window)')
    print(f'Territory Bedrock: M-2 {fk(b_m2)} → M-1 {fk(b_m1)} → M EOM {fk(b_eom)}')
    if b_m1 > 0:
        growth = b_eom - b_m1
        growth_pct = (growth / b_m1) * 100
        print(f'Growth vs M-1: {fd(growth)} ({growth_pct:+.1f}%)')
        if growth > 0:
            print(f'Potential acceleration payout: 15%–30% of {fk(growth)} = {fk(growth * 0.15)}–{fk(growth * 0.30)}')
    print()

# --- Scorecard ---
print()
print(f'### 💰 Estimated Total SPIF Payout: {fk(total_est_payout)}')
print(f'(Excludes Connect % of TVE and Bedrock Acceleration — those require additional data)')
print()

# Count accounts by SPIF status
qualifying = 0
near_miss = 0
leads_only = 0
no_spif = 0
for name in data:
    acct_spif = spif_data.get(name, {})
    if not acct_spif:
        no_spif += 1
        continue
    has_payout = False
    has_lead = False
    has_near = False
    for program, (s_m2, s_m1, s_mtd) in acct_spif.items():
        s_eom = s_mtd * pf
        if program in ('Q Developer (lead)', 'Mainframe Mod (lead)'):
            if s_eom > 0: has_lead = True
            continue
        tier_info = get_tier(program, s_eom)
        if tier_info:
            tier_name, payout, threshold, gap = tier_info
            if payout > 0: has_payout = True
            elif gap and gap < threshold * 0.5: has_near = True
    if has_payout: qualifying += 1
    elif has_near: near_miss += 1
    elif has_lead: leads_only += 1
    else: no_spif += 1

print(f'> 💰 Qualifying: {qualifying} | 🎯 Near miss: {near_miss} | 🔍 Leads only: {leads_only} | ⬜ No SPIF: {no_spif}')
```

### How to populate the script per batch

After each `view_service_usage` response, extract SPIF-eligible service spend and add:

```python
# Example: Isabel NV has Bedrock and SageMaker spend
data['Isabel NV'] = [48636, 51501, 39380, 882000]  # [m2, m1, m_mtd, arr]
spif_data['Isabel NV'] = {
    'Bedrock': [1200, 1850, 1640],      # [m2, m1, m_mtd]
    'SageMaker': [3400, 3100, 2800],
}

# Example: Coteng NV has Connect spend
data['Coteng NV'] = [18200, 19100, 17500, 263000]
spif_data['Coteng NV'] = {
    'Connect': [2100, 2400, 2200],
}

# Example: Account with no SPIF services — still add to data but skip spif_data
data['venturis'] = [22000, 23000, 21000, 292000]
# No spif_data entry → will show as "No SPIF" in scorecard
```

---

## Batch Execution Detail

### User sees at start of the run:
> 📍 **SPIF Territory Scanner** — Scanning [N] accounts for SPIF-eligible services in batches of 5, sorted by ARR.
>
> **Programs tracked:** 🔵 Bedrock, 🟢 Q Business/QuickSight/QuickSuite, 🟠 Connect, 🟡 SageMaker, 🔴 GitLab Duo/Q Transformation leads
>
> **Batch 1/[X]:** [Account1], [Account2], [Account3], [Account4], [Account5]

### Steps A–C: Identical to real-time-territory-usage
Refer to that steering file for payer discovery, spend pulls, validation, and retry logic.

### Step D: SPIF-focused output

After all 5 accounts in the batch have validated data:
1. For each BC response, extract ONLY SPIF-eligible service spend (ignore all other services)
2. Aggregate service codes into programs (e.g., `AmazonConnect` + `AmazonConnectCampaigns` + `ContactCenterTelecomm` → `Connect`)
3. Populate `spif_data` dict
4. Run the script
5. Present: per-account SPIF detail (only accounts with SPIF spend) + batch summary

### Step E: Ask user

> **Batch 1 complete** (5/[N] accounts). [X] accounts have SPIF-eligible spend. Continue with batch 2?
>
> You can also:
> - **"Continue"** — next 5 accounts
> - **"Drill into [account]"** — full service breakdown (not just SPIF)
> - **"Show near-misses"** — accounts close to qualifying for a tier
> - **"Stop"** — show final SPIF summary with what we have

---

## Running Consolidated Table

Starting from **batch 2 onwards**, show a running SPIF summary BEFORE the batch detail.

> **Running SPIF total ([X]/[N] accounts scanned):**

| # | Account | Program | M EOM ↗ | Tier | Payout |
|---|---------|---------|---------|------|--------|

**On the LAST batch**, add the final scorecard + territory Bedrock acceleration estimate.

---

## Near-Miss Detection

For each account, if a SPIF service is **below Tier 1 but within 50% of the threshold**, flag it:

> 🎯 **Near miss:** [Account] has [Program] at [amount] — only [gap] away from Tier 1 ([threshold])

This helps the user prioritize which accounts to push for SPIF qualification.

---

## Campaign Tag Reminders

After presenting results, remind the user about required campaign tags for qualifying accounts:

> **Action required for qualifying accounts:**
> - 🔵 Bedrock accounts → Tag opportunities with `Gen-AI-SPIF-2025-Bedrock`
> - 🟢 Q Business/QuickSight accounts → Tag with `Gen-AI-SPIF-2025-AmazonQ`
> - 🟡 SageMaker accounts → Tag with `Gen-AI-SPIF-2025-Sagemaker`
> - 🟠 Connect accounts → Ensure opportunities launch by May 31, 2026

---

## Exclusion List

Same as real-time-territory-usage:
- `ComputeSavingsPlans`, `DatabaseSavingsPlans`
- `AWSSupportBusiness`, `AWSSupportEssential`, `AWSDeveloperSupport`
- `AwsPremiumSupportSilver`, `AwsPremiumSupportGold`, `AWSSupportEnterprise`
- Any service name containing "savings plan" or "reserved instance" (case-insensitive)

---

## ⛔ Anti-Patterns

All anti-patterns from real-time-territory-usage apply, PLUS:

18. ❌ **Showing non-SPIF services in the main output** — this is a SPIF scanner, not a full billing analysis. Only show SPIF-eligible services unless the user asks for a full breakdown.
19. ❌ **Computing Connect payout as a flat dollar amount** — Connect payout is % of TVE, which requires opportunity data. Show the percentage, not a dollar estimate.
20. ❌ **Claiming GitLab Duo or Q Transformation qualification from BC data alone** — these require AACV/customer reference data. Only flag as leads.
21. ❌ **Forgetting campaign tags** — always remind the user which tags to apply.
22. ❌ **Ignoring near-misses** — accounts close to a tier threshold are actionable. Always flag them.
23. ❌ **Computing Bedrock Acceleration per-account** — it's a territory-level program. Aggregate all Bedrock spend across accounts.

---

## Data Sources

| Data Point | Source | Tool |
|---|---|---|
| Payer ID (primary) | Knowledge Base | `recall` |
| Payer ID (ONLY if no AWS ID exists) | AWSentral | `search_aws_account_mappings` |
| Payer confirmation | Billing Central | `list_linked_accounts` |
| Monthly spend | Billing Central | `view_service_usage` |
| Org structure | Billing Central | `list_linked_accounts` |
| Campaign tags | AWSentral | `search_opportunities` (post-scan action) |
| Connect TVE | AWSentral | `get_opportunity_details` (post-scan action) |
