Developers AI Support Agent

Developers

AI Support Agent

Customer support, verified identity, owner guidance, private knowledge and shared site usage.

AI Support Agent is the product name. The existing frontpage customer-inbox command, /customer-inbox API and widget paths, and manage_customer_inbox tools stay unchanged. See the setup guide for the workspace workflow.

Tested AI Support Agent walkthroughs

Start with these copyable workflows, then use the reference for the full command and field list. Response excerpts come from tested calls; extra fields are omitted and IDs may be normalized.

Install npm i -g frontpage-host@latest, set FRONTPAGE_API_KEY on your server, and replace acme with your site slug. Site roles, CLI access, and product permissions still apply. Use IDs and versions returned by your own calls.

CLI --json wraps success in ok, command and data. HTTP returns the resource body directly.

On this page:

Configure the agent and widget with version checks

Prerequisites: Scale or Internal Inbox entitlement, API access, and an owner/admin key. Check both eligibility and activation. Feature enablement, chat, email transport and agent availability are separate states; claiming an address alone does not prove they are running.

frontpage customer-inbox get status --site acme --json
frontpage customer-inbox get settings --site acme --json

Read version from settings. For a new Inbox it can be 0; use the actual value. Save this as inbox-settings.json:

{
  "expectedVersion": 0,
  "value": {
    "instructions": "Answer from our approved business knowledge. Ask for private owner guidance when important facts are missing.",
    "widget": {
      "enabled": true,
      "origins": [
        "https://example.com"
      ],
      "requireName": true,
      "requireEmail": true
    }
  }
}
frontpage customer-inbox put settings --file inbox-settings.json --request-key inbox_settings_001 --site acme --json
{
  "ok": true,
  "command": "customer-inbox",
  "data": {
    "version": 1,
    "value": {
      "instructions": "Answer from our approved business knowledge. Ask for private owner guidance when important facts are missing.",
      "widget": {
        "color": "#7254d6",
        "title": "Message our team",
        "enabled": true,
        "origins": [
          "https://example.com"
        ],
        "greeting": "How can we help?",
        "requireName": true,
        "requireEmail": true
      }
    }
  }
}

Result: the new settings revision is saved. This example's response includes defaults merged with your patch. It does not activate an otherwise disabled Inbox. To activate an eligible site, save {"enabled":true} as activation.json, then run:

frontpage customer-inbox put activation --file activation.json --request-key inbox_activation_001 --site acme --json
frontpage customer-inbox get status --site acme --json

Retry: resend the same settings file with the same request key after a timeout. Our test returned the same version without applying a second revision. A different key with the old expectedVersion returned:

{
  "ok": false,
  "command": "customer-inbox",
  "error": {
    "code": "conflict",
    "message": "Refresh settings before saving.",
    "httpStatus": 409,
    "exitCode": 3,
    "requestId": "f320a282-2dfb-4a09-8390-6b37ff73bd9a"
  }
}

Fetch settings, review any intervening edit, then save a new revision and request key. Do not replace the version blindly.

Claim an address using the current policy

frontpage customer-inbox get address --site acme --json
{
  "ok": true,
  "command": "customer-inbox",
  "data": {
    "address": null,
    "policyVersion": "2026-10-13.1",
    "domain": "e.frontpage.host"
  }
}

Use the returned policyVersion in address.json; acme-example is an example, not a reserved allocation:

{
  "alias": "acme-example",
  "policyVersion": "2026-10-13.1"
}
frontpage customer-inbox put address --file address.json --request-key inbox_address_001 --site acme --json
{
  "ok": true,
  "command": "customer-inbox",
  "data": {
    "address": "acme-example@e.frontpage.host",
    "policyVersion": "2026-10-13.1",
    "domain": "e.frontpage.host"
  }
}

Result: the address is claimed if policy and collision checks pass. Read the current policy on each new claim; the version shown here is a captured example. Platform aliases such as support, help, and frontpage are reserved. Do not send an email to test an alias you have not claimed.

Give private guidance, take over, and return to the agent

Start with an existing conversation ID returned by get conversations. In this example, the customer is Casey Rivera. Commands use $CONVERSATION_ID; set it to the real UUID. First read the current handler version:

frontpage customer-inbox get "conversations/$CONVERSATION_ID/routing" --site acme --json
{
  "ok": true,
  "command": "customer-inbox",
  "data": {
    "handler": "agent",
    "handlerVersion": 0,
    "identity": {
      "verified": false
    }
  }
}

Save guidance.json:

{
  "text": "For this repair, ask Casey to send a photo before quoting.",
  "handlerVersion": 0,
  "privateGuidance": true
}
frontpage customer-inbox post "conversations/$CONVERSATION_ID/reply" --file guidance.json --request-key inbox_guidance_001 --site acme --json
{
  "ok": true,
  "command": "customer-inbox",
  "data": {
    "messageId": "86adae81-dc05-4422-b003-efb544731336"
  }
}

Result: a team-only message is stored. The agent still owns the conversation; private guidance does not send that text directly to the customer. Customer delivery happens when the running agent posts its public answer. Our command test verified that the handler remained agent.

To send a reply as the owner, explicitly take over. Save {"action":"takeover","handlerVersion":0} as takeover.json:

frontpage customer-inbox post "conversations/$CONVERSATION_ID/control" --file takeover.json --request-key inbox_takeover_001 --site acme --json
{
  "ok": true,
  "command": "customer-inbox",
  "data": {
    "handler": "owner",
    "handlerVersion": 1
  }
}

Then save owner-reply.json with the returned version:

{
  "text": "Hi Casey, please send a photo so we can help with your repair.",
  "handlerVersion": 1
}
frontpage customer-inbox post "conversations/$CONVERSATION_ID/reply" --file owner-reply.json --request-key inbox_reply_001 --site acme --json

A messageId means the public message is stored. Inspect the timeline and routing/delivery state; it does not prove that an email reached an inbox. To return control, save {"action":"return","handlerVersion":1} as return.json:

frontpage customer-inbox post "conversations/$CONVERSATION_ID/control" --file return.json --request-key inbox_return_001 --site acme --json
{
  "ok": true,
  "command": "customer-inbox",
  "data": {
    "handler": "agent",
    "handlerVersion": 2
  }
}

Always read and use the current handler version. Use a fresh key for a distinct action and the same key/body for its retry. Owner email guidance and customer email relay remain different, scoped reply paths; never parse a routing address to construct another customer's address.

Read the complete timeline without exposing private guidance

