Navigation API

A project can publish navigation specifications — entry points in the host application that an agent may direct the user to. They are delivered to clients as the navigationSpecifications array of the project payload returned by GET /api/v2/project/{idOrRef} and GET /api/v2/project?agentKey={agentKey} (see Project and Agent API), and the endpoint on this page resolves whether an individual entry point is currently active for a user.

A specification can also reach the client mid-session, when an evaluation unlocks it — see Runtime Updates.

Navigation Specification Structure

The project payload carries a flat array of specifications. Nesting is expressed through parentRef, not through embedded arrays — a client builds the tree by grouping entries on their parentRef and ordering each level by order.

Only specifications that are static and active in the project configuration are published this way. Both flags are server-side gates and neither is part of the payload:

  • isActive — an entry deactivated in the project configuration is dropped from the array entirely. A client therefore never receives an inactive entry and never has to filter on one.

  • isStatic — a non-static entry is not part of the navigation the client renders up front. It reaches the client only at runtime, when an evaluation unlocks it (see Runtime Updates).

Common Fields

Every specification, regardless of its type, carries these fields.

Field Type Description

type

String

Discriminator of the specification type — one of folder, text, action, route, url. See Specification Types.

ref

String

Stable identifier of the specification within the project. Used as the {ref} path parameter of GET /api/navigation/status/{ref} and as the parentRef of its children.

parentRef

String | null

Ref of the parent folder, or null for a top-level entry.

image

String | null

Absolute URL of a background image for the entry, when one is configured. Render blurred and darkened except top right corner to make the rest information in the tile readable.

name

String

Human-readable label of the entry, to render in the navigation UI and to use as the title of a push notification announcing it. Tool-backed types are exposed to the agent under their ref, not under this name.

description

String

Longer description of the entry. May be rendered as a subtitle and is the text the agent reasons over when deciding whether to navigate the user here.

order

Integer

Sort order among the siblings sharing the same parentRef. Lower values come first.

disabledMessage

String | null

The locked state of the entry. null means the entry is unlocked and can be used. A string means the entry is locked, and the string is the message to show when the user picks it. It is always populated for a locked entry: a specification may carry its own wording, and one that does not falls back to the project’s defaultDisabledMessage, itself defaulting to Currently not available. There is no separate boolean — disabledMessage != null is the whole test.

The lock is resolved per user. An entry with no configured condition is never locked; a conditioned entry stays locked until the condition has been satisfied by an evaluation, and every conditioned entry is locked for an unauthenticated request.

A locked entry is still delivered in the payload — render it as locked and surface disabledMessage, rather than hiding it.

Specification Types

Each type adds its own payload field on top of the common ones.

type Extra field Behaviour

folder

none

Grouping node. Holds no target of its own; its children reference it through parentRef. Folders may nest — a folder can itself have a parentRef.

text

text (String)

Utterance to send to the project’s featured agent (featuredAgentRef) on the user’s behalf, as if the user had said it — e.g. Tell me about my account state. The conversation then continues from the agent’s answer.

action

actionName (String)

Action of the host application to invoke, identified by actionName — e.g. accountState. The client maps the name onto its own handler.

route

route (String)

In-app route to navigate to. Currently only agent routes are used: /a/{agentRef} opens a conversation with that agent — e.g. /a/salesAssistant.

url

url (String)

External URL to open, typically in a browser or an in-app web view — e.g. a deal in the customer’s CRM, https://some.crm.com/deal/12345.

Reference Example

The example below covers every supported type across three top-level folders. Account is the only one with subfolders — Overview and Deals. The types are spread over the tree rather than repeated in every folder, which is how a real project is usually laid out. Two entries are locked, and are exactly the two carrying a disabledMessage; the rest leave it null. Four entries — one per non-folder type — point image at a real, publicly reachable photograph, shown as a thumbnail beside their title in the diagram; the folders use placeholder URLs on a fictional CDN.

Tree of the navigation specifications in the reference example

And the payload that produces it:

