diff --git a/source/includes/rest/_broadcasts.md b/source/includes/rest/_broadcasts.md index 12c5feab971..c4773123411 100644 --- a/source/includes/rest/_broadcasts.md +++ b/source/includes/rest/_broadcasts.md @@ -949,7 +949,7 @@ Rate limits are applied per user. ## Set the audience -> To send a Single-Email Campaign to a saved segment: +> To send a Single-Email Campaign to a saved segment, leaving out people with either of two tags: ```shell curl -X PUT "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID/audience" \ @@ -958,7 +958,10 @@ curl -X PUT "https://api.getdrip.com/v2/YOUR_ACCOUNT_ID/broadcasts/BROADCAST_ID/ -u YOUR_API_KEY: \ -d '{ "filter": [ - [{ "type": "segments", "properties": { "operator": "in", "segment_id": "4815162" } }] + [ + { "type": "segments", "properties": { "operator": "in", "segment_id": "4815162" } }, + { "type": "tags", "properties": { "operator": "not_include_any", "value": ["Churned", "Refunded"] } } + ] ] }' ``` @@ -975,7 +978,10 @@ request["User-Agent"] = "Your App Name (www.yourapp.com)" request["Content-Type"] = "application/vnd.api+json" request.body = { filter: [ - [{ type: "segments", properties: { operator: "in", segment_id: "4815162" } }] + [ + { type: "segments", properties: { operator: "in", segment_id: "4815162" } }, + { type: "tags", properties: { operator: "not_include_any", value: ["Churned", "Refunded"] } } + ] ] }.to_json @@ -998,7 +1004,10 @@ const response = await fetch( }, body: JSON.stringify({ filter: [ - [{ type: "segments", properties: { operator: "in", segment_id: "4815162" } }] + [ + { type: "segments", properties: { operator: "in", segment_id: "4815162" } }, + { type: "tags", properties: { operator: "not_include_any", value: ["Churned", "Refunded"] } } + ] ] }) } @@ -1060,14 +1069,73 @@ true of a person, and a person matching any inner list is included. So `[[A, B], [C]]` means "(A and B) or C". `[[]]` means everyone eligible to receive it, and is how you clear an audience. An empty -filter, `[]`, is rejected. +filter, `[]`, is rejected. A filter takes at most 10 AND-sets, each with at most 10 conditions. + +Two condition types are available: `segments` and `tags`. Each condition is a `type` and a +`properties` object holding the `operator` and its value. More condition types will follow. + +A `segments` condition takes the operator `in` or `not_in` and a `segment_id`, which is the `id` +from the Segments API. `segment_id` takes one id, never a list: `["4815162"]` is rejected. For +several segments, use one condition each. Two conditions in the same inner list mean "in both"; +two in separate inner lists mean "in either". -Today the only condition type available is `segments`, with the operators `in` and -`not_in`. The `segment_id` is the `id` from the Segments API. More condition types will follow. +A `tags` condition takes one of the operators below and a `value`. Name a tag as the Tags API +returns it; matching ignores case and repeated spaces. A tag does not have to exist on the +account yet: a condition naming a tag no one has matches no one, or everyone when negated. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorvalueMatches people who
includeOne tag, as a stringhave the tag
not_includeOne tag, as a stringdo not have the tag
containsText, as a stringhave a tag whose name contains the text
include_anyA list of tagshave at least one of the tags
include_allA list of tagshave every one of the tags
not_include_anyA list of tagshave none of the tags
not_include_allA list of tagsare missing at least one of the tags
no_tagsOmitted, or ""have no tags at all
-`segment_id` takes one id, never a list: `["4815162"]` is rejected. For several segments, use -one condition each. Two conditions in the same inner list mean "in both"; two in separate -inner lists mean "in either". +A blank tag, an empty list, or a blank entry in a list is rejected. Replacing an audience requires `confirm=true` whenever the current audience is anything other than everyone, because what is being replaced cannot be recovered through the API. Read the @@ -1137,7 +1205,10 @@ const body = await response.json(); { "audience": { "filter": [ - [{ "type": "segments", "properties": { "operator": "in", "segment_id": "4815162" } }] + [ + { "type": "segments", "properties": { "operator": "in", "segment_id": "4815162" } }, + { "type": "tags", "properties": { "operator": "not_include_any", "value": ["Churned", "Refunded"] } } + ] ] } } @@ -1149,8 +1220,8 @@ const body = await response.json(); { "audience": { "message": "This filter includes unsupported criteria and cannot be returned as a filter.", - "summary": "People who are tagged with \"vip\"", - "unsupported_types": ["tags"] + "summary": "People who have performed \"Purchased VIP\"", + "unsupported_types": ["actions"] } } ``` @@ -1158,10 +1229,12 @@ const body = await response.json(); `filter` comes back in the same shape it is set in, so reading an audience and writing it straight back changes nothing. -`filter` is present only when writing it again would be accepted. Most audiences built in the +`filter` is present only when writing it again would be accepted. Many audiences built in the Drip app use rules that have no form here yet, and those come back described instead: `summary` is plain-English display text, and `unsupported_types` names the condition types in -the way. Expect that until more condition types are available. +the way. Expect that until more condition types are available. If every condition type is +available but the filter still cannot be returned, for example because of its size, +`unsupported_types` is left out. A present `filter` is not a promise that its segments still exist. That is not checked on read, so an audience naming a segment deleted since it was set reads back intact and responds