frontpage customer-inbox get "conversations/$CONVERSATION_ID/messages?timeline=true&audience=all&limit=1" --site acme --json
{
  "ok": true,
  "command": "customer-inbox",
  "data": {
    "conversationId": "e0aec064-23d3-4531-af02-2728b8228aad",
    "messages": [
      {
        "id": "3c6a2034-1dac-4a5b-aecd-9a576d7fc7a3",
        "text": "Can someone help me with a repair?",
        "audience": "public",
        "kind": "customer"
      }
    ],
    "nextCursor": "3c6a2034-1dac-4a5b-aecd-9a576d7fc7a3"
  }
}

This is an owner-only timeline, including private entries when present. Do not return it from a public chat endpoint. Pass nextCursor unchanged as after to read the next page; stop when it is null:

frontpage customer-inbox get "conversations/$CONVERSATION_ID/messages?timeline=true&audience=all&limit=1&after=$NEXT_CURSOR" --site acme --json

Typed email is not proof of identity. Unverified visitors can ask general business questions and use their own session history, but cannot retrieve or update an existing person's private CRM data, notes, or orders. Verified identity binds support tools to that customer. Customer messages cannot authorize business-knowledge edits; learning requires eligible owner guidance or approved sources, and locked knowledge remains protected. The owner toolbar has its separate, site-authorized management access.

The reference below covers knowledge revisions, locks, sources, exports, files, tool access, identity, storage and billing. The fixed Luna model uses shared site AI credits, with actual token counts recorded for usage tracking.

AI Support Agent reference

AI Support Agent uses the site's existing AI credits and storage allowance. It is available on Scale and internal plans after owner activation. Other public plans do not include Inbox. No pilot allowlist is required. The support agent uses fixed GPT-6 Luna and the site’s shared AI credits. During human handling, customer messages use ordinary routing. Normal owner-control email uses a small metered Luna Decisions call to keep private guidance, ownership controls and customer-facing replies distinct; the full support agent stays paused. UI and CLI controls need no intent call.

Run frontpage help customer-inbox. Use the site's normal CLI key:

frontpage customer-inbox get status --site my-site
frontpage customer-inbox get settings --site my-site
frontpage customer-inbox put settings --file ./settings.json --request-key settings_revision_1 --site my-site

Reads use get. Writes use put or post, a UTF-8 JSON object file, and an 8–128 character request key. Reuse the same key and body after a failed or lost response. Changed bodies need new keys. The service rejects a key reused for a different request. Read current versions before changing records.

Support search and conversation feedback

The support agent searches this business’s public website with focused keywords, reads promising results with website_read, then answers from the evidence. Public business questions do not require customer verification. The agent asks for owner guidance when a material fact or business decision remains unresolved, or an enabled human-help rule applies.

website_search({query,cursor?}) reads up to four pages per batch. Follow nextCursor with the same query; omit the old cursor when changing it. website_read({path,cursor?}) returns up to 8,000 characters of readable public text. Use a returned site-relative path, then follow its cursor with the same path for more text. Search previews and partial coverage are not complete evidence.

Expected failures return error, message, nextAction and retryable. An invalid or expired cursor tells the agent to restart without it; it is not reported as a website outage. Permissions and stale-job checks still stop restricted work.

knowledge_read({query}) returns matching passages from saved notes and enabled approved sources, with hasMore:{notes,sources}. Refine a limited query, then read note chunks with {id,cursor?} or source chunks with {sourceId,cursor?}, following nextCursor as needed. Direct source reads do not depend on the management list’s page. Knowledge approval, locks, freshness and learning permissions stay unchanged.

Chat shows sending/upload feedback and animated dots while a reply is queued or running. When the agent asks the team for guidance, the customer sees a waiting message instead of continued typing. The owner’s private email includes customer-visible conversation history and a link to the full thread; oversized histories use a labeled recent excerpt. Reply with new guidance above the quoted history. Private guidance stays private, and recipients, verification, takeover and email delivery rules remain unchanged.

Setup and settings

Resource Method JSON body
status GET None
activation PUT {"enabled":true}; admin only
address GET Returns the current address, domain and policy version
address PUT {"alias":"acme","policyVersion":"2026-10-13.1"}; admin only
address POST {"retire":true}; admin only
settings GET Returns version, value and fixed model details
settings PUT {"expectedVersion":0,"value":{"instructions":"Ask me if unsure."}}

Setup defaults private guidance to the verified administrator who requested it. An administrator can set value.guidanceEmail to a team mailbox, such as support@example.com; no separate activation link or teammate Frontpage account is required. Teammates reply as that exact mailbox. Private routes and receiving authentication still protect replies. Changing the destination revokes old private routes and cancels queued mail to them. guidanceToMe:true restores your verified account email. The destination cannot be the Inbox’s own public support alias. The agent manages new conversations by default after setup; the platform's Inbox switches control transport and automation independently of site activation. Set value.enabled:false for manual handling. Settings changes cancel stale automatic jobs and queued automatic sends.

Settings allow enabled, instructions, tools, maxSteps (1–12), maxOutputTokens (256–2400), and maxInputBytes (4000–64000). Supported tools: knowledge_read, knowledge_update, customer_read, verify_customer, customer_update, notes_read, note_add, ask_owner, reply_customer, wait, finish. Keep wait and finish enabled. Mailbox verification remains available whenever customer-data tools are enabled; it cannot be bypassed by settings. Defaults: 8 steps, 1200 output tokens per call, 32000 bytes of public text. The model is fixed. Only an administrator can choose the private guidance mailbox; the support agent cannot choose recipients. The worker also enforces platform concurrency, deadlines and input limits.

maxContextTokens (12000–96000, default 32000) keeps its API name but sets a conservative working-size allowance, not exact provider tokens or AI credits. Admission measures the provider request text and tools once, with reply and image reserves. Exact provider receipts still determine billing. Optional older history, preloaded notes and expendable read results may leave the working window. Current input, required owner guidance, safety instructions and completed action outcomes remain intact. Omitted reads carry a clear marker and must be reread before use. Full history and sources remain saved. Screening keeps the complete required public input. Required input that cannot fit needs owner review.

Reads share the remaining allowance across each tool batch. Website reads return smaller chunks with continuation cursors when needed. Knowledge previews report accurate hasMore flags. knowledge_read({id,cursor?}) and knowledge_read({sourceId,cursor?}) return bounded bodies with offset, truncated and nextCursor. Continue the identical ID; changed or expired content requires restarting without a cursor. Management note/source reads retain their full-body contract.

retrieval_budget_exhausted directs the agent to use one focused read or answer from sufficient retained evidence. Optional context can be removed before another paid call. Unresolved paid requests or email sends still need reconciliation.

Recovering a context pause

For input_budget or context_limit, open the conversation in Inbox and choose Retry reply. The same CLI endpoint rebuilds the working transcript for the unanswered request while preserving paid-attempt identities and the remaining step allowance:

