<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Cradicle Explorer</title>
    <link href="/css/bootstrap/bootstrap.min.css" rel="stylesheet">
    <style>
      .form-control-dark::placeholder {
          color: #aaa;
          opacity: 1;
      }
    </style>
    <link rel="stylesheet" href="/assets/fontawesome/css/all.min.css">
    <link rel="icon" type="image/png" href="/favicon.png">


                <link href="/css/dashboard.css" rel="stylesheet">
                </head>
                <body>
                <header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
                  <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6" href="/">Cradicle Explorer</a>
                  <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                  </button>
                  <form method="get" action="/cgi-bin/main" style="width:100%;"><input class="form-control form-control-dark w-100 rounded-0 border-0" type="text" name="q" placeholder="Search repos" aria-label="Search"></form>
                  <div class="navbar-nav flex-row">
                    <div class="nav-item text-nowrap">
                      <a class="nav-link px-3 active" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri">ai-audio-runtimes_whatsapp-ai-agent-sample-for-aws-agentcore</a>
                    </div>
                  </div>
                </header>
                <div class="container-fluid">
                  <div class="row">
                    <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-dark sidebar collapse">
                      <div class="position-sticky pt-3 sidebar-sticky">
                        <ul class="nav flex-column">
                          <li class="nav-item">
                            <a class="nav-link" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri">
                              <i class="align-text-bottom fa-solid fa-info"></i>
                              Info
                            </a>
                          </li>
                          <li class="nav-item">
                            <a class="nav-link" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&issue=list">
                              <i class="align-text-bottom fa-solid fa-layer-group"></i>
                              Issues
                            </a>
                          </li>
                          <li class="nav-item">
                            <a class="nav-link" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&patch=list">
                              <i class="align-text-bottom fa-solid fa-vest-patches"></i>
                              Patches
                            </a>
                          </li>
                          <li class="nav-item">
                            <a class="nav-link" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&wallet=list">
                              <i class="align-text-bottom fa-solid fa-wallet"></i>
                              Wallets
                            </a>
                          </li>
                          <li class="nav-item">
                            <a class="nav-link active" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&source=.">
                              <i class="align-text-bottom fa-solid fa-code"></i>
                              Source
                            </a>
                          </li>
                        <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted text-uppercase">
                          <span></span>
                        </h6>
                        <ul class="nav flex-column mb-2">
                        
    <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-1 mb-1 text-muted text-uppercase">
      <span>Source</span>
    </h6>
    <li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&source=00-agent-agentcore"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> 00-agent-agentcore</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&source=01-whatsapp-end-user-messaging"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> 01-whatsapp-end-user-messaging</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&source=02-multichannel-api-gateway"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> 02-multichannel-api-gateway</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&source=images"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> images</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&source=notebook"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> notebook</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&file=.gitignore"><i class="fa-solid fa-file" style="color:#888;"></i> .gitignore</a></li><li><a class="nav-link py-0 active" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&file=CLAUDE.md"><i class="fa-solid fa-file" style="color:#888;"></i> CLAUDE.md</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&file=CODE_OF_CONDUCT.md"><i class="fa-solid fa-file" style="color:#888;"></i> CODE_OF_CONDUCT.md</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&file=CONTRIBUTING.md"><i class="fa-solid fa-file" style="color:#888;"></i> CONTRIBUTING.md</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&file=LICENSE"><i class="fa-solid fa-file" style="color:#888;"></i> LICENSE</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&file=README.md"><i class="fa-solid fa-file" style="color:#888;"></i> README.md</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&file=architecture.drawio"><i class="fa-solid fa-file" style="color:#888;"></i> architecture.drawio</a></li>
    
                        </ul>
                      </div>
                    </nav>
                <main class="col-md-9 ms-sm-auto col-lg-10">
                  <div class="container px-1 py-3">
        
