diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ff66120..dd7ced1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.48.0" + ".": "0.49.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index d4b4e6b..0b60fcc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-3a307b6fa4a6fc0199acbf0660fd1e5ca0ac68082b073b74dbd22e70f3a9ea6e.yml -openapi_spec_hash: a9a255215f69ae2c4abbef59b07f08ea -config_hash: 70a26cd21f0ca1b34242677b831603db +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml +openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e +config_hash: 7fca08d9dae18513f5d7451cea11e34c diff --git a/CHANGELOG.md b/CHANGELOG.md index f02dd90..d09a2ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 0.49.0 (2026-08-11) + +Full Changelog: [v0.48.0...v0.49.0](https://github.com/zavudev/sdk-python/compare/v0.48.0...v0.49.0) + +### Features + +* **api:** api update ([f90d54e](https://github.com/zavudev/sdk-python/commit/f90d54e6f49ebed82447a815d8c80aecc023b937)) +* **api:** api update ([6d5a3ca](https://github.com/zavudev/sdk-python/commit/6d5a3ca83b4b43226a3309ac2f0a95f88f155cf8)) +* **api:** api update ([9b7009e](https://github.com/zavudev/sdk-python/commit/9b7009ebf8feb54d716c24fec138eb8e06609b10)) +* **api:** api update ([4219934](https://github.com/zavudev/sdk-python/commit/4219934a39d21c244328b7f64ab0554669eb06df)) +* **api:** api update ([142b83f](https://github.com/zavudev/sdk-python/commit/142b83f6f2e703f635a1cbf83400af6fba5fc419)) + ## 0.48.0 (2026-08-05) Full Changelog: [v0.47.0...v0.48.0](https://github.com/zavudev/sdk-python/compare/v0.47.0...v0.48.0) diff --git a/pyproject.toml b/pyproject.toml index 2f4c054..c789218 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "zavudev" -version = "0.48.0" +version = "0.49.0" description = "The official Python library for the zavudev API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/zavudev/_version.py b/src/zavudev/_version.py index 919a552..2b6c575 100644 --- a/src/zavudev/_version.py +++ b/src/zavudev/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "zavudev" -__version__ = "0.48.0" # x-release-please-version +__version__ = "0.49.0" # x-release-please-version diff --git a/src/zavudev/resources/broadcasts/broadcasts.py b/src/zavudev/resources/broadcasts/broadcasts.py index 294ca65..4f2b2bd 100644 --- a/src/zavudev/resources/broadcasts/broadcasts.py +++ b/src/zavudev/resources/broadcasts/broadcasts.py @@ -513,12 +513,27 @@ def send( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BroadcastSendResponse: - """Start sending the broadcast immediately or schedule for later. - - Broadcasts go - through automated AI content review before sending. If the review passes, the - broadcast proceeds. If rejected, use PATCH to edit content, then call POST - /retry-review. Reserves the estimated cost from your balance. + """ + Start sending the broadcast immediately or schedule for later. + + **Verification is required to send, and there are two of them.** The team must + have completed both identity verification (KYC) and business verification (KYB); + passing one is not enough. Drafts can be created, edited and kept without + either. Every send path — dashboard, API and CLI alike — enforces both, + returning `403` with code `kyc_required` or `kyb_required` for whichever is + outstanding. + + **Review depends on the channel, and cannot be bypassed.** A draft is submitted + to automated content review here; it does not go straight out. A WhatsApp + broadcast built on a Meta-approved template skips review (Meta already vetted + the content) and begins sending. An email broadcast sends as soon as the + automated review passes. Every other channel moves to `pending_admin_review` and + waits for a person. If the review rejects it, use PATCH to edit the content then + call POST /retry-review. + + Calling this on a broadcast that is already `approved` or `scheduled` sends or + reschedules it directly, since it has already been reviewed. Reserves the + estimated cost from your balance. Args: scheduled_at: Schedule for future delivery. Omit to send immediately. @@ -1003,12 +1018,27 @@ async def send( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BroadcastSendResponse: - """Start sending the broadcast immediately or schedule for later. - - Broadcasts go - through automated AI content review before sending. If the review passes, the - broadcast proceeds. If rejected, use PATCH to edit content, then call POST - /retry-review. Reserves the estimated cost from your balance. + """ + Start sending the broadcast immediately or schedule for later. + + **Verification is required to send, and there are two of them.** The team must + have completed both identity verification (KYC) and business verification (KYB); + passing one is not enough. Drafts can be created, edited and kept without + either. Every send path — dashboard, API and CLI alike — enforces both, + returning `403` with code `kyc_required` or `kyb_required` for whichever is + outstanding. + + **Review depends on the channel, and cannot be bypassed.** A draft is submitted + to automated content review here; it does not go straight out. A WhatsApp + broadcast built on a Meta-approved template skips review (Meta already vetted + the content) and begins sending. An email broadcast sends as soon as the + automated review passes. Every other channel moves to `pending_admin_review` and + waits for a person. If the review rejects it, use PATCH to edit the content then + call POST /retry-review. + + Calling this on a broadcast that is already `approved` or `scheduled` sends or + reschedules it directly, since it has already been reviewed. Reserves the + estimated cost from your balance. Args: scheduled_at: Schedule for future delivery. Omit to send immediately. diff --git a/src/zavudev/resources/functions/functions.py b/src/zavudev/resources/functions/functions.py index 6bef2dd..5b5e86f 100644 --- a/src/zavudev/resources/functions/functions.py +++ b/src/zavudev/resources/functions/functions.py @@ -69,6 +69,8 @@ def create( slug: str, dependencies: Dict[str, str] | Omit = omit, description: str | Omit = omit, + entrypoint: str | Omit = omit, + files: Dict[str, str] | Omit = omit, http_enabled: bool | Omit = omit, memory_mb: Literal[128, 256, 512, 1024] | Omit = omit, runtime: Literal["nodejs24"] | Omit = omit, @@ -95,11 +97,24 @@ def create( dependencies: npm dependencies. Keys are package names, values are semver ranges. + entrypoint: Which file in `files` is the entry point. Defaults to `index.ts`. + + files: The project's source files, keyed by path relative to the project root (e.g. + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + http_enabled: Whether to expose a public HTTPS URL for this function. runtime: Runtime the function is deployed on. - source_code: TypeScript source code for the function entry point (max ~900KB). + source_code: Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. timeout_sec: Per-invocation timeout in seconds. Event and cron invocations are asynchronous, so a long timeout only bounds cost; a tool called during a live conversation @@ -122,6 +137,8 @@ def create( "slug": slug, "dependencies": dependencies, "description": description, + "entrypoint": entrypoint, + "files": files, "http_enabled": http_enabled, "memory_mb": memory_mb, "runtime": runtime, @@ -174,6 +191,8 @@ def update( function_id: str, *, dependencies: Dict[str, str] | Omit = omit, + entrypoint: str | Omit = omit, + files: Dict[str, str] | Omit = omit, http_enabled: bool | Omit = omit, source_code: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -194,11 +213,24 @@ def update( Args: dependencies: New dependency map (replaces existing dependencies). + entrypoint: Which file in `files` is the entry point. Defaults to `index.ts`. + + files: The project's source files, keyed by path relative to the project root (e.g. + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + http_enabled: Expose the function on its public HTTPS URL, or take it down. Applies to the already-deployed function without redeploying; the URL is returned as `publicUrl`. - source_code: New source code for the draft (replaces it). + source_code: Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. extra_headers: Send extra headers @@ -215,6 +247,8 @@ def update( body=maybe_transform( { "dependencies": dependencies, + "entrypoint": entrypoint, + "files": files, "http_enabled": http_enabled, "source_code": source_code, }, @@ -266,6 +300,8 @@ def deploy( function_id: str, *, dependencies: Dict[str, str] | Omit = omit, + entrypoint: str | Omit = omit, + files: Dict[str, str] | Omit = omit, source_code: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -284,7 +320,20 @@ def deploy( Args: dependencies: New dependency map (replaces existing dependencies). - source_code: New source code to publish (replaces the draft). + entrypoint: Which file in `files` is the entry point. Defaults to `index.ts`. + + files: The project's source files, keyed by path relative to the project root (e.g. + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + + source_code: Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. extra_headers: Send extra headers @@ -301,6 +350,8 @@ def deploy( body=maybe_transform( { "dependencies": dependencies, + "entrypoint": entrypoint, + "files": files, "source_code": source_code, }, function_deploy_params.FunctionDeployParams, @@ -434,6 +485,8 @@ async def create( slug: str, dependencies: Dict[str, str] | Omit = omit, description: str | Omit = omit, + entrypoint: str | Omit = omit, + files: Dict[str, str] | Omit = omit, http_enabled: bool | Omit = omit, memory_mb: Literal[128, 256, 512, 1024] | Omit = omit, runtime: Literal["nodejs24"] | Omit = omit, @@ -460,11 +513,24 @@ async def create( dependencies: npm dependencies. Keys are package names, values are semver ranges. + entrypoint: Which file in `files` is the entry point. Defaults to `index.ts`. + + files: The project's source files, keyed by path relative to the project root (e.g. + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + http_enabled: Whether to expose a public HTTPS URL for this function. runtime: Runtime the function is deployed on. - source_code: TypeScript source code for the function entry point (max ~900KB). + source_code: Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. timeout_sec: Per-invocation timeout in seconds. Event and cron invocations are asynchronous, so a long timeout only bounds cost; a tool called during a live conversation @@ -487,6 +553,8 @@ async def create( "slug": slug, "dependencies": dependencies, "description": description, + "entrypoint": entrypoint, + "files": files, "http_enabled": http_enabled, "memory_mb": memory_mb, "runtime": runtime, @@ -539,6 +607,8 @@ async def update( function_id: str, *, dependencies: Dict[str, str] | Omit = omit, + entrypoint: str | Omit = omit, + files: Dict[str, str] | Omit = omit, http_enabled: bool | Omit = omit, source_code: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -559,11 +629,24 @@ async def update( Args: dependencies: New dependency map (replaces existing dependencies). + entrypoint: Which file in `files` is the entry point. Defaults to `index.ts`. + + files: The project's source files, keyed by path relative to the project root (e.g. + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + http_enabled: Expose the function on its public HTTPS URL, or take it down. Applies to the already-deployed function without redeploying; the URL is returned as `publicUrl`. - source_code: New source code for the draft (replaces it). + source_code: Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. extra_headers: Send extra headers @@ -580,6 +663,8 @@ async def update( body=await async_maybe_transform( { "dependencies": dependencies, + "entrypoint": entrypoint, + "files": files, "http_enabled": http_enabled, "source_code": source_code, }, @@ -631,6 +716,8 @@ async def deploy( function_id: str, *, dependencies: Dict[str, str] | Omit = omit, + entrypoint: str | Omit = omit, + files: Dict[str, str] | Omit = omit, source_code: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -649,7 +736,20 @@ async def deploy( Args: dependencies: New dependency map (replaces existing dependencies). - source_code: New source code to publish (replaces the draft). + entrypoint: Which file in `files` is the entry point. Defaults to `index.ts`. + + files: The project's source files, keyed by path relative to the project root (e.g. + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + + source_code: Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. extra_headers: Send extra headers @@ -666,6 +766,8 @@ async def deploy( body=await async_maybe_transform( { "dependencies": dependencies, + "entrypoint": entrypoint, + "files": files, "source_code": source_code, }, function_deploy_params.FunctionDeployParams, diff --git a/src/zavudev/resources/phone_numbers.py b/src/zavudev/resources/phone_numbers.py index 61b71b1..cd0ee4a 100644 --- a/src/zavudev/resources/phone_numbers.py +++ b/src/zavudev/resources/phone_numbers.py @@ -203,7 +203,10 @@ def purchase( Requires a paid plan: the Free plan cannot purchase phone numbers and receives `402` with code `paid_plan_required`. Paid - plans include the first US number at no charge. + plans include one US number at no charge. The included number is one per account + and is granted once: claiming it spends the benefit for good, so releasing that + number does not make another one free, and numbers the account already bought do + not consume it. Args: phone_number: Phone number in E.164 format. @@ -540,7 +543,10 @@ async def purchase( Requires a paid plan: the Free plan cannot purchase phone numbers and receives `402` with code `paid_plan_required`. Paid - plans include the first US number at no charge. + plans include one US number at no charge. The included number is one per account + and is granted once: claiming it spends the benefit for good, so releasing that + number does not make another one free, and numbers the account already bought do + not consume it. Args: phone_number: Phone number in E.164 format. diff --git a/src/zavudev/resources/senders/agent/tools.py b/src/zavudev/resources/senders/agent/tools.py index bd916f5..11448ba 100644 --- a/src/zavudev/resources/senders/agent/tools.py +++ b/src/zavudev/resources/senders/agent/tools.py @@ -78,7 +78,9 @@ def create( Args: webhook_url: Must be HTTPS. - webhook_secret: Optional secret for webhook signature verification. + webhook_secret: Signing secret for the webhook. Optional: Zavu generates one when omitted and + returns it on this response only. Supply your own if you already have a secret + you want reused. extra_headers: Send extra headers @@ -377,7 +379,9 @@ async def create( Args: webhook_url: Must be HTTPS. - webhook_secret: Optional secret for webhook signature verification. + webhook_secret: Signing secret for the webhook. Optional: Zavu generates one when omitted and + returns it on this response only. Supply your own if you already have a secret + you want reused. extra_headers: Send extra headers diff --git a/src/zavudev/resources/senders/senders.py b/src/zavudev/resources/senders/senders.py index 0510e80..5abd856 100644 --- a/src/zavudev/resources/senders/senders.py +++ b/src/zavudev/resources/senders/senders.py @@ -95,6 +95,7 @@ def create( phone_number: str | Omit = omit, set_as_default: bool | Omit = omit, webhook_events: List[WebhookEvent] | Omit = omit, + webhook_signature_version: Literal["v1", "v1+v2", "v2"] | Omit = omit, webhook_url: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -137,6 +138,19 @@ def create( webhook_events: Events to subscribe to. + webhook_signature_version: Which `X-Zavu-Signature` scheme this receiver is sent. + + - `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was + configurable. Existing webhooks stay on it until you move them. + - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the + default for new senders. It signs the timestamp together with the body. + - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver + reading either one works, so you can deploy and confirm your new verifier + before switching over. + + Moving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See + https://docs.zavu.dev/guides/receiving-messages/signature-migration + webhook_url: HTTPS URL for webhook events. extra_headers: Send extra headers @@ -161,6 +175,7 @@ def create( "phone_number": phone_number, "set_as_default": set_as_default, "webhook_events": webhook_events, + "webhook_signature_version": webhook_signature_version, "webhook_url": webhook_url, }, sender_create_params.SenderCreateParams, @@ -219,6 +234,7 @@ def update( set_as_default: bool | Omit = omit, webhook_active: bool | Omit = omit, webhook_events: List[WebhookEvent] | Omit = omit, + webhook_signature_version: Literal["v1", "v1+v2", "v2"] | Omit = omit, webhook_url: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -257,6 +273,19 @@ def update( webhook_events: Events to subscribe to. + webhook_signature_version: Which `X-Zavu-Signature` scheme this receiver is sent. + + - `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was + configurable. Existing webhooks stay on it until you move them. + - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the + default for new senders. It signs the timestamp together with the body. + - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver + reading either one works, so you can deploy and confirm your new verifier + before switching over. + + Moving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See + https://docs.zavu.dev/guides/receiving-messages/signature-migration + webhook_url: HTTPS URL for webhook events. Set to null to remove webhook. extra_headers: Send extra headers @@ -284,6 +313,7 @@ def update( "set_as_default": set_as_default, "webhook_active": webhook_active, "webhook_events": webhook_events, + "webhook_signature_version": webhook_signature_version, "webhook_url": webhook_url, }, sender_update_params.SenderUpdateParams, @@ -595,6 +625,7 @@ async def create( phone_number: str | Omit = omit, set_as_default: bool | Omit = omit, webhook_events: List[WebhookEvent] | Omit = omit, + webhook_signature_version: Literal["v1", "v1+v2", "v2"] | Omit = omit, webhook_url: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -637,6 +668,19 @@ async def create( webhook_events: Events to subscribe to. + webhook_signature_version: Which `X-Zavu-Signature` scheme this receiver is sent. + + - `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was + configurable. Existing webhooks stay on it until you move them. + - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the + default for new senders. It signs the timestamp together with the body. + - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver + reading either one works, so you can deploy and confirm your new verifier + before switching over. + + Moving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See + https://docs.zavu.dev/guides/receiving-messages/signature-migration + webhook_url: HTTPS URL for webhook events. extra_headers: Send extra headers @@ -661,6 +705,7 @@ async def create( "phone_number": phone_number, "set_as_default": set_as_default, "webhook_events": webhook_events, + "webhook_signature_version": webhook_signature_version, "webhook_url": webhook_url, }, sender_create_params.SenderCreateParams, @@ -719,6 +764,7 @@ async def update( set_as_default: bool | Omit = omit, webhook_active: bool | Omit = omit, webhook_events: List[WebhookEvent] | Omit = omit, + webhook_signature_version: Literal["v1", "v1+v2", "v2"] | Omit = omit, webhook_url: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -757,6 +803,19 @@ async def update( webhook_events: Events to subscribe to. + webhook_signature_version: Which `X-Zavu-Signature` scheme this receiver is sent. + + - `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was + configurable. Existing webhooks stay on it until you move them. + - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the + default for new senders. It signs the timestamp together with the body. + - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver + reading either one works, so you can deploy and confirm your new verifier + before switching over. + + Moving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See + https://docs.zavu.dev/guides/receiving-messages/signature-migration + webhook_url: HTTPS URL for webhook events. Set to null to remove webhook. extra_headers: Send extra headers @@ -784,6 +843,7 @@ async def update( "set_as_default": set_as_default, "webhook_active": webhook_active, "webhook_events": webhook_events, + "webhook_signature_version": webhook_signature_version, "webhook_url": webhook_url, }, sender_update_params.SenderUpdateParams, diff --git a/src/zavudev/types/function_create_params.py b/src/zavudev/types/function_create_params.py index 336a028..38f9026 100644 --- a/src/zavudev/types/function_create_params.py +++ b/src/zavudev/types/function_create_params.py @@ -21,6 +21,21 @@ class FunctionCreateParams(TypedDict, total=False): description: str + entrypoint: str + """Which file in `files` is the entry point. Defaults to `index.ts`.""" + + files: Dict[str, str] + """The project's source files, keyed by path relative to the project root (e.g. + + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + """ + http_enabled: Annotated[bool, PropertyInfo(alias="httpEnabled")] """Whether to expose a public HTTPS URL for this function.""" @@ -30,7 +45,11 @@ class FunctionCreateParams(TypedDict, total=False): """Runtime the function is deployed on.""" source_code: Annotated[str, PropertyInfo(alias="sourceCode")] - """TypeScript source code for the function entry point (max ~900KB).""" + """ + Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. + """ timeout_sec: Annotated[int, PropertyInfo(alias="timeoutSec")] """Per-invocation timeout in seconds. diff --git a/src/zavudev/types/function_deploy_params.py b/src/zavudev/types/function_deploy_params.py index cfc6d80..cf7d143 100644 --- a/src/zavudev/types/function_deploy_params.py +++ b/src/zavudev/types/function_deploy_params.py @@ -14,5 +14,24 @@ class FunctionDeployParams(TypedDict, total=False): dependencies: Dict[str, str] """New dependency map (replaces existing dependencies).""" + entrypoint: str + """Which file in `files` is the entry point. Defaults to `index.ts`.""" + + files: Dict[str, str] + """The project's source files, keyed by path relative to the project root (e.g. + + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + """ + source_code: Annotated[str, PropertyInfo(alias="sourceCode")] - """New source code to publish (replaces the draft).""" + """ + Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. + """ diff --git a/src/zavudev/types/function_deploy_response.py b/src/zavudev/types/function_deploy_response.py index 3aea2b0..4622399 100644 --- a/src/zavudev/types/function_deploy_response.py +++ b/src/zavudev/types/function_deploy_response.py @@ -24,7 +24,17 @@ class Deployment(BaseModel): version: int """Monotonically increasing deployment version, starting at 1.""" + build_logs: Optional[str] = FieldInfo(alias="buildLogs", default=None) + """ + What the build printed: dependency installation, the bundler's output, and the + compiler's message when it failed. Returned when fetching a single deployment, + omitted from the list. Read this first when a deploy fails — `errorMessage` is + often the outer wrapper's summary, and the line that names the broken import or + the syntax error is here. + """ + bundle_bytes: Optional[int] = FieldInfo(alias="bundleBytes", default=None) + """Size of the built bundle in bytes. Null until the build finishes.""" deployed_at: Optional[datetime] = FieldInfo(alias="deployedAt", default=None) @@ -32,6 +42,7 @@ class Deployment(BaseModel): """Failure reason when status is 'failed'.""" source_code_bytes: Optional[int] = FieldInfo(alias="sourceCodeBytes", default=None) + """Total size of the deployed source tree in bytes.""" class FunctionDeployResponse(BaseModel): diff --git a/src/zavudev/types/function_get_deployment_response.py b/src/zavudev/types/function_get_deployment_response.py index 1da6db7..c0301bc 100644 --- a/src/zavudev/types/function_get_deployment_response.py +++ b/src/zavudev/types/function_get_deployment_response.py @@ -24,7 +24,17 @@ class Deployment(BaseModel): version: int """Monotonically increasing deployment version, starting at 1.""" + build_logs: Optional[str] = FieldInfo(alias="buildLogs", default=None) + """ + What the build printed: dependency installation, the bundler's output, and the + compiler's message when it failed. Returned when fetching a single deployment, + omitted from the list. Read this first when a deploy fails — `errorMessage` is + often the outer wrapper's summary, and the line that names the broken import or + the syntax error is here. + """ + bundle_bytes: Optional[int] = FieldInfo(alias="bundleBytes", default=None) + """Size of the built bundle in bytes. Null until the build finishes.""" deployed_at: Optional[datetime] = FieldInfo(alias="deployedAt", default=None) @@ -32,6 +42,7 @@ class Deployment(BaseModel): """Failure reason when status is 'failed'.""" source_code_bytes: Optional[int] = FieldInfo(alias="sourceCodeBytes", default=None) + """Total size of the deployed source tree in bytes.""" class FunctionGetDeploymentResponse(BaseModel): diff --git a/src/zavudev/types/function_update_params.py b/src/zavudev/types/function_update_params.py index 65a687b..85a732d 100644 --- a/src/zavudev/types/function_update_params.py +++ b/src/zavudev/types/function_update_params.py @@ -14,6 +14,21 @@ class FunctionUpdateParams(TypedDict, total=False): dependencies: Dict[str, str] """New dependency map (replaces existing dependencies).""" + entrypoint: str + """Which file in `files` is the entry point. Defaults to `index.ts`.""" + + files: Dict[str, str] + """The project's source files, keyed by path relative to the project root (e.g. + + `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + function is built, so a function can be split across as many files as it needs. + + Paths must be relative and use forward slashes; `..`, `node_modules/` and + `package.json` are rejected. npm packages are not uploaded here — declare them + under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 bytes + for the whole tree. + """ + http_enabled: Annotated[bool, PropertyInfo(alias="httpEnabled")] """Expose the function on its public HTTPS URL, or take it down. @@ -22,4 +37,8 @@ class FunctionUpdateParams(TypedDict, total=False): """ source_code: Annotated[str, PropertyInfo(alias="sourceCode")] - """New source code for the draft (replaces it).""" + """ + Shortcut for a single-file function: exactly equivalent to sending `files` with + one entry named after `entrypoint` (`index.ts` by default). Fully supported — + use whichever fits. If both are sent, `files` wins. + """ diff --git a/src/zavudev/types/phone_number_pricing.py b/src/zavudev/types/phone_number_pricing.py index 661b5ad..0dc1b8a 100644 --- a/src/zavudev/types/phone_number_pricing.py +++ b/src/zavudev/types/phone_number_pricing.py @@ -11,9 +11,10 @@ class PhoneNumberPricing(BaseModel): is_free_eligible: Optional[bool] = FieldInfo(alias="isFreeEligible", default=None) - """ - Whether this number qualifies as the plan-included first US number on paid - plans. + """Whether this number qualifies as the plan-included US number on paid plans. + + The benefit is one per account: it is never offered again once claimed, not even + after the number is released. """ monthly_price: Optional[float] = FieldInfo(alias="monthlyPrice", default=None) diff --git a/src/zavudev/types/sender_create_params.py b/src/zavudev/types/sender_create_params.py index b17db04..8bfb662 100644 --- a/src/zavudev/types/sender_create_params.py +++ b/src/zavudev/types/sender_create_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import List -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from .._utils import PropertyInfo from .webhook_event import WebhookEvent @@ -65,5 +65,20 @@ class SenderCreateParams(TypedDict, total=False): webhook_events: Annotated[List[WebhookEvent], PropertyInfo(alias="webhookEvents")] """Events to subscribe to.""" + webhook_signature_version: Annotated[Literal["v1", "v1+v2", "v2"], PropertyInfo(alias="webhookSignatureVersion")] + """Which `X-Zavu-Signature` scheme this receiver is sent. + + - `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was + configurable. Existing webhooks stay on it until you move them. + - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the + default for new senders. It signs the timestamp together with the body. + - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver + reading either one works, so you can deploy and confirm your new verifier + before switching over. + + Moving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See + https://docs.zavu.dev/guides/receiving-messages/signature-migration + """ + webhook_url: Annotated[str, PropertyInfo(alias="webhookUrl")] """HTTPS URL for webhook events.""" diff --git a/src/zavudev/types/sender_update_params.py b/src/zavudev/types/sender_update_params.py index 3b6d96a..3e7e87d 100644 --- a/src/zavudev/types/sender_update_params.py +++ b/src/zavudev/types/sender_update_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import List, Optional -from typing_extensions import Annotated, TypedDict +from typing_extensions import Literal, Annotated, TypedDict from .._utils import PropertyInfo from .webhook_event import WebhookEvent @@ -64,5 +64,20 @@ class SenderUpdateParams(TypedDict, total=False): webhook_events: Annotated[List[WebhookEvent], PropertyInfo(alias="webhookEvents")] """Events to subscribe to.""" + webhook_signature_version: Annotated[Literal["v1", "v1+v2", "v2"], PropertyInfo(alias="webhookSignatureVersion")] + """Which `X-Zavu-Signature` scheme this receiver is sent. + + - `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was + configurable. Existing webhooks stay on it until you move them. + - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the + default for new senders. It signs the timestamp together with the body. + - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver + reading either one works, so you can deploy and confirm your new verifier + before switching over. + + Moving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See + https://docs.zavu.dev/guides/receiving-messages/signature-migration + """ + webhook_url: Annotated[Optional[str], PropertyInfo(alias="webhookUrl")] """HTTPS URL for webhook events. Set to null to remove webhook.""" diff --git a/src/zavudev/types/sender_webhook.py b/src/zavudev/types/sender_webhook.py index e6ef71c..4d07bab 100644 --- a/src/zavudev/types/sender_webhook.py +++ b/src/zavudev/types/sender_webhook.py @@ -1,6 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo from .._models import BaseModel from .webhook_event import WebhookEvent @@ -17,6 +20,21 @@ class SenderWebhook(BaseModel): events: List[WebhookEvent] """List of events the webhook is subscribed to.""" + signature_version: Literal["v1", "v1+v2", "v2"] = FieldInfo(alias="signatureVersion") + """Which `X-Zavu-Signature` scheme this receiver is sent. + + - `v1`: `v1=HMAC_SHA256(secret, body)`. The scheme used before this was + configurable. Existing webhooks stay on it until you move them. + - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the + default for new senders. It signs the timestamp together with the body. + - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver + reading either one works, so you can deploy and confirm your new verifier + before switching over. + + Moving from `v1` straight to `v2` returns `400`. Set `v1+v2` first. See + https://docs.zavu.dev/guides/receiving-messages/signature-migration + """ + url: str """HTTPS URL that will receive webhook events.""" diff --git a/src/zavudev/types/senders/agent/agent_tool.py b/src/zavudev/types/senders/agent/agent_tool.py index 98fdfb5..5a62e91 100644 --- a/src/zavudev/types/senders/agent/agent_tool.py +++ b/src/zavudev/types/senders/agent/agent_tool.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from datetime import datetime from pydantic import Field as FieldInfo @@ -30,3 +31,14 @@ class AgentTool(BaseModel): webhook_url: str = FieldInfo(alias="webhookUrl") """HTTPS URL to call when the tool is executed.""" + + webhook_secret: Optional[str] = FieldInfo(alias="webhookSecret", default=None) + """Signing secret for this tool's webhook. + + **Returned only when the tool is created**, never on a later read. + + Zavu generates one if you do not supply it, and signs every call to this tool + with it: `X-Zavu-Signature: `, the HMAC-SHA256 of the request body. Verify + it before trusting the call. Lost it? Rotate with + `POST /v1/senders/{senderId}/agent/tools/{toolId}/webhook/secret`. + """ diff --git a/src/zavudev/types/senders/agent/tool_create_params.py b/src/zavudev/types/senders/agent/tool_create_params.py index 2f306ee..c1da1b9 100644 --- a/src/zavudev/types/senders/agent/tool_create_params.py +++ b/src/zavudev/types/senders/agent/tool_create_params.py @@ -23,4 +23,8 @@ class ToolCreateParams(TypedDict, total=False): enabled: bool webhook_secret: Annotated[str, PropertyInfo(alias="webhookSecret")] - """Optional secret for webhook signature verification.""" + """Signing secret for the webhook. + + Optional: Zavu generates one when omitted and returns it on this response only. + Supply your own if you already have a secret you want reused. + """ diff --git a/tests/api_resources/test_functions.py b/tests/api_resources/test_functions.py index 140fae2..162babc 100644 --- a/tests/api_resources/test_functions.py +++ b/tests/api_resources/test_functions.py @@ -42,6 +42,11 @@ def test_method_create_with_all_params(self, client: Zavudev) -> None: slug="order-bot", dependencies={"openai": "^4.20.0"}, description="Replies to order status questions on WhatsApp.", + entrypoint="index.ts", + files={ + "index.ts": "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", + "lib/orders.ts": "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n", + }, http_enabled=True, memory_mb=128, runtime="nodejs24", @@ -134,6 +139,11 @@ def test_method_update_with_all_params(self, client: Zavudev) -> None: function = client.functions.update( function_id="functionId", dependencies={"foo": "string"}, + entrypoint="index.ts", + files={ + "index.ts": "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", + "lib/orders.ts": "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n", + }, http_enabled=True, source_code="sourceCode", ) @@ -229,6 +239,11 @@ def test_method_deploy_with_all_params(self, client: Zavudev) -> None: function = client.functions.deploy( function_id="functionId", dependencies={"foo": "string"}, + entrypoint="index.ts", + files={ + "index.ts": "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", + "lib/orders.ts": "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n", + }, source_code="sourceCode", ) assert_matches_type(FunctionDeployResponse, function, path=["response"]) @@ -387,6 +402,11 @@ async def test_method_create_with_all_params(self, async_client: AsyncZavudev) - slug="order-bot", dependencies={"openai": "^4.20.0"}, description="Replies to order status questions on WhatsApp.", + entrypoint="index.ts", + files={ + "index.ts": "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", + "lib/orders.ts": "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n", + }, http_enabled=True, memory_mb=128, runtime="nodejs24", @@ -479,6 +499,11 @@ async def test_method_update_with_all_params(self, async_client: AsyncZavudev) - function = await async_client.functions.update( function_id="functionId", dependencies={"foo": "string"}, + entrypoint="index.ts", + files={ + "index.ts": "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", + "lib/orders.ts": "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n", + }, http_enabled=True, source_code="sourceCode", ) @@ -574,6 +599,11 @@ async def test_method_deploy_with_all_params(self, async_client: AsyncZavudev) - function = await async_client.functions.deploy( function_id="functionId", dependencies={"foo": "string"}, + entrypoint="index.ts", + files={ + "index.ts": "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n", + "lib/orders.ts": "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n", + }, source_code="sourceCode", ) assert_matches_type(FunctionDeployResponse, function, path=["response"]) diff --git a/tests/api_resources/test_senders.py b/tests/api_resources/test_senders.py index 2683d35..91ae14a 100644 --- a/tests/api_resources/test_senders.py +++ b/tests/api_resources/test_senders.py @@ -46,6 +46,7 @@ def test_method_create_with_all_params(self, client: Zavudev) -> None: phone_number="phoneNumber", set_as_default=True, webhook_events=["message.queued"], + webhook_signature_version="v2", webhook_url="https://example.com", ) assert_matches_type(Sender, sender, path=["response"]) @@ -142,6 +143,7 @@ def test_method_update_with_all_params(self, client: Zavudev) -> None: set_as_default=True, webhook_active=True, webhook_events=["message.queued"], + webhook_signature_version="v2", webhook_url="https://example.com", ) assert_matches_type(Sender, sender, path=["response"]) @@ -477,6 +479,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncZavudev) - phone_number="phoneNumber", set_as_default=True, webhook_events=["message.queued"], + webhook_signature_version="v2", webhook_url="https://example.com", ) assert_matches_type(Sender, sender, path=["response"]) @@ -573,6 +576,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncZavudev) - set_as_default=True, webhook_active=True, webhook_events=["message.queued"], + webhook_signature_version="v2", webhook_url="https://example.com", ) assert_matches_type(Sender, sender, path=["response"])