frontpage customer-inbox get jobs/JOB_ID --site my-site
# Save the returned updatedAt in resume.json: {"updatedAt":"EXACT_RETURNED_VALUE"}
frontpage customer-inbox post jobs/JOB_ID/resume --file resume.json --request-key retry_context_1 --site my-site

Reuse that key and body after a lost response. New input, an existing answer, changed ownership/settings, unresolved paid attempts or uncertain sends block an unsafe restart. essential_context_limit, screening blocks and unknown paid outcomes require review; retry cannot bypass them. Take over to handle the customer when needed. The widget tells visitors their message is saved and the team needs to review automatic replies.

reviewAfterReplies (0–200, default 0/off) and reviewWindowHours (1–168, default 24) request private owner guidance after that many recent automated replies since the last guidance. Pending requests deduplicate. This is a rolling work control, not a cap on returning customers or retained history.

Customer replies commit before optional knowledge, People enrichment and notes. Customer-note maintenance and business learning are separate job modes. Only learning from assigned fresh authenticated owner guidance or an approved source revision can write shared knowledge. It receives no customer transcript, CRM data, attachments, history tools or send tools. The same durable jobs table carries separate internal maintenance with the same shared AI credits and exact usage receipts. It cannot send messages, request owner help, change ownership or edit public content. The workspace shows unfinished internal work separately and offers the existing safe retry controls for recoverable failures. Identical note content does not create another revision. Background business learning uses more reasoning on the same fixed Luna model; it still uses the same usage ledger and shared AI credits. Rejected learning writes cannot be reported as completed. If the bounded turn cannot resolve the failure, internal updates require review while the customer reply remains delivered.

Aliases use 3–48 ASCII letters, numbers and single hyphens. The service normalizes case and checks collisions in one transaction. Reserved platform names and routing namespaces cannot be claimed. Retired addresses remain reserved; they never move to another site.

Internal knowledge and approved sources

Resource Method JSON body
knowledge GET Recent notes; optional ?q=search
knowledge/<id> GET One full note
knowledge/<id>/history GET Immutable revisions for that note
knowledge PUT {"title":"Evening visits","body":"Ask the owner before promising an evening visit.","expectedVersion":0,"reason":"Owner policy"}
knowledge/<id> PUT Current title/body, expectedVersion, reason, optional locked or restoreVersion
sources GET Approved source records; ?overview=true also includes the latest learning job status
sources PUT {"url":"https://example.com/help","enabled":true,"expectedVersion":0}; fetches public text

The workspace’s Knowledge tab separates reusable internal facts from approved webpages. Adding a webpage fetches only that public page’s text; it does not crawl a website or edit an article. Expand View saved webpage text to see the snapshot the agent can use. Learning status reports the existing background job; queued does not mean the knowledge update has finished. Open a knowledge note to see its revisions, lock agent changes or restore a version.

To enable or disable a saved source without fetching it again, use the owner API:

POST /api/v1/sites/my-site/customer-inbox/sources/SOURCE_UUID
Authorization: Bearer <site CLI key>
Idempotency-Key: source-toggle-001
Content-Type: application/json

{"enabled":false,"expectedVersion":2}

The response data is the same source with enabled:false and version 3; its saved body, digest and check time stay unchanged. Repeat the same key and body after a lost response. A stale version returns 409 source_changed; a foreign source returns 404. Disabling prevents future source use and learning, but keeps existing knowledge notes. Refresh webpage fetches a fresh snapshot explicitly. The current packaged CLI retains its existing sources GET/PUT operations.

Supply body on a source write to save an owner-approved excerpt instead of fetching. Published sources refresh when the agent reads a source older than 24 hours. The fetcher reuses Core's DNS-pinned public HTTP infrastructure, rejects private addresses, checks redirects and bounds content. Sources over 16000 text characters require an owner-approved excerpt. No scripts run. A source version/digest fences knowledge updates against a changed source.

Knowledge search uses bounded literal keywords from natural-language queries and ranks notes by matched terms. Read the full matching note before updating it; update its current ID/version instead of creating a conflicting duplicate.

Lock a knowledge note with locked:true. Neither the support agent nor the owner toolbar agent can change locked notes or locks. Human UI/CLI actions can unlock them. Restore an older note with restoreVersion; the service creates a new revision and preserves the old history. The agent can change only internal knowledge, using verified owner guidance or a current approved source. It cannot edit public pages or articles.

Human conversation handling

Resource Method JSON body
conversations GET Paginated list; ?limit=50&after=<id>
conversations/<id> GET Thread and latest public message
conversations/<id>/messages GET ?timeline=true&limit=50&audience=all; follow the returned message-ID nextCursor with after; viewers get public history only
conversations/<id>/routing GET Current handler/version, People link and delivery states
conversations/<id>/reply POST {"text":"Tuesday at 6:30 works. Keep staffing private.","handlerVersion":0,"privateGuidance":true}
conversations/<id>/control POST {"action":"takeover","handlerVersion":0} or {"action":"return","handlerVersion":1}
conversations/<id>/reply POST {"text":"Your quote is ready.","handlerVersion":1}; requires current owner control
files?messageId=<id> GET Bound message files; editor/admin only
files/<id> GET Short-lived download grant; editor/admin only

One verified customer has one continuous thread per site, across direct email, replies and verified website chat. Email subject changes do not create new cases after identity is proven. Fresh mail to the public alias remains provisional until confirmation, even when From and DMARC match an existing customer. Recipient-bound secret reply routes continue the established thread. Typed email, display names and subjects never authorize merging or history access. Closing reopens that same thread on the next message. Legacy trashed threads can be reopened from Closed. Inbox joins retained history without moving messages, files or paid receipts. Old conversation links and recipient-bound reply addresses resolve to the current thread. Public chat includes customer-visible email replies and excludes owner guidance and notes.

Use timeline=true for combined history. Its opaque nextCursor is a message ID, not a sequence or offset; pass it unchanged as after or before. latest=true returns the latest bounded page, and before=<cursor> loads older messages. A cursor must belong to that conversation and audience. Sequence-based reads without timeline=true remain available for the root segment; use the combined timeline for every linked segment. The CLI export follows the complete combined timeline. The widget opens the latest 50 public messages and offers Load earlier messages, so a returning customer does not wait for years of history to download. The support agent's history_read tool likewise follows its returned nextBefore message ID.

Private guidance stays in the same internal thread. The agent asks by a private email and stores its question in the history. Replying by email or UI gives context and leaves the agent responsible for the customer. The agent uses that context to answer and can update unlocked internal knowledge.

General owner facts, such as support hours and standard policies, update related unlocked knowledge without an explicit save request. One-time customer exceptions and private staffing details stay in the conversation. The agent reads the full note and records verified evidence before changing it. Late private guidance can still update knowledge after the customer has an answer; that work cannot send a duplicate customer reply. Locks, manual handling, paused automation and work limits still apply.

