Skip to content

Rule reference

A rule, which the API calls a line item, places one or more offers at one or more moments of an agent, for the customers and situations it describes. This page is the reference for the line item object of the Offer Manager API: its fields, the condition grammar of audience and exclusions, the order in which the Fetch API evaluates a rule, and how writes are validated. For the concept, see Rules.

Rules are managed in the lineItem collection:

POST  https://api.advertising.chat/system/offer-manager/lineItem
PATCH https://api.advertising.chat/system/offer-manager/lineItem/<id>

Fields

Field Type Description
name string, 1 to 200 characters Required. The rule's display name.
application ID Required. The agent the rule belongs to. See Application scoping.
enabled boolean Required. Whether the rule serves.
adUnits array of IDs, at least one Required. The moments the rule applies to, all of the rule's application.
creatives array of IDs The offers the rule can show, in order. A house rule needs at least one. An exchange rule has none.
priority 6, 8, 10, or 12 6 is High, 8 is Default, and 10 is Low. A lower number wins. Required on a house rule. 12 is the priority of every exchange rule.
audience condition group Who sees the rule's offers. Absent means everyone.
exclusions condition group Who doesn't. A customer the group matches is removed. Absent means no one is excluded.
channels array of rcs, sms, webchat, messenger, voice The channels the rule serves on. Absent or empty means every channel. See Channels.
schedule object When the rule serves, in the org's time zone. Absent means always. See Schedule.
frequency { times, interval } The most times one customer is shown the rule within a rolling window. Absent means no cap. See Frequency.
limits array of limits Caps on the whole rule. See Limits.
fallback boolean Makes the rule a fallback rule, considered only when no other rule qualifies.
testMode boolean Marks the rule's responses with isTest: true.
creativeSelectionStrategy default or sequential How the rule picks among its offers. sequential shows them to each customer in the order of creatives, and needs a subscriber on the request.
dailyBudget number, 0 or more A daily budget that paces the rule evenly across the UTC day. Absent or 0 means unpaced.
orderType house or exchange house, the default, shows the rule's offers. exchange asks an ad network for one.
adNetworkCode string, 1 to 100 characters Exchange rules only. The ad network asked to fill. Absent means the default network.

The platform writes three fields that a request can't set:

Field Description
reporting.impressions The impressions the rule has served, which the impressions limit reads.
_description { summary, policyHash, generatedAt }: a generated plain-language summary of the rule, and the hash of the rule it describes.
_provenance Per policy field (adUnits, creatives, priority, audience, exclusions, channels, schedule, frequency), { instruction, author, surface, policyHash, at }: the plain-language instruction the field was last written from. surface is console, copilot, cli, mcp, or api.

When policyHash differs from the rule's current hash, the summary or the instruction describes an earlier version of the rule or field.

Instructions

A create or update can send instructions beside the rule's fields: per policy field, the plain-language instruction that the field's new value was translated from, 1 to 4,000 characters. The platform records each one in _provenance with the field's hash. Send null for a field that was written by hand to remove its entry. instructions isn't stored on the rule.

{
  "audience": { "all": [{ "attribute": "plan", "op": "equals", "value": "prepaid" }] },
  "frequency": null,
  "instructions": { "audience": "prepaid customers", "frequency": null }
}

Application scoping

Every rule and every moment belongs to one application, set when it is created and fixed afterwards, and a rule holds only its own application's moments. The Fetch API resolves the application from the fetch key or the application parameter before it looks up the moment. See Application scoping and How the application and moment are resolved.

Exchange rules

An exchange rule (orderType: "exchange") is a placeholder that asks an ad network for an offer at its moments. It has no creatives (absent or empty), and its priority is 12, below every house priority. When priority is absent it is 12, and any other value is rejected. adNetworkCode names the network, and only an exchange rule may set it.

Every other field applies to an exchange rule as it does to a house rule: its audience, exclusions, channels, schedule, frequency, and limits all restrict it. Because it has no offers of its own, its conditions are evaluated once, and a history condition on this_offer never matches.

Channels

channels restricts a rule to the channels it lists: rcs, sms, webchat, messenger, and voice. The request's channel is the Fetch API's channel parameter, or the channel its render format names. See Channels.

A rule that lists channels never matches a request whose channel is unknown, such as a direqt request with no channel parameter. A rule with no channels, or an empty list, matches every request, including those with no channel.

Schedule

schedule limits when a rule serves. Each property is checked against the current time in the org's time zone, which is the Time zone setting on the org's Settings page, or UTC when it isn't set.

Property Value Matches when
start YYYY-MM-DD Today is on or after the date.
end YYYY-MM-DD Today is on or before the date. It can't be before start.
daysOfWeek 1 to 7 of mon, tue, wed, thu, fri, sat, sun Today is one of the days.
hours { from: 0..23, to: 1..24 } The current hour is at or after from and before to.

