{
  "info": {
    "name": "Commune: Tag a reader the moment they act",
    "description": "A segment you maintain by hand is out of date the day after you build it. Commune can watch for the thing that puts somebody in a group, move the tag the moment it happens, and then say something to them a day or two later, so the segment and the follow-up are the same piece of work.\n\nThe walkthrough: https://usecommune.dev/use-cases/tag-readers-on-an-event\n\nSet the `apiKey` variable to a key from https://usecommune.com/settings/api-keys, then run the requests in order.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://api.usecommune.com"
    },
    {
      "key": "apiKey",
      "value": ""
    },
    {
      "key": "version",
      "value": "2026-08-26"
    },
    {
      "key": "newsletter",
      "value": "example-letter"
    },
    {
      "key": "automation",
      "value": "2d4f6a81-3b5c-4d7e-8f90-a1b2c3d4e5f6"
    }
  ],
  "item": [
    {
      "name": "1. Make the tag the automation will apply",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          },
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          },
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/newsletters/{{newsletter}}/tags",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "newsletters",
            "{{newsletter}}",
            "tags"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"name\": \"Going quiet\"\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "A tag step names a tag by `id`, so the tag has to exist first. A step naming a tag that has been deleted is refused: it would do nothing, every run, forever.\n\nThe name has to be unique among the newsletter's live tags, so creating one that already exists answers `409` rather than quietly handing you the existing one. `known_subscriber_count` starts at zero and is what you will watch.\n\nIt is a tag like any other: you can see it in Commune, apply it by hand, and send an article to it. The automation is just another thing that moves it."
      }
    },
    {
      "name": "2. Write it, filtered to the readers you actually mean",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          },
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          },
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/newsletters/{{newsletter}}/automations",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "newsletters",
            "{{newsletter}}",
            "automations"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"name\": \"Reach out when a reader cools off\",\n  \"trigger\": \"subscriber.status_changed\",\n  \"trigger_filter\": {\n    \"properties\": {\n      \"direction\": {\n        \"const\": \"demoted\"\n      }\n    },\n    \"required\": [\n      \"direction\"\n    ]\n  },\n  \"reentry\": true,\n  \"steps\": [\n    {\n      \"id\": \"mark-quiet\",\n      \"type\": \"add_tag\",\n      \"tag\": \"cc33dd44-ee55-4f66-8a77-b8c9d0e1f203\",\n      \"next\": \"settle\"\n    },\n    {\n      \"id\": \"settle\",\n      \"type\": \"wait\",\n      \"duration\": \"P2D\",\n      \"next\": \"reach-out\"\n    },\n    {\n      \"id\": \"reach-out\",\n      \"type\": \"send_email\",\n      \"email\": {\n        \"subject\": \"Still want this, {{ subscriber.first_name }}?\",\n        \"preview_text\": \"One line back is enough.\",\n        \"body_markdown\": \"# Still want this?\\n\\nYou used to read most of these and have not opened one in a while, which\\nis fine. If the letter has drifted away from what you signed up for, tell\\nme. A one-line reply is worth more to me than any survey.\\n\\n[Unsubscribe]({{ unsubscribe_url }}) · {{ address }}\\n\"\n      }\n    }\n  ]\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "The trigger is a reader crossing a boundary in the newsletter's engagement ladder. That event fires in **both** directions, so an automation on it with no filter would send this email to somebody who had just started reading everything.\n\n`trigger_filter` is what stops that. It is a JSON Schema document in the same closed subset a `condition` step uses, and it decides whether an event starts a run at all. The paths in it are the event payload's own, not the three-branch context a `condition` is evaluated against, because a filter runs before there is a run to have a context. `direction` is `promoted` or `demoted`, and this one keeps the demotions.\n\n`required` is doing real work there. An absent property says nothing in JSON Schema, so `properties` on its own would also match a payload carrying no `direction` at all.\n\nThe graph is three steps wired one to the next, and the last one wires nothing, which is what ends the run. `mark-quiet` moves the tag on the subscriber the run is about. `settle` parks the run for two days, written as an ISO 8601 duration: there is no time-of-day field, because Commune does not know a subscriber's time zone and a time of day would be interpreted in UTC. `reach-out` sends the email, with the unsubscribe link and `{{ address }}` that Commune refuses to send without.\n\n`reentry: true` because this is not a welcome. A reader can cool off, come back, and cool off again two years later, and the note is worth sending both times. The default is `false`, which means once ever."
      }
    },
    {
      "name": "3. Turn it on",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          },
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/automations/{{automation}}/activate",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "automations",
            "{{automation}}",
            "activate"
          ]
        },
        "description": "Read a test copy of `reach-out` first. The step is the same one Welcome every new reader walks through, and the reason is the same: there is no moment at which anybody watches an automation email go out, so without a test copy the first person to see it rendered is a reader you are already at risk of losing.\n\nEvery gate runs again here on the stored automation: the graph, the filter, the email's footer, every image in it, and the newsletter's own ability to send. Each is a `422` naming what is wrong. The newsletter checks apply only because this graph sends; an automation that only moved the tag would be turned on without them.\n\n**It reacts to the next crossing, not to the backlog.** Readers who were already cooling off before you turned this on are not tagged retrospectively. If you want to reach them once, that is a list and a one-off email rather than a standing instruction, and Win back readers is that page."
      }
    },
    {
      "name": "4. Ask who is in the group",
      "request": {
        "method": "GET",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/newsletters/{{newsletter}}/subscribers?tag=cc33dd44-ee55-4f66-8a77-b8c9d0e1f203&status=subscribed&limit=100",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "newsletters",
            "{{newsletter}}",
            "subscribers"
          ],
          "query": [
            {
              "key": "tag",
              "value": "cc33dd44-ee55-4f66-8a77-b8c9d0e1f203"
            },
            {
              "key": "status",
              "value": "subscribed"
            },
            {
              "key": "limit",
              "value": "100"
            }
          ]
        },
        "description": "The tag is an ordinary tag, so this is the ordinary question. `?tag=` takes the tag's `id` and `?status=subscribed` keeps the people the newsletter can still reach, which is the filter you want on a group defined by having gone quiet: an unsubscribe does not remove somebody's tags.\n\nThis needs `audience: read` on top of the sending permission, because it returns email addresses. The automation's own runs do not: a run is read under `sending` and carries no address at all, which is why there are two permissions on this page rather than one.\n\nTo see where each of those readers is in the sequence instead, list the automation's runs. `?status=waiting` is the readers sitting on the two-day wait, which is the group that has been tagged and not yet written to."
      }
    },
    {
      "name": "5. Stop it without losing anybody's place",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          },
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/automations/{{automation}}/pause",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "automations",
            "{{automation}}",
            "pause"
          ]
        },
        "description": "A pause holds every waiting run exactly where it is. A reader one day into the two-day wait stays on that step and resumes from it when you turn the automation back on, so there is no window in which a paused automation still sends and no drain to wait out.\n\n**That is the difference from archiving**, which is what deleting an automation does. An archive ends the runs it was in the middle of, with an `exit_reason` saying so, because an archive is not coming back and a run holding its place on a graph nobody will activate again is a subscriber stuck part way through forever. There is no way back from archived: write a new automation instead.\n\nPausing a paused automation succeeds and changes nothing. A draft or an archived one is refused, because neither is sending and there is nothing to pause.\n\nThe tag stays where it is either way. Pausing stops the automation, not the segment, so the readers it has already tagged are still in the group and still reachable by hand."
      }
    }
  ]
}