Shared knowledge must remain useful and true for other customers when the current customer, order and case are removed. Individual delivery estimates, arrangements and fee waivers stay in that conversation or its bound People contact's factual notes. A “customer-specific” disclaimer does not make them shared business policy. Maintenance can finish successfully without writing any knowledge.

Search results contain previews. The support agent reads the complete matching note or approved source before deciding an answer is missing. A verified owner reply can provide lasting facts on a different topic from the customer's question; the agent answers the customer first, then separate internal maintenance processes lasting facts. The worker verifies the message kind and owner identity. Customer text cannot claim that authority. A specific offer to one customer stays in that thread even when the same owner reply also contains a general policy.

Email replies route through opaque, recipient-bound Reply-To addresses linked to the site, conversation, purpose, owner and handler version. These are local addresses on one domain, not a new subdomain per thread. Subjects and message text never select the customer thread. The service checks the exact verified sender, current owner membership, route and receiving-server DMARC proof before interpreting owner mail.

Every owner email and its files enter private custody first. On normal guidance and owner-control routes, a Luna Decisions call reads the fresh text and bounded, labeled thread context to distinguish guidance, takeover, return, customer reply or unclear input. Use ordinary language to ask to handle the customer, or ask the agent to resume. Questions, quoted commands, negations and future conditions do not transfer control. UI/CLI control remains a typed, model-free action. No phrase whitelist controls ownership. A valid but uncertain control answer can use one bounded Luna tool-call fallback. It can choose only private guidance, takeover, return or unclear, with an exact fresh-text excerpt; it cannot authorize a customer reply. Both calls require current credits and distinct once-only receipts. Invalid responses, unknown paid calls and provider outages stay private rather than triggering another inference.

Guidance keeps the agent responsible. Takeover sends the owner a separate customer-visible transcript; reply through that relay address so Inbox preserves the history. A customer-facing email creates a separate public message only after a settled decision and current owner-session check. The original intake remains private for audit. Mixed or uncertain input, unavailable inference and exhausted credits stay private with an attention notice. Retry recoverable budget/receipt pauses using the existing job operation; an unknown paid attempt is never inferred again. A fresh reply to the original guidance address can also request return, while stale customer-relay sessions cannot publish.

Operational support failures and exhausted AI credits use the existing owner takeover. The owner receives a Customer conversation email whose body says replies go directly to the customer without AI. These server-marked reply routes relay authenticated owner messages without Decisions or support inference; normal guidance routes remain private. Use the workspace or typed CLI control action to return these conversations to the agent. Text sent to a direct-reply route is customer-facing, not an instruction to interpret. If automation stops while you reply through an older control-capable thread, that input stays private; Inbox offers the explicit direct-reply email instead. Send only the customer-facing message through that route. Widget chat uses the same owner relay and shows the resulting public reply in its saved conversation. Recovery never silently returns control.

An existing unretired address can continue forwarding to the configured verified owner after site deactivation or a plan downgrade. New address claims, chat and automation still require Scale/internal eligibility and activation. Forwarding uses the current site's existing mail and storage allowance and current owner checks; exhausted allowances, revoked owners, banned/deleted sites and retired addresses cannot send. Use address POST with {"retire":true} to stop forwarding, including after deactivation or downgrade. This requires a current verified admin. Other paid Inbox UI/API operations remain gated on eligible plans; the exception grants email transport and address retirement only.

This fallback depends on working Inbox transport, its database, Core and Resend. A complete service/database outage delays mail until existing webhook retries and reconciliation recover it. There is no independent forwarding service, second webhook or instant-outage-delivery promise. Unknown send outcomes stay held for reconciliation rather than risking another customer answer.

Private owner help, customer relay and customer email use separate threading chains. Inbox stores Resend's actual Message-ID and sends In-Reply-To/References; it never invents provider message IDs. Missing metadata uses scoped reconciliation without another send. Mail clients still control their own grouping. Owners and customers do not share a CC help thread. If the owner already addressed the customer, Inbox records the canonical public reply but does not send a second customer email.

Return checks current settings, site eligibility, unanswered work and site credits before support can resume. It cancels stale automatic work rather than replaying an old answer.

Set enabled:false in settings to use manual handling for new conversations. The service binds that relay to the configured verified owner. Existing agent threads switch to manual handling on the next customer message or public owner reply. Re-enable the agent before returning a thread to it. Private guidance alone always stays private and never transfers control.

Set guidanceToMe:true in a versioned settings update to bind the signed current owner's verified address. Arbitrary recipient addresses and user IDs remain forbidden. The service saves only the resolved owner identity, not this setup flag.

Verify a direct email sender

When automation is active, fresh mail to the business address receives one generic confirmation email before support inference or People access. In human forwarding, the owner can respond to the supplied message without granting the sender history or account access. With passing receiving-service DMARC checks, a normal reply to its secret Reply-To confirms that mailbox. The customer can use Confirm email instead. No special reply words or AI decision are needed. Confirmation itself uses no AI credits; the email send and saved files use existing site allowances.

DMARC alone proves a sending domain, not a particular mailbox. Inbox requires the delivered secret destination, matching sender and site, active route, and live proof. It ignores sender-written authentication headers. Missing or gray checks offer the button only. A hard failure is held without an automatic reply. Temporary checks retry through the existing transport outbox and fall back to link confirmation after the third attempt.

The challenge lasts one hour. A successful email-only proof lasts 30 days. Repeated replies do not extend it. A used challenge address continues while its route and proof remain valid. Unused expired challenges and expired/revoked proofs grant no access. Send a new message to the business or ask its owner for a fresh link. An expired-route reply remains held with its original captured email; it does not silently become verified input. Owners can inspect held receipts and request a fresh confirmation rather than weakening identity checks.

Email confirmation never signs an existing browser into a customer account. A visitor who typed the same email still needs their own browser/app proof before history, orders, People notes or customer updates are available. Existing chat confirmation and recovery keep their browser-specific behavior. Once proven, email support joins the customer's continuous site thread and resumes the original question plus any new reply. Empty human replies can prove ownership without adding a blank customer message. Private guidance, manual handling, site eligibility and knowledge locks keep their existing rules.

Read conversations/<id>/routing for identity.verified and the email-only verification status. Status is unrequested, pending, verified, expired, revoked, held, retrying or failed; reasons and timestamps help separate proof from delivery. CLI 0.2.5 or later supports the new resend resource. A current human admin or owner-toolbar manager can send a fresh generic link to the already bound customer address:

printf '{}\n' > confirmation.json
frontpage customer-inbox post conversations/CONVERSATION_UUID/verification \
  --file confirmation.json --request-key confirm_customer_20261016 --site my-site --json

