Agentic Pages Implementation Handoff
Use this handoff when you are enabling, validating, or troubleshooting 10x Agentic Pages for an external integration. It is intentionally more detailed than the user guide.
Runtime model
The public page agent combines:
- public page context from the current published page
- related published page summaries from the same handle
- handle knowledge retrieved with
containerTags: ["public-page-agent"] - Bedrock generation for the final answer through AI SDK streaming
- AI SDK UI message parts for the browser answer and generated lead qualification section
- browser rendering through
/assets/public-page-agent.js
Embeddings stay on the existing Titan path:
amazon.titan-embed-text-v2:0
Generation uses the configured public-page model:
PUBLIC_PAGE_AGENT_MODEL_ID
The default planned generation target is:
anthropic.claude-haiku-4-5-20251001-v1:0
The configured value may be a Bedrock model ID, geo inference ID, global inference ID, or full inference-profile ARN. The streaming agentic-chat route also has a fallback generation target:
PUBLIC_PAGE_AGENT_FALLBACK_MODEL_ID
The default fallback target is:
openai.gpt-oss-20b-1:0
Set the fallback value to none, off, false, or disabled to disable fallback. Before production traffic, verify both configured values in the target AWS account and region with direct Bedrock smoke tests.
Bedrock Mantle Gemma 4 31B model option
The streaming chat route supports provider-prefixed model ids. Unprefixed values and bedrock: values use the existing Amazon Bedrock Runtime provider. bedrock-mantle: values use the Bedrock Mantle OpenAI-compatible Chat Completions endpoint.
Gemma 4 31B must use Mantle:
PUBLIC_PAGE_AGENT_MODEL_ID=bedrock-mantle:google.gemma-4-31b
PUBLIC_PAGE_AGENT_FALLBACK_MODEL_ID=us.anthropic.claude-haiku-4-5-20251001-v1:0
PUBLIC_PAGE_AGENT_MANTLE_BASE_URL=https://bedrock-mantle.us-east-1.api.aws/openai/v1
PUBLIC_PAGE_AGENT_MANTLE_TIMEOUT_MS=8000
The Lambda uses @aws/bedrock-token-generator to mint a short-term Bedrock bearer token at runtime. Do not store a long-term Bedrock API key in SSM. The API Lambda role must allow bedrock:CallWithBearerToken with bedrock:bearerTokenType set to SHORT_TERM.
The Mantle lane does not ask Gemma to emit AI SDK tool calls. It asks for strict JSON, validates the payload server-side, normalizes the generated lead section through the existing lead-section safety rules, and emits the same UI-message SSE parts used by the React widget. If Mantle transport or payload validation fails, the route logs public_page_agent_chat_primary_failed without visitor text or retrieved snippets and uses fallback to Haiku when configured.
Example prod SSM switch after the code is deployed:
aws ssm put-parameter \
--region us-east-1 \
--profile 10xdotin-target-prod-deploy \
--name /10x-dot-in/prod/api/public-page-agent-model-id \
--type String \
--value 'bedrock-mantle:google.gemma-4-31b' \
--overwrite
aws ssm put-parameter \
--region us-east-1 \
--profile 10xdotin-target-prod-deploy \
--name /10x-dot-in/prod/api/public-page-agent-fallback-model-id \
--type String \
--value 'us.anthropic.claude-haiku-4-5-20251001-v1:0' \
--overwrite
Rollback is config-first. Set PUBLIC_PAGE_AGENT_MODEL_ID back to Haiku, refresh runtime config through the normal release process, and revalidate /agentic-chat. Code rollback is only needed if the Bedrock Runtime fallback path breaks.
Availability gates
The visitor route is unauthenticated, but availability is enforced internally per handle.
Required gates:
- Global switch:
PUBLIC_PAGE_AGENT_ENABLED. - Handle override:
PUBLIC_PAGE_AGENT_ALLOWED_HANDLES. - Tenant entitlement for non-allowlisted handles:
marketing_tools. - Public route disabled response: return non-leaky
404for anonymous traffic. - Contract shorthand: disabled handle returns non-leaky
404.
Do not expose whether a handle exists, whether a tenant is entitled, or which gate failed.
Operator setup sequence
Use this sequence when enabling Agentic Pages in a new environment or for a new handle.
- Confirm the runtime route is deployed:
``text POST /v2/public/pages/{handle}/{pageSlug}/agentic-question POST /v2/public/pages/{handle}/{pageSlug}/agentic-chat ``
- Confirm the page shell can inject the widget asset:
```html
```
- Set runtime availability:
``text PUBLIC_PAGE_AGENT_ENABLED=true ``
or add the handle to:
``text PUBLIC_PAGE_AGENT_ALLOWED_HANDLES=acme,productivity-ops ``
- Set the model:
``text PUBLIC_PAGE_AGENT_MODEL_ID=us.anthropic.claude-haiku-4-5-20251001-v1:0 PUBLIC_PAGE_AGENT_FALLBACK_MODEL_ID=openai.gpt-oss-20b-1:0 ``
- If the environment reads these values from SSM/CDK context, refresh the runtime SSM context before synth and deploy. Then deploy the API stack, the public page shell asset, and any edge/site asset changes needed for
/assets/public-page-agent.js.
- Smoke test from the public host, not only the API host. The HTML response must include
tenx-public-page-agent-config,/assets/public-page-agent.js, and either the inline mount or the floating launcher path.
JWT cannot enable the anonymous route by itself. JWT creates and publishes handle-owned content; deployment and environment configuration make the visitor runtime available.
Runtime fallback and alerts
The streaming route first tries PUBLIC_PAGE_AGENT_MODEL_ID. If model streaming fails, it logs public_page_agent_chat_primary_failed without question text or retrieved content and retries once with PUBLIC_PAGE_AGENT_FALLBACK_MODEL_ID. If the fallback also fails, it logs public_page_agent_chat_failed and returns a non-leaky SSE error event:
event: error
data: {"error":"chat_unavailable"}
CloudWatch metric filters emit TenXDotIn/PublicPageAgent metrics for both log events. The alarm public-page-agent-primary-model-failures catches primary-model degradation even when fallback protects visitors. The alarm public-page-agent-chat-failures catches visitor-visible failures after all configured models fail.
Creator JWT setup sequence
Use a creator/operator JWT only for setup calls:
export API_BASE="https://ai.10x.in"
export HANDLE="acme"
export PAGE_SLUG="launch-agent"
export JWT_TOKEN="<jwt-token>"
Create or update the page with template: "html_render", publish it, then seed public-agent KB documents:
POST /v2/handles/{handle}/pages
PUT /v2/handles/{handle}/pages/{pageSlug}
POST /v2/handles/{handle}/pages/{pageSlug}/publish
POST /v2/handles/{handle}/knowledge/documents
POST /v2/handles/{handle}/knowledge/query
After setup, validate through anonymous public routes:
POST /v2/public/pages/{handle}/{pageSlug}/agentic-question
POST /v2/public/pages/{handle}/{pageSlug}/agentic-chat
If setup calls succeed but anonymous routes return 404, the likely issue is availability/deployment, not the JWT.
Public route contract
Route registry entry:
{
"method": "POST",
"pathTemplate": "/v2/public/pages/{handle}/{pageSlug}/agentic-question",
"authMode": "none",
"module": "pages",
"operation": "agenticQuestion"
}
The route mirrors existing public page siblings:
POST /v2/public/pages/{handle}/{pageSlug}/access-check
POST /v2/public/pages/{handle}/{pageSlug}/checkout
POST /v2/public/pages/{handle}/{pageSlug}/agentic-question
POST /v2/public/pages/{handle}/{pageSlug}/agentic-chat
Request:
{
"question": "What support is included?"
}
Limits:
questionis required.- Maximum question length is 500 characters.
- A 501-character request returns
400 question_too_long.
Response:
{
"answerMarkdown": "Answer text",
"evidenceCards": [],
"ctaCards": [],
"followUps": [],
"degraded": false,
"retrievalMode": "page+related_pages+vector",
"modelId": "anthropic.claude-haiku-4-5-20251001-v1:0"
}
Streaming UI route registry entry:
{
"method": "POST",
"pathTemplate": "/v2/public/pages/{handle}/{pageSlug}/agentic-chat",
"authMode": "none",
"module": "pages",
"operation": "agenticChat"
}
Streaming request:
{
"messages": [
{
"role": "user",
"parts": [{ "type": "text", "text": "What support is included?" }]
}
]
}
Streaming response:
content-type: text/event-stream
The stream uses AI SDK UI message parts. The model lane uses PUBLIC_PAGE_AGENT_MODEL_ID with the Amazon Bedrock provider. Retrieval, availability, teaser-safe paid-page context, 500-character latest-user-message limit, and non-leaky 404 behavior must match the legacy JSON route.
Page embed contract
Eligible 10x public pages receive a JSON config script and the public agent asset from the page shell.
Inline mode:
<div id="tenx-public-page-agent-inline"></div>
Alternative inline mode:
<div data-tenx-public-page-agent-inline></div>
If an inline mount exists, the widget renders in place and hides the floating launcher. If no inline mount exists, the widget appends a floating Ask launcher.
The browser widget calls the same-origin streaming public route with no visitor credentials:
POST /v2/public/pages/{handle}/{pageSlug}/agentic-chat
The legacy JSON endpoint remains available for non-widget browser Q&A and API validation:
POST /v2/public/pages/{handle}/{pageSlug}/agentic-question
Widget customization contract
The resolved widget customization config is injected through tenx-public-page-agent-config.
Resolution order:
built-in variant defaults -> handle defaults -> page overrides -> embed safe overrides
Rich slot HTML is authenticated-only. Public embeds can select variants and safe tokens with JSON or data-*, but cannot provide rich HTML or CSS.
Generated lead form HTML remains sandboxed in the iframe. Custom leadIntro content renders above the lead card/frame and must not replace native lead submission.
Validation checklist:
- Reject scripts, iframes, forms, inputs, inline event handlers, external scripts/styles, unsafe URLs, and unsafe CSS.
- Cap rich slot HTML and CSS sizes.
- Clamp starter chip counts and copy lengths.
- Keep
/agentic-chatand/v2/public/handles/{handle}/leadssame-origin and credentialless.
Rich HTML page contract
The authenticated page APIs store content and teaser as HTML. For template: "html_render", the public renderer wraps the stored content inside the standard page shell. The content payload should be an HTML fragment:
<main>
<section>
<h1>Launch Agent</h1>
<p>Approved public context for the assistant.</p>
</section>
<section>
<h2>Ask the launch guide</h2>
<div id="tenx-public-page-agent-inline"></div>
</section>
</main>
Do not send a complete document with duplicate <html>, <head>, or <body> tags. The shell owns the document wrapper, meta tags, default typography, and public-agent script injection.
Operational rules:
- Put critical public facts in text nodes so retrieval can use them.
- Keep images and visual assets optional for comprehension.
- Place the inline mount at the interaction point. Without it, the widget falls back to floating mode.
- Treat page HTML as trusted creator-authored public HTML. Never copy untrusted visitor HTML into
content. - Do not include secrets, private signed URLs, internal dashboard links, or customer-specific data.
- For paid pages, the anonymous agent must use teaser-safe public context only. Keep paid-only claims out of
teaser.
Generated lead form contract
The streaming route exposes one model tool to the AI SDK generation call:
renderLeadQualificationSection
Tool output shape:
{
"sectionId": "lead-qualification",
"collapsedTitle": "Plan your next step",
"collapsedBody": "Share a few details and the team can follow up.",
"html": "<form>...</form>",
"fields": [],
"submitLabel": "Send",
"contextDisclosure": "This form includes the current page, question, answer, evidence titles, and retrieval mode.",
"dynamicView": {
"headline": "Launch support workspace",
"subhead": "A prompt-specific summary rendered by the host React widget.",
"recommendedPath": {
"label": "Best next move",
"body": "Share the launch goal and blocker so the owner can route the follow-up."
},
"momentumSteps": [],
"fitSignals": [],
"questionChips": [],
"visualAccent": "launch"
},
"leadContext": {}
}
Server-side validation requirements:
- Generated HTML max size: 12 KB.
- Disallow
script, inlineon*attributes, and external images, scripts, or styles. - Fields max: 8.
- Required field:
email. - Optional native fields:
name,company,phone, andmessage. - Up to 3 generated qualification fields.
- Field names must match the lead field key rules.
dynamicViewis rendered by the host React widget, not by iframe HTML. It may include a prompt-specific headline, subhead, recommended path, 3 to 4 momentum steps, 2 to 4 fit signals, follow-up chips, and an accent ofsignal,launch,support, orproof.
Browser containment and submission requirements:
- Render generated HTML only inside
<iframe sandbox="allow-forms allow-scripts">. - Do not inject generated HTML into the host page DOM.
- Iframe posts form data to the parent widget with
postMessage. - Parent widget validates the payload, attaches disclosed
leadContext, and submits throughPOST /v2/public/handles/{handle}/leadswithcredentials: "omit". - Visitor-facing disclosure must state that the answer context is included with the lead.
Generated HTML is deliberately narrower than page HTML. It is form HTML only. The server removes or blocks script, style, iframe, object, embed, link, img, inline event handlers, inline styles, and external src, href, or action values. Use dynamicView and native widget components for rich presentation instead of trying to ship arbitrary HTML, CSS, or JavaScript through the model.
Structured rich-card KB contract
The streaming route can expose additional native UI tools when retrieved public KB includes recognized structured blocks. The productivity lesson-card lane uses:
productivity_lesson_card
and streams:
renderProductivityLessonCards
Document pattern:
{ "lessonId": "time-blocking", "title": "Time Blocking", "category": "Planning", "summary": "Reserve focused calendar blocks for one kind of work.", "whenToUse": ["Context switching is high"], "steps": ["Choose one priority", "Block 45 minutes", "Review the result"], "tryNow": "Block the next 45 minutes for one task.", "relatedTactics": ["daily shutdown", "single-tasking"], "sourceDisclosure": "Public productivity guide." }
Rules:
- The model must preserve values from retrieved snippets. It should not invent lesson IDs, claims, or steps.
- The widget renders lesson cards as native React UI.
- The structured card block is public KB, so it must not contain paid-only or private content.
- Use this pattern only for known native widgets. Arbitrary rich HTML still belongs in the page body or the sandboxed lead-form iframe.
Group-buy coordinator safety contract
Agent-managed group buying uses stored visitor skill.md instructions. Mutating authority in v1 is limited to group-buy interest creation or update and strict-consensus handle-owned calendar booking.
The markdown must include deterministic constraints:
{
"groupBuy": {
"autoCommitInterest": true,
"quantity": 2,
"maxUnitPriceCents": 6000,
"currency": "usd"
},
"calendar": {
"autoBook": true,
"eventSlug": "group-buy-call",
"attendeeEmail": "buyer@example.com",
"attendeeName": "Buyer",
"timeZone": "UTC",
"availableWindows": [
{ "start": "2026-06-20T13:00:00.000Z", "end": "2026-06-25T18:00:00.000Z" }
],
"notifyByEmail": true,
"allowGroupNotification": true
}
}
Agent-managed interest does not auto-close, publish, pay, unlock offers, or message vendors. Visitors do not connect calendars. The coordinator books only against the handle-owned scheduling event and only when every matched participant has calendar.autoBook: true.
KB setup and steering
Only public-agent KB documents should use:
{
"containerTags": ["public-page-agent"]
}
Recommended document categories:
- Public facts: feature claims, support windows, launch steps, proof points.
- Pricing boundaries: what can be answered publicly and what requires sales.
- Competitor guidance: approved comparisons and fit criteria.
- Escalation policy: when to route to support, sales, or implementation.
- Refusal rules: secrets, paid content, private customer data, and internal-only processes.
Keep documents short. Prefer several single-purpose documents over one broad policy blob. Public-agent KB is a steering surface, not a private memory store.
Paid content and leak safety
For PUBLIC pages, page context can use the published content body.
For PAID pages, page context must use only teaser-safe public content. The assistant must not read or reveal paid body content to anonymous visitors.
Validation marker pattern:
- Paid page body contains a private marker, for example
DO-NOT-LEAK-APQA-PRIVATE. - Paid teaser excludes that marker.
- A public agent question about the marker must not include it in the API response or UI.
Abuse controls and logs
V1 controls:
- API Gateway throttle: burst
40, rate20. - 500-character question cap.
- Safe Markdown rendering in the browser asset.
- Generated HTML is constrained to the sandboxed iframe.
- Lead submission uses the native public lead endpoint, not information-request submissions.
- No logging of question text.
- No logging of retrieved page or KB content.
Allowed structured log metadata:
handle
pageSlug
modelId
retrievalMode
degraded
snippetCount
tookMs
Do not add visitor-level metering until the feature proves useful. If later added, keep it separate from the V1 safety/logging contract.
Staging validation
Use persistent fixtures so validation can repeat after every deploy.
Public page fixture:
- Published and visitor-visible.
- Contains the phrase:
onboarding checklists, launch templates, and implementation support. - Contains the inline mount when validating chat-first pages.
KB fixture:
{
"title": "Public Agent QA Knowledge Fixture",
"source": "public-agent-validation",
"contentType": "text/plain",
"containerTags": ["public-page-agent"],
"content": "QA support code is APQA-2026. The handoff window is two business days."
}
Paid leak fixture:
- Published as
PAID. - Body contains
DO-NOT-LEAK-APQA-PRIVATE. - Teaser excludes
DO-NOT-LEAK-APQA-PRIVATE.
Blocking checks:
- Focused tests pass for route, shell, asset, and stack coverage.
- Bedrock preflight succeeds for the exact configured
PUBLIC_PAGE_AGENT_MODEL_ID. - Direct KB query with
containerTags: ["public-page-agent"]returnsAPQA-2026. - Public page-context question returns
200, answer, evidence cards, CTA cards, and follow-ups. - Streaming chat question returns
200withtext/event-streamand one generated lead section tool part. - Generated lead section renders inside the iframe sandbox and posts to
POST /v2/public/handles/{handle}/leads. - RAG question returns
APQA-2026,two business days, and akb_docevidence card. - Unsupported secret question abstains.
- 501-character question returns
400 question_too_long. - Disabled handle returns non-leaky
404. - Paid private marker never appears in API output, UI output, or logs.
Local verification commands
Run the focused tests before deploying changes to the public agent surface:
npx vitest run \
test/api.public-page-agent.test.ts \
test/api.pages-public-agent-shell.test.ts \
test/api.route-registry.contract.test.ts \
test/public-page-agent-asset.test.ts \
--minWorkers=1 \
--maxWorkers=1
When stack resources or throttling change, also run:
npx vitest run test/stack.creator-economy.contract.test.ts --minWorkers=1 --maxWorkers=1
Run GitNexus checks in this workspace:
npx gitnexus analyze
gitnexus impact <symbol-or-flow>
gitnexus detect-changes
If your installed GitNexus CLI does not support detect-changes, run the closest available status/diff command and record the limitation in the release notes.
Related docs
Updated Jun 19, 2026
