Skip to main content
Put your own support bot — your name, your icon — into a client’s Microsoft Teams. Their employees message it; your chat agent answers; everything runs on your Neo configuration and bills to you. This page is the canonical deployment procedure, written to be followed top to bottom for each client you roll out to. For what channels are, see Channels.

Before you start

  • A chat agent you’ve tested in the dashboard.
  • The client’s Microsoft 365 tenant ID, set on their company record under Companies.
  • A Microsoft 365 admin in the client tenant to approve the one-time install — their admin, or you if you have delegated (GDAP) admin into their tenant. The tenant must allow custom Teams apps either way.

Test in your own tenant first

Two ways to try a bot before a client ever sees it, covering different things:
  • Behavior — does the agent answer the way you want for a given client? Use the dashboard Test as end-client picker — no install, instant.
  • The Teams experience — branding, the welcome message, adaptive and approval cards, proactive messages, the install greeting? Install your branded app into your own Microsoft 365 tenant and message it as an employee would.
To test the Teams experience:
  1. On the channel’s Installs tab, click Test in your own Teams. It is the first step on a new channel, and at the top of the tab after a client has installed.
  2. Click Open install page, sign in as a Global admin of your Microsoft 365, and approve. Not an admin? Copy the link from the same window and send it to one.
  3. Back in the dashboard, the window changes to Open the chat in Teams. The page you land on after the approval has the same button.
Teams does not list a bot you have never messaged, so use that button to find the chat. After the install, the Test in your own Teams button reads Open in your own Teams and opens the chat again, also when an older chat carries the wrong name (see the note below). Your tenant needs no company record, and you skip the Install for everyone step that clients do. The test bot answers as an end-user bot, with the same company scoping a client gets. One install in your own tenant tests the whole Teams experience.
If your tenant has an internal bot created before internal bots got their own identities, the self-install reports that the identity is taken. Move the internal bot onto its own identity first, and finish that move before you install here; custom internal bots has the steps. Removing the internal channel’s registration is only the first step of the move — its Teams app stays in your catalog on the shared identity until an administrator opens the new install link and approves it, and installing the end-user bot before that leaves your technicians with a chat that shows a stranger’s name and cannot send. Neo refuses the install until you have generated that link. The refusal lifts then, before the approval, so wait for the approval yourself. Once it is approved, install the end-user bot. It takes over the chat your technicians had with the internal bot, and its first message there says which bot it now is and where the internal one went. That chat can keep the internal bot’s old name in the Teams sidebar for a while, so it is easy to miss: open the end-user bot from the Open in your own Teams button instead of hunting for its name.

Deploy

The fastest path is a magic install link: you generate it, the client’s admin clicks it once and approves, and Neo publishes your branded app into their tenant automatically — no zip to upload. (No admin access? See Install the package manually below.)
1

Create and activate the channel

Open Chat → Channels → New Channel. Pick the Teams transport and the End-user audience, name the channel, assign your chat agent, and click Create channel. The channel starts Active and opens on its Installs tab.
The New channel form: Teams transport, End-user audience, the channel name, the chat agent picker and the Create channel button
Branding is optional: any field you leave blank uses the Neo default. To make the bot yours, set at least the Display name (the bot’s name in Teams) on the Teams app tab and the Welcome message (the first thing users see) on the Chat tab. Where every setting lives: the channel page.
2

Generate the install link

On the channel’s Installs tab, White-label installs is a table of every Companies entry with a Microsoft 365 tenant ID — one row per tenant (companies sharing a tenant are covered by a single install) — with each row’s status: Not enabled, Link sent (with its expiry), Link expired, Registered, Installed, or Live for everyone. Once a tenant is bound, rows still Not enabled fold behind the Not enabled button above the table; a search looks through every row.Click Install link on a row — or tick several rows and Generate install links in bulk — then copy each link from the dialog. A link is tied to its one channel and tenant and expires in 2 days; the row shows Link sent until the client’s admin approves, so you can see at a glance who still needs a nudge. Generating a new link for a tenant replaces the previous one (an already-sent copy stops working), and removing the row revokes an outstanding link entirely. One branded bot per client — a tenant already installed on another channel is refused when you generate its link.
3

Send it to the client's Teams administrator

Send the link to a Global Administrator of the client’s Microsoft 365 tenant. They open it, sign in, and approve the permission prompt for their whole organization; Neo then publishes your branded app into their catalog on their behalf and registers routing — all in one sign-in. They land on a “You’re all set” confirmation.The organization-wide approval is what lets every employee open the Tickets tab without a prompt. A Teams Administrator can still finish the install, because publishing to the catalog is a Teams admin action, but cannot approve for the organization: the confirmation page then says so, and the Tickets tab asks for an administrator’s approval until a Global Administrator opens the link again or approves the request (see Troubleshooting).
The sign-in authorizes the dedicated Neo Installer app to publish apps to the catalog as that admin, and lets it confirm who is looking at the Tickets tab — nothing more. The bot itself is still messaging-only and has no standing Microsoft Graph access in the client tenant; its manifest permissions are just identity and messageTeamMembers. If you have GDAP admin into the tenant, you can open the link and sign in yourself.That sign-in leaves a Neo Installer app in the tenant’s Enterprise applications — leave it in place. If a sign-in errors, have the admin re-open the link rather than deleting it to retry: deleting the app soft-deletes its service principal and blocks the next sign-in with AADSTS650051 (see Troubleshooting).
Once they’ve approved, routing is live: messages from that tenant to your bot route to this channel’s agent. There’s no separate confirmation step and no ordering to get right — the publish and the routing registration happen together.
4

Make it reach their users

