Skip to content

Categorize ​

Classifies the email into the categories you define and routes it down the matching branch. Each category is one output of the node.

Categorize is a router: each category you declare draws its own output on the canvas, and the model decides which one the email takes. Use it to triage incoming mail (quotes, invoices, complaints, newsletters) and link a different branch behind each category.

Choose Extract when you need values out of the email rather than a decision, and Summarize when you need a short text. When the routing rule is deterministic (a sender, a word in the subject), a Switch or a Condition (If) costs nothing and never hesitates.

At a glance ​

  • Type: ai.categorize · version 1
  • Category: AI
  • Kind: Step — one stage of a run
  • Effect: No external effect (none) — nothing is written outside Mankomail; safe to replay
  • Needs a carrier email: No
  • Connection: None
  • Inputs: main
  • Outputs: Computed from the parameters (with the default parameters: other)
  • Service ports: model (llm.model, optional)

Parameters ​

categories ​

Categories — Each category names one output of the node. The description is read by the model: it is what makes the classification good.

  • Type: List of items (collection)
  • Required: Yes
  • 1 to 20 items
  • Each item has:
    • name — Name
      • Type: Text (string)
      • Required: Yes
      • 120 characters at most
      • Example: Quote request
      • Expressions: {{ }} not accepted
    • description — Description. What belongs in this category, and what does not.
      • Type: Long text (text)
      • Required: No
      • Default: "" (empty)
      • 1000 characters at most

prompt ​

System instructions — The general instructions given to the model. The shipped default is in English — the language models are most reliable in — but you can rewrite it in your own: the answer follows the email, not the instructions. The category list, the output format and the safety rules are appended automatically.

  • Type: Long text (text)
  • Required: Yes
  • 5000 characters at most
  • Shown under “Advanced” in the editor
  • Expressions: {{ }} not accepted
    Default value
    text
    You are an email triage assistant for a company.
    Classify the email into the categories below, using only its content and the description of each category.
    
    Rules:
    - Rely on the actual intent of the message, not on the presence of any particular word.
    - Use only the categories provided: never invent one, never rename one.
    - When in serious doubt, prefer the fallback over an approximate classification.

inputTemplate ​

Content to process — What the model sees of the email. Subject and text body by default.

  • Type: Long text (text)
  • Required: Yes
  • 10000 characters at most
  • Shown under “Advanced” in the editor
  • Expressions: {{ }} accepted
    Default value
    text
    {{ email.subject }}
    
    {{ email.bodyText }}

multiLabel ​

Multiple categories — The email exits through the first true category, but every recognised category is available in the working data (categories).

  • Type: Yes / no (boolean)
  • Required: No
  • Default: false

fallback ​

When no category fits

  • Type: One choice (options)
  • Required: Yes
  • Default: other
  • Options:
    • other — Exit through "Other": An extra branch receives the unclassified emails.
    • discard — Stop cleanly: The execution ends without error: nothing flows downstream.

Outputs ​

Computed from the parameters.

  • other — Present only when the fallback is set to exit through "Other". Taken when the model marked no category as true.
  • cat:<category> — One port per declared category, named cat: followed by the category name exactly as typed (for example cat:Invoices). The email leaves through the first category the model marked as true, in the order the categories are declared.

Data produced ​

What this node adds to the run data, and how to read it in an expression. <step> stands for the step key: the node name turned into an identifier (see Data and expressions).

  • {{ data.<step>.category }} — string or null. The name of the category the email left through (the first true category in declaration order). null when no category fits.
  • {{ data.<step>.categories.<category> }} — object of booleans. One boolean per declared category, keyed by the category name. Holds every recognised category, which is what makes multi-label useful.
  • {{ data.<step>.matched }} — array of string. The names of every category marked as true, in declaration order. Empty when no category fits.
  • {{ data.<step>.multiLabel }} — boolean. The value of the Multiple categories setting used for this run.
  • {{ data.<step>.discarded }} — boolean. true when no category fit and the fallback is set to stop cleanly: the branch ended without error.
  • {{ data.<step>.simulated }} — boolean. true when the output was fabricated instead of coming from a model (test run on an instance with no usable AI provider). false for a real model answer.

Example ​

A shared sales mailbox receives quote requests, invoices and everything else. Add a Categorize node named Sort with these categories:

categories:
  - name: Quote
    description: A prospect or customer asks for a price, a quote or an estimate. Not an invoice we must pay.
  - name: Invoice
    description: A supplier sends an invoice, a payment reminder or a credit note.
fallback: other
multiLabel: false

The node shows three outputs: cat:Quote, cat:Invoice and other. For an email asking "Could you send us a price for 200 units?", the email leaves through cat:Quote and the step data reads:

json
{
  "category": "Quote",
  "categories": { "Quote": true, "Invoice": false },
  "matched": ["Quote"],
  "multiLabel": false,
  "discarded": false,
  "simulated": false
}

Downstream, {{ data.sort.category }} gives Quote, for instance to fill a table column or a notification.

How the model is chosen ​

The node has a model service port. Leave it empty and the call uses the default model the administrator set for the Classify use on the Connections page (Artificial intelligence section) (or the instance default when that use has none). Link a provider node such as Anthropic (Claude), OpenAI (GPT) or Mistral AI to the model port to run this node on that provider, with its connection and model. Only one provider can be linked to the port.

Prompt and safety ​

The System prompt field holds the general instructions. It is prefilled with an English default and can be rewritten in any language. It is not templatable: {{ }} expressions are not allowed there. The node appends the rules (one category or several, what to do when nothing fits, the output format) and the safety instructions itself.

The email content, as rendered by Content to process, is never placed in the system message. It is sent as a separate user message, inside a delimited block, with an explicit instruction to treat it as data and ignore any instruction it contains. Delimiter tags found inside the email are neutralised so an email cannot close its own block. The category list and descriptions are sent on the user side too, because descriptions are templatable and may carry third-party content.

The model answers with one required boolean per category (plus one for the fallback), so it cannot invent a category: unknown keys are ignored. Category descriptions can be written in any language; the model matches them to the email by meaning.

Tips ​

  • Multi-label. With Multiple categories ticked, several categories can be true at once, but the email still leaves through a single port: the first true category in declaration order. Read the others from {{ data.<step>.categories.<category> }} or {{ data.<step>.matched }} in a condition downstream.
  • Stop cleanly. With the fallback set to stop cleanly, there is no other port. When nothing fits, nothing runs downstream and the run ends without error; discarded is true.
  • Category names in expressions. Expression paths only accept segments made of letters, digits, _ and $. A category named Invoice is readable as {{ data.sort.categories.Invoice }}, but a name with spaces or accents (Quote request) is not reachable that way. The port name and category keep the name exactly as typed in every case.
  • Unique names. Two categories with the same name, or an empty name, are refused when the workflow is validated. At most 20 categories are read.
  • Order matters. Put the most specific categories first: in multi-label mode, the first true one wins the routing.
  • Long emails. The content sent to the model is cut at 20,000 characters, and the model is told the text was truncated.
  • Test runs. In test runs the model is really called (and its usage recorded), so you see the category it actually picks. If the instance has no usable AI provider, the output is fabricated instead (the first category is true), simulated is true and the run detail says the AI was skipped.
  • Errors. No usable category gives node_invalid_param; empty content to process gives node_nothing_to_do. A model answer that is not a JSON object gives llm_invalid_output, which is retried. See Error handling.