{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/hellou-ai/json-schema/hellou-ai-voice-campaign-report-stats-schema.json",
  "title": "voice_campaign_report_stats",
  "x-generated": "2026-11-11",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/hellou-ai-openapi.json#/components/schemas/voice_campaign_report_stats",
  "type": "object",
  "properties": {
    "agents": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "agent_id": {
            "type": "integer"
          },
          "agent_name": {
            "type": "string"
          },
          "contacts_handled": {
            "type": "integer"
          },
          "acw_timeouts": {
            "type": "integer",
            "description": "Contacts the agent left without a disposition until the wrap-up timeout."
          },
          "avg_handle_time_secs": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      }
    },
    "avg_handle_time_secs": {
      "type": [
        "integer",
        "null"
      ],
      "description": "Average handle time across the campaign, in seconds."
    },
    "ai": {
      "type": [
        "object",
        "null"
      ],
      "description": "An AI voice campaign's calls (test calls excluded). Null for a campaign worked by people.",
      "properties": {
        "calls": {
          "type": "integer"
        },
        "answered": {
          "type": "integer",
          "description": "Calls answered, by a person or by a machine such as an answering machine."
        },
        "answered_by_person": {
          "type": "integer",
          "description": "Calls a person picked up: voicemail, call screeners and carrier announcements left out."
        },
        "minutes": {
          "type": "number",
          "description": "AI minutes of answered calls, billed per second, two decimals."
        },
        "avg_duration_secs": {
          "type": [
            "integer",
            "null"
          ]
        },
        "transferred": {
          "type": "integer",
          "description": "Calls handed to a person."
        },
        "voicemail": {
          "type": "integer"
        },
        "opt_outs": {
          "type": "integer",
          "description": "Contacts who asked not to be called again, said during the call or recorded as one of the campaign's do-not-call outcomes."
        },
        "hot_leads": {
          "type": "integer",
          "description": "Contacts a call handed to a person as a hot lead."
        },
        "callbacks_pending": {
          "type": "integer",
          "description": "Contacts waiting for a call back they asked for."
        },
        "by_decided_by": {
          "type": "object",
          "description": "Contacts per who decided their outcome (`ai_agent`, `jev`, `post_call`, `human`, `system`).",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "dispositions": {
          "type": "object",
          "description": "Contacts per disposition key.",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "dial_causes": {
          "type": "object",
          "description": "Calls that reached nobody, per cause code (`voicemail_detected`, `busy`, `declined`, `unavailable`, `no_answer`, `number_not_found`, `no_route`, `carrier_rejected`, `timeout`, `platform_error`, `unknown`). Causes with no calls are left out.",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "unreached": {
          "type": "object",
          "description": "Contacts the campaign has not reached, by why. `other` is a contact whose last call ended with a disposition the agent recorded.",
          "properties": {
            "failed": {
              "type": "integer",
              "description": "Contacts whose call failed."
            },
            "no_answer": {
              "type": "integer",
              "description": "Contacts nobody answered for, a busy line included."
            },
            "voicemail": {
              "type": "integer",
              "description": "Contacts a machine answered for."
            },
            "no_conversation": {
              "type": "integer",
              "description": "Contacts given up on after the call broke off during the opening, before any conversation, or after a call whose summary could not tell what it came to."
            },
            "other": {
              "type": "integer"
            }
          },
          "required": [
            "failed",
            "no_answer",
            "voicemail",
            "no_conversation",
            "other"
          ]
        },
        "undecided": {
          "$ref": "#/$defs/undecided_calls"
        }
      }
    },
    "fallback_groups": {
      "type": [
        "object",
        "null"
      ],
      "description": "The campaign's groups (for example companies whose people are called one after another) by outcome. Null when the campaign has no groups.",
      "properties": {
        "groups": {
          "type": "integer"
        },
        "contacts": {
          "type": "integer"
        },
        "max_chain": {
          "type": "integer",
          "description": "The most people in one group."
        },
        "reached": {
          "type": "integer",
          "description": "Groups where someone was reached; the rest of the group is not called."
        },
        "reached_by_order": {
          "type": "object",
          "description": "Reached groups per the place in the chain of the person who was reached.",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "open": {
          "type": "integer",
          "description": "Groups still being called."
        },
        "unreached": {
          "type": "integer",
          "description": "Groups where nobody was reached. A follow-up campaign calls them again."
        },
        "no_number": {
          "type": "integer",
          "description": "Groups with no number that could be dialled."
        }
      }
    },
    "interest": {
      "type": "object",
      "description": "Contacts of the campaign per interest (see `interest` on a contact result). Only contacts that have an interest are counted.",
      "properties": {
        "counts": {
          "type": "object",
          "properties": {
            "interested": {
              "type": "integer"
            },
            "undecided": {
              "type": "integer"
            },
            "not_interested": {
              "type": "integer"
            },
            "not_presented": {
              "type": "integer"
            }
          },
          "required": [
            "interested",
            "undecided",
            "not_interested",
            "not_presented"
          ]
        },
        "interested_groups": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Groups with at least one interested contact. Null when the campaign has no groups."
        }
      },
      "required": [
        "counts",
        "interested_groups"
      ]
    },
    "variable_breakdowns": {
      "type": "array",
      "description": "How many contacts carry each value of a contact-file column the AI agent declares as a variable, over every contact\nof the campaign. A list or yes/no variable always appears; a text variable only when the file has at most 10 distinct\nvalues; numbers and dates are left out. Values that differ only in case or surrounding spaces count as one. Empty\nfor a campaign worked by people.\n",
      "items": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "The declared variable's key. Use it in the `variables` filter of the contacts list."
          },
          "label": {
            "type": "string",
            "description": "The variable's name in the requester's language."
          },
          "type": {
            "type": "string",
            "enum": [
              "enum",
              "boolean",
              "string"
            ]
          },
          "distinct_count": {
            "type": "integer",
            "description": "Distinct non-blank values in the file."
          },
          "values": {
            "type": "array",
            "description": "Most contacts first, at most 50 values. A blank cell is the last row, with `value` null.",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The value as written in the file; null for contacts with the cell blank."
                },
                "contacts": {
                  "type": "integer"
                },
                "reached": {
                  "type": "integer",
                  "description": "Contacts of this value that were answered."
                }
              },
              "required": [
                "value",
                "contacts",
                "reached"
              ]
            }
          }
        },
        "required": [
          "key",
          "label",
          "type",
          "distinct_count",
          "values"
        ]
      }
    }
  },
  "$defs": {
    "undecided_calls": {
      "type": "object",
      "description": "Voice campaign calls that ended with no disposition (neither the AI agent nor the call summary chose one), by why. A call the platform settled with its own disposition when it ended is not counted; an undetermined call is still counted, although its contact is retried as no_conversation.",
      "properties": {
        "calls": {
          "type": "integer",
          "description": "Undecided calls, every reason together."
        },
        "undetermined": {
          "type": "integer",
          "description": "The summary could not tell which disposition applied."
        },
        "missing": {
          "type": "integer",
          "description": "The summary named no disposition."
        },
        "unknown": {
          "type": "integer",
          "description": "The summary named a disposition the campaign does not have."
        },
        "failed": {
          "type": "integer",
          "description": "The summary could not be made."
        }
      },
      "required": [
        "calls",
        "undetermined",
        "missing",
        "unknown",
        "failed"
      ]
    }
  }
}