openapi: 3.0.3
info:
  title: AXO Web Signals for Data 360
  version: "1.0.0"
  description: >-
    Ingestion API schema for the AXO standing stream. Upload this file when
    creating the "AXO" Ingestion API connector in Data 360 Setup, then create
    one data stream per object below. Primary key and event-time fields are
    chosen in the data-stream wizard: see docs/reference/features/api.data360-stream.md.
    Visitor-level objects join on anonymous_id and are all marketing-consent
    gated. Stable identifiers and behavioral history are personal data even
    without an email hash. Aggregate lift requires separate operator opt-in.
paths: {}
components:
  schemas:
    axo_session:
      type: object
      description: One AXO session. Primary key session_id; event time started_at.
      properties:
        session_id: { type: string }
        site_id: { type: string }
        anonymous_id: { type: string, description: "AXO visitor id; the join key on every object" }
        profile_id: { type: string }
        email_sha256: { type: string, description: "SHA-256 of the canonicalized email, when identified and consented" }
        segment_id: { type: string }
        segment_name: { type: string }
        segment_confidence: { type: number }
        in_holdout: { type: boolean, description: "True when this session was held out of personalization" }
        holdout_unit: { type: string, description: "How this session's holdout was assigned - visitor (current) or visit (pre-cutover)" }
        holdout_basis: { type: string, description: "The site's holdout epoch for this window - per_visitor or all_pooled" }
        holdout_comparable: { type: boolean, description: "True when this row belongs to the population AXO's own lift report compares; filter on it before comparing arms" }
        landing_url: { type: string, description: "First page of the session, query string removed" }
        page_count: { type: number }
        duration_ms: { type: number }
        converted: { type: boolean }
        revenue: { type: number }
        started_at: { type: string, format: date-time }
        observed_at: { type: string, format: date-time, description: "When AXO last sent this row" }
    axo_segment_membership:
      type: object
      description: One (session, AXO audience) pair. Primary key membership_id; event time assigned_at.
      properties:
        membership_id: { type: string }
        session_id: { type: string }
        anonymous_id: { type: string }
        segment_id: { type: string }
        segment_name: { type: string }
        confidence: { type: number }
        assigned_at: { type: string, format: date-time }
    axo_exposure:
      type: object
      description: One served content variant on one pageview. Primary key exposure_id; event time exposed_at.
      properties:
        exposure_id: { type: string }
        event_id: { type: string }
        session_id: { type: string }
        anonymous_id: { type: string }
        variant_id: { type: string }
        zone_key: { type: string }
        variant_type: { type: string }
        segment_id: { type: string }
        in_holdout: { type: boolean }
        holdout_unit: { type: string, description: "visitor or visit" }
        holdout_basis: { type: string, description: "per_visitor or all_pooled" }
        holdout_comparable: { type: boolean, description: "Filter on it before comparing arms" }
        page_url: { type: string, description: "Query string removed" }
        exposed_at: { type: string, format: date-time }
    axo_outcome:
      type: object
      description: One conversion or goal completion. Primary key outcome_id; event time occurred_at.
      properties:
        outcome_id: { type: string }
        session_id: { type: string }
        anonymous_id: { type: string }
        revenue: { type: number }
        currency: { type: string }
        order_id: { type: string }
        goal_id: { type: string }
        goal_kind: { type: string }
        goal_priority: { type: string }
        source: { type: string, description: "tag, shopify, webhook, list, …" }
        occurred_at: { type: string, format: date-time }
    axo_audience_lift:
      type: object
      description: >-
        One row per (AXO audience, UTC day) with treatment-vs-holdout arms and the relative
        lift AXO's own report would show. Primary key lift_id; event time computed_at.
        Rows for the trailing two days are re-sent daily as late conversions settle.
      properties:
        lift_id: { type: string, description: "site_id:audience_id:day" }
        site_id: { type: string }
        day: { type: string, format: date }
        audience_id: { type: string }
        audience_name: { type: string }
        audience_kind: { type: string, description: "scorer (AXO behavioral segment), custom (operator-built), or data360 (a published Data 360 segment synced into AXO)" }
        data360_segment_id: { type: string, description: "The Data 360 MarketSegment id when audience_kind is data360" }
        measure: { type: string, description: "variant_eligible (scorer - only sessions on a page with a live variant for the segment, as the AXO Scorecard counts) or audience_holdout (custom/data360 - all of the audience's sessions split by the site holdout)" }
        holdout_basis: { type: string, description: "per_visitor or all_pooled" }
        treatment_sessions: { type: number }
        treatment_conversions: { type: number }
        holdout_sessions: { type: number }
        holdout_conversions: { type: number }
        treatment_cvr: { type: number }
        holdout_cvr: { type: number }
        lift_relative: { type: number, description: "(treatment_cvr - holdout_cvr) / holdout_cvr; null until both arms have 20 sessions that day - pool days before judging" }
        computed_at: { type: string, format: date-time }
        membership_basis: { type: string, description: "session_segment_at_rollup or current_profile_membership; not a historical membership guarantee" }
        membership_observed_at: { type: string, format: date-time, description: "Export observation time" }
        interpretation: { type: string, description: "observed_association; not proof of causal effect" }
    axo_experiment:
      type: object
      description: >-
        One AXO on-site experiment - a layout test or a content A/B/n test - with its
        status, flight window and the decision AXO itself recorded. Primary key
        experiment_id; record modified observed_at. Re-sent daily so a status change or
        a winner shows within a day. Requires the experiment-export opt-in.
      properties:
        experiment_id: { type: string }
        site_id: { type: string }
        kind: { type: string, description: "layout (arms x containers on a page type) or content (fixed-split test of variants in one zone)" }
        name: { type: string }
        hypothesis: { type: string }
        status: { type: string, description: "draft, running, paused or ended" }
        page_type: { type: string, description: "Layout tests - the page type the test is authored under" }
        page_pattern: { type: string, description: "Layout tests - the primary container's page pattern" }
        zone_key: { type: string, description: "The primary container (layout) or the tested zone (content)" }
        zone_name: { type: string }
        audience_id: { type: string, description: "Content tests - the AXO audience the test is limited to" }
        audience: { type: string, description: "Content tests - everyone or segment" }
        control_arm_key: { type: string, description: "The arm that shows the original page" }
        arm_keys: { type: string, description: "Comma-separated arm keys in order, for example A,B,C" }
        arm_count: { type: number }
        split: { type: string, description: "The configured fixed split as JSON text; blank means an equal split" }
        started_at: { type: string, format: date-time }
        ended_at: { type: string, format: date-time }
        scheduled_end_at: { type: string, format: date-time, description: "Layout tests - the scheduled end" }
        measured_from: { type: string, format: date-time, description: "Where the measured window starts; content tests restart it after a split change" }
        measured_since_split_change: { type: boolean }
        stop_reason: { type: string, description: "manual, scheduled, significance or guardrail" }
        winner_arm_key: { type: string, description: "The arm AXO's own peek-safe verdict named; blank until decided" }
        decided_at: { type: string, format: date-time, description: "When AXO recorded the winner" }
        winner_shipped: { type: boolean, description: "True when the winner was promoted to all traffic" }
        site_holdout_pct: { type: number, description: "The site's effective always-on holdout at export time; context only - holdout visitors are never exposed to an arm" }
        site_holdout_changed_at: { type: string, format: date-time }
        observed_at: { type: string, format: date-time, description: "When AXO last sent this row" }
    axo_experiment_daily:
      type: object
      description: >-
        One row per (experiment, arm, UTC day) - sessions first exposed to the arm that
        day, how many converted and the conversion rate. Counts only, attributed the way
        AXO's own results page attributes; pool days before judging and take the verdict
        from axo_experiment. Primary key experiment_day_id; record modified computed_at.
        The trailing two days are re-sent daily as late conversions settle.
      properties:
        experiment_day_id: { type: string, description: "experiment_id:arm_key:day" }
        experiment_id: { type: string }
        site_id: { type: string }
        kind: { type: string, description: "layout or content" }
        day: { type: string, format: date }
        arm_key: { type: string }
        arm_name: { type: string }
        arm_kind: { type: string, description: "control or treatment" }
        is_control: { type: boolean }
        exposures: { type: number, description: "Sessions whose first exposure to this arm fell on this day" }
        conversions: { type: number, description: "Of those sessions, the ones with a conversion at or after that first exposure" }
        conversion_rate: { type: number, description: "conversions / exposures; null when there were no exposures" }
        measurement: { type: string, description: "sessions_first_exposed_on_day" }
        computed_at: { type: string, format: date-time }
    axo_lifecycle_event:
      type: object
      description: >-
        One lifecycle recipe event (abandoned cart, abandoned browse, abandoned checkout,
        high-intent return or lapsed visitor) delivered to Marketing Cloud Next. Primary key
        delivery_id; event time occurred_at. Sent only for people with marketing consent. Carries
        the email hash and never the raw email or phone. Written by triggers installed from the
        AXO lifecycle recipe library; add this object to the data stream before installing one.
      properties:
        delivery_id: { type: string, description: "Stable per moment and destination; a re-fire of the same moment upserts" }
        fire_id: { type: string, description: "AXO fire id of this attempt" }
        event_name: { type: string, description: "For example axo.lifecycle.abandoned_cart" }
        recipe_id: { type: string }
        recipe_version: { type: number }
        trigger_id: { type: string }
        occurred_at: { type: string, format: date-time }
        site_id: { type: string }
        site_domain: { type: string }
        profile_id: { type: string }
        anonymous_id: { type: string, description: "AXO visitor id when known; joins axo_session" }
        email_sha256: { type: string, description: "SHA-256 of the canonicalized email when known" }
        external_id: { type: string }
        consent_basis: { type: string, description: "The consent purpose the event was cleared under - marketing" }
        cart_value: { type: number }
        currency: { type: string }
        resume_url: { type: string, description: "Tracked link back to the cart or page" }
        page_url: { type: string, description: "The page that started the moment" }
        source_event_type: { type: string }
        item_count: { type: number }
        items_json: { type: string, description: "Every line item as JSON text" }
        item_1_product_id: { type: string }
        item_1_sku: { type: string }
        item_1_name: { type: string }
        item_1_url: { type: string }
        item_1_image_url: { type: string }
        item_1_price: { type: number }
        item_1_quantity: { type: number }
        item_2_product_id: { type: string }
        item_2_sku: { type: string }
        item_2_name: { type: string }
        item_2_url: { type: string }
        item_2_image_url: { type: string }
        item_2_price: { type: number }
        item_2_quantity: { type: number }
        item_3_product_id: { type: string }
        item_3_sku: { type: string }
        item_3_name: { type: string }
        item_3_url: { type: string }
        item_3_image_url: { type: string }
        item_3_price: { type: number }
        item_3_quantity: { type: number }