The JSON CLI result contains {"ok":true,"command":"customer-inbox","data":{"verification":"pending"}}. Reuse the same key after a lost response. Inbox keeps one live pending challenge and enforces a one-minute resend cooldown. A reminder of a still-valid challenge keeps its original link and deadline; it does not create another grant or extend expiry. A pacing response uses HTTP 429 with verification_cooldown; retry after one minute. The request accepts no recipient or identity fields. A resend never grants authority by itself. The workspace provides the same Send fresh confirmation action. Owner-toolbar operation: conversation_verify_email with {"id":"CONVERSATION_UUID"}.

Security review and recovery

Resource Method JSON body
blocks GET Site-scoped block evidence references; human editor/admin only
blocks/<id>/unblock POST {"version":1}
jobs GET Job state/reason and current updatedAt
jobs/<id>/resume POST {"updatedAt":"<current ISO time>"} for recoverable budget, receipt, rate or service pauses
receipts GET Held inbound mail; admin only
receipts/<id> POST {} queues a held capture retry; admin only

Only a validated clear attack can create an automatic site-scoped block. DMARC-passing customer mail provides a site-scoped sender identity for abuse blocking only; it does not grant customer-data access. Unauthenticated mail stays thread-scoped. Blocks never ban a People contact across sites or a shared IP. Unblock preserves evidence and does not replay the attack. Uncertain, malformed, refused or unavailable screening pauses the agent and leaves human handling available. Known blocks and rate/size checks stop work before another paid call.

The fixed policy requires allow probability at least 0.90 and confidence at least 0.80, with no attack category. Automatic blocking requires malicious probability and confidence at least 0.98 and an attack category. Other results pause for human review; a review does not label the customer malicious. These are platform controls, not customer settings. Review email notices deduplicate per conversation and reason group per hour. Saved receipt retries wait at least 30 seconds so one failed receipt cannot monopolize recovery.

Receipt recovery resubmits saved provider usage to the existing ledger. It never repeats inference. An interrupted request with unknown completion requires review. Resume cannot bypass screening, replay a blocked attack, or pretend unknown usage is zero. Exact input/output/cache/reasoning counts stay unchanged; the ledger records credit usage, job/call/conversation references and saved prices.

Website-agent management boundary

The owner toolbar agent uses manage_customer_inbox and get_customer_inbox_docs, with a signed manager principal bound to the authenticated owner run. It can read all site-wide Inbox conversations, files and private guidance; configure settings, tools, aliases and sources; create/update/restore unlocked knowledge; take over/return conversations; respond through the bound customer relay; add private guidance/notes; and inspect/retry recoverable jobs and receipts. Public replies still require current human handling and handler version, so the agent must take over explicitly before replying. Customer recipients stay server-bound. Existing People and other owner tools retain their site-wide access. Knowledge locks remain a human UI/CLI action: toolbar and support agents may read a locked note but cannot edit, restore over or unlock it.

The support agent has none of that owner authority. The server supplies its site, conversation, job, verified customer, recipient and permissions. There are no model-selectable site/person/customer/recipient IDs, bulk sends, arbitrary queries or owner-management tools. Customer text cannot grant permissions. Never give either agent a human API key or keychain credential.

The developer guide lives at https://frontpage.host/developers/customer-inbox. Changes to that site use the Frontpage CLI pull/push/deploy/publish process.

Public website knowledge

The new website resource and source-ID toggle commands require frontpage-host 0.2.6 or later.

Website searching is enabled by default. Knowledge shows Search your public website, its resolved address and availability. Turn it off to remove both support website tools. Explicit false survives unrelated settings saves. This permission is separate from internal notes and approved webpage snapshots.

frontpage customer-inbox get website --site my-site --json

Example response data (inside the normal CLI response envelope):

{
  "enabled": true,
  "origin": "https://example.com",
  "revision": "PUBLISHED_REVISION",
  "status": "available",
  "retrieval": "on-demand",
  "managedOrigins": [
    {"origin":"https://my-site.frontpage.host","label":"Frontpage address","public":false},
    {"origin":"https://example.com","label":"Connected domain","public":true}
  ]
}

Read the current settings version, then save this JSON as website-settings.json to disable public website searching:

{"expectedVersion":4,"value":{"websiteKnowledge":{"enabled":false}}}
frontpage customer-inbox put settings --site my-site --file website-settings.json --request-key website_search_off_001

The support agent can call website_search({query,cursor?}) and website_read({path,cursor?}). Both are bound to the active job's own public business website. They accept no other site ID, hostname, source path or credential. Searches match focused keywords and fetch at most four public pages per call. Results include matching URLs/excerpts, scanned, discovered, unavailable, partial and nextCursor.

Example search result for {"query":"repairs"}:

{
  "query":"repairs",
  "matches":[{"path":"/help/repairs","url":"https://example.com/help/repairs","title":"Repairs","excerpt":"We offer repairs for eligible products."}],
  "scanned":4,
  "discovered":12,
  "unavailable":[],
  "partial":true,
  "nextCursor":"next_1",
  "retrievedAt":"2026-10-17T12:00:00.000Z"
}

Follow the returned cursor with the same query. Remaining pages, failed reads or incomplete route discovery mean coverage is partial, never proof that a policy does not exist. Page reads return url, up to 8,000 text characters, retrievedAt, truncated and nextCursor; continue before relying on an incomplete policy. Cursors are signed and bound to the site, publication, query or page content.

Reads use the existing DNS-pinned safe fetcher without cookies or bypass credentials. They remove scripts/styles and deny source files, private app paths, query tokens and foreign redirects. Signed-in or JavaScript-only pages may not be readable; use an approved excerpt or owner guidance when needed. A website outage does not stop manual/email support.

This is on-demand retrieval: no persistent page-text cache, embeddings, background indexing or crawler service. Retrieved text is data, never instructions. Reading a page does not authorize a shared business-knowledge update. Learning jobs keep their separate owner-guidance/approved-source boundary and have no website browsing tools.

The owner manager now supports source_toggle, sources_list with overview:true, conversations_list with state:"attention", receipts_list with after, and website_status. CLI equivalents use the same versioned endpoints:

frontpage customer-inbox get 'sources?overview=true' --site my-site --json
frontpage customer-inbox get 'conversations?state=attention' --site my-site --json
frontpage customer-inbox get 'receipts?after=RECEIPT_UUID' --site my-site --json
frontpage customer-inbox post sources/SOURCE_UUID --site my-site --file source-toggle.json --request-key source_toggle_001

Use a real returned UUID in each placeholder. source-toggle.json contains {"enabled":false,"expectedVersion":2} using that source's current version. This toggles saved text without fetching or replacing it. A stale version returns a conflict; reread before retrying. Receipt retries still run every routing and identity check.

Owner workspace and widget