{
  "navigationSpecifications": [
    {
      "type": "folder",
      "ref": "account",
      "parentRef": null,
      "image": "https://cdn.example.com/acme/nav/account.png",
      "name": "Account",
      "description": "Everything about the account the user is working on.",
      "order": 1,
      "disabledMessage": null
    },
    {
      "type": "folder",
      "ref": "accountOverview",
      "parentRef": "account",
      "image": null,
      "name": "Overview",
      "description": "Current state of the account.",
      "order": 1,
      "disabledMessage": null
    },
    {
      "type": "text",
      "ref": "accountState",
      "parentRef": "accountOverview",
      "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Analytics_2016-02-26_at_10.19.24_AM.jpg/960px-Analytics_2016-02-26_at_10.19.24_AM.jpg",
      "name": "Account state",
      "description": "Asks the agent to summarise the state of the account.",
      "order": 1,
      "disabledMessage": null,
      "text": "Tell me about my account state"
    },
    {
      "type": "action",
      "ref": "accountPanel",
      "parentRef": "accountOverview",
      "image": null,
      "name": "Open account panel",
      "description": "Opens the account state panel of the host application.",
      "order": 2,
      "disabledMessage": null,
      "actionName": "accountState"
    },
    {
      "type": "folder",
      "ref": "accountDeals",
      "parentRef": "account",
      "image": null,
      "name": "Deals",
      "description": "Open opportunities on the account.",
      "order": 2,
      "disabledMessage": null
    },
    {
      "type": "text",
      "ref": "openDeals",
      "parentRef": "accountDeals",
      "image": null,
      "name": "Open deals",
      "description": "Asks the agent which opportunities are currently open.",
      "order": 1,
      "disabledMessage": null,
      "text": "What deals are open right now?"
    },
    {
      "type": "url",
      "ref": "dealInCrm",
      "parentRef": "accountDeals",
      "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Handshake-2056021.jpg/960px-Handshake-2056021.jpg",
      "name": "Deal in CRM",
      "description": "Opens the deal record in the external CRM.",
      "order": 2,
      "disabledMessage": null,
      "url": "https://some.crm.com/deal/12345"
    },
    {
      "type": "route",
      "ref": "accountAssistant",
      "parentRef": "account",
      "image": null,
      "name": "Account assistant",
      "description": "Starts a conversation with the account assistant.",
      "order": 3,
      "disabledMessage": null,
      "route": "/a/accountAssistant"
    },
    {
      "type": "folder",
      "ref": "assistants",
      "parentRef": null,
      "image": "https://cdn.example.com/acme/nav/assistants.png",
      "name": "Assistants",
      "description": "Agents the user can switch to.",
      "order": 2,
      "disabledMessage": null
    },
    {
      "type": "route",
      "ref": "salesAssistant",
      "parentRef": "assistants",
      "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6b/Business-170645.jpg/960px-Business-170645.jpg",
      "name": "Sales assistant",
      "description": "Starts a conversation with the sales assistant.",
      "order": 1,
      "disabledMessage": null,
      "route": "/a/salesAssistant"
    },
    {
      "type": "route",
      "ref": "onboardingAssistant",
      "parentRef": "assistants",
      "image": null,
      "name": "Onboarding assistant",
      "description": "Starts a conversation with the onboarding assistant.",
      "order": 2,
      "disabledMessage": "Available once your account setup is complete.",
      "route": "/a/onboardingAssistant"
    },
    {
      "type": "folder",
      "ref": "reports",
      "parentRef": null,
      "image": "https://cdn.example.com/acme/nav/reports.png",
      "name": "Reports",
      "description": "Exports and dashboards.",
      "order": 3,
      "disabledMessage": null
    },
    {
      "type": "action",
      "ref": "exportReport",
      "parentRef": "reports",
      "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/Laptop_with_many_charts_on_screen_on_cluttered_desk.jpg/960px-Laptop_with_many_charts_on_screen_on_cluttered_desk.jpg",
      "name": "Export report",
      "description": "Exports the current report as a PDF.",
      "order": 1,
      "disabledMessage": "Report export is available to account owners only.",
      "actionName": "exportReport"
    },
    {
      "type": "url",
      "ref": "pipelineReport",
      "parentRef": "reports",
      "image": null,
      "name": "Pipeline report",
      "description": "Opens the pipeline report in the external CRM.",
      "order": 2,
      "disabledMessage": null,
      "url": "https://some.crm.com/report/pipeline"
    }
  ]
}

Rendered

One top-level section of the same payload — the assistants folder — as a client would render it. Every string comes from the entries above; the numbers key each part of a tile to the field it is built from.

