Skip to the content
Menu: Sales training and results

Train your team and read their results

By the end of this guide your sales team practises with an avatar customer on a portal of your own, filled with your own product and prices, and every released result flows back to your HR or learning system.

You need: a key made with the Learning platform preset, and the Node SDK.

1. Build the portal from a template#

sales-training.ts
const templates = await zentiq.listPortalTemplates();
if (!templates.some((t) => t.key === "sales-trainer" && t.available)) throw new Error("sales-trainer isn't on this plan");

const portal = await zentiq.createPortal({
  name: "Acme Sales Academy",
  templates: ["sales-trainer"],
  loginMethods: ["email"],
}); // a draft: nothing is live yet
const subjectId = portal.subjects[0].id; // sub_…

The portal is a draft, and its subject (sub_…) starts from the template's example content, which can never go live. See Portals and subjects.

2. See what is missing#

sales-training.ts
const check = await zentiq.getPortalGoLive(portal.id);
for (const p of check.problems) console.log(`still to do: ${p.message}`); // the template's own facts to replace

3. Fill in your own content#

Content is saved as a new draft version. Send the version you read as baseVersion: if someone saved a newer one meanwhile, your save is refused (409 version_conflict) instead of overwriting theirs.

sales-training.ts
const content = await zentiq.getSubjectContent(subjectId);
try {
  const draft = await zentiq.updateSubjectContent(subjectId, {
    baseVersion: content.version, // refuse to overwrite someone else's newer draft
    set: {
      facts: { company: "Acme", product: "Acme CRM", price: "49 dollars a seat a month", customer: "operations managers at mid-sized firms" },
      scenarios: [{ title: "The price objection", counterpart: "A careful operations manager", goal: "Agree a trial" }],
    },
  });
  console.log(`draft v${draft.version}, ready to publish: ${draft.goLive.ready}`);
} catch (e) {
  if (e instanceof VersionConflictError) console.log(`someone saved v${e.currentVersion} first: read it again`);
  else throw e;
}

4. Publish#

sales-training.ts
await zentiq.publishSubject(subjectId); // the draft becomes the live version
await zentiq.publishPortal(portal.id); // the portal opens at its address

Each publish goes through the go-live check. If it refuses, the answer is 422 go_live_refused with details.problems, and nothing changes.

5. Add your people#

A team, and the people in it, by your own ids: sending the same externalId again gives back the same person, so your HR system can run this every night.

sales-training.ts
const team = await zentiq.createUnit({ name: "Sales EMEA", kind: "team" });
for (const [externalId, name] of [["E1001", "Ana Silva"], ["E1002", "Ben Carter"]]) {
  // externalId is your own id for the person: sending the same one again gives back the same person.
  await zentiq.createPerson({ externalId, name, email: `${externalId.toLowerCase()}@acme.example`, role: "LEARNER", unitId: team.id });
}

People sign in with the methods you chose (loginMethods), or reach the portal from your own app with embed tokens.

6. Hear about results#

sales-training.ts
await zentiq.createWebhook({ url: "https://lms.acme.example/zentiq/webhooks", events: ["report.produced", "assessment.completed"] });

A finished role-play sends report.produced with the rubric's numbers once the result is released, never the conversation's words: the summary and feedback lines go only to a webhook made with includeConversation.

7. Read the results#

sales-training.ts
// Sync what changed since your last run: released results only, with their numbers (never the conversation's words).
const since = new Date(Date.now() - 24 * 3600 * 1000).toISOString();
for await (const result of zentiq.paginate("listResults", { portalId: portal.id, updatedAfter: since })) {
  console.log(result.id, result.percent, result.passed);
}

Only released results are listed, with their numbers and each category's score. Practice attempts belong to the person and are never listed. See Results.