Open /workspace/sites/<siteId>/customer-inbox on a Scale or internal site. The workspace uses the same versioned service operations as the CLI. Conversation rows show the latest public message. Customer messages, agent replies, owner replies, private guidance, notes and handling changes have distinct labels. Private guidance stays in the thread and does not transfer ownership. Needs attention is a Conversations filter for human-handled or paused support threads. Admins also see held incoming emails there. These emails remain outside customer history until routing and identity checks succeed; a retry queues the same checks, not a successful delivery or verification. The held list is bounded to 100 entries; pass its nextCursor as receipts?after=<id> to read more. A sender address or subject displayed there does not prove identity.

Chat & settings groups the support address, website access, required visitor details and appearance. Enable website chat shows chat automatically on your live Frontpage website, without an edit, rebuild or publish. Frontpage resolves its own address and verified connected domains automatically. Add other websites one address at a time; those need the external installation code. New configurations require both name and email; saved opt-outs remain unchanged. Collection does not verify ownership. The appearance preview uses the installed widget renderer without sending messages or making model calls. Site allowances remain on the normal Usage page.

Signing keys stay hidden in the workspace until an admin opens the developer options and chooses Reveal signing key. Ordinary setup reads use widget?includeIdentity=false, which returns scriptUrl, apiOrigin and siteId without a key. The existing human-admin get widget CLI response still includes the key; treat that output as a secret.

Use frontpage customer-inbox get widget --site my-site as a human admin to get the script URL, site ID and server-side app identity key. For a website or app hosted outside Frontpage, install the script in its template:

<script defer src="https://app.frontpage.host/customer-inbox/widget.js" data-site="SITE_UUID"></script>

The gateway adds the public loader through its existing HTML transform on Scale/internal public websites. It does not call Inbox while serving HTML. The browser checks availability before loading the interface; disabled chat or an Inbox outage does not stop website delivery. Existing explicit app installations keep their identity options without a duplicate launcher. Disabling chat rejects new operations and hides connected chats through the existing event stream. Idle/suspended tabs recheck when used, returned to or reloaded. Conversations and email support remain intact.

Update settings using the current expectedVersion:

{
  "expectedVersion": 4,
  "value": {
    "widget": {
      "enabled": true,
      "title": "Message our team",
      "greeting": "How can we help?",
      "color": "#7254d6",
      "requireName": true,
      "requireEmail": true,
      "origins": ["https://my-site.frontpage.host", "https://app.example.com"]
    },
    "ownerCopies": false,
    "helpRules": {
      "humanRequest": true,
      "missingAnswer": true,
      "frustration": true,
      "repeatedFailure": true
    }
  }
}

Use exact HTTPS origins without paths or credentials. This controls cross-origin browser access, not owner permissions. Required intake fields collect details; they do not prove email ownership. The customer must confirm the emailed link before email continuation or authenticated People access. A compact verification prompt appears only while confirmation needs action. Public business questions can continue while private customer/account data stay locked. After confirmation, the widget removes the prompt and enables email follow-up automatically; there is no separate continuation switch. The branded HTML email uses the shared transactional renderer and its Confirm email button opens app.frontpage.host/customer-inbox/confirm. GET never consumes a proof; an explicit same-origin POST reaches Inbox through a signed server bridge. Confirmation rotates the visitor credential. Recovery uses a new email proof and returns no history based on a typed address alone.

The agent reads help rules in its support prompt and uses ask_owner. Work caps remain server-enforced. Owner copies are private emails of future agent replies. Replying gives private guidance unless the owner explicitly requests takeover. Neither copies nor guidance put owner and customer on a shared CC thread.

Signed app identity

Keep the identityKey returned by widget on your app server. Never include it in HTML, browser storage or a client bundle. Sign a short-lived assertion after authenticating your app user. Use the verified email from that trusted account; never sign an email or subject supplied in a browser request:

import { createHmac } from "node:crypto";
const now = Math.floor(Date.now() / 1000);
const claims = {
  siteId: process.env.INBOX_SITE_ID,
  origin: "https://app.example.com",
  subject: authenticatedUser.id,
  email: authenticatedUser.email,
  name: authenticatedUser.name,
  iat: now,
  exp: now + 120
};
const encoded = Buffer.from(JSON.stringify(claims)).toString("base64url");
const identity = encoded + "." + createHmac("sha256", process.env.INBOX_IDENTITY_KEY)
  .update(encoded).digest("hex");

The service verifies the signature, site, origin and expiry. Assertions can last at most five minutes. They grant customer identity only, never owner or manager authority. Every app-session request must include the current signed identity matching that session's subject and email. Put a server-rendered assertion in data-identity, or preferably provide data-identity-url="/api/support-identity". That same-origin HTTPS endpoint authenticates the current app user and returns {"identity":"<signed assertion>"}. The widget refreshes it; after logout or account changes, old app sessions cannot be read without the matching identity. Anonymous widgets need no assertion. Each approved origin has its own visitor session; email continues the bound conversation.

A new direct email to the public agent address can receive general business answers, but it does not prove mailbox ownership. DMARC validates the sending domain, not authority to read a customer record. The agent requests confirmation before reading or updating People, customer notes, orders or previous customer history. Fresh alias mail stays provisional until proof. Replies to a valid recipient-bound route can continue without a fresh click while the customer proof remains valid.

Unverified visitors can ask general business questions in their provisional chat. They cannot read or update an existing People contact, notes, orders or customer history. The same gate protects automatic People capture and every customer-data tool. After proof, People receives the customer and subsequent factual updates; the existing customer notes receipt remains idempotent. Public support and identity-protected support are distinct permissions.

Browser access lasts 30 days from creation or successful email verification/recovery. Verification links last one hour and work once; confirming rotates the credential. The widget retains access in that browser's storage and keeps it through temporary network failures. Returning browsers with valid access reopen their history automatically. When access is missing, ordinary Continue and email confirmation select the verified customer's continuous public-support conversation for that site. Both empty and nonempty provisional chats join only after proof; approved public origins can share the customer history through their own verified sessions. Typed email never restores history. Revoked credentials remain revoked; fresh proof does not reactivate an old token. Signed app identities remain separate by site, origin and subject. Open confirmation links in the same browser and on the same device where you want to continue chatting. If confirmation elsewhere replaces the loaded chat's access, the widget offers Send a new link to its original email through the existing recovery endpoint. This recovery action has no email-change field and grants no history before fresh proof. An empty unverified saved session with an email requests confirmation automatically; a receipt prevents duplicate requests during the link's one-hour lifetime, then rotates so an expired link cannot block a fresh confirmation.

The same conversation's customer-visible email and chat messages load together, including owner replies relayed through Inbox. They stay stored after browser access expires. Private guidance, internal notes and system notices never enter that public history. Recovery restores access to the saved conversation rather than starting its history over.