Publishing puts the app in the org catalog, but users still have to get it. In the client’s Teams admin center → Teams apps → Manage apps, open your app, go to the Users and groups tab → Installs → Install app (or Edit installs), set it to Everyone, and Apply. This installs the bot for everyone — including people who join later — so new hires get it automatically.On the same tab, check Availability too. It must include the people who use the app and the admin who opened the install link: click Edit availability and choose Everyone (the simplest, and it includes guests) or a group that contains them. Many tenants default custom apps to No one. Microsoft treats an app available to no one as blocked for its users and hides it from the admin’s view of the catalogue, which can make Publish update fail (see Troubleshooting).This last step is a manual admin action and can’t be automated — Microsoft gates the “Install for everyone” assignment to the admin center (and the Teams admin PowerShell), with no API a third-party app can call. Neo reminds you of it after an install and lets you tick the company’s row under White-label installs once it’s done: the Everyone column, or For everyone in the status cell on a narrow screen. The row then reads Live for everyone. Changes can take up to 24 hours to reach all users.
Teams admin center — Manage apps, an app's Users and groups tab, Installs set to Installed for everyone, with the Edit installs button
Optional — pin it. Installing puts the app in the user’s Teams; pinning keeps it on the sidebar instead of under More apps (…). Pinning is separate (it isn’t read-only under app-centric management) and lives in Teams apps → Setup policies → Global (Org-wide default) → Pinned apps → Add apps → your app → Save. A pin only shows for users who already have the app, so do the Install for everyone step first.
This is app-centric management (the “moved to unified app management” experience most tenants are now on). On a tenant not yet migrated, use Teams apps → Setup policies → Global (Org-wide default) → Add apps instead — once a tenant moves to app-centric management, setup-policy installs become read-only and the Install for everyone step above is the way.
5

Verify

From a user account in the client tenant, open the bot in Teams:
  • First contact shows your welcome message (or “Hi! I’m your display name” if you left it blank).
  • A 1:1 question gets an answer from your agent.
  • “start a new chat” acknowledges and starts a fresh conversation. It’s in the bot’s command menu, and typing it works anywhere.
The user’s company is recognized from the Microsoft 365 tenant the message arrives from, via the tenant ID on the Company record — no per-user setup (see Sessions & billing).

Install the package manually

If the tenant blocks the consent flow, or the app is already installed and you only need to bind routing, use the zip instead of the link:
1

Download the Teams app package

On the channel’s page, open the ⋮ menu → Download Teams app package. You get a .zip containing the manifest and your icons — upload it exactly as downloaded; re-download it whenever you change branding rather than reusing an old copy.
2

Upload it in the client's Teams admin center

Open the Teams admin center → Teams apps → Manage apps → Actions → Upload new app, and upload the .zip.
3

Register routing on the channel

Back under White-label installs, open the company row’s ⋮ menu → App already installed? Register directly to bind routing. Do this before users start messaging — the bot greets on install, and an unregistered tenant gets a neutral “finishing setup” note instead of your welcome message (routing still self-heals on the next message once registered).
With the Tickets tab on, the package still signs users in to the tab through the Neo Installer app, which a manual upload never approves. The first employee to open the tab is asked to approve it. In a tenant where users can’t approve apps, that becomes a request an administrator approves once in the Microsoft Entra admin center under Enterprise applications → Admin consent requests; after that nobody is asked again.

What end users can access

Every conversation through this bot is scoped to the end user’s company, resolved from the Microsoft 365 tenant the message arrives from. That scope is enforced by Neo, not by the agent’s configuration (the architecture behind it: how end-user bots work):
  • The bot runs a purpose-built set of end-user tools: it answers from your knowledge base (for a common how-to your knowledge base doesn’t cover, it gives brief general steps and says they are general guidance, never your company’s details), looks up the user’s own tickets, and (if you enable it) files new tickets and posts replies as the user — all scoped to that one person and their company.
  • Tools that operate across your whole client base — MSP-wide ticket search, company listings, configuration/contact search, generic PSA or Microsoft Graph API access, sandbox scripting — never run in end-user conversations, even if they’re enabled on the agent’s toolbox.
  • If the inbound tenant isn’t linked to any of your Companies, the bot refuses the conversation and tells the user to contact their IT support team — it never answers unscoped.
  • One Microsoft 365 tenant can be linked to several companies (branch offices sharing a tenant). Neo scopes each conversation to the company whose PSA contacts hold the speaker’s verified email; if the email isn’t found there (or appears in more than one), Neo picks deterministically — the parent company (Autotask), then the lowest company ID.
  • If the speaker’s email matches more than one contact record within the resolved company (some MSPs keep a duplicate contact per site), the bot treats tickets filed under any of those contacts as the user’s own — they can view, reply to, and resolve every one of them. New tickets and replies are still authored under one consistent contact for stable attribution.
  • This applies to your own tenant too: a self-install of an end-user channel is company-scoped like any client install. For cross-company technician work, use the Neo AI Agent app and Neo Support — or build a custom internal bot from an Internal channel, which runs with your full MSP toolbox.
