---
title: "Схема графа кампании"
description: "Форма сохранённого графа кампании — поля узла и связи, объект настройки для каждого типа узла, и ограничения, которым удовлетворяет валидный граф."
canonical: https://docs.afp.monster/ru/reference/campaign-graph-schema
updated: 2026-08-19
pageType: reference
---

Сохранённый граф кампании — это два массива: `nodes`, где каждый несёт тип и объект настройки, и `edges`, где каждый соединяет пронумерованный выход одного узла со входом другого. В графе больше ничего не хранится — имя кампании, её страница и активное состояние живут на кампании, а не в графе. Сохранение записывает новую версию обоих массивов.

## Поля узла

| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| `id` | строка | да | Идентифицирует узел внутри этого графа, и именно на него указывают связи. Заменяется свежим идентификатором при сохранении графа, так что стабилен только в пределах одной версии. |
| `type` | строка | да | Один из типов в [Типы узлов](/ru/reference/node-types), или один из типов только для рантайма, перечисленных ниже. |
| `config` | объект | да | Настройки узла. Пустой объект допустим, и именно с ним начинает только что добавленный узел. |
| `canvas_x` | число | да | Горизонтальная позиция узла на холсте. Восстанавливается при открытии графа. |
| `canvas_y` | число | да | Вертикальная позиция узла на холсте. Восстанавливается при открытии графа. |

## Поля связи

| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| `id` | строка | да | Идентифицирует связь внутри этого графа. |
| `from_node_id` | строка | да | `id` узла, который связь покидает. Должен присутствовать в том же массиве `nodes`, иначе связь отбрасывается при сохранении. |
| `to_node_id` | строка | да | `id` узла, в который связь входит. Должен присутствовать в том же массиве `nodes`, иначе связь отбрасывается при сохранении. |
| `output_index` | число | да | С какого выхода исходного узла уходит связь. `0` — выход по умолчанию; на `condition` `0` — это Match, а `1` — No Match; на `random_split` от `0` до N-1 — это ветви в настроенном порядке. |
| `label` | строка | нет | Текст, отображаемый на связи, взятый из имени исходного выхода — например, `Match` или `No Match`. Выполнения маршрутизируются по `output_index`, никогда по `label`. |

## Разобранный пример

Комментарий запускает кампанию, список ключевых слов решает, по какой ветке пойти, совпавшие люди получают личное сообщение и тег через час, а любое другое выполнение завершается немедленно.

```d2 title="A saved graph: trigger to condition, then send DM, delay and add tag on match, and stop on no match"
direction: down
trigger: trigger_comment
cond: "condition\nkeyword_list" {shape: diamond}
dm: send_dm
wait: "delay 3600s"
tag: add_tag
halt: stop
trigger -> cond
cond -> dm: "Match (output 0)"
dm -> wait -> tag
cond -> halt: "No Match (output 1)"
```

