Title: ServerMessageEndOfCallReport can't parse payloads from Voice Agent because camelCase fields aren't honored · Issue #18 · VapiAI/server-sdk-python · GitHub
Open Graph Title: ServerMessageEndOfCallReport can't parse payloads from Voice Agent because camelCase fields aren't honored · Issue #18 · VapiAI/server-sdk-python
X Title: ServerMessageEndOfCallReport can't parse payloads from Voice Agent because camelCase fields aren't honored · Issue #18 · VapiAI/server-sdk-python
Description: Description When receiving a payload from a Vapi Voice Agent, the ServerMessageEndOfCallReport model in the server-sdk-python fails to parse the incoming JSON. The payload uses camelCase field names (e.g., endedReason, endTime, secondsFr...
Open Graph Description: Description When receiving a payload from a Vapi Voice Agent, the ServerMessageEndOfCallReport model in the server-sdk-python fails to parse the incoming JSON. The payload uses camelCase field name...
X Description: Description When receiving a payload from a Vapi Voice Agent, the ServerMessageEndOfCallReport model in the server-sdk-python fails to parse the incoming JSON. The payload uses camelCase field name...
Opengraph URL: https://github.com/VapiAI/server-sdk-python/issues/18
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"ServerMessageEndOfCallReport can't parse payloads from Voice Agent because camelCase fields aren't honored","articleBody":"**Description** \nWhen receiving a payload from a Vapi Voice Agent, the `ServerMessageEndOfCallReport` model in the `server-sdk-python` fails to parse the incoming JSON. The payload uses **camelCase field names** (e.g., `endedReason`, `endTime`, `secondsFromStart`), but the SDK model expects **snake_case** (`ended_reason`, `end_time`, `seconds_from_start`). \n\nAlthough the generated model defines aliases using `FieldMetadata(alias=\"...\")`, those aliases are not actually used during model validation. As a result, the SDK throws large validation errors stating that required fields are missing.\n\n---\n\n**Example**\n\n```python\nfrom vapi_server_sdk.models import ServerMessageEndOfCallReport\n\nmsg = {\n \"timestamp\": 1762133576209,\n \"type\": \"end-of-call-report\",\n \"endedReason\": \"customer-ended-call\",\n \"analysis\": {\"summary\": \"...\", \"successEvaluation\": \"true\"},\n \"artifact\": {\n \"messages\": [\n {\n \"role\": \"bot\",\n \"message\": \"Hi, this is a test.\",\n \"time\": 1762133501000.0,\n \"endTime\": 1762133502000.0,\n \"secondsFromStart\": 1.0,\n }\n ],\n \"transcript\": \"AI: Hi, this is a test.\"\n }\n}\n\nServerMessageEndOfCallReport.model_validate(msg)\n```\n\n**Actual behavior**\n\n```bash\n80 validation errors for ServerMessageEndOfCallReport \nended_reason \n Field required [type=missing, input_value={...}, input_type=dict] \nartifact.messages.0.BotMessage.end_time \n Field required [type=missing, input_value={...}, input_type=dict] \nartifact.messages.0.BotMessage.seconds_from_start \n Field required [type=missing, input_value={...}, input_type=dict] \n...\n```\n\n---\n\n**Expected behavior** \nThe SDK should correctly parse the Voice Agent’s camelCase JSON payload, populating the corresponding snake_case model fields using Pydantic’s alias handling.\n\n---\n\n**Root cause** \nThe generated models use Fern’s custom `FieldMetadata(alias=\"...\")` annotations instead of Pydantic’s native `Field(alias=\"...\")`. \nPydantic v2 does **not** honor those aliases when parsing input (`model_validate`, `__init__`), only when serializing output (`model_dump(by_alias=True)`). \n\nThis means that incoming camelCase payloads from the Voice Agent (which follow the documented API schema) cannot be deserialized without manual key remapping.\n\n---\n\n**Workarounds**\n\nConvert the incoming JSON keys from camelCase → snake_case before validation:\n\n```python\nimport re \nfrom typing import Any, Mapping\n\nCAMEL_RE = re.compile(r'(?\u003c!^)(?=[A-Z])')\n\ndef camel_to_snake(name: str) -\u003e str:\n return CAMEL_RE.sub('_', name).lower()\n\ndef normalize_keys(obj: Any) -\u003e Any:\n if isinstance(obj, Mapping):\n return {camel_to_snake(k): normalize_keys(v) for k, v in obj.items()}\n elif isinstance(obj, list):\n return [normalize_keys(x) for x in obj]\n else:\n return obj\n\nnormalized = normalize_keys(msg) \nServerMessageEndOfCallReport.model_validate(normalized)\n```\n\nOr manually subclass the model to redeclare fields using `pydantic.Field(alias=\"...\")`.\n\n---\n\n**Proposed fix** \n- Update code generation for `server-sdk-python` to use **`pydantic.Field(alias=...)`** rather than `FieldMetadata(alias=...)`, ensuring proper alias resolution during validation. \n- Alternatively, update the SDK’s `UncheckedBaseModel` to include: \n\n```python\nmodel_config = ConfigDict(populate_by_name=True)\n```\n\nand propagate real Pydantic alias definitions into the generated models.\n\n---\n\n**Environment** \n- SDK: `vapi/server-sdk-python`\n- SDK Version: 1.7.3\n- Pydantic: 2.12.3 \n- Python: 3.11\n- Platform: macOS\n\n---\n\n**Impact** \nPayloads received from Voice Agents fail validation because camelCase fields aren’t recognized. This breaks out-of-the-box use of the server SDK for handling voice agent events.\n","author":{"url":"https://github.com/pratyushsahay","@type":"Person","name":"pratyushsahay"},"datePublished":"2025-11-03T07:08:22.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/18/server-sdk-python/issues/18"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:d64c0aa7-fbb3-73c6-3abe-126acc582040 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D56E:256AE2:5897EF:79E938:697BC484 |
| html-safe-nonce | b3317b179e227df68c296505fabf80cfb96e474997896fa8caf56f03cf32126a |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJENTZFOjI1NkFFMjo1ODk3RUY6NzlFOTM4OjY5N0JDNDg0IiwidmlzaXRvcl9pZCI6IjgyMjI4MzU3ODQ3MDExNjY3MjQiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 9d77a339abeabeee7474ceac06744cf40317e6864f98c48befa1063c405f8b56 |
| hovercard-subject-tag | issue:3580841784 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/VapiAI/server-sdk-python/18/issue_layout |
| twitter:image | https://opengraph.githubassets.com/a0b6b8e3e764bdac7f383e4f2a7076157fc7de1a7c9b2fd50909ffb3b2794b79/VapiAI/server-sdk-python/issues/18 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/a0b6b8e3e764bdac7f383e4f2a7076157fc7de1a7c9b2fd50909ffb3b2794b79/VapiAI/server-sdk-python/issues/18 |
| og:image:alt | Description When receiving a payload from a Vapi Voice Agent, the ServerMessageEndOfCallReport model in the server-sdk-python fails to parse the incoming JSON. The payload uses camelCase field name... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | pratyushsahay |
| hostname | github.com |
| expected-hostname | github.com |
| None | 300bc1c13c6b7e78c073c9264c722c624187bbdfc3b25a5b021396c805f56978 |
| turbo-cache-control | no-preview |
| go-import | github.com/VapiAI/server-sdk-python git https://github.com/VapiAI/server-sdk-python.git |
| octolytics-dimension-user_id | 144965430 |
| octolytics-dimension-user_login | VapiAI |
| octolytics-dimension-repository_id | 858207722 |
| octolytics-dimension-repository_nwo | VapiAI/server-sdk-python |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 858207722 |
| octolytics-dimension-repository_network_root_nwo | VapiAI/server-sdk-python |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | f571aa04a5e12a930e3b781f4b1c4b4408e5ba61 |
| ui-target | canary-2 |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width