A schedule sets at least one property. When from is greater than to, the hours run overnight: { "from": 22, "to": 6 } is 10 PM to 6 AM. The window must be shorter than the whole day, so from and to can't be equal, and { "from": 0, "to": 24 } is rejected; omit hours instead.

{ "schedule": { "start": "2026-11-01", "daysOfWeek": ["fri", "sat"], "hours": { "from": 17, "to": 23 } } }

Frequency

frequency allows at most times serves of the rule to one customer within any rolling interval:

Property Value
times A whole number from 1 to 20.
interval Milliseconds, from 60,000 (one minute) to 7,776,000,000 (90 days).

{ "times": 2, "interval": 604800000 } shows the rule to a customer at most twice in any seven days. Moments and offers carry a cap of the same shape. The serves are read from subscriber history, which keeps 20 events per type for 95 days; that is where the bounds come from. A request without a subscriber has no history, so no frequency cap applies to it.

Limits

Each entry of limits is { limitType, value, enabled }. An entry with enabled: false is ignored.

limitType value Effect
impressions A whole number, 0 or more. The rule stops qualifying once reporting.impressions reaches the value.
percentage A whole number from 0 to 100. The rule qualifies for that percentage of requests, chosen at random.

Conditions

audience and exclusions share one grammar. Each is a condition group, which holds conditions and at most one level of nested groups:

group      := { "all": [item, …] } | { "any": [item, …] }    1 to 20 items
item       := condition | nested group                        a nested group holds conditions only
condition  := attribute condition | history condition

An all group matches when every item matches, and an any group matches when at least one does. audience admits the customer when its group matches, and exclusions removes the customer when its group matches. Nesting stops at a depth of two, which covers "A and (B or C)" and "(A and B) or (C and D)":

{
  "audience": {
    "all": [
      { "attribute": "tier", "op": "equals", "value": "gold" },
      { "any": [
          { "attribute": "nights", "op": "gte", "value": 3 },
          { "attribute": "loyaltyMember", "op": "true" }
      ] }
    ]
  }
}

Attribute conditions

An attribute condition, { attribute, op, value }, tests one of the attributes sent on the fetch request. attribute is the key exactly as the agent sends it, 1 to 128 characters, and must name an entry in the attribute catalog of the rule's agent that isn't retired. The operator must apply to the entry's type (see Types), and for an enum, every value must be one of the entry's values. The shape of value depends on op:

op value Matches when the attribute
equals A string, number, or boolean Equals the value.
not_equals A string, number, or boolean Doesn't equal the value, or is missing.
in A list of 1 to 100 strings, numbers, or dates as YYYY-MM-DD Equals one of the values. A date matches on the same day.
not_in Same as in Equals none of the values, or is missing.
gt, gte, lt, lte A number, or a date as YYYY-MM-DD Is greater than, at least, less than, or at most the value.
between [low, high], two numbers or two dates, low not above high Is between the two, inclusive.
contains One string or number Is a list that contains the value.
within_last Whole days, 1 to 3650 Is a date within that many days up to today. 7 is today and the six days before it.
more_than_ago Whole days, 1 to 3650 Is a date earlier than that many days before today.
exists None Is present and readable.
not_exists None Is missing or unreadable.
true, false None Reads as that boolean.

String values are at most 500 characters. Numbers must be finite.

How attributes are read

Every attribute arrives from the request as sent, often as a string. It is read as the type of its entry in the agent's attribute catalog. So { "attribute": "renewal", "op": "equals", "value": "2026-12-01" } on a date attribute matches 2026-12-01T18:30, the same calendar day, and on a string attribute it doesn't. An enum reads as a string.

Type How a value is read
String Compared with loose equality against the value sent: the number 5 equals "5", and "05" doesn't equal "5".
Number Read from the start of the value, so "10%" reads as 10.
Boolean true, false, "true", "false", "yes", "no", "1", "0", 1, and 0, in any case.
Date Compared as calendar days in the org's time zone. A date (2026-05-01) or a local date and time (2026-05-01T10:00) names the day written. A date and time with an offset or Z is converted to the org's time zone first. A number is epoch milliseconds.
List A JSON array, or a comma-separated string with each item trimmed.

Today, for within_last and more_than_ago, is the current day in the org's time zone, so the agent doesn't send the current date.

Two states decide how a condition treats an attribute that can't be compared:

  • Missing means the request has no such key or sends null. For number, boolean, and date reads, an empty string is also missing. not_equals, not_in, and not_exists match a missing attribute, and every other operator doesn't. So { "attribute": "plan", "op": "not_equals", "value": "unlimited" } includes customers whose plan is unknown.
  • Unreadable means present but not readable as its type, such as "abc" for a number, "soon" for a date, or "maybe" for a boolean. Only not_exists matches an unreadable attribute.

