Skip to content

Customer onboarding agent

This page explains how a customer onboarding agent observes the customer environment's configuration state under a read-only grant, combines it with customer goals and communication history to generate a personalized onboarding checklist, and hands write actions back to the customer. After reading it, you will understand why all three modules are core here, why environment state must come from the source system rather than memory, and how the takeover handback works.

Use case

Customer success teams want an Agent that generates an onboarding plan and next-step checklist from the customer's industry, purchased products, and communication history, then guides the new customer through configuration based on the actual state of their environment (authorized pages). Checking each customer's environment manually is expensive; giving a script an account that can read and write the customer environment means any slip lands on the customer's production configuration.

Typical triggers:

  • A new customer signs, and a personalized onboarding plan is needed for their industry and purchased products.
  • Configuration stalls at some step, and the environment state must be read to locate the blocker and produce the next move.
  • Before trial-to-paid conversion, all required configuration items must be verified as complete.

Engineering challenges

  • Environment state drifts constantly: the checklist depends on the customer environment's actual configuration, which the customer can change at any moment. Inferring current state from the last conversation or a previous checklist is guaranteed to go wrong — every run must observe the source system fresh.
  • The read/write responsibility boundary: verifying configuration only needs reads, but "guiding the customer to enable a feature" naturally tempts the Agent to do the write itself. Once it writes into the customer's production environment, any mistake is a vendor-side incident with no clean way to split responsibility.
  • Context lives in two kinds of systems: customer goals and communication history are cross-session customer memory; configuration reality is the environment's current fact. Confusing the two — treating memory as environment truth — produces checklists that walk the customer through steps already done or no longer valid.

Module composition

ModuleRoleNotes
GenAuthCoreInteractive read-only delegation, revocation, and the audit chain; writes are excluded from the grant.
Web AgentCoreControlled sessions read authorized environment state pages with per-item evidence; write steps issue a takeover link handed back to the customer.
GUMemCoreCustomer goals, communication highlights, and confirmed preferences — the customer context that spans sessions; environment configuration state is not part of it and is observed fresh each run.

Customer onboarding agent architecture

Permission and delegation boundaries

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

  • The delegated scope covers only "read this customer's records, communication history, and authorized environment state pages" — no changing customer environment configuration, signing commitments, or altering contract terms.
  • Delegation credentials are short-lived; minute-level validity is recommended for a single onboarding check, with re-delegation after expiry.
  • The customer success lead or an administrator can revoke the grant at any time; new environment reads fail immediately after revocation.
  • Out-of-scope attempts (for example, submitting a configuration change form) 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 — the customer environment's 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

Customer onboarding agent workflow

  1. The customer success user opens the customer workspace and triggers the onboarding task.

  2. GenAuth runs interactive delegation for the lead's explicit consent and issues a read-only credential.

  3. GUMem recalls customer goals, communication highlights, and confirmed preferences — no configuration state.

  4. Web Agent reads the authorized environment state pages and verifies completed and missing configuration items one by one.

    Checkpoint: Every environment-state judgment comes from this run's observation and maps to concrete page evidence; unreadable items are marked "unknown" — never filled in from memory or a previous report.

  5. The Agent generates the personalized onboarding checklist: completed items, missing items, recommended order, and matching doc links.

  6. For steps that require a write (changing configuration, enabling a feature), the Agent raises a takeover interaction; your app forwards the takeover link to the end customer, who performs the action in their own session.

    Checkpoint: The Agent never modifies production configuration for the customer; after the customer hands the session back, Web Agent re-observes the environment state before updating the checklist.

  7. The customer success user reviews the checklist and progress, then shares it with the customer; progress milestones and confirmed communication highlights are written back to GUMem.

Example code

The example below wires this scenario into your backend with the official Qoni SDK (@qoniai/qoni): interactive read-only delegation (full callback) → recall customer context → one doAnything.run() to observe environment state → app-side checklist validation → write back progress after the lead's review.

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

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

interface ChecklistItem {
  step: string
  status: 'done' | 'missing' | 'unknown' | 'blocked'
  evidenceUrl: string
}

// App-side validation: parse the JSON contract set by the prompt;
// items without page evidence are downgraded to blocked instead of
// being treated as valid judgments
function parseChecklist(output: string): ChecklistItem[] {
  const items = JSON.parse(output) as ChecklistItem[]
  return items
    .filter((item) => item.step && item.status)
    .map((item) =>
      item.evidenceUrl ? item : { ...item, status: 'blocked' as const },
    )
}

export async function startOnboardingCheck(csUserId: string, customerId: string) {
  // 1. Interactive delegation: customer environment sign-in is involved,
  //    so the lead confirms the grant in the Qoni Console
  const { data: authorization } = await qoni.delegateToken({
    mode: 'interactive',
    agent: 'customer-onboarding',
    scopes: [
      QoniScopes.DO_ANYTHING_READ,
      QoniScopes.DO_ANYTHING_MANAGE,
      QoniScopes.GUMEM_MEMORY_READ,
      QoniScopes.GUMEM_MEMORY_WRITE,
    ],
    redirectUri: 'https://app.example.com/qoni/callback',
    state: `onboarding-${customerId}`,
    user: { id: csUserId },
    expiresIn: 900, // minute-level validity for one onboarding check
  })
  redirectUserTo(authorization.authorizationUrl)
}