Conversation state, exports and shared usage

Use frontpage-host 0.2.2 or later to export every linked history segment through message-ID cursors. Update with npm install -g frontpage-host@latest.

Resource Method Body or behavior
conversations?state=active GET Open threads; also closed, trash or all
conversations/<id>/messages?latest=true&limit=50&audience=all GET Latest page in message order
conversations/<id>/messages?before=<sequence>&limit=50&audience=all GET Earlier page; do not combine with after or latest
conversations/<id>/state POST {"action":"close","expectedVersion":0}; actions: close, reopen
conversations/<id>/revoke-sessions POST {}; revoke existing visitor credentials

Close retains history and file references. Reopen does not erase prior handling decisions. State writes reject stale versions. Closing cancels queued sends and refuses unresolved provider outcomes. New customer input reopens a closed thread. Legacy trashed threads appear under Closed and can be reopened there. Visitor revocation also disables recovery of that session.

Export the full authorized history and file manifest, one JSON record per line:

frontpage customer-inbox export CONVERSATION_UUID --out ./conversation.jsonl --site my-site
frontpage customer-inbox download FILE_UUID --out ./attachment.png --site my-site
frontpage usage --site my-site --json

Export uses paginated reads rather than loading all messages into memory. Download uses an authorized file grant. Both commands refuse to overwrite an existing output file. Full private-history export and file access require editor/admin owner authority. The toolbar uses its signed manager tool rather than a human CLI credential. Viewers can read public history through the normal message resource.

Inbox sends count in the site's existing email allowance. Provider-accepted send receipts settle that usage once, including private owner notifications and copies. AI calls use the site's existing shared AI credits, with token counts unchanged. Storage uses the existing Blob scanner and counts actual attachments, originals and previews; conversation text adds no separate meter.

File custody and cleanup

Email capture keeps the original MIME message, attachments and provider receipt. Email and chat image uploads keep the original file and a smaller agent preview. Repeated upload/message receipts retain the same file/message IDs. Validated raster customer images up to 12 megapixels use the existing public bucket. Larger or undecodable files remain downloadable and require human review. Other files and private mail originals use authenticated downloads over encrypted bytes in that same bucket. No Blob firewall changes or separate bucket are part of this release.

The existing backup process enumerates the whole bucket. The existing reaper scans Inbox files in bounded pages and asks Inbox for exact retained references; it does not read message bodies. Unknown reference results protect Inbox files while legacy cleanup continues. Live and closed history, legacy Trash, capture holds and original/preview links retain their files. The existing archive/restore/purge coordinator claims a file in Inbox before changing bytes. A claim blocks new links and remains held after an unknown outcome. Accepted email custody, message links, closed/legacy trashed conversations and capture holds protect files; abandoned chat uploads can follow the existing grace/archive rules. Unknown file records stay protected. Site finalization freezes Inbox writes before moving its bytes through the same archive process. Broader shared-reaper performance work remains a later phase.

On app logout or account change, call window.dispatchEvent(new Event("frontpage-inbox:reset")). This clears the displayed history, stored chat credential and pending input. The next request fetches a fresh assertion from data-identity-url; authenticated app sessions require the same current app identity on every request. Do not use a long-lived static assertion for an app with account switching. Verification returns to the same origin and the stored page path. Query strings and arbitrary redirect URLs are not accepted.

jobs/<id> reads one human-visible recovery state. Retry only the documented recoverable pauses using its current updatedAt; invalid screening and unknown sends require human review. An unknown shared-media operation keeps its claimId, claimAction and lifecycleVersion. Operators verify live/archive byte hashes and the existing archive row before acknowledging the matching service-only /media-finish operation. Never clear a claim solely because time passed. These endpoints accept only the inbox-media service actor; owner keys and website managers cannot submit them.

Support agent tool contract

The support agent has its own system prompt plus the versioned private business instructions. Tool calls stay in Inbox's tenant, conversation, handler, configuration and job context. The standard tool grants start enabled; wait and finish cannot be removed. A disabled tool fails at the server.

Tool Inputs Effect and limits
knowledge_read query, or id/sourceId with optional cursor Search internal notes or read bounded approved evidence with continuation cursors.
history_read before, optional query Retrieve up to ten older eligible messages in the assigned conversation under the knowledge_read grant; bounded to 6000 text characters.
knowledge_update Optional id; title, body, expectedVersion, reason, evidenceKind, evidenceId; source evidenceVersion Available only in a restricted learning job. Update unlocked business facts from that job's assigned owner guidance or approved source revision. One-off guidance stays in the conversation. No public editing.
customer_read sections (profile, notes, orders, activity), optional before Read only the request-bound verified customer. Notes page 25 at a time; orders page 20. A cursor must belong to that same customer and site. Order ownership ambiguity for an app identity returns account_mapping_required, with no orders.
verify_customer None Request proof at the server-bound mailbox and pause. Resume unanswered work after confirmation and People projection. No recipient selector.
customer_update name/phone, evidenceMessageId Enrich from an explicit customer statement; cannot change identity or permission.
notes_read Optional before Read only this verified customer's factual People notes, 25 per page.
note_add text Save a private factual action note in People, with an idempotent receipt. Do not store hidden reasoning.
ask_owner question Save the private question in the thread, email the administrator-authorized guidance mailbox and pause. Guidance does not take over.
reply_customer text Save a public answer and queue email to the bound verified customer when available. Never choose recipients.
wait reason Pause safely without sending.
finish None Complete this bounded job.

Read tools can run in one batch. Inbox defers reply_customer, verify_customer, ask_owner, wait and finish when the model requests them alongside other tools, so it observes the results before taking a terminal action. This does not change business permissions or allow extra work beyond the existing budget and step limits.

Decisions screens malicious customer input and interprets authenticated owner-email intent. It never chooses IDs, recipients, permissions, business answers, support tools or knowledge changes. Owner intent uses bounded labeled context, one primary Luna call, at most one bounded same-Luna control fallback, and the same usage receipt tracking as support. Shared inference utilities provide the existing same-model credential/transport fallback; no expensive model substitution or customer model picker is added.

The installed widget loads two public static scripts: /customer-inbox/widget.js and /customer-inbox/view.js on https://app.frontpage.host. The shared renderer contains no session, owner data or credentials. If your app sets a Content Security Policy, allow those script URLs and connections to https://frontpage-inbox-api.fly.dev; keep the existing widget origin and identity checks. Owner Inbox pages remain authenticated.

Live updates and email continuation