Whenever the bot mentions a ticket, it shows the human ticket number — and, where your PSA has a client-facing portal link, that number is a clickable link the end user can actually open. For Autotask, set this up once: copy your Client Portal address from Autotask (Admin → Client Portal Global Settings → “Generic Client Portal Login”, e.g. https://yourcompany.itclientportal.com) and paste it under Integrations → Microsoft Teams — End-user Chat Bot → Ticket links in the chat. Autotask’s API doesn’t expose that address, so Neo can’t fill it in for you. Until it’s set, the bot shows plain ticket numbers — never an internal Autotask link an end user can’t open. Note the portal only admits contacts you’ve granted Client Portal access in Autotask; if only some of your users have it, you can gate the link per contact from the agent’s instructions.

The Tickets tab

Show the Tickets tab on the channel’s Teams app tab gives the app a Tickets tab beside the chat. A new end-user channel starts with it on. It lists the signed-in employee’s open tickets, most recently updated first, and the tickets closed in the last 30 days — every ticket filed under their PSA contact, not only the ones the bot created, so a ticket they emailed or phoned in shows up too. Each row has the ticket number, title, status and dates; where your PSA has a client-portal link (see Ticket links in the chat), the number opens the ticket there. New ticket at the top of the tab opens the chat, and the bot asks what the ticket is about, in the end user’s Teams language where Neo has a translation and in English otherwise. The bot is told the user asked for a ticket, so it files one for what they describe next, also under the Troubleshoot first ticket policy, and that message is not recorded on a ticket the chat was already on (see What the ticket records). The request lasts an hour, or until the bot files or links a ticket. A ticket the bot files runs your workflows like any other. In Outlook and the Microsoft 365 app, which show the tab but not the chat, the button opens Teams on the chat. Chat about this on an open ticket moves the chat onto that ticket. The bot says which ticket the conversation is now on, with its title and status unless you hide them under Relayed technician messages, and asks what the user would like to know or add, in the same languages as New ticket. From the user’s next reply, the conversation is recorded on that ticket, and your technicians’ client-facing updates on it reach this chat. It also works for a user who has not chatted with the bot yet: their first message continues the ticket when they send it within 20 minutes of the tap. The tab follows the end user’s Teams theme, including dark and high contrast, and fits a phone screen. It is written in the user’s Teams language when that is English, French, German, Spanish, Dutch, Italian or Portuguese, and in English otherwise; dates follow their regional format. Ticket titles and status names come from your PSA as they are. Your link labels can follow the language too: see below. A Slack channel shows the same list as the Slack app’s Home tab: see the Slack Tickets tab. Open the app on picks the tab people see first. A new end-user channel opens on Tickets, with the chat next to it; a channel created before this default opens on Chat until you change it. Nobody signs in: Teams tells Neo who is looking, through the Neo Installer app the client’s admin approved with the install link, and the list is scoped exactly like the bot’s own “my tickets” answer — the user’s company, resolved from their Microsoft 365 tenant, and the PSA contacts that hold their email address. If no contact has their address, the tab says so and suggests contacting you. Under Links on the Tickets tab, add up to five links for the top of the page, such as your emergency line or your IT Glue portal. A link is a web address (https only), a phone number or an email address, with a label of up to 40 characters. A phone number can end in an extension, such as 514-555-0199 x6, ext. 6 or poste 6. Under Label in other languages, a link can carry its label in any of the tab’s languages; each person sees the one for their Teams language, and the main label when you left theirs blank. The tab shows a phone number or email address as text beside its label; a tap calls the number, dialling the extension once the call connects, or opens an email, where the device can. Links need no publish: they show the next time the tab loads, or when the user clicks Refresh on it. They also show while the client’s tenant is not yet linked to one of your companies, when the tab cannot list tickets. Three things to know:
  • The tab and Open the app on are part of the installed Teams app, so they reach a client only after you publish an update. Adding or removing the tab is a change Microsoft makes each user approve: users who added the app themselves click Update in Teams once, while Install for everyone tenants update without a prompt. A change to Open the app on alone needs no approval.
  • Turning the tab off also sets Open the app on back to Chat.
  • The status column follows Show the ticket status below: turn that off and the tab shows no status names either.

Live ticket updates in the chat

Intake is only half of it. When work on a ticket the user raised here produces a client-facing update, that update is delivered into the same Teams conversation — and the user’s reply goes straight back onto the ticket. The Teams thread and the ticket’s client-facing notes become two views of one exchange, so a user follows their ticket without leaving Teams or digging through email.
1

The user raises a ticket

Through the bot — a new ticket, or a reply on one they already have.
2

A technician (or a Neo workflow) responds

Someone posts a client-facing note on that ticket — the normal way you’d update a customer.
3

It lands in the chat

Neo relays that update into the same Teams conversation as the bot’s next message, usually within seconds of the update. It arrives as a card showing who replied — by default the technician’s own name and avatar, taken from the note’s author in your PSA — with their message and the ticket’s title, number and current status below it, so the employee can tell a person replied, not the AI — and which of their tickets it is about.That avatar is a coloured disc of the technician’s initials unless you have connected Microsoft Teams — Technician Mapping, in which case a mapped technician’s card carries their real Microsoft 365 profile photo. It is one setup for your whole client base, with nothing to enable per channel, and anyone Neo can’t match keeps the initials.You choose per channel what that card shows, under Relayed technician messages on the channel’s Chat tab:
  • Show the technician’s name (the default) — the individual who wrote the note, with their profile photo where Neo can find one and coloured initials otherwise. If your PSA doesn’t tell us who wrote it, the card falls back to the label you set.
  • Show your own name instead — every relayed message carries your label and your channel’s icon, and never an individual’s name. Pick this if your technicians rotate, or you’d rather present one brand to end-clients.
  • Don’t label them — the note is relayed as plain text with no attribution, as it arrived before labelling existed.
The label itself has ready-made wordings in several languages, or write your own.Two switches control the footer under the message, and the ticket number is always shown whatever you do with them.Show the ticket title (on by default) puts the ticket’s own title on its own line above the number. That is what tells one of a contact’s open tickets from another — somebody holding six of them thinks in “the password one”, not in numbers. A long title is shortened to fit the card. Turn it off if your ticket titles are internal shorthand rather than the words the end-user raised the ticket in.Show the ticket status (on by default) puts the current status beside the number. The status is the status name straight from your PSA, in whatever language your PSA is configured in — Neo passes it through and can’t translate it on this card, even if your agent is otherwise answering in the end-user’s language. Turn it off when that language isn’t one this channel’s end-users read; it also hides the status column on the Tickets tab.
4

The user replies in Teams

Their reply is recorded on the ticket as a customer reply, so the technician sees it on the ticket they’re already working — no inbox round-trip.

What the ticket records

Once a conversation is linked to a ticket — the bot created it, or the user replied about an existing one — every message from both sides lands on that ticket automatically, turn by turn, while the conversation stays active. Nothing depends on the bot deciding to log something, and nothing in your instructions is needed to make it happen. After more than a day of quiet, the next message starts a fresh conversation: the bot links a ticket again or files a new one before anything more is recorded. One chat covers many problems over its life, so each message is recorded on the ticket it is actually about. A follow-up, a correction, an answer to the bot’s question, a “thanks, that worked” hours later — all of that continues the ticket the conversation is on. A message that raises a different problem does not: it is held back rather than filed as a customer reply on a ticket nobody mentioned, which would have started your customer-replied rules on the wrong ticket. Once the new problem has a ticket of its own, everything said from then on is recorded on it as usual. The messages before that ticket existed are not moved onto it — if the bot asked two questions before filing, those two exchanges are in the ticket’s own description rather than as replies. That judgement is made per message, and it can fail: if your PSA won’t return the ticket Neo is comparing against, or the check itself errors, the message falls back to the older rule and is recorded on the ticket the conversation was already on. Neo would rather keep the record than lose it, so a rare wrong-ticket reply is possible when the check can’t run.
  • The user’s messages are recorded as customer replies in your PSA’s own shape — authored by their contact on Autotask, ConnectWise and Halo (the shape your customer-reply rules react to; on ConnectWise it also sets the ticket’s customer-updated flag), and as a visible comment carrying their name on ServiceNow and Syncro.
  • The bot’s replies are recorded as internal notes, so they never email the customer or fire customer-facing rules — the user already has the reply in Teams. Technicians and Neo’s own ticket summaries still see the full exchange, in order. They’re written as plain text: PSA note fields don’t render the formatting Teams shows, so headings, bold and links are converted rather than left as ** and ### characters.
  • A closed ticket never receives a customer reply. A customer reply on a completed ticket is what most “customer replied” rules re-open it on, so once a ticket is resolved the follow-up (“thanks, that worked”) is still recorded on it, but as internal notes on both sides — the audit trail is complete and nothing re-opens by accident. If the user comes back with the same problem, the bot files a new ticket, and the conversation follows that decision.
If a message ever fails to reach the PSA, Neo keeps it and retries on the next turn — and, when the conversation has ended, within about an hour of the last message, so the final exchange is retried without waiting for a message that never comes. In one rare case — the note landed but Neo could not record that it had — the retry posts the same message a second time; a duplicate note is preferred to a lost one. A ticket deleted in your PSA stops the retries on ConnectWise, Autotask and Syncro. Those three report the deletion precisely when the note is rejected, and there is nothing left to write to, so the messages held for that ticket are released instead of being tried again every turn. HaloPSA and ServiceNow reject the note without saying the ticket is gone, so those messages keep retrying until they age out after seven days. Either way, anything the conversation says afterwards is recorded on whichever ticket it is about next.
Don’t instruct the bot to log the conversation. “Add the whole conversation as one note at the end”, “log every reply to the ticket”, “post the transcript when we’re done” — the ticket already carries it, and the bot has no tool that posts a note you dictate, so the instruction only gives it a job it cannot do.That is a separate question from when a ticket gets opened, which is yours to set — see Choose when the bot opens a ticket below. An instruction like “wait until the conversation is finished, then log it” answers both at once: keep the timing half, drop the logging half.
Only client-facing communications relay — exactly what the user already sees under their own tickets. Internal, technician-only notes never reach the chat; the user’s own replies don’t echo back to them; and Neo’s own bookkeeping notes are filtered out. The conversation runs through the ticket, entry by entry. Your technicians never type in the end user’s Teams conversation — there is no technician seat in it, and the only two speakers are the end user and the bot. A technician replies the way they already do, by saving a client-facing entry on the ticket, and Neo carries that entry across. The end user’s answer comes back on the same ticket, where the technician reads it, in the shape What the ticket records sets out above. Messages travel both ways, but each one is a ticket entry rather than a live conversation between two people, and which entries cross is the setting below.

Choose when the bot opens a ticket

Two questions decide what your PSA ends up with, and only the first is yours to answer:
  • When does a ticket get opened? The bot’s decision, and you set the policy in its custom instructions — there is no hidden setting. That is this section.
  • How does the conversation get onto that ticket? Neo’s job, always, from the moment a ticket is linked. Nothing you write changes it — see What the ticket records above.
The policy covers new problems only. A message about one of the user’s open tickets (“any news on my email ticket?”) or a question about the bot itself (“can I reply to my ticket here?”) is not a new problem, so it never opens a ticket, whichever shape you pick. Your instructions don’t need to say so. The bot reads the user’s open tickets from your PSA when a conversation starts (a new chat, or one reset by /new or the idle timer), so it can tell an existing problem from a new one without being asked. “My email ticket” matches the open ticket about email; “Outlook still isn’t getting mail” as the opening message matches the open Outlook ticket instead of opening a second one. When the read fails or your PSA is slow to answer, the bot is told the list is missing and looks the tickets up with Get My Tickets before it acts on a possible follow-up or files a ticket. The list carries the time it was read: in a conversation that runs past two hours, the bot rechecks with Get My Tickets before filing, in case a ticket reached you another way meanwhile. Three shapes cover every bot in production today. Creating an end-user agent asks you which one — the cards under Audience on the agent’s Overview tab — and the choice writes the matching block into the starter instructions for you. Nothing is stored behind it: from then on the instruction text is the only source of truth, so you change the policy by editing that text (or asking Neo Support to). To move an existing agent to another shape, replace its When to create a ticket block with one of these:
What reaches the ticket, and how, under each shape. Once the ticket exists, every later message is written to it automatically. What happened before it existed is carried by the ticket the bot files — so under Ticket first that is one message, and under Record at the end it is the whole exchange, under the bot’s summary. Either way you write nothing about logging; the difference is only how much of the conversation the new ticket has to carry. Neo rebuilds that exchange from the stored conversation, so the wording and the speaker on each turn are the real ones. Where it can’t — a conversation older than the last 200 messages of a long-running session is the usual reason — the bot’s own rendering of the exchange is used instead, which can paraphrase a turn or attribute it to the wrong speaker. Everything from the ticket onward is unaffected: those messages are written from the stored conversation. A long exchange doesn’t fit a PSA description field, so it isn’t kept there. The body then ends with “Transcript too long for this field — the full chat transcript is in an internal note on this ticket”, and the transcript follows as one or more internal notes titled Chat transcript, split into parts if it needs them. A technician reading a body that stops mid-sentence is told where the rest is. That note is written on a best-effort basis, so the ticket is never held up by it. If your PSA rejects it, the ticket keeps the truncated body and its pointer, the message the user had just sent still reaches the ticket as a customer reply, and the earlier part of the exchange that the body could not hold is not on the ticket. If the problem a closed ticket was about comes back, the bot files a new ticket and names the closed one in its description.
Record at the end has one limit. A conversation the bot never opens a ticket for leaves nothing in your PSA. The conversation itself is still in Chat → History under the End-user lens; what’s missing is the ticket. If every conversation must produce one regardless of outcome, use Ticket first.

What counts as a client-facing update, per PSA

Each PSA models customer communication differently, so Neo keys off that PSA’s own signal for “the customer is meant to see this”. In ConnectWise, a note marked Internal Analysis never relays, whatever the settings. So your technicians reply the way they already do: save a Discussion note on the ticket and the employee sees it in their chat. Ticking Email Resource/Contact is optional — it sends your PSA’s usual email as well. If your technicians instead use Discussion notes to document their work, that documentation would reach the employee. Turn off All public Discussion notes in the relay settings and Neo falls back to notes ConnectWise actually emailed to the ticket’s contact.
If you connected ConnectWise before this default changed, your account keeps the settings you had saved. Open the relay settings and check All public Discussion notes to pick up the new behaviour.
These defaults work out of the box. To tune exactly what relays for your PSA — for example narrow ConnectWise back to emailed notes, include customer-visible time entries, or pin the specific HaloPSA outcomes you use — open Integrations → Microsoft Teams — End-user Chat Bot and adjust the relay settings. The change applies to your whole client base.

Autotask: narrow to a Work Type or Note Type

If your technicians log all their work on the ticket and only some of it is meant for the customer, the toggles above are too coarse — turning on time entries relays every one of them. Autotask lets you narrow further:
  • Limit to specific Work Types — relay a time entry only when its Autotask Work Type is one you pick. The common setup is a dedicated Communication work type (often applied by a speedcode) that technicians use whenever they contact the customer: pick it here and ordinary work time on the same ticket stays out of the chat.
  • Leave out specific Work Types — relay every time entry except those of the work types you pick. Use it when most time entries are fine for the customer and only one or two work types are noise, such as an internal tech-assist speedcode. A work type you add to Autotask later still relays, with nothing to update here.
  • Limit to specific Note Types — relay a published note only when its Note Type is one you pick.
Each picker sits under the toggle it narrows and is only active while that toggle is on. Leaving a picker empty means “don’t narrow” — every entry of that kind relays, which is the behaviour you have today. Clearing a picker restores that, so you can’t accidentally mute your relay by emptying it; to stop a kind relaying altogether, turn its toggle off instead. You can set both Work Type pickers. A time entry then relays only when its work type is in the limit and not in the list to leave out; a work type in both stays out of the chat. If Neo can’t read a time entry’s work type, the limit keeps it out, and the list to leave out lets it through. Why a time entry needs the Work Type, when a note doesn’t. An Autotask ticket note carries a Publish setting, so a technician decides per note whether the customer sees it, and Neo relays only the notes published to All Autotask Users. A time entry has no equivalent setting: Autotask requires Summary Notes on a ticket time entry and treats that field as the customer-facing description of the work, while Internal Notes is the internal field and Neo never relays it. A technician can’t mark one time entry as internal, so with time entries switched on, every summary reaches the chat — a bare “Reviewed ticket” and a technical description of the fix alike. The Work Type is the per-entry intent signal Autotask does give you: log customer contact under the work type you allowlisted (or log internal work under a work type you leave out), and keep technical detail on any other work type or in Internal Notes. Gating on the ticket’s status instead doesn’t hold up, which is why there’s no setting for it: the status describes the ticket, not the entry. Once a ticket sits in Waiting Client, every time entry added while it waits would relay, and a genuine customer update written while the ticket is still In Progress would be held back.

Autotask: time entries need one more webhook field

Autotask sends Neo no webhook when a technician saves a time entry and changes nothing else on the ticket. So when time entries relay, Neo also subscribes your Autotask ticket webhook to the Last Activity Date field. That field changes on every save to a ticket, so Autotask sends Neo more webhooks than before. Neo adds the field at the next scheduled PSA sync, which runs about once a day. Until then, a time entry reaches the chat only when the same save also changes the ticket’s status or queue. The relay is on by default. The same panel has a master Relay ticket updates into the chat switch at the top — turn it off to stop relaying entirely (your PSA’s own customer email still goes out as usual), and the integration card shows Connected whenever it’s on. Which tickets relay. By default, only tickets your bot raised relay updates — a ticket created directly in your PSA or by email stays out of the chat, even if a user later asks about it through the bot. If you’d rather follow updates on every ticket a user picks up through the bot, turn off Only tickets the bot raised in the same panel.
Two things worth knowing today:
  • The relay is delivered in addition to your PSA’s own customer email, so a user who is actively chatting with the bot may receive both a Teams message and an email for the same update.
  • A user receives chat updates only once they’ve messaged the bot at least once (so there’s a live conversation to deliver into). Before that, updates reach them through your PSA’s email as usual.
(ServiceNow note: only Additional comments relay — a message sent from ServiceNow’s separate Email tab doesn’t appear in the chat.)
The user simply replies in the conversation — “yes, it’s my laptop”, “still happening” — and it lands on the right ticket as a customer reply (see What the ticket records). They can also ask the bot for the status of any of their tickets at any time (see What end users can access). Two separate Microsoft identities are involved. It’s worth being precise about what each can do — for your own assurance and to answer a client admin’s questions. The bot (the app published into the client tenant) is messaging-only. Its manifest requests just identity and messageTeamMembers — receive and send Teams messages, nothing else. It has no Microsoft Graph access in the client tenant: it can’t read mail, files, directory, or anything else there. Everything the agent does runs on your integrations under your permissions, and every end-user conversation is company-scoped (see What end users can access). The Neo Installer is the app a client admin signs into when they open a magic install link. They grant it, for their whole organization, delegated AppCatalog.ReadWrite.All (the one permission needed to publish your app to their tenant’s catalog as that admin), plus offline_access, so Neo can re-publish your branding later with Publish update instead of asking for consent again, and the sign-in permissions openid, profile and email, which the Tickets tab uses to confirm which employee is looking. A delegated permission never exceeds what the signed-in person can do, and only the consenting admin’s sign-in is ever used to publish. It is not the bot, and it grants no standing access to the client’s data. If you’d rather not have it in a tenant at all, use the manual zip path with the Tickets tab off; with the tab on, the tab still signs users in through the Neo Installer. If you approve the Neo Installer with CIPP or another tenant management tool, give it all five delegated Microsoft Graph permissions the install link grants: openid, profile, email, offline_access and AppCatalog.ReadWrite.All. These tools set the approval to exactly the list you give, so leaving out AppCatalog.ReadWrite.All removes Neo’s permission to publish your app in every tenant the tool runs on. If the tool applies the approval as a standard or template, give it the same five, or its next run removes them again. A tenant installed from the manual zip may leave out AppCatalog.ReadWrite.All, because Neo never publishes there. Routing is authorized server-side, not by the app package. The Teams package carries your branding and the shared bot id — it carries no authority. When a message arrives, Neo trusts only Microsoft’s signed account of it: the message is authenticated by Microsoft’s Bot Framework, and the tenant it came from is the tenant ID Microsoft stamps on it — never anything in the manifest. Neo resolves which channel (and which of your agents) handles it from the install record for that (bot, tenant), and the company scope from that tenant’s Company. So a copied or edited package can’t make the bot answer as a different MSP’s channel, reach a tenant it wasn’t installed in, or widen a conversation beyond the end user’s company. A registration is a claim until it’s proven. When you register a tenant under White-label installs, the binding is unverified until a tenant admin completes a magic-link consent, or the bot first receives a Microsoft-verified message from that tenant — then it’s verified and locked to your channel. An unverified claim can be replaced (register the wrong tenant ID, or two providers race on the same one, and a real install wins); a verified install can’t be taken over — to move it, remove the holding channel’s registration first. Installs into your own tenant are verified automatically.

Updating the bot after rollout

Settings on the channel’s Chat tab (the welcome message and the assigned agent among them) apply at once. Settings on the Teams app tab (display name, description, icons, the Tickets tab) reach a client only after you publish them from the bar pinned to the bottom of the channel’s page, which needs no re-consent and no admin-center step. How it works and what users see: Publishing an update. If a user’s Teams then says You cannot send messages to this bot, see Troubleshooting.
A publish lists any tenants it couldn’t reach under Some tenants didn’t update — usually because the admin’s sign-in has expired or been revoked, or the tenant now blocks custom apps. Send each of them a new link for an admin to re-authorize: the row’s ⋮ menu → Send re-consent link. A publish doesn’t cover tenants you installed via the manual zip; re-upload there with New version → Upload file in the admin center.

Automating with GDAP (CIPP-style)

If you run delegated admin (GDAP) across your clients, you don’t need to wait on each client’s admin — open the install link yourself for any tenant you administer (you’ll sign in with your delegated admin rights). For full automation, the same publish is a Microsoft Graph call against the client tenant with a delegated admin token holding AppCatalog.ReadWrite.All — POST /appCatalogs/teamsApps with the package from Download Teams app package (then POST .../appDefinitions to update) — which slots into a CIPP-style runbook. (Graph’s Teams-catalog publish is delegated-only; there is no app-only equivalent.)

Offboarding a client

Remove the company’s row under White-label installs — routing stops immediately and the bot no longer responds in that tenant. Then have the client’s admin remove the app from their Teams admin center so users don’t message a dead bot.

Troubleshooting

The link removes the upload step, not the tenant’s policy. The client tenant must allow custom (line-of-business) Teams apps. Have their admin enable custom app uploads in the Teams admin center, then re-open the link.
This almost always follows someone deleting the Neo Installer app from the tenant’s Enterprise applications to retry a failed install. Deleting it only soft-deletes the service principal — Microsoft keeps it in a recoverable state for up to 30 days and keeps its app ID reserved — so the next sign-in can’t re-create it and fails with AADSTS650051.The catch: a soft-deleted service principal is not listed on the Entra portal’s Deleted applications page — that page shows deleted App registrations, not deleted Enterprise apps. Find and purge it with Microsoft Graph (Graph Explorer works), then re-open the link:
  1. GET https://graph.microsoft.com/v1.0/directory/deletedItems/microsoft.graph.servicePrincipal?$filter=appId eq 'a77de2a9-75e6-4627-b50c-82c19e5d28b4' — the Neo Installer’s app ID is the same in every tenant.
  2. DELETE https://graph.microsoft.com/v1.0/directory/deletedItems/{id}, using the id from step 1, to permanently remove it.
Then open the install link again. (Waiting up to 30 days for Microsoft to purge the object on its own also clears it — the Graph purge is just the immediate fix.) To avoid this entirely, never delete the Neo Installer app to retry — just re-open the link.
The Installs tab shows, under each tenant, what stops Neo publishing there: why a publish failed, or a sign-in that the daily check found broken. It stays until a later publish there succeeds or the sign-in works again:
  • Update pending: Microsoft Teams was busy or still hid the app. Neo’s daily run sends it again when it can (above); publishing once more also works.
  • Needs a new install link: the sign-in Neo stored for the tenant no longer works. Use the row’s ⋮ menu → Send re-consent link.
  • Needs a Teams admin: the person who approved the link is not a Teams or Global administrator, or the tenant blocks custom apps.
  • Check the app availability: the app is in the tenant but hidden from the admin who approved it. See the last item below.
  • Last update failed: anything else. The detail under the line says what Microsoft, or Neo, reported.
The same per-tenant reasons are in GET /public-api/channels/{id}/teams-installs, which the Neo Support Agent also reads.When the reason is status 429, status 502, or Value cannot be null, Microsoft Teams was busy, or was still hiding the app from the admin’s view after an install or an update. Value cannot be null often means Microsoft applied the update and then answered with an error. Once a day, Neo tries again: it sends the app you last published to each tenant that doesn’t have it yet and still holds its admin’s sign-in, as long as the publish reached at least one tenant, you haven’t changed the app since, and its icons are uploaded images rather than web links (Neo can’t tell whether the image behind a link changed). A tenant still behind a day later needs one of the fixes below. If you want it sooner, publish once more (who a publish reaches).Otherwise, that tenant either removed the Neo Installer app (revoking publish rights) or now blocks custom apps. Send its admin a fresh install link to re-consent; the next publish then succeeds.Variants worth telling apart from the error text:
  • “Consent expired or was revoked”: the admin’s sign-in that Neo stored for that tenant can no longer publish to its Teams app catalog. Either it expired or was revoked, or a later change to the Neo Installer’s permissions in that tenant removed the catalog permission, as a CIPP approval without AppCatalog.ReadWrite.All does (Consent and security). A fresh install link, approved by a Teams or Global administrator, restores it. Neo also checks every tenant’s sign-in once a day, marks a tenant that needs a new link on the Installs tab, and sends a notice to your Neo Inbox naming it. When the sign-in works again, both clear by themselves.
  • “TenantAppDefinitionManifestVersionAlreadyExists”: the tenant already holds an app version with the number in the error, so Neo did not count this publish as done. Publish once more: each publish sends a higher version number. The admin’s role is not the cause.
  • “valid Office365 license” — the person who originally consented has since lost their Microsoft 365 licence, so Microsoft won’t let them manage the app catalogue any more. A fresh install link, consented by a licensed admin, fixes it.
  • “does not show it to the admin who signed in” — the app is in the tenant, but Microsoft’s catalogue hides it from the admin who opened the install link, so Neo can’t find it to update it. Keep the app installed: its users still have it. The usual cause is the app’s Availability. In the Teams admin center → Teams apps → Manage apps, open your app, click Edit availability, include that admin (Everyone is simplest) and Apply, then publish again. Microsoft can take a day or more to apply the change. Right after an install or an update, Teams can also hide the app for a short time, so if the error shows once, publish again later first. Only if Manage apps doesn’t list your app was it added another way, such as a hand upload: then remove it and install again from a fresh link.
The client’s tenant has not approved the Neo Installer app for everyone, and its users can’t approve apps themselves, so Microsoft sends each employee’s request to an administrator (the prompt reads Approval required). This happens where the install link was approved by a Teams Administrator, or without the organization-wide approval, and on tenants installed with the manual zip.One approval in the client’s tenant fixes it for everyone. The simplest way:
  • A Global Administrator opens a fresh install link: the row’s ⋮ menu → Send re-consent link. It also publishes the app again.
  • Or a Global Administrator or Cloud Application Administrator approves the pending request in the Microsoft Entra admin center under Enterprise applications → Admin consent requests → Neo Installer. The request is there, not in the Teams admin center.
  • Or, with CIPP or a similar tool, approve the Neo Installer for the whole organization with the five permissions listed under Consent and security.
Check that the channel’s page no longer shows the Not published bar, and that the tenant reads Up to date under White-label installs. Teams can take a few hours to show the update. A user who added the app themselves, where the tenant has no Install for everyone, must click Update in Teams once when the Tickets tab was added or removed; an admin cannot do it for them. See Publishing an update.
Teams desktop can keep the previous version of the app in a window that was open during the update. That window locks the message box, while the same chat in a new window still works. Have the user quit Teams fully (Quit from the system tray on Windows, or Cmd+Q on a Mac) and open it again. The bot and Neo are not affected.
The app was installed before the channel was Active or before routing was registered — most common on the manual-zip path. The greeting is one-time, and routing self-heals: activate the channel and confirm the company is registered, and the next message a user sends reaches your agent normally.
The agent reads the post that started the thread (see In a Teams channel thread), but only once the installed app carries the permission to read channel messages. An app installed before that shipped keeps its old manifest: publish an update from the channel’s page, then have a team owner re-add or re-consent the app in that team — the consent prompt is per team, not per tenant. Until then the agent answers using only your reply, so paste the details in or quote the post.
Teams attaches a chat to the bot identity. Your internal bot moved onto its own identity, and the end-user bot you then installed in your own tenant took the identity that chat is attached to. So the chat still named after the internal bot now runs the end-user bot. The internal bot is in its new app, linked in the message Neo posted into the old chat. Ask technicians to open it from that link (or from Apps) and hide the old chat. For a fresh start in the old chat, type start a new chat there.The same link is on the channel itself, so you can send it again without finding the Teams message: Open the chat in Teams on the internal channel’s Installs tab, and Open in your own Teams on the end-user channel’s Installs tab.
That chat is on a bot identity no app in your tenant carries any more, so Teams falls back to the raw bot registration name and blocks the message. It happens when the end-user bot was installed in your own tenant before the internal bot had finished moving onto its own identity. Typing start a new chat cannot fix it — sending is exactly what is blocked.The technician gets out of it in Apps: open the app by name (the end-user bot’s Display name, or the internal bot’s) and message it from there, which starts a chat on the right identity. The old chat can be hidden. If the app does not appear, have them remove and re-add it from Apps → Manage your apps.Neo now refuses an end-user self-install while an internal channel is still on the shared identity. Once its new link is generated, have an administrator approve it before you install the end-user bot.
Check in order: the channel is Active (a Disconnected channel stays installed but doesn’t route); the client company shows under White-label installs with the correct tenant ID; and the user messaged 1:1 or @mentioned the bot in a group chat.
Ask the user to type /new or “start a new chat”. Either one starts a fresh conversation, which clears a chat stuck on an old session. If the same message comes back in the fresh conversation, contact Neo support.
The branded app is blocked in the client’s tenant. Microsoft rejects messages and notifications with “(BotDisabledByAdmin) The tenant admin disabled this bot”, and Teams greys out the compose box — it’s per-user, so some of the client’s users may work while others are blocked. The client’s Teams admin allows it in the Teams admin center: Manage apps → the branded Neo app → Allowed, and the app permission policy assigned to those users must permit it; then have them re-open the app. When Neo sees Microsoft refuse the install welcome this way, the client’s row on the channel’s Installs tab says Blocked by the tenant’s Teams admin (blocked_by_admin_at in GET /public-api/channels/{id}/teams-installs). A row without the warning proves nothing: a block Neo did not see leaves no trace, and a new chat or a new install from any user of that tenant clears it. (This is distinct from the tenant blocking custom app uploads — that’s the publishing-failed accordion above.)
That’s expected: the agent is mention-only in group chats and channels — it answers when @mentioned and stays quiet otherwise, so it never talks over a busy channel. “start a new chat” works in groups even without a mention. See mention mode.
The user’s Microsoft 365 tenant reached your bot but isn’t linked to any company, so Neo refuses to answer rather than run unscoped: the tenant ID was removed from (or never set on) the company under Companies. Set it, and the fix takes effect on the user’s next message; nothing needs reinstalling. (Several companies sharing one tenant ID is fine — Neo scopes each conversation by the speaker’s email; see What end users can access.)
Your Neo account is switched off, so every branded bot you installed stops answering. Your clients’ users see “This assistant isn’t active any more. Please contact your IT provider.” — the message points them to you and never names Neo. The bots stay installed in your clients’ tenants. When your account is active again, the next message a user sends gets an answer. Nothing needs reinstalling.
When a user replies in the chat, Neo records it on the ticket as a customer reply authored by that person’s Autotask contact — which is what flips the ticket to Customer Note Added and fires your customer-note workflow rules. If the note shows your Neo API user as the author instead (and no workflow rules fire), the API user’s security level is missing Contact Impersonation.Autotask fails this silently: it accepts the note, drops the contact, and returns success. Nothing errors, so the only symptom is the author name on the note.Fix it on the security level assigned to your Neo API user — Admin → Organization Settings & Users → Resources/Users (HR) → Security Levels → your Neo API user’s level → Web Services API → Contact Impersonation — and check Add for Tickets and Ticket Notes (or click Full Permission on the whole Web Services API section, as the security level guide shows). Save, and the next reply a user sends is authored by their contact. Nothing needs reinstalling and existing notes are not rewritten.If another API user in the same Autotask instance already writes notes attributed to contacts, that confirms the instance supports it and only this security level is missing the grant — compare the two levels side by side.This is separate from Resource Impersonation on the same page, which is what makes records a technician asks Neo for in Teams carry the technician’s name.
Two causes: the company has no Microsoft 365 tenant ID (set it on Companies — it must be the tenant GUID), or that tenant is already bound to another of your channels — one branded bot per client.
This bot is for your clients’ end users. For your own technicians, use the Neo AI Agent app (notifications, approvals, Neo Support) or a custom internal bot you build.