```json
{
  "nodes": [
    {
      "id": "3f6c1a80-5d2b-4c11-9e64-7a0b8d43f215",
      "type": "trigger_comment",
      "config": { "post_scope": "all", "unique_per_user": false },
      "canvas_x": 240,
      "canvas_y": 40
    },
    {
      "id": "9b1d47e2-08af-4d5c-bb37-2c6e5f90a4d1",
      "type": "condition",
      "config": {
        "match_type": "keyword_list",
        "keyword_list_id": 41,
        "reverse_keywords": false,
        "wait_for_reply": false
      },
      "canvas_x": 240,
      "canvas_y": 200
    },
    {
      "id": "c74a2f19-6e30-4b8d-8f52-19d7c3b6e084",
      "type": "send_dm",
      "config": {
        "message_type": "text",
        "text": "Thanks for commenting. The price list is on its way."
      },
      "canvas_x": 80,
      "canvas_y": 380
    },
    {
      "id": "1a58e63d-4c97-42f0-9b18-6d2f7ae5c390",
      "type": "delay",
      "config": { "delay_seconds": 3600, "only_if_no_reply": true },
      "canvas_x": 80,
      "canvas_y": 540
    },
    {
      "id": "e2079b45-13cd-4a6e-8c71-5f83d0b29a6c",
      "type": "add_tag",
      "config": { "tag_ids": [12] },
      "canvas_x": 80,
      "canvas_y": 700
    },
    {
      "id": "7d3fb0c6-92e1-4f57-a840-3b6c1d5e2087",
      "type": "stop",
      "config": {},
      "canvas_x": 440,
      "canvas_y": 380
    }
  ],
  "edges": [
    {
      "id": "5c8e1b73-2f40-4d19-9a6b-8e7d0c34f512",
      "from_node_id": "3f6c1a80-5d2b-4c11-9e64-7a0b8d43f215",
      "to_node_id": "9b1d47e2-08af-4d5c-bb37-2c6e5f90a4d1",
      "output_index": 0,
      "label": ""
    },
    {
      "id": "b06d4a92-7e15-48c3-bf20-6a9c2d813e47",
      "from_node_id": "9b1d47e2-08af-4d5c-bb37-2c6e5f90a4d1",
      "to_node_id": "c74a2f19-6e30-4b8d-8f52-19d7c3b6e084",
      "output_index": 0,
      "label": "Match"
    },
    {
      "id": "48f1c25e-3b7d-4096-8ea4-1c5b7f02d9a3",
      "from_node_id": "9b1d47e2-08af-4d5c-bb37-2c6e5f90a4d1",
      "to_node_id": "7d3fb0c6-92e1-4f57-a840-3b6c1d5e2087",
      "output_index": 1,
      "label": "No Match"
    },
    {
      "id": "a91b7e04-6c58-4d2f-93a1-7e04b8c5f261",
      "from_node_id": "c74a2f19-6e30-4b8d-8f52-19d7c3b6e084",
      "to_node_id": "1a58e63d-4c97-42f0-9b18-6d2f7ae5c390",
      "output_index": 0,
      "label": ""
    },
    {
      "id": "d5720af8-9134-4be6-8c07-2f6a5d18b3e9",
      "from_node_id": "1a58e63d-4c97-42f0-9b18-6d2f7ae5c390",
      "to_node_id": "e2079b45-13cd-4a6e-8c71-5f83d0b29a6c",
      "output_index": 0,
      "label": ""
    }
  ]
}
```

## Ключи настройки по типу узла

| Тип узла | Ключи в `config` |
|---|---|
| `trigger_comment` | `post_scope` (`all` или `specific`), `post_id` (обязателен, когда `post_scope` — `specific`), `unique_per_user` |
| `trigger_dm` | `unique_per_user` |
| `condition` | `match_type` (`any`, `contains`, `exact`, `keyword_list`), `text`, `keyword_list_id`, `reverse_keywords`, `wait_for_reply`, `filter_images`, `filter_gifs`, `filter_links` |
| `send_dm` | `message_type` (`text`, `image`, `card`, `text_with_buttons`), `text`, `image_url`, `title`, `subtitle`, `buttons`, `buttons_text` |
| `reply_comment` | `text` |
| `send_comment` | `text`, `post_id` |
| `hide_comment`, `delete_comment` | `also_block_user` (только Facebook) |
| `delay` | `delay_seconds` (от 1 до 31536000), `only_if_no_reply` |
| `random_split` | `paths` — массив `{ "percent": number }`, где каждый percent от 0 до 100, а сумма не больше 100 |
| `add_tag`, `remove_tag` | `tag_ids` — массив числовых идентификаторов тегов |
| `stop` | нет |

[Типы узлов](/ru/reference/node-types) описывает, что делает каждый ключ и что делает каждый узел, когда до него доходит выполнение.

## Ограничения связей

Холст применяет их, пока связь перетаскивается, так что недопустимая связь никогда не попадает в сохранённый граф.

