{
  "info": {
    "name": "Commune: Welcome every new reader",
    "description": "The first email somebody gets from you decides whether they open the second. Commune can send it the moment they subscribe, follow it up a few days later, and stop the moment they tell you to. You write the two emails once.\n\nThe walkthrough: https://usecommune.dev/use-cases/welcome-new-subscribers\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": "9c1e4b7a-2d35-4f61-8a90-1b2c3d4e5f60"
    },
    {
      "key": "step",
      "value": "welcome"
    }
  ],
  "item": [
    {
      "name": "1. Find your newsletter",
      "request": {
        "method": "GET",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/newsletters",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "newsletters"
          ]
        },
        "description": "A key reaches one newsletter or several, whichever its owner ticked when creating it. List them and pick yours by `handle` (or `id`, either works in a path): the automation is written under it."
      }
    },
    {
      "name": "2. Read what the trigger carries",
      "request": {
        "method": "GET",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/automation-triggers?limit=100",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "automation-triggers"
          ],
          "query": [
            {
              "key": "limit",
              "value": "100"
            }
          ]
        },
        "description": "Every event Commune publishes is a trigger. This is the catalog, and it answers two questions you need before writing anything: whether a topic's events are about a subscriber (`carries_subscriber`), and what a condition may ask about under it (`context`).\n\n`subscriber.created` is the one a welcome sequence waits for, and `carries_subscriber` is `true`, which is what makes a `send_email` step legal: a trigger whose events are not about a person cannot send an email or move a tag, and saving one that tries is refused rather than left to fail once per event forever.\n\nAny credential that reaches a newsletter can read this. It describes Commune's event vocabulary rather than anybody's data."
      }
    },
    {
      "name": "3. Write the whole sequence in one request",
      "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\": \"Welcome sequence\",\n  \"trigger\": \"subscriber.created\",\n  \"reentry\": false,\n  \"steps\": [\n    {\n      \"id\": \"welcome\",\n      \"type\": \"send_email\",\n      \"next\": \"settle\",\n      \"email\": {\n        \"subject\": \"Welcome, {{ subscriber.first_name }}\",\n        \"preview_text\": \"Here is where to start.\",\n        \"body_markdown\": \"# Glad you are here\\n\\nYou just subscribed to The Example Letter, which means a short piece about\\nnewsletters and communities lands in your inbox every Thursday.\\n\\nReply to this one and tell me what you are working on. I read all of them.\\n\\n[Unsubscribe]({{ unsubscribe_url }}) · {{ address }}\\n\"\n      }\n    },\n    {\n      \"id\": \"settle\",\n      \"type\": \"wait\",\n      \"duration\": \"P3D\",\n      \"next\": \"best-of\"\n    },\n    {\n      \"id\": \"best-of\",\n      \"type\": \"send_email\",\n      \"email\": {\n        \"subject\": \"Three things worth reading\",\n        \"body_markdown\": \"# Three things worth reading\\n\\nThese explain what the letter is about better than a welcome email can.\\n\\n- Why a newsletter is not a broadcast\\n- The twenty readers who decide whether you keep going\\n\\n[Unsubscribe]({{ unsubscribe_url }}) · {{ address }}\\n\"\n      }\n    }\n  ]\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "`steps` is every step the sequence will have, and every edge between them is written down: `welcome` names `settle`, `settle` names `best-of`, and `best-of` names nothing, so the run ends there. A `next` left out is not \"the next step\", it is no step, which ends the run (it defaulted to the following entry until `2026-10-14`). There is no operation that adds one step later: a step is only meaningful against the edges pointing at it.\n\nThe emails arrive inline as `body_markdown`, parsed by the same strict parser article bodies use. **Keep the unsubscribe link and `{{ address }}` in every body.** Commune seeds both into a draft as ordinary content, which means they can be written away, and a body missing either is refused with `missing_footer` rather than sent. Every image in a body is probed too, because an inbox fetches images fresh when the reader opens the email.\n\n`reentry: false` means once ever, however many times the trigger fires for that person. A resubscribe never re-enters whatever this says: an address coming back to a list it was on before does not get a first welcome email for the second time.\n\nIt comes back `draft`, and a draft cannot email anybody."
      }
    },
    {
      "name": "4. Read the email before a subscriber does",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          },
          {
            "key": "Idempotency-Key",
            "value": "{{$guid}}"
          },
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/automations/{{automation}}/steps/{{step}}/send-test",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "automations",
            "{{automation}}",
            "steps",
            "{{step}}",
            "send-test"
          ]
        },
        "body": {
          "mode": "raw",
          "raw": "{\n  \"to\": [\n    \"me@example.com\",\n    \"editor@example.com\"\n  ]\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "description": "This is the step people skip, and it is the one that matters most here. An article is written, looked at and sent within an afternoon. An automation email is written once and then sent, unattended, for months, to people who arrive one at a time, so there is no moment at which anybody watches it go out. Without this the first person to see it rendered is a subscriber.\n\nIt renders through the same renderer and the same personalization pass the real send uses, so what lands is what a subscriber would get: the placeholders filled in from example values, and the unsubscribe link pointing at a page that explains itself rather than at a live token. It touches nothing else, so run it as many times as you need to.\n\nUp to five addresses. Leave `to` out and the copy goes to the address on the account this credential belongs to, which is why `recipients` can come back empty with `sent_to_owner: true`.\n\nRepeat it for `best-of`. The step id in the path is the one you chose when you wrote the graph."
      }
    },
    {
      "name": "5. 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": "**This is the request that starts sending.** Everything above describes a sequence; this installs it. From the moment it returns, Commune mails whoever the trigger names, as often as it fires, with no further request from anybody, until a person turns it off.\n\nEvery gate runs again here, on the stored automation, and the repetition is deliberate: a sequence can be saved by one tool and turned on by another weeks later, and in between an image can rot, a tag a step applies can be deleted, and a sending domain can fall out of verification. Each is a `422` naming what is wrong: the graph, a condition, `missing_footer`, `broken_images`, or the newsletter having no verified sending address.\n\nIt is charged to a daily cap of its own, counted per newsletter, and reported as a `429` that is not one of the `RateLimit-Policy` budgets: its `Retry-After` is measured in hours, which is how to tell it from an ordinary rate limit. An activation that was refused costs nothing, so fixing a graph and trying again is free until it succeeds.\n\nTurning on a sequence that is already `active` succeeds and changes nothing. To stop it later, pause it: a pause holds every waiting run exactly where it is, and there is no window in which a paused automation still sends."
      }
    },
    {
      "name": "6. See who is in it",
      "request": {
        "method": "GET",
        "header": [
          {
            "key": "Commune-Version",
            "value": "{{version}}"
          }
        ],
        "url": {
          "raw": "{{baseUrl}}/automations/{{automation}}/runs?status=waiting&limit=100",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "automations",
            "{{automation}}",
            "runs"
          ],
          "query": [
            {
              "key": "status",
              "value": "waiting"
            },
            {
              "key": "limit",
              "value": "100"
            }
          ]
        },
        "description": "One row per subscriber who entered the sequence, most recent first. `status` and `current_step` say where they are, `next_run_at` says when they move, and `steps` is what each email did for that one person: sent, delivered, opened, clicked, bounced, complained, each with its own time.\n\n`?status=waiting` is the question you will actually ask: who is part way through right now. `?subscriber=` answers \"did this sequence reach that person\", and returns one row unless the automation allows re-entry.\n\n**No row carries an email address.** A run is read under `sending` and an address belongs to `audience`, so `subscriber` is a reference that `?expand=subscriber` deliberately will not inflate. Resolve it with the subscriber endpoint if you need the address, under a key that holds `audience: read`."
      }
    }
  ]
}
