Skip to content

Product launch messaging agent

This page explains how a product launch messaging agent reads the release pages it is authorized to see in your internal docs system — under an interactive, controlled grant — combines them with public competitor research, and drafts launch messaging that stays consistent across channels. After reading it, you will understand which modules this scenario needs, how unreleased information is constrained by the authorization boundary rather than the prompt, and why positioning and claims rules belong in a policy store rather than Memory.

Use case

Product marketing teams need positioning, launch announcements, FAQs, sales snippets, and internal enablement materials for a new product or feature. The release materials — release notes, product docs, internal discussion — live in an internal docs library, and only some pages are relevant to this launch. A launch spans the website, blog, email, social channels, and sales talk tracks; independently written materials drift apart, and information keeps changing as launch day approaches.

Typical triggers:

  • The launch date is set, and a full cross-channel messaging set is due before the launch window.
  • A competitor ships a similar capability around the same time, and the positioning language must be adjusted.
  • Product scope or pricing changes shortly before launch, and every asset must be updated in sync.

Engineering challenges

  • Reading unreleased material needs a real boundary: the common shortcut is pasting internal docs into the prompt in plaintext — which hands confidential content to the task description with no control over who read it, how much, or where it went. The right approach is to let the Agent read the pages it is authorized to see inside the docs library, with every read recorded.
  • Cross-channel consistency does not survive manual checks: positioning and claim wording drift between the website, email, and sales talk tracks, and every pre-launch change must be propagated to all assets by hand — one miss is a messaging incident.
  • Internal and public research must stay separated: competitor research goes through the public web, internal reads go through authorized pages; if the two channels mix, unreleased details can leak into public queries.

Module composition

ModuleRoleNotes
GenAuthCoreRead-only delegation, revocation, and the audit chain for the authorized pages in the internal docs library; release projects outside the delegated list are unreadable.
Web AgentCoreControlled sessions read the authorized internal release pages and research competitor launches and public industry context with WebSearch, keeping the two channels strictly separate.
GUMemNot usedProduct positioning and claims rules are versioned policy — keep them in your policy store and inject them per version; launch drafts and language decisions are business state and belong in your release archive. Neither is Memory.

Product launch messaging agent architecture

Permission and delegation boundaries