The assistants section rendered as tiles

Runtime Updates

Navigation is not only fetched up front. When an evaluation finishes, the engine re-checks the project’s navigation specifications against that evaluation’s results and pushes the ones it has just unlocked to the user, over two channels at once.

A specification is picked up when both hold:

  • it is active in the project configuration, and

  • it has a condition, and that condition is satisfied by the insight or featured values the evaluation produced.

An entry without a condition is never delivered this way — it is unconditional, so it is already in the project payload and there is nothing to announce.

For each specification it picks up, the engine does three things:

  1. Records the unlock for that user and project, so GET /api/navigation/status/{ref} and every later project payload deliver the entry unlocked, with disabledMessage back to null. An entry already recorded as unlocked is skipped, so a repeated match does not produce a second notification.

  2. Sends a push notification to each of the user’s registered devices.

  3. Emits a pipeline output element into the running session.

Applying an Update by Ref

Both channels carry the specification’s ref, and ref is the identity of an entry within a project. A client applies an update by matching on it:

  • ref is already known — replace the stored entry with the delivered one. This is how a static entry already rendered from the project payload flips from locked to unlocked.

  • ref is unknown — insert the delivered entry into the tree, under its parentRef and at its order. This is how a non-static entry, which is never part of the project payload, first appears.

A delivered specification always arrives unlocked — its disabledMessage is null, because the engine only pushes unlocks. There is no element for the reverse direction, and none for withdrawal: an entry that is re-locked, deactivated or deleted in the project configuration simply stops being delivered. Re-fetch the project payload to pick those changes up — the pushed stream alone never shrinks a client’s navigation tree.

Pipeline Element

The specification arrives on the conversation transport as a navigation-specification output element, carrying the same object as the project payload — see Pipeline Socket. turnId is always null, because the unlock belongs to the evaluation rather than to a turn.

{
  "type": "navigation-specification",
  "sessionId": "9f1d2c3b-4a5e-6f70-8192-a3b4c5d6e7f8",
  "turnId": null,
  "time": 1774000000000,
  "specification": {
    "type": "route",
    "ref": "onboardingAssistant",
    "parentRef": "assistants",
    "image": null,
    "name": "Onboarding assistant",
    "description": "Starts a conversation with the onboarding assistant.",
    "order": 2,
    "disabledMessage": null,
    "route": "/a/onboardingAssistant"
  }
}

Push Notification

The same unlock is sent to every FCM token registered for the user. A user with no registered token receives nothing — the unlock is still recorded, so the entry appears unlocked the next time the project payload is fetched.

The notification’s title is the specification’s name and its body is the description. The picture is the specification’s image, falling back to the thumbnail of the project’s published featured agent when the specification has none.

data is a flat, string-keyed subset of the specification, not the whole object: parentRef, description, image and disabledMessage are not included, and keys with no value are dropped. type and its payload field are present for text, action, route and url entries, and absent for a folder. Treat it as enough to identify and act on the entry — take the full object from the pipeline element or the project payload.

{
  "id": "4c6b8a90-1d2e-3f40-5a6b-7c8d9e0f1a2b",
  "title": "Onboarding assistant",
  "body": "Starts a conversation with the onboarding assistant.",
  "image": "https://cdn.example.com/acme/nav/assistants.png",
  "data": {
    "ref": "onboardingAssistant",
    "name": "Onboarding assistant",
    "order": 2,
    "type": "route",
    "route": "/a/onboardingAssistant"
  }
}

GET /api/navigation/status/{ref}

Resolve whether an in-app navigation hint is currently active for the authenticated user. Requires the consumer role.

Use it to re-check a single entry without refetching the whole project — for example after a conversation that may have unlocked it.

Path Parameters

Parameter Type Description

ref

String

Ref of the navigation specification, as published in the project’s navigationSpecifications.

Query Parameters

Parameter Required Type Description

projectRef

Yes

String

Project the navigation specification belongs to.

Response

{ "isEnabled": true }

This endpoint answers with a plain boolean rather than with a specification, so it is the one place the lock is reported as isEnabled instead of through disabledMessage. isEnabled: false here means the same as a non-null disabledMessage on the specification itself.

The engine evaluates the specification’s condition against the user’s past evaluation results. Use it to re-check a single entry — after a conversation that may have unlocked it, say — without refetching the whole project.