The workspace uses a short authenticated stream through its cookie bridge; the installed widget uses authenticated fetch with its existing visitor/app-identity headers. Streams carry empty invalidation hints, never message text, private counters, recipients or file metadata. Clients re-read their authorized history/status. One restricted Postgres listener per Inbox process wakes the existing outbox publisher and fans out hints. Notifications are best-effort; durable rows, leases, recovery scans and receipt reconciliation remain authoritative. Disconnected clients refresh and use bounded fallback polling. No token enters a stream URL, and each short stream reauthorizes on reconnect.

A new unverified chat with an email requests a generic verification email automatically. It does not send private history or assume that typing an address proves identity. The owner can still answer in chat while the visitor verifies. Confirmation rotates the session and binds future public replies to that email; if the latest customer input already has an unsent public answer, Inbox queues that one answer once. It does not mail a backlog or an answer that predates a newer customer question. Browser replies enter the existing mail outbox with a 30-second grace period. The open, visible widget acknowledges public replies in its transcript viewport using its current verified session. This cancels only that customer’s queued, unattempted follow-up email; the reply stays in history and still counts as answered. Hidden/minimized chat, lost connections and failed acknowledgments leave email queued. The existing publisher sends it after the grace period, normally on its next sweep (up to about 15 seconds later, plus provider time). Direct inbound-email replies keep immediate delivery. No extra continuation switch or presence service is required. A late read cannot cancel an already attempted or uncertain send; retries keep the same provider identity and accounting receipt.

The workspace shows customer name/email first, latest public message in each row, and a copyable reference in conversation details. Conversation list and history scroll independently inside a bounded viewport. Settings use shared workspace controls and a presentation-only preview built from the same renderer as the installed widget. Preview changes send no email or model request. Internal note history/locks/revert stay on the note's own page.

Shared team guidance

Read settings and use the returned version. This example assumes version: 3:

{"expectedVersion":3,"value":{"guidanceEmail":"support@example.com"}}
frontpage customer-inbox put settings --file ./team-guidance.json --request-key team_guidance_01
frontpage customer-inbox get settings

The settings response has version: 4, value.guidanceOwnerEmail: "support@example.com", and the authorizing administrator in value.guidanceOwnerId. Other settings remain unchanged. Repeating the exact write with the same key does not apply it twice. A stale version with a new key returns settings_changed. Editors cannot change the mailbox. This is the private team destination, distinct from the public address customers email.

When the agent asks for help, anyone with shared-mailbox access can reply as support@example.com. Guidance remains private and keeps the agent handling the customer. An explicit takeover request changes handling; the customer relay stays within Inbox. Replies from an unrelated address or a revoked private route cannot act as team guidance. The audit records the actual sending mailbox; a shared sender does not identify the individual teammate.

Simple conversation management

Use Open and Closed. Close preserves history; a later customer message reopens the same conversation. Legacy trashed conversations appear under Closed and can be reopened. Mark read and Trash are no longer supported management actions.

{"action":"close","expectedVersion":2}
frontpage customer-inbox post conversations/CONVERSATION_ID/state --file ./close.json --request-key close_conversation_01

Read the conversation’s current stateVersion first. The response increments it and sets closedAt; reopening clears closedAt and any legacy trash marker. Closing does not transfer handling or delete messages. Take over before sending a human customer reply; private guidance does not take over.

Setup and knowledge behavior

Claim the public support address before relying on account confirmation, owner guidance or email replies. General browser chat can work without it. Missing setup returns address_required; the widget explains that account confirmation is unavailable and keeps customer data locked. Finish setup, then retry the saved request from Inbox.

The support agent receives a dedicated system prompt, private business instructions, bounded conversation context and permitted tool schemas. It reads saved business notes with knowledge_read, live public pages with website_search / website_read, and saved approved webpage snapshots with knowledge_read {sourceId}. Search excerpts require a complete read before relying on them. Website search does not create internal notes. Only separate server-authorized learning from team guidance or approved source revisions may update unlocked shared knowledge. Customer assertions never authorize those writes.

Compact chat and workspace controls

  • The chat header says “We’ll reply here, or email you if you’re away.” Confirmed email needs no permanent success banner. The file and send buttons use icons with accessible labels; Enter sends and Shift+Enter adds a line. The message input grows within a fixed bound. Selected files can be removed before sending.
  • Before a support address exists, Chat & settings shows Set up your support email and Create support address. After assignment, it shows the active address, its site, Copy address, and Change address. The replacement form opens only after Change. Saving a replacement stops new mail to the old alias; it does not delete conversation history. Reserved aliases stay protected.
  • While the agent handles a conversation, the composer offers Guide your agent and Private note. After takeover it offers Reply to customer and Private note. Guidance, public reply and note drafts have separate keys; changing control never makes a private draft public. A hand marks Take over; the return arrow marks Return to agent. Only the current owner can send a manual public reply. Existing API/CLI guidance and control operations remain available.

Widget read acknowledgment

The installed widget uses this automatically; owners do not need to call it. The endpoint accepts only the current origin-bound verified browser credential (and a current app identity assertion when the session belongs to an app).

POST /chat/sites/<site-id>/seen
Origin: https://your-approved-website.example
Authorization: Bearer <verified-visitor-session>
Idempotency-Key: seen_<unique-request-key>
Content-Type: application/json

{"messageIds":["11111111-1111-4111-8111-111111111111"]}
{"acknowledged":true}

Use one to fifty unique public message IDs from this customer’s conversation. The session, site, origin, current email, app binding and public audience must match. A typed but unverified email returns verification_required; a foreign conversation or private message returns message_unavailable. Mixed valid and invalid IDs change nothing. Retries are harmless. This endpoint neither exposes message content nor changes ownership, knowledge, model work or charges. Canceled follow-ups show Seen in chat · email not needed in the workspace. If the request fails, normal email delivery remains the fallback.

Chat links, page context and usage receipts

Embedded chat and the owner Inbox render named Markdown links and ordinary HTTP/HTTPS URLs safely. Stored messages stay plain text. Arbitrary HTML and unsafe URL schemes do not execute. Support can reply with [descriptive text](https://example.com/help). Email delivery formatting is unchanged.

Support website tools return a short nextCursor reference, such as next_1. Pass it with the same query or page path. The runtime retains the exact signed server cursor privately in the saved job, including across worker restarts. References cannot select another page, query or job. Direct internal website API clients retain signed cursors.

New browser widgets send optional pagePath when the API advertises pageContext:true. The validated same-site path belongs to the individual message, excludes query strings and fragments, and enters bounded agent history and security screening. It is an untrusted context hint, never identity, permission or live browsing history. Older clients may omit it. Email-confirmation return navigation remains separate.

Decisions cache-read and cache-write counts are included in total input. New receipts use the published cache rates and a new provider-price snapshot; historical snapshots keep their rules. Customer credit multipliers, budgets and model selection do not change. This release adds no recovery flow and does not repair old requests.