{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/hail-so/json-schema/hail-so-call-create-schema.json",
  "title": "CallCreate",
  "x-generated": "2026-11-11",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/hail-so-openapi.json#/components/schemas/CallCreate",
  "properties": {
    "recipient_consent": {
      "type": "boolean",
      "title": "Recipient Consent",
      "description": "Attestation that you have obtained the lawful consent required to contact this recipient. Hail does not verify consent itself — you are responsible for a lawful basis under TCPA/ePrivacy/PECR/CAN-SPAM/GDPR as applicable. Rejected (422) if not true."
    },
    "consent_source": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Consent Source",
      "description": "Where/how consent was obtained (e.g. 'signup form', 'prior customer relationship'). Required (non-empty) when message_type is 'marketing'."
    },
    "consent_obtained_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ],
      "title": "Consent Obtained At",
      "description": "When consent was obtained, if known."
    },
    "message_type": {
      "type": "string",
      "enum": [
        "marketing",
        "informational"
      ],
      "title": "Message Type",
      "description": "'marketing' additionally requires a non-empty consent_source. Use 'informational' for transactional/service communications.",
      "default": "informational"
    },
    "to": {
      "type": "string",
      "title": "To",
      "description": "Recipient phone number, E.164 format (e.g. +14155551234)."
    },
    "from": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "From",
      "description": "Caller-id phone number, E.164 format. Must be a number owned by the organization with the voice capability. Omitted: an active org-owned number is used if one exists, else a number is claimed from the shared pool."
    },
    "system_prompt": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "System Prompt",
      "description": "Task instructions for the agent, sent as its leading system message. At least one of system_prompt or llm is required; both together is also valid."
    },
    "llm": {
      "anyOf": [
        {
          "$ref": "#/$defs/LLMConfig"
        },
        {
          "type": "null"
        }
      ],
      "description": "BYO LLM endpoint the call runs on instead of Hail's default model. At least one of system_prompt or llm is required; both together is also valid."
    },
    "first_message": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "First Message",
      "description": "Opening line the agent speaks first. Omitted: the agent waits for the callee to speak first."
    },
    "recording_enabled": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Recording Enabled",
      "description": "Best-effort audio recording for this call. true records and false does not, whatever the workspace setting. Null inherits the agent, then the workspace. Recording failure never prevents the call. The recording-enabled call rate applies regardless of capture outcome."
    },
    "ai_disclosure": {
      "type": "boolean",
      "title": "Ai Disclosure",
      "description": "Speak the AI self-disclosure line ('Hi, this is an AI assistant calling on behalf of ...') as the first thing on the call. Enabled by default. Disable only if you have verified the disclosure is not required for this call — 47 CFR 64.1200(b)(1) requires identifying the initiating business at the start of artificial-voice calls in the US, and several jurisdictions have AI bot-disclosure laws. Hail does not verify this for you. The agent still identifies itself as an AI if asked.",
      "default": true
    },
    "voice_config": {
      "$ref": "#/$defs/VoiceConfig",
      "description": "TTS voice, VAD, turn-detection, and spoken-language settings for this call."
    },
    "metadata": {
      "additionalProperties": true,
      "type": "object",
      "title": "Metadata",
      "description": "Free-form JSON object attached to the call and echoed back on reads. Not interpreted by Hail. Keys starting with 'recording_' and the key 'failure_code' are reserved and rejected."
    },
    "tools": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Tools",
      "description": "Agent tools to allow on this call. Omitted: every tool the organization's configured channels support (new channels appear automatically). Empty list: no tools. Tool names are validated against the server's registry."
    },
    "agent_id": {
      "anyOf": [
        {
          "type": "string",
          "format": "uuid"
        },
        {
          "type": "null"
        }
      ],
      "title": "Agent Id",
      "description": "Place the call with a saved agent (POST /agents). The agent supplies system_prompt, first_message, ai_disclosure, recording_enabled, voice_config, tools and max duration; any of those given explicitly on this request wins. Satisfies the system_prompt or llm requirement on its own. A paused agent is refused with 409."
    }
  },
  "additionalProperties": false,
  "type": "object",
  "required": [
    "recipient_consent",
    "to"
  ],
  "$defs": {
    "LLMConfig": {
      "properties": {
        "base_url": {
          "type": "string",
          "title": "Base Url",
          "description": "Public HTTPS base URL of the BYO LLM endpoint the call runs on. Must resolve to a public address — private/internal hosts are rejected."
        },
        "api_key": {
          "type": "string",
          "title": "Api Key",
          "description": "API key sent to the BYO LLM endpoint. Write-only — never echoed back."
        },
        "model": {
          "type": "string",
          "title": "Model",
          "description": "Model name to request from the BYO LLM endpoint."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "required": [
        "base_url",
        "api_key",
        "model"
      ],
      "title": "LLMConfig"
    },
    "VoiceConfig": {
      "properties": {
        "tts": {
          "type": "string",
          "const": "cartesia",
          "title": "Tts",
          "description": "Text-to-speech provider. Currently only 'cartesia'.",
          "default": "cartesia"
        },
        "vad": {
          "type": "string",
          "const": "silero",
          "title": "Vad",
          "description": "Voice-activity-detection engine. Currently only 'silero'.",
          "default": "silero"
        },
        "turn_detection": {
          "type": "string",
          "const": "livekit",
          "title": "Turn Detection",
          "description": "Turn-detection engine. Currently only 'livekit'.",
          "default": "livekit"
        },
        "voice_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Voice Id",
          "description": "Per-call TTS voice override, applied to whichever TTS provider serves the call. Omitted: the organization's or environment's default voice."
        },
        "language": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "ar",
                "bg",
                "bn",
                "cs",
                "da",
                "de",
                "el",
                "en",
                "es",
                "fi",
                "fr",
                "gu",
                "he",
                "hi",
                "hr",
                "hu",
                "id",
                "it",
                "ja",
                "kn",
                "ko",
                "mr",
                "ms",
                "nl",
                "no",
                "pl",
                "pt",
                "ro",
                "ru",
                "sk",
                "sv",
                "ta",
                "te",
                "th",
                "tl",
                "tr",
                "uk",
                "vi",
                "zh"
              ]
            },
            {
              "type": "null"
            }
          ],
          "title": "Language",
          "description": "Spoken language for the call as a lowercase ISO 639-1 code (e.g. 'da'). One of the 39 supported codes — see docs/languages.md. Applied to STT, TTS, and turn detection. Omitted: the providers' defaults (English)."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "title": "VoiceConfig"
    }
  }
}