{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/cekura-ai/json-schema/cekura-ai-project-schema.json",
  "title": "Project",
  "x-generated": "2026-10-29",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/cekura-ai-openapi.json#/components/schemas/Project",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "readOnly": true
    },
    "name": {
      "type": "string",
      "description": "\nName of the project\nExample: `\"Default Project\"`\n"
    },
    "members": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/MembershipInline"
      },
      "readOnly": true
    },
    "organization": {
      "allOf": [
        {
          "$ref": "#/$defs/OrganizationInline"
        }
      ],
      "readOnly": true
    },
    "organization_id": {
      "type": "integer",
      "writeOnly": true,
      "description": "ID of the organization to create the project under. Required when authenticating as a user (you must be an admin of that organization); omit it when using an organization-scoped API key, where the organization is inferred from the key."
    },
    "outbound_timeout": {
      "type": "integer",
      "description": "\nOutbound timeout in seconds\nExample: `300`\n"
    },
    "max_call_duration": {
      "type": "integer",
      "description": "\nMax call duration in seconds\nExample: `300`\n"
    },
    "min_call_duration": {
      "type": "integer",
      "description": "\nMin call duration in seconds\nExample: `30`\n"
    },
    "sample_rate": {
      "type": "number",
      "format": "double",
      "description": "\nMetric sampling rate (0.0 to 1.0). Default is 1.0 (100% of calls).\nExample: `0.5` (50%), `0.1` (10%), `0.001` (0.1%)\n"
    },
    "max_messages_chat_runs_limit": {
      "type": "integer",
      "description": "\nMax messages chat runs limit\nExample: `100`\n"
    },
    "webhook_url": {
      "type": "string",
      "format": "uri",
      "description": "\nWebhook URL\nExample: `\"https://yourdomain.com/webhook\"`\n"
    },
    "webhook_secret": {
      "type": "string",
      "writeOnly": true,
      "description": "\nWebhook secret. Sent in `X-CEKURA-SECRET` header when invoking `webhook_url`.\nWrite-only — never returned in API responses. Use `webhook_secret_configured`\nto check whether one is set.\nExample: `\"your-webhook-secret\"`\n"
    },
    "webhook_secret_configured": {
      "type": "string",
      "readOnly": true
    },
    "evaluate_relevant_metrics_enabled": {
      "type": "boolean",
      "description": "\nEvaluate relevant metrics enabled\nExample: `true` or `false`\n"
    },
    "end_call_enabled": {
      "type": "boolean",
      "description": "\nEnd call enabled\nExample: `true` or `false`\n"
    },
    "generate_scenario_auto_assign_numbers_enabled": {
      "type": "boolean",
      "description": "\nGenerate scenario auto assign numbers enabled\nExample: `true` or `false`\n"
    },
    "enable_client_side_testing": {
      "type": "boolean",
      "description": "\nEnable client side testing\nExample: `true` or `false`\n"
    },
    "use_provider_transcript": {
      "type": "boolean",
      "description": "When true, evaluations use the provider transcript instead of Cekura transcript with provider side tool calls. Defaults to false."
    },
    "reconcile_transcripts_with_cekura": {
      "type": "boolean",
      "description": "When true, observability calls that arrive with both a recording and a provider transcript are transcribed by Cekura, and for dual-channel audio the Cekura transcript is published before metrics run (provider tool and system events are kept). Mono audio and transcription failures keep the provider transcript. Billed per minute of audio at the reconciliation rate whenever a Cekura transcript is published. The create-CallLog API field `reconcile_with_cekura_transcript` overrides this per request. Defaults to false."
    },
    "should_show_powered_by": {
      "type": "boolean",
      "description": "\nShould show powered by\nExample: `true` or `false`\n"
    },
    "deep_research_enabled": {
      "type": "boolean",
      "description": "\nEnable Deep Research for this project. Customers own this from project\nsettings: turning it on surfaces the Deep Research section and opts the\nproject into the weekly audit cron and the on-demand Generate button.\nThe global DEEP_RESEARCH_ENABLED SettingValue remains a kill switch above\nit.\nExample: `true` or `false`\n"
    },
    "notify_results_completion": {
      "type": "boolean",
      "description": "\nNotify on result completion\nExample: `true` or `false`\n"
    },
    "notify_daily_report": {
      "type": "boolean",
      "description": "\nNotify daily report\nExample: `true` or `false`\n"
    },
    "notify_daily_report_no_calls": {
      "type": "boolean",
      "description": "\nNotify daily report even if there are no calls\nExample: `true` or `false`\n"
    },
    "daily_report_ai_summary_enabled": {
      "type": "boolean",
      "description": "\nReplace the default daily report data dump with an AI-narrated Top Issues\nsection in Slack and email. Off by default. When on, Slack also exposes a\n\"Show more\" button that opens a modal with the legacy detail sections.\nExample: `true` or `false`\n"
    },
    "notify_cron_failure": {
      "type": "boolean",
      "description": "\nNotify cron failure\nExample: `true` or `false`\n"
    },
    "notify_cron_success": {
      "type": "boolean",
      "description": "\nNotify cron success\nExample: `true` or `false`\n"
    },
    "notify_no_calls": {
      "type": "boolean",
      "description": "\nNotify no calls\nExample: `true` or `false`\n"
    },
    "no_calls_notify_time_period": {
      "description": "\nConfiguration for no calls notification time periods, timeframes, exception numbers, and agents to exclude\nExample:\n```json\n{\n    \"time_zone\": \"America/New_York\",\n    \"time_periods\": [\n        {\n            \"start_time\": \"09:00\",\n            \"end_time\": \"17:00\",\n            \"time_frame\": 30\n        },\n        {\n            \"start_time\": \"18:00\",\n            \"end_time\": \"23:59\",\n            \"time_frame\": 60\n        }\n    ],\n    \"exception_numbers\": [\"+15551234567\", \"+14155552671\"],\n    \"agents\": [1, 10, 90]\n}\n```\nNote:\n- Calls with customer_number in exception_numbers list will be ignored when checking for no calls alert.\n- If agents list is provided and not empty, those specific agents will be excluded from monitoring. All other agents in the project will be monitored. If omitted or empty, all agents in the project will be monitored.\n"
    },
    "notify_daily_report_webhook": {
      "type": "boolean",
      "description": "\nNotify daily report webhook\nExample: `true` or `false`\n"
    },
    "notify_pre_run_webhook": {
      "type": "boolean",
      "description": "\nSend a webhook at the start of a run (before calls dispatch) to configure the external test sandbox\nExample: `true` or `false`\n"
    },
    "daily_report_dashboard_ids": {
      "type": "array",
      "items": {
        "type": "integer"
      },
      "description": "\nDashboard IDs to include as visual snapshots in the daily report\nExample: `[1, 2, 3]`\n"
    },
    "notify_result_webhook_failed": {
      "type": "boolean",
      "description": "\nNotify result webhook failed\nExample: `true` or `false`\n"
    },
    "notify_result_webhook_success": {
      "type": "boolean",
      "description": "\nNotify result webhook success\nExample: `true` or `false`\n"
    },
    "notify_result_webhook_error": {
      "type": "boolean",
      "description": "\nNotify result webhook for error runs (runs with error_message)\nExample: `true` or `false`\n"
    },
    "notify_cronjob_webhook_failed": {
      "type": "boolean",
      "description": "\nNotify cronjob webhook failed\nExample: `true` or `false`\n"
    },
    "notify_cronjob_webhook_success": {
      "type": "boolean",
      "description": "\nNotify cronjob webhook success\nExample: `true` or `false`\n"
    },
    "notify_cronjob_webhook_error": {
      "type": "boolean",
      "description": "\nNotify cronjob webhook for error runs (runs with error_message)\nExample: `true` or `false`\n"
    },
    "notify_call_log_webhook_failed": {
      "type": "boolean",
      "description": "\nNotify call log webhook failed\nExample: `true` or `false`\n"
    },
    "notify_call_log_webhook_success": {
      "type": "boolean",
      "description": "\nNotify call log webhook success\nExample: `true` or `false`\n"
    },
    "call_log_webhook_redact_fields": {
      "type": "array",
      "items": {
        "enum": [
          "person_name",
          "age",
          "phone_number",
          "email_address",
          "date",
          "time",
          "location",
          "origin",
          "gender_sexuality",
          "physical_attribute",
          "occupation",
          "username",
          "password",
          "ip_address",
          "url",
          "filename",
          "event",
          "vehicle_id",
          "dob",
          "healthcare_number",
          "medical_professional",
          "credit_card",
          "account_number",
          "bank_account",
          "money",
          "ssn",
          "passport_number",
          "driver_license",
          "numerical_pii"
        ],
        "type": "string",
        "description": "* `person_name` - person_name\n* `age` - age\n* `phone_number` - phone_number\n* `email_address` - email_address\n* `date` - date\n* `time` - time\n* `location` - location\n* `origin` - origin\n* `gender_sexuality` - gender_sexuality\n* `physical_attribute` - physical_attribute\n* `occupation` - occupation\n* `username` - username\n* `password` - password\n* `ip_address` - ip_address\n* `url` - url\n* `filename` - filename\n* `event` - event\n* `vehicle_id` - vehicle_id\n* `dob` - dob\n* `healthcare_number` - healthcare_number\n* `medical_professional` - medical_professional\n* `credit_card` - credit_card\n* `account_number` - account_number\n* `bank_account` - bank_account\n* `money` - money\n* `ssn` - ssn\n* `passport_number` - passport_number\n* `driver_license` - driver_license\n* `numerical_pii` - numerical_pii",
        "x-spec-enum-id": "99d2b7efd95189c4"
      },
      "description": "\nPII fields to redact from outgoing call log webhook payloads (including metadata and dynamic variables).\nStored call logs are not changed. Empty list disables it.\n`voice_recording_url` is sent as null unless the agent's own `redact_fields` include every field listed here\n(recordings ingested before the agent enabled redaction are not redacted), and provider recording/log links in\n`metadata` are removed. Forwarded raw provider webhooks (`forward_vapi_webhook`, `forward_retell_webhook`) are not redacted.\nExample: `[\"ssn\", \"dob\", \"email_address\"]`\n"
    },
    "skip_call_log_webhook_no_content": {
      "type": "boolean",
      "description": "\nSkip call log webhooks for calls with no conversation content: no spoken transcript, or a busy / no-answer ended reason\nExample: `true` or `false`\n"
    },
    "notify_frequent_calls_for_number": {
      "type": "boolean"
    },
    "frequent_calls_threshold": {
      "type": "integer",
      "maximum": 2147483647,
      "minimum": -2147483648
    },
    "frequent_calls_time_period": {
      "type": "integer",
      "maximum": 2147483647,
      "minimum": -2147483648
    },
    "frequent_calls_cooldown": {
      "type": "integer",
      "maximum": 2147483647,
      "minimum": -2147483648
    },
    "forward_vapi_webhook": {
      "type": "boolean",
      "description": "\nForward vapi webhook\nExample: `true` or `false`\n"
    },
    "forward_retell_webhook": {
      "type": "boolean",
      "description": "\nForward retell webhook\nExample: `true` or `false`\n"
    },
    "is_send_emails_enabled": {
      "type": "boolean",
      "description": "\nIs send emails enabled\nExample: `true` or `false`\n"
    },
    "rubric_config": {
      "description": "\nProject-level scoring rubric for evaluating call success. Each call log produced under this project is run against the rubric and gets a single pass/fail outcome based on its metric results.\n\nIMPORTANT (read before writing):\n- NEVER blindly overwrite this field. Always first GET the project, read the existing `rubric_config`, then merge in only the rule(s) you intend to add/update, and PATCH the full merged value. Sending a partial value will silently delete every rule you did not include.\n\nStructure:\n- `evaluation_logic`: MUST be `\"and\"`. All rules have to pass for a call to be marked successful. (Other values are not supported by the product currently.)\n- `rules`: List of rules. Use one rule per (metric, condition) — keep each rule simple, with a single condition inside.\n\nEach rule shape:\n```json\n{\n    \"metric_id\": <metric id>,\n    \"conditions\": {\n        \"operator\": \"and\",\n        \"conditions\": [\n            {\"field\": <field>, \"op\": <op>, \"value\": <value>}\n        ]\n    }\n}\n```\n\nWhat `field`, `op`, and `value` to use depends on the metric type. The three supported cases:\n\n1. Numeric / score metrics (qualitative scores, numeric metrics, workflow adherence with a score, etc.):\n   - `field`: `\"score\"`\n   - `op`: one of `\"gt\"`, `\"gte\"`, `\"lt\"`, `\"lte\"`, `\"eq\"`\n   - `value`: a number (qualitative scores are on a 0–5 scale)\n   - Example (score must be at least 4): `{\"field\": \"score\", \"op\": \"gte\", \"value\": 4}`\n\n2. Boolean / binary metrics (eval_type `binary`, or `evaluate_expected_outcome`):\n   - `field`: `\"score\"`\n   - `op`: `\"eq\"` or `\"neq\"`\n   - `value`: `true` for pass, `false` for fail\n   - Example (must pass): `{\"field\": \"score\", \"op\": \"eq\", \"value\": true}`\n   - Example (must fail): `{\"field\": \"score\", \"op\": \"eq\", \"value\": false}`\n   - Legacy values `5` (pass) and `0` (fail) are still accepted for back-compat but new rules should use bool.\n\n3. Enum metrics (eval_type `enum`, or `observability_topics` / `observability_dropoff`):\n   - `field`: `\"enum\"`\n   - `op`: `\"eq\"`, `\"neq\"`, or `\"in\"`\n   - `value`: a single string for `eq`/`neq`, or a list of strings for `in`. Must match one of the metric's defined `enum_values`.\n   - Example: `{\"field\": \"enum\", \"op\": \"eq\", \"value\": \"resolved\"}`\n   - Example (any of): `{\"field\": \"enum\", \"op\": \"in\", \"value\": [\"resolved\", \"partially_resolved\"]}`\n\nFull example:\n```json\n{\n    \"evaluation_logic\": \"and\",\n    \"rules\": [\n        {\n            \"metric_id\": 115164,\n            \"conditions\": {\n                \"operator\": \"and\",\n                \"conditions\": [\n                    {\"op\": \"eq\", \"field\": \"score\", \"value\": true}\n                ]\n            }\n        },\n        {\n            \"metric_id\": 5512,\n            \"conditions\": {\n                \"operator\": \"and\",\n                \"conditions\": [\n                    {\"op\": \"gte\", \"field\": \"score\", \"value\": 4}\n                ]\n            }\n        },\n        {\n            \"metric_id\": 8821,\n            \"conditions\": {\n                \"operator\": \"and\",\n                \"conditions\": [\n                    {\"op\": \"in\", \"field\": \"enum\", \"value\": [\"resolved\", \"partially_resolved\"]}\n                ]\n            }\n        }\n    ]\n}\n```\n\nNotes:\n- `metric_id` must reference a metric belonging to this project (project-scoped or agent-scoped under this project); unknown ids are rejected.\n- Rules whose metric did not produce a value on a given call are skipped (neither pass nor fail) and do not affect the outcome.\n- Pass an empty object `{}` to clear the rubric.\n"
    },
    "timezone": {
      "type": "string",
      "description": "\nProject Timezone\n"
    },
    "vapi_api_key": {
      "type": "string",
      "writeOnly": true,
      "description": "\nVAPI Private API Key. Used for all VAPI operations (calls, chat, and testing).\nExample: `\"vapi_api_key_123\"`\n"
    },
    "vapi_api_key_configured": {
      "type": "string",
      "readOnly": true
    },
    "vapi_public_key_configured": {
      "type": "string",
      "readOnly": true
    },
    "retell_api_key": {
      "type": "string",
      "writeOnly": true,
      "description": "\nRetell API key\nExample: `\"retell_api_key_123\"`\n"
    },
    "retell_api_key_configured": {
      "type": "string",
      "readOnly": true
    },
    "syntflow_api_key": {
      "type": "string",
      "writeOnly": true,
      "description": "\nSyntflow API key\nExample: `\"syntflow_api_key_123\"`\n"
    },
    "syntflow_api_key_configured": {
      "type": "string",
      "readOnly": true
    },
    "elevenlabs_api_key": {
      "type": "string",
      "writeOnly": true,
      "description": "\nElevenLabs API key\nExample: `\"elevenlabs_api_key_123\"`\n"
    },
    "elevenlabs_api_key_configured": {
      "type": "string",
      "readOnly": true
    },
    "bland_api_key": {
      "type": "string",
      "writeOnly": true,
      "description": "\nBland API key\nExample: `\"bland_api_key_123\"`\n"
    },
    "bland_api_key_configured": {
      "type": "string",
      "readOnly": true
    },
    "livekit_api_key": {
      "type": "string",
      "writeOnly": true,
      "description": "\nLiveKit API key\nExample: `\"livekit_api_key_123\"`\n"
    },
    "livekit_api_key_configured": {
      "type": "string",
      "readOnly": true
    },
    "pipecat_api_key": {
      "type": "string",
      "writeOnly": true,
      "description": "\nPipecat/Daily.co API key\nExample: `\"pipecat_api_key_123\"`\n"
    },
    "pipecat_api_key_configured": {
      "type": "string",
      "readOnly": true
    },
    "is_deleted": {
      "type": "boolean"
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    },
    "updated_at": {
      "type": "string",
      "format": "date-time",
      "readOnly": true
    }
  },
  "required": [
    "name"
  ],
  "$defs": {
    "MembershipInline": {
      "type": "object",
      "description": "",
      "properties": {
        "id": {
          "type": "integer",
          "readOnly": true
        },
        "user": {
          "allOf": [
            {
              "$ref": "#/$defs/UserInline"
            }
          ],
          "readOnly": true
        },
        "role": {
          "enum": [
            "admin",
            "member",
            "view_only",
            "internal_view",
            "labs",
            "labs_view_only",
            "insights_view"
          ],
          "type": "string",
          "description": "* `admin` - Admin\n* `member` - Member\n* `view_only` - View Only\n* `internal_view` - Internal View Only\n* `labs` - Labs\n* `labs_view_only` - Labs View Only\n* `insights_view` - Insights View Only",
          "x-spec-enum-id": "2068ae3fe31eed3a"
        },
        "is_email_notify_enabled": {
          "type": "boolean"
        },
        "created_at": {
          "type": "string",
          "format": "date-time",
          "readOnly": true
        },
        "updated_at": {
          "type": "string",
          "format": "date-time",
          "readOnly": true
        }
      }
    },
    "OrganizationInline": {
      "type": "object",
      "description": "",
      "properties": {
        "id": {
          "type": "integer",
          "readOnly": true
        },
        "name": {
          "type": "string",
          "maxLength": 255
        },
        "members": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/MembershipInline"
          },
          "readOnly": true
        },
        "created_at": {
          "type": "string",
          "format": "date-time",
          "readOnly": true
        },
        "updated_at": {
          "type": "string",
          "format": "date-time",
          "readOnly": true
        }
      },
      "required": [
        "name"
      ]
    },
    "UserInline": {
      "type": "object",
      "description": "",
      "properties": {
        "id": {
          "type": [
            "integer",
            "null"
          ],
          "readOnly": true
        },
        "name": {
          "type": "string",
          "maxLength": 255
        },
        "email": {
          "type": [
            "string",
            "null"
          ],
          "format": "email",
          "readOnly": true
        }
      },
      "required": [
        "name"
      ]
    }
  }
}