<div class="mb-2" style="font-size:1.1rem;"><a href="/cgi-bin/repo?id=zTt2FPfNim3LDH4EBtSNsdLae3ri&source=.">/</a> CLAUDE.md</div>
        <div class="list-group">
        <div class="list-group-item">
        <div class="mb-2" style="font-weight:bold;"><i class="fa-solid fa-file"></i> CLAUDE.md</div>
        <pre style="margin:0; font-size:0.85rem; overflow-x:auto; color:#fafafa;"><span style="color:#666; user-select:none;">  1</span>  # CLAUDE.md
<span style="color:#666; user-select:none;">  2</span>  
<span style="color:#666; user-select:none;">  3</span>  This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
<span style="color:#666; user-select:none;">  4</span>  
<span style="color:#666; user-select:none;">  5</span>  ## Architecture
<span style="color:#666; user-select:none;">  6</span>  
<span style="color:#666; user-select:none;">  7</span>  Three independent CDK stacks deployed in sequence, sharing configuration via SSM Parameter Store:
<span style="color:#666; user-select:none;">  8</span>  
<span style="color:#666; user-select:none;">  9</span>  - **00-agent-agentcore** — Standalone AgentCore Runtime with a Strands multimodal agent + AgentCore Memory. Exports ARNs to SSM.
<span style="color:#666; user-select:none;"> 10</span>  - **01-whatsapp-end-user-messaging** — WhatsApp via AWS End User Messaging Social: SNS -&gt; receiver Lambda -&gt; DynamoDB (Stream + tumbling window) -&gt; processor Lambda -&gt; AgentCore.
<span style="color:#666; user-select:none;"> 11</span>  - **02-multichannel-api-gateway** — WhatsApp + Instagram DM via Meta Cloud API: API Gateway -&gt; receiver Lambda (dual-channel detection) -&gt; DynamoDB (Stream + tumbling window) -&gt; processor Lambda -&gt; AgentCore. Single webhook serves both platforms.
<span style="color:#666; user-select:none;"> 12</span>  
<span style="color:#666; user-select:none;"> 13</span>  Each stack has its own `app.py`, `cdk.json`, and `requirements.txt`. They are independent CDK apps, not a single multi-stack app.
<span style="color:#666; user-select:none;"> 14</span>  
<span style="color:#666; user-select:none;"> 15</span>  ## Build &amp; Deploy Commands
<span style="color:#666; user-select:none;"> 16</span>  
<span style="color:#666; user-select:none;"> 17</span>  Each stack is deployed from its own directory:
<span style="color:#666; user-select:none;"> 18</span>  
<span style="color:#666; user-select:none;"> 19</span>  ```bash
<span style="color:#666; user-select:none;"> 20</span>  cd 00-agent-agentcore  # or 01-... or 02-...
<span style="color:#666; user-select:none;"> 21</span>  python3 -m venv .venv &amp;&amp; source .venv/bin/activate
<span style="color:#666; user-select:none;"> 22</span>  uv pip install -r requirements.txt
<span style="color:#666; user-select:none;"> 23</span>  cdk deploy
<span style="color:#666; user-select:none;"> 24</span>  ```
<span style="color:#666; user-select:none;"> 25</span>  
<span style="color:#666; user-select:none;"> 26</span>  For the agent stack specifically, build the deployment package first:
<span style="color:#666; user-select:none;"> 27</span>  
<span style="color:#666; user-select:none;"> 28</span>  ```bash
<span style="color:#666; user-select:none;"> 29</span>  cd 00-agent-agentcore
<span style="color:#666; user-select:none;"> 30</span>  bash create_deployment_package.sh  # builds ARM64 ZIP in agent_files/
<span style="color:#666; user-select:none;"> 31</span>  cdk deploy
<span style="color:#666; user-select:none;"> 32</span>  ```
<span style="color:#666; user-select:none;"> 33</span>  
<span style="color:#666; user-select:none;"> 34</span>  For Stack 02, install Lambda layer dependencies before deploy:
<span style="color:#666; user-select:none;"> 35</span>  
<span style="color:#666; user-select:none;"> 36</span>  ```bash
<span style="color:#666; user-select:none;"> 37</span>  cd 02-multichannel-api-gateway/layers/common
<span style="color:#666; user-select:none;"> 38</span>  pip install requests -t python/
<span style="color:#666; user-select:none;"> 39</span>  cd ../..
<span style="color:#666; user-select:none;"> 40</span>  cdk deploy
<span style="color:#666; user-select:none;"> 41</span>  ```
<span style="color:#666; user-select:none;"> 42</span>  
<span style="color:#666; user-select:none;"> 43</span>  After deploying Stack 00, update the TwelveLabs API key in Secrets Manager:
<span style="color:#666; user-select:none;"> 44</span>  
<span style="color:#666; user-select:none;"> 45</span>  ```bash
<span style="color:#666; user-select:none;"> 46</span>  aws secretsmanager put-secret-value \
<span style="color:#666; user-select:none;"> 47</span>    --secret-id &lt;TwelveLabsSecretArn from stack output&gt; \
<span style="color:#666; user-select:none;"> 48</span>    --secret-string &#x27;{&quot;TL_API_KEY&quot;:&quot;your-actual-key&quot;}&#x27;
<span style="color:#666; user-select:none;"> 49</span>  ```
<span style="color:#666; user-select:none;"> 50</span>  
<span style="color:#666; user-select:none;"> 51</span>  ## Key Design Decisions
<span style="color:#666; user-select:none;"> 52</span>  
<span style="color:#666; user-select:none;"> 53</span>  ### Message Buffering (Tumbling Window)
<span style="color:#666; user-select:none;"> 54</span>  
<span style="color:#666; user-select:none;"> 55</span>  Both Stack 01 and 02 use DynamoDB Streams with a tumbling window (20 seconds) to aggregate rapid-fire WhatsApp messages into a single agent invocation. Based on [sample-whatsapp-end-user-messaging-connect-chat](https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat).
<span style="color:#666; user-select:none;"> 56</span>  
<span style="color:#666; user-select:none;"> 57</span>  - DynamoDB table PK=`from_phone`, SK=`id` ensures same-user messages land in the same shard
<span style="color:#666; user-select:none;"> 58</span>  - `tumbling_window` + `max_batching_window` on the Lambda event source mapping (configurable via `buffer_seconds` in CDK)
<span style="color:#666; user-select:none;"> 59</span>  - Processor Lambda deserializes DDB stream records, groups by sender, concatenates texts with `\n`, keeps last media
<span style="color:#666; user-select:none;"> 60</span>  
<span style="color:#666; user-select:none;"> 61</span>  ### Configurable Models
<span style="color:#666; user-select:none;"> 62</span>  
<span style="color:#666; user-select:none;"> 63</span>  Both the LLM and video analysis model are configurable via environment variables set before `cdk deploy`:
<span style="color:#666; user-select:none;"> 64</span>  
<span style="color:#666; user-select:none;"> 65</span>  - `MODEL_ID` — Claude model (default: `us.anthropic.claude-sonnet-4-20250514-v1:0`)
<span style="color:#666; user-select:none;"> 66</span>  - `TL_MODEL_NAME` — TwelveLabs model (default: `pegasus1.2`)
<span style="color:#666; user-select:none;"> 67</span>  
<span style="color:#666; user-select:none;"> 68</span>  ### AgentCore Memory IDs
<span style="color:#666; user-select:none;"> 69</span>  
<span style="color:#666; user-select:none;"> 70</span>  Two IDs drive memory — they must be different strings (enforced by the SDK):
<span style="color:#666; user-select:none;"> 71</span>  
<span style="color:#666; user-select:none;"> 72</span>  - **actor_id**: Canonical `u-user-{uuid}` from the unified users table (padded to 33 chars). Same across all channels for linked users.
<span style="color:#666; user-select:none;"> 73</span>  - **session_id**: Channel-specific — `wa-chat-{phone}` or `ig-chat-{sender_id}` (padded to 33 chars). Keeps conversation turns separate per channel.
<span style="color:#666; user-select:none;"> 74</span>  
<span style="color:#666; user-select:none;"> 75</span>  Stack 01 (SNS) uses legacy format `wa-user-{phone}` as actor_id (no unified users table).
<span style="color:#666; user-select:none;"> 76</span>  
<span style="color:#666; user-select:none;"> 77</span>  ### Unified User Identity (Stack 02)
<span style="color:#666; user-select:none;"> 78</span>  
<span style="color:#666; user-select:none;"> 79</span>  The `unified_users` DynamoDB table maps channel-specific IDs to a canonical `user_id`:
<span style="color:#666; user-select:none;"> 80</span>  - GSI `wa-phone-index` on `wa_phone` for WhatsApp lookups
<span style="color:#666; user-select:none;"> 81</span>  - GSI `ig-id-index` on `ig_id` for Instagram lookups
<span style="color:#666; user-select:none;"> 82</span>  - Table name exported to SSM: `/agentcore/unified_users_table_name`
<span style="color:#666; user-select:none;"> 83</span>  - AgentCore runtime role has read/write access
<span style="color:#666; user-select:none;"> 84</span>  
<span style="color:#666; user-select:none;"> 85</span>  The agent has a `link_account` tool that merges two user records when a user provides their other channel identity. The tool reads the table name from SSM at runtime (`ssm:GetParameter` on `/agentcore/*`). This permission is granted in `agentcore_role.py` (Stack 00) because the table is created by Stack 02 after Stack 00 deploys — SSM bridges the dependency.
<span style="color:#666; user-select:none;"> 86</span>  
<span style="color:#666; user-select:none;"> 87</span>  **Important**: When `link_account` is called from WhatsApp with an IG username, it saves `ig_username` but cannot resolve the numeric `ig_id` (only known when IG sends a webhook). The processor handles this with a fallback: if lookup by `ig_id` GSI fails, it scans by `ig_username`, finds the linked user, and backfills the `ig_id` for instant future lookups.
<span style="color:#666; user-select:none;"> 88</span>  
<span style="color:#666; user-select:none;"> 89</span>  ### Memory Retrieval Config
<span style="color:#666; user-select:none;"> 90</span>  
<span style="color:#666; user-select:none;"> 91</span>  - Namespace format: `/strategies/{memoryStrategyId}/actors/{actorId}/` (NOT `/users/{actorId}/facts`)
<span style="color:#666; user-select:none;"> 92</span>  - Strategy IDs passed as env vars: `FACTS_STRATEGY_ID`, `PREFERENCES_STRATEGY_ID` (set at `cdk deploy` time)
<span style="color:#666; user-select:none;"> 93</span>  - `top_k: 20` for facts, `top_k: 10` for preferences, `relevance_score: 0.3` for both
<span style="color:#666; user-select:none;"> 94</span>  - The agent prompt includes explicit &quot;Fact:&quot; lines in responses to improve long-term memory extraction quality (AgentCore summarizes aggressively and drops structured data like IDs)
<span style="color:#666; user-select:none;"> 95</span>  
<span style="color:#666; user-select:none;"> 96</span>  ### Agent Prompt Tags
<span style="color:#666; user-select:none;"> 97</span>  
<span style="color:#666; user-select:none;"> 98</span>  The processor prepends context tags to every prompt sent to AgentCore:
<span style="color:#666; user-select:none;"> 99</span>  - `[Channel: whatsapp|instagram]` — current channel
<span style="color:#666; user-select:none;">100</span>  - `[UserID: user-xxx]` — canonical user ID (only in Stack 02)
<span style="color:#666; user-select:none;">101</span>  - `[User: Name]` — display name from WhatsApp contact or Instagram profile
<span style="color:#666; user-select:none;">102</span>  
<span style="color:#666; user-select:none;">103</span>  The agent only offers cross-channel linking when `[UserID:]` is present.
<span style="color:#666; user-select:none;">104</span>  
<span style="color:#666; user-select:none;">105</span>  ### Multimedia Processing
<span style="color:#666; user-select:none;">106</span>  
<span style="color:#666; user-select:none;">107</span>  AgentCore Memory only stores text. All multimedia is converted to text before entering memory:
<span style="color:#666; user-select:none;">108</span>  
<span style="color:#666; user-select:none;">109</span>  - **Image**: Claude vision (inline content blocks) — text block MUST be first in content array
<span style="color:#666; user-select:none;">110</span>  - **Audio**: Amazon Transcribe -&gt; transcript sent as text prompt to agent (no media block)
<span style="color:#666; user-select:none;">111</span>  - **Video**: TwelveLabs API direct (`api.twelvelabs.io/v1.3`), NOT via Bedrock Marketplace. Agent stores `[VIDEO: id={video_id} | desc=&quot;{description}&quot;]` tags for follow-up queries. API key in Secrets Manager (`TL_SECRET_ARN` env var).
<span style="color:#666; user-select:none;">112</span>  - **Document**: Claude reads PDF/DOCX inline — filename must be sanitized (alphanumeric, spaces, hyphens, parens, brackets only)
<span style="color:#666; user-select:none;">113</span>  
<span style="color:#666; user-select:none;">114</span>  ### Content Block Ordering (Critical)
<span style="color:#666; user-select:none;">115</span>  
<span style="color:#666; user-select:none;">116</span>  When sending multimodal content blocks to the agent, **text must be the first content block**. `AgentCoreMemorySessionManager` reads `content[0][&quot;text&quot;]` for memory retrieval. If the first block is an image or document, it crashes with `KeyError: &#x27;text&#x27;` and the invalid content pollutes memory permanently.
<span style="color:#666; user-select:none;">117</span>  
<span style="color:#666; user-select:none;">118</span>  ### Input Validation (Critical)
<span style="color:#666; user-select:none;">119</span>  
<span style="color:#666; user-select:none;">120</span>  All media must be validated BEFORE reaching the agent to prevent memory contamination. If invalid content enters memory, it cannot be deleted per-record — the entire AgentCore Memory resource must be recreated.
<span style="color:#666; user-select:none;">121</span>  
<span style="color:#666; user-select:none;">122</span>  ### Agent Security Prompt
<span style="color:#666; user-select:none;">123</span>  
<span style="color:#666; user-select:none;">124</span>  The system prompt includes security rules: never reveal S3 bucket names, ARNs, error stack traces, or internal details to users. If a tool fails, the agent says &quot;I had a technical issue&quot; without sharing the error. The prompt also handles personalization via `[User: Name]` tags extracted from WhatsApp contact profiles.
<span style="color:#666; user-select:none;">125</span>  
<span style="color:#666; user-select:none;">126</span>  ### Runtime Container Caching
<span style="color:#666; user-select:none;">127</span>  
<span style="color:#666; user-select:none;">128</span>  AgentCore runs each session in an isolated microVM. Sessions stay active for up to **8 hours** and terminate after **15 minutes** of inactivity. After redeploying agent code, wait for the idle timeout or use a new `session_id`.
<span style="color:#666; user-select:none;">129</span>  
<span style="color:#666; user-select:none;">130</span>  ### SSM Parameter Sharing
<span style="color:#666; user-select:none;">131</span>  
<span style="color:#666; user-select:none;">132</span>  `get_param.py` (in stacks 01 and 02) reads SSM at CDK **synthesis** time using boto3. AWS credentials must be available when running `cdk synth/deploy`. Parameters exported by Stack 00: `/agentcore/agent_runtime_arn`, `/agentcore/s3_bucket_name`, `/agentcore/memory_id`, `/agentcore/runtime_role_arn`. Stack 02 exports: `/agentcore/unified_users_table_name`.
<span style="color:#666; user-select:none;">133</span>  
<span style="color:#666; user-select:none;">134</span>  ### Agent Runtime Requirements
<span style="color:#666; user-select:none;">135</span>  
<span style="color:#666; user-select:none;">136</span>  - `multimodal_agent.py` must end with `app.run()` or the runtime fails to start within the 30s initialization window
<span style="color:#666; user-select:none;">137</span>  - `requirements.txt` must include `bedrock-agentcore-starter-toolkit`, `requests`, and `twelvelabs`
<span style="color:#666; user-select:none;">138</span>  - `create_deployment_package.sh` must NOT exclude any dependencies (strands needs watchdog, etc.)
<span style="color:#666; user-select:none;">139</span>  
<span style="color:#666; user-select:none;">140</span>  ### Stack 02 Secrets Manager
<span style="color:#666; user-select:none;">141</span>  
<span style="color:#666; user-select:none;">142</span>  **WhatsApp secret** (no `WHATS_PHONE_ID` — phone_id comes from the webhook payload):
<span style="color:#666; user-select:none;">143</span>  
<span style="color:#666; user-select:none;">144</span>  - `WHATS_VERIFICATION_TOKEN` — webhook verify token (you define it, must match Meta config)
<span style="color:#666; user-select:none;">145</span>  - `WHATS_TOKEN` — Meta Graph API access token
<span style="color:#666; user-select:none;">146</span>  - `DISPLAY_PHONE_NUMBER` — business phone number for message filtering
<span style="color:#666; user-select:none;">147</span>  
<span style="color:#666; user-select:none;">148</span>  **Instagram secret** (separate from WhatsApp — different token):
<span style="color:#666; user-select:none;">149</span>  
<span style="color:#666; user-select:none;">150</span>  - `IG_TOKEN` — Instagram API access token (generated from Meta App Dashboard &gt; Instagram &gt; API setup with Instagram business login)
<span style="color:#666; user-select:none;">151</span>  - `IG_ACCOUNT_ID` — Instagram Business Account ID (used to filter own messages / echo)
<span style="color:#666; user-select:none;">152</span>  - `IG_VERIFICATION_TOKEN` — webhook verify token for Instagram (you define it)
<span style="color:#666; user-select:none;">153</span>  
<span style="color:#666; user-select:none;">154</span>  ### AgentCore Invocation Retry (Stack 02)
<span style="color:#666; user-select:none;">155</span>  
<span style="color:#666; user-select:none;">156</span>  The processor Lambda retries `InvokeAgentRuntime` with exponential backoff (3 attempts, base 2s) on:
<span style="color:#666; user-select:none;">157</span>  - `RuntimeClientError` (424) — runtime microVM unavailable or crashed
<span style="color:#666; user-select:none;">158</span>  - `InternalServerException` (500) — temporary, resolves with retries per AWS docs
<span style="color:#666; user-select:none;">159</span>  - `ThrottlingException` (429) — rate limit exceeded
<span style="color:#666; user-select:none;">160</span>  
<span style="color:#666; user-select:none;">161</span>  This handles cold starts after session resume (new microVM provisioning) and transient runtime errors.
<span style="color:#666; user-select:none;">162</span>  References: [InvokeAgentRuntime API](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html), [Runtime Sessions](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-sessions.html).
<span style="color:#666; user-select:none;">163</span>  
<span style="color:#666; user-select:none;">164</span>  ### Stack 02 Dual-Channel Webhook
<span style="color:#666; user-select:none;">165</span>  
<span style="color:#666; user-select:none;">166</span>  The receiver Lambda detects the channel from `body[&quot;object&quot;]`:
<span style="color:#666; user-select:none;">167</span>  - `whatsapp_business_account` -&gt; WhatsApp processing (parses `entry[].changes[].value.messages[]`)
<span style="color:#666; user-select:none;">168</span>  - `instagram` -&gt; Instagram processing (parses `entry[].messaging[]`)
<span style="color:#666; user-select:none;">169</span>  
<span style="color:#666; user-select:none;">170</span>  Instagram messages use `from_phone = ig-{sender_id}` as DDB partition key to avoid collision with WhatsApp phone numbers. The processor reads the `channel` field from each DDB record to dispatch replies via the correct API.
</pre>
        </div>
        </div>

</div>
</main>
</div>
</div>


</body>
</html>

