Build a Conversational AI for One Pipeline
Most AI chatbots answer a message and stop. This one holds a real conversation, and it holds it on your behalf inside a single pipeline. You assign it to one pipeline and it talks to every contact in that pipeline across SMS, Messenger, Instagram and email, collecting the same information, working toward the same conversion, moving cards as it succeeds. This is the whole build, six steps, every field and every trap.
This guide is for insurance agency operators running Ambrose alongside GoHighLevel. It assumes you have a GHL account with at least one pipeline in it, and nothing else. There is no terminal, no repository, and no deploy step anywhere in it.
One thing to be clear about before you start, because the two get confused constantly. What you are building here is a conversational agent scoped to one pipeline. It talks to people. It is not the Digital Pipeline Manager, which is a separate thing with a separate job. Do not point them at the same work.
What you actually get
Picture a lead who fills in a form at 11pm on a Saturday.
Ambrose opens the conversation and asks the qualifying question your best setter would ask. The lead replies from bed. Ambrose answers, offers two real times from your calendar, books one, moves the card from Interested to Appointment Set, tags the contact, and writes a note explaining what the lead said.
Monday morning, there is an appointment on your pipeline.
Then the lead no-shows. Ambrose sees that too, moves the card to Appointment No Show, and starts working on rebooking.
The two builds this covers
Same engine, different goal. Pick one and build it. The second is easier once the first exists, because every step below is identical apart from the two definitions in step 4.
| Build | What it collects | Conversion goal |
|---|---|---|
| Appointment-Setting AI | Name, coverage interest, ZIP, age band, current coverage, best time to talk | A specific appointment slot accepted and booked |
| Customer Service AI | What they need, urgency, which project or policy it concerns | The current stage's deliverable accepted, and the client still happy |
The Customer Service AI is the easier first build. Lower stakes, friendlier conversations, and the client already knows who you are.
Three ways to build it
Path A: import it, three steps
Someone already built this. Import their finished work.
-
Call
ambrose_template_list, pick one, thenambrose_template_import. It arrives as a draft, with bundled sequences switched off. - Copy its webhook URL from the Integrations tab into a GHL workflow.
- Turn it on.
Path B: describe it, one conversation
Open the chat widget inside Ambrose and say what you want.
Build me an appointment-setting agent for my {your pipeline name}
pipeline. It should qualify inbound leads, book onto my calendar,
and hand off to a human on anything about pricing. Ambrose stages the whole agent as one pending action: persona files, tools, skills, the lot. You see a preview, click Approve, and a complete agent exists. To make it sound like your agency instead of a template, ask it to build the agent from your website and it rewrites every persona file from your own marketing copy.
No developer, no terminal, no separate tool. It is the same chat you use for everything else in Ambrose. How far this goes is worth reading, because it goes further than most people expect.
Path C: build it yourself
Steps 1 to 5 below. Full control.
Start at A. Customize with B. Reach C when you know what you want changed.
HIPAA: read this before you build
These conversations are PHI. A lead who texts "turning 65 in March, 85023" alongside their name is already identifiable health information. There are only three things you need to understand, and the third one is the one nobody tells you.
1. Ambrose itself is already covered
You do not need to go sign anything to run an agent on Ambrose. Ambrose runs on AWS, and its AI runs on AWS Bedrock under a BAA that Strategic AI Architects holds and passes through to you. That agreement was in place months before Ambrose launched. Your conversations, your book, your vault, all inside that boundary.
Add to that:
- Every agency gets its own encrypted vault, separated at the database level, not by a filter someone could forget.
- We do not touch it. We do not pool it. We do not train on it.
- Your competitor down the road can be on Ambrose too, and their AI will never get one inch smarter off your clients.
For everything that happens inside Ambrose, that is the whole answer.
2. Anything you connect is your paperwork
The moment you wire an outside system in, whether that is a different AI provider direct, a chatbot vendor, a dialer, a phone system or a scheduling bot, that vendor is outside our BAA. If PHI reaches them, you need a BAA with them. Not with us. Not with AWS. With that specific vendor, signed by you.
This is exactly where most HIPAA compliant AI setups actually break. Not on the CRM. Not on the model. On the sixth tool somebody plugged in on a Tuesday because the demo looked good.
Ask it plainly, per tool: has this vendor signed a BAA with me? If the answer is no, that tool cannot receive PHI.
3. So do not send them PHI
Here is the part that turns a legal problem into a settings problem. A connected tool only gets PHI if you configure it to get PHI. Most of them never need it. Your lead-discovery tool does not need a member ID. Your content tool does not need a diagnosis. Your reporting tool needs numbers, not names.
Ambrose is built so you decide this per agent and per tool. That is what the team, connector and permission setup is actually for. Three levers.
Pick the toolbox. Each agent's spokes are chosen, not assumed. An agent that does not have a tool cannot leak through it.
Every tool is already labeled. Each one ships with its PHI posture, so you are not guessing.
| Posture | What it means | Examples |
|---|---|---|
| safe | Local only. Full PHI by design, never leaves the machine. | agent vault, client vault, lead memory |
| scrubbed | Identifiers are stripped before anything egresses. | reply bot, plan quoter, channel bridge, AgencyBloc |
| free | Never touches PHI at all. | lead hunter, demo forge, content audit, the watchdogs |
Scrub what has to go out. When an outside system genuinely needs to see a conversation, it goes through the PHI Gateway: identifiers aliased on the way out, real names restored on the way back, and the mapping never leaving your machine. It fails closed, so if the gateway cannot be reached the call is refused rather than sent in the clear.
So the two options are simply these.
| Option | When it applies |
|---|---|
| BAA | The destination is covered, either Ambrose's own provider or a vendor you signed with. Send it through. |
| Scrub | No BAA. Alias it, or do not send it at all. |
GoHighLevel's own posture
Per sub-account there is a toggle: "BAA on file with GHL for this sub-account." On means the GHL
tools are treated as safe for that sub-account. Off means scrubbed. Set it
honestly, because it drives real behavior.
Everything runs inside Ambrose
The persona, the memory, the tools, the reply logic and the audit trail all live in Ambrose. That is the runtime, and it is the part already covered. Nothing outside Ambrose belongs in the conversation path with real leads. Build it here, test it here, run it here, and every outside system you add is a deliberate decision with a known posture rather than a surprise.
Before you go live
- Every connected tool listed, and its PHI posture known
- For each one that receives PHI, a BAA with that vendor, signed by you
- Everything else configured so it never receives PHI in the first place
- Provider route set explicitly on the node, not left to fallback
- Verified in the Logs tab which provider served the calls
- GHL sub-account BAA toggle set truthfully
- Vault encryption on
NOTES.mdhard stops in place: no claims talk, no coverage specifics- Someone in the agency can name where PHI goes and who has a BAA with whom
That last one is not paperwork. If nobody can answer it, you are not ready.
Step 1: create the PIT token in GHL
Steps 1 and 2 are one-time setup for your whole agency. Do them once and every future agent is steps 3 to 6 only.
A Private Integration Token is how Ambrose proves it is allowed into your GHL account. You make it inside GHL.
- In GHL, go to Settings, Private Integrations, New Private Integration.
- Give it all the scopes Ambrose needs: Contacts, Conversations, Opportunities, Calendars, Tasks, Notes, Custom Fields, Workflows, Social Planner, Voice AI, Users.
- Create it and copy the token. GHL shows it once.
- Also copy your Location ID, from Settings, Business Profile.
Be generous with scopes. A missing scope does not produce a clear error. It produces an agent that can read contacts but mysteriously cannot move a card, and you will spend an afternoon on it. Grant them all now.
Step 2: connect GHL to Ambrose
- In Ambrose, go to Settings, Integrations, GoHighLevel.
- Paste the Location ID and the PIT.
- Save.
- Click Test. Ambrose pings the GHL contacts endpoint with a limit of 1. Green means connected.
More than one sub-account? Repeat for each. The ghl spoke federates across
all of them, so agents see one search surface, scoped per sub-account where it matters.
HIPAA. Per sub-account there is that toggle, "BAA on file with GHL for this
sub-account." On means the spoke is treated as safe for that sub-account. Off means
scrubbed, and content routes through the PHI Gateway before it reaches a model. Set it
honestly.
Conversation Provider ID. Set this in Settings, GHL. Without it, messages your agent sends will deliver to the contact but never appear in the GHL Conversations interface, which looks exactly like the agent doing nothing.
Step 3: one agent, or a team
Skip this step if one agent is enough. Most of these are a single agent, and a single agent is simpler to build, cheaper to run and easier to debug. Read the choice below, decide, and move on.
| Question | Agent | Team |
|---|---|---|
| What it is | One specialist. One persona, one job. | A coordinator with its own persona and attached agents it can hand work to. |
| Use when | The pipeline needs one consistent conversation | The work needs specialists the coordinator calls on |
| GHL webhook | Works (kind=agent) | Works (kind=team), and this surface was originally team-only |
From Ambrose's own description of the team's Agents tab: "Attach agents and this team can hand work to them by name, then use what comes back. Each agent runs with its own files, tools and skills, so you are delegating to a specialist rather than copying its prompt in here."
That last clause is the whole point. A team is not a bigger prompt, it is a dispatcher. If you would only be pasting one agent's instructions into another, you want an agent.
Where a team genuinely earns its place on a pipeline:
- The conversational agent handles the thread, and hands off to a quoting specialist for plan data
- A compliance reviewer checks anything touching coverage language before it sends
- A research agent pulls carrier or plan information the responder does not carry in its own context
You would tell it: "ask the research agent for this contact's plan history, then give that to the follow-up writer." It runs each in turn and uses what comes back.
Configuring the team
A team has the same tabs as an agent, plus one.
| Tab | What is there |
|---|---|
| Identity | The persona files, the same set as an agent |
| Agents | Team-only. The specialists this team can delegate to |
| Tools | Spokes, skills, default model |
| MCP | External MCP servers |
| Integrations | Webhook URLs, including the GHL one |
| Test, Conversations, Logs, Routines, Security | As per an agent |
Give the team's Description the same two definitions from step 4, what to collect and the conversion goal, and add a third: when to delegate, and to whom, by name. A coordinator that does not know when to hand off will just answer everything itself.
Build the specialists first, the coordinator second. You cannot attach an agent that does not exist yet.
Step 4: build the agent
4a. Create it
Ask Ambrose, or build it by hand. Either way you are filling in the same things. An agent has these tabs.
| Tab | What is there |
|---|---|
| Identity | The persona files |
| Tools | Spokes, skills, and their permissions |
| MCP | External MCP servers |
| Integrations | Your webhook URLs, including the GHL one |
| Test | Try it before it touches a real contact |
| Conversations | Every thread it has had, per contact |
| Logs | What it actually did, call by call |
4b. Write the persona
| File | What belongs in it |
|---|---|
| Description | The operating brief. Name the pipeline it manages. Highest-leverage field, because it is prepended above everything else. |
| IDENTITY.md | Who the agent is. One paragraph. |
| PERSONALITY.md | Voice and tone, including per-channel register. |
| NOTES.md | Rules. What it must never do. The hard stops. |
| FAQ.md | Answers to what your leads actually ask. |
| VOICE.md | Brand voice override, if you have one. |
| MEMORY.md | Facts it learns. Auto-updated. Never travels in a template. |
Edit a file, behavior changes. No deploy.
4c. Define the two things that actually matter
This is the whole design, and it is much simpler than it first looks. Because the workflow filter in step 5 guarantees this agent only ever sees contacts inside one pipeline, you do not write a different job description per stage. You write two things, once.
1. What to collect. The same qualification set for every contact in this pipeline. For an appointment-setting AI that might be: name, what coverage they are asking about, ZIP, age band, whether they have coverage today, best time to talk.
2. The conversion goal. The one outcome this agent is driving every conversation toward: get a specific appointment slot accepted and booked on the calendar.
That is it. Put both in the Description, and the specifics in NOTES.md.
The stage is context, not a separate job. The agent reads which stage the contact is already in and adjusts its approach, so a no-show gets a different opening line than a fresh lead, but the information it is gathering and the outcome it wants do not change. One pipeline, one job.
When you would want per-stage detail: if a stage genuinely needs a different
conversion, say Quote Sent chasing a signature rather than an appointment, write that stage its own short
brief in NOTES.md. Most pipelines need one or two of these, not thirteen.
4d. Copy-paste starting point
Anything in {curly braces} is yours to fill in. Those are the parts
that cannot be guessed for you: your pipeline name, your stage names, your tag names, and the
qualification set your line of business actually turns on. Replace the braces and the text inside them,
and delete any line that does not apply to you. Everything outside the braces works as written.
Your stage and tag names have to match what is really in your GoHighLevel account, character for character. An agent told to move a card to a stage that does not exist fails quietly, which is the whole theme of this build.
Paste this into the Description.
This agent manages the {your pipeline name} pipeline for {your agency name}.
Every contact I speak to is already in this pipeline. My job never changes:
collect the qualification set below, and drive toward the conversion goal.
WHAT I COLLECT (ask naturally, over the course of the conversation, never
interrogate, never ask more than two things in one message):
- First name
- {what they're asking about, e.g. what coverage they need}
- {location field you actually use, e.g. ZIP code}
- {qualifier your line of business turns on, e.g. age band}
- {their current situation, e.g. whether they have coverage today}
- Best time to talk
CONVERSION GOAL:
{the one outcome this agent drives at, e.g. get a specific appointment
slot accepted and booked on the calendar}
HOW I USE THE STAGE:
The contact's current pipeline stage tells me where they already are. I
adjust my opening and tone accordingly, but what I collect and what I'm
driving toward do not change.
WHEN I'M DONE:
Once {the conversion happens}, I confirm it, add a note summarizing what
the contact told me, tag the contact, and move the card to
{your won stage}. And this into NOTES.md.
# Hard rules, these override everything else
NEVER message a contact who is DND. STOP is final and permanent.
NEVER message a contact whose card is in the {your paused stage} stage.
NEVER message a contact tagged `{your pause tag}`.
HAND OFF TO A HUMAN IMMEDIATELY, do not attempt to answer, when the
contact raises any of:
- a specific price, premium, or "how much will it cost"
- what a plan does or doesn't cover
- an active claim
- a complaint, or any sign of anger or frustration
- a direct request to speak to a person
- {anything else only a licensed human should answer in your line}
When handing off: acknowledge warmly, say a human will follow up, add a note
explaining why, tag `{your handoff tag}`, and stop.
NEVER invent a fact. Only reference what is in this conversation or the
contact record. If I don't know, I say I'll find out.
NEVER send more than one message per inbound message. No double-texting.
MAXIMUM {your attempt cap, e.g. 4} follow-up attempts with no reply. After
the last one, stop and move the card to {your dead stage}. Why NOTES.md and not the Description: the Description is the brief, what the
agent is for. NOTES.md is the rulebook. Keeping hard stops separate means you can rewrite the
brief without accidentally dropping a compliance rule.
4e. Set the hard stops
In NOTES.md, as absolute rules:
- Never message a DND contact. STOP is final.
- Never message a contact in your paused stage, or carrying your pause tag.
ai-pausedis a fine name for it if you do not already have one. - Hand off to a human on pricing, coverage specifics, claims, complaints, anger, or a direct request for a person.
- Never invent facts. Only reference what is in the conversation or the contact record.
4f. One trap when changing tools by chat
If you ask Ambrose to change an agent's tools or skills, remember that an empty list means "all available", not "none". Telling it to clear the tools grants the agent everything rather than nothing. Say what should remain, not what should go.
4g. Turn on write permissions
This is the step people skip. Write actions, meaning send message, move stage and create task, default to Ask. That stages them for your approval instead of executing them, so an agent that is supposed to run unattended will appear to do nothing.
On the Tools page, flip the writes this agent needs to Allow. Keep them on Ask during your pilot, and flip them when you are ready to go live.
Step 5: wire the GHL workflow
The agent has to exist first, because the webhook URL is generated by the agent.
5a. Get the URL
Agent, then the Integrations tab, then the GHL Workflow (shortcut) card. Copy it.
POST /api/agent/{webhook_id}/webhook/ghl
That webhook_id is an 8-character permanent id and it is immutable. It is how
Ambrose proves which agent, in which agency, the request belongs to. Deleting an agent to recreate it gives
you a different URL and breaks every connector pointing at the old one.
5b. Build the workflow
- Trigger: Customer Replied. This fires for SMS, Facebook Messenger, Instagram DM and email alike, because GHL unifies them into one Conversations surface.
- Action: Send Webhook, POST, your URL.
5c. The pipeline filter, which is what scopes the agent
Add filters so the workflow only fires for contacts in this agent's pipeline:
- Opportunity is in pipeline equals your target pipeline
- Contact is not DND
- Contact is not tagged with your pause tag, for example
ai-paused - Inside business hours, if you want that
This filter is doing real architectural work. It is what lets you keep the agent's design simple, because every conversation it ever sees is inside one pipeline, so one set of instructions covers all of them. It also means the agent cannot move the wrong card, because there is only ever one card in play.
One agent per pipeline. Do not point two pipelines at the same agent.
5d. customData
| Key | Effect |
|---|---|
| instructions | The agent's job for this run. Present means it acts. Absent means it only drafts. |
| sync | true waits for the AI and returns sms and email_body. Default off. |
| auto_create_fields | true lets it create custom fields that do not exist. Default off. |
| engine | Leave unset. |
Two things catch everyone.
No instructions means it will never send. With instructions empty, the agent
runs in default mode: it reads the conversation, decides whether outreach is warranted, and
drafts an email and an SMS. Drafting is the ceiling, regardless of what the persona says.
That is a safety feature, and it is exactly what you want for your first two weeks.
The response comes back empty by default. The webhook acknowledges in under 100ms and runs
the AI afterward, so sms and email_body return as empty strings. If your workflow
uses {{webhook.sms}} in a Send SMS step, you will send blank messages. Pass
sync=true, or let the agent send directly and do not reference the response at all.
Never set engine=sdk on an agent that needs to send. It routes through the
full Agent SDK, whose GHL write tools stage for approval instead of executing, so your autonomous agent
will silently do nothing.
5e. The instruction, written out
This is the text you put in customData.instructions. It is the difference between an agent
that drafts and an agent that runs your pipeline.
Pilot version, drafts only. Leave instructions empty entirely. The agent
reads, decides, and drafts. Nothing sends. Run this for two weeks.
Live version, copy this in.
Read the conversation history for this contact.
Work out which stage their pipeline card is in and what they have already
told us. Then continue the conversation toward the conversion goal in your
operating brief.
Rules for this run:
- Send exactly one reply, on the channel they messaged from.
- If they asked a question, answer it before asking anything of your own.
- Ask at most two of your qualification questions, and only ones we don't
already have answers to.
- If you have everything you need and they are ready, offer two specific
appointment times.
- If they accept a time, book it, then move the card to {your booked stage}
and tag the contact `{your booked tag}`.
- If they say stop, are hostile, or ask for a person: do not reply, tag
`{your handoff tag}`, add a note explaining why, and end.
- Add a short internal note summarizing anything new they told us.
Do not invent facts. Do not quote prices. Do not discuss what a plan covers. Read that carefully, it is the actual product. Every line is either a guardrail or a step.
Notice what it does not do: it does not restate the persona, does not re-explain the pipeline, and
does not list the qualification questions. All of that already lives in the agent's Description and
NOTES.md. The instruction is only the job for this run.
Keep it short. A 40-line instruction competes with the persona and the skills for the model's attention, and the agent gets worse, not better. If you find yourself writing paragraphs here, that content belongs in the Description.
One instruction per workflow, not per stage. If you genuinely need different behavior for one stage, say a no-show rebooking push, build a second workflow filtered to that stage with its own instruction, pointed at the same agent. The agent's persona and memory stay shared.
5f. Memory is automatic
Ambrose keys the conversation to the contact, by contact id, falling back to email, then phone. Every webhook for the same person continues one thread. You do not build memory, pass history, or store state. Read it in the Conversations tab.
Step 6: test it
In Ambrose first. Use the Test tab. Run a scenario before it touches a real lead.
Then one real contact. Put a test contact into the pipeline, send yourself a message, and watch three places.
| Look at | To confirm |
|---|---|
| Conversations tab | What the agent said, in thread |
| Logs tab | What it actually did, tool call by tool call |
| GHL | The card moved, the tag landed, the note is there |
The Logs tab is the one that matters. An agent can write a confident summary saying it added a tag without having called the tool. Logs show the truth.
Then pilot in draft mode. Leave instructions empty for two weeks. The agent
drafts, a human sends. You will learn more from reading fifty drafts than from any amount of prompt tuning,
and you will find out what your leads actually ask.
The two pipelines, stage by stage
These are examples, not a template to copy. Your stage names are whatever they already are in your account, and you should not rename anything to match this page. Map each of your real stages onto the question in the right-hand column, because that mapping is the only part that matters. An agency running eight stages and one running twenty are doing the same thing.
The Appointment-Setting AI
Collect: name, what they are asking about, location, the qualifier your line turns on, their current situation, best time to talk. Conversion goal: a specific appointment slot accepted and booked.
A typical lead pipeline looks something like this. The stages give the agent context, not a different job.
| Stage | What it means for the agent |
|---|---|
| Interested | Fresh. Open the conversation, start collecting. |
| Contacted / Contact Request | Engaged. Push for the booking. |
| Future Sale | Interested, wrong timing. Stay warm, re-engage on a date. |
| Appointment Set | Booked. Confirm and reduce no-show risk. |
| Appointment No Show | Rebook, different tone. |
| Appointment Cancelled | Rebook. |
| Appointment Showed Up | Hand toward quote. |
| Quote Sent | Chase the close. |
| Paused | Silent. Do not message. |
| DND / Lost | Silent, permanently. |
| Won / Client | Done. |
The Customer Service AI
Collect: what they need, how urgent it is, which policy or household it concerns. Conversion goal: the current stage's next step completed, and the client still happy.
Same engine, pointed at a client pipeline instead of a lead pipeline. A typical one runs something like New Client, Application Submitted, Pending Underwriting, Policy Issued, Welcome Call, Active Client, Service Request Open, and Renewal Review.
The stages carry the same kind of meaning they do above. Pending Underwriting means chase the outstanding requirement and keep the client calm while nothing appears to be happening. Service Request Open means find out what they need and route it, never solve a coverage question. Renewal Review means get the review on the calendar before the deadline does it for you.
Every channel, one agent
You do not build a separate agent per channel.
| Channel | What to know |
|---|---|
| SMS | Short. Agent sends directly. STOP and DND obligations are absolute. |
| Messenger and Instagram | Conversational, longer. Delivery goes through the workflow's own send step, which targets the native channel. |
| Needs a subject and HTML. Plain text alone is rejected. Use it for quotes and recaps. |
Put the register rules in PERSONALITY.md: "SMS: under 160 characters where possible.
Messenger: two or three sentences. Email: full paragraphs, always a subject line."
Package it and share it
- Save it.
ambrose_template_save(kind, slug), private to your agency. - Edit the agent, not the template. The agent is the source of truth. To change a
template's content, edit the agent and call
ambrose_template_refresh(id, kind, slug). It re-snapshots in place, keeping the template id, import count and shared links, and bumps the version. - Share it.
ambrose_template_update(id, scope: 'global')requests publication to all Ambrose users. It stayspendingand invisible to others until the Ambrose team approves it.
What travels: persona markdown, tool and skill selection, bundled skills and sequences.
What never travels: conversation history, MEMORY.md, credentials, integration
keys, webhook ids, vault data.
On pushing updates: a super-admin can push an updated template over copies others already imported, overwriting a live agent in someone else's account, including their edits. The previous snapshot is stored for rollback, importers can opt out, and it never happens automatically. That is a governance decision, not a deploy step.
The eight first-build mistakes
- Skipping the write-permission flip. Writes default to Ask. The agent stages actions instead of doing them and looks broken.
- Referencing
{{webhook.sms}}and sending blank messages. Fast-ack returns empty fields. Usesync=trueor let the agent send. - Leaving
instructionsempty and wondering why nothing sends. That is draft mode working correctly. - Setting
engine=sdkfor an autonomous agent. Its writes stage for approval and nothing happens. - A stingy PIT. Missing scopes fail quietly and look like bugs.
- Writing a native field through
update_custom_fields.firstName,email,phone,dnd,dateOfBirthand the rest are native, so useupdate_contact_native. The other way creates a ghost field with the right name and leaves the real one untouched. It looks like it worked. - Clearing a list field to remove access. Empty means all available.
- No pipeline filter. The agent starts answering conversations it was never designed for.
One run, start to finish
What actually happens on one inbound message. This is the whole system in sixty seconds.
11:04pm Saturday. Dana texts your business number: "saw your ad, do you do medicare?"
- GHL receives the SMS. The Customer Replied trigger fires. The filter checks: is Dana's card in this agent's pipeline? Yes. Is she DND? No. Is she carrying the pause tag? No. So Send Webhook fires.
- Ambrose receives the POST, matches it to Dana by contact id, and finds the conversation thread it has already had with her, or starts one.
- The agent runs. It calls
read_conversation(nothing prior),read_opportunities(card is in Interested), and reads the contact record (first name Dana, no ZIP, no age). - It composes. Stage is Interested, so it opens rather than pushes. It answers her actual question first, then asks for the two most useful missing facts, but only two.
- It sends one SMS: "Hi Dana, yes, Medicare is most of what we do. Are you turning 65 soon, or already on a plan you're thinking of changing?"
- It records.
add_notewith what it learned,add_tagswithai-engaged. - It stops. No card move yet, because the conversion has not happened.
11:09pm. Dana replies: "turning 65 in march. 85023"
The whole loop runs again, on the same thread. This time the agent has age band and ZIP, and it is still in
Interested, so it moves to the ask: it offers two specific times. Dana takes Tuesday at 2. The
agent books it, moves the card to Appointment Set, tags ai-booked, and writes
a note.
Monday 9am. You open your pipeline and there is a qualified appointment on it with a note explaining exactly who Dana is and what she wants.
Total human effort: zero. Total elapsed time from her first text to a booked appointment: eleven minutes, on a Saturday night.
Troubleshooting
| Symptom | Almost always | Fix |
|---|---|---|
| Agent does nothing at all | Writes are on Ask | Tools page, flip to Allow |
| Agent drafts but never sends | instructions is empty | Add the instruction to customData |
| Blank SMS going out | Workflow reads {{webhook.sms}} under fast-ack | Set sync=true, or let the agent send |
| Agent says it tagged but nothing happened | It reported without calling the tool, or engine=sdk staged it | Check the Logs tab, remove engine=sdk |
| Messages send but never appear in GHL | No Conversation Provider ID | Settings, GHL, set it |
| Can read contacts, cannot move cards | PIT missing the Opportunities scope | Recreate the PIT with all scopes |
| Custom field updated but still blank | Wrote a native field via update_custom_fields | Use update_contact_native |
| Agent replying to the wrong people | No pipeline filter on the workflow | Add the pipeline condition |
| Wrong card moved | Contact holds cards in several pipelines | One agent per pipeline, or tag and let GHL move |
| Agent talking to itself | Its own outbound re-triggers the workflow | Filter outbound messages out of the trigger |
Read the Logs tab before you believe anything. It is the only place that shows tool calls rather than the agent's account of them.
What Ambrose can build for you
Mostly all of it, and more than you would expect. Every write below is staged: Ambrose shows a preview, you click Approve once, and it happens.
| What | Tool |
|---|---|
| Create a complete team, persona files and all | admin_team_create |
| Create a complete agent, persona files and all | admin_agent_create |
| Change spokes, skills, MCPs, integrations, model | admin_agent_update, admin_team_update |
| Add an MCP server (http, sse, stdio, oauth) | admin_mcp_add |
| Add a third-party integration, secrets included | admin_integration_create |
| Write a new skill and attach it | admin_skill_create |
| Build a sequence or a scheduled routine | admin_sequence_create, admin_claude_routine_create |
| Write the whole persona from your website | admin_generate_from_website |
| Fetch your real webhook, MCP and run URLs | admin_get_integrations |
| Save, refresh, publish and import templates | admin_template_* |
So MCP servers and integrations, the two things that feel like they must need a developer, are both things you can just ask for.
What it cannot, and who has to do it
| What | Why |
|---|---|
| Create the PIT inside GHL | It is in GHL, not Ambrose |
| Build the GHL workflow and its pipeline filter | Same |
| Connect the native GoHighLevel card | Settings, Integrations, by hand |
| Flip write permissions from Ask to Allow | Tools page, by hand |
| Attach agents to a team | See below |
The team-agents gap is worth knowing about. attached_agents is a real setting
on a team, but neither admin_team_create nor admin_team_update exposes it. So
Ambrose can build your team, write its persona, and wire its tools, but you have to open the
Agents tab and attach the specialists yourself. Otherwise you get a coordinator with
nobody to coordinate.
Ambrose will not take your API keys in chat
admin_integration_create accepts an api_key, and stores it server-side without
ever echoing it. But Ambrose is deliberately instructed not to ask you for secrets in
conversation, and points you at Settings, Integrations instead.
That is the right call, because it keeps credentials out of chat transcripts and out of the model call log. Expect the split: Ambrose builds the connection, you paste the secret.
Do not let it invent your URLs
If you ask Ambrose for a webhook URL, it should call admin_get_integrations and copy the
answer verbatim. The URLs key off the node's immutable webhook_id, not its
slug, and for agents the slug form does not work at all. A hand-built URL will look right and 404.
Questions
Do I need a developer to build this?
No. Two of the three paths need no technical work at all. You can import a finished agent from a template in three clicks, or you can describe what you want in the Ambrose chat and approve the preview it stages. The only things a person has to do by hand are inside GoHighLevel: creating the Private Integration Token, and building the workflow with its pipeline filter. Both are settings screens, not code.
Is an AI texting my Medicare leads HIPAA compliant?
It depends entirely on where the conversation goes. Ambrose runs on AWS and its AI runs on AWS Bedrock under a BAA that Strategic AI Architects holds and passes through to you, and every agency gets its own encrypted vault separated at the database level. That covers everything happening inside Ambrose. The moment you wire in an outside vendor, that vendor sits outside our BAA, and if PHI reaches it you need a BAA with that vendor, signed by you. The practical answer is that most connected tools never need PHI at all, so you configure them so they never receive it.
Why is my agent reading messages but never sending anything?
Almost always one of two things. Either the write permissions on the Tools page are still set to Ask, which stages every action for your approval instead of executing it, or customData.instructions is empty, which puts the agent in default mode where drafting is the ceiling regardless of what the persona says. Both are working as designed. Check the Logs tab, which shows the tool calls that actually happened rather than the agent's account of them.
Do I need a separate agent for SMS, Messenger, Instagram and email?
No. GoHighLevel unifies all four into one Conversations surface, and the Customer Replied trigger fires for all of them. One agent handles every channel. What you do need is register rules in PERSONALITY.md so it writes differently per channel: short for SMS, two or three sentences for Messenger, full paragraphs with a subject line for email. Email is the one hard requirement, because it needs a subject and HTML and rejects plain text alone.
Why did my agent move the wrong pipeline card?
Because the contact holds cards in more than one pipeline and nothing scoped the agent to yours. The fix is the workflow filter: require that the contact has an opportunity in this agent's pipeline before the webhook fires. That filter is doing real architectural work, because it guarantees the agent only ever sees one card and one context, which is what lets one set of instructions cover every conversation.
How long should I run it in draft mode before going live?
Two weeks. Leave instructions empty entirely and the agent reads every conversation, decides whether outreach is warranted, and drafts an email and an SMS without sending either. A human sends. You will learn more from reading fifty drafts than from any amount of prompt tuning, and you find out what your leads actually ask instead of what you assumed they ask.
What is the difference between an agent and a team?
An agent is one specialist with one persona and one job. A team is a coordinator with its own persona plus attached agents it can hand work to by name, each running with its own files, tools and skills. A team is a dispatcher, not a bigger prompt. If you would only be pasting one agent's instructions into another, you want an agent. A team earns its place when the conversational agent needs to hand off to a quoting specialist, a compliance reviewer, or a research agent that carries context the responder does not.
Does the agent remember previous conversations with the same person?
Yes, automatically. Ambrose keys the conversation to the contact by contact id, falling back to email and then phone, so every webhook for the same person continues one thread. You do not build memory, pass history, or store state anywhere. You read it back in the Conversations tab, per contact.
Talk it through
Want a second pair of eyes on it?
Free 30 minutes. Bring what you found, or bring nothing and we will look together at how AI engines read your site and which fixes move first.
Want to see what an AI can already read on your site?
A conversational agent works the pipeline you already have. Filling that pipeline is the other half, and it starts with whether an answer engine can read your pages at all. Our AI visibility audit checks exactly that, and it is free.