From e9a56919080dd8e543f6b073f95bb25078fce358 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2026 18:00:08 +0000 Subject: [PATCH 01/15] feat(api): api update --- .stats.yml | 4 +- .../models/number_10dlc/ten_dlc_brand.rb | 40 ++++++++++++- .../models/number_10dlc/ten_dlc_brand.rbi | 57 +++++++++++++++++++ .../models/number_10dlc/ten_dlc_brand.rbs | 10 +++- .../resources/number_10dlc/brands_test.rb | 1 + 5 files changed, 108 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index ddb1fe8..10360c4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5d75bbdcac3a0b0a8b798c0b4ef0dacfbc64ad4a20605489dc46dda7c8d1d8d8.yml -openapi_spec_hash: bb242cfd8cc43354412164c2dceae89c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-997e957e7b8c6a15c356fd9182f73a1a149e78b702566daf23b1e7519c06ee95.yml +openapi_spec_hash: a0ee7a79588077d95d4bf9fccc3837a2 config_hash: 261e1b852ca7f8364e4a283a3edd350a diff --git a/lib/zavudev/models/number_10dlc/ten_dlc_brand.rb b/lib/zavudev/models/number_10dlc/ten_dlc_brand.rb index 036c8b9..fe0ecaf 100644 --- a/lib/zavudev/models/number_10dlc/ten_dlc_brand.rb +++ b/lib/zavudev/models/number_10dlc/ten_dlc_brand.rb @@ -66,6 +66,17 @@ class TenDlcBrand < Zavudev::Internal::Type::BaseModel # @!attribute status # Status of a 10DLC brand registration. # + # - `draft`: created, not yet submitted to the carrier. + # - `pending`: submitted, awaiting the carrier's answer. + # - `verified`: the carrier registered the brand AND verified the business behind + # it. + # - `unverified`: the carrier registered the brand but did not verify the business + # — the registration exists, the identity check did not pass or has not been + # resolved. Campaigns are allowed, with lower daily limits. Read + # `identityStatus` for the carrier's own wording. + # - `rejected`: refused by the carrier. + # - `failed`: the registration never reached the carrier; the fee is refunded. + # # @return [Symbol, Zavudev::Models::Number10dlc::TenDlcBrand::Status] required :status, enum: -> { Zavudev::Number10dlc::TenDlcBrand::Status } @@ -119,6 +130,15 @@ class TenDlcBrand < Zavudev::Internal::Type::BaseModel # @return [String, nil] optional :first_name, String, api_name: :firstName, nil?: true + # @!attribute identity_status + # The carrier's raw identity verdict on the business, as the carrier spells it + # (`VERIFIED`, `VETTED_VERIFIED`, `SELF_DECLARED`, `UNVERIFIED`). Null while the + # identity has not been resolved — which is not the same as verified, and is why + # such a brand reports `status: unverified`. + # + # @return [String, nil] + optional :identity_status, String, api_name: :identityStatus, nil?: true + # @!attribute last_name # # @return [String, nil] @@ -149,7 +169,10 @@ class TenDlcBrand < Zavudev::Internal::Type::BaseModel # @return [String, nil] optional :website, String, nil?: true - # @!method initialize(id:, city:, country:, created_at:, display_name:, email:, entity_type:, phone:, postal_code:, state:, status:, street:, updated_at:, vertical:, brand_relationship: nil, brand_score: nil, company_name: nil, ein: nil, failure_reason: nil, first_name: nil, last_name: nil, stock_exchange: nil, stock_symbol: nil, submitted_at: nil, verified_at: nil, website: nil) + # @!method initialize(id:, city:, country:, created_at:, display_name:, email:, entity_type:, phone:, postal_code:, state:, status:, street:, updated_at:, vertical:, brand_relationship: nil, brand_score: nil, company_name: nil, ein: nil, failure_reason: nil, first_name: nil, identity_status: nil, last_name: nil, stock_exchange: nil, stock_symbol: nil, submitted_at: nil, verified_at: nil, website: nil) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::Number10dlc::TenDlcBrand} for more details. + # # @param id [String] # # @param city [String] @@ -190,6 +213,8 @@ class TenDlcBrand < Zavudev::Internal::Type::BaseModel # # @param first_name [String, nil] # + # @param identity_status [String, nil] The carrier's raw identity verdict on the business, as the carrier spells it (`V + # # @param last_name [String, nil] # # @param stock_exchange [String, nil] @@ -220,6 +245,17 @@ module EntityType # Status of a 10DLC brand registration. # + # - `draft`: created, not yet submitted to the carrier. + # - `pending`: submitted, awaiting the carrier's answer. + # - `verified`: the carrier registered the brand AND verified the business behind + # it. + # - `unverified`: the carrier registered the brand but did not verify the business + # — the registration exists, the identity check did not pass or has not been + # resolved. Campaigns are allowed, with lower daily limits. Read + # `identityStatus` for the carrier's own wording. + # - `rejected`: refused by the carrier. + # - `failed`: the registration never reached the carrier; the fee is refunded. + # # @see Zavudev::Models::Number10dlc::TenDlcBrand#status module Status extend Zavudev::Internal::Type::Enum @@ -227,7 +263,9 @@ module Status DRAFT = :draft PENDING = :pending VERIFIED = :verified + UNVERIFIED = :unverified REJECTED = :rejected + FAILED = :failed # @!method self.values # @return [Array] diff --git a/rbi/zavudev/models/number_10dlc/ten_dlc_brand.rbi b/rbi/zavudev/models/number_10dlc/ten_dlc_brand.rbi index 67cec06..625ec4c 100644 --- a/rbi/zavudev/models/number_10dlc/ten_dlc_brand.rbi +++ b/rbi/zavudev/models/number_10dlc/ten_dlc_brand.rbi @@ -46,6 +46,17 @@ module Zavudev attr_accessor :state # Status of a 10DLC brand registration. + # + # - `draft`: created, not yet submitted to the carrier. + # - `pending`: submitted, awaiting the carrier's answer. + # - `verified`: the carrier registered the brand AND verified the business behind + # it. + # - `unverified`: the carrier registered the brand but did not verify the business + # — the registration exists, the identity check did not pass or has not been + # resolved. Campaigns are allowed, with lower daily limits. Read + # `identityStatus` for the carrier's own wording. + # - `rejected`: refused by the carrier. + # - `failed`: the registration never reached the carrier; the fee is refunded. sig { returns(Zavudev::Number10dlc::TenDlcBrand::Status::TaggedSymbol) } attr_accessor :status @@ -81,6 +92,13 @@ module Zavudev sig { returns(T.nilable(String)) } attr_accessor :first_name + # The carrier's raw identity verdict on the business, as the carrier spells it + # (`VERIFIED`, `VETTED_VERIFIED`, `SELF_DECLARED`, `UNVERIFIED`). Null while the + # identity has not been resolved — which is not the same as verified, and is why + # such a brand reports `status: unverified`. + sig { returns(T.nilable(String)) } + attr_accessor :identity_status + sig { returns(T.nilable(String)) } attr_accessor :last_name @@ -122,6 +140,7 @@ module Zavudev ein: T.nilable(String), failure_reason: T.nilable(String), first_name: T.nilable(String), + identity_status: T.nilable(String), last_name: T.nilable(String), stock_exchange: T.nilable(String), stock_symbol: T.nilable(String), @@ -146,6 +165,17 @@ module Zavudev postal_code:, state:, # Status of a 10DLC brand registration. + # + # - `draft`: created, not yet submitted to the carrier. + # - `pending`: submitted, awaiting the carrier's answer. + # - `verified`: the carrier registered the brand AND verified the business behind + # it. + # - `unverified`: the carrier registered the brand but did not verify the business + # — the registration exists, the identity check did not pass or has not been + # resolved. Campaigns are allowed, with lower daily limits. Read + # `identityStatus` for the carrier's own wording. + # - `rejected`: refused by the carrier. + # - `failed`: the registration never reached the carrier; the fee is refunded. status:, street:, updated_at:, @@ -161,6 +191,11 @@ module Zavudev # Reason for rejection, if applicable. failure_reason: nil, first_name: nil, + # The carrier's raw identity verdict on the business, as the carrier spells it + # (`VERIFIED`, `VETTED_VERIFIED`, `SELF_DECLARED`, `UNVERIFIED`). Null while the + # identity has not been resolved — which is not the same as verified, and is why + # such a brand reports `status: unverified`. + identity_status: nil, last_name: nil, stock_exchange: nil, stock_symbol: nil, @@ -194,6 +229,7 @@ module Zavudev ein: T.nilable(String), failure_reason: T.nilable(String), first_name: T.nilable(String), + identity_status: T.nilable(String), last_name: T.nilable(String), stock_exchange: T.nilable(String), stock_symbol: T.nilable(String), @@ -254,6 +290,17 @@ module Zavudev end # Status of a 10DLC brand registration. + # + # - `draft`: created, not yet submitted to the carrier. + # - `pending`: submitted, awaiting the carrier's answer. + # - `verified`: the carrier registered the brand AND verified the business behind + # it. + # - `unverified`: the carrier registered the brand but did not verify the business + # — the registration exists, the identity check did not pass or has not been + # resolved. Campaigns are allowed, with lower daily limits. Read + # `identityStatus` for the carrier's own wording. + # - `rejected`: refused by the carrier. + # - `failed`: the registration never reached the carrier; the fee is refunded. module Status extend Zavudev::Internal::Type::Enum @@ -278,11 +325,21 @@ module Zavudev :verified, Zavudev::Number10dlc::TenDlcBrand::Status::TaggedSymbol ) + UNVERIFIED = + T.let( + :unverified, + Zavudev::Number10dlc::TenDlcBrand::Status::TaggedSymbol + ) REJECTED = T.let( :rejected, Zavudev::Number10dlc::TenDlcBrand::Status::TaggedSymbol ) + FAILED = + T.let( + :failed, + Zavudev::Number10dlc::TenDlcBrand::Status::TaggedSymbol + ) sig do override.returns( diff --git a/sig/zavudev/models/number_10dlc/ten_dlc_brand.rbs b/sig/zavudev/models/number_10dlc/ten_dlc_brand.rbs index 0904ef1..21d4ecc 100644 --- a/sig/zavudev/models/number_10dlc/ten_dlc_brand.rbs +++ b/sig/zavudev/models/number_10dlc/ten_dlc_brand.rbs @@ -23,6 +23,7 @@ module Zavudev ein: String?, failure_reason: String?, first_name: String?, + identity_status: String?, last_name: String?, stock_exchange: String?, stock_symbol: String?, @@ -72,6 +73,8 @@ module Zavudev attr_accessor first_name: String? + attr_accessor identity_status: String? + attr_accessor last_name: String? attr_accessor stock_exchange: String? @@ -105,6 +108,7 @@ module Zavudev ?ein: String?, ?failure_reason: String?, ?first_name: String?, + ?identity_status: String?, ?last_name: String?, ?stock_exchange: String?, ?stock_symbol: String?, @@ -134,6 +138,7 @@ module Zavudev ein: String?, failure_reason: String?, first_name: String?, + identity_status: String?, last_name: String?, stock_exchange: String?, stock_symbol: String?, @@ -161,7 +166,8 @@ module Zavudev def self?.values: -> ::Array[Zavudev::Models::Number10dlc::TenDlcBrand::entity_type] end - type status = :draft | :pending | :verified | :rejected + type status = + :draft | :pending | :verified | :unverified | :rejected | :failed module Status extend Zavudev::Internal::Type::Enum @@ -169,7 +175,9 @@ module Zavudev DRAFT: :draft PENDING: :pending VERIFIED: :verified + UNVERIFIED: :unverified REJECTED: :rejected + FAILED: :failed def self?.values: -> ::Array[Zavudev::Models::Number10dlc::TenDlcBrand::status] end diff --git a/test/zavudev/resources/number_10dlc/brands_test.rb b/test/zavudev/resources/number_10dlc/brands_test.rb index 638c043..0ca2509 100644 --- a/test/zavudev/resources/number_10dlc/brands_test.rb +++ b/test/zavudev/resources/number_10dlc/brands_test.rb @@ -101,6 +101,7 @@ def test_list ein: String | nil, failure_reason: String | nil, first_name: String | nil, + identity_status: String | nil, last_name: String | nil, stock_exchange: String | nil, stock_symbol: String | nil, From dac48101d0b462154984874cb60992ca2879807e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:22:17 +0000 Subject: [PATCH 02/15] feat(api): api update --- .stats.yml | 4 +- lib/zavudev/models/address_create_params.rb | 42 ++++--- lib/zavudev/models/owned_phone_number.rb | 59 +++++++++- lib/zavudev/models/phone_number_pricing.rb | 9 +- .../models/phone_number_purchase_params.rb | 64 +++++++++- .../phone_number_requirements_params.rb | 29 +++-- lib/zavudev/models/phone_number_status.rb | 6 + .../models/phone_number_update_params.rb | 9 +- lib/zavudev/models/requirement.rb | 4 +- lib/zavudev/models/requirement_type.rb | 3 +- lib/zavudev/models/sender_create_params.rb | 6 +- lib/zavudev/resources/addresses.rb | 29 +++-- lib/zavudev/resources/phone_numbers.rb | 89 +++++++++++--- rbi/zavudev/models/address_create_params.rbi | 38 +++--- rbi/zavudev/models/owned_phone_number.rbi | 89 ++++++++++++++ rbi/zavudev/models/phone_number_pricing.rbi | 14 ++- .../models/phone_number_purchase_params.rbi | 110 ++++++++++++++++++ .../phone_number_requirements_params.rbi | 36 ++++-- rbi/zavudev/models/phone_number_status.rbi | 6 + .../models/phone_number_update_params.rbi | 8 +- rbi/zavudev/models/requirement.rbi | 4 +- rbi/zavudev/models/requirement_type.rbi | 2 + rbi/zavudev/models/sender_create_params.rbi | 12 +- rbi/zavudev/resources/addresses.rbi | 21 ++-- rbi/zavudev/resources/phone_numbers.rbi | 95 +++++++++++++-- rbi/zavudev/resources/senders.rbi | 6 +- sig/zavudev/models/address_create_params.rbs | 26 ++--- sig/zavudev/models/owned_phone_number.rbs | 17 +++ .../models/phone_number_purchase_params.rbs | 36 +++++- .../phone_number_requirements_params.rbs | 18 ++- sig/zavudev/models/phone_number_status.rbs | 5 +- sig/zavudev/resources/addresses.rbs | 4 +- sig/zavudev/resources/phone_numbers.rbs | 5 +- test/zavudev/resources/addresses_test.rb | 2 + test/zavudev/resources/phone_numbers_test.rb | 5 +- 35 files changed, 770 insertions(+), 142 deletions(-) diff --git a/.stats.yml b/.stats.yml index 10360c4..bbcd963 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-997e957e7b8c6a15c356fd9182f73a1a149e78b702566daf23b1e7519c06ee95.yml -openapi_spec_hash: a0ee7a79588077d95d4bf9fccc3837a2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-6a4c9cdb8d971155ac22e679a37d27525a97b8a65991908a1c12969252457811.yml +openapi_spec_hash: 5d999f34f096d592deaa6319f52190b3 config_hash: 261e1b852ca7f8364e4a283a3edd350a diff --git a/lib/zavudev/models/address_create_params.rb b/lib/zavudev/models/address_create_params.rb index 391bbfc..136c379 100644 --- a/lib/zavudev/models/address_create_params.rb +++ b/lib/zavudev/models/address_create_params.rb @@ -12,6 +12,18 @@ class AddressCreateParams < Zavudev::Internal::Type::BaseModel # @return [String] required :country_code, String, api_name: :countryCode + # @!attribute first_name + # First name of the person the address is registered to. + # + # @return [String] + required :first_name, String, api_name: :firstName + + # @!attribute last_name + # Last name of the person the address is registered to. + # + # @return [String] + required :last_name, String, api_name: :lastName + # @!attribute locality # # @return [String] @@ -33,6 +45,8 @@ class AddressCreateParams < Zavudev::Internal::Type::BaseModel optional :administrative_area, String, api_name: :administrativeArea # @!attribute business_name + # Business name, when the address belongs to a business. Defaults to the person's + # full name. # # @return [String, nil] optional :business_name, String, api_name: :businessName @@ -42,26 +56,28 @@ class AddressCreateParams < Zavudev::Internal::Type::BaseModel # @return [String, nil] optional :extended_address, String, api_name: :extendedAddress - # @!attribute first_name - # - # @return [String, nil] - optional :first_name, String, api_name: :firstName - - # @!attribute last_name + # @!method initialize(country_code:, first_name:, last_name:, locality:, postal_code:, street_address:, administrative_area: nil, business_name: nil, extended_address: nil, request_options: {}) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::AddressCreateParams} for more details. # - # @return [String, nil] - optional :last_name, String, api_name: :lastName - - # @!method initialize(country_code:, locality:, postal_code:, street_address:, administrative_area: nil, business_name: nil, extended_address: nil, first_name: nil, last_name: nil, request_options: {}) # @param country_code [String] + # + # @param first_name [String] First name of the person the address is registered to. + # + # @param last_name [String] Last name of the person the address is registered to. + # # @param locality [String] + # # @param postal_code [String] + # # @param street_address [String] + # # @param administrative_area [String] - # @param business_name [String] + # + # @param business_name [String] Business name, when the address belongs to a business. Defaults to the person's + # # @param extended_address [String] - # @param first_name [String] - # @param last_name [String] + # # @param request_options [Zavudev::RequestOptions, Hash{Symbol=>Object}] end end diff --git a/lib/zavudev/models/owned_phone_number.rb b/lib/zavudev/models/owned_phone_number.rb index ceb0378..bd6e31d 100644 --- a/lib/zavudev/models/owned_phone_number.rb +++ b/lib/zavudev/models/owned_phone_number.rb @@ -29,7 +29,31 @@ class OwnedPhoneNumber < Zavudev::Internal::Type::BaseModel # @return [Zavudev::Models::OwnedPhoneNumberPricing] required :pricing, -> { Zavudev::OwnedPhoneNumberPricing } + # @!attribute regulatory_status + # Regulatory review state. Numbers that need no review are `approved` immediately. + # A number bought with regulatory information is owned and billed from purchase + # and starts `pending_review`; it cannot send messages or place calls until this + # is `approved`. The state is re-checked every 6 hours: poll + # `GET /v1/phone-numbers/{phoneNumberId}` to follow it. + # + # Assign it to a sender with `PATCH /v1/phone-numbers/{phoneNumberId}` + # (`senderId`) before or after approval. A number assigned while under review is + # recorded and connected to that sender when it is approved; the connection is + # retried until it succeeds. A sender created over the API is set up for SMS as + # part of the assignment. `rejected` means review refused the information: the + # number cannot be assigned to a sender. A number that stays `pending_review` may + # be waiting on information the API cannot supply; contact support. + # + # @return [Symbol, Zavudev::Models::OwnedPhoneNumber::RegulatoryStatus] + required :regulatory_status, + enum: -> { Zavudev::OwnedPhoneNumber::RegulatoryStatus }, + api_name: :regulatoryStatus + # @!attribute status + # Billing state of an owned number, separate from `regulatoryStatus`. `pending` is + # legacy and is not written to numbers today. The SDKs carry `active`, `suspended` + # and `pending` only; `releasing` and `released` are returned by the REST API + # until their next release. # # @return [Symbol, Zavudev::Models::PhoneNumberStatus] required :status, enum: -> { Zavudev::PhoneNumberStatus } @@ -56,7 +80,10 @@ class OwnedPhoneNumber < Zavudev::Internal::Type::BaseModel # @return [Time, nil] optional :updated_at, Time, api_name: :updatedAt - # @!method initialize(id:, capabilities:, created_at:, phone_number:, pricing:, status:, name: nil, next_renewal_date: nil, sender_id: nil, updated_at: nil) + # @!method initialize(id:, capabilities:, created_at:, phone_number:, pricing:, regulatory_status:, status:, name: nil, next_renewal_date: nil, sender_id: nil, updated_at: nil) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::OwnedPhoneNumber} for more details. + # # @param id [String] # # @param capabilities [Array] @@ -67,7 +94,9 @@ class OwnedPhoneNumber < Zavudev::Internal::Type::BaseModel # # @param pricing [Zavudev::Models::OwnedPhoneNumberPricing] # - # @param status [Symbol, Zavudev::Models::PhoneNumberStatus] + # @param regulatory_status [Symbol, Zavudev::Models::OwnedPhoneNumber::RegulatoryStatus] Regulatory review state. Numbers that need no review are `approved` immediately. + # + # @param status [Symbol, Zavudev::Models::PhoneNumberStatus] Billing state of an owned number, separate from `regulatoryStatus`. `pending` is # # @param name [String] Optional custom name for the phone number. # @@ -76,6 +105,32 @@ class OwnedPhoneNumber < Zavudev::Internal::Type::BaseModel # @param sender_id [String] Sender ID if the phone number is assigned to a sender. # # @param updated_at [Time] + + # Regulatory review state. Numbers that need no review are `approved` immediately. + # A number bought with regulatory information is owned and billed from purchase + # and starts `pending_review`; it cannot send messages or place calls until this + # is `approved`. The state is re-checked every 6 hours: poll + # `GET /v1/phone-numbers/{phoneNumberId}` to follow it. + # + # Assign it to a sender with `PATCH /v1/phone-numbers/{phoneNumberId}` + # (`senderId`) before or after approval. A number assigned while under review is + # recorded and connected to that sender when it is approved; the connection is + # retried until it succeeds. A sender created over the API is set up for SMS as + # part of the assignment. `rejected` means review refused the information: the + # number cannot be assigned to a sender. A number that stays `pending_review` may + # be waiting on information the API cannot supply; contact support. + # + # @see Zavudev::Models::OwnedPhoneNumber#regulatory_status + module RegulatoryStatus + extend Zavudev::Internal::Type::Enum + + APPROVED = :approved + PENDING_REVIEW = :pending_review + REJECTED = :rejected + + # @!method self.values + # @return [Array] + end end end end diff --git a/lib/zavudev/models/phone_number_pricing.rb b/lib/zavudev/models/phone_number_pricing.rb index 0cdda25..08ebf48 100644 --- a/lib/zavudev/models/phone_number_pricing.rb +++ b/lib/zavudev/models/phone_number_pricing.rb @@ -4,9 +4,10 @@ module Zavudev module Models class PhoneNumberPricing < Zavudev::Internal::Type::BaseModel # @!attribute is_free_eligible - # 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. + # Whether this number qualifies as the plan-included number: a US or Canadian + # number (a +1 number) costing $20 a month or less. The benefit is one per + # account: it is never offered again once claimed, not even after the number is + # released. # # @return [Boolean, nil] optional :is_free_eligible, Zavudev::Internal::Type::Boolean, api_name: :isFreeEligible @@ -27,7 +28,7 @@ class PhoneNumberPricing < Zavudev::Internal::Type::BaseModel # Some parameter documentations has been truncated, see # {Zavudev::Models::PhoneNumberPricing} for more details. # - # @param is_free_eligible [Boolean] Whether this number qualifies as the plan-included US number on paid plans. The + # @param is_free_eligible [Boolean] Whether this number qualifies as the plan-included number: a US or Canadian numb # # @param monthly_price [Float] Monthly price in USD. # diff --git a/lib/zavudev/models/phone_number_purchase_params.rb b/lib/zavudev/models/phone_number_purchase_params.rb index f72a0b4..c2838f3 100644 --- a/lib/zavudev/models/phone_number_purchase_params.rb +++ b/lib/zavudev/models/phone_number_purchase_params.rb @@ -19,12 +19,74 @@ class PhoneNumberPurchaseParams < Zavudev::Internal::Type::BaseModel # @return [String, nil] optional :name, String - # @!method initialize(phone_number:, name: nil, request_options: {}) + # @!attribute regulatory_requirements + # Regulatory information, for numbers whose requirements list is not empty. Get + # the list with `GET /v1/phone-numbers/requirements?phoneNumber=...` and send one + # entry per requirement id, except `action` requirements, which take no value. + # Every required id must be present, once, and no unknown id may be sent; + # otherwise the purchase is refused with `400 invalid_request` before anything is + # charged. + # + # The information is kept for your project under the number's country and `type`. + # A later purchase there may omit this field if what is kept still covers that + # number's requirements. Omit it for numbers without requirements. + # + # @return [Array, nil] + optional :regulatory_requirements, + -> { + Zavudev::Internal::Type::ArrayOf[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement] + }, + api_name: :regulatoryRequirements + + # @!attribute type + # Type of phone number. `mobile` is stocked in countries where no geographic + # (`local`) or non-geographic (`national`) inventory exists, and in several + # markets it is the only type that can receive SMS. + # + # @return [Symbol, Zavudev::Models::PhoneNumberType, nil] + optional :type, enum: -> { Zavudev::PhoneNumberType } + + # @!method initialize(phone_number:, name: nil, regulatory_requirements: nil, type: nil, request_options: {}) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::PhoneNumberPurchaseParams} for more details. + # # @param phone_number [String] Phone number in E.164 format. # # @param name [String] Optional custom name for the phone number. # + # @param regulatory_requirements [Array] Regulatory information, for numbers whose requirements list is not empty. Get th + # + # @param type [Symbol, Zavudev::Models::PhoneNumberType] Type of phone number. `mobile` is stocked in countries where no geographic (`loc + # # @param request_options [Zavudev::RequestOptions, Hash{Symbol=>Object}] + + class RegulatoryRequirement < Zavudev::Internal::Type::BaseModel + # @!attribute field_value + # Depends on the requirement's `type`: the text itself for `textual`; for + # `address`, the `id` of an address created in this project with + # `POST /v1/addresses`; for `document`, the `id` of a document created with + # `POST /v1/documents`. An address or document from another project, or one + # rejected in review, is refused. + # + # @return [String] + required :field_value, String, api_name: :fieldValue + + # @!attribute requirement_type + # A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each id may + # appear only once. + # + # @return [String] + required :requirement_type, String, api_name: :requirementType + + # @!method initialize(field_value:, requirement_type:) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::PhoneNumberPurchaseParams::RegulatoryRequirement} for more + # details. + # + # @param field_value [String] Depends on the requirement's `type`: the text itself for `textual`; for `address + # + # @param requirement_type [String] A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each id may + end end end end diff --git a/lib/zavudev/models/phone_number_requirements_params.rb b/lib/zavudev/models/phone_number_requirements_params.rb index 3a42130..bfe30e7 100644 --- a/lib/zavudev/models/phone_number_requirements_params.rb +++ b/lib/zavudev/models/phone_number_requirements_params.rb @@ -8,21 +8,36 @@ class PhoneNumberRequirementsParams < Zavudev::Internal::Type::BaseModel include Zavudev::Internal::Type::RequestParameters # @!attribute country_code - # Two-letter ISO country code. + # Two-letter ISO country code. Required unless `phoneNumber` is given. # - # @return [String] - required :country_code, String + # @return [String, nil] + optional :country_code, String + + # @!attribute phone_number + # E.164 number from `GET /v1/phone-numbers/available`, with `+` encoded as `%2B`. + # Returns the requirements the purchase of that number checks. Takes precedence + # over `countryCode`. + # + # @return [String, nil] + optional :phone_number, String # @!attribute type - # Type of phone number (local, mobile, tollFree). + # Type of phone number (local, national, mobile, tollFree). Defaults to `local`. + # With `phoneNumber`, used only when the number's own requirements cannot be + # resolved and the country list is returned. # # @return [Symbol, Zavudev::Models::PhoneNumberType, nil] optional :type, enum: -> { Zavudev::PhoneNumberType } - # @!method initialize(country_code:, type: nil, request_options: {}) - # @param country_code [String] Two-letter ISO country code. + # @!method initialize(country_code: nil, phone_number: nil, type: nil, request_options: {}) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::PhoneNumberRequirementsParams} for more details. + # + # @param country_code [String] Two-letter ISO country code. Required unless `phoneNumber` is given. + # + # @param phone_number [String] E.164 number from `GET /v1/phone-numbers/available`, with `+` encoded as `%2B`. # - # @param type [Symbol, Zavudev::Models::PhoneNumberType] Type of phone number (local, mobile, tollFree). + # @param type [Symbol, Zavudev::Models::PhoneNumberType] Type of phone number (local, national, mobile, tollFree). Defaults to `local`. W # # @param request_options [Zavudev::RequestOptions, Hash{Symbol=>Object}] end diff --git a/lib/zavudev/models/phone_number_status.rb b/lib/zavudev/models/phone_number_status.rb index 9ad355b..0712b19 100644 --- a/lib/zavudev/models/phone_number_status.rb +++ b/lib/zavudev/models/phone_number_status.rb @@ -2,12 +2,18 @@ module Zavudev module Models + # Billing state of an owned number, separate from `regulatoryStatus`. `pending` is + # legacy and is not written to numbers today. The SDKs carry `active`, `suspended` + # and `pending` only; `releasing` and `released` are returned by the REST API + # until their next release. module PhoneNumberStatus extend Zavudev::Internal::Type::Enum ACTIVE = :active SUSPENDED = :suspended PENDING = :pending + RELEASING = :releasing + RELEASED = :released # @!method self.values # @return [Array] diff --git a/lib/zavudev/models/phone_number_update_params.rb b/lib/zavudev/models/phone_number_update_params.rb index 4e8658d..c6dbb4c 100644 --- a/lib/zavudev/models/phone_number_update_params.rb +++ b/lib/zavudev/models/phone_number_update_params.rb @@ -19,17 +19,22 @@ class PhoneNumberUpdateParams < Zavudev::Internal::Type::BaseModel optional :name, String, nil?: true # @!attribute sender_id - # Sender ID to assign the phone number to. Set to null to unassign. + # Sender ID to assign the phone number to. Set to null to unassign. A number under + # regulatory review is recorded now and connected to the sender when approved; a + # rejected number is refused. # # @return [String, nil] optional :sender_id, String, api_name: :senderId, nil?: true # @!method initialize(phone_number_id:, name: nil, sender_id: nil, request_options: {}) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::PhoneNumberUpdateParams} for more details. + # # @param phone_number_id [String] # # @param name [String, nil] Custom name for the phone number. Set to null to clear. # - # @param sender_id [String, nil] Sender ID to assign the phone number to. Set to null to unassign. + # @param sender_id [String, nil] Sender ID to assign the phone number to. Set to null to unassign. A number under # # @param request_options [Zavudev::RequestOptions, Hash{Symbol=>Object}] end diff --git a/lib/zavudev/models/requirement.rb b/lib/zavudev/models/requirement.rb index 7411974..c9b52dd 100644 --- a/lib/zavudev/models/requirement.rb +++ b/lib/zavudev/models/requirement.rb @@ -31,7 +31,9 @@ class Requirement < Zavudev::Internal::Type::BaseModel api_name: :requirementTypes # @!method initialize(id:, action:, country_code:, phone_number_type:, requirement_types:) - # A group of requirements for a specific country/phone type combination. + # The requirements for ordering a number: for a country and number type, or for + # one specific number when requested with `phoneNumber` (then `id` is that phone + # number and `countryCode` is taken from it). # # @param id [String] # @param action [String] diff --git a/lib/zavudev/models/requirement_type.rb b/lib/zavudev/models/requirement_type.rb index 6a30990..822ac90 100644 --- a/lib/zavudev/models/requirement_type.rb +++ b/lib/zavudev/models/requirement_type.rb @@ -4,6 +4,7 @@ module Zavudev module Models class RequirementType < Zavudev::Internal::Type::BaseModel # @!attribute id + # Send this as `requirementType` in `regulatoryRequirements` when purchasing. # # @return [String] required :id, String @@ -40,7 +41,7 @@ class RequirementType < Zavudev::Internal::Type::BaseModel # @!method initialize(id:, description:, name:, type:, acceptance_criteria: nil, example: nil) # A specific requirement type within a requirement group. # - # @param id [String] + # @param id [String] Send this as `requirementType` in `regulatoryRequirements` when purchasing. # # @param description [String] # diff --git a/lib/zavudev/models/sender_create_params.rb b/lib/zavudev/models/sender_create_params.rb index 71f13ea..539285d 100644 --- a/lib/zavudev/models/sender_create_params.rb +++ b/lib/zavudev/models/sender_create_params.rb @@ -61,8 +61,10 @@ class SenderCreateParams < Zavudev::Internal::Type::BaseModel # Phone number in E.164 format, and it must be a number your project already owns # (see `GET /v1/phone-numbers`). The number is routed to the sender as part of # this call, which is what turns the SMS channel on. Passing a number the project - # does not own, or one already attached to another sender, returns 400 rather than - # creating a sender that cannot send. Omit for an email-only sender. + # does not own, one already attached to another sender, or one rejected in + # regulatory review returns 400 rather than creating a sender that cannot send. A + # number still under review is attached and starts carrying messages when it is + # approved. Omit for an email-only sender. # # @return [String, nil] optional :phone_number, String, api_name: :phoneNumber diff --git a/lib/zavudev/resources/addresses.rb b/lib/zavudev/resources/addresses.rb index 9c6cc29..594d496 100644 --- a/lib/zavudev/resources/addresses.rb +++ b/lib/zavudev/resources/addresses.rb @@ -3,20 +3,33 @@ module Zavudev module Resources class Addresses - # Create a regulatory address for phone number purchases. Some countries require a - # verified address before phone numbers can be activated. + # Some parameter documentations has been truncated, see + # {Zavudev::Models::AddressCreateParams} for more details. # - # @overload create(country_code:, locality:, postal_code:, street_address:, administrative_area: nil, business_name: nil, extended_address: nil, first_name: nil, last_name: nil, request_options: {}) + # Create a regulatory address, to use as the value of an `address` requirement + # when buying a phone number. It is registered for review when it is created, with + # status `pending`. + # + # @overload create(country_code:, first_name:, last_name:, locality:, postal_code:, street_address:, administrative_area: nil, business_name: nil, extended_address: nil, request_options: {}) # # @param country_code [String] + # + # @param first_name [String] First name of the person the address is registered to. + # + # @param last_name [String] Last name of the person the address is registered to. + # # @param locality [String] + # # @param postal_code [String] + # # @param street_address [String] + # # @param administrative_area [String] - # @param business_name [String] + # + # @param business_name [String] Business name, when the address belongs to a business. Defaults to the person's + # # @param extended_address [String] - # @param first_name [String] - # @param last_name [String] + # # @param request_options [Zavudev::RequestOptions, Hash{Symbol=>Object}, nil] # # @return [Zavudev::Models::AddressCreateResponse] @@ -76,7 +89,9 @@ def list(params = {}) ) end - # Delete a regulatory address. Cannot delete addresses that are in use. + # Delete a regulatory address from this project. Any address can be deleted, + # whatever its status. Phone numbers already purchased with it are not affected, + # and neither is information already submitted for later purchases in its country. # # @overload delete(address_id, request_options: {}) # diff --git a/lib/zavudev/resources/phone_numbers.rb b/lib/zavudev/resources/phone_numbers.rb index d5b6318..85a7999 100644 --- a/lib/zavudev/resources/phone_numbers.rb +++ b/lib/zavudev/resources/phone_numbers.rb @@ -22,6 +22,9 @@ def retrieve(phone_number_id, params = {}) ) end + # Some parameter documentations has been truncated, see + # {Zavudev::Models::PhoneNumberUpdateParams} for more details. + # # Update a phone number's name or sender assignment. # # @overload update(phone_number_id, name: nil, sender_id: nil, request_options: {}) @@ -30,7 +33,7 @@ def retrieve(phone_number_id, params = {}) # # @param name [String, nil] Custom name for the phone number. Set to null to clear. # - # @param sender_id [String, nil] Sender ID to assign the phone number to. Set to null to unassign. + # @param sender_id [String, nil] Sender ID to assign the phone number to. Set to null to unassign. A number under # # @param request_options [Zavudev::RequestOptions, Hash{Symbol=>Object}, nil] # @@ -76,19 +79,57 @@ def list(params = {}) ) end - # Purchase an available phone number. Requires a paid plan: the Free plan cannot - # purchase phone numbers and receives `402` with code `paid_plan_required`. Paid - # 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. + # Some parameter documentations has been truncated, see + # {Zavudev::Models::PhoneNumberPurchaseParams} for more details. # - # @overload purchase(phone_number:, name: nil, request_options: {}) + # Purchase an available phone number. Requires a paid plan: the Free plan cannot + # purchase phone numbers and receives `402` with code `paid_plan_required`. + # + # **The included number.** A paid plan includes one number at no charge, once per + # account: it must be a US or Canadian number (a +1 number) costing $20 a month or + # less. `isFreeEligible` in `GET /v1/phone-numbers/available` marks the numbers + # that qualify. Claiming it spends the benefit for good, across every team the + # account owner owns, so releasing that number does not make another one free. + # + # **Numbers with regulatory requirements.** Which numbers need regulatory + # information is decided per number, not by a fixed country list. The purchase + # looks the requirements up for the exact number before charging anything: + # + # 1. `GET /v1/phone-numbers/requirements?phoneNumber=...`. If `items` is empty, + # buy normally. + # 2. Create what it asks for: addresses with `POST /v1/addresses`, documents with + # `POST /v1/documents`. + # 3. Purchase with `type` and `regulatoryRequirements`. The number is bought and + # billed at once with `regulatoryStatus: pending_review`. + # 4. Poll `GET /v1/phone-numbers/{phoneNumberId}` until `regulatoryStatus` is + # `approved`. Assign it to a sender before or after approval; it starts + # carrying messages once approved. + # + # **Reuse.** Information you submitted is kept for your project, per country and + # `type`, and a later purchase there may omit `regulatoryRequirements`. Reuse only + # happens when what is kept still covers every requirement of the new number and + # every address and document in it belongs to the project. Otherwise, or when + # nothing is kept, the purchase returns `400 regulatory_compliance_required` with + # the missing requirements in `details`. + # + # Invalid values (a missing, unknown or repeated requirement id, an address or + # document from another project, or one rejected in review) return + # `400 invalid_request`. If an address or document cannot be registered for + # review, the purchase returns `400 invalid_request` naming the requirement. If + # the requirements cannot be looked up, the purchase returns + # `502 requirements_unavailable`, except for US and Canadian numbers, which are + # sold as numbers without requirements. None of these errors charge anything. + # + # @overload purchase(phone_number:, name: nil, regulatory_requirements: nil, type: nil, request_options: {}) # # @param phone_number [String] Phone number in E.164 format. # # @param name [String] Optional custom name for the phone number. # + # @param regulatory_requirements [Array] Regulatory information, for numbers whose requirements list is not empty. Get th + # + # @param type [Symbol, Zavudev::Models::PhoneNumberType] Type of phone number. `mobile` is stocked in countries where no geographic (`loc + # # @param request_options [Zavudev::RequestOptions, Hash{Symbol=>Object}, nil] # # @return [Zavudev::Models::PhoneNumberPurchaseResponse] @@ -124,28 +165,44 @@ def release(phone_number_id, params = {}) ) end - # Get regulatory requirements for purchasing phone numbers in a specific country. - # Some countries require additional documentation (addresses, identity documents) - # before phone numbers can be activated. + # Some parameter documentations has been truncated, see + # {Zavudev::Models::PhoneNumberRequirementsParams} for more details. # - # @overload requirements(country_code:, type: nil, request_options: {}) + # Get the regulatory information needed to buy a phone number, for one specific + # number or for a country and number type. Prefer `phoneNumber`: the response is + # then exactly the list the purchase of that number validates against. Pass each + # `requirementTypes[].id` back as `requirementType` in `regulatoryRequirements` on + # `POST /v1/phone-numbers`. # - # @param country_code [String] Two-letter ISO country code. + # For `phoneNumber`, the requirements of that exact number are returned. When they + # cannot be resolved for the number itself, the list for its country and `type` is + # returned instead, and the purchase uses the same list. An empty `items` array + # means the number needs no regulatory information. If the requirements cannot be + # retrieved at all, the response is `502 requirements_unavailable`, never an empty + # list. + # + # URL-encode the `+` of `phoneNumber` as `%2B`. An unencoded `+` is also accepted. + # + # @overload requirements(country_code: nil, phone_number: nil, type: nil, request_options: {}) # - # @param type [Symbol, Zavudev::Models::PhoneNumberType] Type of phone number (local, mobile, tollFree). + # @param country_code [String] Two-letter ISO country code. Required unless `phoneNumber` is given. + # + # @param phone_number [String] E.164 number from `GET /v1/phone-numbers/available`, with `+` encoded as `%2B`. + # + # @param type [Symbol, Zavudev::Models::PhoneNumberType] Type of phone number (local, national, mobile, tollFree). Defaults to `local`. W # # @param request_options [Zavudev::RequestOptions, Hash{Symbol=>Object}, nil] # # @return [Zavudev::Models::PhoneNumberRequirementsResponse] # # @see Zavudev::Models::PhoneNumberRequirementsParams - def requirements(params) + def requirements(params = {}) parsed, options = Zavudev::PhoneNumberRequirementsParams.dump_request(params) query = Zavudev::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "v1/phone-numbers/requirements", - query: query.transform_keys(country_code: "countryCode"), + query: query.transform_keys(country_code: "countryCode", phone_number: "phoneNumber"), model: Zavudev::Models::PhoneNumberRequirementsResponse, options: options ) diff --git a/rbi/zavudev/models/address_create_params.rbi b/rbi/zavudev/models/address_create_params.rbi index 2edc226..3495158 100644 --- a/rbi/zavudev/models/address_create_params.rbi +++ b/rbi/zavudev/models/address_create_params.rbi @@ -14,6 +14,14 @@ module Zavudev sig { returns(String) } attr_accessor :country_code + # First name of the person the address is registered to. + sig { returns(String) } + attr_accessor :first_name + + # Last name of the person the address is registered to. + sig { returns(String) } + attr_accessor :last_name + sig { returns(String) } attr_accessor :locality @@ -29,6 +37,8 @@ module Zavudev sig { params(administrative_area: String).void } attr_writer :administrative_area + # Business name, when the address belongs to a business. Defaults to the person's + # full name. sig { returns(T.nilable(String)) } attr_reader :business_name @@ -41,42 +51,34 @@ module Zavudev sig { params(extended_address: String).void } attr_writer :extended_address - sig { returns(T.nilable(String)) } - attr_reader :first_name - - sig { params(first_name: String).void } - attr_writer :first_name - - sig { returns(T.nilable(String)) } - attr_reader :last_name - - sig { params(last_name: String).void } - attr_writer :last_name - sig do params( country_code: String, + first_name: String, + last_name: String, locality: String, postal_code: String, street_address: String, administrative_area: String, business_name: String, extended_address: String, - first_name: String, - last_name: String, request_options: Zavudev::RequestOptions::OrHash ).returns(T.attached_class) end def self.new( country_code:, + # First name of the person the address is registered to. + first_name:, + # Last name of the person the address is registered to. + last_name:, locality:, postal_code:, street_address:, administrative_area: nil, + # Business name, when the address belongs to a business. Defaults to the person's + # full name. business_name: nil, extended_address: nil, - first_name: nil, - last_name: nil, request_options: {} ) end @@ -85,14 +87,14 @@ module Zavudev override.returns( { country_code: String, + first_name: String, + last_name: String, locality: String, postal_code: String, street_address: String, administrative_area: String, business_name: String, extended_address: String, - first_name: String, - last_name: String, request_options: Zavudev::RequestOptions } ) diff --git a/rbi/zavudev/models/owned_phone_number.rbi b/rbi/zavudev/models/owned_phone_number.rbi index c3c6b67..e754ffc 100644 --- a/rbi/zavudev/models/owned_phone_number.rbi +++ b/rbi/zavudev/models/owned_phone_number.rbi @@ -26,6 +26,26 @@ module Zavudev sig { params(pricing: Zavudev::OwnedPhoneNumberPricing::OrHash).void } attr_writer :pricing + # Regulatory review state. Numbers that need no review are `approved` immediately. + # A number bought with regulatory information is owned and billed from purchase + # and starts `pending_review`; it cannot send messages or place calls until this + # is `approved`. The state is re-checked every 6 hours: poll + # `GET /v1/phone-numbers/{phoneNumberId}` to follow it. + # + # Assign it to a sender with `PATCH /v1/phone-numbers/{phoneNumberId}` + # (`senderId`) before or after approval. A number assigned while under review is + # recorded and connected to that sender when it is approved; the connection is + # retried until it succeeds. A sender created over the API is set up for SMS as + # part of the assignment. `rejected` means review refused the information: the + # number cannot be assigned to a sender. A number that stays `pending_review` may + # be waiting on information the API cannot supply; contact support. + sig { returns(Zavudev::OwnedPhoneNumber::RegulatoryStatus::TaggedSymbol) } + attr_accessor :regulatory_status + + # Billing state of an owned number, separate from `regulatoryStatus`. `pending` is + # legacy and is not written to numbers today. The SDKs carry `active`, `suspended` + # and `pending` only; `releasing` and `released` are returned by the REST API + # until their next release. sig { returns(Zavudev::PhoneNumberStatus::TaggedSymbol) } attr_accessor :status @@ -62,6 +82,8 @@ module Zavudev created_at: Time, phone_number: String, pricing: Zavudev::OwnedPhoneNumberPricing::OrHash, + regulatory_status: + Zavudev::OwnedPhoneNumber::RegulatoryStatus::OrSymbol, status: Zavudev::PhoneNumberStatus::OrSymbol, name: String, next_renewal_date: Time, @@ -75,6 +97,24 @@ module Zavudev created_at:, phone_number:, pricing:, + # Regulatory review state. Numbers that need no review are `approved` immediately. + # A number bought with regulatory information is owned and billed from purchase + # and starts `pending_review`; it cannot send messages or place calls until this + # is `approved`. The state is re-checked every 6 hours: poll + # `GET /v1/phone-numbers/{phoneNumberId}` to follow it. + # + # Assign it to a sender with `PATCH /v1/phone-numbers/{phoneNumberId}` + # (`senderId`) before or after approval. A number assigned while under review is + # recorded and connected to that sender when it is approved; the connection is + # retried until it succeeds. A sender created over the API is set up for SMS as + # part of the assignment. `rejected` means review refused the information: the + # number cannot be assigned to a sender. A number that stays `pending_review` may + # be waiting on information the API cannot supply; contact support. + regulatory_status:, + # Billing state of an owned number, separate from `regulatoryStatus`. `pending` is + # legacy and is not written to numbers today. The SDKs carry `active`, `suspended` + # and `pending` only; `releasing` and `released` are returned by the REST API + # until their next release. status:, # Optional custom name for the phone number. name: nil, @@ -93,6 +133,8 @@ module Zavudev created_at: Time, phone_number: String, pricing: Zavudev::OwnedPhoneNumberPricing, + regulatory_status: + Zavudev::OwnedPhoneNumber::RegulatoryStatus::TaggedSymbol, status: Zavudev::PhoneNumberStatus::TaggedSymbol, name: String, next_renewal_date: Time, @@ -103,6 +145,53 @@ module Zavudev end def to_hash end + + # Regulatory review state. Numbers that need no review are `approved` immediately. + # A number bought with regulatory information is owned and billed from purchase + # and starts `pending_review`; it cannot send messages or place calls until this + # is `approved`. The state is re-checked every 6 hours: poll + # `GET /v1/phone-numbers/{phoneNumberId}` to follow it. + # + # Assign it to a sender with `PATCH /v1/phone-numbers/{phoneNumberId}` + # (`senderId`) before or after approval. A number assigned while under review is + # recorded and connected to that sender when it is approved; the connection is + # retried until it succeeds. A sender created over the API is set up for SMS as + # part of the assignment. `rejected` means review refused the information: the + # number cannot be assigned to a sender. A number that stays `pending_review` may + # be waiting on information the API cannot supply; contact support. + module RegulatoryStatus + extend Zavudev::Internal::Type::Enum + + TaggedSymbol = + T.type_alias do + T.all(Symbol, Zavudev::OwnedPhoneNumber::RegulatoryStatus) + end + OrSymbol = T.type_alias { T.any(Symbol, String) } + + APPROVED = + T.let( + :approved, + Zavudev::OwnedPhoneNumber::RegulatoryStatus::TaggedSymbol + ) + PENDING_REVIEW = + T.let( + :pending_review, + Zavudev::OwnedPhoneNumber::RegulatoryStatus::TaggedSymbol + ) + REJECTED = + T.let( + :rejected, + Zavudev::OwnedPhoneNumber::RegulatoryStatus::TaggedSymbol + ) + + sig do + override.returns( + T::Array[Zavudev::OwnedPhoneNumber::RegulatoryStatus::TaggedSymbol] + ) + end + def self.values + end + end end end end diff --git a/rbi/zavudev/models/phone_number_pricing.rbi b/rbi/zavudev/models/phone_number_pricing.rbi index 21ecdbe..a9f4f8c 100644 --- a/rbi/zavudev/models/phone_number_pricing.rbi +++ b/rbi/zavudev/models/phone_number_pricing.rbi @@ -8,9 +8,10 @@ module Zavudev T.any(Zavudev::PhoneNumberPricing, Zavudev::Internal::AnyHash) end - # 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. + # Whether this number qualifies as the plan-included number: a US or Canadian + # number (a +1 number) costing $20 a month or less. The benefit is one per + # account: it is never offered again once claimed, not even after the number is + # released. sig { returns(T.nilable(T::Boolean)) } attr_reader :is_free_eligible @@ -39,9 +40,10 @@ module Zavudev ).returns(T.attached_class) end def self.new( - # 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. + # Whether this number qualifies as the plan-included number: a US or Canadian + # number (a +1 number) costing $20 a month or less. The benefit is one per + # account: it is never offered again once claimed, not even after the number is + # released. is_free_eligible: nil, # Monthly price in USD. monthly_price: nil, diff --git a/rbi/zavudev/models/phone_number_purchase_params.rbi b/rbi/zavudev/models/phone_number_purchase_params.rbi index c3db9d9..082499b 100644 --- a/rbi/zavudev/models/phone_number_purchase_params.rbi +++ b/rbi/zavudev/models/phone_number_purchase_params.rbi @@ -22,10 +22,53 @@ module Zavudev sig { params(name: String).void } attr_writer :name + # Regulatory information, for numbers whose requirements list is not empty. Get + # the list with `GET /v1/phone-numbers/requirements?phoneNumber=...` and send one + # entry per requirement id, except `action` requirements, which take no value. + # Every required id must be present, once, and no unknown id may be sent; + # otherwise the purchase is refused with `400 invalid_request` before anything is + # charged. + # + # The information is kept for your project under the number's country and `type`. + # A later purchase there may omit this field if what is kept still covers that + # number's requirements. Omit it for numbers without requirements. + sig do + returns( + T.nilable( + T::Array[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement] + ) + ) + end + attr_reader :regulatory_requirements + + sig do + params( + regulatory_requirements: + T::Array[ + Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement::OrHash + ] + ).void + end + attr_writer :regulatory_requirements + + # Type of phone number. `mobile` is stocked in countries where no geographic + # (`local`) or non-geographic (`national`) inventory exists, and in several + # markets it is the only type that can receive SMS. + sig { returns(T.nilable(Zavudev::PhoneNumberType::OrSymbol)) } + attr_reader :type + + sig { params(type: Zavudev::PhoneNumberType::OrSymbol).void } + attr_writer :type + sig do params( phone_number: String, name: String, + regulatory_requirements: + T::Array[ + Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement::OrHash + ], + type: Zavudev::PhoneNumberType::OrSymbol, request_options: Zavudev::RequestOptions::OrHash ).returns(T.attached_class) end @@ -34,6 +77,21 @@ module Zavudev phone_number:, # Optional custom name for the phone number. name: nil, + # Regulatory information, for numbers whose requirements list is not empty. Get + # the list with `GET /v1/phone-numbers/requirements?phoneNumber=...` and send one + # entry per requirement id, except `action` requirements, which take no value. + # Every required id must be present, once, and no unknown id may be sent; + # otherwise the purchase is refused with `400 invalid_request` before anything is + # charged. + # + # The information is kept for your project under the number's country and `type`. + # A later purchase there may omit this field if what is kept still covers that + # number's requirements. Omit it for numbers without requirements. + regulatory_requirements: nil, + # Type of phone number. `mobile` is stocked in countries where no geographic + # (`local`) or non-geographic (`national`) inventory exists, and in several + # markets it is the only type that can receive SMS. + type: nil, request_options: {} ) end @@ -43,12 +101,64 @@ module Zavudev { phone_number: String, name: String, + regulatory_requirements: + T::Array[ + Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement + ], + type: Zavudev::PhoneNumberType::OrSymbol, request_options: Zavudev::RequestOptions } ) end def to_hash end + + class RegulatoryRequirement < Zavudev::Internal::Type::BaseModel + OrHash = + T.type_alias do + T.any( + Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement, + Zavudev::Internal::AnyHash + ) + end + + # Depends on the requirement's `type`: the text itself for `textual`; for + # `address`, the `id` of an address created in this project with + # `POST /v1/addresses`; for `document`, the `id` of a document created with + # `POST /v1/documents`. An address or document from another project, or one + # rejected in review, is refused. + sig { returns(String) } + attr_accessor :field_value + + # A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each id may + # appear only once. + sig { returns(String) } + attr_accessor :requirement_type + + sig do + params(field_value: String, requirement_type: String).returns( + T.attached_class + ) + end + def self.new( + # Depends on the requirement's `type`: the text itself for `textual`; for + # `address`, the `id` of an address created in this project with + # `POST /v1/addresses`; for `document`, the `id` of a document created with + # `POST /v1/documents`. An address or document from another project, or one + # rejected in review, is refused. + field_value:, + # A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each id may + # appear only once. + requirement_type: + ) + end + + sig do + override.returns({ field_value: String, requirement_type: String }) + end + def to_hash + end + end end end end diff --git a/rbi/zavudev/models/phone_number_requirements_params.rbi b/rbi/zavudev/models/phone_number_requirements_params.rbi index e9caa9f..4e3bf88 100644 --- a/rbi/zavudev/models/phone_number_requirements_params.rbi +++ b/rbi/zavudev/models/phone_number_requirements_params.rbi @@ -14,11 +14,25 @@ module Zavudev ) end - # Two-letter ISO country code. - sig { returns(String) } - attr_accessor :country_code + # Two-letter ISO country code. Required unless `phoneNumber` is given. + sig { returns(T.nilable(String)) } + attr_reader :country_code - # Type of phone number (local, mobile, tollFree). + sig { params(country_code: String).void } + attr_writer :country_code + + # E.164 number from `GET /v1/phone-numbers/available`, with `+` encoded as `%2B`. + # Returns the requirements the purchase of that number checks. Takes precedence + # over `countryCode`. + sig { returns(T.nilable(String)) } + attr_reader :phone_number + + sig { params(phone_number: String).void } + attr_writer :phone_number + + # Type of phone number (local, national, mobile, tollFree). Defaults to `local`. + # With `phoneNumber`, used only when the number's own requirements cannot be + # resolved and the country list is returned. sig { returns(T.nilable(Zavudev::PhoneNumberType::OrSymbol)) } attr_reader :type @@ -28,14 +42,21 @@ module Zavudev sig do params( country_code: String, + phone_number: String, type: Zavudev::PhoneNumberType::OrSymbol, request_options: Zavudev::RequestOptions::OrHash ).returns(T.attached_class) end def self.new( - # Two-letter ISO country code. - country_code:, - # Type of phone number (local, mobile, tollFree). + # Two-letter ISO country code. Required unless `phoneNumber` is given. + country_code: nil, + # E.164 number from `GET /v1/phone-numbers/available`, with `+` encoded as `%2B`. + # Returns the requirements the purchase of that number checks. Takes precedence + # over `countryCode`. + phone_number: nil, + # Type of phone number (local, national, mobile, tollFree). Defaults to `local`. + # With `phoneNumber`, used only when the number's own requirements cannot be + # resolved and the country list is returned. type: nil, request_options: {} ) @@ -45,6 +66,7 @@ module Zavudev override.returns( { country_code: String, + phone_number: String, type: Zavudev::PhoneNumberType::OrSymbol, request_options: Zavudev::RequestOptions } diff --git a/rbi/zavudev/models/phone_number_status.rbi b/rbi/zavudev/models/phone_number_status.rbi index ec201e1..1a0a8fc 100644 --- a/rbi/zavudev/models/phone_number_status.rbi +++ b/rbi/zavudev/models/phone_number_status.rbi @@ -2,6 +2,10 @@ module Zavudev module Models + # Billing state of an owned number, separate from `regulatoryStatus`. `pending` is + # legacy and is not written to numbers today. The SDKs carry `active`, `suspended` + # and `pending` only; `releasing` and `released` are returned by the REST API + # until their next release. module PhoneNumberStatus extend Zavudev::Internal::Type::Enum @@ -11,6 +15,8 @@ module Zavudev ACTIVE = T.let(:active, Zavudev::PhoneNumberStatus::TaggedSymbol) SUSPENDED = T.let(:suspended, Zavudev::PhoneNumberStatus::TaggedSymbol) PENDING = T.let(:pending, Zavudev::PhoneNumberStatus::TaggedSymbol) + RELEASING = T.let(:releasing, Zavudev::PhoneNumberStatus::TaggedSymbol) + RELEASED = T.let(:released, Zavudev::PhoneNumberStatus::TaggedSymbol) sig do override.returns(T::Array[Zavudev::PhoneNumberStatus::TaggedSymbol]) diff --git a/rbi/zavudev/models/phone_number_update_params.rbi b/rbi/zavudev/models/phone_number_update_params.rbi index d5c1ff7..6fde265 100644 --- a/rbi/zavudev/models/phone_number_update_params.rbi +++ b/rbi/zavudev/models/phone_number_update_params.rbi @@ -18,7 +18,9 @@ module Zavudev sig { returns(T.nilable(String)) } attr_accessor :name - # Sender ID to assign the phone number to. Set to null to unassign. + # Sender ID to assign the phone number to. Set to null to unassign. A number under + # regulatory review is recorded now and connected to the sender when approved; a + # rejected number is refused. sig { returns(T.nilable(String)) } attr_accessor :sender_id @@ -34,7 +36,9 @@ module Zavudev phone_number_id:, # Custom name for the phone number. Set to null to clear. name: nil, - # Sender ID to assign the phone number to. Set to null to unassign. + # Sender ID to assign the phone number to. Set to null to unassign. A number under + # regulatory review is recorded now and connected to the sender when approved; a + # rejected number is refused. sender_id: nil, request_options: {} ) diff --git a/rbi/zavudev/models/requirement.rbi b/rbi/zavudev/models/requirement.rbi index e215a56..2394311 100644 --- a/rbi/zavudev/models/requirement.rbi +++ b/rbi/zavudev/models/requirement.rbi @@ -21,7 +21,9 @@ module Zavudev sig { returns(T::Array[Zavudev::RequirementType]) } attr_accessor :requirement_types - # A group of requirements for a specific country/phone type combination. + # The requirements for ordering a number: for a country and number type, or for + # one specific number when requested with `phoneNumber` (then `id` is that phone + # number and `countryCode` is taken from it). sig do params( id: String, diff --git a/rbi/zavudev/models/requirement_type.rbi b/rbi/zavudev/models/requirement_type.rbi index 6ed0cb8..a524c1d 100644 --- a/rbi/zavudev/models/requirement_type.rbi +++ b/rbi/zavudev/models/requirement_type.rbi @@ -8,6 +8,7 @@ module Zavudev T.any(Zavudev::RequirementType, Zavudev::Internal::AnyHash) end + # Send this as `requirementType` in `regulatoryRequirements` when purchasing. sig { returns(String) } attr_accessor :id @@ -47,6 +48,7 @@ module Zavudev ).returns(T.attached_class) end def self.new( + # Send this as `requirementType` in `regulatoryRequirements` when purchasing. id:, description:, name:, diff --git a/rbi/zavudev/models/sender_create_params.rbi b/rbi/zavudev/models/sender_create_params.rbi index e62991f..b688bbe 100644 --- a/rbi/zavudev/models/sender_create_params.rbi +++ b/rbi/zavudev/models/sender_create_params.rbi @@ -68,8 +68,10 @@ module Zavudev # Phone number in E.164 format, and it must be a number your project already owns # (see `GET /v1/phone-numbers`). The number is routed to the sender as part of # this call, which is what turns the SMS channel on. Passing a number the project - # does not own, or one already attached to another sender, returns 400 rather than - # creating a sender that cannot send. Omit for an email-only sender. + # does not own, one already attached to another sender, or one rejected in + # regulatory review returns 400 rather than creating a sender that cannot send. A + # number still under review is attached and starts carrying messages when it is + # approved. Omit for an email-only sender. sig { returns(T.nilable(String)) } attr_reader :phone_number @@ -171,8 +173,10 @@ module Zavudev # Phone number in E.164 format, and it must be a number your project already owns # (see `GET /v1/phone-numbers`). The number is routed to the sender as part of # this call, which is what turns the SMS channel on. Passing a number the project - # does not own, or one already attached to another sender, returns 400 rather than - # creating a sender that cannot send. Omit for an email-only sender. + # does not own, one already attached to another sender, or one rejected in + # regulatory review returns 400 rather than creating a sender that cannot send. A + # number still under review is attached and starts carrying messages when it is + # approved. Omit for an email-only sender. phone_number: nil, set_as_default: nil, # Events to subscribe to. diff --git a/rbi/zavudev/resources/addresses.rbi b/rbi/zavudev/resources/addresses.rbi index 07c7b3b..c03d54e 100644 --- a/rbi/zavudev/resources/addresses.rbi +++ b/rbi/zavudev/resources/addresses.rbi @@ -3,32 +3,37 @@ module Zavudev module Resources class Addresses - # Create a regulatory address for phone number purchases. Some countries require a - # verified address before phone numbers can be activated. + # Create a regulatory address, to use as the value of an `address` requirement + # when buying a phone number. It is registered for review when it is created, with + # status `pending`. sig do params( country_code: String, + first_name: String, + last_name: String, locality: String, postal_code: String, street_address: String, administrative_area: String, business_name: String, extended_address: String, - first_name: String, - last_name: String, request_options: Zavudev::RequestOptions::OrHash ).returns(Zavudev::Models::AddressCreateResponse) end def create( country_code:, + # First name of the person the address is registered to. + first_name:, + # Last name of the person the address is registered to. + last_name:, locality:, postal_code:, street_address:, administrative_area: nil, + # Business name, when the address belongs to a business. Defaults to the person's + # full name. business_name: nil, extended_address: nil, - first_name: nil, - last_name: nil, request_options: {} ) end @@ -54,7 +59,9 @@ module Zavudev def list(cursor: nil, limit: nil, request_options: {}) end - # Delete a regulatory address. Cannot delete addresses that are in use. + # Delete a regulatory address from this project. Any address can be deleted, + # whatever its status. Phone numbers already purchased with it are not affected, + # and neither is information already submitted for later purchases in its country. sig do params( address_id: String, diff --git a/rbi/zavudev/resources/phone_numbers.rbi b/rbi/zavudev/resources/phone_numbers.rbi index 122711c..b3ccf17 100644 --- a/rbi/zavudev/resources/phone_numbers.rbi +++ b/rbi/zavudev/resources/phone_numbers.rbi @@ -26,7 +26,9 @@ module Zavudev phone_number_id, # Custom name for the phone number. Set to null to clear. name: nil, - # Sender ID to assign the phone number to. Set to null to unassign. + # Sender ID to assign the phone number to. Set to null to unassign. A number under + # regulatory review is recorded now and connected to the sender when approved; a + # rejected number is refused. sender_id: nil, request_options: {} ) @@ -52,15 +54,51 @@ module Zavudev end # Purchase an available phone number. Requires a paid plan: the Free plan cannot - # purchase phone numbers and receives `402` with code `paid_plan_required`. Paid - # 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. + # purchase phone numbers and receives `402` with code `paid_plan_required`. + # + # **The included number.** A paid plan includes one number at no charge, once per + # account: it must be a US or Canadian number (a +1 number) costing $20 a month or + # less. `isFreeEligible` in `GET /v1/phone-numbers/available` marks the numbers + # that qualify. Claiming it spends the benefit for good, across every team the + # account owner owns, so releasing that number does not make another one free. + # + # **Numbers with regulatory requirements.** Which numbers need regulatory + # information is decided per number, not by a fixed country list. The purchase + # looks the requirements up for the exact number before charging anything: + # + # 1. `GET /v1/phone-numbers/requirements?phoneNumber=...`. If `items` is empty, + # buy normally. + # 2. Create what it asks for: addresses with `POST /v1/addresses`, documents with + # `POST /v1/documents`. + # 3. Purchase with `type` and `regulatoryRequirements`. The number is bought and + # billed at once with `regulatoryStatus: pending_review`. + # 4. Poll `GET /v1/phone-numbers/{phoneNumberId}` until `regulatoryStatus` is + # `approved`. Assign it to a sender before or after approval; it starts + # carrying messages once approved. + # + # **Reuse.** Information you submitted is kept for your project, per country and + # `type`, and a later purchase there may omit `regulatoryRequirements`. Reuse only + # happens when what is kept still covers every requirement of the new number and + # every address and document in it belongs to the project. Otherwise, or when + # nothing is kept, the purchase returns `400 regulatory_compliance_required` with + # the missing requirements in `details`. + # + # Invalid values (a missing, unknown or repeated requirement id, an address or + # document from another project, or one rejected in review) return + # `400 invalid_request`. If an address or document cannot be registered for + # review, the purchase returns `400 invalid_request` naming the requirement. If + # the requirements cannot be looked up, the purchase returns + # `502 requirements_unavailable`, except for US and Canadian numbers, which are + # sold as numbers without requirements. None of these errors charge anything. sig do params( phone_number: String, name: String, + regulatory_requirements: + T::Array[ + Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement::OrHash + ], + type: Zavudev::PhoneNumberType::OrSymbol, request_options: Zavudev::RequestOptions::OrHash ).returns(Zavudev::Models::PhoneNumberPurchaseResponse) end @@ -69,6 +107,21 @@ module Zavudev phone_number:, # Optional custom name for the phone number. name: nil, + # Regulatory information, for numbers whose requirements list is not empty. Get + # the list with `GET /v1/phone-numbers/requirements?phoneNumber=...` and send one + # entry per requirement id, except `action` requirements, which take no value. + # Every required id must be present, once, and no unknown id may be sent; + # otherwise the purchase is refused with `400 invalid_request` before anything is + # charged. + # + # The information is kept for your project under the number's country and `type`. + # A later purchase there may omit this field if what is kept still covers that + # number's requirements. Omit it for numbers without requirements. + regulatory_requirements: nil, + # Type of phone number. `mobile` is stocked in countries where no geographic + # (`local`) or non-geographic (`national`) inventory exists, and in several + # markets it is the only type that can receive SMS. + type: nil, request_options: {} ) end @@ -83,20 +136,38 @@ module Zavudev def release(phone_number_id, request_options: {}) end - # Get regulatory requirements for purchasing phone numbers in a specific country. - # Some countries require additional documentation (addresses, identity documents) - # before phone numbers can be activated. + # Get the regulatory information needed to buy a phone number, for one specific + # number or for a country and number type. Prefer `phoneNumber`: the response is + # then exactly the list the purchase of that number validates against. Pass each + # `requirementTypes[].id` back as `requirementType` in `regulatoryRequirements` on + # `POST /v1/phone-numbers`. + # + # For `phoneNumber`, the requirements of that exact number are returned. When they + # cannot be resolved for the number itself, the list for its country and `type` is + # returned instead, and the purchase uses the same list. An empty `items` array + # means the number needs no regulatory information. If the requirements cannot be + # retrieved at all, the response is `502 requirements_unavailable`, never an empty + # list. + # + # URL-encode the `+` of `phoneNumber` as `%2B`. An unencoded `+` is also accepted. sig do params( country_code: String, + phone_number: String, type: Zavudev::PhoneNumberType::OrSymbol, request_options: Zavudev::RequestOptions::OrHash ).returns(Zavudev::Models::PhoneNumberRequirementsResponse) end def requirements( - # Two-letter ISO country code. - country_code:, - # Type of phone number (local, mobile, tollFree). + # Two-letter ISO country code. Required unless `phoneNumber` is given. + country_code: nil, + # E.164 number from `GET /v1/phone-numbers/available`, with `+` encoded as `%2B`. + # Returns the requirements the purchase of that number checks. Takes precedence + # over `countryCode`. + phone_number: nil, + # Type of phone number (local, national, mobile, tollFree). Defaults to `local`. + # With `phoneNumber`, used only when the number's own requirements cannot be + # resolved and the country list is returned. type: nil, request_options: {} ) diff --git a/rbi/zavudev/resources/senders.rbi b/rbi/zavudev/resources/senders.rbi index d295d7c..0e3fa1f 100644 --- a/rbi/zavudev/resources/senders.rbi +++ b/rbi/zavudev/resources/senders.rbi @@ -57,8 +57,10 @@ module Zavudev # Phone number in E.164 format, and it must be a number your project already owns # (see `GET /v1/phone-numbers`). The number is routed to the sender as part of # this call, which is what turns the SMS channel on. Passing a number the project - # does not own, or one already attached to another sender, returns 400 rather than - # creating a sender that cannot send. Omit for an email-only sender. + # does not own, one already attached to another sender, or one rejected in + # regulatory review returns 400 rather than creating a sender that cannot send. A + # number still under review is attached and starts carrying messages when it is + # approved. Omit for an email-only sender. phone_number: nil, set_as_default: nil, # Events to subscribe to. diff --git a/sig/zavudev/models/address_create_params.rbs b/sig/zavudev/models/address_create_params.rbs index 2372ef3..cf32388 100644 --- a/sig/zavudev/models/address_create_params.rbs +++ b/sig/zavudev/models/address_create_params.rbs @@ -3,14 +3,14 @@ module Zavudev type address_create_params = { country_code: String, + first_name: String, + last_name: String, locality: String, postal_code: String, street_address: String, administrative_area: String, business_name: String, - extended_address: String, - first_name: String, - last_name: String + extended_address: String } & Zavudev::Internal::Type::request_parameters @@ -20,6 +20,10 @@ module Zavudev attr_accessor country_code: String + attr_accessor first_name: String + + attr_accessor last_name: String + attr_accessor locality: String attr_accessor postal_code: String @@ -38,37 +42,29 @@ module Zavudev def extended_address=: (String) -> String - attr_reader first_name: String? - - def first_name=: (String) -> String - - attr_reader last_name: String? - - def last_name=: (String) -> String - def initialize: ( country_code: String, + first_name: String, + last_name: String, locality: String, postal_code: String, street_address: String, ?administrative_area: String, ?business_name: String, ?extended_address: String, - ?first_name: String, - ?last_name: String, ?request_options: Zavudev::request_opts ) -> void def to_hash: -> { country_code: String, + first_name: String, + last_name: String, locality: String, postal_code: String, street_address: String, administrative_area: String, business_name: String, extended_address: String, - first_name: String, - last_name: String, request_options: Zavudev::RequestOptions } end diff --git a/sig/zavudev/models/owned_phone_number.rbs b/sig/zavudev/models/owned_phone_number.rbs index 447b94b..f18b4b1 100644 --- a/sig/zavudev/models/owned_phone_number.rbs +++ b/sig/zavudev/models/owned_phone_number.rbs @@ -7,6 +7,7 @@ module Zavudev created_at: Time, phone_number: String, pricing: Zavudev::OwnedPhoneNumberPricing, + regulatory_status: Zavudev::Models::OwnedPhoneNumber::regulatory_status, status: Zavudev::Models::phone_number_status, name: String, next_renewal_date: Time, @@ -25,6 +26,8 @@ module Zavudev attr_accessor pricing: Zavudev::OwnedPhoneNumberPricing + attr_accessor regulatory_status: Zavudev::Models::OwnedPhoneNumber::regulatory_status + attr_accessor status: Zavudev::Models::phone_number_status attr_reader name: String? @@ -49,6 +52,7 @@ module Zavudev created_at: Time, phone_number: String, pricing: Zavudev::OwnedPhoneNumberPricing, + regulatory_status: Zavudev::Models::OwnedPhoneNumber::regulatory_status, status: Zavudev::Models::phone_number_status, ?name: String, ?next_renewal_date: Time, @@ -62,12 +66,25 @@ module Zavudev created_at: Time, phone_number: String, pricing: Zavudev::OwnedPhoneNumberPricing, + regulatory_status: Zavudev::Models::OwnedPhoneNumber::regulatory_status, status: Zavudev::Models::phone_number_status, name: String, next_renewal_date: Time, sender_id: String, updated_at: Time } + + type regulatory_status = :approved | :pending_review | :rejected + + module RegulatoryStatus + extend Zavudev::Internal::Type::Enum + + APPROVED: :approved + PENDING_REVIEW: :pending_review + REJECTED: :rejected + + def self?.values: -> ::Array[Zavudev::Models::OwnedPhoneNumber::regulatory_status] + end end end end diff --git a/sig/zavudev/models/phone_number_purchase_params.rbs b/sig/zavudev/models/phone_number_purchase_params.rbs index 70a9520..c7a7a2d 100644 --- a/sig/zavudev/models/phone_number_purchase_params.rbs +++ b/sig/zavudev/models/phone_number_purchase_params.rbs @@ -1,7 +1,12 @@ module Zavudev module Models type phone_number_purchase_params = - { phone_number: String, name: String } + { + phone_number: String, + name: String, + regulatory_requirements: ::Array[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement], + type: Zavudev::Models::phone_number_type + } & Zavudev::Internal::Type::request_parameters class PhoneNumberPurchaseParams < Zavudev::Internal::Type::BaseModel @@ -14,17 +19,46 @@ module Zavudev def name=: (String) -> String + attr_reader regulatory_requirements: ::Array[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement]? + + def regulatory_requirements=: ( + ::Array[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement] + ) -> ::Array[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement] + + attr_reader type: Zavudev::Models::phone_number_type? + + def type=: ( + Zavudev::Models::phone_number_type + ) -> Zavudev::Models::phone_number_type + def initialize: ( phone_number: String, ?name: String, + ?regulatory_requirements: ::Array[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement], + ?type: Zavudev::Models::phone_number_type, ?request_options: Zavudev::request_opts ) -> void def to_hash: -> { phone_number: String, name: String, + regulatory_requirements: ::Array[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement], + type: Zavudev::Models::phone_number_type, request_options: Zavudev::RequestOptions } + + type regulatory_requirement = + { field_value: String, requirement_type: String } + + class RegulatoryRequirement < Zavudev::Internal::Type::BaseModel + attr_accessor field_value: String + + attr_accessor requirement_type: String + + def initialize: (field_value: String, requirement_type: String) -> void + + def to_hash: -> { field_value: String, requirement_type: String } + end end end end diff --git a/sig/zavudev/models/phone_number_requirements_params.rbs b/sig/zavudev/models/phone_number_requirements_params.rbs index 8eddabf..d98d4c5 100644 --- a/sig/zavudev/models/phone_number_requirements_params.rbs +++ b/sig/zavudev/models/phone_number_requirements_params.rbs @@ -1,14 +1,24 @@ module Zavudev module Models type phone_number_requirements_params = - { country_code: String, type: Zavudev::Models::phone_number_type } + { + country_code: String, + phone_number: String, + type: Zavudev::Models::phone_number_type + } & Zavudev::Internal::Type::request_parameters class PhoneNumberRequirementsParams < Zavudev::Internal::Type::BaseModel extend Zavudev::Internal::Type::RequestParameters::Converter include Zavudev::Internal::Type::RequestParameters - attr_accessor country_code: String + attr_reader country_code: String? + + def country_code=: (String) -> String + + attr_reader phone_number: String? + + def phone_number=: (String) -> String attr_reader type: Zavudev::Models::phone_number_type? @@ -17,13 +27,15 @@ module Zavudev ) -> Zavudev::Models::phone_number_type def initialize: ( - country_code: String, + ?country_code: String, + ?phone_number: String, ?type: Zavudev::Models::phone_number_type, ?request_options: Zavudev::request_opts ) -> void def to_hash: -> { country_code: String, + phone_number: String, type: Zavudev::Models::phone_number_type, request_options: Zavudev::RequestOptions } diff --git a/sig/zavudev/models/phone_number_status.rbs b/sig/zavudev/models/phone_number_status.rbs index cc12f1c..3cebf5f 100644 --- a/sig/zavudev/models/phone_number_status.rbs +++ b/sig/zavudev/models/phone_number_status.rbs @@ -1,6 +1,7 @@ module Zavudev module Models - type phone_number_status = :active | :suspended | :pending + type phone_number_status = + :active | :suspended | :pending | :releasing | :released module PhoneNumberStatus extend Zavudev::Internal::Type::Enum @@ -8,6 +9,8 @@ module Zavudev ACTIVE: :active SUSPENDED: :suspended PENDING: :pending + RELEASING: :releasing + RELEASED: :released def self?.values: -> ::Array[Zavudev::Models::phone_number_status] end diff --git a/sig/zavudev/resources/addresses.rbs b/sig/zavudev/resources/addresses.rbs index 92088e6..b39896c 100644 --- a/sig/zavudev/resources/addresses.rbs +++ b/sig/zavudev/resources/addresses.rbs @@ -3,14 +3,14 @@ module Zavudev class Addresses def create: ( country_code: String, + first_name: String, + last_name: String, locality: String, postal_code: String, street_address: String, ?administrative_area: String, ?business_name: String, ?extended_address: String, - ?first_name: String, - ?last_name: String, ?request_options: Zavudev::request_opts ) -> Zavudev::Models::AddressCreateResponse diff --git a/sig/zavudev/resources/phone_numbers.rbs b/sig/zavudev/resources/phone_numbers.rbs index af6e66b..3c1847b 100644 --- a/sig/zavudev/resources/phone_numbers.rbs +++ b/sig/zavudev/resources/phone_numbers.rbs @@ -23,6 +23,8 @@ module Zavudev def purchase: ( phone_number: String, ?name: String, + ?regulatory_requirements: ::Array[Zavudev::PhoneNumberPurchaseParams::RegulatoryRequirement], + ?type: Zavudev::Models::phone_number_type, ?request_options: Zavudev::request_opts ) -> Zavudev::Models::PhoneNumberPurchaseResponse @@ -32,7 +34,8 @@ module Zavudev ) -> nil def requirements: ( - country_code: String, + ?country_code: String, + ?phone_number: String, ?type: Zavudev::Models::phone_number_type, ?request_options: Zavudev::request_opts ) -> Zavudev::Models::PhoneNumberRequirementsResponse diff --git a/test/zavudev/resources/addresses_test.rb b/test/zavudev/resources/addresses_test.rb index 5e4caea..79d241f 100644 --- a/test/zavudev/resources/addresses_test.rb +++ b/test/zavudev/resources/addresses_test.rb @@ -9,6 +9,8 @@ def test_create_required_params response = @zavudev.addresses.create( country_code: "DE", + first_name: "John", + last_name: "Doe", locality: "Berlin", postal_code: "10115", street_address: "123 Main St" diff --git a/test/zavudev/resources/phone_numbers_test.rb b/test/zavudev/resources/phone_numbers_test.rb index eb71712..bdf11fe 100644 --- a/test/zavudev/resources/phone_numbers_test.rb +++ b/test/zavudev/resources/phone_numbers_test.rb @@ -58,6 +58,7 @@ def test_list created_at: Time, phone_number: String, pricing: Zavudev::OwnedPhoneNumberPricing, + regulatory_status: Zavudev::OwnedPhoneNumber::RegulatoryStatus, status: Zavudev::PhoneNumberStatus, name: String | nil, next_renewal_date: Time | nil, @@ -93,10 +94,10 @@ def test_release end end - def test_requirements_required_params + def test_requirements skip("Mock server tests are disabled") - response = @zavudev.phone_numbers.requirements(country_code: "xx") + response = @zavudev.phone_numbers.requirements assert_pattern do response => Zavudev::Models::PhoneNumberRequirementsResponse From 6d0edebcd2babc98f3862ce247ada7326b30fa41 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:48:00 +0000 Subject: [PATCH 03/15] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index bbcd963..52045d0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-6a4c9cdb8d971155ac22e679a37d27525a97b8a65991908a1c12969252457811.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-c6e6f410e3490fde4e2a5aaca9a4b609856685cfc4b8540c6cef5c2cef097622.yml openapi_spec_hash: 5d999f34f096d592deaa6319f52190b3 -config_hash: 261e1b852ca7f8364e4a283a3edd350a +config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 From 222b7c07d19c7fbabd8c1a508890f19e533e6c99 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 09:10:52 +0000 Subject: [PATCH 04/15] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 52045d0..bbcd963 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-c6e6f410e3490fde4e2a5aaca9a4b609856685cfc4b8540c6cef5c2cef097622.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-6a4c9cdb8d971155ac22e679a37d27525a97b8a65991908a1c12969252457811.yml openapi_spec_hash: 5d999f34f096d592deaa6319f52190b3 -config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 +config_hash: 261e1b852ca7f8364e4a283a3edd350a From 3f5328113fa931346f79b6c8837bda674f924784 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 09:12:57 +0000 Subject: [PATCH 05/15] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index bbcd963..52045d0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-6a4c9cdb8d971155ac22e679a37d27525a97b8a65991908a1c12969252457811.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-c6e6f410e3490fde4e2a5aaca9a4b609856685cfc4b8540c6cef5c2cef097622.yml openapi_spec_hash: 5d999f34f096d592deaa6319f52190b3 -config_hash: 261e1b852ca7f8364e4a283a3edd350a +config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 From 5ef766844c3078b7c3a6930250ba4eb658028b26 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 14:22:09 +0000 Subject: [PATCH 06/15] feat(api): api update --- .stats.yml | 4 ++-- lib/zavudev/models/webhook_event.rb | 20 +++++++++++++------- rbi/zavudev/models/webhook_event.rbi | 20 +++++++++++++------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/.stats.yml b/.stats.yml index 52045d0..0384ef5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-c6e6f410e3490fde4e2a5aaca9a4b609856685cfc4b8540c6cef5c2cef097622.yml -openapi_spec_hash: 5d999f34f096d592deaa6319f52190b3 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-cf8de13bbc99e47431a3597b98b7a1d7c5161639ed7513c2b7db3e5ea49c4a20.yml +openapi_spec_hash: 4b35637b2a0e3645fc53e8ccc0b1f2de config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/models/webhook_event.rb b/lib/zavudev/models/webhook_event.rb index 6535ee3..b8f1e84 100644 --- a/lib/zavudev/models/webhook_event.rb +++ b/lib/zavudev/models/webhook_event.rb @@ -55,13 +55,19 @@ module Models # # **Partner events:** # - # - `invitation.status_changed`: A partner invitation status changed (pending, - # in_progress, completed, cancelled, failed). `data` carries `invitationId`, - # `clientName`, `clientEmail`, `connectionType` (`whatsapp_waba` or - # `messenger`), `previousStatus`, and `currentStatus`. On `completed` it also - # carries `senderId` and `connectedAccount` (`channel`, `id`, `name`) — the - # WhatsApp number or Facebook Page that was linked. On `failed` it carries - # `failureReason`; the invitation link stays usable, so a client can retry it. + # - `invitation.status_changed`: A partner invitation's stored status changed: to + # `in_progress`, `completed`, `failed`, `cancelled`, or back to `pending` when + # it is resent from the dashboard. A change to the same status sends nothing, + # and expiry is not a stored change, so no event is sent when an invitation + # expires. Delivered to the project webhook (`POST /v1/invitations/webhook`) of + # the project that created the invitation; a parent project does not receive its + # sub-accounts' events. `data` carries `invitationId`, `clientName`, + # `clientEmail`, `connectionType` (`whatsapp_waba` or `messenger`), + # `previousStatus`, and `currentStatus`. On `completed` it also carries + # `senderId`, `connectedAccount` (`channel`, `id`, `name`) — the WhatsApp number + # or Facebook Page that was linked — and, for WhatsApp, `wabaAccountId`. On + # `failed` it carries `failureReason`; the invitation link stays usable, so a + # client can retry it. # # **Voice Agent events:** For every voice event, `data` carries `callId`, # `direction`, `from`, `to`, `status`, `durationSeconds`, `endReason`, and diff --git a/rbi/zavudev/models/webhook_event.rbi b/rbi/zavudev/models/webhook_event.rbi index d558e4d..df16359 100644 --- a/rbi/zavudev/models/webhook_event.rbi +++ b/rbi/zavudev/models/webhook_event.rbi @@ -55,13 +55,19 @@ module Zavudev # # **Partner events:** # - # - `invitation.status_changed`: A partner invitation status changed (pending, - # in_progress, completed, cancelled, failed). `data` carries `invitationId`, - # `clientName`, `clientEmail`, `connectionType` (`whatsapp_waba` or - # `messenger`), `previousStatus`, and `currentStatus`. On `completed` it also - # carries `senderId` and `connectedAccount` (`channel`, `id`, `name`) — the - # WhatsApp number or Facebook Page that was linked. On `failed` it carries - # `failureReason`; the invitation link stays usable, so a client can retry it. + # - `invitation.status_changed`: A partner invitation's stored status changed: to + # `in_progress`, `completed`, `failed`, `cancelled`, or back to `pending` when + # it is resent from the dashboard. A change to the same status sends nothing, + # and expiry is not a stored change, so no event is sent when an invitation + # expires. Delivered to the project webhook (`POST /v1/invitations/webhook`) of + # the project that created the invitation; a parent project does not receive its + # sub-accounts' events. `data` carries `invitationId`, `clientName`, + # `clientEmail`, `connectionType` (`whatsapp_waba` or `messenger`), + # `previousStatus`, and `currentStatus`. On `completed` it also carries + # `senderId`, `connectedAccount` (`channel`, `id`, `name`) — the WhatsApp number + # or Facebook Page that was linked — and, for WhatsApp, `wabaAccountId`. On + # `failed` it carries `failureReason`; the invitation link stays usable, so a + # client can retry it. # # **Voice Agent events:** For every voice event, `data` carries `callId`, # `direction`, `from`, `to`, `status`, `durationSeconds`, `endReason`, and From bf90d0e31f2c22ed1df4ab7ca80b419d149ac6cb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:58:28 +0000 Subject: [PATCH 07/15] feat(api): api update --- .stats.yml | 4 ++-- lib/zavudev/models/sender.rb | 12 +++++++----- rbi/zavudev/models/sender.rbi | 20 ++++++++++++-------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0384ef5..63896e7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-cf8de13bbc99e47431a3597b98b7a1d7c5161639ed7513c2b7db3e5ea49c4a20.yml -openapi_spec_hash: 4b35637b2a0e3645fc53e8ccc0b1f2de +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-212cea44bc8db5e2be7beb83eafaba27da3ad2d2989b630e05a472e7c91dd6fc.yml +openapi_spec_hash: 965feddccfebb0fc94a7682954a950f0 config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/models/sender.rb b/lib/zavudev/models/sender.rb index b9a840a..d6f5ea6 100644 --- a/lib/zavudev/models/sender.rb +++ b/lib/zavudev/models/sender.rb @@ -21,10 +21,12 @@ class Sender < Zavudev::Internal::Type::BaseModel required :phone_number, String, api_name: :phoneNumber # @!attribute channels - # Channels this sender can actually send on right now, computed from its - # configuration. Empty means the sender cannot send or receive anything yet: a - # phoneNumber alone does not enable SMS or voice. Check this rather than inferring - # capability from phoneNumber or emailAddress. + # Channels this sender can actually send on right now: configured AND activated. + # Empty means the sender cannot send or receive anything yet: a phoneNumber alone + # does not enable SMS or voice, and a connected account that is not activated is + # left out, because every send on it is refused. Check this rather than inferring + # capability from phoneNumber or emailAddress, and turn a connected channel on + # with `POST /v1/senders/{senderId}/channels/{channel}/activate`. # # @return [Array, nil] optional :channels, Zavudev::Internal::Type::ArrayOf[String] @@ -88,7 +90,7 @@ class Sender < Zavudev::Internal::Type::BaseModel # # @param phone_number [String] Phone number in E.164 format. # - # @param channels [Array] Channels this sender can actually send on right now, computed from its configura + # @param channels [Array] Channels this sender can actually send on right now: configured AND activated. E # # @param created_at [Time] # diff --git a/rbi/zavudev/models/sender.rbi b/rbi/zavudev/models/sender.rbi index e1a4b82..bf068a9 100644 --- a/rbi/zavudev/models/sender.rbi +++ b/rbi/zavudev/models/sender.rbi @@ -16,10 +16,12 @@ module Zavudev sig { returns(String) } attr_accessor :phone_number - # Channels this sender can actually send on right now, computed from its - # configuration. Empty means the sender cannot send or receive anything yet: a - # phoneNumber alone does not enable SMS or voice. Check this rather than inferring - # capability from phoneNumber or emailAddress. + # Channels this sender can actually send on right now: configured AND activated. + # Empty means the sender cannot send or receive anything yet: a phoneNumber alone + # does not enable SMS or voice, and a connected account that is not activated is + # left out, because every send on it is refused. Check this rather than inferring + # capability from phoneNumber or emailAddress, and turn a connected channel on + # with `POST /v1/senders/{senderId}/channels/{channel}/activate`. sig { returns(T.nilable(T::Array[String])) } attr_reader :channels @@ -104,10 +106,12 @@ module Zavudev name:, # Phone number in E.164 format. phone_number:, - # Channels this sender can actually send on right now, computed from its - # configuration. Empty means the sender cannot send or receive anything yet: a - # phoneNumber alone does not enable SMS or voice. Check this rather than inferring - # capability from phoneNumber or emailAddress. + # Channels this sender can actually send on right now: configured AND activated. + # Empty means the sender cannot send or receive anything yet: a phoneNumber alone + # does not enable SMS or voice, and a connected account that is not activated is + # left out, because every send on it is refused. Check this rather than inferring + # capability from phoneNumber or emailAddress, and turn a connected channel on + # with `POST /v1/senders/{senderId}/channels/{channel}/activate`. channels: nil, created_at: nil, # From-address for the email channel, if configured. From b2e8ee71812c37479e29b8fa84b792a7424cfa2d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:13:46 +0000 Subject: [PATCH 08/15] feat(api): api update --- .stats.yml | 4 ++-- lib/zavudev/models/sender_create_params.rb | 8 +++++--- lib/zavudev/models/sender_update_params.rb | 7 +++++-- lib/zavudev/resources/senders.rb | 4 ++-- rbi/zavudev/models/sender_create_params.rbi | 12 ++++++++---- rbi/zavudev/models/sender_update_params.rbi | 10 ++++++++-- rbi/zavudev/resources/senders.rbi | 11 ++++++++--- 7 files changed, 38 insertions(+), 18 deletions(-) diff --git a/.stats.yml b/.stats.yml index 63896e7..1a193ff 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-212cea44bc8db5e2be7beb83eafaba27da3ad2d2989b630e05a472e7c91dd6fc.yml -openapi_spec_hash: 965feddccfebb0fc94a7682954a950f0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5257b1c4e3b9259ae2df6cd9ac2d2a31a39ddb336f50baf0dadbcc865c1913f3.yml +openapi_spec_hash: 4103decc37e4053c1cba58ba9527a5e7 config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/models/sender_create_params.rb b/lib/zavudev/models/sender_create_params.rb index 539285d..6958e5c 100644 --- a/lib/zavudev/models/sender_create_params.rb +++ b/lib/zavudev/models/sender_create_params.rb @@ -34,8 +34,10 @@ class SenderCreateParams < Zavudev::Internal::Type::BaseModel optional :email_from_name, String, api_name: :emailFromName # @!attribute email_receiving_enabled - # Enable inbound email receiving on this sender. Requires a verified MX record on - # the domain; ignored otherwise. + # Enable inbound email receiving on this sender. Requires a verified inbound MX + # record on the domain; the request is ignored otherwise. Read + # `emailReceivingEnabled` back off the response to see whether it was applied — it + # comes back `false` when the MX has not verified. # # @return [Boolean, nil] optional :email_receiving_enabled, Zavudev::Internal::Type::Boolean, api_name: :emailReceivingEnabled @@ -119,7 +121,7 @@ class SenderCreateParams < Zavudev::Internal::Type::BaseModel # # @param email_from_name [String] Display name shown in the recipient's inbox for the email channel. # - # @param email_receiving_enabled [Boolean] Enable inbound email receiving on this sender. Requires a verified MX record on + # @param email_receiving_enabled [Boolean] Enable inbound email receiving on this sender. Requires a verified inbound MX re # # @param enable_sms_oneway [Boolean] Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone num # diff --git a/lib/zavudev/models/sender_update_params.rb b/lib/zavudev/models/sender_update_params.rb index c960083..8031718 100644 --- a/lib/zavudev/models/sender_update_params.rb +++ b/lib/zavudev/models/sender_update_params.rb @@ -41,7 +41,10 @@ class SenderUpdateParams < Zavudev::Internal::Type::BaseModel optional :email_from_name, String, api_name: :emailFromName # @!attribute email_receiving_enabled - # Enable or disable inbound email receiving for this sender. + # Enable or disable inbound email receiving for this sender. Enabling requires a + # verified inbound MX record on the domain; the request is ignored otherwise, and + # `emailReceivingEnabled` comes back `false` on the response. Disabling always + # applies. # # @return [Boolean, nil] optional :email_receiving_enabled, Zavudev::Internal::Type::Boolean, api_name: :emailReceivingEnabled @@ -125,7 +128,7 @@ class SenderUpdateParams < Zavudev::Internal::Type::BaseModel # # @param email_from_name [String] Display name shown in the recipient's inbox for the email channel. # - # @param email_receiving_enabled [Boolean] Enable or disable inbound email receiving for this sender. + # @param email_receiving_enabled [Boolean] Enable or disable inbound email receiving for this sender. Enabling requires a v # # @param enable_sms_oneway [Boolean] Turn the one-way SMS channel on or off. Enabling needs nothing else and takes ef # diff --git a/lib/zavudev/resources/senders.rb b/lib/zavudev/resources/senders.rb index d01e7c7..6fcec44 100644 --- a/lib/zavudev/resources/senders.rb +++ b/lib/zavudev/resources/senders.rb @@ -27,7 +27,7 @@ class Senders # # @param email_from_name [String] Display name shown in the recipient's inbox for the email channel. # - # @param email_receiving_enabled [Boolean] Enable inbound email receiving on this sender. Requires a verified MX record on + # @param email_receiving_enabled [Boolean] Enable inbound email receiving on this sender. Requires a verified inbound MX re # # @param enable_sms_oneway [Boolean] Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone num # @@ -95,7 +95,7 @@ def retrieve(sender_id, params = {}) # # @param email_from_name [String] Display name shown in the recipient's inbox for the email channel. # - # @param email_receiving_enabled [Boolean] Enable or disable inbound email receiving for this sender. + # @param email_receiving_enabled [Boolean] Enable or disable inbound email receiving for this sender. Enabling requires a v # # @param enable_sms_oneway [Boolean] Turn the one-way SMS channel on or off. Enabling needs nothing else and takes ef # diff --git a/rbi/zavudev/models/sender_create_params.rbi b/rbi/zavudev/models/sender_create_params.rbi index b688bbe..bb7cd9c 100644 --- a/rbi/zavudev/models/sender_create_params.rbi +++ b/rbi/zavudev/models/sender_create_params.rbi @@ -38,8 +38,10 @@ module Zavudev sig { params(email_from_name: String).void } attr_writer :email_from_name - # Enable inbound email receiving on this sender. Requires a verified MX record on - # the domain; ignored otherwise. + # Enable inbound email receiving on this sender. Requires a verified inbound MX + # record on the domain; the request is ignored otherwise. Read + # `emailReceivingEnabled` back off the response to see whether it was applied — it + # comes back `false` when the MX has not verified. sig { returns(T.nilable(T::Boolean)) } attr_reader :email_receiving_enabled @@ -158,8 +160,10 @@ module Zavudev email_domain_id: nil, # Display name shown in the recipient's inbox for the email channel. email_from_name: nil, - # Enable inbound email receiving on this sender. Requires a verified MX record on - # the domain; ignored otherwise. + # Enable inbound email receiving on this sender. Requires a verified inbound MX + # record on the domain; the request is ignored otherwise. Read + # `emailReceivingEnabled` back off the response to see whether it was applied — it + # comes back `false` when the MX has not verified. email_receiving_enabled: nil, # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. diff --git a/rbi/zavudev/models/sender_update_params.rbi b/rbi/zavudev/models/sender_update_params.rbi index c4d805c..48f24a6 100644 --- a/rbi/zavudev/models/sender_update_params.rbi +++ b/rbi/zavudev/models/sender_update_params.rbi @@ -46,7 +46,10 @@ module Zavudev sig { params(email_from_name: String).void } attr_writer :email_from_name - # Enable or disable inbound email receiving for this sender. + # Enable or disable inbound email receiving for this sender. Enabling requires a + # verified inbound MX record on the domain; the request is ignored otherwise, and + # `emailReceivingEnabled` comes back `false` on the response. Disabling always + # applies. sig { returns(T.nilable(T::Boolean)) } attr_reader :email_receiving_enabled @@ -166,7 +169,10 @@ module Zavudev email_domain_id: nil, # Display name shown in the recipient's inbox for the email channel. email_from_name: nil, - # Enable or disable inbound email receiving for this sender. + # Enable or disable inbound email receiving for this sender. Enabling requires a + # verified inbound MX record on the domain; the request is ignored otherwise, and + # `emailReceivingEnabled` comes back `false` on the response. Disabling always + # applies. email_receiving_enabled: nil, # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with diff --git a/rbi/zavudev/resources/senders.rbi b/rbi/zavudev/resources/senders.rbi index 0e3fa1f..f8768b0 100644 --- a/rbi/zavudev/resources/senders.rbi +++ b/rbi/zavudev/resources/senders.rbi @@ -42,8 +42,10 @@ module Zavudev email_domain_id: nil, # Display name shown in the recipient's inbox for the email channel. email_from_name: nil, - # Enable inbound email receiving on this sender. Requires a verified MX record on - # the domain; ignored otherwise. + # Enable inbound email receiving on this sender. Requires a verified inbound MX + # record on the domain; the request is ignored otherwise. Read + # `emailReceivingEnabled` back off the response to see whether it was applied — it + # comes back `false` when the MX has not verified. email_receiving_enabled: nil, # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. @@ -129,7 +131,10 @@ module Zavudev email_domain_id: nil, # Display name shown in the recipient's inbox for the email channel. email_from_name: nil, - # Enable or disable inbound email receiving for this sender. + # Enable or disable inbound email receiving for this sender. Enabling requires a + # verified inbound MX record on the domain; the request is ignored otherwise, and + # `emailReceivingEnabled` comes back `false` on the response. Disabling always + # applies. email_receiving_enabled: nil, # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with From 03fffc7f3f4f1ffa3ccefd6da911001c3b81e87d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Sep 2026 16:54:10 +0000 Subject: [PATCH 09/15] feat(api): api update --- .stats.yml | 4 ++-- lib/zavudev/models/template_sync_response.rb | 5 +++-- lib/zavudev/models/webhook_event.rb | 9 ++++++++- lib/zavudev/resources/templates.rb | 15 +++++++++++---- rbi/zavudev/models/template_sync_response.rbi | 6 ++++-- rbi/zavudev/models/webhook_event.rbi | 9 ++++++++- rbi/zavudev/resources/templates.rbi | 15 +++++++++++---- 7 files changed, 47 insertions(+), 16 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1a193ff..a3be4c8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5257b1c4e3b9259ae2df6cd9ac2d2a31a39ddb336f50baf0dadbcc865c1913f3.yml -openapi_spec_hash: 4103decc37e4053c1cba58ba9527a5e7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-7ddbc5b54ce2afee5a9c67569f652309a8f79e02643e74af3afba4cb7310f737.yml +openapi_spec_hash: de4fcf9ef758597cfcc59299d12d7b33 config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/models/template_sync_response.rb b/lib/zavudev/models/template_sync_response.rb index 042562a..705d3fe 100644 --- a/lib/zavudev/models/template_sync_response.rb +++ b/lib/zavudev/models/template_sync_response.rb @@ -38,7 +38,8 @@ class TemplateSyncResponse < Zavudev::Internal::Type::BaseModel required :skipped, Integer # @!attribute updated - # Templates whose approval status changed to match Meta. + # Templates brought back in line with Meta — approval status, category, or both. A + # template whose status and category both moved is counted once. # # @return [Integer] required :updated, Integer @@ -57,7 +58,7 @@ class TemplateSyncResponse < Zavudev::Internal::Type::BaseModel # # @param skipped [Integer] Meta templates left alone: already linked to a Zavu template, or rejected/disabl # - # @param updated [Integer] Templates whose approval status changed to match Meta. + # @param updated [Integer] Templates brought back in line with Meta — approval status, category, or both. A end end end diff --git a/lib/zavudev/models/webhook_event.rb b/lib/zavudev/models/webhook_event.rb index b8f1e84..dcb6ca4 100644 --- a/lib/zavudev/models/webhook_event.rb +++ b/lib/zavudev/models/webhook_event.rb @@ -51,7 +51,14 @@ module Models # `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`), the # `phoneNumber` or `email` key, `channel`, `firstMessageId`, `firstMessageText`, # and `profileName`. - # - `template.status_changed`: WhatsApp template approval status changed + # - `template.status_changed`: WhatsApp template approval status changed. `data` + # carries `templateId`, `name`, `previousStatus`, `currentStatus`, + # `rejectionReason`, and `category` — the category Meta currently bills the + # template under. Meta can recategorize a template (typically `UTILITY` to + # `MARKETING`) at approval or long afterwards, which changes what each message + # costs; `category` is how that reaches you. A recategorization with no status + # change is delivered as this same event, so compare `category` against what you + # hold rather than only reacting to `currentStatus`. # # **Partner events:** # diff --git a/lib/zavudev/resources/templates.rb b/lib/zavudev/resources/templates.rb index bf86322..235be56 100644 --- a/lib/zavudev/resources/templates.rb +++ b/lib/zavudev/resources/templates.rb @@ -145,11 +145,18 @@ def submit(template_id, params) # Some parameter documentations has been truncated, see # {Zavudev::Models::TemplateSyncParams} for more details. # - # Reconcile this project's templates against WhatsApp. Two things happen per + # Reconcile this project's templates against WhatsApp. Three things happen per # connected WhatsApp Business Account: templates that exist on Meta but not in - # Zavu are imported (or linked to an existing template with the same name), and - # the approval status of the templates Zavu already knows about is refreshed from - # Meta. + # Zavu are imported (or linked to an existing template with the same name), the + # approval status of the templates Zavu already knows about is refreshed from + # Meta, and their **category** is refreshed from Meta. + # + # The category matters because it is what each message is billed under, and Meta + # reassigns it on its own — commonly `UTILITY` to `MARKETING`, on a template that + # is already approved and whose status therefore never moves. A template whose + # category changed but whose status did not is still counted in `updated`. This is + # the way to repair templates whose category drifted before you started listening + # for `template.status_changed`. # # This is what to call when a template was created outside Zavu — in Meta Business # Manager, or by another tool — or when a `template.status_changed` webhook was diff --git a/rbi/zavudev/models/template_sync_response.rbi b/rbi/zavudev/models/template_sync_response.rbi index c9bb402..35dcaa0 100644 --- a/rbi/zavudev/models/template_sync_response.rbi +++ b/rbi/zavudev/models/template_sync_response.rbi @@ -34,7 +34,8 @@ module Zavudev sig { returns(Integer) } attr_accessor :skipped - # Templates whose approval status changed to match Meta. + # Templates brought back in line with Meta — approval status, category, or both. A + # template whose status and category both moved is counted once. sig { returns(Integer) } attr_accessor :updated @@ -62,7 +63,8 @@ module Zavudev # Meta templates left alone: already linked to a Zavu template, or # rejected/disabled on Meta. skipped:, - # Templates whose approval status changed to match Meta. + # Templates brought back in line with Meta — approval status, category, or both. A + # template whose status and category both moved is counted once. updated: ) end diff --git a/rbi/zavudev/models/webhook_event.rbi b/rbi/zavudev/models/webhook_event.rbi index df16359..e0d549c 100644 --- a/rbi/zavudev/models/webhook_event.rbi +++ b/rbi/zavudev/models/webhook_event.rbi @@ -51,7 +51,14 @@ module Zavudev # `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`), the # `phoneNumber` or `email` key, `channel`, `firstMessageId`, `firstMessageText`, # and `profileName`. - # - `template.status_changed`: WhatsApp template approval status changed + # - `template.status_changed`: WhatsApp template approval status changed. `data` + # carries `templateId`, `name`, `previousStatus`, `currentStatus`, + # `rejectionReason`, and `category` — the category Meta currently bills the + # template under. Meta can recategorize a template (typically `UTILITY` to + # `MARKETING`) at approval or long afterwards, which changes what each message + # costs; `category` is how that reaches you. A recategorization with no status + # change is delivered as this same event, so compare `category` against what you + # hold rather than only reacting to `currentStatus`. # # **Partner events:** # diff --git a/rbi/zavudev/resources/templates.rbi b/rbi/zavudev/resources/templates.rbi index ba48b7a..dac8f7f 100644 --- a/rbi/zavudev/resources/templates.rbi +++ b/rbi/zavudev/resources/templates.rbi @@ -106,11 +106,18 @@ module Zavudev ) end - # Reconcile this project's templates against WhatsApp. Two things happen per + # Reconcile this project's templates against WhatsApp. Three things happen per # connected WhatsApp Business Account: templates that exist on Meta but not in - # Zavu are imported (or linked to an existing template with the same name), and - # the approval status of the templates Zavu already knows about is refreshed from - # Meta. + # Zavu are imported (or linked to an existing template with the same name), the + # approval status of the templates Zavu already knows about is refreshed from + # Meta, and their **category** is refreshed from Meta. + # + # The category matters because it is what each message is billed under, and Meta + # reassigns it on its own — commonly `UTILITY` to `MARKETING`, on a template that + # is already approved and whose status therefore never moves. A template whose + # category changed but whose status did not is still counted in `updated`. This is + # the way to repair templates whose category drifted before you started listening + # for `template.status_changed`. # # This is what to call when a template was created outside Zavu — in Meta Business # Manager, or by another tool — or when a `template.status_changed` webhook was From 827bce1963625363e316b2d14e97895466eb0657 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Sep 2026 21:54:01 +0000 Subject: [PATCH 10/15] feat(api): api update --- .stats.yml | 4 ++-- lib/zavudev/resources/messages.rb | 13 ++++++++++--- rbi/zavudev/resources/messages.rbi | 13 ++++++++++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index a3be4c8..ac9266e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-7ddbc5b54ce2afee5a9c67569f652309a8f79e02643e74af3afba4cb7310f737.yml -openapi_spec_hash: de4fcf9ef758597cfcc59299d12d7b33 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-be6c0709d91750e8a9ea4ac11c26c00ef39b0e09233beb454131452d10a99e95.yml +openapi_spec_hash: ba279d70955f6b4da3fcf63e92158406 config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/resources/messages.rb b/lib/zavudev/resources/messages.rb index 338cdec..ccc250a 100644 --- a/lib/zavudev/resources/messages.rb +++ b/lib/zavudev/resources/messages.rb @@ -129,9 +129,16 @@ def react(message_id, params) # **Plan allowances and email billing:** # # - WhatsApp, Telegram, Instagram and Messenger share an allowance of 2,000 - # messages per month on Free. Over it, sends return 429 with code - # `a2p_limit_exceeded` and upgrade details; the counter resets on the 1st of - # each month. Paid plans have no message caps + # messages per month on Free. **It counts messages in both directions**: a + # message a contact sends you consumes one unit exactly as a message you send + # them does, so a project that has sent 300 and received 1,700 has used the + # whole allowance. Messages you send from the WhatsApp Business App on your own + # phone under coexistence are mirrored into your inbox but never counted, and + # neither are failed sends. Over the allowance, sends return 429 with code + # `a2p_limit_exceeded` and upgrade details, **and inbound messages on those + # channels are refused as well**: not stored, not shown in the inbox, and no + # `message.inbound` webhook, and not delivered later when the month resets. The + # counter resets on the 1st of each month. Paid plans have no message caps # - Email is billed from your prepaid balance in 1,000-message blocks: $0.40 per # 1,000 transactional emails, $0.80 per 1,000 marketing (broadcast) emails. A # block is charged when your monthly count crosses each 1,000 boundary, and at diff --git a/rbi/zavudev/resources/messages.rbi b/rbi/zavudev/resources/messages.rbi index 8677127..b19e209 100644 --- a/rbi/zavudev/resources/messages.rbi +++ b/rbi/zavudev/resources/messages.rbi @@ -89,9 +89,16 @@ module Zavudev # **Plan allowances and email billing:** # # - WhatsApp, Telegram, Instagram and Messenger share an allowance of 2,000 - # messages per month on Free. Over it, sends return 429 with code - # `a2p_limit_exceeded` and upgrade details; the counter resets on the 1st of - # each month. Paid plans have no message caps + # messages per month on Free. **It counts messages in both directions**: a + # message a contact sends you consumes one unit exactly as a message you send + # them does, so a project that has sent 300 and received 1,700 has used the + # whole allowance. Messages you send from the WhatsApp Business App on your own + # phone under coexistence are mirrored into your inbox but never counted, and + # neither are failed sends. Over the allowance, sends return 429 with code + # `a2p_limit_exceeded` and upgrade details, **and inbound messages on those + # channels are refused as well**: not stored, not shown in the inbox, and no + # `message.inbound` webhook, and not delivered later when the month resets. The + # counter resets on the 1st of each month. Paid plans have no message caps # - Email is billed from your prepaid balance in 1,000-message blocks: $0.40 per # 1,000 transactional emails, $0.80 per 1,000 marketing (broadcast) emails. A # block is charged when your monthly count crosses each 1,000 boundary, and at From e8f7c926be5debc22f30565bd603f400c9501076 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 19:25:20 +0000 Subject: [PATCH 11/15] feat(api): api update --- .stats.yml | 4 ++-- lib/zavudev/models/sender_create_params.rb | 4 +++- lib/zavudev/models/sender_update_params.rb | 4 +++- lib/zavudev/resources/broadcasts.rb | 17 ++++++++++------- lib/zavudev/resources/messages.rb | 5 ++++- rbi/zavudev/models/sender_create_params.rbi | 8 ++++++-- rbi/zavudev/models/sender_update_params.rbi | 8 ++++++-- rbi/zavudev/resources/broadcasts.rbi | 17 ++++++++++------- rbi/zavudev/resources/messages.rbi | 5 ++++- rbi/zavudev/resources/senders.rbi | 8 ++++++-- 10 files changed, 54 insertions(+), 26 deletions(-) diff --git a/.stats.yml b/.stats.yml index ac9266e..ea60dc8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-be6c0709d91750e8a9ea4ac11c26c00ef39b0e09233beb454131452d10a99e95.yml -openapi_spec_hash: ba279d70955f6b4da3fcf63e92158406 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-2c4f59b60c0f846111040e670ba837bee5fe7a3eefb3e7fb905903b710df1be0.yml +openapi_spec_hash: 0a1d60101d1226bce5c3879b70f9050f config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/models/sender_create_params.rb b/lib/zavudev/models/sender_create_params.rb index 6958e5c..0d7db6b 100644 --- a/lib/zavudev/models/sender_create_params.rb +++ b/lib/zavudev/models/sender_create_params.rb @@ -46,7 +46,9 @@ class SenderCreateParams < Zavudev::Internal::Type::BaseModel # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. # Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on - # the response. + # the response. Turning the channel on needs nothing, but SENDING on it requires + # an approved business verification (KYB): without one every send is refused with + # `403 kyb_required`. # # @return [Boolean, nil] optional :enable_sms_oneway, Zavudev::Internal::Type::Boolean, api_name: :enableSmsOneway diff --git a/lib/zavudev/models/sender_update_params.rb b/lib/zavudev/models/sender_update_params.rb index 8031718..fbe4ea2 100644 --- a/lib/zavudev/models/sender_update_params.rb +++ b/lib/zavudev/models/sender_update_params.rb @@ -52,7 +52,9 @@ class SenderUpdateParams < Zavudev::Internal::Type::BaseModel # @!attribute enable_sms_oneway # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with - # the `channels` array on the response. + # the `channels` array on the response. Turning the channel on needs nothing, but + # SENDING on it requires an approved business verification (KYB): without one + # every send is refused with `403 kyb_required`. # # @return [Boolean, nil] optional :enable_sms_oneway, Zavudev::Internal::Type::Boolean, api_name: :enableSmsOneway diff --git a/lib/zavudev/resources/broadcasts.rb b/lib/zavudev/resources/broadcasts.rb index d183f8c..7cd6654 100644 --- a/lib/zavudev/resources/broadcasts.rb +++ b/lib/zavudev/resources/broadcasts.rb @@ -259,13 +259,16 @@ def retry_review(broadcast_id, params = {}) # An account that has verified nothing is refused with `403` and code # `kyc_required` on every channel other than `whatsapp`. Any one of these lifts # it: identity verification (KYC), a saved payment method, a settled deposit, or a - # paid plan. Business verification (KYB) is not required to broadcast; it gates - # 10DLC registration only. A `whatsapp` broadcast is exempt: it can only be built - # on a template, and Meta vets the business and the content when it approves that - # template, so an unapproved template is refused instead. `smart` is not exempt, - # since it can route a contact to SMS or email. Drafts can be created, edited and - # kept without any check. Every send path (dashboard, API and CLI) enforces the - # same rule. + # paid plan. Business verification (KYB) is not required to broadcast on any + # channel except `sms_oneway`, which is refused with `403` and code `KYB_REQUIRED` + # until it is approved; KYB also gates 10DLC registration. A `smart` broadcast is + # never refused for KYB: without it, one-way SMS is simply dropped from the + # channels smart routing may pick for a contact. A `whatsapp` broadcast is exempt: + # it can only be built on a template, and Meta vets the business and the content + # when it approves that template, so an unapproved template is refused instead. + # `smart` is not exempt, since it can route a contact to SMS or email. Drafts can + # be created, edited and kept without any check. Every send path (dashboard, API + # and CLI) enforces the same rule. # # **Daily ceilings apply per recipient.** Each message a broadcast sends counts # against the channel's daily ceiling (see `POST /v1/messages`). Once the ceiling diff --git a/lib/zavudev/resources/messages.rb b/lib/zavudev/resources/messages.rb index ccc250a..d7591a2 100644 --- a/lib/zavudev/resources/messages.rb +++ b/lib/zavudev/resources/messages.rb @@ -158,7 +158,10 @@ def react(message_id, params) # Zavu's sandbox number. One verification covers WhatsApp, SMS and calls, up to # 5 numbers per project. To send to any destination, do any one of these: verify # your identity, add a payment method, settle a deposit, or subscribe to a paid - # plan. Business verification (KYB) is never required to send + # plan. Business verification (KYB) is required for **one channel only**: + # `sms_oneway`. Without an approved KYB, one-way SMS returns `403` with code + # `kyb_required` and `details.dashboardUrl` pointing at `/kyb`, whatever the + # account has otherwise verified. No other channel asks for it # - Daily ceilings apply per channel group and rise with verification. An account # that has verified nothing: 25/day across `sms` + `sms_oneway`, 5/day for # `voice`, 100/day across WhatsApp, Telegram, Instagram and Messenger combined. diff --git a/rbi/zavudev/models/sender_create_params.rbi b/rbi/zavudev/models/sender_create_params.rbi index bb7cd9c..57d2e53 100644 --- a/rbi/zavudev/models/sender_create_params.rbi +++ b/rbi/zavudev/models/sender_create_params.rbi @@ -51,7 +51,9 @@ module Zavudev # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. # Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on - # the response. + # the response. Turning the channel on needs nothing, but SENDING on it requires + # an approved business verification (KYB): without one every send is refused with + # `403 kyb_required`. sig { returns(T.nilable(T::Boolean)) } attr_reader :enable_sms_oneway @@ -168,7 +170,9 @@ module Zavudev # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. # Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on - # the response. + # the response. Turning the channel on needs nothing, but SENDING on it requires + # an approved business verification (KYB): without one every send is refused with + # `403 kyb_required`. enable_sms_oneway: nil, # Let this sender place and answer phone calls. Requires `phoneNumber`; enabling # it without one returns 400. Check the `channels` array on the response to diff --git a/rbi/zavudev/models/sender_update_params.rbi b/rbi/zavudev/models/sender_update_params.rbi index 48f24a6..214d3a1 100644 --- a/rbi/zavudev/models/sender_update_params.rbi +++ b/rbi/zavudev/models/sender_update_params.rbi @@ -58,7 +58,9 @@ module Zavudev # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with - # the `channels` array on the response. + # the `channels` array on the response. Turning the channel on needs nothing, but + # SENDING on it requires an approved business verification (KYB): without one + # every send is refused with `403 kyb_required`. sig { returns(T.nilable(T::Boolean)) } attr_reader :enable_sms_oneway @@ -176,7 +178,9 @@ module Zavudev email_receiving_enabled: nil, # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with - # the `channels` array on the response. + # the `channels` array on the response. Turning the channel on needs nothing, but + # SENDING on it requires an approved business verification (KYB): without one + # every send is refused with `403 kyb_required`. enable_sms_oneway: nil, # Turn the voice channel on or off. The sender must already have a phone number # provisioned for calls; enabling it otherwise returns 400 instead of storing a diff --git a/rbi/zavudev/resources/broadcasts.rbi b/rbi/zavudev/resources/broadcasts.rbi index 05f31ce..ed32d0f 100644 --- a/rbi/zavudev/resources/broadcasts.rbi +++ b/rbi/zavudev/resources/broadcasts.rbi @@ -180,13 +180,16 @@ module Zavudev # An account that has verified nothing is refused with `403` and code # `kyc_required` on every channel other than `whatsapp`. Any one of these lifts # it: identity verification (KYC), a saved payment method, a settled deposit, or a - # paid plan. Business verification (KYB) is not required to broadcast; it gates - # 10DLC registration only. A `whatsapp` broadcast is exempt: it can only be built - # on a template, and Meta vets the business and the content when it approves that - # template, so an unapproved template is refused instead. `smart` is not exempt, - # since it can route a contact to SMS or email. Drafts can be created, edited and - # kept without any check. Every send path (dashboard, API and CLI) enforces the - # same rule. + # paid plan. Business verification (KYB) is not required to broadcast on any + # channel except `sms_oneway`, which is refused with `403` and code `KYB_REQUIRED` + # until it is approved; KYB also gates 10DLC registration. A `smart` broadcast is + # never refused for KYB: without it, one-way SMS is simply dropped from the + # channels smart routing may pick for a contact. A `whatsapp` broadcast is exempt: + # it can only be built on a template, and Meta vets the business and the content + # when it approves that template, so an unapproved template is refused instead. + # `smart` is not exempt, since it can route a contact to SMS or email. Drafts can + # be created, edited and kept without any check. Every send path (dashboard, API + # and CLI) enforces the same rule. # # **Daily ceilings apply per recipient.** Each message a broadcast sends counts # against the channel's daily ceiling (see `POST /v1/messages`). Once the ceiling diff --git a/rbi/zavudev/resources/messages.rbi b/rbi/zavudev/resources/messages.rbi index b19e209..78b0274 100644 --- a/rbi/zavudev/resources/messages.rbi +++ b/rbi/zavudev/resources/messages.rbi @@ -118,7 +118,10 @@ module Zavudev # Zavu's sandbox number. One verification covers WhatsApp, SMS and calls, up to # 5 numbers per project. To send to any destination, do any one of these: verify # your identity, add a payment method, settle a deposit, or subscribe to a paid - # plan. Business verification (KYB) is never required to send + # plan. Business verification (KYB) is required for **one channel only**: + # `sms_oneway`. Without an approved KYB, one-way SMS returns `403` with code + # `kyb_required` and `details.dashboardUrl` pointing at `/kyb`, whatever the + # account has otherwise verified. No other channel asks for it # - Daily ceilings apply per channel group and rise with verification. An account # that has verified nothing: 25/day across `sms` + `sms_oneway`, 5/day for # `voice`, 100/day across WhatsApp, Telegram, Instagram and Messenger combined. diff --git a/rbi/zavudev/resources/senders.rbi b/rbi/zavudev/resources/senders.rbi index f8768b0..292148b 100644 --- a/rbi/zavudev/resources/senders.rbi +++ b/rbi/zavudev/resources/senders.rbi @@ -50,7 +50,9 @@ module Zavudev # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. # Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on - # the response. + # the response. Turning the channel on needs nothing, but SENDING on it requires + # an approved business verification (KYB): without one every send is refused with + # `403 kyb_required`. enable_sms_oneway: nil, # Let this sender place and answer phone calls. Requires `phoneNumber`; enabling # it without one returns 400. Check the `channels` array on the response to @@ -138,7 +140,9 @@ module Zavudev email_receiving_enabled: nil, # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with - # the `channels` array on the response. + # the `channels` array on the response. Turning the channel on needs nothing, but + # SENDING on it requires an approved business verification (KYB): without one + # every send is refused with `403 kyb_required`. enable_sms_oneway: nil, # Turn the voice channel on or off. The sender must already have a phone number # provisioned for calls; enabling it otherwise returns 400 instead of storing a From 39fe83bd5fc91daa66d34bab6a7fd06744623fa9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 19 Sep 2026 16:34:01 +0000 Subject: [PATCH 12/15] feat(api): api update --- .stats.yml | 4 ++-- lib/zavudev/models/broadcast.rb | 17 ++++++++++++++-- lib/zavudev/models/broadcast_contact.rb | 13 ++++++++++++ .../models/broadcast_contact_status.rb | 11 ++++++++++ lib/zavudev/models/broadcast_progress.rb | 14 ++++++++++--- .../models/broadcasts/contact_list_params.rb | 13 ++++++++++++ lib/zavudev/resources/broadcasts/contacts.rb | 3 +++ rbi/zavudev/models/broadcast.rbi | 15 ++++++++++++++ rbi/zavudev/models/broadcast_contact.rbi | 20 +++++++++++++++++++ .../models/broadcast_contact_status.rbi | 11 ++++++++++ rbi/zavudev/models/broadcast_progress.rbi | 15 ++++++++++++-- .../models/broadcasts/contact_list_params.rbi | 20 +++++++++++++++++++ rbi/zavudev/resources/broadcasts/contacts.rbi | 10 ++++++++++ sig/zavudev/models/broadcast.rbs | 7 +++++++ .../models/broadcast_contact_status.rbs | 3 ++- sig/zavudev/models/broadcast_progress.rbs | 7 +++++++ test/zavudev/resources/broadcasts_test.rb | 2 ++ 17 files changed, 175 insertions(+), 10 deletions(-) diff --git a/.stats.yml b/.stats.yml index ea60dc8..9cb1abb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-2c4f59b60c0f846111040e670ba837bee5fe7a3eefb3e7fb905903b710df1be0.yml -openapi_spec_hash: 0a1d60101d1226bce5c3879b70f9050f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-055bbc3b65106faf7e50f3d3d93e961d5a9f07f2aef41b5d5524e07eb36a2ce1.yml +openapi_spec_hash: 10eb30ac64baa679f739dc5484f9dfea config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/models/broadcast.rb b/lib/zavudev/models/broadcast.rb index 90b349b..a4dbecc 100644 --- a/lib/zavudev/models/broadcast.rb +++ b/lib/zavudev/models/broadcast.rb @@ -61,6 +61,7 @@ class Broadcast < Zavudev::Internal::Type::BaseModel optional :content, -> { Zavudev::BroadcastContent } # @!attribute delivered_count + # Recipients with confirmed delivery to the device. # # @return [Integer, nil] optional :delivered_count, Integer, api_name: :deliveredCount @@ -124,6 +125,13 @@ class Broadcast < Zavudev::Internal::Type::BaseModel # @return [Integer, nil] optional :sending_count, Integer, api_name: :sendingCount + # @!attribute sent_count + # Recipients whose message the provider accepted, without a confirmed delivery + # yet. Channels that never report delivery keep their recipients here. + # + # @return [Integer, nil] + optional :sent_count, Integer, api_name: :sentCount + # @!attribute started_at # # @return [Time, nil] @@ -139,7 +147,10 @@ class Broadcast < Zavudev::Internal::Type::BaseModel # @return [Time, nil] optional :updated_at, Time, api_name: :updatedAt - # @!method initialize(id:, channel:, created_at:, message_type:, name:, status:, total_contacts:, actual_cost: nil, completed_at: nil, content: nil, delivered_count: nil, email_subject: nil, estimated_cost: nil, failed_count: nil, metadata: nil, pending_count: nil, reserved_amount: nil, review_attempts: nil, review_result: nil, scheduled_at: nil, sender_id: nil, sending_count: nil, started_at: nil, text: nil, updated_at: nil) + # @!method initialize(id:, channel:, created_at:, message_type:, name:, status:, total_contacts:, actual_cost: nil, completed_at: nil, content: nil, delivered_count: nil, email_subject: nil, estimated_cost: nil, failed_count: nil, metadata: nil, pending_count: nil, reserved_amount: nil, review_attempts: nil, review_result: nil, scheduled_at: nil, sender_id: nil, sending_count: nil, sent_count: nil, started_at: nil, text: nil, updated_at: nil) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::Broadcast} for more details. + # # @param id [String] # # @param channel [Symbol, Zavudev::Models::BroadcastChannel] Broadcast delivery channel. Use 'smart' for per-contact intelligent routing. @@ -160,7 +171,7 @@ class Broadcast < Zavudev::Internal::Type::BaseModel # # @param content [Zavudev::Models::BroadcastContent] Content for non-text broadcast message types. # - # @param delivered_count [Integer] + # @param delivered_count [Integer] Recipients with confirmed delivery to the device. # # @param email_subject [String] # @@ -184,6 +195,8 @@ class Broadcast < Zavudev::Internal::Type::BaseModel # # @param sending_count [Integer] # + # @param sent_count [Integer] Recipients whose message the provider accepted, without a confirmed delivery yet + # # @param started_at [Time] # # @param text [String] diff --git a/lib/zavudev/models/broadcast_contact.rb b/lib/zavudev/models/broadcast_contact.rb index 1286c20..b130f9a 100644 --- a/lib/zavudev/models/broadcast_contact.rb +++ b/lib/zavudev/models/broadcast_contact.rb @@ -30,6 +30,16 @@ class BroadcastContact < Zavudev::Internal::Type::BaseModel # @!attribute status # Status of a contact within a broadcast. # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. + # # @return [Symbol, Zavudev::Models::BroadcastContactStatus] required :status, enum: -> { Zavudev::BroadcastContactStatus } @@ -79,6 +89,9 @@ class BroadcastContact < Zavudev::Internal::Type::BaseModel optional :template_variables, Zavudev::Internal::Type::HashOf[String], api_name: :templateVariables # @!method initialize(id:, created_at:, recipient:, recipient_type:, status:, cost: nil, error_code: nil, error_message: nil, message_id: nil, processed_at: nil, template_button_variables: nil, template_header_variables: nil, template_variables: nil) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::BroadcastContact} for more details. + # # @param id [String] # # @param created_at [Time] diff --git a/lib/zavudev/models/broadcast_contact_status.rb b/lib/zavudev/models/broadcast_contact_status.rb index 6522283..bc1697b 100644 --- a/lib/zavudev/models/broadcast_contact_status.rb +++ b/lib/zavudev/models/broadcast_contact_status.rb @@ -3,12 +3,23 @@ module Zavudev module Models # Status of a contact within a broadcast. + # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. module BroadcastContactStatus extend Zavudev::Internal::Type::Enum PENDING = :pending QUEUED = :queued SENDING = :sending + SENT = :sent DELIVERED = :delivered FAILED = :failed SKIPPED = :skipped diff --git a/lib/zavudev/models/broadcast_progress.rb b/lib/zavudev/models/broadcast_progress.rb index 9676a7f..1acf99d 100644 --- a/lib/zavudev/models/broadcast_progress.rb +++ b/lib/zavudev/models/broadcast_progress.rb @@ -10,7 +10,7 @@ class BroadcastProgress < Zavudev::Internal::Type::BaseModel required :broadcast_id, String, api_name: :broadcastId # @!attribute delivered - # Successfully delivered. + # Confirmed delivered to the device. # # @return [Integer] required :delivered, Integer @@ -80,15 +80,21 @@ class BroadcastProgress < Zavudev::Internal::Type::BaseModel # @return [Float, nil] optional :reserved_amount, Float, api_name: :reservedAmount, nil?: true + # @!attribute sent + # Accepted by the provider, delivery not confirmed yet. + # + # @return [Integer, nil] + optional :sent, Integer + # @!attribute started_at # # @return [Time, nil] optional :started_at, Time, api_name: :startedAt - # @!method initialize(broadcast_id:, delivered:, failed:, pending:, percent_complete:, sending:, skipped:, status:, total:, actual_cost: nil, estimated_completion_at: nil, estimated_cost: nil, reserved_amount: nil, started_at: nil) + # @!method initialize(broadcast_id:, delivered:, failed:, pending:, percent_complete:, sending:, skipped:, status:, total:, actual_cost: nil, estimated_completion_at: nil, estimated_cost: nil, reserved_amount: nil, sent: nil, started_at: nil) # @param broadcast_id [String] # - # @param delivered [Integer] Successfully delivered. + # @param delivered [Integer] Confirmed delivered to the device. # # @param failed [Integer] Failed to deliver. # @@ -112,6 +118,8 @@ class BroadcastProgress < Zavudev::Internal::Type::BaseModel # # @param reserved_amount [Float, nil] Amount reserved from balance in USD. # + # @param sent [Integer] Accepted by the provider, delivery not confirmed yet. + # # @param started_at [Time] end end diff --git a/lib/zavudev/models/broadcasts/contact_list_params.rb b/lib/zavudev/models/broadcasts/contact_list_params.rb index 4a8d36e..7a95a84 100644 --- a/lib/zavudev/models/broadcasts/contact_list_params.rb +++ b/lib/zavudev/models/broadcasts/contact_list_params.rb @@ -26,10 +26,23 @@ class ContactListParams < Zavudev::Internal::Type::BaseModel # @!attribute status # Status of a contact within a broadcast. # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. + # # @return [Symbol, Zavudev::Models::BroadcastContactStatus, nil] optional :status, enum: -> { Zavudev::BroadcastContactStatus } # @!method initialize(broadcast_id:, cursor: nil, limit: nil, status: nil, request_options: {}) + # Some parameter documentations has been truncated, see + # {Zavudev::Models::Broadcasts::ContactListParams} for more details. + # # @param broadcast_id [String] # # @param cursor [String] diff --git a/lib/zavudev/resources/broadcasts/contacts.rb b/lib/zavudev/resources/broadcasts/contacts.rb index d035e02..ef688f1 100644 --- a/lib/zavudev/resources/broadcasts/contacts.rb +++ b/lib/zavudev/resources/broadcasts/contacts.rb @@ -4,6 +4,9 @@ module Zavudev module Resources class Broadcasts class Contacts + # Some parameter documentations has been truncated, see + # {Zavudev::Models::Broadcasts::ContactListParams} for more details. + # # List contacts in a broadcast with optional status filter. # # @overload list(broadcast_id, cursor: nil, limit: nil, status: nil, request_options: {}) diff --git a/rbi/zavudev/models/broadcast.rbi b/rbi/zavudev/models/broadcast.rbi index 89c4c7d..6f58704 100644 --- a/rbi/zavudev/models/broadcast.rbi +++ b/rbi/zavudev/models/broadcast.rbi @@ -48,6 +48,7 @@ module Zavudev sig { params(content: Zavudev::BroadcastContent::OrHash).void } attr_writer :content + # Recipients with confirmed delivery to the device. sig { returns(T.nilable(Integer)) } attr_reader :delivered_count @@ -119,6 +120,14 @@ module Zavudev sig { params(sending_count: Integer).void } attr_writer :sending_count + # Recipients whose message the provider accepted, without a confirmed delivery + # yet. Channels that never report delivery keep their recipients here. + sig { returns(T.nilable(Integer)) } + attr_reader :sent_count + + sig { params(sent_count: Integer).void } + attr_writer :sent_count + sig { returns(T.nilable(Time)) } attr_reader :started_at @@ -161,6 +170,7 @@ module Zavudev scheduled_at: Time, sender_id: String, sending_count: Integer, + sent_count: Integer, started_at: Time, text: String, updated_at: Time @@ -183,6 +193,7 @@ module Zavudev completed_at: nil, # Content for non-text broadcast message types. content: nil, + # Recipients with confirmed delivery to the device. delivered_count: nil, email_subject: nil, # Estimated total cost in USD. @@ -199,6 +210,9 @@ module Zavudev scheduled_at: nil, sender_id: nil, sending_count: nil, + # Recipients whose message the provider accepted, without a confirmed delivery + # yet. Channels that never report delivery keep their recipients here. + sent_count: nil, started_at: nil, text: nil, updated_at: nil @@ -230,6 +244,7 @@ module Zavudev scheduled_at: Time, sender_id: String, sending_count: Integer, + sent_count: Integer, started_at: Time, text: String, updated_at: Time diff --git a/rbi/zavudev/models/broadcast_contact.rbi b/rbi/zavudev/models/broadcast_contact.rbi index db97f16..669f066 100644 --- a/rbi/zavudev/models/broadcast_contact.rbi +++ b/rbi/zavudev/models/broadcast_contact.rbi @@ -21,6 +21,16 @@ module Zavudev attr_accessor :recipient_type # Status of a contact within a broadcast. + # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. sig { returns(Zavudev::BroadcastContactStatus::TaggedSymbol) } attr_accessor :status @@ -93,6 +103,16 @@ module Zavudev recipient:, recipient_type:, # Status of a contact within a broadcast. + # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. status:, cost: nil, error_code: nil, diff --git a/rbi/zavudev/models/broadcast_contact_status.rbi b/rbi/zavudev/models/broadcast_contact_status.rbi index 0b2ff78..4f4fdbb 100644 --- a/rbi/zavudev/models/broadcast_contact_status.rbi +++ b/rbi/zavudev/models/broadcast_contact_status.rbi @@ -3,6 +3,16 @@ module Zavudev module Models # Status of a contact within a broadcast. + # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. module BroadcastContactStatus extend Zavudev::Internal::Type::Enum @@ -13,6 +23,7 @@ module Zavudev PENDING = T.let(:pending, Zavudev::BroadcastContactStatus::TaggedSymbol) QUEUED = T.let(:queued, Zavudev::BroadcastContactStatus::TaggedSymbol) SENDING = T.let(:sending, Zavudev::BroadcastContactStatus::TaggedSymbol) + SENT = T.let(:sent, Zavudev::BroadcastContactStatus::TaggedSymbol) DELIVERED = T.let(:delivered, Zavudev::BroadcastContactStatus::TaggedSymbol) FAILED = T.let(:failed, Zavudev::BroadcastContactStatus::TaggedSymbol) diff --git a/rbi/zavudev/models/broadcast_progress.rbi b/rbi/zavudev/models/broadcast_progress.rbi index 81b235d..a836ca8 100644 --- a/rbi/zavudev/models/broadcast_progress.rbi +++ b/rbi/zavudev/models/broadcast_progress.rbi @@ -11,7 +11,7 @@ module Zavudev sig { returns(String) } attr_accessor :broadcast_id - # Successfully delivered. + # Confirmed delivered to the device. sig { returns(Integer) } attr_accessor :delivered @@ -61,6 +61,13 @@ module Zavudev sig { returns(T.nilable(Float)) } attr_accessor :reserved_amount + # Accepted by the provider, delivery not confirmed yet. + sig { returns(T.nilable(Integer)) } + attr_reader :sent + + sig { params(sent: Integer).void } + attr_writer :sent + sig { returns(T.nilable(Time)) } attr_reader :started_at @@ -82,12 +89,13 @@ module Zavudev estimated_completion_at: Time, estimated_cost: T.nilable(Float), reserved_amount: T.nilable(Float), + sent: Integer, started_at: Time ).returns(T.attached_class) end def self.new( broadcast_id:, - # Successfully delivered. + # Confirmed delivered to the device. delivered:, # Failed to deliver. failed:, @@ -110,6 +118,8 @@ module Zavudev estimated_cost: nil, # Amount reserved from balance in USD. reserved_amount: nil, + # Accepted by the provider, delivery not confirmed yet. + sent: nil, started_at: nil ) end @@ -130,6 +140,7 @@ module Zavudev estimated_completion_at: Time, estimated_cost: T.nilable(Float), reserved_amount: T.nilable(Float), + sent: Integer, started_at: Time } ) diff --git a/rbi/zavudev/models/broadcasts/contact_list_params.rbi b/rbi/zavudev/models/broadcasts/contact_list_params.rbi index ef3617d..d959d73 100644 --- a/rbi/zavudev/models/broadcasts/contact_list_params.rbi +++ b/rbi/zavudev/models/broadcasts/contact_list_params.rbi @@ -31,6 +31,16 @@ module Zavudev attr_writer :limit # Status of a contact within a broadcast. + # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. sig { returns(T.nilable(Zavudev::BroadcastContactStatus::OrSymbol)) } attr_reader :status @@ -51,6 +61,16 @@ module Zavudev cursor: nil, limit: nil, # Status of a contact within a broadcast. + # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. status: nil, request_options: {} ) diff --git a/rbi/zavudev/resources/broadcasts/contacts.rbi b/rbi/zavudev/resources/broadcasts/contacts.rbi index 10d4f7d..e5bc585 100644 --- a/rbi/zavudev/resources/broadcasts/contacts.rbi +++ b/rbi/zavudev/resources/broadcasts/contacts.rbi @@ -19,6 +19,16 @@ module Zavudev cursor: nil, limit: nil, # Status of a contact within a broadcast. + # + # - `pending`, `queued`, `sending`: not handed to the provider yet. + # - `sent`: accepted by the provider; delivery is not confirmed yet. Channels that + # never report delivery leave the recipient here. + # - `delivered`: the channel confirmed delivery to the device. A WhatsApp read + # receipt also counts as delivered. + # - `failed`: not delivered. A recipient can move from `sent` or `delivered` to + # `failed` when the provider reports a failure late. + # - `skipped`: not sent, because the recipient opted out of the channel or the + # broadcast was cancelled before reaching it. status: nil, request_options: {} ) diff --git a/sig/zavudev/models/broadcast.rbs b/sig/zavudev/models/broadcast.rbs index 35691ae..57aad83 100644 --- a/sig/zavudev/models/broadcast.rbs +++ b/sig/zavudev/models/broadcast.rbs @@ -24,6 +24,7 @@ module Zavudev scheduled_at: Time, sender_id: String, sending_count: Integer, + sent_count: Integer, started_at: Time, text: String, updated_at: Time @@ -94,6 +95,10 @@ module Zavudev def sending_count=: (Integer) -> Integer + attr_reader sent_count: Integer? + + def sent_count=: (Integer) -> Integer + attr_reader started_at: Time? def started_at=: (Time) -> Time @@ -129,6 +134,7 @@ module Zavudev ?scheduled_at: Time, ?sender_id: String, ?sending_count: Integer, + ?sent_count: Integer, ?started_at: Time, ?text: String, ?updated_at: Time @@ -157,6 +163,7 @@ module Zavudev scheduled_at: Time, sender_id: String, sending_count: Integer, + sent_count: Integer, started_at: Time, text: String, updated_at: Time diff --git a/sig/zavudev/models/broadcast_contact_status.rbs b/sig/zavudev/models/broadcast_contact_status.rbs index d8e3c6f..a343761 100644 --- a/sig/zavudev/models/broadcast_contact_status.rbs +++ b/sig/zavudev/models/broadcast_contact_status.rbs @@ -1,7 +1,7 @@ module Zavudev module Models type broadcast_contact_status = - :pending | :queued | :sending | :delivered | :failed | :skipped + :pending | :queued | :sending | :sent | :delivered | :failed | :skipped module BroadcastContactStatus extend Zavudev::Internal::Type::Enum @@ -9,6 +9,7 @@ module Zavudev PENDING: :pending QUEUED: :queued SENDING: :sending + SENT: :sent DELIVERED: :delivered FAILED: :failed SKIPPED: :skipped diff --git a/sig/zavudev/models/broadcast_progress.rbs b/sig/zavudev/models/broadcast_progress.rbs index c7d1c81..13f0a19 100644 --- a/sig/zavudev/models/broadcast_progress.rbs +++ b/sig/zavudev/models/broadcast_progress.rbs @@ -15,6 +15,7 @@ module Zavudev estimated_completion_at: Time, estimated_cost: Float?, reserved_amount: Float?, + sent: Integer, started_at: Time } @@ -47,6 +48,10 @@ module Zavudev attr_accessor reserved_amount: Float? + attr_reader sent: Integer? + + def sent=: (Integer) -> Integer + attr_reader started_at: Time? def started_at=: (Time) -> Time @@ -65,6 +70,7 @@ module Zavudev ?estimated_completion_at: Time, ?estimated_cost: Float?, ?reserved_amount: Float?, + ?sent: Integer, ?started_at: Time ) -> void @@ -82,6 +88,7 @@ module Zavudev estimated_completion_at: Time, estimated_cost: Float?, reserved_amount: Float?, + sent: Integer, started_at: Time } end diff --git a/test/zavudev/resources/broadcasts_test.rb b/test/zavudev/resources/broadcasts_test.rb index 38816af..f492c7f 100644 --- a/test/zavudev/resources/broadcasts_test.rb +++ b/test/zavudev/resources/broadcasts_test.rb @@ -91,6 +91,7 @@ def test_list scheduled_at: Time | nil, sender_id: String | nil, sending_count: Integer | nil, + sent_count: Integer | nil, started_at: Time | nil, text: String | nil, updated_at: Time | nil @@ -164,6 +165,7 @@ def test_progress estimated_completion_at: Time | nil, estimated_cost: Float | nil, reserved_amount: Float | nil, + sent: Integer | nil, started_at: Time | nil } end From 5f2c06855f0fd716bda8fa691dc56aa2bc0f1844 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 20 Sep 2026 12:34:53 +0000 Subject: [PATCH 13/15] feat(api): api update --- .stats.yml | 4 +- lib/zavudev/models/sender_create_params.rb | 4 +- lib/zavudev/models/sender_update_params.rb | 4 +- lib/zavudev/resources/broadcasts.rb | 32 +++++------- lib/zavudev/resources/calls.rb | 19 +++---- lib/zavudev/resources/messages.rb | 57 +++++++++++++-------- rbi/zavudev/models/sender_create_params.rbi | 8 +-- rbi/zavudev/models/sender_update_params.rbi | 8 +-- rbi/zavudev/resources/broadcasts.rbi | 32 +++++------- rbi/zavudev/resources/calls.rbi | 19 +++---- rbi/zavudev/resources/messages.rbi | 55 +++++++++++++------- rbi/zavudev/resources/senders.rbi | 8 +-- 12 files changed, 131 insertions(+), 119 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9cb1abb..6c831ea 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-055bbc3b65106faf7e50f3d3d93e961d5a9f07f2aef41b5d5524e07eb36a2ce1.yml -openapi_spec_hash: 10eb30ac64baa679f739dc5484f9dfea +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-42c22e5daf48fe8d621b322e6c9d23d33e160660c2938973490571673ea1ad0d.yml +openapi_spec_hash: f571c734ad0085b66d9a51a0a05d50c0 config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/models/sender_create_params.rb b/lib/zavudev/models/sender_create_params.rb index 0d7db6b..6958e5c 100644 --- a/lib/zavudev/models/sender_create_params.rb +++ b/lib/zavudev/models/sender_create_params.rb @@ -46,9 +46,7 @@ class SenderCreateParams < Zavudev::Internal::Type::BaseModel # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. # Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on - # the response. Turning the channel on needs nothing, but SENDING on it requires - # an approved business verification (KYB): without one every send is refused with - # `403 kyb_required`. + # the response. # # @return [Boolean, nil] optional :enable_sms_oneway, Zavudev::Internal::Type::Boolean, api_name: :enableSmsOneway diff --git a/lib/zavudev/models/sender_update_params.rb b/lib/zavudev/models/sender_update_params.rb index fbe4ea2..8031718 100644 --- a/lib/zavudev/models/sender_update_params.rb +++ b/lib/zavudev/models/sender_update_params.rb @@ -52,9 +52,7 @@ class SenderUpdateParams < Zavudev::Internal::Type::BaseModel # @!attribute enable_sms_oneway # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with - # the `channels` array on the response. Turning the channel on needs nothing, but - # SENDING on it requires an approved business verification (KYB): without one - # every send is refused with `403 kyb_required`. + # the `channels` array on the response. # # @return [Boolean, nil] optional :enable_sms_oneway, Zavudev::Internal::Type::Boolean, api_name: :enableSmsOneway diff --git a/lib/zavudev/resources/broadcasts.rb b/lib/zavudev/resources/broadcasts.rb index 7cd6654..3b9cdd1 100644 --- a/lib/zavudev/resources/broadcasts.rb +++ b/lib/zavudev/resources/broadcasts.rb @@ -255,20 +255,11 @@ def retry_review(broadcast_id, params = {}) # Start sending the broadcast immediately or schedule for later. # - # **The account must be past the unverified level to send, except on WhatsApp.** - # An account that has verified nothing is refused with `403` and code - # `kyc_required` on every channel other than `whatsapp`. Any one of these lifts - # it: identity verification (KYC), a saved payment method, a settled deposit, or a - # paid plan. Business verification (KYB) is not required to broadcast on any - # channel except `sms_oneway`, which is refused with `403` and code `KYB_REQUIRED` - # until it is approved; KYB also gates 10DLC registration. A `smart` broadcast is - # never refused for KYB: without it, one-way SMS is simply dropped from the - # channels smart routing may pick for a contact. A `whatsapp` broadcast is exempt: - # it can only be built on a template, and Meta vets the business and the content - # when it approves that template, so an unapproved template is refused instead. - # `smart` is not exempt, since it can route a contact to SMS or email. Drafts can - # be created, edited and kept without any check. Every send path (dashboard, API - # and CLI) enforces the same rule. + # **Sending a broadcast needs no account verification.** Identity and business + # verification raise daily ceilings; neither is a permission to broadcast, on any + # channel. What stands in front of a broadcast is the content review below, and it + # applies to every send path — dashboard, API and CLI. Drafts can be created, + # edited and kept freely. # # **Daily ceilings apply per recipient.** Each message a broadcast sends counts # against the channel's daily ceiling (see `POST /v1/messages`). Once the ceiling @@ -278,10 +269,15 @@ def retry_review(broadcast_id, params = {}) # **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. + # the content) and begins sending. An email broadcast sends as soon as the review + # passes it, unless the review asks for a person. Every other channel moves to + # `pending_admin_review` and waits for a person. A broadcast the review refuses + # lands on `rejected`: use PATCH to edit the content then call POST /retry-review, + # or escalate it for a manual review. + # + # A broadcast is read once, on its own text, rather than per recipient. While an + # account's sending is suspended its recipients fail individually with `errorCode` + # `SENDING_SUSPENDED`; see `POST /v1/messages`. # # Calling this on a broadcast that is already `approved` or `scheduled` sends or # reschedules it directly, since it has already been reviewed. Reserves the diff --git a/lib/zavudev/resources/calls.rb b/lib/zavudev/resources/calls.rb index 8d2c71a..fa00cb8 100644 --- a/lib/zavudev/resources/calls.rb +++ b/lib/zavudev/resources/calls.rb @@ -14,15 +14,16 @@ class Calls # **Requirements:** # # - The Voice Agents feature must be enabled for your team (otherwise `403`). - # - An account that has verified nothing may only call the phone numbers the - # project has verified (`403` with code `destination_not_verified`, and - # `details.verifiedNumbers` lists them), and at most 5 calls a day (`429` with - # code `daily_limit_exceeded`). A number is verified from the dashboard's - # Sandbox screen by sending the pre-filled WhatsApp message from that phone; the - # same verification covers SMS and calls. Verify your identity, add a payment - # method, settle a deposit or subscribe to call any destination. That raises the - # ceiling to 50 calls a day on Free; paid plans have no daily call ceiling. Full - # reference: https://docs.zavu.dev/concepts/sending-limits + # - An account may call any destination from its first minute, within the daily + # ceiling: 5 calls a day for an account that has verified nothing, 50 a day on + # Free once it has verified its identity, added a payment method, settled a + # deposit or subscribed. Paid plans have no daily call ceiling. Over it, `429` + # with code `daily_limit_exceeded`. Full reference: + # https://docs.zavu.dev/concepts/sending-limits + # - The call is read by Zavu's automated risk review before it is dialed, the way + # a message is (see `POST /v1/messages`). A call is never held for a person: one + # the review stops is failed rather than placed late. Repeated refusals suspend + # the account's calling, answered with `403` and code `sending_suspended`. # - The sender's agent must have `voice.enabled` set to `true`. # - Not available with test-mode API keys. # diff --git a/lib/zavudev/resources/messages.rb b/lib/zavudev/resources/messages.rb index d7591a2..2e28f2a 100644 --- a/lib/zavudev/resources/messages.rb +++ b/lib/zavudev/resources/messages.rb @@ -147,21 +147,14 @@ def react(message_id, params) # 100/day. Teams on earlier plans keep their original email quotas instead # - SMS and voice are billed per message from your balance on every plan # - # **Account verification and daily limits:** - # - # - A brand-new account can send on every channel immediately, but `sms`, - # `sms_oneway` and `voice` reach only the phone numbers the project has - # verified. Sending elsewhere returns `403` with code - # `destination_not_verified`; `details.verifiedNumbers` lists the numbers that - # are reachable. A number is verified from the dashboard's Sandbox screen: - # generate a code and send the pre-filled WhatsApp message from that phone to - # Zavu's sandbox number. One verification covers WhatsApp, SMS and calls, up to - # 5 numbers per project. To send to any destination, do any one of these: verify - # your identity, add a payment method, settle a deposit, or subscribe to a paid - # plan. Business verification (KYB) is required for **one channel only**: - # `sms_oneway`. Without an approved KYB, one-way SMS returns `403` with code - # `kyb_required` and `details.dashboardUrl` pointing at `/kyb`, whatever the - # account has otherwise verified. No other channel asks for it + # **Daily limits:** + # + # - An account sends on every channel from its first minute, to any destination. + # Verification is not a permission to send: identity verification and business + # verification (KYB) raise the ceilings below and nothing else asks for them + # here. KYB is still required to register a 10DLC brand and campaign, which + # every US and Canadian (+1) SMS destination needs — a carrier rule, answered + # separately with `403 ten_dlc_required` # - Daily ceilings apply per channel group and rise with verification. An account # that has verified nothing: 25/day across `sms` + `sms_oneway`, 5/day for # `voice`, 100/day across WhatsApp, Telegram, Instagram and Messenger combined. @@ -173,13 +166,37 @@ def react(message_id, params) # - The daily ceiling never reduces the monthly allowance: 100/day on the # conversational group still reaches the 2,000 monthly A2P messages Free # includes - # - Email needs no account verification here: a sender with a verified domain - # sends from day one, within the plan quota (100/day and 3,000/month on Free). - # Over the daily quota it returns `429` with code `daily_limit_exceeded`. Email - # broadcasts are the exception: they need the account past the unverified level, - # see `POST /v1/broadcasts/{broadcastId}/send` + # - Email: a sender with a verified domain sends from day one, within the plan + # quota (100/day and 3,000/month on Free). Over the daily quota it returns `429` + # with code `daily_limit_exceeded` # - Full reference: https://docs.zavu.dev/concepts/sending-limits # + # **Risk review:** Every outbound `sms`, `sms_oneway`, `email` and `voice` message + # is read before it is sent — the content, and how this account has been sending. + # What is checked is the message, not who you are. + # + # - A message can be **held** for a short review. It stays `queued` while it + # waits: no new status exists for this, and `MessageStatus` is unchanged. When + # it is approved it sends normally. + # - A message that is not approved moves to `failed` and fires `message.failed`. + # `errorCode` says which happened: `RISK_REJECTED` (a reviewer refused it), + # `RISK_REVIEW_EXPIRED` (the review window closed first — it is a couple of + # hours, because a code that arrives late is worse than one that does not + # arrive), or `RISK_BLOCKED` (refused outright, without a hold). An SMS that + # fails this way is not charged; the prepaid amount is returned. + # - A call is never held. `POST /v1/calls` fails a call the review stops rather + # than placing it hours late. + # - A message whose content cannot be read — the check is briefly unavailable — is + # held rather than sent. An account with an approved business verification is + # unaffected, and so is one that has verified something, already sends real + # traffic, and has a clean recent record. + # - Repeated refusals suspend an account's sending. While it is suspended every + # send is refused with `403` and code `sending_suspended`, + # `details.dashboardUrl` points at support, and a message already queued fails + # with `errorCode` `SENDING_SUSPENDED`. + # - A broadcast is read once, on the broadcast itself, rather than per recipient — + # see `POST /v1/broadcasts/{broadcastId}/send`. + # # **Email recipient pre-flight:** Email messages are validated automatically # before dispatch. Sends that would be a guaranteed hard bounce are failed instead # of sent, protecting your bounce rate: the message transitions to `failed` diff --git a/rbi/zavudev/models/sender_create_params.rbi b/rbi/zavudev/models/sender_create_params.rbi index 57d2e53..bb7cd9c 100644 --- a/rbi/zavudev/models/sender_create_params.rbi +++ b/rbi/zavudev/models/sender_create_params.rbi @@ -51,9 +51,7 @@ module Zavudev # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. # Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on - # the response. Turning the channel on needs nothing, but SENDING on it requires - # an approved business verification (KYB): without one every send is refused with - # `403 kyb_required`. + # the response. sig { returns(T.nilable(T::Boolean)) } attr_reader :enable_sms_oneway @@ -170,9 +168,7 @@ module Zavudev # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. # Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on - # the response. Turning the channel on needs nothing, but SENDING on it requires - # an approved business verification (KYB): without one every send is refused with - # `403 kyb_required`. + # the response. enable_sms_oneway: nil, # Let this sender place and answer phone calls. Requires `phoneNumber`; enabling # it without one returns 400. Check the `channels` array on the response to diff --git a/rbi/zavudev/models/sender_update_params.rbi b/rbi/zavudev/models/sender_update_params.rbi index 214d3a1..48f24a6 100644 --- a/rbi/zavudev/models/sender_update_params.rbi +++ b/rbi/zavudev/models/sender_update_params.rbi @@ -58,9 +58,7 @@ module Zavudev # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with - # the `channels` array on the response. Turning the channel on needs nothing, but - # SENDING on it requires an approved business verification (KYB): without one - # every send is refused with `403 kyb_required`. + # the `channels` array on the response. sig { returns(T.nilable(T::Boolean)) } attr_reader :enable_sms_oneway @@ -178,9 +176,7 @@ module Zavudev email_receiving_enabled: nil, # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with - # the `channels` array on the response. Turning the channel on needs nothing, but - # SENDING on it requires an approved business verification (KYB): without one - # every send is refused with `403 kyb_required`. + # the `channels` array on the response. enable_sms_oneway: nil, # Turn the voice channel on or off. The sender must already have a phone number # provisioned for calls; enabling it otherwise returns 400 instead of storing a diff --git a/rbi/zavudev/resources/broadcasts.rbi b/rbi/zavudev/resources/broadcasts.rbi index ed32d0f..e7529b6 100644 --- a/rbi/zavudev/resources/broadcasts.rbi +++ b/rbi/zavudev/resources/broadcasts.rbi @@ -176,20 +176,11 @@ module Zavudev # Start sending the broadcast immediately or schedule for later. # - # **The account must be past the unverified level to send, except on WhatsApp.** - # An account that has verified nothing is refused with `403` and code - # `kyc_required` on every channel other than `whatsapp`. Any one of these lifts - # it: identity verification (KYC), a saved payment method, a settled deposit, or a - # paid plan. Business verification (KYB) is not required to broadcast on any - # channel except `sms_oneway`, which is refused with `403` and code `KYB_REQUIRED` - # until it is approved; KYB also gates 10DLC registration. A `smart` broadcast is - # never refused for KYB: without it, one-way SMS is simply dropped from the - # channels smart routing may pick for a contact. A `whatsapp` broadcast is exempt: - # it can only be built on a template, and Meta vets the business and the content - # when it approves that template, so an unapproved template is refused instead. - # `smart` is not exempt, since it can route a contact to SMS or email. Drafts can - # be created, edited and kept without any check. Every send path (dashboard, API - # and CLI) enforces the same rule. + # **Sending a broadcast needs no account verification.** Identity and business + # verification raise daily ceilings; neither is a permission to broadcast, on any + # channel. What stands in front of a broadcast is the content review below, and it + # applies to every send path — dashboard, API and CLI. Drafts can be created, + # edited and kept freely. # # **Daily ceilings apply per recipient.** Each message a broadcast sends counts # against the channel's daily ceiling (see `POST /v1/messages`). Once the ceiling @@ -199,10 +190,15 @@ module Zavudev # **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. + # the content) and begins sending. An email broadcast sends as soon as the review + # passes it, unless the review asks for a person. Every other channel moves to + # `pending_admin_review` and waits for a person. A broadcast the review refuses + # lands on `rejected`: use PATCH to edit the content then call POST /retry-review, + # or escalate it for a manual review. + # + # A broadcast is read once, on its own text, rather than per recipient. While an + # account's sending is suspended its recipients fail individually with `errorCode` + # `SENDING_SUSPENDED`; see `POST /v1/messages`. # # Calling this on a broadcast that is already `approved` or `scheduled` sends or # reschedules it directly, since it has already been reviewed. Reserves the diff --git a/rbi/zavudev/resources/calls.rbi b/rbi/zavudev/resources/calls.rbi index 8fd778f..9709bb0 100644 --- a/rbi/zavudev/resources/calls.rbi +++ b/rbi/zavudev/resources/calls.rbi @@ -11,15 +11,16 @@ module Zavudev # **Requirements:** # # - The Voice Agents feature must be enabled for your team (otherwise `403`). - # - An account that has verified nothing may only call the phone numbers the - # project has verified (`403` with code `destination_not_verified`, and - # `details.verifiedNumbers` lists them), and at most 5 calls a day (`429` with - # code `daily_limit_exceeded`). A number is verified from the dashboard's - # Sandbox screen by sending the pre-filled WhatsApp message from that phone; the - # same verification covers SMS and calls. Verify your identity, add a payment - # method, settle a deposit or subscribe to call any destination. That raises the - # ceiling to 50 calls a day on Free; paid plans have no daily call ceiling. Full - # reference: https://docs.zavu.dev/concepts/sending-limits + # - An account may call any destination from its first minute, within the daily + # ceiling: 5 calls a day for an account that has verified nothing, 50 a day on + # Free once it has verified its identity, added a payment method, settled a + # deposit or subscribed. Paid plans have no daily call ceiling. Over it, `429` + # with code `daily_limit_exceeded`. Full reference: + # https://docs.zavu.dev/concepts/sending-limits + # - The call is read by Zavu's automated risk review before it is dialed, the way + # a message is (see `POST /v1/messages`). A call is never held for a person: one + # the review stops is failed rather than placed late. Repeated refusals suspend + # the account's calling, answered with `403` and code `sending_suspended`. # - The sender's agent must have `voice.enabled` set to `true`. # - Not available with test-mode API keys. # diff --git a/rbi/zavudev/resources/messages.rbi b/rbi/zavudev/resources/messages.rbi index 78b0274..9601472 100644 --- a/rbi/zavudev/resources/messages.rbi +++ b/rbi/zavudev/resources/messages.rbi @@ -107,21 +107,14 @@ module Zavudev # 100/day. Teams on earlier plans keep their original email quotas instead # - SMS and voice are billed per message from your balance on every plan # - # **Account verification and daily limits:** + # **Daily limits:** # - # - A brand-new account can send on every channel immediately, but `sms`, - # `sms_oneway` and `voice` reach only the phone numbers the project has - # verified. Sending elsewhere returns `403` with code - # `destination_not_verified`; `details.verifiedNumbers` lists the numbers that - # are reachable. A number is verified from the dashboard's Sandbox screen: - # generate a code and send the pre-filled WhatsApp message from that phone to - # Zavu's sandbox number. One verification covers WhatsApp, SMS and calls, up to - # 5 numbers per project. To send to any destination, do any one of these: verify - # your identity, add a payment method, settle a deposit, or subscribe to a paid - # plan. Business verification (KYB) is required for **one channel only**: - # `sms_oneway`. Without an approved KYB, one-way SMS returns `403` with code - # `kyb_required` and `details.dashboardUrl` pointing at `/kyb`, whatever the - # account has otherwise verified. No other channel asks for it + # - An account sends on every channel from its first minute, to any destination. + # Verification is not a permission to send: identity verification and business + # verification (KYB) raise the ceilings below and nothing else asks for them + # here. KYB is still required to register a 10DLC brand and campaign, which + # every US and Canadian (+1) SMS destination needs — a carrier rule, answered + # separately with `403 ten_dlc_required` # - Daily ceilings apply per channel group and rise with verification. An account # that has verified nothing: 25/day across `sms` + `sms_oneway`, 5/day for # `voice`, 100/day across WhatsApp, Telegram, Instagram and Messenger combined. @@ -133,13 +126,37 @@ module Zavudev # - The daily ceiling never reduces the monthly allowance: 100/day on the # conversational group still reaches the 2,000 monthly A2P messages Free # includes - # - Email needs no account verification here: a sender with a verified domain - # sends from day one, within the plan quota (100/day and 3,000/month on Free). - # Over the daily quota it returns `429` with code `daily_limit_exceeded`. Email - # broadcasts are the exception: they need the account past the unverified level, - # see `POST /v1/broadcasts/{broadcastId}/send` + # - Email: a sender with a verified domain sends from day one, within the plan + # quota (100/day and 3,000/month on Free). Over the daily quota it returns `429` + # with code `daily_limit_exceeded` # - Full reference: https://docs.zavu.dev/concepts/sending-limits # + # **Risk review:** Every outbound `sms`, `sms_oneway`, `email` and `voice` message + # is read before it is sent — the content, and how this account has been sending. + # What is checked is the message, not who you are. + # + # - A message can be **held** for a short review. It stays `queued` while it + # waits: no new status exists for this, and `MessageStatus` is unchanged. When + # it is approved it sends normally. + # - A message that is not approved moves to `failed` and fires `message.failed`. + # `errorCode` says which happened: `RISK_REJECTED` (a reviewer refused it), + # `RISK_REVIEW_EXPIRED` (the review window closed first — it is a couple of + # hours, because a code that arrives late is worse than one that does not + # arrive), or `RISK_BLOCKED` (refused outright, without a hold). An SMS that + # fails this way is not charged; the prepaid amount is returned. + # - A call is never held. `POST /v1/calls` fails a call the review stops rather + # than placing it hours late. + # - A message whose content cannot be read — the check is briefly unavailable — is + # held rather than sent. An account with an approved business verification is + # unaffected, and so is one that has verified something, already sends real + # traffic, and has a clean recent record. + # - Repeated refusals suspend an account's sending. While it is suspended every + # send is refused with `403` and code `sending_suspended`, + # `details.dashboardUrl` points at support, and a message already queued fails + # with `errorCode` `SENDING_SUSPENDED`. + # - A broadcast is read once, on the broadcast itself, rather than per recipient — + # see `POST /v1/broadcasts/{broadcastId}/send`. + # # **Email recipient pre-flight:** Email messages are validated automatically # before dispatch. Sends that would be a guaranteed hard bounce are failed instead # of sent, protecting your bounce rate: the message transitions to `failed` diff --git a/rbi/zavudev/resources/senders.rbi b/rbi/zavudev/resources/senders.rbi index 292148b..f8768b0 100644 --- a/rbi/zavudev/resources/senders.rbi +++ b/rbi/zavudev/resources/senders.rbi @@ -50,9 +50,7 @@ module Zavudev # Enable the one-way SMS channel (`sms_oneway`). Needs nothing else — no phone # number, no credential — so it is the fastest way to get a sender that can send. # Recipients cannot reply. Confirm with `sms_oneway` in the `channels` array on - # the response. Turning the channel on needs nothing, but SENDING on it requires - # an approved business verification (KYB): without one every send is refused with - # `403 kyb_required`. + # the response. enable_sms_oneway: nil, # Let this sender place and answer phone calls. Requires `phoneNumber`; enabling # it without one returns 400. Check the `channels` array on the response to @@ -140,9 +138,7 @@ module Zavudev email_receiving_enabled: nil, # Turn the one-way SMS channel on or off. Enabling needs nothing else and takes # effect immediately; disabling removes the channel from the sender. Confirm with - # the `channels` array on the response. Turning the channel on needs nothing, but - # SENDING on it requires an approved business verification (KYB): without one - # every send is refused with `403 kyb_required`. + # the `channels` array on the response. enable_sms_oneway: nil, # Turn the voice channel on or off. The sender must already have a phone number # provisioned for calls; enabling it otherwise returns 400 instead of storing a From d612f30b8f678064e0ed3f325b570aee3e0d2265 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 20 Sep 2026 13:42:02 +0000 Subject: [PATCH 14/15] feat(api): api update --- .stats.yml | 4 +- .../models/senders/agent/flow_trigger.rb | 43 +++++++++++--- .../models/senders/agent/flow_trigger.rbi | 59 ++++++++++++++++--- 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6c831ea..98dd734 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-42c22e5daf48fe8d621b322e6c9d23d33e160660c2938973490571673ea1ad0d.yml -openapi_spec_hash: f571c734ad0085b66d9a51a0a05d50c0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e6e18b26cd123fcd2dcaf3554352c9aa1aa890768d7928fa117382f0b4ca89aa.yml +openapi_spec_hash: 6bc9a1491dec8d8157477abeb4aa8f3c config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/lib/zavudev/models/senders/agent/flow_trigger.rb b/lib/zavudev/models/senders/agent/flow_trigger.rb index 4988e74..b8db01a 100644 --- a/lib/zavudev/models/senders/agent/flow_trigger.rb +++ b/lib/zavudev/models/senders/agent/flow_trigger.rb @@ -6,31 +6,60 @@ module Senders module Agent class FlowTrigger < Zavudev::Internal::Type::BaseModel # @!attribute type - # Type of trigger for a flow. + # What starts a flow. + # + # - `keyword`: the message contains one of the words listed in `keywords`. Plain + # substring matching, so a word inside another word still counts. + # - `intent`: the message MEANS what `intent` describes, whatever words it uses. + # - `always`: any message starts it. + # - `manual`: reserved. Nothing starts a `manual` flow today — it is accepted and + # stored, and no message or endpoint runs it. # # @return [Symbol, Zavudev::Models::Senders::Agent::FlowTrigger::Type] required :type, enum: -> { Zavudev::Senders::Agent::FlowTrigger::Type } # @!attribute intent - # Intent that triggers the flow (for intent type). + # One plain sentence describing what the contact wants, for `intent` triggers. Any + # language. + # + # The message is judged for meaning, not for words, so "kiero saber el presio" + # starts a flow whose intent is "quiere saber precios o cotizar", and "no quiero + # info de precios" starts nothing. + # + # A `keyword` or `always` flow with a higher `priority` is matched first and wins. + # At most 12 intent flows are considered per message, highest priority first. When + # the classification is unavailable or uncertain, the message is handled as if no + # intent matched, so a flow never starts on a guess. # # @return [String, nil] optional :intent, String # @!attribute keywords - # Keywords that trigger the flow (for keyword type). + # Words that start the flow, for `keyword` triggers. Matched as substrings, + # case-insensitively, against the whole message: a flow on `info` also starts on + # "no quiero info". Use `intent` when that matters. # # @return [Array, nil] optional :keywords, Zavudev::Internal::Type::ArrayOf[String] # @!method initialize(type:, intent: nil, keywords: nil) - # @param type [Symbol, Zavudev::Models::Senders::Agent::FlowTrigger::Type] Type of trigger for a flow. + # Some parameter documentations has been truncated, see + # {Zavudev::Models::Senders::Agent::FlowTrigger} for more details. # - # @param intent [String] Intent that triggers the flow (for intent type). + # @param type [Symbol, Zavudev::Models::Senders::Agent::FlowTrigger::Type] What starts a flow. # - # @param keywords [Array] Keywords that trigger the flow (for keyword type). + # @param intent [String] One plain sentence describing what the contact wants, for `intent` triggers. Any + # + # @param keywords [Array] Words that start the flow, for `keyword` triggers. Matched as substrings, case-i - # Type of trigger for a flow. + # What starts a flow. + # + # - `keyword`: the message contains one of the words listed in `keywords`. Plain + # substring matching, so a word inside another word still counts. + # - `intent`: the message MEANS what `intent` describes, whatever words it uses. + # - `always`: any message starts it. + # - `manual`: reserved. Nothing starts a `manual` flow today — it is accepted and + # stored, and no message or endpoint runs it. # # @see Zavudev::Models::Senders::Agent::FlowTrigger#type module Type diff --git a/rbi/zavudev/models/senders/agent/flow_trigger.rbi b/rbi/zavudev/models/senders/agent/flow_trigger.rbi index af19de2..d82c757 100644 --- a/rbi/zavudev/models/senders/agent/flow_trigger.rbi +++ b/rbi/zavudev/models/senders/agent/flow_trigger.rbi @@ -13,18 +13,37 @@ module Zavudev ) end - # Type of trigger for a flow. + # What starts a flow. + # + # - `keyword`: the message contains one of the words listed in `keywords`. Plain + # substring matching, so a word inside another word still counts. + # - `intent`: the message MEANS what `intent` describes, whatever words it uses. + # - `always`: any message starts it. + # - `manual`: reserved. Nothing starts a `manual` flow today — it is accepted and + # stored, and no message or endpoint runs it. sig { returns(Zavudev::Senders::Agent::FlowTrigger::Type::OrSymbol) } attr_accessor :type - # Intent that triggers the flow (for intent type). + # One plain sentence describing what the contact wants, for `intent` triggers. Any + # language. + # + # The message is judged for meaning, not for words, so "kiero saber el presio" + # starts a flow whose intent is "quiere saber precios o cotizar", and "no quiero + # info de precios" starts nothing. + # + # A `keyword` or `always` flow with a higher `priority` is matched first and wins. + # At most 12 intent flows are considered per message, highest priority first. When + # the classification is unavailable or uncertain, the message is handled as if no + # intent matched, so a flow never starts on a guess. sig { returns(T.nilable(String)) } attr_reader :intent sig { params(intent: String).void } attr_writer :intent - # Keywords that trigger the flow (for keyword type). + # Words that start the flow, for `keyword` triggers. Matched as substrings, + # case-insensitively, against the whole message: a flow on `info` also starts on + # "no quiero info". Use `intent` when that matters. sig { returns(T.nilable(T::Array[String])) } attr_reader :keywords @@ -39,11 +58,30 @@ module Zavudev ).returns(T.attached_class) end def self.new( - # Type of trigger for a flow. + # What starts a flow. + # + # - `keyword`: the message contains one of the words listed in `keywords`. Plain + # substring matching, so a word inside another word still counts. + # - `intent`: the message MEANS what `intent` describes, whatever words it uses. + # - `always`: any message starts it. + # - `manual`: reserved. Nothing starts a `manual` flow today — it is accepted and + # stored, and no message or endpoint runs it. type:, - # Intent that triggers the flow (for intent type). + # One plain sentence describing what the contact wants, for `intent` triggers. Any + # language. + # + # The message is judged for meaning, not for words, so "kiero saber el presio" + # starts a flow whose intent is "quiere saber precios o cotizar", and "no quiero + # info de precios" starts nothing. + # + # A `keyword` or `always` flow with a higher `priority` is matched first and wins. + # At most 12 intent flows are considered per message, highest priority first. When + # the classification is unavailable or uncertain, the message is handled as if no + # intent matched, so a flow never starts on a guess. intent: nil, - # Keywords that trigger the flow (for keyword type). + # Words that start the flow, for `keyword` triggers. Matched as substrings, + # case-insensitively, against the whole message: a flow on `info` also starts on + # "no quiero info". Use `intent` when that matters. keywords: nil ) end @@ -60,7 +98,14 @@ module Zavudev def to_hash end - # Type of trigger for a flow. + # What starts a flow. + # + # - `keyword`: the message contains one of the words listed in `keywords`. Plain + # substring matching, so a word inside another word still counts. + # - `intent`: the message MEANS what `intent` describes, whatever words it uses. + # - `always`: any message starts it. + # - `manual`: reserved. Nothing starts a `manual` flow today — it is accepted and + # stored, and no message or endpoint runs it. module Type extend Zavudev::Internal::Type::Enum From 46c89998e3af3c8de0ce8e6cc58ab298fdd0255e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 20 Sep 2026 13:42:29 +0000 Subject: [PATCH 15/15] release: 0.22.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ Gemfile.lock | 2 +- README.md | 2 +- lib/zavudev/version.rb | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 86b0e83..cb9d254 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.21.0" + ".": "0.22.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f578315..c5db85a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 0.22.0 (2026-09-20) + +Full Changelog: [v0.21.0...v0.22.0](https://github.com/zavudev/sdk-ruby/compare/v0.21.0...v0.22.0) + +### Features + +* **api:** api update ([d612f30](https://github.com/zavudev/sdk-ruby/commit/d612f30b8f678064e0ed3f325b570aee3e0d2265)) +* **api:** api update ([5f2c068](https://github.com/zavudev/sdk-ruby/commit/5f2c06855f0fd716bda8fa691dc56aa2bc0f1844)) +* **api:** api update ([39fe83b](https://github.com/zavudev/sdk-ruby/commit/39fe83bd5fc91daa66d34bab6a7fd06744623fa9)) +* **api:** api update ([e8f7c92](https://github.com/zavudev/sdk-ruby/commit/e8f7c926be5debc22f30565bd603f400c9501076)) +* **api:** api update ([827bce1](https://github.com/zavudev/sdk-ruby/commit/827bce1963625363e316b2d14e97895466eb0657)) +* **api:** api update ([03fffc7](https://github.com/zavudev/sdk-ruby/commit/03fffc7f3f4f1ffa3ccefd6da911001c3b81e87d)) +* **api:** api update ([b2e8ee7](https://github.com/zavudev/sdk-ruby/commit/b2e8ee71812c37479e29b8fa84b792a7424cfa2d)) +* **api:** api update ([bf90d0e](https://github.com/zavudev/sdk-ruby/commit/bf90d0e31f2c22ed1df4ab7ca80b419d149ac6cb)) +* **api:** api update ([5ef7668](https://github.com/zavudev/sdk-ruby/commit/5ef766844c3078b7c3a6930250ba4eb658028b26)) +* **api:** api update ([dac4810](https://github.com/zavudev/sdk-ruby/commit/dac48101d0b462154984874cb60992ca2879807e)) +* **api:** api update ([e9a5691](https://github.com/zavudev/sdk-ruby/commit/e9a56919080dd8e543f6b073f95bb25078fce358)) + ## 0.21.0 (2026-09-08) Full Changelog: [v0.20.0...v0.21.0](https://github.com/zavudev/sdk-ruby/compare/v0.20.0...v0.21.0) diff --git a/Gemfile.lock b/Gemfile.lock index 3b487f5..d243e8f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,7 +11,7 @@ GIT PATH remote: . specs: - zavudev (0.21.0) + zavudev (0.22.0) cgi connection_pool diff --git a/README.md b/README.md index 7db178a..d3c4d55 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ To use this gem, install via Bundler by adding the following to your application ```ruby -gem "zavudev", "~> 0.21.0" +gem "zavudev", "~> 0.22.0" ``` diff --git a/lib/zavudev/version.rb b/lib/zavudev/version.rb index ae50b9c..f66182d 100644 --- a/lib/zavudev/version.rb +++ b/lib/zavudev/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Zavudev - VERSION = "0.21.0" + VERSION = "0.22.0" end