When to use it, when not to
Use it
- Use it when the main contact form has one free-text field and the inbox behind it mixes sales requests, support questions, vendor pitches and spam, and someone sorts that by hand every morning.
- Use it when the categories are few and stable (4 to 6). The prompt and the enumeration both get worse as the list grows.
Skip it
- Do not use it when the form already has a required "Reason for contact" dropdown. Branch on that dropdown instead, it costs nothing and never misreads.
- Do not use it for lead assignment inside the sales team by territory or size. That is lead-routing-010 territory; this spec only decides which team sees the record first.
Prerequisites
Needs Sales Hub Professional and Marketing Hub Professional.
On per-hub plans, one of its steps belongs to another subscription: add to static list needs Marketing Hub Professional. Both are required to build it as written. On seat pricing the edition covers both.
Properties to create on the contact object:
- Label
AI category, internal nameai_category, object contact, field type dropdown select, optionssales,support,partnership,spam,unclear. Why: the Data Agent output is written here, the branches read it, and reports count it. - Label
AI output at, internal nameai_output_at, object contact, field type date and time picker. Why: stamps when the AI last ran so you can see stale or missing outputs. - Label
AI review status, internal nameai_review_status, object contact, field type dropdown select, optionsnot needed,pending review,reviewed. Why: the fallback branch marks records a human must look at.
Other prerequisites:
- Sales Hub Professional or Enterprise. Every AI action in workflows needs a Professional or Enterprise subscription of Marketing Hub, Sales Hub, Service Hub, Data Hub, Smart CRM or Revenue Hub.
- HubSpot Credits in the account. Each run of
Data Agent: Custom promptconsumes credits. When the balance hits zero the action fails and its output is null, which is why step 3 branches on an empty output. Per-action credit cost is not published:*(check in your portal: how many credits one Data Agent: Custom prompt run consumes in your account, read from the credits usage page after the test plan)*. - Account settings
Give users access to generative AI toolsswitched on, plus the CRM data setting for AI. - A form with a free-text field, for example the default
Messageproperty, and the form connected to this workflow through the enrollment trigger. - A sales rep pool, a support team queue and a partnerships owner to receive the routed records.
Enrollment
| Trigger type | event-based. |
|---|---|
| Criteria | Form submission on form
Contact us (any page), filtered to Message is
known. |
| Re-enrollment | on, re-enroll on every new form submission. A contact who writes twice gets categorized twice, and the second category overwrites the first. |
| Suppression / exclusions | Lifecycle stage is none of
Customer. Customers writing in go straight to support
through tickets-sla specs, not through this categorization. |
Steps
- Set property value:
AI review status=not needed. Resets the flag before the AI runs, so a second submission does not inherit a stale review state. - Data Agent: Custom prompt. Output type: enumeration with the exact
five options of
AI category. Prompt (well under the 3,000 character limit): "You classify a website contact form message. The sender works at {{contact.company}} with the job title {{contact.jobtitle}}. The message is: {{contact.message}}. Return exactly one of: sales (wants to buy, pricing, demo), support (existing product problem), partnership (resell, integrate, co-market), spam (unrelated, promotional, nonsense), unclear (cannot tell)." The model has no internet access, so every piece of context must be a token in the prompt like this. - If/then branch: Data Agent output is known.
- No (credits exhausted, action failed, model returned nothing): Set
property value
AI review status=pending review. Create task, type To-do, assigned to the inbound owner, due in 1 business day, subject "Categorize by hand: {{contact.email}}", body "AI categorization returned no value. Read the message and set AI category manually." End of this branch. - Yes: continue to step 4.
- No (credits exhausted, action failed, model returned nothing): Set
property value
- Set property value:
AI category= the Data Agent output (Edit record, pick the action output as the value source). Set property value:AI output at= current date and time. - If/then branch on
AI category:sales: Rotate record to owner, pool = inbound sales reps. Create task, type Call, assigned to record owner, due in 1 business day, subject "New inbound sales request: {{contact.firstname}} {{contact.lastname}}".support: Set property valueContact owner= support queue owner. Send internal email notification to the support inbox, subject "Website message flagged as support: {{contact.email}}".partnership: Set property valueContact owner= partnerships manager. Send internal email notification to that manager.spam: Set property valueLifecycle stage=Other. No notification. Done.unclear: Set property valueAI review status=pending review. Create task, type To-do, assigned to the inbound owner, due in 1 business day, subject "Unclear website message: {{contact.email}}".
Known pitfalls
- The enumeration output and the dropdown property must carry the same
option values, spelled the same way. One typo in
partnershipand the Edit record step writes nothing, which looks exactly like an AI failure. - The output must be defined before the action saves. If the editor refuses to save the Data Agent step, the output configuration is incomplete, not the prompt.
- Re-enrollment on every submission means every submission spends
credits. A contact who submits the same form five times costs five runs.
Add a suppression on
Number of form submissionsgreater than 10 if bots hit the form. - A null output and a genuine
unclearare two different things. The first means the AI did not run, the second means it ran and could not decide. Keep both paths, they tell you different things about the setup. - Spam classification by the model is a guess. Review the
spambucket weekly for the first month before trusting it to hide records from the team. Rotate record to owneronly rotates among users who are active and in the chosen team. A rep on leave still receives records unless removed from the team.
Test plan
- In the test portal, submit the form with a message that reads like a pricing request. Confirm the contact enrolls (Automation > Workflows > this workflow > History).
- Open the contact record:
AI category=sales,AI output atstamped,Contact ownerset to a pool member, a Call task created. - Submit again from the same contact with a support-type message.
Confirm re-enrollment and that
AI categorynow readssupport. - Submit with nonsense text and confirm
AI category=spamandLifecycle stage=Other. - Force the failure path: temporarily switch the generative AI setting
off, submit once more, and confirm the task "Categorize by hand" appears
with
AI review status=pending review. Switch the setting back on.
The UAT script of this spec holds 9 cases: a negative case, one case per branch, the expected result of each and a sign-off sheet. It comes as Markdown and as a CSV for your spreadsheet.
Variants
- Ticket object: run the same prompt on new tickets from the support
form and set
Categoryinstead of routing contacts; see ai-agents-012 for the sentiment and urgency version. - Marketing Hub Professional portal: replace the sales rotation with
Add to static listper category and let the marketing team work the lists. - Wait until: instead of acting on the first output, add a
Wait untilstep onAI review statusisreviewedwith a 2 business day expiry for theunclearbranch, then route on the human-correctedAI category.
Build spec
trigger:
type: event-based
object: contact
event: form_submission
form: "Contact us"
criteria:
- message: is_known
- lifecyclestage: is_none_of ["customer"]
reenrollment: true
reenrollment_event: form_submission
actions:
- type: set_property_value
property: ai_review_status
value: not_needed
- type: data_agent_custom_prompt
installable: false
manual_step: "Add the AI action Data Agent: Custom prompt. Output type enumeration with options sales, support, partnership, spam, unclear. Paste the prompt from step 2 with the contact tokens."
output_type: enumeration
output_options: [sales, support, partnership, spam, unclear]
- type: if_then_branch
condition: data_agent_output_is_known
if_false:
- type: set_property_value
property: ai_review_status
value: pending_review
- type: create_task
task_type: todo
title: "Categorize by hand: {{contact.email}}"
due: +1_business_day
assigned_to: inbound_owner
if_true:
- type: set_property_value
property: ai_category
value: "{{action_output.category}}"
- type: set_property_value
property: ai_output_at
value: current_datetime
- type: if_then_branch
condition: ai_category
branches:
sales:
- type: rotate_record_to_owner
pool: "Inbound sales reps"
- type: create_task
task_type: call
title: "New inbound sales request: {{contact.firstname}} {{contact.lastname}}"
due: +1_business_day
assigned_to: record_owner
support:
- type: set_property_value
property: hubspot_owner_id
value: support_queue_owner
- type: send_internal_email_notification
to: support_inbox
subject: "Website message flagged as support: {{contact.email}}"
partnership:
- type: set_property_value
property: hubspot_owner_id
value: partnerships_manager
- type: send_internal_email_notification
to: partnerships_manager
subject: "Partnership request: {{contact.email}}"
spam:
- type: set_property_value
property: lifecyclestage
value: other
unclear:
- type: set_property_value
property: ai_review_status
value: pending_review
- type: create_task
task_type: todo
title: "Unclear website message: {{contact.email}}"
due: +1_business_day
assigned_to: inbound_ownerFiles and install
| File | What it holds |
|---|---|
| PDF and Markdown | The spec as you read it here, with the YAML build spec at the end. |
| Properties JSON | Creates the fields in one API call. |
| Workflow JSON | Documents the build: the workflow in Automation API v4 format, switched off. |
| UAT script | The test cases, the expected results and a sign-off sheet, as Markdown and CSV. |
Take the PDF and build it by hand. One-click install comes with a category pack or the full library.
Install ai-agents-001
One email, and we tell you where this stands. When free installs open, that address is how you hear it first.
No card, no password, unsubscribe in one click.
Nothing deleted, enabled or enrolled.
ai-agents-001Spec version 2026-10