attach_asset_to_variantwrite
Attach a library asset to a content variant field — overwrites whatever image was on that field with the asset's blob_url. Also copies the asset's provenance (prompt, brief / reference / planner attribution, tags) into the variant's generated_metadata so the audit trail follows the image. Returns { ok, asset_id, variant_id, blob_url, field_key }. Use after search_assets surfaces an existing asset that matches what the operator wants — saves a generation call. The variant's image_url is also synced to the asset's blob_url so the runtime tag picks it up immediately.
4 parameters
| Parameter | Type | Description |
|---|
asset_id required | string | Asset id from search_assets (asset_<uuid> shape). |
variant_id required | string | content_variants.id to attach to. |
field_key required | string | The payload field key on the variant to overwrite (e.g. "image_url", "hero_image_url"). |
alt optional | string | Optional alt text override. When omitted, the variant's existing image_alt is preserved. |
author_movewrite
Author a "move object" placement: relocate an EXISTING element on the page to a new location, WITHOUT changing its copy or injecting new content (pure rearrangement). Creates a source zone (element_selector = the element to move) + a move_object content variant whose payload carries the destination anchor + position. Use after a two-pick (the extension / preview picker gives you source_selector + dest_selector). Target ONE segment via segment_id, OR all_visitors:true for everyone (the default — moves are usually global). CAUTION: once published this ships to the live site within ~60s. To preview without persisting on a no-tag site, use the extension demo flow instead.
8 parameters
| Parameter | Type | Description |
|---|
page_pattern required | string | URL pattern the move applies to (e.g. "/", "/blog/*") |
source_selector required | string | CSS selector of the EXISTING element to relocate (the object to move) |
dest_selector required | string | CSS selector of the destination anchor to move it next to / into |
position optional | "before" | "after" | "inside_start" | "inside_end" | Where to place the source relative to the destination. Default "after". ("replace" is intentionally not allowed for a move — it would destroy the destination.) |
zone_key optional | string | Zone identifier. Defaults to a generated "move_*" key. |
display_name optional | string | Human-readable name for the move. |
segment_id optional | string | Audience segment. Omit when all_visitors is true. |
all_visitors optional | boolean | Apply to ALL visitors regardless of segment (default true for moves). When false, provide segment_id. |
clone_trigger_templatewrite
Clone a system or site template into a new site-owned template.
2 parameters
| Parameter | Type | Description |
|---|
template_id required | string | Source template ID |
name optional | string | Name for the clone |
configure_product_recowrite
Configure a product_reco variant: choose recommendation mode (auto / curated / mixed), retrieval strategy, catalog filters, catalog weights, and pinned products. A FILTER removes items ("do not recommend anything with 10 or fewer in stock"); a WEIGHT re-ranks them without hiding them ("de-emphasise low stock"). Reach for a weight whenever the ask is to show something less rather than not at all. Call list_catalog_filter_fields first so field names are real. Use list_products to find product IDs.
8 parameters
| Parameter | Type | Description |
|---|
variant_id required | string | Existing product_reco variant ID |
source required | "auto" | "curated" | "mixed" | auto: Vibe Engine picks all products. curated: only show pinned product_ids. mixed: pinned products first, engine fills the rest. |
strategy optional | "session_behavior" | "bestsellers" | "new_arrivals" | "similar" | Engine retrieval strategy (used when source=auto or mixed). Default session_behavior. |
limit optional | integer | Max products to render (default 5). |
category optional | string | Restrict candidates to this category (case-insensitive exact match). |
product_ids optional | string[] | Catalog product IDs to pin. Required when source=curated. Optional when source=mixed. |
filters optional | object[] | Catalog filters, ANDed. e.g. [{"field":"inventory_quantity","op":"gt","value":10}] serves only items with more than 10 in stock. Applied to auto-ranked AND pinned items alike. Pass [] to clear. |
catalog_weights optional | object[] | Catalog scoring weights. e.g. [{"field":"inventory_quantity","op":"lt","value":10,"weight":0.5}] de-emphasises nearly-sold-out items without hiding them. Weights re-rank auto-ranked recommendations and deliberately do NOT reorder curated pins, which are an ordering the operator authored by hand. Pass [] to clear. |
create_campaignwrite
Create a new campaign with a brief (objective, offer, tone, themes).
7 parameters
| Parameter | Type | Description |
|---|
name required | string | Campaign name |
brief optional | object | Campaign brief |
status optional | "active" | "draft" | "paused" | "archived" | |
starts_at optional | string | ISO-8601 start date for automated scheduling |
ends_at optional | string | ISO-8601 end date for automated scheduling |
eligibility optional | object | Advanced segment matching rules (e.g. segment_member, audience_signals) |
priority optional | number | Evaluation priority (higher = evaluated first) |
create_content_variantwrite
Create a personalized content variant for a zone + segment combination. For copy_swap/hero_swap, check the zone's field_schema (get_zones) FIRST and key the payload by its payload_key/canonical_name entries — a payload sharing NO renderable key with a schema-bearing zone binds nothing and is refused with a 400 naming the accepted keys. Do not copy another variant's payload shape without checking the target zone's schema. For product_reco variants, create with an empty payload ({}) and then call configure_product_reco to set the recommendation mode and curated products.
8 parameters
| Parameter | Type | Description |
|---|
zone_id required | string | Zone ID |
segment_id optional | string | Target segment ID (legacy mode, or mirrored from eligibility) |
type required | "overlay" | "hero_swap" | "copy_swap" | "product_reco" | "move_object" | Variant type |
payload required | object | Variant payload. For overlay/hero_swap/copy_swap: { headline, body, cta, image_url }. For product_reco: pass {} and configure via configure_product_reco. For move_object: { dest_selector, position } — but prefer author_move, which also creates the source zone. |
eligibility optional | object | Advanced segment matching rules (e.g. segment_member, audience_signals) |
priority optional | number | Evaluation priority (higher = evaluated first) |
campaign_id optional | string | Link this variant to an orchestration campaign |
destination_page_id optional | string | Make this a ROUTE to an axo Page (migration 260): the CTA URL is then SERVER-derived (<pageUrl>?vt_route=<variantId>) — do not supply URL fields in the payload. Prefer create_route. |
create_formwrite
Create a reusable lead-capture form template. The payload is the form design; it is copied onto each placement.
2 parameters
| Parameter | Type | Description |
|---|
name required | string | Internal template name |
payload required | object | Form design: { title, cta_text, tcpa_text, success_text, accent_color, fields (subset of name/email/phone), calendar_enabled } |
create_offerwrite
Create an offer in this site's library. It is ALWAYS created as an UNAPPROVED DRAFT: a human must ratify it in the dashboard (approve the terms, and attest the code exists and is active in their commerce platform) before it can ever serve. There is no `status` argument here and no tool on any surface that can make an offer live — that transition is dashboard-only, by construction. MONEY IS CENTS AND PERCENTS ARE BASIS POINTS, integers only: value_bp 2500 = 25% off, value_cents 1000 = $10 off. The write is REJECTED when the depth exceeds the site's max_discount_pct ceiling, or when it meets/exceeds the site's gross margin (every order at that depth loses money and no conversion lift can fix it). `code` must be a discount code the merchant's platform ALREADY honors — fulfillment_mode accepts only "existing_code". Returns { id, warnings }; warnings cover mirrored terms and the break-even lift this discount needs. Then place it with place_offer.
19 parameters
| Parameter | Type | Description |
|---|
name required | string | Internal offer name (max 200 chars) |
offer_type required | "percent" | "fixed" | "free_shipping" | "gift" | "tiered" | percent needs value_bp. fixed needs value_cents. free_shipping/gift/tiered carry no single headline value, so the depth cap and break-even hint do not apply to them. |
value_bp optional | integer | Percent discount in BASIS POINTS, 0-10000 (2500 = 25.00%). Required for offer_type=percent. |
value_cents optional | integer | Fixed discount in CENTS (1000 = $10.00). Required for offer_type=fixed. |
currency optional | string | 3-letter ISO-4217 code (e.g. "USD"). Leave unset for percent / free_shipping. |
code optional | string | The discount code AS IT EXISTS in the merchant's commerce platform. AXO never mints codes. Two live offers cannot claim the same code. |
min_order_cents optional | integer | MIRRORED TERM (cents): minimum basket. Displayed as fine print and used to reason about a fixed offer's depth — their platform enforces it, and if the two disagree theirs wins. |
max_discount_cents optional | integer | MIRRORED TERM (cents): cap on the discount. Displayed as fine print only; AXO never enforces it at checkout. |
max_exposures_per_visitor optional | integer | The ONE cap AXO owns and enforces: how many times we show this offer to one visitor. There is deliberately no redemption limit — that ledger belongs to the merchant's platform. |
starts_at optional | string | ISO-8601 timestamp the offer becomes servable. |
ends_at optional | string | ISO-8601 timestamp the offer stops serving. Must be after starts_at. Enforced at config-build time, so the offer goes dark everywhere on the next build. |
stacking optional | "exclusive" | "stackable" | "group" | MIRRORED TERM, rendered as fine print ("Not combinable"). Default "exclusive". Their cart decides what actually stacks. |
fulfillment_mode optional | "existing_code" | Only "existing_code" is accepted: we reference a code the merchant already honors and never mint one. Minting is deliberately deferred — it is where AXO would start competing with their loyalty platform on accounting and liability. |
tiers optional | object[] | For offer_type=tiered: up to 10 entries of { min_order_cents, value_bp | value_cents }. |
gift_sku optional | string | For offer_type=gift: the catalog SKU the merchant adds to the cart. Required for gift offers. |
eligibility optional | object | SegmentPredicate (same shape as a content variant's eligibility), so loyalty tier / lapse (crm_attribute, profile_attribute) and anonymous behavior (propensity, intent, affinity) feed ONE evaluator. |
redeem_url optional | string | Where the offer CTA sends the visitor. Falls back to the placement's own cta_href when unset. |
terms_url optional | string | Full terms page; the renderer links it as "Terms apply". |
presentation optional | object | Default presentation inherited by every placement: { headline, body, cta, cta_href, show_code, show_expiry }. A placement's own presentation keys win over these. |
create_page_configwrite
Create a page type configuration for sitemap analysis.
3 parameters
| Parameter | Type | Description |
|---|
page_type required | string | Page type (product, category, blog) |
url_pattern required | string | URL pattern to match |
selectors optional | object | CSS selectors for page elements |
create_triggerwrite
Create a new triggered message. Requires name, mode (immediate|dormancy), watch_event, and at least one destination.
13 parameters
| Parameter | Type | Description |
|---|
name required | string | Human-readable name |
description optional | string | |
mode required | "immediate" | "dormancy" | Fire mode |
watch_event required | string | Event type to watch |
cancel_event optional | string | Event that cancels dormancy |
criteria optional | object | Matching criteria |
delay_ms optional | number | Dormancy delay in ms |
window_ms optional | number | Cancel-event window in ms |
identifier_type optional | "email" | "phone" | "device" | "external_id" | "profile_id" | |
destinations required | object[] | Destination configs |
cooldown_per_profile_ms optional | number | |
global_rate_limit_per_min optional | number | |
optimization_mode optional | "axo_bandit" | "external" | Optimization mode (axo_bandit or external) |
create_trigger_templatewrite
Create a new site-owned payload template for triggered messages.
5 parameters
| Parameter | Type | Description |
|---|
name required | string | Template name |
description optional | string | |
format required | "json" | "html" | "form" | Template format |
body required | string | Template body with mustache placeholders |
example_destination optional | string | Intended destination type |
create_zonewrite
Create a new personalization zone on the site. For lightbox zones (zone_key="lightbox"), pass display_rules to set when/where/who/how-often the popup fires — including country targeting (audience.country) and day/hour windows (schedule); otherwise call list_lightbox_presets and use one of those rule objects verbatim.
6 parameters
| Parameter | Type | Description |
|---|
page_pattern required | string | URL pattern (e.g. "/", "/product*") |
zone_key required | string | Zone identifier |
display_name required | string | Human-readable name |
element_selector optional | string | CSS selector |
position optional | string | Position (before/after/replace) |
display_rules optional | object | Lightbox-only display rules (DisplayRules shape — see lib/display-rules.ts). Sub-objects: trigger {type, delay_ms?, scroll_pct?, inactivity_ms?}, page_targeting {exclude[]}, audience {visitor_type, converted, device[], country[]}, schedule {days[], hours[], tz}, frequency {cap, n_days?, cooldown_seconds?}, suppression {after_dismiss_days?, after_convert_days?, mutual_exclusion?}. audience.country takes ISO 3166-1 alpha-2 codes ("GB", not "UK" — invalid codes are DROPPED, which turns the filter off rather than erroring, so read the saved rules back to confirm). schedule.days is 0=Sunday..6=Saturday, schedule.hours is [{start,end}] with end EXCLUSIVE and end<=start wrapping past midnight, and schedule.tz is either "visitor" (default — each visitor's own device clock, so 11-14 is everyone's lunchtime) or an IANA zone like "America/Chicago" (one real-world window everyone sees at the same instant). The API sanitizes — bad fields are dropped, not 400d. Ignored on non-lightbox zones. |
delete_campaignadmin
Delete a campaign permanently.
1 parameter
| Parameter | Type | Description |
|---|
campaign_id required | string | Campaign ID |
delete_content_variantadmin
Soft-delete a content variant. The variant is hidden immediately but recoverable via restore_content_variant within 30 days. A history snapshot is automatically created before deletion.
1 parameter
| Parameter | Type | Description |
|---|
variant_id required | string | Variant ID |
delete_formwrite
Delete a form template. Already-deployed placements are unaffected (placements copy the payload).
1 parameter
| Parameter | Type | Description |
|---|
id required | string | Form template ID |
delete_offerwrite
Soft-delete an offer. It vanishes from list_offers and stops being hydrated on the next config build, but the row survives so historical per-offer lift stays attributable. Its PLACEMENTS are deliberately not deleted: they keep their offer_id, hydrate to nothing, and are dropped from tag config, so the page shows no half-resolved offer rather than a stale code — fail closed. If a slot should now carry something else, remove or repoint the placement yourself with delete_content_variant.
1 parameter
| Parameter | Type | Description |
|---|
id required | string | Offer ID (from list_offers) |
delete_page_configadmin
Delete a page type configuration.
1 parameter
| Parameter | Type | Description |
|---|
config_id required | string | Config ID |
delete_triggeradmin
Permanently delete a trigger and its fire history.
1 parameter
| Parameter | Type | Description |
|---|
trigger_id required | string | Trigger ID |
delete_trigger_templateadmin
Delete a site-owned trigger payload template.
1 parameter
| Parameter | Type | Description |
|---|
template_id required | string | Template ID |
delete_zoneadmin
Delete a zone and all its content variants.
1 parameter
| Parameter | Type | Description |
|---|
zone_id required | string | Zone ID |
draft_content_variantwrite
Use AI to generate a content variant draft for a segment + zone. Returns suggested headline, body, and CTA.
3 parameters
| Parameter | Type | Description |
|---|
segment_id required | string | Target segment |
zone_key optional | string | Zone key (e.g. "hero") |
variant_type optional | "overlay" | "hero_swap" | "copy_swap" | Variant type |
get_optimizerread
Get the multi-armed bandit optimizer status. Shows Thompson Sampling allocation weights, which variants are winning, conversion rates per variant, and Beta distribution parameters.
no parameters
get_triggerread
Get full trigger configuration: events, dormancy, identifier type, destinations, payload templates.
1 parameter
| Parameter | Type | Description |
|---|
trigger_id required | string | Trigger ID |
get_trigger_templateread
Get full details of a trigger payload template.
1 parameter
| Parameter | Type | Description |
|---|
template_id required | string | Template ID |
get_zonesread
Get personalization zones. Pass zone_id to fetch a single zone (recommended when working on a specific zone — full response is 200KB+). Without zone_id, returns all zones for the site.
1 parameter
| Parameter | Type | Description |
|---|
zone_id optional | string | If provided, return only this zone (much smaller response — use this when repairing a specific zone's field_schema) |
list_campaignsread
List all campaigns for the site with status and briefs.
no parameters
list_formsread
List reusable lead-capture form templates for this site. Each has a name and a payload (title, cta_text, tcpa_text, success_text, accent_color, fields[], calendar_enabled).
no parameters
list_lightbox_presetsread
List the canned lightbox display-rule presets (Welcome offer, Exit intent newsletter, Cart recovery, Promo bar, Returning visitor offer). Each preset returns a complete DisplayRules object that can be passed verbatim as create_zone.display_rules or update_zone.display_rules. Use this instead of authoring the rules JSON from scratch unless you need a custom combination.
no parameters
list_offersread
List this site's offer library plus the guardrail dials every offer write is validated against. Returns each non-deleted offer (name, status, offer_type, value_bp / value_cents, code, validity window, stacking, approved, code_verified_at), `site_dials` (max_discount_pct — the hard depth ceiling; gross_margin_pct — null when the operator has not configured one), and a per-offer `break_even` hint: the relative conversion lift the discount must produce just to pay for itself (d / (m - d)), null when no margin rate is set. Call this before authoring or placing anything — it is how you tell a ratified, servable offer (status "live" AND approved AND code_verified_at) from a draft that will render nothing. AXO is a SHADOW promo layer: these rows REFERENCE a discount code the merchant's own platform already honors; we never mint one and never touch redemption accounting.
no parameters
list_page_configsread
List page type configurations (product, category, blog).
no parameters
list_trigger_firesread
Get the audit log of fire attempts for a trigger: successes, errors, skipped fires with skip_reason.
2 parameters
| Parameter | Type | Description |
|---|
trigger_id required | string | Trigger ID |
limit optional | number | Max rows (default 100, max 500) |
list_trigger_templatesread
List available payload templates: system seeds and site-owned custom templates.
no parameters
list_triggersread
List all triggered messages for the site with watch event, mode, destinations, and enabled status.
no parameters
place_lead_formwrite
Deploy a lead-capture form onto a page: creates a lead_form content variant bound to a zone (anchor) + audience. The form INSERTS relative to the anchor by `position` (default "after" — slots between sections without taking over). Target ONE segment via `segment_id`, OR set `all_visitors: true` to show the form to EVERYONE regardless of segment (provide exactly one). Get a payload from list_forms (copy-on-place) or pass one directly. To anchor somewhere that is not yet a zone, call extension_pick_element then create_zone first.
5 parameters
| Parameter | Type | Description |
|---|
zone_id required | string | Anchor zone to place the form relative to |
segment_id optional | string | Audience segment to show it to. Omit when all_visitors is true. |
all_visitors optional | boolean | Show the form to ALL visitors regardless of segment. When true, omit segment_id. |
payload required | object | Form design payload (e.g. from a template via list_forms). Optional form_id string is sent with each capture and drives CRM funnel routing (form_routing). |
position optional | "after" | "before" | "inside_start" | "inside_end" | "replace" | Where the form inserts relative to the anchor element. Default "after". "replace" takes over the anchor (legacy / dedicated containers). |
place_offerwrite
Deploy an offer into a zone: creates a type="offer" content variant bound to a zone + audience. Target ONE segment via `segment_id`, OR set `all_visitors: true` to show it to EVERYONE regardless of segment (provide exactly one). The placement stores ONLY the offer_id plus presentation overrides — the economics (value, code, window, terms) are joined in from the offer row on every config build, which is what makes an offer ending at midnight go dark everywhere instead of leaving a stale code on the page. The renderer writes into the anchor zone's element, so pick a zone whose contents the offer should own (create_zone first if none exists). Two DIFFERENT offers cannot compete in one zone + audience and the write is rejected if you try: the optimizer ranks on conversion rate, so given a choice of depths it always crowns the deepest one and reports the lost margin as a win — competing arms must share one offer and vary only presentation. Placing an unapproved or non-live offer succeeds but serves nothing until a human ratifies it in the dashboard; check status/approved via list_offers first.
5 parameters
| Parameter | Type | Description |
|---|
offer_id required | string | Offer to serve (from list_offers). Must belong to this site and not be deleted. |
zone_id required | string | Zone whose element the offer renders into |
segment_id optional | string | Audience segment to show it to. Omit when all_visitors is true. |
all_visitors optional | boolean | Show the offer to ALL visitors regardless of segment. When true, omit segment_id. |
presentation optional | object | Per-placement presentation overrides — { headline, body, cta, cta_href, show_code, show_expiry }. These win over the offer's own presentation defaults. Never put economics here: value and code come from the offer row. |
restore_content_variantwrite
Restore a content variant. Two modes: (1) pass only variant_id to un-delete a soft-deleted variant; (2) also pass version to roll back the payload to a specific historical version (use get_content_history to find version numbers). Restoring to a version is itself snapshotted, so you can always roll forward again.
2 parameters
| Parameter | Type | Description |
|---|
variant_id required | string | Variant ID to restore |
version optional | integer | History version to restore payload to. Omit to only un-delete. |
suggest_donation_amountsread
Return the segment-aware donation ask ladder for a donor — the amounts a fundraising form should show first, plus the recurrence default + rationale. Pass `segment_suffix` for the default ladder per segment (e.g. "donor_ready", "high_capacity_uncontacted", "lapsed_quarterly"), OR pass `profile_id` to personalise off the donor's CRM-fed attributes (last_gift_amount_cents / recurring_amount_cents / lifetime_giving_cents / capacity_score). When `profile_id` is given without `segment_suffix`, the profile's current segment is used. Returns { ladder: { amounts_cents, recurrence, rationale, highlight_index?, highlight_label? }, resolved }. Amounts are in CENTS. Use before authoring a copy_swap variant that writes the amounts onto a donation form, or when the agent needs to suggest "what should we ask for" from the donor context.
6 parameters
| Parameter | Type | Description |
|---|
segment_suffix optional | string | verticals.ts segment suffix (e.g. donor_ready, high_capacity_uncontacted, lapsed_quarterly). |
profile_id optional | string | Profile to personalise off — last gift, recurring amount, capacity. CRM attributes pulled from profiles.attributes. |
last_gift_amount_cents optional | integer | Override (cents). Useful when profile data is stale or unavailable. |
recurring_amount_cents optional | integer | Override (cents). For recurring_card_declined the ladder surfaces ONLY this amount. |
lifetime_giving_cents optional | integer | Override (cents). Elevates the high-capacity ladder when ≥ $10k. |
capacity_score optional | number | Override (0..1). Elevates the high-capacity ladder when ≥ 0.9. |
test_fire_triggerwrite
Fire a trigger against a test target, bypassing cooldown/rate limits. Goes through the real dispatch pipeline — WILL send real email/SMS.
4 parameters
| Parameter | Type | Description |
|---|
trigger_id required | string | Trigger ID |
profile_id optional | string | Profile ID |
identifier optional | object | Test target identifier |
event_properties optional | object | Override event properties |
update_campaignwrite
Update a campaign name, brief, or status.
8 parameters
| Parameter | Type | Description |
|---|
campaign_id required | string | Campaign ID |
name optional | string | |
brief optional | object | |
status optional | "active" | "draft" | "paused" | "archived" | |
starts_at optional | string | null | |
ends_at optional | string | null | |
eligibility optional | object | null | |
priority optional | number | |
update_content_variantwrite
Update a content variant payload or active status. A history snapshot of the previous state is automatically saved before the update, so changes are always reversible via restore_content_variant. When replacing a copy_swap/hero_swap payload, read-modify-write against the zone's field_schema keys — a re-keyed payload sharing NO renderable key with the zone is refused with a 400 naming the accepted keys.
9 parameters
| Parameter | Type | Description |
|---|
variant_id required | string | Variant ID |
payload optional | object | Updated payload |
active optional | boolean | Enable/disable |
type optional | "overlay" | "hero_swap" | "copy_swap" | "product_reco" | "move_object" | |
eligibility optional | object | |
priority optional | number | |
segment_id optional | string | |
campaign_id optional | string | null | |
destination_page_id optional | string | null | Re-point the route at another page, or null to clear it (strips the vt_route attribution param). On a routed variant the CTA URL stays server-derived; activating re-validates the destination is published. |
update_formwrite
Update a form template name and/or payload.
3 parameters
| Parameter | Type | Description |
|---|
id required | string | Form template ID |
name optional | string | New name |
payload optional | object | Updated form design |
update_offerwrite
Update an offer's terms, or move it between draft / paused / archived. `status` accepts ONLY those three. "live" is a dashboard-only transition gated on human approval plus code verification, and it is refused outright on the API-key path that every agent surface authenticates with — so you CAN pause or archive an offer that is misbehaving, and you CANNOT start one. Ask the operator to approve it in the dashboard instead. Editing the economics (offer_type, value_bp, value_cents, currency, tiers, gift_sku, code) UN-RATIFIES the offer and it stops serving until a human re-approves: approval attaches to specific terms, not to a row id, so a 10%-off offer approved yesterday can never silently become 40% off today — the response reports `approval_reset`. Changing `code` also clears the code verification. Same units and guardrails as create_offer (cents, basis points, the site depth ceiling, the margin floor). OMITTING a field leaves it untouched; sending it as NULL REMOVES it, which is how you make a promo open-ended (ends_at: null = runs indefinitely) or drop a threshold, a cap or a link. Only starts_at, ends_at, min_order_cents, max_discount_cents, max_exposures_per_visitor, code, redeem_url and terms_url accept null — null on anything else is rejected rather than ignored, so a mistaken null never silently no-ops. The response echoes what you removed as `cleared`.
20 parameters
| Parameter | Type | Description |
|---|
id required | string | Offer ID (from list_offers) |
status optional | "draft" | "paused" | "archived" | draft | paused | archived only. "live" is dashboard-only and is rejected on this path — no agent tool can make an offer servable. |
name optional | string | New internal name (max 200 chars) |
offer_type optional | "percent" | "fixed" | "free_shipping" | "gift" | "tiered" | Changing this un-ratifies the offer. |
value_bp optional | integer | Percent discount in BASIS POINTS, 0-10000 (2500 = 25.00%). Changing it un-ratifies the offer. |
value_cents optional | integer | Fixed discount in CENTS. Changing it un-ratifies the offer. |
currency optional | string | 3-letter ISO-4217 code. Changing it un-ratifies the offer. |
code optional | string | null | Discount code as it exists in the merchant's platform. Changing it un-ratifies the offer AND clears code_verified_at, so a human must re-verify it. null removes the code — do this only for an offer that needs none (an automatic discount, a free-shipping threshold), because a code-less offer CANNOT be verified and so cannot go live again until a code is set. |
min_order_cents optional | integer | null | MIRRORED TERM (cents) — fine print only; their platform enforces it. null removes the minimum-spend threshold. |
max_discount_cents optional | integer | null | MIRRORED TERM (cents) — fine print only; their platform enforces it. null removes the cap. |
max_exposures_per_visitor optional | integer | null | How many times WE show it to one visitor — the only cap AXO enforces. null uncaps it. |
starts_at optional | string | null | ISO-8601 start of the validity window. null removes the start date, so it is valid from now. |
ends_at optional | string | null | ISO-8601 end of the validity window; must be after starts_at. null removes the end date, so the offer runs INDEFINITELY until someone pauses it — the only way to express an open-ended promo. |
stacking optional | "exclusive" | "stackable" | "group" | MIRRORED TERM rendered as fine print. |
tiers optional | object[] | Tier ladder for offer_type=tiered (max 10). Changing it un-ratifies the offer. |
gift_sku optional | string | Gift SKU for offer_type=gift. Changing it un-ratifies the offer. |
eligibility optional | object | SegmentPredicate gating who the offer is valid for. |
redeem_url optional | string | null | Where the offer CTA sends the visitor. null removes it. |
terms_url optional | string | null | Full terms page. null removes it. |
presentation optional | object | Default presentation inherited by placements: { headline, body, cta, cta_href, show_code, show_expiry }. |
update_page_configwrite
Update a page type configuration.
3 parameters
| Parameter | Type | Description |
|---|
config_id required | string | Config ID |
url_pattern optional | string | Updated URL pattern |
selectors optional | object | Updated selectors |
update_triggerwrite
Update an existing trigger. Pass only the fields to change.
15 parameters
| Parameter | Type | Description |
|---|
trigger_id required | string | Trigger ID |
name optional | string | |
description optional | string | |
enabled optional | boolean | |
mode optional | "immediate" | "dormancy" | |
watch_event optional | string | |
cancel_event optional | string | |
criteria optional | object | |
delay_ms optional | number | |
window_ms optional | number | |
identifier_type optional | string | |
destinations optional | object[] | |
cooldown_per_profile_ms optional | number | |
global_rate_limit_per_min optional | number | |
optimization_mode optional | "axo_bandit" | "external" | Optimization mode |
update_trigger_templatewrite
Update a site-owned trigger payload template.
6 parameters
| Parameter | Type | Description |
|---|
template_id required | string | Template ID |
name optional | string | |
description optional | string | |
format optional | "json" | "html" | "form" | |
body optional | string | |
example_destination optional | string | |
update_zonewrite
Update an existing zone (selector, name, priority, approval, display_rules, or field schema). To add a missing field (e.g. image_url): call get_zones(zone_id) first to read the existing field_schema, then pass the full updated array with the new entry appended.
7 parameters
| Parameter | Type | Description |
|---|
zone_id required | string | Zone ID |
display_name optional | string | |
element_selector optional | string | |
approved optional | boolean | |
priority optional | number | |
display_rules optional | object | Lightbox display rules — same shape as create_zone.display_rules (incl. audience.country ISO 3166-1 alpha-2 and schedule {days, hours, tz}). Send the full object to overwrite; omit to leave unchanged. Sanitized at the API — invalid country codes and unresolvable timezones are dropped silently, so read the zone back if the targeting matters. |
field_schema optional | object[] | Full replacement field schema — pass the complete array including existing entries you want to keep. Read the current schema first with get_zones(zone_id). |