Creating and publishing a flow
Guided selling is not yet available. This page describes it ahead of its release, so details can still change. The changelog announces when it is released.
A flow goes live in three steps: you create it with version 1 as a draft, you test the draft and you publish it. Every later change to the content is a new version that you test and publish the same way. The operations on this page need the Configuration role, see Access.
Creating a flow
guidedSellingFlowCreate creates the flow with version 1 as a draft:
mutation CreateFlow($input: GuidedSellingFlowCreateInput!) {
guidedSellingFlowCreate(input: $input) {
id
name
active
version
}
}
The input holds a name for the flow, the Anthropic API key its model calls are billed to and the content of version 1. The complete variables for a label printer advisor:
Variables
{
"input": {
"name": "Label printer advisor",
"modelApiKey": "YOUR_ANTHROPIC_API_KEY",
"content": {
"instruction": "# Label printer advisor\n\nYou help warehouse managers, operations leads and office staff choose a label printer from this catalog.\n\n## How to narrow down\n\n1. Where the printer is used: a packing desk or office; a warehouse or production floor; on the move.\n2. How many labels the customer prints on a busy day.\n3. The widest label they print: 2, 4 or 6 inch.\n4. How the printer connects: USB, Ethernet, Wi-Fi or Bluetooth.\n\nRule printers out on what the customer tells you. Do not ask a question whose answer would not change the advice.\n\n## Rules\n\n- On the move means battery power and a wireless connection: only mobile printers fit.\n- More than 5,000 labels a day needs an industrial printer.\n- Labels wider than 4 inch need a 6-inch printer.\n- Thermal transfer labels last longer than direct thermal ones. Mention it when labels are stored outdoors or for months.\n- When the customer already knows which model they want, ask which one and advise it.\n\n## In the advice\n\nIn each reason, name the customer's needs that the printer meets. When no printer here meets a need, say so and suggest talking to a specialist.",
"catalogue": {
"products": [
{
"productId": 18240,
"sku": "LP-D420",
"name": "LP-D420 Desktop Label Printer",
"description": "Compact printer for shipping labels at a packing desk or in an office.",
"attributes": [
{ "name": "ENVIRONMENT", "value": "Office, packing desk" },
{ "name": "LABELS_PER_DAY", "value": "Up to 500" },
{ "name": "MAX_LABEL_WIDTH", "value": "4 inch" },
{ "name": "PRINT_METHOD", "value": "Direct thermal" },
{ "name": "CONNECTIVITY", "value": "USB, Ethernet" }
]
},
{
"productId": 18243,
"sku": "LP-I440",
"name": "LP-I440 Industrial Label Printer",
"description": "Metal-cased printer for continuous labeling in a warehouse.",
"attributes": [
{ "name": "ENVIRONMENT", "value": "Warehouse, production floor" },
{ "name": "LABELS_PER_DAY", "value": "Up to 5,000" },
{ "name": "MAX_LABEL_WIDTH", "value": "4 inch" },
{ "name": "PRINT_METHOD", "value": "Thermal transfer, direct thermal" },
{ "name": "CONNECTIVITY", "value": "USB, Ethernet, Wi-Fi" }
]
},
{
"productId": 18241,
"sku": "LP-I640",
"name": "LP-I640 Industrial Label Printer",
"description": "High-volume printer for pallet and shipping labels up to 6 inch wide.",
"attributes": [
{ "name": "ENVIRONMENT", "value": "Warehouse, production floor" },
{ "name": "LABELS_PER_DAY", "value": "Up to 10,000" },
{ "name": "MAX_LABEL_WIDTH", "value": "6 inch" },
{ "name": "PRINT_METHOD", "value": "Thermal transfer, direct thermal" },
{ "name": "CONNECTIVITY", "value": "USB, Ethernet, Wi-Fi" }
]
},
{
"productId": 18242,
"sku": "LP-M220",
"name": "LP-M220 Mobile Label Printer",
"description": "Battery-powered printer worn on the belt for shelf and picking labels.",
"attributes": [
{ "name": "ENVIRONMENT", "value": "Warehouse aisles, customer sites" },
{ "name": "LABELS_PER_DAY", "value": "Up to 300" },
{ "name": "MAX_LABEL_WIDTH", "value": "2 inch" },
{ "name": "PRINT_METHOD", "value": "Direct thermal" },
{ "name": "CONNECTIVITY", "value": "Bluetooth, Wi-Fi" },
{ "name": "POWER_SOURCE", "value": "Battery" }
]
}
]
},
"settings": {
"language": "EN",
"languages": ["EN", "NL"],
"opener": {
"questions": [
{ "language": "EN", "value": "Where will the printer be used?" },
{ "language": "NL", "value": "Waar gaat u de printer gebruiken?" }
],
"options": [
{
"labels": [
{ "language": "EN", "value": "At a packing desk or in an office" },
{ "language": "NL", "value": "Aan een inpaktafel of op kantoor" }
]
},
{
"labels": [
{ "language": "EN", "value": "In a warehouse or on the production floor" },
{ "language": "NL", "value": "In een magazijn of in de productie" }
]
},
{
"labels": [
{ "language": "EN", "value": "On the move: in the aisles or at customer sites" },
{ "language": "NL", "value": "Onderweg: tussen de stellingen of bij klanten" }
]
},
{
"labels": [
{ "language": "EN", "value": "I already know which model I want" },
{ "language": "NL", "value": "Ik weet al welk model ik wil" }
]
}
]
},
"freeText": true,
"maxQuestions": 5,
"model": { "model": "claude-sonnet-5-5", "effort": "MEDIUM" }
}
}
}
}
Expected response:
{
"data": {
"guidedSellingFlowCreate": {
"id": "01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90",
"name": "Label printer advisor",
"active": true,
"version": null
}
}
}
version is the published version, so it stays null until you publish one. The id is what a storefront needs to run the flow.
Content that breaks a rule is refused with GUIDED_SELLING_CONTENT_INVALID_ERROR or GUIDED_SELLING_INPUT_INVALID_ERROR, listing every problem at once. See Writing flow content.
The model key is not checked when you create the flow. It is first used when you test the draft beyond the opening question or when you publish.
Testing a draft
Run the draft with guidedSellingStep and its version number, using a key or user with the Configuration role. Without version, the step runs the published version:
query GuidedSellingStep($input: GuidedSellingStepInput!) {
guidedSellingStep(input: $input) {
__typename
... on GuidedSellingQuestion {
questionId
question
acknowledgement
options {
optionId
label
}
freeTextAllowed
}
... on GuidedSellingAdvice {
headline
items {
productId
reason
}
}
}
}
Variables:
{
"input": {
"flowId": "01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90",
"version": 1,
"language": "EN",
"conversationId": "draft-test-1",
"answers": []
}
}
The response is the opening question of version 1. Add answers as a storefront would, see Running a guided selling conversation.
A storefront cannot reach a draft. Until a version is published, its steps fail with GUIDED_SELLING_FLOW_NOT_PUBLISHED_ERROR, and a step that names any version other than the published one fails with GUIDED_SELLING_FLOW_VERSION_NOT_ALLOWED_ERROR.
Play the draft through the way different customers would: pick options, type answers, answer "not sure" and ask for something the catalog does not have. Check that each question is relevant, that the advice is right and that each reason is accurate. Test conversations are recorded like any other, so you can read them back with guidedSellingConversation.
Publishing a version
mutation PublishVersion($flowId: ID!, $version: Int!) {
guidedSellingFlowVersionPublish(flowId: $flowId, version: $version) {
version
status
publishedAt
warmStatus
}
}
Variables:
{
"flowId": "01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90",
"version": 1
}
Expected response:
{
"data": {
"guidedSellingFlowVersionPublish": {
"version": 1,
"status": "PUBLISHED",
"publishedAt": "2026-10-06T13:02:41.000Z",
"warmStatus": null
}
}
}
New conversations start on this version right away. The version that was published before is archived, and conversations that started on it finish on it. Only a draft can be published: publishing another version fails with GUIDED_SELLING_FLOW_VERSION_NOT_DRAFT_ERROR.
Checking that the model key and model work
Right after publishing, guided selling prepares the first steps of the version in every language it offers. This is the first full use of the flow's key and model, and warmStatus reports how it went:
query FlowVersions($input: GuidedSellingFlowVersionSearchInput!) {
guidedSellingFlowVersions(input: $input) {
items {
version
status
warmStatus
warmError
warmedAt
}
}
}
Variables:
{
"input": {
"flowId": "01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90"
}
}
Expected response:
{
"data": {
"guidedSellingFlowVersions": {
"items": [
{
"version": 1,
"status": "PUBLISHED",
"warmStatus": "FAILED",
"warmError": "LLM_AUTH_FAILED: The model vendor refused the guide's key: invalid x-api-key",
"warmedAt": "2026-10-06T13:02:44.000Z"
}
]
}
}
}
warmStatus | Meaning |
|---|---|
RUNNING | The first steps are being prepared |
DONE | Every step was prepared |
PARTIAL | Some steps could not be prepared. warmError says why. |
FAILED | Nothing could be prepared. warmError has the reason, for example a refused key or an unknown model. Customers get the same error. |
A refused key (LLM_AUTH_FAILED) is fixed by replacing the key, which takes effect without a new version. A model the vendor does not know (LLM_MODEL_NOT_FOUND) needs a new version with another model.
Changing the content
Read the content of the current version, change it and send it back as a new version. Versions are listed newest first, and content has the same shape as the input:
query FlowContent($input: GuidedSellingFlowVersionSearchInput!) {
guidedSellingFlowVersions(input: $input) {
items {
version
status
content {
instruction
catalogue {
products {
productId
sku
name
description
attributes {
name
value
}
}
}
settings {
language
languages
freeText
maxQuestions
opener {
questions {
language
value
}
options {
labels {
language
value
}
}
}
model {
model
effort
}
}
}
}
}
}
Variables:
{
"input": {
"flowId": "01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90",
"offset": 1
}
}
With offset set to 1, the response holds only the newest version. Create the next version from its content with guidedSellingFlowVersionCreate:
mutation CreateVersion($input: GuidedSellingFlowVersionCreateInput!) {
guidedSellingFlowVersionCreate(input: $input) {
version
status
}
}
A script that reads the newest version, lowers its question budget and saves the result as a new draft:
const FLOW_CONTENT = `...`; // the FlowContent query above
const CREATE_VERSION = `...`; // the CreateVersion mutation above
const flowId = '01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90';
async function propeller(query: string, variables: object) {
const response = await fetch('https://api.helice.cloud/v2/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json', apiKey: process.env.PROPELLER_API_KEY! },
body: JSON.stringify({ query, variables }),
});
const { data, errors } = await response.json();
if (errors?.length) throw new Error(errors[0].message);
return data;
}
const read = await propeller(FLOW_CONTENT, { input: { flowId, offset: 1 } });
const { content } = read.guidedSellingFlowVersions.items[0];
content.settings.maxQuestions = 4;
const created = await propeller(CREATE_VERSION, { input: { flowId, content } });
console.log(created.guidedSellingFlowVersionCreate);
The script prints the new draft:
{
"version": 2,
"status": "DRAFT"
}
Test version 2 by naming it in the step, then publish it. Storefronts keep running version 1 until you do.
Renaming, switching off and replacing the key
guidedSellingFlowUpdate changes only the fields you send:
mutation UpdateFlow($id: ID!, $input: GuidedSellingFlowUpdateInput!) {
guidedSellingFlowUpdate(id: $id, input: $input) {
id
name
active
lastModifiedAt
}
}
Variables:
{
"id": "01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90",
"input": {
"active": false
}
}
Expected response:
{
"data": {
"guidedSellingFlowUpdate": {
"id": "01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90",
"name": "Label printer advisor",
"active": false,
"lastModifiedAt": "2026-10-06T14:10:05.000Z"
}
}
}
| Field | Effect |
|---|---|
name | Changes the display name. Customers never see it. |
active | false switches the flow off: storefronts get GUIDED_SELLING_FLOW_INACTIVE_ERROR until you switch it on again. |
modelApiKey | Replaces the model key. It takes effect on the next step, without a new version. |
Listing flows
query Flows($input: GuidedSellingFlowSearchInput) {
guidedSellingFlows(input: $input) {
itemsFound
items {
id
name
active
version
lastModifiedAt
}
}
}
Expected response:
{
"data": {
"guidedSellingFlows": {
"itemsFound": 1,
"items": [
{
"id": "01a10f3c-5b7e-7d42-9a61-3c8e2f4b7d90",
"name": "Label printer advisor",
"active": true,
"version": 1,
"lastModifiedAt": "2026-10-06T13:02:41.000Z"
}
]
}
}
}
Flows are listed most recently changed first. Filter with active in the input.
Deleting a flow
mutation DeleteFlow($id: ID!) {
guidedSellingFlowDelete(id: $id)
}
Deleting a flow removes it with every version, stored step and recorded conversation. The mutation returns true, and the deletion cannot be undone. Storefronts that still use the flow id get GUIDED_SELLING_FLOW_NOT_FOUND_ERROR, so remove guided selling from the storefront first, or switch the flow off when you want to keep its history.