{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://docs.advertising.chat/schemas/flow-creative-1.json",
  "title": "Advertising.chat Flow Creative",
  "type": "object",
  "required": [
    "specVersion",
    "entryNodeId",
    "metadata",
    "nodes"
  ],
  "additionalProperties": false,
  "properties": {
    "specVersion": {
      "type": "string",
      "pattern": "^1(\\.[0-9]+)?$",
      "description": "Spec major.minor. v1 executors MUST reject an unknown major and MUST ignore unknown fields introduced by minors."
    },
    "entryNodeId": {
      "type": "string",
      "description": "id of the node where execution starts. Must reference a message node."
    },
    "metadata": {
      "type": "object",
      "required": [
        "advertiserName",
        "disclosureText"
      ],
      "additionalProperties": false,
      "properties": {
        "advertiserName": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80
        },
        "disclosureText": {
          "type": "string",
          "minLength": 1,
          "maxLength": 100,
          "description": "REQUIRED. Shown/spoken before or with the entry node on every surface (e.g. 'Sponsored · Acme Coffee'). Executors MUST NOT suppress it."
        },
        "brandLogoUrl": {
          "type": "string",
          "format": "uri"
        },
        "teaser": {
          "type": "string",
          "maxLength": 120,
          "description": "One-line invitation used by the web embed's collapsed state and as optional SMS lead-in. Defaults to the entry node's text truncated."
        },
        "language": {
          "type": "string",
          "default": "en"
        }
      }
    },
    "nodes": {
      "type": "array",
      "minItems": 1,
      "maxItems": 12,
      "items": {
        "$ref": "#/$defs/node"
      }
    }
  },
  "$defs": {
    "node": {
      "oneOf": [
        {
          "$ref": "#/$defs/messageNode"
        },
        {
          "$ref": "#/$defs/choiceSetNode"
        },
        {
          "$ref": "#/$defs/actionNode"
        }
      ]
    },
    "nodeId": {
      "type": "string",
      "pattern": "^[a-z0-9][a-z0-9_-]{0,31}$"
    },
    "messageNode": {
      "type": "object",
      "required": [
        "id",
        "type",
        "text"
      ],
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/nodeId"
        },
        "type": {
          "const": "message"
        },
        "text": {
          "type": "string",
          "minLength": 1,
          "maxLength": 640,
          "description": "Advertiser-authored copy. Executors MUST render verbatim."
        },
        "smsText": {
          "type": "string",
          "maxLength": 320,
          "description": "Optional SMS-length variant. If absent, SMS uses `text`."
        },
        "imageUrl": {
          "type": "string",
          "format": "uri"
        },
        "imageAltText": {
          "type": "string",
          "maxLength": 200
        },
        "next": {
          "$ref": "#/$defs/nodeId",
          "description": "Unconditional edge. Required unless this node is followed by nothing (invalid — message nodes must lead somewhere; terminals are action nodes)."
        }
      }
    },
    "choiceSetNode": {
      "type": "object",
      "required": [
        "id",
        "type",
        "prompt",
        "choices"
      ],
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/nodeId"
        },
        "type": {
          "const": "choice-set"
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 640
        },
        "smsPrompt": {
          "type": "string",
          "maxLength": 320
        },
        "choices": {
          "type": "array",
          "minItems": 1,
          "maxItems": 4,
          "items": {
            "type": "object",
            "required": [
              "id",
              "label",
              "target"
            ],
            "additionalProperties": false,
            "properties": {
              "id": {
                "$ref": "#/$defs/nodeId"
              },
              "label": {
                "type": "string",
                "minLength": 1,
                "maxLength": 25
              },
              "target": {
                "$ref": "#/$defs/nodeId"
              }
            }
          }
        },
        "intents": {
          "type": "array",
          "maxItems": 4,
          "description": "NLU edges, executed by the publisher's LLM (AdKit) or by keyword match on dumb channels. Advisory on SMS.",
          "items": {
            "type": "object",
            "required": [
              "intent",
              "description",
              "examples",
              "target"
            ],
            "additionalProperties": false,
            "properties": {
              "intent": {
                "type": "string",
                "pattern": "^[a-z0-9_]{1,32}$"
              },
              "description": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "description": "Natural-language definition of when this edge fires, written for an LLM. e.g. 'The user asks about price, cost, or discounts.'"
              },
              "examples": {
                "type": "array",
                "minItems": 1,
                "maxItems": 5,
                "items": {
                  "type": "string",
                  "maxLength": 100
                }
              },
              "target": {
                "$ref": "#/$defs/nodeId"
              }
            }
          }
        },
        "noMatchTarget": {
          "$ref": "#/$defs/nodeId",
          "description": "Edge taken when free text matches no intent. If absent, executors re-present the choices once, then treat a second miss as flow_abandon(reason:'no_match')."
        }
      }
    },
    "actionNode": {
      "type": "object",
      "required": [
        "id",
        "type",
        "action"
      ],
      "additionalProperties": false,
      "properties": {
        "id": {
          "$ref": "#/$defs/nodeId"
        },
        "type": {
          "const": "action"
        },
        "text": {
          "type": "string",
          "maxLength": 640,
          "description": "Optional closing copy rendered with the action."
        },
        "action": {
          "oneOf": [
            {
              "type": "object",
              "required": [
                "kind",
                "url",
                "label"
              ],
              "additionalProperties": false,
              "properties": {
                "kind": {
                  "const": "link_out"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "label": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 25
                }
              }
            },
            {
              "type": "object",
              "required": [
                "kind",
                "conversionName"
              ],
              "additionalProperties": false,
              "properties": {
                "kind": {
                  "const": "conversion"
                },
                "conversionName": {
                  "type": "string",
                  "maxLength": 64
                },
                "value": {
                  "type": "number"
                },
                "cur": {
                  "type": "string",
                  "maxLength": 3
                }
              }
            },
            {
              "type": "object",
              "required": [
                "kind",
                "url",
                "label"
              ],
              "additionalProperties": false,
              "properties": {
                "kind": {
                  "const": "handoff"
                },
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "Deep link to the advertiser's own conversation surface (their bot, WhatsApp, an AnyChat agent, ...)."
                },
                "label": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 25
                }
              }
            },
            {
              "type": "object",
              "required": [
                "kind"
              ],
              "additionalProperties": false,
              "properties": {
                "kind": {
                  "const": "end"
                }
              }
            }
          ]
        },
        "next": {
          "$ref": "#/$defs/nodeId",
          "description": "Optional continuation after a conversion action. link_out / handoff / end are terminal; `next` is invalid on them."
        }
      }
    }
  }
}
