included_tags and excluded_tags become filters

Emails get one composable filters field in place of two tag lists

August 15, 2024
Breaking API change

This is a backwards-incompatible change to Buttondown's API. Pinning your newsletter or request to an older API version keeps the previous behavior where a version was cut for it.

Breaking change: we've combined the two fields of included_tags and excluded_tags on emails into the filters field, which is now more flexible and composable.

Historically, the included_tags and excluded_tags fields have been intersections rather than unions:

  • An email with included_tags ["foo"] and excluded_tags [] would only be sent to subscribers with the foo tag.
  • An email with included_tags ["foo", "bar"] and excluded_tags [] would be sent to subscribers with the foo and bar tag.
  • An email with included_tags ["foo", "bar"] and excluded_tags ["baz"] would be sent to subscribers with the foo and bar tag except for subscribers with the baz tag.

This meant it was impossible to model more complex filters or audiences, such as:

  • Sending an email to subscribers tagged foo or bar.
  • Sending an email to subscribers tagged foo and either bar or baz.
  • Sending an email to subscribers tagged foo or those with a given metadata key-value pair.

Our new filters field allows you to express these kinds of filters in a more flexible way by letting you nest specific fields. It's more verbose, but within that verbosity lies power.

Here are some examples of how you can use the filters field to express the same filters as above.

A simple filter

Before

{
  "email": {
    "included_tags": ["foo"],
    "excluded_tags": []
  }
}

After

{
  "email": {
    "filters": {
      "predicate": "and",
      "groups": [],
      "filters": [
        {
          "operator": "contains",
          "field": "subscriber.tags",
          "value": "foo"
        }
      ]
    }
  }
}

A more complex filter

Before

{
  "email": {
    "included_tags": ["foo", "bar"],
    "excluded_tags": ["baz"]
  }
}

After

{
  "email": {
    "filters": {
      "predicate": "and",
      "groups": [],
      "filters": [
        {
          "operator": "contains",
          "field": "subscriber.tags",
          "value": "foo"
        },
        {
          "operator": "contains",
          "field": "subscriber.tags",
          "value": "bar"
        },
        {
          "operator": "not_contains",
          "field": "subscriber.tags",
          "value": "baz"
        }
      ]
    }
  }
}

(Note that these changes do not apply to the RSS feeds endpoints, which similarly have taken an optional included_tags field. That change will come too, in good time.)

Super-breaking change: we're also removing the included_tags and excluded_tags filters from the list endpoint. This is not backwards compatible: I investigated the possibility of adding a new filters field to the list endpoint, but decided that it would be too confusing and too onerous for what it achieves. No active API clients have used either filter in the past 90 days, so we're deeming it safe; if you have a specific use case in mind that hinged on this functionality, email us and we can discuss your options.

Buttondown is the last email platform you’ll switch to.