// After the lead consents, Qoni calls your server route to redeem the grant
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 customerId = parseCustomerId(query.get('state')!)
  return runOnboardingCheck(grant, customerId)
}

async function runOnboardingCheck(
  grant: { token: string; auditId: string; grantedScopes: string[] },
  customerId: string,
) {
  // 2. Before the run: recall goals and communication highlights —
  //    configuration state never comes from Memory
  const { data: customerContext } = await qoni.gumem.recall({
    token: grant.token,
    sessionId: `customer-${customerId}`,
    query: 'customer goals, communication highlights, confirmed preferences',
  })

  // 3. One call runs the onboarding check: read-only observation,
  //    every write escalates; the prompt pins down a JSON output contract
  const run = await qoni.doAnything.run({
    token: grant.token,
    prompt: `
      Check onboarding progress for customer ${customerId}.
      Read the authorized environment state pages and return ONLY a JSON
      array of checklist items, each shaped as
      { "step": string, "status": "done" | "missing" | "unknown",
        "evidenceUrl": string },
      where evidenceUrl points to concrete page evidence from THIS run;
      mark unreadable items as "unknown" — never fill gaps from memory
      or previous reports.
      Read-only: never change the customer's environment configuration.
      For any step that requires a write, raise a takeover interaction.
      Customer context from Memory: ${JSON.stringify(customerContext)}
    `,
    capture: { screenshots: true },
  })

  // 4. Event stream: progress and screenshots go to the CSM frontend,
  //    takeover requests are forwarded to the end customer; the done
  //    event's output only becomes the checklist after validation
  let checklist: ChecklistItem[] = []
  for await (const event of run.events()) {
    if (event.type === 'progress') appendTrace(customerId, event.data)
    if (event.type === 'screenshot') renderScreenshot(customerId, event.image)
    if (event.type === 'interaction') forwardTakeoverToCustomer(customerId, event.data)
    if (event.type === 'done') checklist = parseChecklist(event.data.output)
  }

  return {
    checklist,
    audit: { auditId: grant.auditId, permissionBoundary: grant.grantedScopes },
  }
}

// 5. Called after the customer success user reviews the checklist:
//    only whitelisted fields (goal progress, confirmed preferences,
//    communication highlights) are written. Configuration-state fields
//    like step/status/evidenceUrl are explicitly filtered out —
//    environment state is observed fresh each run and never enters Memory
export async function confirmOnboardingProgress(
  grantToken: string,
  customerId: string,
  confirmed: {
    goalProgress: string[]
    preferences: string[]
    communicationHighlights: string[]
  },
) {
  await qoni.gumem.addMessages({
    token: grantToken,
    sessionId: `customer-${customerId}`,
    messages: [
      {
        role: 'user',
        content: [
          ...confirmed.goalProgress.map((g) => `Goal progress: ${g}`),
          ...confirmed.preferences.map((p) => `Confirmed preference: ${p}`),
          ...confirmed.communicationHighlights.map(
            (h) => `Communication highlight: ${h}`,
          ),
        ].join('\n'),
      },
    ],
  })
}

The checklist structure is a JSON contract set by the task prompt, and parseChecklist enforces it on the app side: every item must carry step, status, and page evidence, and items without evidence are downgraded to blocked. Writing back to Memory happens in the separate confirmOnboardingProgress, called after the lead's review and accepting only whitelisted fields — configuration-state fields never pass through it. The SDK itself returns the generic RunResult (runId, status, output, artifacts, and so on). If the event stream drops, the SDK reconnects and resumes with Last-Event-ID.

Memory strategy

  • Into Memory: customer goals, communication highlights, confirmed preferences, and onboarding progress milestones, all timestamped — the customer context that spans sessions.
  • Not into Memory: the customer environment's configuration state. The customer can change it at any moment, and GUMem is not the source of configuration truth — "done/missing" judgments come only from Web Agent's observation in this run, never from memory or a previous report.
  • Corrections: when customer goals or commitments change, mark the old record invalidated and point it to the new memory instead of physically deleting it, keeping past guidance decisions traceable.

Failure handling

SituationRecommended handling
Customer environment sign-in state expiresSuspend the task, notify the customer or the lead to sign in again, and resume from the checkpoint.
An environment page is unreadable or its structure changedMark the item "unknown" and replay the session recording; never emit state without evidence.
A configuration write request outside the grantReject and record it; the attempted action remains visible in the audit chain.
A recalled commitment conflicts with this conversationThe lead's confirmation in this task wins; write the correction back to GUMem.

Production notes

Environment state must come from the source system: every "done/missing" judgment in the checklist derives only from this run's page evidence, never from GUMem or historical reports. Customer commitments, contracts, and SLAs must not be generated or modified by the Agent — they require the lead's confirmation. No write to the customer's production environment belongs in the Agent's grant: changes go through a takeover link handed back to the end customer, every takeover and handback leaves an audit record, and after handback the Agent re-observes before updating the checklist.

Next steps