1. Триггер никогда не является целью связи.
2. Узел никогда не подключается сам к себе.
3. Связь, которая создала бы цикл, отклоняется.
4. Узел принимает одну входящую связь.
5. Каждый выход несёт одну исходящую связь.
6. Триггер подключается только к `condition`.
7. `condition` никогда не подключается к другому `condition`.
8. Действия с комментарием недоступны, когда триггер — `trigger_dm`.
9. После `delete_comment` `reply_comment` и `hide_comment` отклоняются.

На практике граф держит один триггер, хотя форма данных допускает больше одного. Граф, чьи узлы содержат ошибки настройки, вообще нельзя сохранить — вместо этого холст сообщает об ошибках. [Построение кампании на холсте](/ru/guides/build-a-campaign) проводит через сборку графа, который удовлетворяет всему этому.

## Типы узлов вне палитры

Рантайм распознаёт ещё восемь типов, у которых нет плитки в палитре и панели настроек. Граф, построенный на холсте, никогда их не содержит; они появляются только через путь, минующий редактор, так что считайте их типами только для рантайма, а не функциями.

| Тип узла | Что делает выполнение |
|---|---|
| `switch` | Проверяет правила каждой ветви и следует по первой совпавшей, или по ветви по умолчанию, если она настроена. Без совпадения и без ветви по умолчанию выполнение завершается. |
| `wait_until` | Ждёт до настроенного времени, затем продолжает, запланировано так же, как `delay`. |
| `block_user` | Блокирует человека и продолжает. Только Facebook; в Instagram ничего не делает и предупреждает. |
| `unhide_comment`, `typing_indicator`, `mark_seen`, `set_field`, `note` | Выполняемые, но у них нет панели настроек, и источники, стоящие за этой страницей, не фиксируют для них настроек. |

## Что сохранение делает с графом

- Каждое сохранение записывает новую версию графа. Кампания указывает на самую новую версию, и только триггер этой версии запускает новые выполнения.
- Каждый идентификатор узла перегенерируется при сохранении, и связи переназначаются на новые идентификаторы. Поэтому идентификаторы стабильны в пределах версии, но не между сохранениями.
- Связь, чей `from_node_id` или `to_node_id` отсутствует в том же массиве `nodes`, отбрасывается, а остальная часть графа всё равно сохраняется.
- Пустой граф — оба массива пусты — сохраняется успешно. Без триггера в нём ничто не запускает выполнение.
- Выполнение, уже идущее, остаётся на версии, на которой оно началось, так что редактирование и сохранение кампании никогда не меняет выполнения, идущие в процессе.
- Более старые версии удаляются, как только ни одно выполнение на них не ссылается.

## Примечания

- Кнопка `postback` на узле `send_dm` получает собственный выход. При сохранении холст разворачивает каждую ветку кнопки в дополнительные узлы `condition` и подписывает связи идентификатором кнопки; открытие графа сворачивает их обратно в выходы кнопок.
- Выполнение следует по связи, чей `output_index` совпадает с выходом, который выбрал узел. Когда ни одна связь не несёт этот индекс, выполнение завершается на этом узле.
- Выполнение, которое достигает узла, уже посещённого, завершается ошибкой цикла, именно поэтому редактор в принципе отклоняет циклы.
- Ключи настройки в этом справочнике — те, что нужно писать. Некоторые типы узлов также реагируют на более старые написания ключей, сохранённые для того, чтобы графы, сохранённые ранее, всё ещё работали.

## Связанное

- [Типы узлов](/ru/reference/node-types) — полный справочник по каждому узлу для каждого ключа в `config`
- [Построение кампании на холсте](/ru/guides/build-a-campaign) — построение графа, который удовлетворяет ограничениям выше
- [Тест и отладка кампании](/ru/guides/test-and-debug-campaigns) — запуск сохранённого графа и чтение пройденного им пути
- [Состояния выполнения](/ru/reference/execution-states) — состояния, через которые проходит выполнение, продвигаясь по графу
