> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.graze.social/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Editor Documentation

This page lists every node you can add in the Graze editor, in the order they appear, with every option and the things that commonly trip people up. If you're new to the editor, start with [Nodes for Beginners](https://www.graze.social/docs/nodes-for-beginners) and come back here when you need the detail.

## How the editor is organised

Every feed is one **Feed** block. It can't be deleted, and it has two sections:

- **Page** (top, the layers icon) decides how each page a reader sees is put together. See [Page Layers and Audiences](https://www.graze.social/docs/layers).
- **Rules** (below, the filter icon) decide which posts are allowed into the feed at all. Everything on this page is about Rules.

The **Rules** section holds exactly one node. That node is the only filter applied to your feed, so in practice it's almost always a **Logic** block set to **All of these**, with your other nodes inside it. If you drag a second node into Rules, it's pushed back out.

The left sidebar has two buttons for adding things:

- **Rules** (the filter icon) opens the node palette, with four tabs: **Basic**, **AI**, **Markup** and **Custom**.
- **Page layers** (the layers icon) opens the layers and audience blocks. These are covered on the [layers page](https://www.graze.social/docs/layers).

To add a node, pick it from the palette, then click the canvas. Drag it into the Feed's Rules section, or into a Logic block. A node sitting loose on the canvas looks faded and does nothing until you do.

Hover the ⓘ on any node's header for the editor's own help text. To check what your rules do with a real post, use [Debug](https://www.graze.social/docs/debugging-a-post).

## Comparators

Most nodes compare something against a value. The editor shows these comparators in plain words:

- **at least**: greater than or equal to (`>=`)
- **at most**: less than or equal to (`<=`)
- **above**: strictly greater than (`>`)
- **below**: strictly less than (`<`)
- **is**: exactly equal (`==`)
- **not**: not equal (`!=`)
- **before** and **after**: for dates
- **newer than seconds** and **older than seconds**: for dates, measured back from the moment the post is checked

AI nodes only offer **at least** and **at most**, with a threshold shown as a percentage.

## Basic tab: Attribute

### Word List

Matches or excludes a post based on a list of words. If any word on the list is found, the node is active.

- **Field**: which text to search. Only text fields are offered, such as **Text**, **Alt Text**, **Link Card Title**, **User Bio** or **Quoted Post Text** (see the full list under Attribute Comparison).
- **contains** or **missing**: include posts with any of the words, or only posts with none of them.
- The word list: one word or phrase per row. Use **Add Another** for more.
- The gear menu (**Settings**): **Sort Alphabetically**, **Case Sensitive** (off by default), **Regex Match** (off by default), and **Match terms as**: **Whole words** (the default) or **Anywhere in the text**.

With **Whole words**, a term can't touch a letter, number or hyphen on either side, so "art" doesn't match "cartoon" or "pixel-art". With **Anywhere in the text**, "art" matches "cartoon" too.

The node's title changes to show its settings, for example **Case Sensitive Word List** or **Regex List**.

> Wildcards like `foot*` don't work unless **Regex Match** is on. Without it, the `*` is matched literally and the term can never match. The node warns you and offers a **Use Regex Match** button that converts the list safely.

### Entity List

Includes or excludes posts based on structured things attached to them, rather than words in the text.

Pick **Include** or **Exclude**, then an entity type, then add your values one per row:

- **Hashtags**: type the tag with or without the `#`.
- **Languages**: pick from the list. This uses the language tag the posting app put on the post.
- **URLs**: full links, such as `https://example.com/blah`.
- **Mentions**: handles of people mentioned, such as `username.bsky.social`. You can also paste a Bluesky list or starter pack URL, and the post matches if it mentions anyone in it.
- **Domains**: link domains, such as `example.com`.
- **Post Self Labels**: the adult-content labels an author can put on their own post: porn, sexual, nudity or graphic-media.

A post matches if any of your values is present.

### Embed

Matches or excludes posts by what's embedded in them.

- **contains** or **missing**.
- The embed type: **Link**, **Quote Post**, **Image**, **Gallery**, **Image Group**, **Gif** or **Video**.

**Image** matches both regular image posts and galleries, so older feeds keep working. Choose **Gallery** to target galleries only. Images attached to a quote post count too.

### Post Type

Matches or excludes replies and quote posts.

- **is** or **is not**.
- **reply** or **quote**.

**is not reply** is the simplest way to keep replies out of a feed.

### Regex

Matches or excludes a single regular expression in one field. Use it when a Word List can't express what you need, such as a partial word.

- The case toggle: **No case sensitivity** (the default) or **Case sensitive**.
- **Field**: any text field, as for Word List.
- **contains** or **missing**.
- The pattern.

The title reads **Case Sensitive Regex** when case sensitivity is on.

### Attribute Comparison

Compares one field of the post (or its author) against a value you type.

- **Field**: pick from the list below.
- A comparator. Which ones you get depends on the field: text fields offer **is** and **not**; number fields offer **at least**, **at most**, **above**, **below**, **is** and **not**; date fields offer **before**, **after**, **newer than seconds**, **older than seconds**, **is** and **not**.
- **Test value**: the value to compare against. Leave it empty to test for "nothing there".

For text fields, **is** means the whole field must equal your value exactly. To look for a word inside text, use a Word List instead.

The fields, grouped:

- **The post**: Text, Post Created Date, Post Arrival Date, Reply, Embed, Language, Post Self Labels, Facets Features URI.
- **Links and media**: Link Card URL, Link Card Title, Link Card Description, Link Media URL, Link Media Title, Link Media Description, Video Alt Text, Alt Text, Media Alt Text, Internal Bluesky URL.
- **The author**: User DID, User Handle, User Display Name, User Bio, User Followers, User Following, User Post Count, User Created Date, Account Age (days), User Labels, User Verification, User Chat Settings.
- **People mentioned**: Mentioned User Handles, Mentioned User Names, Mentioned User Followers, Mentioned User Bios, Mentioned User Created Date.
- **A quoted post**: Quoted Author Handle, Quoted Author Name, Quoted Author Verified, Quoted Post Text, Quoted Post Likes, Quoted Post Replies, Quoted Post Reposts.
- **The post being replied to**: Parent Author Handle, Parent Author Name, Parent Post Text, Parent Post Likes.
- **The top of the thread**: Thread Root Author, Thread Root Text, Thread Root Likes.

Some fields are easy to mix up:

- **Post Created Date** is when the posting app says the post was written. **Post Arrival Date** is when it actually reached the network, and is the time the Bluesky app shows. They usually agree, but bridged accounts (anything ending `.brid.gy`) are often hours apart. For anything time-based, use **Post Arrival Date**.
- **Post Self Labels** are labels the author put on this one post. **User Labels** are labels on the account, such as `bot` or `bridged-from-bridgy-fed-activitypub`. To keep bot accounts out, use **User Labels**.
- **Account Age (days)** is how old the account was when the post arrived, with fractions (six hours is 0.25). It doesn't change as the post gets older.

For date fields, **before** and **after** take a date such as `2026-10-01`. **newer than seconds** and **older than seconds** take a number of seconds: **User Created Date** **newer than seconds** `604800` matches accounts less than a week old.

### Ratio Comparison

Divides one number about the author by another and compares the result, for example "at least twice as many followers as accounts followed". Nothing matches when the bottom number is zero. See [Ratio Comparison](https://www.graze.social/docs/ratio-comparison) for the details.

### Time of Day

Matches posts made between two hours of the day, on any day. Use it for things like "posts made in the evening".

- **Field**: the timestamp to read. New nodes use **Post Arrival Date**, which is almost always what you want.
- **in**: the timezone, as a fixed offset from UTC (for example UTC-05:00 or UTC+05:30). This is your audience's timezone, not the poster's.
- **between** and **and**: the start hour and the end hour. New nodes start at 6pm to 9pm.
- **Add another window** for more windows. A post matches if it falls in any of them.

Things to know:

- The end hour is not included, so 6pm to 9pm covers the hours 6, 7 and 8.
- A window can cross midnight: 10pm to 2am is four hours.
- The offset is fixed and does not shift for daylight saving. If your audience changes clocks, you'll need to update the node when they do.
- A post whose timestamp can't be read is excluded.

### Date Range

Matches posts made within one or more date ranges. Put it inside a Logic block to date-limit part of a feed rather than the whole thing.

- **Field** and **in** (timezone): as for Time of Day.
- Start and end dates, each with an optional time. An empty time means midnight.
- **Add another range** for more. A post matches if it falls in any of them.
- **Paste a schedule**: paste one window per line, such as `2026-10-22 7:00pm to 10:30pm`, or a date on its own for the whole day. Choose a named timezone under **Times are in** and each time keeps the right offset across a daylight-saving change, which makes this the easiest way to enter a season of games.

The end date is not included: to cover all of the 2nd, end on the 3rd. The timezone decides where a day begins.

## Basic tab: List

### Member of List

Includes or excludes posts by people on a Bluesky list.

- **Include** or **Exclude**.
- **List Member URL**: paste the list's link, or use the search button next to it to find one.

The node shows a preview of the list once the link is recognised.

### Social Graph

Includes or excludes posts based on who someone follows, or who follows them.

- **Include** or **Exclude**.
- **Username**: the account whose follows or followers you want.
- **follows** (accounts this user follows) or **followers** (accounts that follow this user).

Including your own **follows** is a quick way to build a feed of people you've chosen, and including an account's **followers** lets people join your feed just by following it.

### Starter Pack

Includes or excludes posts by people in a Bluesky starter pack.

- **Include** or **Exclude**.
- **Starter Pack URL**: paste the link, or search for a starter pack with the button next to it.

### Users

Includes or excludes posts by specific accounts.

- **Include** or **Exclude**.
- **User DID or handle**: one account per row. You can type a handle (with or without the `@`) or a DID. Use **Add Another** for more.

> With **Include**, inside an **All of these** block, only posts by these accounts can match. The node warns you about this. If you meant "also show posts from these people", put the Users node in an **Any of these** block instead.

## Basic tab: Logic

The **Logic** node combines other nodes. Drag nodes inside it, and pick:

- **All of these**: a post must pass every node inside.
- **Any of these**: a post must pass at least one node inside.

Logic blocks can be nested as deeply as you like, which is how you build rules like "about LEGO, and (an image or a video), and not a reply".

## Basic tab: Custom

When you're editing a custom node, the Basic tab also has a **Custom** section with **Param Compare** (compare one of the node's parameters to a value) and **Each** (repeat a condition for every item in a list parameter). See [Using Custom Nodes](https://www.graze.social/docs/using-custom-nodes) and the [Custom Node API](https://www.graze.social/docs/custom-node-api).

## AI tab

AI nodes score each post with a model. For most of them you pick a category, a comparator (**at least** or **at most**) and a threshold from 0% to 100%. The editor's own guide to thresholds:

- **30%**: somewhat likely
- **70%**: pretty likely
- **90%**: very likely

[Using AI Nodes](https://www.graze.social/docs/using-ai-nodes) has advice on choosing between them and picking thresholds.

### Natural Language

Scores each post against a statement you write in plain English, such as "is a job posting and does not mention internships". New nodes start at **at least** 75%. This node is marked **metered**: each feed has an hourly budget for it. See [Natural Language and Ask a Question](https://www.graze.social/docs/natural-language-and-ask-a-question) and [Rate Limits](https://www.graze.social/docs/rate-limits).

### Ask a Question

Asks a question about each post, as **Yes / no**, **Pick one of my options** or **Rate on my scale**. Also **metered**. See [Natural Language and Ask a Question](https://www.graze.social/docs/natural-language-and-ask-a-question).

### Content Moderation

Scores a post's text against moderation categories. English only. New nodes start at **OK** **at least** 90%.

The categories:

- **sexual**: content meant to arouse, or that promotes sexual services (not sex education).
- **hate**: hate based on race, gender, ethnicity, religion, nationality, sexual orientation, disability or caste.
- **violence**: promotes or glorifies violence or others' suffering.
- **harassment**: content that may be used to torment or annoy people.
- **self-harm**: promotes, encourages or depicts self-harm.
- **sexual/minors**: sexual content involving anyone under 18.
- **hate/threatening**: hateful content that also threatens violence or serious harm.
- **violence/graphic**: death, violence or serious injury in graphic detail.
- **OK**: not offensive.

### Image Moderation

Scores a post's images as **NSFW** or **SFW**. New nodes start at **SFW** **at least** 50%.

### Text Categorization

Scores a post's text against one category from a fixed list of 25. New nodes start with no category and **at least** 50%.

The categories: Academic & Intellectual, Adult & Sexual Content, AI & Machine Learning, Animals, Arts & Creative, Aviation & Maritime, Data & Computing, Entertainment & Culture, Film & TV, Food & Beverages, Food & Lifestyle, Game Development, Gaming, Healthcare & Medicine, Medical Education, Medical Specialties, Music, Nature & Outdoors, News & Media, Politics, Programming, Society & Culture, Software Development, Sports, Visual Arts.

Older versions of this node let you type any label. That no longer works: a node with a label that isn't on the list pulses and asks you to "choose a standard category".

### Image Categorization

The same 25 categories as Text Categorization, scored against the post's images.

### Spam Analysis

Scores a post as **Marketing Spam** or **Organic Content**. New nodes start at **Marketing Spam** **at least** 50%, which keeps only spammy posts. To keep spam out instead, switch to **at most**, or use **Organic Content** **at least**.

### Topic Analysis

Scores a post's text against one of 19 topics: Arts & Culture, Business & Entrepreneurs, Celebrity & Pop Culture, Diaries & Daily Life, Family, Fashion & Style, Film, TV & Video, Fitness & Health, Food & Dining, Gaming, Learning & Educational, Music, News & Social Concern, Other Hobbies, Relationships, Science & Technology, Sports, Travel & Adventure, Youth & Student Life.

### Language Analysis

Detects the language of a post's text with AI, for posts whose language tag is missing or wrong. It covers 20 languages: Arabic, Bulgarian, Chinese, Dutch, English, French, German, Greek, Hindi, Italian, Japanese, Polish, Portuguese, Russian, Spanish, Swahili, Thai, Turkish, Urdu, Vietnamese. New nodes start at **English** **at least** 90%.

If the posting app's language tag is good enough, an Entity List set to **Languages** is simpler and covers more languages.

### Sentiment Analysis

Scores the tone of a post as **Positive**, **Negative** or **Neutral**.

### Toxicity Analysis

Scores a post for **Toxic**, **Severe Toxicity**, **Obscene**, **Threat**, **Insult** or **Identity Hate**. New nodes start at **Toxic** **at most** 50%, which keeps toxic posts out.

### Emotion Analysis

Scores a post for one of 28 emotions: Admiration, Amusement, Anger, Annoyance, Approval, Caring, Confusion, Curiosity, Desire, Disappointment, Disapproval, Disgust, Embarrassment, Excitement, Fear, Gratitude, Grief, Joy, Love, Nervousness, Optimism, Pride, Realization, Relief, Remorse, Sadness, Surprise, Neutral.

### Financial Sentiment Analysis

Scores the tone of a post about financial topics as **Positive**, **Negative** or **Neutral**.

### Text Similarity

Compares a field of the post with a piece of text you write, and scores how similar they are in meaning.

- The text to compare to (up to 250 tokens, roughly a short paragraph).
- **Field**: any text field. New nodes use **Text**.
- **at least** or **at most**, and a threshold. New nodes start at **at least** 75%.

### Model Probability

Some older feeds contain a **Model Probability** node. It's retired and can no longer be added. If one of your feeds still has it, remove it: a feed whose rules contain it stops matching posts.

> Image nodes (Image Moderation and Image Categorization) never match a post that has no images, whichever comparator you use. If you use one to keep unsafe images out, text-only posts are dropped too. To keep them, put the image node in an **Any of these** block next to an **Embed** node set to **missing** **Image**.

## Markup tab

Markup items document your canvas. They never affect which posts match.

- **Markdown Note**: a note with formatting, for explaining your feed's design.
- **Text**: plain text on the canvas.
- **Arrow**: point from one thing to another.
- **Sticky Note**: a quick sticky note (shortcut `n`).

## Custom tab

The **Custom** tab lists custom nodes: reusable groups of rules that you or others have published. Use **Search custom nodes...** to find one by name, and click it, then the canvas, to add it. See [Using Custom Nodes](https://www.graze.social/docs/using-custom-nodes).

## Legacy nodes

**Magic Audience** is a legacy node. Older feeds may still contain it, but it can't be added any more.

## Editing rules as JSON

The **Editor type** button at the top of the sidebar switches between **Canvas** and **JSON**. In JSON, your rules sit under `manifest` → `filter`, as a tree of `and` and `or` lists. Each node is an object with one key, whose value is a list of its settings. For example:

```json
{
  "and": [
    {"regex_any": ["text", ["lego", "duplo"], true, false]},
    {"post_type": ["not_in", ["reply"]]},
    {"or": [
      {"embed_type": ["==", "image"]},
      {"embed_type": ["==", "video"]}
    ]},
    {"content_moderation": ["OK", ">=", 0.9]}
  ]
}
```

This reads: mentions "lego" or "duplo" (case-insensitive), isn't a reply, has an image or a video, and is at least 90% likely to be OK. AI thresholds are written as decimals between 0 and 1, so 90% is `0.9`.

The easiest way to learn the format for any node is to build it on the canvas and switch to **JSON** to see what it produced.