History conditions

A history condition, { event, subject, within_days, at_least }, tests the customer's past offer events within the rule's agent, as recorded in subscriber history:

Property Value
event shown, declined, accepted, or converted.
subject this_offer, the offer being considered; offer:<id>, one offer; rule:<id>, one rule; or any_offer, every offer of the agent.
within_days A rolling window ending now, from 1 to 90 days.
at_least The number of events required, from 1 to 20. Defaults to 1.

The condition matches when the subject has at least at_least events of that type in the window. A decline is a tap on a reply with the decline role, or an offer_declined event. Silence is not a decline: a customer who doesn't answer has no declined event. A request without a subscriber has no history, so its history conditions don't match.

Conditions on this_offer are evaluated for each of the rule's offers in turn, so an exclusion can remove one offer and leave the rule's other offers eligible.

How a rule is evaluated

For each request, the Fetch API first checks the moment's own frequency cap; a customer who has reached it gets an empty response. It then checks each enabled rule of the moment in this order. The first check a rule fails removes it, and the decision trace records that check as the rule's reasonCode:

  1. limit: the rule's limits. Fallback rules are set aside.
  2. channel: the request's channel must be one of the rule's channels.
  3. schedule: the current time must fall within the rule's schedule.
  4. audience and exclusions: evaluated once for each offer. An offer passes when the audience matches and the exclusions don't. The rule continues with the offers that passed, and the trace lists the removed ones in offersExcluded. If no offer passes, the rule is removed, and the trace records the deciding condition in condition.
  5. format: the rule needs an offer that can render in the requested format.
  6. If no rule remains, the fallback rules go through steps 1 to 5.
  7. frequency: the customer must be under the rule's frequency cap.
  8. The highest priority among the remaining rules wins, and the winning rule picks one of its eligible offers that is under the offer's own cap.

The full request flow, including context and the appropriateness check, is in How a request is decided.

Validation

Every create and update is validated against the rule model. A request that fails returns 422 with a message naming each problem and a propertyErrors list of { property, message }. Validation rejects:

  • An unknown field, including the retired targets. Targeting is audience and exclusions.
  • A value outside its type or bounds, such as a priority of 7, a frequency.times of 30, or a between whose first value exceeds the second.
  • A house rule with no creatives or no priority, or with priority 12 or an adNetworkCode.
  • An exchange rule with creatives, or with a priority other than 12.
  • An attribute that isn't in the catalog of the rule's application, or is retired, and a condition whose operator or values don't fit the attribute's type.
  • A history condition that names an offer outside your org, or a rule of another application.
  • A moment of another application, or an offer that isn't in your org.
  • An application that differs from the rule's application, on an update.

An update is validated twice: as sent, and merged with the stored rule. The merged rule must be a valid rule, so an update that makes a house rule into an exchange rule must also clear its offers.

To clear an optional field on an update, send it as null. These fields can be cleared: audience, exclusions, channels, schedule, frequency, limits, dailyBudget, and adNetworkCode. Sending null for any other field returns 422. A field that the update omits is unchanged.

Examples

Create a rule

The resort concierge agent from Moments, offers, and rules offers a spa booking after check-in to gold-tier guests on RCS, at most once a week:

{
  "name": "Spa for gold guests",
  "application": "6650a1f0c3b2a1d4e5f60718",
  "enabled": true,
  "adUnits": ["6650a1f0c3b2a1d4e5f60720"],
  "creatives": ["6650a1f0c3b2a1d4e5f60730"],
  "priority": 8,
  "audience": { "all": [ { "attribute": "tier", "op": "equals", "value": "gold" } ] },
  "channels": ["rcs"],
  "frequency": { "times": 1, "interval": 604800000 }
}

Exclude anyone who turned the offer down in the last 30 days

{ "exclusions": { "any": [ { "event": "declined", "subject": "this_offer", "within_days": 30 } ] } }

Sent in a PATCH, this adds the exclusion and leaves the rest of the rule unchanged. After 30 days without another decline, the customer sees the offer again.

Exclude recent buyers and guests checking out soon

{
  "exclusions": {
    "any": [
      { "event": "converted", "subject": "any_offer", "within_days": 14 },
      { "attribute": "checkOutDate", "op": "lte", "value": "2026-11-03" }
    ]
  }
}

Clear fields

This PATCH removes the channel restriction and the schedule:

{ "channels": null, "schedule": null }

Create an exchange rule

{
  "name": "Network ads after resolution",
  "application": "6650a1f0c3b2a1d4e5f60718",
  "enabled": true,
  "orderType": "exchange",
  "adUnits": ["6650a1f0c3b2a1d4e5f60721"],
  "channels": ["webchat"]
}

The rule takes priority 12 and asks the default ad network for an offer.