The Agent holds no inherent permissions. The effective authority for each launch task is the intersection of three sets: what the user actually holds ∩ what was explicitly delegated for this task ∩ what the enterprise has approved. Applied here:

  • The delegated scope covers only "read the pages of the current release project that are authorized in the internal docs library, and query public market pages" — no reading other product lines' materials, editing product docs, or publishing externally.
  • Delegation credentials are short-lived; minute-level validity is recommended for a single launch task, with re-delegation after expiry.
  • The user or an administrator can revoke the grant at any time; new material-read requests fail immediately after revocation.
  • Out-of-scope attempts (for example, reading another release project's confidential materials) are rejected and recorded — the audit chain covers all attempts, not just successful actions.

Note: the SDK example requests product-level scopes (such as webagent.do_anything:read). Fine-grained boundaries — domain lists, page ranges, action whitelists — are enforced by the GenAuth Agent Profile or your policy layer, not by the task prompt; that configuration is not shown on this page. See Delegate token and attenuation for the full semantics.

Workflow

Product launch messaging agent workflow

  1. The user selects the release project and defines the launch scope, target channels, and the list of authorized release pages.

  2. GenAuth starts an interactive delegation; after the user confirms in Qoni Console, your server-side callback exchanges it for a least-privilege credential.

  3. Your app loads the current version of positioning language and claims rules from the policy store and injects them into the task.

  4. Web Agent opens the authorized release pages in a controlled session and reads the release notes, docs, and discussion highlights.

    Checkpoint: Unreleased information is read and used only within the authorization boundary; subsequent public web queries must not contain any unreleased detail.

  5. Web Agent researches competitor launches and public industry context through WebSearch, keeping a source URL for every external fact.

  6. The Agent generates cross-channel messaging drafts — positioning language, launch announcement, FAQs, sales snippets, and enablement materials — and checks consistency channel by channel.

  7. The Agent returns the full draft set, a source list, and flagged discrepancies with an audit id attached; after app-side validation they go to product and legal for confirmation.

    Checkpoint: Product claims across channel drafts should agree with each other and follow the claims policy version; statements that conflict with established positioning should be flagged for confirmation, never adopted silently.

Example code

The example below wires this scenario into your backend with the official Qoni SDK (@qoniai/qoni): interactive delegation (with a full callback) → load positioning and claims rules from your policy store → one doAnything.run() that reads the authorized pages, runs the public research, and drafts the messaging → parse and validate the drafts. Note that the prompt points to the authorized internal pages; it never pastes internal material into the task description.

ts
import { Qoni, QoniScopes } from '@qoniai/qoni'

const qoni = new Qoni({
  accessKey: process.env.QONI_ACCESS_KEY!,
  secretKey: process.env.QONI_SECRET_KEY!,
})

export async function startLaunchTask(userId: string, taskId: string) {
  // 1. Interactive delegation: internal docs sign-in and confidential reads
  //    are involved, so the user confirms in Qoni Console
  const { data: authorization } = await qoni.delegateToken({
    mode: 'interactive',
    agent: 'product-launch-messaging',
    scopes: [QoniScopes.DO_ANYTHING_READ, QoniScopes.DO_ANYTHING_MANAGE],
    redirectUri: 'https://app.example.com/qoni/callback',
    state: taskId,
    user: { id: userId },
    expiresIn: 900, // minute-level validity for a single launch task
  })
  redirectUserTo(authorization.authorizationUrl)
}

// GET /qoni/callback — after the user approves, exchange for the grant
// server-side and continue the task
export async function handleQoniCallback(request: Request) {
  const query = new URL(request.url).searchParams
  const { data: grant } = await qoni.completeDelegateToken({
    grantId: query.get('grantId')!,
    code: query.get('code')!,
    state: query.get('state')!,
  })
  const task = await loadLaunchTask(query.get('state')!) // your task store
  return runLaunchTask(grant, task)
}

async function runLaunchTask(
  grant: { token: string; auditId: string; grantedScopes: string[] },
  task: { releaseId: string; releasePages: string[]; channels: string[] },
) {
  // 2. Before the run: load the current positioning and claims rules
  //    from YOUR policy store (this is versioned policy, not Memory)
  const policy = await loadLaunchPolicy() // e.g. { version: '2026-08', positioning: ..., claimsRules: [...] }

  // 3. One call reads and drafts: the prompt points to authorized internal
  //    pages — unreleased content is never pasted into the task description
  const run = await qoni.doAnything.run({
    token: grant.token,
    prompt: `
      Open and read these internal release pages for release
      "${task.releaseId}" (they are within your grant):
      ${task.releasePages.join(', ')}.
      Research competitor launches on the public web, keeping a source
      URL for every external fact. Draft cross-channel messaging for
      ${task.channels.join(', ')}: positioning, announcement, FAQs,
      sales snippets and enablement. Return drafts as a JSON array of
      { channel, draft, claimIds, sourceUrls } objects, where sourceUrls
      lists the public source URL for every external fact and the granted
      internal release page URL for internal references, and flag any
      cross-channel claim conflicts. Never include unreleased details
      in any public query or page visit. Do not edit or publish anything.

      Launch policy (version ${policy.version}):
      ${JSON.stringify(policy)}
    `,
    capture: { screenshots: true },
  })

  const result = await run.wait({
    // Docs library login walls / confirmations: forward to the user
    onInteraction: (interaction) => notifyUserActionRequired(interaction),
  })

  // 4. Parse and validate the output contract on the app side: drafts without
  //    a channel, claim annotations or a source list never reach the deliverable.
  //    External facts must carry a public source URL; internal references cite
  //    the granted release page URL
  const drafts = parseLaunchDrafts(result.output).filter(
    (d) => d.channel && d.claimIds?.length && d.sourceUrls?.length > 0,
  )

  return {
    drafts,
    artifacts: result.artifacts, // step screenshots, archived with the release
    policyVersion: policy.version,
    audit: { auditId: grant.auditId, permissionBoundary: grant.grantedScopes },
  }
}

The output structure is a contract set by the task prompt: here it is an array of { channel, draft, claimIds, sourceUrls }, parsed and validated by parseLaunchDrafts on the app side, and any entry missing a channel, claim annotations, or a source list (sourceUrls) is dropped — external facts must carry a public source URL, and internal references cite the granted release page URL as their source. The SDK itself returns the generic RunResult (runId, status, output, artifacts, and so on).

Data and memory boundaries

This scenario touches four kinds of data; GUMem is not used here:

  • Versioned rules: positioning language and claims rules — managed by version in your policy store, referenced by version number in every draft set.
  • Business state: release materials, cross-channel drafts, language decisions — kept in your internal docs library and release archive; unreleased information never leaves the authorization boundary.
  • Audit records: the delegation and behavior chain formed by grantId and auditId, including every internal page read — maintained by GenAuth.
  • User Memory (optional): only long-term personal preferences a user has explicitly confirmed belong in GUMem; launch language is team-level policy, not personal memory, so this scenario neither recalls nor writes back by default.

Failure handling

SituationRecommended handling
Access to an authorized release page is deniedTreat it as a boundary rejection with a record, prompt the user to confirm the delegated scope, and never fall back to guessing.
No reliable source for competitor or industry informationMark it as unconfirmed; never write unsourced conclusions into external materials.
Material request outside the delegated scopeReject and record it; the attempted access remains visible in the audit chain.
A draft lacks a channel, claim annotations, or a source listApp-side validation drops the draft and records how many were dropped and the policy version applied.

Production notes

Unreleased information must not enter public web tasks — and must not be pasted into task descriptions in plaintext either; controlled reads of authorized pages are the point of this scenario. External messaging needs product, legal, or owner confirmation before release. Information changes frequently before launch day, so deliverables should carry a generation timestamp, the release pages they are based on, and the policy version, preventing stale drafts from being mistaken for the final language.

Next steps