From c1e89384e72a0a05e2a94819a2e8656872a804e2 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 10 Sep 2026 17:59:02 +0000
Subject: [PATCH 01/15] feat(api): api update
---
.stats.yml | 4 +-
.../Brands/BrandCreateResponseTest.cs | 7 ++++
.../Brands/BrandListPageResponseTest.cs | 11 +++++
.../Brands/BrandRetrieveResponseTest.cs | 7 ++++
.../Brands/BrandSubmitResponseTest.cs | 7 ++++
.../Brands/BrandSyncStatusResponseTest.cs | 7 ++++
.../Brands/BrandUpdateResponseTest.cs | 7 ++++
.../Number10dlc/Brands/TenDlcBrandTest.cs | 19 +++++++++
.../Models/Number10dlc/Brands/TenDlcBrand.cs | 41 +++++++++++++++++++
9 files changed, 108 insertions(+), 2 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/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandCreateResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandCreateResponseTest.cs
index 43c2c6d..c1f5bb6 100644
--- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandCreateResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandCreateResponseTest.cs
@@ -34,6 +34,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -65,6 +66,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -224,6 +229,7 @@ public void Validation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -263,6 +269,7 @@ public void CopyConstructor_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandListPageResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandListPageResponseTest.cs
index a156a87..7aeee6a 100644
--- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandListPageResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandListPageResponseTest.cs
@@ -37,6 +37,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -72,6 +73,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -119,6 +121,7 @@ public void SerializationRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -168,6 +171,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -210,6 +214,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -257,6 +262,7 @@ public void Validation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -300,6 +306,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -343,6 +350,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -385,6 +393,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -430,6 +439,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -474,6 +484,7 @@ public void CopyConstructor_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandRetrieveResponseTest.cs
index d9f55f4..77a7e8e 100644
--- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandRetrieveResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandRetrieveResponseTest.cs
@@ -34,6 +34,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -65,6 +66,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -224,6 +229,7 @@ public void Validation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -263,6 +269,7 @@ public void CopyConstructor_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSubmitResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSubmitResponseTest.cs
index 8be86d2..9bc2a8c 100644
--- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSubmitResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSubmitResponseTest.cs
@@ -34,6 +34,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -65,6 +66,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -224,6 +229,7 @@ public void Validation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -263,6 +269,7 @@ public void CopyConstructor_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSyncStatusResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSyncStatusResponseTest.cs
index 0352ea2..85f6922 100644
--- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSyncStatusResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSyncStatusResponseTest.cs
@@ -34,6 +34,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -65,6 +66,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -224,6 +229,7 @@ public void Validation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -263,6 +269,7 @@ public void CopyConstructor_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandUpdateResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandUpdateResponseTest.cs
index 5ecd2d8..c663036 100644
--- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandUpdateResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandUpdateResponseTest.cs
@@ -34,6 +34,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -65,6 +66,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -224,6 +229,7 @@ public void Validation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -263,6 +269,7 @@ public void CopyConstructor_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/TenDlcBrandTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/TenDlcBrandTest.cs
index 85a89e7..819ec60 100644
--- a/src/Zavudev.Tests/Models/Number10dlc/Brands/TenDlcBrandTest.cs
+++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/TenDlcBrandTest.cs
@@ -33,6 +33,7 @@ public void FieldRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -62,6 +63,7 @@ public void FieldRoundtrip_Works()
string expectedEin = "12-3456789";
string expectedFailureReason = "failureReason";
string expectedFirstName = "firstName";
+ string expectedIdentityStatus = "VERIFIED";
string expectedLastName = "lastName";
string expectedStockExchange = "stockExchange";
string expectedStockSymbol = "stockSymbol";
@@ -89,6 +91,7 @@ public void FieldRoundtrip_Works()
Assert.Equal(expectedEin, model.Ein);
Assert.Equal(expectedFailureReason, model.FailureReason);
Assert.Equal(expectedFirstName, model.FirstName);
+ Assert.Equal(expectedIdentityStatus, model.IdentityStatus);
Assert.Equal(expectedLastName, model.LastName);
Assert.Equal(expectedStockExchange, model.StockExchange);
Assert.Equal(expectedStockSymbol, model.StockSymbol);
@@ -122,6 +125,7 @@ public void SerializationRoundtrip_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -164,6 +168,7 @@ public void FieldRoundtripThroughSerialization_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -200,6 +205,7 @@ public void FieldRoundtripThroughSerialization_Works()
string expectedEin = "12-3456789";
string expectedFailureReason = "failureReason";
string expectedFirstName = "firstName";
+ string expectedIdentityStatus = "VERIFIED";
string expectedLastName = "lastName";
string expectedStockExchange = "stockExchange";
string expectedStockSymbol = "stockSymbol";
@@ -227,6 +233,7 @@ public void FieldRoundtripThroughSerialization_Works()
Assert.Equal(expectedEin, deserialized.Ein);
Assert.Equal(expectedFailureReason, deserialized.FailureReason);
Assert.Equal(expectedFirstName, deserialized.FirstName);
+ Assert.Equal(expectedIdentityStatus, deserialized.IdentityStatus);
Assert.Equal(expectedLastName, deserialized.LastName);
Assert.Equal(expectedStockExchange, deserialized.StockExchange);
Assert.Equal(expectedStockSymbol, deserialized.StockSymbol);
@@ -260,6 +267,7 @@ public void Validation_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -304,6 +312,8 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works()
Assert.False(model.RawData.ContainsKey("failureReason"));
Assert.Null(model.FirstName);
Assert.False(model.RawData.ContainsKey("firstName"));
+ Assert.Null(model.IdentityStatus);
+ Assert.False(model.RawData.ContainsKey("identityStatus"));
Assert.Null(model.LastName);
Assert.False(model.RawData.ContainsKey("lastName"));
Assert.Null(model.StockExchange);
@@ -368,6 +378,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
Ein = null,
FailureReason = null,
FirstName = null,
+ IdentityStatus = null,
LastName = null,
StockExchange = null,
StockSymbol = null,
@@ -388,6 +399,8 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
Assert.True(model.RawData.ContainsKey("failureReason"));
Assert.Null(model.FirstName);
Assert.True(model.RawData.ContainsKey("firstName"));
+ Assert.Null(model.IdentityStatus);
+ Assert.True(model.RawData.ContainsKey("identityStatus"));
Assert.Null(model.LastName);
Assert.True(model.RawData.ContainsKey("lastName"));
Assert.Null(model.StockExchange);
@@ -428,6 +441,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works()
Ein = null,
FailureReason = null,
FirstName = null,
+ IdentityStatus = null,
LastName = null,
StockExchange = null,
StockSymbol = null,
@@ -464,6 +478,7 @@ public void CopyConstructor_Works()
Ein = "12-3456789",
FailureReason = "failureReason",
FirstName = "firstName",
+ IdentityStatus = "VERIFIED",
LastName = "lastName",
StockExchange = "stockExchange",
StockSymbol = "stockSymbol",
@@ -548,7 +563,9 @@ public class StatusTest : TestBase
[InlineData(Status.Draft)]
[InlineData(Status.Pending)]
[InlineData(Status.Verified)]
+ [InlineData(Status.Unverified)]
[InlineData(Status.Rejected)]
+ [InlineData(Status.Failed)]
public void Validation_Works(Status rawValue)
{
// force implicit conversion because Theory can't do that for us
@@ -572,7 +589,9 @@ public void InvalidEnumValidationThrows_Works()
[InlineData(Status.Draft)]
[InlineData(Status.Pending)]
[InlineData(Status.Verified)]
+ [InlineData(Status.Unverified)]
[InlineData(Status.Rejected)]
+ [InlineData(Status.Failed)]
public void SerializationRoundtrip_Works(Status rawValue)
{
// force implicit conversion because Theory can't do that for us
diff --git a/src/Zavudev/Models/Number10dlc/Brands/TenDlcBrand.cs b/src/Zavudev/Models/Number10dlc/Brands/TenDlcBrand.cs
index c01be15..d596f2f 100644
--- a/src/Zavudev/Models/Number10dlc/Brands/TenDlcBrand.cs
+++ b/src/Zavudev/Models/Number10dlc/Brands/TenDlcBrand.cs
@@ -128,6 +128,15 @@ public required string 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.
///
public required ApiEnum Status
{
@@ -244,6 +253,22 @@ public string? FirstName
init { this._rawData.Set("firstName", value); }
}
+ ///
+ /// 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`.
+ ///
+ public string? IdentityStatus
+ {
+ get
+ {
+ this._rawData.Freeze();
+ return this._rawData.GetNullableClass("identityStatus");
+ }
+ init { this._rawData.Set("identityStatus", value); }
+ }
+
public string? LastName
{
get
@@ -327,6 +352,7 @@ public override void Validate()
_ = this.Ein;
_ = this.FailureReason;
_ = this.FirstName;
+ _ = this.IdentityStatus;
_ = this.LastName;
_ = this.StockExchange;
_ = this.StockSymbol;
@@ -428,6 +454,15 @@ JsonSerializerOptions options
///
/// 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.
///
[JsonConverter(typeof(StatusConverter))]
public enum Status
@@ -435,7 +470,9 @@ public enum Status
Draft,
Pending,
Verified,
+ Unverified,
Rejected,
+ Failed,
}
sealed class StatusConverter : JsonConverter
@@ -451,7 +488,9 @@ JsonSerializerOptions options
"draft" => Status.Draft,
"pending" => Status.Pending,
"verified" => Status.Verified,
+ "unverified" => Status.Unverified,
"rejected" => Status.Rejected,
+ "failed" => Status.Failed,
_ => (Status)(-1),
};
}
@@ -465,7 +504,9 @@ public override void Write(Utf8JsonWriter writer, Status value, JsonSerializerOp
Status.Draft => "draft",
Status.Pending => "pending",
Status.Verified => "verified",
+ Status.Unverified => "unverified",
Status.Rejected => "rejected",
+ Status.Failed => "failed",
_ => throw new ZavudevInvalidDataException(
string.Format("Invalid value '{0}' in {1}", value, nameof(value))
),
From 710ab9f1f328f1aeef304b058650140f0f0285db 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:21:37 +0000
Subject: [PATCH 02/15] feat(api): api update
---
.stats.yml | 4 +-
.../Addresses/AddressCreateParamsTest.cs | 32 ++--
.../PhoneNumbers/OwnedPhoneNumberTest.cs | 74 ++++++++
.../PhoneNumberListPageResponseTest.cs | 11 ++
.../PhoneNumberPurchaseParamsTest.cs | 133 +++++++++++++
.../PhoneNumberPurchaseResponseTest.cs | 7 +
.../PhoneNumberRequirementsParamsTest.cs | 21 ++-
.../PhoneNumberRetrieveResponseTest.cs | 7 +
.../PhoneNumbers/PhoneNumberStatusTest.cs | 4 +
.../PhoneNumberUpdateResponseTest.cs | 7 +
.../Services/AddressServiceTest.cs | 2 +
.../Services/PhoneNumberServiceTest.cs | 2 +-
src/Zavudev/Core/ModelBase.cs | 1 +
.../Models/Addresses/AddressCreateParams.cs | 70 ++++---
.../Models/Addresses/AddressDeleteParams.cs | 4 +-
.../Models/PhoneNumbers/OwnedPhoneNumber.cs | 97 ++++++++++
.../Models/PhoneNumbers/PhoneNumberPricing.cs | 6 +-
.../PhoneNumbers/PhoneNumberPurchaseParams.cs | 176 +++++++++++++++++-
.../PhoneNumberRequirementsParams.cs | 60 +++++-
.../Models/PhoneNumbers/PhoneNumberStatus.cs | 12 ++
.../PhoneNumbers/PhoneNumberUpdateParams.cs | 4 +-
.../Models/PhoneNumbers/Requirement.cs | 4 +-
.../Models/PhoneNumbers/RequirementType.cs | 3 +
.../Models/Senders/SenderCreateParams.cs | 6 +-
src/Zavudev/Services/IAddressService.cs | 9 +-
src/Zavudev/Services/IPhoneNumberService.cs | 64 +++++--
src/Zavudev/Services/PhoneNumberService.cs | 6 +-
27 files changed, 726 insertions(+), 100 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/src/Zavudev.Tests/Models/Addresses/AddressCreateParamsTest.cs b/src/Zavudev.Tests/Models/Addresses/AddressCreateParamsTest.cs
index a67d4b7..50d3c19 100644
--- a/src/Zavudev.Tests/Models/Addresses/AddressCreateParamsTest.cs
+++ b/src/Zavudev.Tests/Models/Addresses/AddressCreateParamsTest.cs
@@ -11,35 +11,35 @@ public void FieldRoundtrip_Works()
var parameters = new AddressCreateParams
{
CountryCode = "DE",
+ FirstName = "John",
+ LastName = "Doe",
Locality = "Berlin",
PostalCode = "10115",
StreetAddress = "123 Main St",
AdministrativeArea = "administrativeArea",
BusinessName = "businessName",
ExtendedAddress = "extendedAddress",
- FirstName = "John",
- LastName = "Doe",
};
string expectedCountryCode = "DE";
+ string expectedFirstName = "John";
+ string expectedLastName = "Doe";
string expectedLocality = "Berlin";
string expectedPostalCode = "10115";
string expectedStreetAddress = "123 Main St";
string expectedAdministrativeArea = "administrativeArea";
string expectedBusinessName = "businessName";
string expectedExtendedAddress = "extendedAddress";
- string expectedFirstName = "John";
- string expectedLastName = "Doe";
Assert.Equal(expectedCountryCode, parameters.CountryCode);
+ Assert.Equal(expectedFirstName, parameters.FirstName);
+ Assert.Equal(expectedLastName, parameters.LastName);
Assert.Equal(expectedLocality, parameters.Locality);
Assert.Equal(expectedPostalCode, parameters.PostalCode);
Assert.Equal(expectedStreetAddress, parameters.StreetAddress);
Assert.Equal(expectedAdministrativeArea, parameters.AdministrativeArea);
Assert.Equal(expectedBusinessName, parameters.BusinessName);
Assert.Equal(expectedExtendedAddress, parameters.ExtendedAddress);
- Assert.Equal(expectedFirstName, parameters.FirstName);
- Assert.Equal(expectedLastName, parameters.LastName);
}
[Fact]
@@ -48,6 +48,8 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works()
var parameters = new AddressCreateParams
{
CountryCode = "DE",
+ FirstName = "John",
+ LastName = "Doe",
Locality = "Berlin",
PostalCode = "10115",
StreetAddress = "123 Main St",
@@ -59,10 +61,6 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works()
Assert.False(parameters.RawBodyData.ContainsKey("businessName"));
Assert.Null(parameters.ExtendedAddress);
Assert.False(parameters.RawBodyData.ContainsKey("extendedAddress"));
- Assert.Null(parameters.FirstName);
- Assert.False(parameters.RawBodyData.ContainsKey("firstName"));
- Assert.Null(parameters.LastName);
- Assert.False(parameters.RawBodyData.ContainsKey("lastName"));
}
[Fact]
@@ -71,6 +69,8 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works()
var parameters = new AddressCreateParams
{
CountryCode = "DE",
+ FirstName = "John",
+ LastName = "Doe",
Locality = "Berlin",
PostalCode = "10115",
StreetAddress = "123 Main St",
@@ -79,8 +79,6 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works()
AdministrativeArea = null,
BusinessName = null,
ExtendedAddress = null,
- FirstName = null,
- LastName = null,
};
Assert.Null(parameters.AdministrativeArea);
@@ -89,10 +87,6 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works()
Assert.False(parameters.RawBodyData.ContainsKey("businessName"));
Assert.Null(parameters.ExtendedAddress);
Assert.False(parameters.RawBodyData.ContainsKey("extendedAddress"));
- Assert.Null(parameters.FirstName);
- Assert.False(parameters.RawBodyData.ContainsKey("firstName"));
- Assert.Null(parameters.LastName);
- Assert.False(parameters.RawBodyData.ContainsKey("lastName"));
}
[Fact]
@@ -101,6 +95,8 @@ public void Url_Works()
AddressCreateParams parameters = new()
{
CountryCode = "DE",
+ FirstName = "John",
+ LastName = "Doe",
Locality = "Berlin",
PostalCode = "10115",
StreetAddress = "123 Main St",
@@ -117,14 +113,14 @@ public void CopyConstructor_Works()
var parameters = new AddressCreateParams
{
CountryCode = "DE",
+ FirstName = "John",
+ LastName = "Doe",
Locality = "Berlin",
PostalCode = "10115",
StreetAddress = "123 Main St",
AdministrativeArea = "administrativeArea",
BusinessName = "businessName",
ExtendedAddress = "extendedAddress",
- FirstName = "John",
- LastName = "Doe",
};
AddressCreateParams copied = new(parameters);
diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/OwnedPhoneNumberTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/OwnedPhoneNumberTest.cs
index 1ae8af4..161645a 100644
--- a/src/Zavudev.Tests/Models/PhoneNumbers/OwnedPhoneNumberTest.cs
+++ b/src/Zavudev.Tests/Models/PhoneNumbers/OwnedPhoneNumberTest.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text.Json;
using Zavudev.Core;
+using Zavudev.Exceptions;
using Zavudev.Models.PhoneNumbers;
namespace Zavudev.Tests.Models.PhoneNumbers;
@@ -24,6 +25,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -42,6 +44,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
};
+ ApiEnum expectedRegulatoryStatus = RegulatoryStatus.Approved;
ApiEnum expectedStatus = PhoneNumberStatus.Active;
string expectedName = "name";
DateTimeOffset expectedNextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
@@ -57,6 +60,7 @@ public void FieldRoundtrip_Works()
Assert.Equal(expectedCreatedAt, model.CreatedAt);
Assert.Equal(expectedPhoneNumber, model.PhoneNumber);
Assert.Equal(expectedPricing, model.Pricing);
+ Assert.Equal(expectedRegulatoryStatus, model.RegulatoryStatus);
Assert.Equal(expectedStatus, model.Status);
Assert.Equal(expectedName, model.Name);
Assert.Equal(expectedNextRenewalDate, model.NextRenewalDate);
@@ -80,6 +84,7 @@ public void SerializationRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -112,6 +117,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -137,6 +143,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
};
+ ApiEnum expectedRegulatoryStatus = RegulatoryStatus.Approved;
ApiEnum expectedStatus = PhoneNumberStatus.Active;
string expectedName = "name";
DateTimeOffset expectedNextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
@@ -152,6 +159,7 @@ public void FieldRoundtripThroughSerialization_Works()
Assert.Equal(expectedCreatedAt, deserialized.CreatedAt);
Assert.Equal(expectedPhoneNumber, deserialized.PhoneNumber);
Assert.Equal(expectedPricing, deserialized.Pricing);
+ Assert.Equal(expectedRegulatoryStatus, deserialized.RegulatoryStatus);
Assert.Equal(expectedStatus, deserialized.Status);
Assert.Equal(expectedName, deserialized.Name);
Assert.Equal(expectedNextRenewalDate, deserialized.NextRenewalDate);
@@ -175,6 +183,7 @@ public void Validation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -201,6 +210,7 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
};
@@ -230,6 +240,7 @@ public void OptionalNonNullablePropertiesUnsetValidation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
};
@@ -252,6 +263,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
// Null should be interpreted as omitted for these properties
@@ -287,6 +299,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
// Null should be interpreted as omitted for these properties
@@ -315,6 +328,7 @@ public void CopyConstructor_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -327,3 +341,63 @@ public void CopyConstructor_Works()
Assert.Equal(model, copied);
}
}
+
+public class RegulatoryStatusTest : TestBase
+{
+ [Theory]
+ [InlineData(RegulatoryStatus.Approved)]
+ [InlineData(RegulatoryStatus.PendingReview)]
+ [InlineData(RegulatoryStatus.Rejected)]
+ public void Validation_Works(RegulatoryStatus rawValue)
+ {
+ // force implicit conversion because Theory can't do that for us
+ ApiEnum value = rawValue;
+ value.Validate();
+ }
+
+ [Fact]
+ public void InvalidEnumValidationThrows_Works()
+ {
+ var value = JsonSerializer.Deserialize>(
+ JsonSerializer.SerializeToElement("invalid value"),
+ ModelBase.SerializerOptions
+ );
+
+ Assert.NotNull(value);
+ Assert.Throws(() => value.Validate());
+ }
+
+ [Theory]
+ [InlineData(RegulatoryStatus.Approved)]
+ [InlineData(RegulatoryStatus.PendingReview)]
+ [InlineData(RegulatoryStatus.Rejected)]
+ public void SerializationRoundtrip_Works(RegulatoryStatus rawValue)
+ {
+ // force implicit conversion because Theory can't do that for us
+ ApiEnum value = rawValue;
+
+ string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions);
+ var deserialized = JsonSerializer.Deserialize>(
+ json,
+ ModelBase.SerializerOptions
+ );
+
+ Assert.Equal(value, deserialized);
+ }
+
+ [Fact]
+ public void InvalidEnumSerializationRoundtrip_Works()
+ {
+ var value = JsonSerializer.Deserialize>(
+ JsonSerializer.SerializeToElement("invalid value"),
+ ModelBase.SerializerOptions
+ );
+ string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions);
+ var deserialized = JsonSerializer.Deserialize>(
+ json,
+ ModelBase.SerializerOptions
+ );
+
+ Assert.Equal(value, deserialized);
+ }
+}
diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberListPageResponseTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberListPageResponseTest.cs
index 553206c..95c363c 100644
--- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberListPageResponseTest.cs
+++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberListPageResponseTest.cs
@@ -28,6 +28,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -53,6 +54,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -90,6 +92,7 @@ public void SerializationRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -129,6 +132,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -161,6 +165,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -198,6 +203,7 @@ public void Validation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -231,6 +237,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -264,6 +271,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -296,6 +304,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -331,6 +340,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -365,6 +375,7 @@ public void CopyConstructor_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseParamsTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseParamsTest.cs
index 467b80f..297d65d 100644
--- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseParamsTest.cs
+++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseParamsTest.cs
@@ -1,4 +1,7 @@
using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using Zavudev.Core;
using Zavudev.Models.PhoneNumbers;
namespace Zavudev.Tests.Models.PhoneNumbers;
@@ -12,13 +15,38 @@ public void FieldRoundtrip_Works()
{
PhoneNumber = "+15551234567",
Name = "Primary Line",
+ RegulatoryRequirements =
+ [
+ new()
+ {
+ FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc",
+ RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10",
+ },
+ ],
+ Type = PhoneNumberType.Local,
};
string expectedPhoneNumber = "+15551234567";
string expectedName = "Primary Line";
+ List expectedRegulatoryRequirements =
+ [
+ new()
+ {
+ FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc",
+ RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10",
+ },
+ ];
+ ApiEnum expectedType = PhoneNumberType.Local;
Assert.Equal(expectedPhoneNumber, parameters.PhoneNumber);
Assert.Equal(expectedName, parameters.Name);
+ Assert.NotNull(parameters.RegulatoryRequirements);
+ Assert.Equal(expectedRegulatoryRequirements.Count, parameters.RegulatoryRequirements.Count);
+ for (int i = 0; i < expectedRegulatoryRequirements.Count; i++)
+ {
+ Assert.Equal(expectedRegulatoryRequirements[i], parameters.RegulatoryRequirements[i]);
+ }
+ Assert.Equal(expectedType, parameters.Type);
}
[Fact]
@@ -28,6 +56,10 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works()
Assert.Null(parameters.Name);
Assert.False(parameters.RawBodyData.ContainsKey("name"));
+ Assert.Null(parameters.RegulatoryRequirements);
+ Assert.False(parameters.RawBodyData.ContainsKey("regulatoryRequirements"));
+ Assert.Null(parameters.Type);
+ Assert.False(parameters.RawBodyData.ContainsKey("type"));
}
[Fact]
@@ -39,10 +71,16 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works()
// Null should be interpreted as omitted for these properties
Name = null,
+ RegulatoryRequirements = null,
+ Type = null,
};
Assert.Null(parameters.Name);
Assert.False(parameters.RawBodyData.ContainsKey("name"));
+ Assert.Null(parameters.RegulatoryRequirements);
+ Assert.False(parameters.RawBodyData.ContainsKey("regulatoryRequirements"));
+ Assert.Null(parameters.Type);
+ Assert.False(parameters.RawBodyData.ContainsKey("type"));
}
[Fact]
@@ -62,6 +100,15 @@ public void CopyConstructor_Works()
{
PhoneNumber = "+15551234567",
Name = "Primary Line",
+ RegulatoryRequirements =
+ [
+ new()
+ {
+ FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc",
+ RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10",
+ },
+ ],
+ Type = PhoneNumberType.Local,
};
PhoneNumberPurchaseParams copied = new(parameters);
@@ -69,3 +116,89 @@ public void CopyConstructor_Works()
Assert.Equal(parameters, copied);
}
}
+
+public class RegulatoryRequirementTest : TestBase
+{
+ [Fact]
+ public void FieldRoundtrip_Works()
+ {
+ var model = new RegulatoryRequirement
+ {
+ FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc",
+ RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10",
+ };
+
+ string expectedFieldValue = "jd7x2k3m4n5p6q7r8s9t0abc";
+ string expectedRequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10";
+
+ Assert.Equal(expectedFieldValue, model.FieldValue);
+ Assert.Equal(expectedRequirementType, model.RequirementType);
+ }
+
+ [Fact]
+ public void SerializationRoundtrip_Works()
+ {
+ var model = new RegulatoryRequirement
+ {
+ FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc",
+ RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10",
+ };
+
+ string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions);
+ var deserialized = JsonSerializer.Deserialize(
+ json,
+ ModelBase.SerializerOptions
+ );
+
+ Assert.Equal(model, deserialized);
+ }
+
+ [Fact]
+ public void FieldRoundtripThroughSerialization_Works()
+ {
+ var model = new RegulatoryRequirement
+ {
+ FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc",
+ RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10",
+ };
+
+ string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions);
+ var deserialized = JsonSerializer.Deserialize(
+ element,
+ ModelBase.SerializerOptions
+ );
+ Assert.NotNull(deserialized);
+
+ string expectedFieldValue = "jd7x2k3m4n5p6q7r8s9t0abc";
+ string expectedRequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10";
+
+ Assert.Equal(expectedFieldValue, deserialized.FieldValue);
+ Assert.Equal(expectedRequirementType, deserialized.RequirementType);
+ }
+
+ [Fact]
+ public void Validation_Works()
+ {
+ var model = new RegulatoryRequirement
+ {
+ FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc",
+ RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10",
+ };
+
+ model.Validate();
+ }
+
+ [Fact]
+ public void CopyConstructor_Works()
+ {
+ var model = new RegulatoryRequirement
+ {
+ FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc",
+ RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10",
+ };
+
+ RegulatoryRequirement copied = new(model);
+
+ Assert.Equal(model, copied);
+ }
+}
diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseResponseTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseResponseTest.cs
index 3b78216..f37a293 100644
--- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseResponseTest.cs
+++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseResponseTest.cs
@@ -25,6 +25,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -46,6 +47,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -74,6 +76,7 @@ public void SerializationRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -109,6 +112,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -137,6 +141,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -165,6 +170,7 @@ public void Validation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -194,6 +200,7 @@ public void CopyConstructor_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRequirementsParamsTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRequirementsParamsTest.cs
index 1f4a908..922e0c5 100644
--- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRequirementsParamsTest.cs
+++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRequirementsParamsTest.cs
@@ -12,21 +12,28 @@ public void FieldRoundtrip_Works()
var parameters = new PhoneNumberRequirementsParams
{
CountryCode = "xx",
+ PhoneNumber = "phoneNumber",
Type = PhoneNumberType.Local,
};
string expectedCountryCode = "xx";
+ string expectedPhoneNumber = "phoneNumber";
ApiEnum expectedType = PhoneNumberType.Local;
Assert.Equal(expectedCountryCode, parameters.CountryCode);
+ Assert.Equal(expectedPhoneNumber, parameters.PhoneNumber);
Assert.Equal(expectedType, parameters.Type);
}
[Fact]
public void OptionalNonNullableParamsUnsetAreNotSet_Works()
{
- var parameters = new PhoneNumberRequirementsParams { CountryCode = "xx" };
+ var parameters = new PhoneNumberRequirementsParams { };
+ Assert.Null(parameters.CountryCode);
+ Assert.False(parameters.RawQueryData.ContainsKey("countryCode"));
+ Assert.Null(parameters.PhoneNumber);
+ Assert.False(parameters.RawQueryData.ContainsKey("phoneNumber"));
Assert.Null(parameters.Type);
Assert.False(parameters.RawQueryData.ContainsKey("type"));
}
@@ -36,12 +43,16 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works()
{
var parameters = new PhoneNumberRequirementsParams
{
- CountryCode = "xx",
-
// Null should be interpreted as omitted for these properties
+ CountryCode = null,
+ PhoneNumber = null,
Type = null,
};
+ Assert.Null(parameters.CountryCode);
+ Assert.False(parameters.RawQueryData.ContainsKey("countryCode"));
+ Assert.Null(parameters.PhoneNumber);
+ Assert.False(parameters.RawQueryData.ContainsKey("phoneNumber"));
Assert.Null(parameters.Type);
Assert.False(parameters.RawQueryData.ContainsKey("type"));
}
@@ -52,6 +63,7 @@ public void Url_Works()
PhoneNumberRequirementsParams parameters = new()
{
CountryCode = "xx",
+ PhoneNumber = "phoneNumber",
Type = PhoneNumberType.Local,
};
@@ -60,7 +72,7 @@ public void Url_Works()
Assert.True(
TestBase.UrisEqual(
new Uri(
- "https://api.zavu.dev/v1/phone-numbers/requirements?countryCode=xx&type=local"
+ "https://api.zavu.dev/v1/phone-numbers/requirements?countryCode=xx&phoneNumber=phoneNumber&type=local"
),
url
)
@@ -73,6 +85,7 @@ public void CopyConstructor_Works()
var parameters = new PhoneNumberRequirementsParams
{
CountryCode = "xx",
+ PhoneNumber = "phoneNumber",
Type = PhoneNumberType.Local,
};
diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRetrieveResponseTest.cs
index 3e564c9..09d4c92 100644
--- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRetrieveResponseTest.cs
+++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRetrieveResponseTest.cs
@@ -25,6 +25,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -46,6 +47,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -74,6 +76,7 @@ public void SerializationRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -109,6 +112,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -137,6 +141,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -165,6 +170,7 @@ public void Validation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -194,6 +200,7 @@ public void CopyConstructor_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberStatusTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberStatusTest.cs
index 3b192ae..c9d5823 100644
--- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberStatusTest.cs
+++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberStatusTest.cs
@@ -11,6 +11,8 @@ public class PhoneNumberStatusTest : TestBase
[InlineData(PhoneNumberStatus.Active)]
[InlineData(PhoneNumberStatus.Suspended)]
[InlineData(PhoneNumberStatus.Pending)]
+ [InlineData(PhoneNumberStatus.Releasing)]
+ [InlineData(PhoneNumberStatus.Released)]
public void Validation_Works(PhoneNumberStatus rawValue)
{
// force implicit conversion because Theory can't do that for us
@@ -34,6 +36,8 @@ public void InvalidEnumValidationThrows_Works()
[InlineData(PhoneNumberStatus.Active)]
[InlineData(PhoneNumberStatus.Suspended)]
[InlineData(PhoneNumberStatus.Pending)]
+ [InlineData(PhoneNumberStatus.Releasing)]
+ [InlineData(PhoneNumberStatus.Released)]
public void SerializationRoundtrip_Works(PhoneNumberStatus rawValue)
{
// force implicit conversion because Theory can't do that for us
diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberUpdateResponseTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberUpdateResponseTest.cs
index 3bcc4a5..803ea6d 100644
--- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberUpdateResponseTest.cs
+++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberUpdateResponseTest.cs
@@ -25,6 +25,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -46,6 +47,7 @@ public void FieldRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -74,6 +76,7 @@ public void SerializationRoundtrip_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -109,6 +112,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -137,6 +141,7 @@ public void FieldRoundtripThroughSerialization_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -165,6 +170,7 @@ public void Validation_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -194,6 +200,7 @@ public void CopyConstructor_Works()
MonthlyPrice = 0,
UpfrontCost = 0,
},
+ RegulatoryStatus = RegulatoryStatus.Approved,
Status = PhoneNumberStatus.Active,
Name = "name",
NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Services/AddressServiceTest.cs b/src/Zavudev.Tests/Services/AddressServiceTest.cs
index 6ac263d..78aeb47 100644
--- a/src/Zavudev.Tests/Services/AddressServiceTest.cs
+++ b/src/Zavudev.Tests/Services/AddressServiceTest.cs
@@ -11,6 +11,8 @@ public async Task Create_Works()
new()
{
CountryCode = "DE",
+ FirstName = "John",
+ LastName = "Doe",
Locality = "Berlin",
PostalCode = "10115",
StreetAddress = "123 Main St",
diff --git a/src/Zavudev.Tests/Services/PhoneNumberServiceTest.cs b/src/Zavudev.Tests/Services/PhoneNumberServiceTest.cs
index 00de373..1775a58 100644
--- a/src/Zavudev.Tests/Services/PhoneNumberServiceTest.cs
+++ b/src/Zavudev.Tests/Services/PhoneNumberServiceTest.cs
@@ -60,7 +60,7 @@ await this.client.PhoneNumbers.Release(
public async Task Requirements_Works()
{
var response = await this.client.PhoneNumbers.Requirements(
- new() { CountryCode = "xx" },
+ new(),
TestContext.Current.CancellationToken
);
response.Validate();
diff --git a/src/Zavudev/Core/ModelBase.cs b/src/Zavudev/Core/ModelBase.cs
index 40d3e4f..1db4b4c 100644
--- a/src/Zavudev/Core/ModelBase.cs
+++ b/src/Zavudev/Core/ModelBase.cs
@@ -94,6 +94,7 @@ protected ModelBase(ModelBase modelBase)
new ApiEnumConverter(),
new ApiEnumConverter(),
new ApiEnumConverter(),
+ new ApiEnumConverter(),
new ApiEnumConverter(),
new ApiEnumConverter(),
new ApiEnumConverter(),
diff --git a/src/Zavudev/Models/Addresses/AddressCreateParams.cs b/src/Zavudev/Models/Addresses/AddressCreateParams.cs
index efa1bfc..4f3d881 100644
--- a/src/Zavudev/Models/Addresses/AddressCreateParams.cs
+++ b/src/Zavudev/Models/Addresses/AddressCreateParams.cs
@@ -10,8 +10,8 @@
namespace Zavudev.Models.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`.
///
/// NOTE: Do not inherit from this type outside the SDK unless you're okay with
/// breaking changes in non-major versions. We may add new methods in the future that
@@ -35,6 +35,32 @@ public required string CountryCode
init { this._rawBodyData.Set("countryCode", value); }
}
+ ///
+ /// First name of the person the address is registered to.
+ ///
+ public required string FirstName
+ {
+ get
+ {
+ this._rawBodyData.Freeze();
+ return this._rawBodyData.GetNotNullClass("firstName");
+ }
+ init { this._rawBodyData.Set("firstName", value); }
+ }
+
+ ///
+ /// Last name of the person the address is registered to.
+ ///
+ public required string LastName
+ {
+ get
+ {
+ this._rawBodyData.Freeze();
+ return this._rawBodyData.GetNotNullClass("lastName");
+ }
+ init { this._rawBodyData.Set("lastName", value); }
+ }
+
public required string Locality
{
get
@@ -83,6 +109,10 @@ public string? AdministrativeArea
}
}
+ ///
+ /// Business name, when the address belongs to a business. Defaults to the person's
+ /// full name.
+ ///
public string? BusinessName
{
get
@@ -119,42 +149,6 @@ public string? ExtendedAddress
}
}
- public string? FirstName
- {
- get
- {
- this._rawBodyData.Freeze();
- return this._rawBodyData.GetNullableClass("firstName");
- }
- init
- {
- if (value == null)
- {
- return;
- }
-
- this._rawBodyData.Set("firstName", value);
- }
- }
-
- public string? LastName
- {
- get
- {
- this._rawBodyData.Freeze();
- return this._rawBodyData.GetNullableClass("lastName");
- }
- init
- {
- if (value == null)
- {
- return;
- }
-
- this._rawBodyData.Set("lastName", value);
- }
- }
-
public AddressCreateParams() { }
#pragma warning disable CS8618
diff --git a/src/Zavudev/Models/Addresses/AddressDeleteParams.cs b/src/Zavudev/Models/Addresses/AddressDeleteParams.cs
index 3e6a742..7c943fc 100644
--- a/src/Zavudev/Models/Addresses/AddressDeleteParams.cs
+++ b/src/Zavudev/Models/Addresses/AddressDeleteParams.cs
@@ -9,7 +9,9 @@
namespace Zavudev.Models.Addresses;
///
-/// 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.
///
/// NOTE: Do not inherit from this type outside the SDK unless you're okay with
/// breaking changes in non-major versions. We may add new methods in the future that
diff --git a/src/Zavudev/Models/PhoneNumbers/OwnedPhoneNumber.cs b/src/Zavudev/Models/PhoneNumbers/OwnedPhoneNumber.cs
index b8ba0eb..79679eb 100644
--- a/src/Zavudev/Models/PhoneNumbers/OwnedPhoneNumber.cs
+++ b/src/Zavudev/Models/PhoneNumbers/OwnedPhoneNumber.cs
@@ -6,6 +6,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Zavudev.Core;
+using Zavudev.Exceptions;
namespace Zavudev.Models.PhoneNumbers;
@@ -68,6 +69,39 @@ public required OwnedPhoneNumberPricing Pricing
init { this._rawData.Set("pricing", value); }
}
+ ///
+ /// 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.
+ ///
+ public required ApiEnum RegulatoryStatus
+ {
+ get
+ {
+ this._rawData.Freeze();
+ return this._rawData.GetNotNullClass>(
+ "regulatoryStatus"
+ );
+ }
+ init { this._rawData.Set("regulatoryStatus", value); }
+ }
+
+ ///
+ /// 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.
+ ///
public required ApiEnum Status
{
get
@@ -164,6 +198,7 @@ public override void Validate()
_ = this.CreatedAt;
_ = this.PhoneNumber;
this.Pricing.Validate();
+ this.RegulatoryStatus.Validate();
this.Status.Validate();
_ = this.Name;
_ = this.NextRenewalDate;
@@ -207,3 +242,65 @@ class OwnedPhoneNumberFromRaw : IFromRawJson
public OwnedPhoneNumber FromRawUnchecked(IReadOnlyDictionary rawData) =>
OwnedPhoneNumber.FromRawUnchecked(rawData);
}
+
+///
+/// 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.
+///
+[JsonConverter(typeof(RegulatoryStatusConverter))]
+public enum RegulatoryStatus
+{
+ Approved,
+ PendingReview,
+ Rejected,
+}
+
+sealed class RegulatoryStatusConverter : JsonConverter
+{
+ public override RegulatoryStatus Read(
+ ref Utf8JsonReader reader,
+ Type typeToConvert,
+ JsonSerializerOptions options
+ )
+ {
+ return JsonSerializer.Deserialize(ref reader, options) switch
+ {
+ "approved" => RegulatoryStatus.Approved,
+ "pending_review" => RegulatoryStatus.PendingReview,
+ "rejected" => RegulatoryStatus.Rejected,
+ _ => (RegulatoryStatus)(-1),
+ };
+ }
+
+ public override void Write(
+ Utf8JsonWriter writer,
+ RegulatoryStatus value,
+ JsonSerializerOptions options
+ )
+ {
+ JsonSerializer.Serialize(
+ writer,
+ value switch
+ {
+ RegulatoryStatus.Approved => "approved",
+ RegulatoryStatus.PendingReview => "pending_review",
+ RegulatoryStatus.Rejected => "rejected",
+ _ => throw new ZavudevInvalidDataException(
+ string.Format("Invalid value '{0}' in {1}", value, nameof(value))
+ ),
+ },
+ options
+ );
+ }
+}
diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberPricing.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberPricing.cs
index e935103..1e1ddbd 100644
--- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberPricing.cs
+++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberPricing.cs
@@ -11,9 +11,9 @@ namespace Zavudev.Models.PhoneNumbers;
public sealed record class PhoneNumberPricing : JsonModel
{
///
- /// 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.
///
public bool? IsFreeEligible
{
diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberPurchaseParams.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberPurchaseParams.cs
index a0e5286..422f0d4 100644
--- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberPurchaseParams.cs
+++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberPurchaseParams.cs
@@ -1,21 +1,51 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Text;
using System.Text.Json;
+using System.Text.Json.Serialization;
using Zavudev.Core;
namespace Zavudev.Models.PhoneNumbers;
///
/// 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.
///
/// NOTE: Do not inherit from this type outside the SDK unless you're okay with
/// breaking changes in non-major versions. We may add new methods in the future that
@@ -63,6 +93,64 @@ public string? 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.
+ ///
+ public IReadOnlyList? RegulatoryRequirements
+ {
+ get
+ {
+ this._rawBodyData.Freeze();
+ return this._rawBodyData.GetNullableStruct>(
+ "regulatoryRequirements"
+ );
+ }
+ init
+ {
+ if (value == null)
+ {
+ return;
+ }
+
+ this._rawBodyData.Set?>(
+ "regulatoryRequirements",
+ value == null ? null : ImmutableArray.ToImmutableArray(value)
+ );
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ public ApiEnum? Type
+ {
+ get
+ {
+ this._rawBodyData.Freeze();
+ return this._rawBodyData.GetNullableClass>("type");
+ }
+ init
+ {
+ if (value == null)
+ {
+ return;
+ }
+
+ this._rawBodyData.Set("type", value);
+ }
+ }
+
public PhoneNumberPurchaseParams() { }
#pragma warning disable CS8618
@@ -172,3 +260,81 @@ public override int GetHashCode()
return 0;
}
}
+
+[JsonConverter(typeof(JsonModelConverter))]
+public sealed record class RegulatoryRequirement : JsonModel
+{
+ ///
+ /// 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.
+ ///
+ public required string FieldValue
+ {
+ get
+ {
+ this._rawData.Freeze();
+ return this._rawData.GetNotNullClass("fieldValue");
+ }
+ init { this._rawData.Set("fieldValue", value); }
+ }
+
+ ///
+ /// A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each
+ /// id may appear only once.
+ ///
+ public required string RequirementType
+ {
+ get
+ {
+ this._rawData.Freeze();
+ return this._rawData.GetNotNullClass("requirementType");
+ }
+ init { this._rawData.Set("requirementType", value); }
+ }
+
+ ///
+ public override void Validate()
+ {
+ _ = this.FieldValue;
+ _ = this.RequirementType;
+ }
+
+ public RegulatoryRequirement() { }
+
+#pragma warning disable CS8618
+ [SetsRequiredMembers]
+ public RegulatoryRequirement(RegulatoryRequirement regulatoryRequirement)
+ : base(regulatoryRequirement) { }
+#pragma warning restore CS8618
+
+ public RegulatoryRequirement(IReadOnlyDictionary rawData)
+ {
+ this._rawData = new(rawData);
+ }
+
+#pragma warning disable CS8618
+ [SetsRequiredMembers]
+ RegulatoryRequirement(FrozenDictionary rawData)
+ {
+ this._rawData = new(rawData);
+ }
+#pragma warning restore CS8618
+
+ ///
+ public static RegulatoryRequirement FromRawUnchecked(
+ IReadOnlyDictionary rawData
+ )
+ {
+ return new(FrozenDictionary.ToFrozenDictionary(rawData));
+ }
+}
+
+class RegulatoryRequirementFromRaw : IFromRawJson
+{
+ ///
+ public RegulatoryRequirement FromRawUnchecked(
+ IReadOnlyDictionary rawData
+ ) => RegulatoryRequirement.FromRawUnchecked(rawData);
+}
diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberRequirementsParams.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberRequirementsParams.cs
index bc2229f..29338c5 100644
--- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberRequirementsParams.cs
+++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberRequirementsParams.cs
@@ -9,9 +9,20 @@
namespace Zavudev.Models.PhoneNumbers;
///
-/// 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.
///
/// NOTE: Do not inherit from this type outside the SDK unless you're okay with
/// breaking changes in non-major versions. We may add new methods in the future that
@@ -20,20 +31,53 @@ namespace Zavudev.Models.PhoneNumbers;
public record class PhoneNumberRequirementsParams : ParamsBase
{
///
- /// Two-letter ISO country code.
+ /// Two-letter ISO country code. Required unless `phoneNumber` is given.
///
- public required string CountryCode
+ public string? CountryCode
{
get
{
this._rawQueryData.Freeze();
- return this._rawQueryData.GetNotNullClass("countryCode");
+ return this._rawQueryData.GetNullableClass("countryCode");
+ }
+ init
+ {
+ if (value == null)
+ {
+ return;
+ }
+
+ this._rawQueryData.Set("countryCode", value);
+ }
+ }
+
+ ///
+ /// 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`.
+ ///
+ public string? PhoneNumber
+ {
+ get
+ {
+ this._rawQueryData.Freeze();
+ return this._rawQueryData.GetNullableClass("phoneNumber");
+ }
+ init
+ {
+ if (value == null)
+ {
+ return;
+ }
+
+ this._rawQueryData.Set("phoneNumber", value);
}
- init { this._rawQueryData.Set("countryCode", value); }
}
///
- /// 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.
///
public ApiEnum? Type
{
diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberStatus.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberStatus.cs
index 824fad7..7637ada 100644
--- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberStatus.cs
+++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberStatus.cs
@@ -5,12 +5,20 @@
namespace Zavudev.Models.PhoneNumbers;
+///
+/// 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.
+///
[JsonConverter(typeof(PhoneNumberStatusConverter))]
public enum PhoneNumberStatus
{
Active,
Suspended,
Pending,
+ Releasing,
+ Released,
}
sealed class PhoneNumberStatusConverter : JsonConverter
@@ -26,6 +34,8 @@ JsonSerializerOptions options
"active" => PhoneNumberStatus.Active,
"suspended" => PhoneNumberStatus.Suspended,
"pending" => PhoneNumberStatus.Pending,
+ "releasing" => PhoneNumberStatus.Releasing,
+ "released" => PhoneNumberStatus.Released,
_ => (PhoneNumberStatus)(-1),
};
}
@@ -43,6 +53,8 @@ JsonSerializerOptions options
PhoneNumberStatus.Active => "active",
PhoneNumberStatus.Suspended => "suspended",
PhoneNumberStatus.Pending => "pending",
+ PhoneNumberStatus.Releasing => "releasing",
+ PhoneNumberStatus.Released => "released",
_ => throw new ZavudevInvalidDataException(
string.Format("Invalid value '{0}' in {1}", value, nameof(value))
),
diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberUpdateParams.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberUpdateParams.cs
index 5ba796c..8ce36d9 100644
--- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberUpdateParams.cs
+++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberUpdateParams.cs
@@ -40,7 +40,9 @@ public string? 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.
///
public string? SenderID
{
diff --git a/src/Zavudev/Models/PhoneNumbers/Requirement.cs b/src/Zavudev/Models/PhoneNumbers/Requirement.cs
index 39cbd2a..cf598a6 100644
--- a/src/Zavudev/Models/PhoneNumbers/Requirement.cs
+++ b/src/Zavudev/Models/PhoneNumbers/Requirement.cs
@@ -9,7 +9,9 @@
namespace Zavudev.Models.PhoneNumbers;
///
-/// 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).
///
[JsonConverter(typeof(JsonModelConverter))]
public sealed record class Requirement : JsonModel
diff --git a/src/Zavudev/Models/PhoneNumbers/RequirementType.cs b/src/Zavudev/Models/PhoneNumbers/RequirementType.cs
index c997e99..6db9129 100644
--- a/src/Zavudev/Models/PhoneNumbers/RequirementType.cs
+++ b/src/Zavudev/Models/PhoneNumbers/RequirementType.cs
@@ -13,6 +13,9 @@ namespace Zavudev.Models.PhoneNumbers;
[JsonConverter(typeof(JsonModelConverter))]
public sealed record class RequirementType : JsonModel
{
+ ///
+ /// Send this as `requirementType` in `regulatoryRequirements` when purchasing.
+ ///
public required string ID
{
get
diff --git a/src/Zavudev/Models/Senders/SenderCreateParams.cs b/src/Zavudev/Models/Senders/SenderCreateParams.cs
index 28dee3d..2bdd9ae 100644
--- a/src/Zavudev/Models/Senders/SenderCreateParams.cs
+++ b/src/Zavudev/Models/Senders/SenderCreateParams.cs
@@ -176,8 +176,10 @@ public bool? EnableVoice
/// 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.
+ /// the project 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.
///
public string? PhoneNumber
{
diff --git a/src/Zavudev/Services/IAddressService.cs b/src/Zavudev/Services/IAddressService.cs
index 07f6d5a..27c4619 100644
--- a/src/Zavudev/Services/IAddressService.cs
+++ b/src/Zavudev/Services/IAddressService.cs
@@ -27,8 +27,9 @@ public interface IAddressService
IAddressService WithOptions(Func modifier);
///
- /// 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`.
///
Task Create(
AddressCreateParams parameters,
@@ -59,7 +60,9 @@ Task List(
);
///
- /// 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.
///
Task Delete(AddressDeleteParams parameters, CancellationToken cancellationToken = default);
diff --git a/src/Zavudev/Services/IPhoneNumberService.cs b/src/Zavudev/Services/IPhoneNumberService.cs
index 31f4435..bd113e1 100644
--- a/src/Zavudev/Services/IPhoneNumberService.cs
+++ b/src/Zavudev/Services/IPhoneNumberService.cs
@@ -66,11 +66,41 @@ Task List(
///
/// 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.
///
Task Purchase(
PhoneNumberPurchaseParams parameters,
@@ -93,12 +123,24 @@ Task Release(
);
///
- /// 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.
///
Task Requirements(
- PhoneNumberRequirementsParams parameters,
+ PhoneNumberRequirementsParams? parameters = null,
CancellationToken cancellationToken = default
);
@@ -192,10 +234,10 @@ Task Release(
///
/// Returns a raw HTTP response for get /v1/phone-numbers/requirements, but is otherwise the
- /// same as .
+ /// same as .
///
Task> Requirements(
- PhoneNumberRequirementsParams parameters,
+ PhoneNumberRequirementsParams? parameters = null,
CancellationToken cancellationToken = default
);
diff --git a/src/Zavudev/Services/PhoneNumberService.cs b/src/Zavudev/Services/PhoneNumberService.cs
index b1eb811..e94f6fb 100644
--- a/src/Zavudev/Services/PhoneNumberService.cs
+++ b/src/Zavudev/Services/PhoneNumberService.cs
@@ -130,7 +130,7 @@ await this.Release(parameters with { PhoneNumberID = phoneNumberID }, cancellati
///
public async Task Requirements(
- PhoneNumberRequirementsParams parameters,
+ PhoneNumberRequirementsParams? parameters = null,
CancellationToken cancellationToken = default
)
{
@@ -352,10 +352,12 @@ public Task Release(
///
public async Task> Requirements(
- PhoneNumberRequirementsParams parameters,
+ PhoneNumberRequirementsParams? parameters = null,
CancellationToken cancellationToken = default
)
{
+ parameters ??= new();
+
HttpRequest request = new()
{
Method = HttpMethod.Get,
From d1f6cf57b47f3930723f85989cc90bbe2c1a5c5c 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:47:27 +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 8d1cf600e13e038e7da9097d0c44ab91f1ce8c48 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:20 +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 f26d51fc4dc8e3b1a2a8ae21f4b6dfe7d3c3dab3 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:18 +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 01367bbf6af82761f861601a214216e503fdd481 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:21:31 +0000
Subject: [PATCH 06/15] feat(api): api update
---
.stats.yml | 4 ++--
src/Zavudev/Models/Senders/WebhookEvent.cs | 19 ++++++++++++-------
2 files changed, 14 insertions(+), 9 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/src/Zavudev/Models/Senders/WebhookEvent.cs b/src/Zavudev/Models/Senders/WebhookEvent.cs
index f3103b5..0279052 100644
--- a/src/Zavudev/Models/Senders/WebhookEvent.cs
+++ b/src/Zavudev/Models/Senders/WebhookEvent.cs
@@ -43,13 +43,18 @@ namespace Zavudev.Models.Senders;
/// or `email` key, `channel`, `firstMessageId`, `firstMessageText`, and `profileName`.
/// - `template.status_changed`: WhatsApp template approval status changed
///
-/// **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.
+/// **Partner events:** - `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 `transcriptAvailable`.
From 5456b2c8998185dff2f91bf699cc506f32c02864 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:57:45 +0000
Subject: [PATCH 07/15] feat(api): api update
---
.stats.yml | 4 ++--
src/Zavudev/Models/Senders/Sender.cs | 8 +++++---
2 files changed, 7 insertions(+), 5 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/src/Zavudev/Models/Senders/Sender.cs b/src/Zavudev/Models/Senders/Sender.cs
index d6f9706..c452bc8 100644
--- a/src/Zavudev/Models/Senders/Sender.cs
+++ b/src/Zavudev/Models/Senders/Sender.cs
@@ -46,10 +46,12 @@ public required string PhoneNumber
}
///
- /// Channels this sender can actually send on right now, computed from its configuration.
+ /// 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. Check this rather than inferring capability
- /// from phoneNumber or emailAddress.
+ /// 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`.
///
public IReadOnlyList? Channels
{
From 415d31dbddf1211b0ce0fe66310f5c1e453c2de4 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:04 +0000
Subject: [PATCH 08/15] feat(api): api update
---
.stats.yml | 4 ++--
src/Zavudev/Models/Senders/SenderCreateParams.cs | 6 ++++--
src/Zavudev/Models/Senders/SenderUpdateParams.cs | 5 ++++-
3 files changed, 10 insertions(+), 5 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/src/Zavudev/Models/Senders/SenderCreateParams.cs b/src/Zavudev/Models/Senders/SenderCreateParams.cs
index 2bdd9ae..8d67ab3 100644
--- a/src/Zavudev/Models/Senders/SenderCreateParams.cs
+++ b/src/Zavudev/Models/Senders/SenderCreateParams.cs
@@ -104,8 +104,10 @@ public string? EmailFromName
}
///
- /// 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.
///
public bool? EmailReceivingEnabled
{
diff --git a/src/Zavudev/Models/Senders/SenderUpdateParams.cs b/src/Zavudev/Models/Senders/SenderUpdateParams.cs
index 646c795..a448603 100644
--- a/src/Zavudev/Models/Senders/SenderUpdateParams.cs
+++ b/src/Zavudev/Models/Senders/SenderUpdateParams.cs
@@ -118,7 +118,10 @@ public string? EmailFromName
}
///
- /// 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.
///
public bool? EmailReceivingEnabled
{
From 0d869f1ea9fd1fc2049483bfd76d6ce8803df627 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:53:30 +0000
Subject: [PATCH 09/15] feat(api): api update
---
.stats.yml | 4 ++--
src/Zavudev/Models/Senders/WebhookEvent.cs | 8 +++++++-
.../Models/Templates/TemplateSyncParams.cs | 16 ++++++++++++----
.../Models/Templates/TemplateSyncResponse.cs | 3 ++-
src/Zavudev/Services/ITemplateService.cs | 15 +++++++++++----
5 files changed, 34 insertions(+), 12 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/src/Zavudev/Models/Senders/WebhookEvent.cs b/src/Zavudev/Models/Senders/WebhookEvent.cs
index 0279052..119d6a2 100644
--- a/src/Zavudev/Models/Senders/WebhookEvent.cs
+++ b/src/Zavudev/Models/Senders/WebhookEvent.cs
@@ -41,7 +41,13 @@ namespace Zavudev.Models.Senders;
/// contact. `data` carries `conversationId` (the inbox thread id — deep-link with
/// `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:** - `invitation.status_changed`: A partner invitation's
/// stored status changed: to `in_progress`, `completed`, `failed`, `cancelled`, or
diff --git a/src/Zavudev/Models/Templates/TemplateSyncParams.cs b/src/Zavudev/Models/Templates/TemplateSyncParams.cs
index c8ac37e..5c7d338 100644
--- a/src/Zavudev/Models/Templates/TemplateSyncParams.cs
+++ b/src/Zavudev/Models/Templates/TemplateSyncParams.cs
@@ -10,10 +10,18 @@
namespace Zavudev.Models.Templates;
///
-/// Reconcile this project's templates against WhatsApp. Two 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.
+/// 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), 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
diff --git a/src/Zavudev/Models/Templates/TemplateSyncResponse.cs b/src/Zavudev/Models/Templates/TemplateSyncResponse.cs
index 63b698a..b230954 100644
--- a/src/Zavudev/Models/Templates/TemplateSyncResponse.cs
+++ b/src/Zavudev/Models/Templates/TemplateSyncResponse.cs
@@ -86,7 +86,8 @@ public required long 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.
///
public required long Updated
{
diff --git a/src/Zavudev/Services/ITemplateService.cs b/src/Zavudev/Services/ITemplateService.cs
index fc71bb7..7764c4f 100644
--- a/src/Zavudev/Services/ITemplateService.cs
+++ b/src/Zavudev/Services/ITemplateService.cs
@@ -88,11 +88,18 @@ Task Submit(
);
///
- /// 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`
From 3e405cc8b4b01a0e4ac758f7f2d45b9a81d4f80c 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:53:21 +0000
Subject: [PATCH 10/15] feat(api): api update
---
.stats.yml | 4 +--
.../Models/Messages/MessageSendParams.cs | 13 ++++++---
src/Zavudev/Services/IMessageService.cs | 27 ++++++++++++-------
3 files changed, 29 insertions(+), 15 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/src/Zavudev/Models/Messages/MessageSendParams.cs b/src/Zavudev/Models/Messages/MessageSendParams.cs
index cc83de7..59b9838 100644
--- a/src/Zavudev/Models/Messages/MessageSendParams.cs
+++ b/src/Zavudev/Models/Messages/MessageSendParams.cs
@@ -23,9 +23,16 @@ namespace Zavudev.Models.Messages;
/// messages to initiate conversations outside the window
///
/// **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 - Email is billed from
+/// Messenger share an allowance of 2,000 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 zero balance email sends return 402
diff --git a/src/Zavudev/Services/IMessageService.cs b/src/Zavudev/Services/IMessageService.cs
index bebcd35..e7882e4 100644
--- a/src/Zavudev/Services/IMessageService.cs
+++ b/src/Zavudev/Services/IMessageService.cs
@@ -97,16 +97,23 @@ Task React(
/// template messages to initiate conversations outside the window
///
/// **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 - 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 zero balance
- /// email sends return 402 with code `insufficient_balance`. Free teams start with
- /// $2 of credit and additionally cap at 3,000 emails/month and 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
+ /// Messenger share an allowance of 2,000 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
+ /// zero balance email sends return 402 with code `insufficient_balance`. Free teams
+ /// start with $2 of credit and additionally cap at 3,000 emails/month and 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
From 2c961fc5c02d0de95ec3c4dbed98333f097818f8 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:24:42 +0000
Subject: [PATCH 11/15] feat(api): api update
---
.stats.yml | 4 +--
.../Models/Broadcasts/BroadcastSendParams.cs | 15 +++++----
.../Models/Messages/MessageSendParams.cs | 32 ++++++++++---------
.../Models/Senders/SenderCreateParams.cs | 4 ++-
.../Models/Senders/SenderUpdateParams.cs | 4 ++-
src/Zavudev/Services/IBroadcastService.cs | 17 ++++++----
src/Zavudev/Services/IMessageService.cs | 5 ++-
7 files changed, 48 insertions(+), 33 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/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs b/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs
index d36ffc5..e592ccb 100644
--- a/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs
+++ b/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs
@@ -16,12 +16,15 @@ namespace Zavudev.Models.Broadcasts;
/// 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.
+/// (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
diff --git a/src/Zavudev/Models/Messages/MessageSendParams.cs b/src/Zavudev/Models/Messages/MessageSendParams.cs
index 59b9838..580fb60 100644
--- a/src/Zavudev/Models/Messages/MessageSendParams.cs
+++ b/src/Zavudev/Models/Messages/MessageSendParams.cs
@@ -50,21 +50,23 @@ namespace Zavudev.Models.Messages;
/// 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 - 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. Past that floor: 200/day for SMS, or 10,000/day
-/// once identity or business verification is approved (or a higher limit agreed
-/// for your account); 50/day voice and 250/day conversational on Free. **Paid plans
-/// have no voice or conversational daily ceiling.** Over a ceiling, sends return
-/// `429` with code `daily_limit_exceeded` and `details.limit`; the count resets at
-/// 00:00 UTC - 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` - Full reference: https://docs.zavu.dev/concepts/sending-limits
+/// 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.
+/// Past that floor: 200/day for SMS, or 10,000/day once identity or business verification
+/// is approved (or a higher limit agreed for your account); 50/day voice and 250/day
+/// conversational on Free. **Paid plans have no voice or conversational daily ceiling.**
+/// Over a ceiling, sends return `429` with code `daily_limit_exceeded` and `details.limit`;
+/// the count resets at 00:00 UTC - 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` - Full reference: https://docs.zavu.dev/concepts/sending-limits
///
/// **Email recipient pre-flight:** Email messages are validated automatically
/// before dispatch. Sends that would be a guaranteed hard bounce are failed instead
diff --git a/src/Zavudev/Models/Senders/SenderCreateParams.cs b/src/Zavudev/Models/Senders/SenderCreateParams.cs
index 8d67ab3..42e501b 100644
--- a/src/Zavudev/Models/Senders/SenderCreateParams.cs
+++ b/src/Zavudev/Models/Senders/SenderCreateParams.cs
@@ -131,7 +131,9 @@ public bool? EmailReceivingEnabled
/// 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.
+ /// 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`.
///
public bool? EnableSmsOneway
{
diff --git a/src/Zavudev/Models/Senders/SenderUpdateParams.cs b/src/Zavudev/Models/Senders/SenderUpdateParams.cs
index a448603..9f5ce33 100644
--- a/src/Zavudev/Models/Senders/SenderUpdateParams.cs
+++ b/src/Zavudev/Models/Senders/SenderUpdateParams.cs
@@ -144,7 +144,9 @@ public bool? EmailReceivingEnabled
///
/// 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.
+ /// 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`.
///
public bool? EnableSmsOneway
{
diff --git a/src/Zavudev/Services/IBroadcastService.cs b/src/Zavudev/Services/IBroadcastService.cs
index ac4ad51..2dcb648 100644
--- a/src/Zavudev/Services/IBroadcastService.cs
+++ b/src/Zavudev/Services/IBroadcastService.cs
@@ -174,13 +174,16 @@ Task RetryReview(
/// 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; 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
diff --git a/src/Zavudev/Services/IMessageService.cs b/src/Zavudev/Services/IMessageService.cs
index e7882e4..d8c69ac 100644
--- a/src/Zavudev/Services/IMessageService.cs
+++ b/src/Zavudev/Services/IMessageService.cs
@@ -124,7 +124,10 @@ Task React(
/// 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 - Daily ceilings
+ /// 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. Past that floor: 200/day
From d73876e64f5cf89766a28baddb7979576157c928 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:33:19 +0000
Subject: [PATCH 12/15] feat(api): api update
---
.stats.yml | 4 +--
.../Broadcasts/BroadcastCancelResponseTest.cs | 7 +++++
.../Broadcasts/BroadcastContactStatusTest.cs | 2 ++
.../Broadcasts/BroadcastCreateResponseTest.cs | 7 +++++
.../BroadcastEscalateReviewResponseTest.cs | 7 +++++
.../BroadcastListPageResponseTest.cs | 11 ++++++++
.../Broadcasts/BroadcastProgressTest.cs | 19 ++++++++++++++
.../BroadcastRescheduleResponseTest.cs | 7 +++++
.../BroadcastRetrieveResponseTest.cs | 7 +++++
.../BroadcastRetryReviewResponseTest.cs | 7 +++++
.../Broadcasts/BroadcastSendResponseTest.cs | 7 +++++
.../Models/Broadcasts/BroadcastTest.cs | 19 ++++++++++++++
.../Broadcasts/BroadcastUpdateResponseTest.cs | 7 +++++
src/Zavudev/Models/Broadcasts/Broadcast.cs | 26 +++++++++++++++++++
.../Models/Broadcasts/BroadcastContact.cs | 9 +++++++
.../Broadcasts/BroadcastContactStatus.cs | 11 ++++++++
.../Models/Broadcasts/BroadcastProgress.cs | 24 ++++++++++++++++-
.../Broadcasts/Contacts/ContactListParams.cs | 9 +++++++
18 files changed, 187 insertions(+), 3 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/src/Zavudev.Tests/Models/Broadcasts/BroadcastCancelResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastCancelResponseTest.cs
index 869a838..c70fd91 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastCancelResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastCancelResponseTest.cs
@@ -60,6 +60,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -107,6 +108,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -348,6 +353,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -409,6 +415,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastContactStatusTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastContactStatusTest.cs
index 2be479f..4fa5d86 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastContactStatusTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastContactStatusTest.cs
@@ -11,6 +11,7 @@ public class BroadcastContactStatusTest : TestBase
[InlineData(BroadcastContactStatus.Pending)]
[InlineData(BroadcastContactStatus.Queued)]
[InlineData(BroadcastContactStatus.Sending)]
+ [InlineData(BroadcastContactStatus.Sent)]
[InlineData(BroadcastContactStatus.Delivered)]
[InlineData(BroadcastContactStatus.Failed)]
[InlineData(BroadcastContactStatus.Skipped)]
@@ -37,6 +38,7 @@ public void InvalidEnumValidationThrows_Works()
[InlineData(BroadcastContactStatus.Pending)]
[InlineData(BroadcastContactStatus.Queued)]
[InlineData(BroadcastContactStatus.Sending)]
+ [InlineData(BroadcastContactStatus.Sent)]
[InlineData(BroadcastContactStatus.Delivered)]
[InlineData(BroadcastContactStatus.Failed)]
[InlineData(BroadcastContactStatus.Skipped)]
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastCreateResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastCreateResponseTest.cs
index dece232..543a267 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastCreateResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastCreateResponseTest.cs
@@ -60,6 +60,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -107,6 +108,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -348,6 +353,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -409,6 +415,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastEscalateReviewResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastEscalateReviewResponseTest.cs
index 43818e2..ad2c2c8 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastEscalateReviewResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastEscalateReviewResponseTest.cs
@@ -60,6 +60,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -107,6 +108,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -348,6 +353,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -409,6 +415,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastListPageResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastListPageResponseTest.cs
index 32d238b..6bb4b43 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastListPageResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastListPageResponseTest.cs
@@ -65,6 +65,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -122,6 +123,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -194,6 +196,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -268,6 +271,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -332,6 +336,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -404,6 +409,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -472,6 +478,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -540,6 +547,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -607,6 +615,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -677,6 +686,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -746,6 +756,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastProgressTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastProgressTest.cs
index f235fd8..5d0c669 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastProgressTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastProgressTest.cs
@@ -25,6 +25,7 @@ public void FieldRoundtrip_Works()
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
EstimatedCost = 0,
ReservedAmount = 0,
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
};
@@ -43,6 +44,7 @@ public void FieldRoundtrip_Works()
);
double expectedEstimatedCost = 0;
double expectedReservedAmount = 0;
+ long expectedSent = 0;
DateTimeOffset expectedStartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
Assert.Equal(expectedBroadcastID, model.BroadcastID);
@@ -58,6 +60,7 @@ public void FieldRoundtrip_Works()
Assert.Equal(expectedEstimatedCompletionAt, model.EstimatedCompletionAt);
Assert.Equal(expectedEstimatedCost, model.EstimatedCost);
Assert.Equal(expectedReservedAmount, model.ReservedAmount);
+ Assert.Equal(expectedSent, model.Sent);
Assert.Equal(expectedStartedAt, model.StartedAt);
}
@@ -79,6 +82,7 @@ public void SerializationRoundtrip_Works()
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
EstimatedCost = 0,
ReservedAmount = 0,
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
};
@@ -109,6 +113,7 @@ public void FieldRoundtripThroughSerialization_Works()
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
EstimatedCost = 0,
ReservedAmount = 0,
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
};
@@ -134,6 +139,7 @@ public void FieldRoundtripThroughSerialization_Works()
);
double expectedEstimatedCost = 0;
double expectedReservedAmount = 0;
+ long expectedSent = 0;
DateTimeOffset expectedStartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
Assert.Equal(expectedBroadcastID, deserialized.BroadcastID);
@@ -149,6 +155,7 @@ public void FieldRoundtripThroughSerialization_Works()
Assert.Equal(expectedEstimatedCompletionAt, deserialized.EstimatedCompletionAt);
Assert.Equal(expectedEstimatedCost, deserialized.EstimatedCost);
Assert.Equal(expectedReservedAmount, deserialized.ReservedAmount);
+ Assert.Equal(expectedSent, deserialized.Sent);
Assert.Equal(expectedStartedAt, deserialized.StartedAt);
}
@@ -170,6 +177,7 @@ public void Validation_Works()
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
EstimatedCost = 0,
ReservedAmount = 0,
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
};
@@ -197,6 +205,8 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works()
Assert.Null(model.EstimatedCompletionAt);
Assert.False(model.RawData.ContainsKey("estimatedCompletionAt"));
+ Assert.Null(model.Sent);
+ Assert.False(model.RawData.ContainsKey("sent"));
Assert.Null(model.StartedAt);
Assert.False(model.RawData.ContainsKey("startedAt"));
}
@@ -243,11 +253,14 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works()
// Null should be interpreted as omitted for these properties
EstimatedCompletionAt = null,
+ Sent = null,
StartedAt = null,
};
Assert.Null(model.EstimatedCompletionAt);
Assert.False(model.RawData.ContainsKey("estimatedCompletionAt"));
+ Assert.Null(model.Sent);
+ Assert.False(model.RawData.ContainsKey("sent"));
Assert.Null(model.StartedAt);
Assert.False(model.RawData.ContainsKey("startedAt"));
}
@@ -272,6 +285,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works()
// Null should be interpreted as omitted for these properties
EstimatedCompletionAt = null,
+ Sent = null,
StartedAt = null,
};
@@ -293,6 +307,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works()
Status = BroadcastStatus.Draft,
Total = 0,
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
};
@@ -319,6 +334,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works()
Status = BroadcastStatus.Draft,
Total = 0,
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
};
@@ -340,6 +356,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
Status = BroadcastStatus.Draft,
Total = 0,
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
ActualCost = null,
@@ -370,6 +387,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works()
Status = BroadcastStatus.Draft,
Total = 0,
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
ActualCost = null,
@@ -398,6 +416,7 @@ public void CopyConstructor_Works()
EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
EstimatedCost = 0,
ReservedAmount = 0,
+ Sent = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
};
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRescheduleResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRescheduleResponseTest.cs
index 63e1af8..d0cb5f9 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRescheduleResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRescheduleResponseTest.cs
@@ -60,6 +60,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -107,6 +108,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -348,6 +353,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -409,6 +415,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetrieveResponseTest.cs
index 63b229b..ac3cb45 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetrieveResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetrieveResponseTest.cs
@@ -60,6 +60,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -107,6 +108,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -348,6 +353,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -409,6 +415,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetryReviewResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetryReviewResponseTest.cs
index f66ae30..5626558 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetryReviewResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetryReviewResponseTest.cs
@@ -60,6 +60,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -107,6 +108,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -348,6 +353,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -409,6 +415,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastSendResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastSendResponseTest.cs
index 42b8408..afb19cc 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastSendResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastSendResponseTest.cs
@@ -60,6 +60,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -107,6 +108,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -348,6 +353,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -409,6 +415,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastTest.cs
index 64ad99a..2fe364d 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastTest.cs
@@ -52,6 +52,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -96,6 +97,7 @@ public void FieldRoundtrip_Works()
DateTimeOffset expectedScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
string expectedSenderID = "senderId";
long expectedSendingCount = 0;
+ long expectedSentCount = 0;
DateTimeOffset expectedStartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
string expectedText = "text";
DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
@@ -129,6 +131,7 @@ public void FieldRoundtrip_Works()
Assert.Equal(expectedScheduledAt, model.ScheduledAt);
Assert.Equal(expectedSenderID, model.SenderID);
Assert.Equal(expectedSendingCount, model.SendingCount);
+ Assert.Equal(expectedSentCount, model.SentCount);
Assert.Equal(expectedStartedAt, model.StartedAt);
Assert.Equal(expectedText, model.Text);
Assert.Equal(expectedUpdatedAt, model.UpdatedAt);
@@ -178,6 +181,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -233,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -284,6 +289,7 @@ public void FieldRoundtripThroughSerialization_Works()
DateTimeOffset expectedScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
string expectedSenderID = "senderId";
long expectedSendingCount = 0;
+ long expectedSentCount = 0;
DateTimeOffset expectedStartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
string expectedText = "text";
DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
@@ -317,6 +323,7 @@ public void FieldRoundtripThroughSerialization_Works()
Assert.Equal(expectedScheduledAt, deserialized.ScheduledAt);
Assert.Equal(expectedSenderID, deserialized.SenderID);
Assert.Equal(expectedSendingCount, deserialized.SendingCount);
+ Assert.Equal(expectedSentCount, deserialized.SentCount);
Assert.Equal(expectedStartedAt, deserialized.StartedAt);
Assert.Equal(expectedText, deserialized.Text);
Assert.Equal(expectedUpdatedAt, deserialized.UpdatedAt);
@@ -366,6 +373,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -420,6 +428,8 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works()
Assert.False(model.RawData.ContainsKey("senderId"));
Assert.Null(model.SendingCount);
Assert.False(model.RawData.ContainsKey("sendingCount"));
+ Assert.Null(model.SentCount);
+ Assert.False(model.RawData.ContainsKey("sentCount"));
Assert.Null(model.StartedAt);
Assert.False(model.RawData.ContainsKey("startedAt"));
Assert.Null(model.Text);
@@ -493,6 +503,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works()
ScheduledAt = null,
SenderID = null,
SendingCount = null,
+ SentCount = null,
StartedAt = null,
Text = null,
UpdatedAt = null,
@@ -518,6 +529,8 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works()
Assert.False(model.RawData.ContainsKey("senderId"));
Assert.Null(model.SendingCount);
Assert.False(model.RawData.ContainsKey("sendingCount"));
+ Assert.Null(model.SentCount);
+ Assert.False(model.RawData.ContainsKey("sentCount"));
Assert.Null(model.StartedAt);
Assert.False(model.RawData.ContainsKey("startedAt"));
Assert.Null(model.Text);
@@ -562,6 +575,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works()
ScheduledAt = null,
SenderID = null,
SendingCount = null,
+ SentCount = null,
StartedAt = null,
Text = null,
UpdatedAt = null,
@@ -602,6 +616,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -651,6 +666,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -691,6 +707,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -746,6 +763,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -804,6 +822,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastUpdateResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastUpdateResponseTest.cs
index 6fb4c09..f635c5c 100644
--- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastUpdateResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastUpdateResponseTest.cs
@@ -60,6 +60,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -107,6 +108,7 @@ public void FieldRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -348,6 +353,7 @@ public void Validation_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -409,6 +415,7 @@ public void CopyConstructor_Works()
ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
SenderID = "senderId",
SendingCount = 0,
+ SentCount = 0,
StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
Text = "text",
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev/Models/Broadcasts/Broadcast.cs b/src/Zavudev/Models/Broadcasts/Broadcast.cs
index fe4f9f7..bc41d52 100644
--- a/src/Zavudev/Models/Broadcasts/Broadcast.cs
+++ b/src/Zavudev/Models/Broadcasts/Broadcast.cs
@@ -148,6 +148,9 @@ public BroadcastContent? Content
}
}
+ ///
+ /// Recipients with confirmed delivery to the device.
+ ///
public long? DeliveredCount
{
get
@@ -347,6 +350,28 @@ public long? SendingCount
}
}
+ ///
+ /// Recipients whose message the provider accepted, without a confirmed delivery
+ /// yet. Channels that never report delivery keep their recipients here.
+ ///
+ public long? SentCount
+ {
+ get
+ {
+ this._rawData.Freeze();
+ return this._rawData.GetNullableStruct("sentCount");
+ }
+ init
+ {
+ if (value == null)
+ {
+ return;
+ }
+
+ this._rawData.Set("sentCount", value);
+ }
+ }
+
public DateTimeOffset? StartedAt
{
get
@@ -426,6 +451,7 @@ public override void Validate()
_ = this.ScheduledAt;
_ = this.SenderID;
_ = this.SendingCount;
+ _ = this.SentCount;
_ = this.StartedAt;
_ = this.Text;
_ = this.UpdatedAt;
diff --git a/src/Zavudev/Models/Broadcasts/BroadcastContact.cs b/src/Zavudev/Models/Broadcasts/BroadcastContact.cs
index 9a129ca..6aa240d 100644
--- a/src/Zavudev/Models/Broadcasts/BroadcastContact.cs
+++ b/src/Zavudev/Models/Broadcasts/BroadcastContact.cs
@@ -54,6 +54,15 @@ public required ApiEnum RecipientType
///
/// 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.
///
public required ApiEnum Status
{
diff --git a/src/Zavudev/Models/Broadcasts/BroadcastContactStatus.cs b/src/Zavudev/Models/Broadcasts/BroadcastContactStatus.cs
index 3012798..5ea97e6 100644
--- a/src/Zavudev/Models/Broadcasts/BroadcastContactStatus.cs
+++ b/src/Zavudev/Models/Broadcasts/BroadcastContactStatus.cs
@@ -7,6 +7,14 @@ namespace Zavudev.Models.Broadcasts;
///
/// 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.
///
[JsonConverter(typeof(BroadcastContactStatusConverter))]
public enum BroadcastContactStatus
@@ -14,6 +22,7 @@ public enum BroadcastContactStatus
Pending,
Queued,
Sending,
+ Sent,
Delivered,
Failed,
Skipped,
@@ -32,6 +41,7 @@ JsonSerializerOptions options
"pending" => BroadcastContactStatus.Pending,
"queued" => BroadcastContactStatus.Queued,
"sending" => BroadcastContactStatus.Sending,
+ "sent" => BroadcastContactStatus.Sent,
"delivered" => BroadcastContactStatus.Delivered,
"failed" => BroadcastContactStatus.Failed,
"skipped" => BroadcastContactStatus.Skipped,
@@ -52,6 +62,7 @@ JsonSerializerOptions options
BroadcastContactStatus.Pending => "pending",
BroadcastContactStatus.Queued => "queued",
BroadcastContactStatus.Sending => "sending",
+ BroadcastContactStatus.Sent => "sent",
BroadcastContactStatus.Delivered => "delivered",
BroadcastContactStatus.Failed => "failed",
BroadcastContactStatus.Skipped => "skipped",
diff --git a/src/Zavudev/Models/Broadcasts/BroadcastProgress.cs b/src/Zavudev/Models/Broadcasts/BroadcastProgress.cs
index 8bb96fb..b876037 100644
--- a/src/Zavudev/Models/Broadcasts/BroadcastProgress.cs
+++ b/src/Zavudev/Models/Broadcasts/BroadcastProgress.cs
@@ -22,7 +22,7 @@ public required string BroadcastID
}
///
- /// Successfully delivered.
+ /// Confirmed delivered to the device.
///
public required long Delivered
{
@@ -182,6 +182,27 @@ public double? ReservedAmount
init { this._rawData.Set("reservedAmount", value); }
}
+ ///
+ /// Accepted by the provider, delivery not confirmed yet.
+ ///
+ public long? Sent
+ {
+ get
+ {
+ this._rawData.Freeze();
+ return this._rawData.GetNullableStruct("sent");
+ }
+ init
+ {
+ if (value == null)
+ {
+ return;
+ }
+
+ this._rawData.Set("sent", value);
+ }
+ }
+
public DateTimeOffset? StartedAt
{
get
@@ -216,6 +237,7 @@ public override void Validate()
_ = this.EstimatedCompletionAt;
_ = this.EstimatedCost;
_ = this.ReservedAmount;
+ _ = this.Sent;
_ = this.StartedAt;
}
diff --git a/src/Zavudev/Models/Broadcasts/Contacts/ContactListParams.cs b/src/Zavudev/Models/Broadcasts/Contacts/ContactListParams.cs
index a4a4155..0d906b2 100644
--- a/src/Zavudev/Models/Broadcasts/Contacts/ContactListParams.cs
+++ b/src/Zavudev/Models/Broadcasts/Contacts/ContactListParams.cs
@@ -57,6 +57,15 @@ public long? 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.
///
public ApiEnum? Status
{
From da0a2c07b9299dcaa0c1ae7c078e5642ca679531 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:15 +0000
Subject: [PATCH 13/15] feat(api): api update
---
.stats.yml | 4 +-
.../Models/Broadcasts/BroadcastSendParams.cs | 30 ++++----
src/Zavudev/Models/Calls/CallCreateParams.cs | 21 +++---
.../Models/Messages/MessageSendParams.cs | 64 ++++++++++-------
.../Models/Senders/SenderCreateParams.cs | 4 +-
.../Models/Senders/SenderUpdateParams.cs | 4 +-
src/Zavudev/Services/IBroadcastService.cs | 30 ++++----
src/Zavudev/Services/ICallService.cs | 20 +++---
src/Zavudev/Services/IMessageService.cs | 69 +++++++++++--------
9 files changed, 131 insertions(+), 115 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/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs b/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs
index e592ccb..50fca67 100644
--- a/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs
+++ b/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs
@@ -12,19 +12,11 @@ namespace Zavudev.Models.Broadcasts;
///
/// 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
@@ -34,9 +26,15 @@ namespace Zavudev.Models.Broadcasts;
/// **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.
+/// 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/src/Zavudev/Models/Calls/CallCreateParams.cs b/src/Zavudev/Models/Calls/CallCreateParams.cs
index 41288c9..9d8b2a3 100644
--- a/src/Zavudev/Models/Calls/CallCreateParams.cs
+++ b/src/Zavudev/Models/Calls/CallCreateParams.cs
@@ -16,16 +16,17 @@ namespace Zavudev.Models.Calls;
/// interruption handling).
///
/// **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
-/// - The sender's agent must have `voice.enabled` set to `true`. - Not available
-/// with test-mode API keys.
+/// (otherwise `403`). - 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.
///
/// **Billing:** Voice calls are billed per minute of connected time plus telephony,
/// deducted from your prepaid balance. A short-duration estimate is reserved when
diff --git a/src/Zavudev/Models/Messages/MessageSendParams.cs b/src/Zavudev/Models/Messages/MessageSendParams.cs
index 580fb60..a3ef7f2 100644
--- a/src/Zavudev/Models/Messages/MessageSendParams.cs
+++ b/src/Zavudev/Models/Messages/MessageSendParams.cs
@@ -41,32 +41,44 @@ namespace Zavudev.Models.Messages;
/// 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 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.
-/// Past that floor: 200/day for SMS, or 10,000/day once identity or business verification
-/// is approved (or a higher limit agreed for your account); 50/day voice and 250/day
-/// conversational on Free. **Paid plans have no voice or conversational daily ceiling.**
-/// Over a ceiling, sends return `429` with code `daily_limit_exceeded` and `details.limit`;
-/// the count resets at 00:00 UTC - 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` - Full reference: https://docs.zavu.dev/concepts/sending-limits
+/// **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. Past that floor: 200/day for SMS, or 10,000/day once identity
+/// or business verification is approved (or a higher limit agreed for your account);
+/// 50/day voice and 250/day conversational on Free. **Paid plans have no voice or
+/// conversational daily ceiling.** Over a ceiling, sends return `429` with code `daily_limit_exceeded`
+/// and `details.limit`; the count resets at 00:00 UTC - 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: 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
diff --git a/src/Zavudev/Models/Senders/SenderCreateParams.cs b/src/Zavudev/Models/Senders/SenderCreateParams.cs
index 42e501b..8d67ab3 100644
--- a/src/Zavudev/Models/Senders/SenderCreateParams.cs
+++ b/src/Zavudev/Models/Senders/SenderCreateParams.cs
@@ -131,9 +131,7 @@ public bool? EmailReceivingEnabled
/// 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`.
+ /// array on the response.
///
public bool? EnableSmsOneway
{
diff --git a/src/Zavudev/Models/Senders/SenderUpdateParams.cs b/src/Zavudev/Models/Senders/SenderUpdateParams.cs
index 9f5ce33..a448603 100644
--- a/src/Zavudev/Models/Senders/SenderUpdateParams.cs
+++ b/src/Zavudev/Models/Senders/SenderUpdateParams.cs
@@ -144,9 +144,7 @@ public bool? EmailReceivingEnabled
///
/// 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`.
+ /// with the `channels` array on the response.
///
public bool? EnableSmsOneway
{
diff --git a/src/Zavudev/Services/IBroadcastService.cs b/src/Zavudev/Services/IBroadcastService.cs
index 2dcb648..4e3e43d 100644
--- a/src/Zavudev/Services/IBroadcastService.cs
+++ b/src/Zavudev/Services/IBroadcastService.cs
@@ -170,20 +170,11 @@ Task RetryReview(
///
/// 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
@@ -194,9 +185,14 @@ Task RetryReview(
/// 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.
+ /// 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
diff --git a/src/Zavudev/Services/ICallService.cs b/src/Zavudev/Services/ICallService.cs
index a9db150..03107f9 100644
--- a/src/Zavudev/Services/ICallService.cs
+++ b/src/Zavudev/Services/ICallService.cs
@@ -33,16 +33,16 @@ public interface ICallService
/// real-time interruption handling).
///
/// **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 - The sender's agent must have
+ /// (otherwise `403`). - 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.
///
/// **Billing:** Voice calls are billed per minute of connected time plus
diff --git a/src/Zavudev/Services/IMessageService.cs b/src/Zavudev/Services/IMessageService.cs
index d8c69ac..8e55022 100644
--- a/src/Zavudev/Services/IMessageService.cs
+++ b/src/Zavudev/Services/IMessageService.cs
@@ -115,34 +115,47 @@ Task React(
/// 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 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. Past that floor: 200/day
- /// for SMS, or 10,000/day once identity or business verification is approved (or a
- /// higher limit agreed for your account); 50/day voice and 250/day conversational
- /// on Free. **Paid plans have no voice or conversational daily ceiling.** Over a
- /// ceiling, sends return `429` with code `daily_limit_exceeded` and
- /// `details.limit`; the count resets at 00:00 UTC - 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`
- /// - Full reference: https://docs.zavu.dev/concepts/sending-limits
+ /// **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. Past that floor: 200/day for SMS, or
+ /// 10,000/day once identity or business verification is approved (or a higher limit
+ /// agreed for your account); 50/day voice and 250/day conversational on Free.
+ /// **Paid plans have no voice or conversational daily ceiling.** Over a ceiling,
+ /// sends return `429` with code `daily_limit_exceeded` and `details.limit`; the
+ /// count resets at 00:00 UTC - 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: 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
From 5fb8697f9affa1b561efce67b6be0f56d13677fb 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:41:21 +0000
Subject: [PATCH 14/15] feat(api): api update
---
.stats.yml | 4 +--
.../Senders/Agent/Flows/AgentFlowTest.cs | 22 ++++++-------
.../Agent/Flows/FlowCreateParamsTest.cs | 12 +++----
.../Agent/Flows/FlowCreateResponseTest.cs | 14 ++++----
.../Agent/Flows/FlowDuplicateResponseTest.cs | 14 ++++----
.../Agent/Flows/FlowListPageResponseTest.cs | 22 ++++++-------
.../Agent/Flows/FlowRetrieveResponseTest.cs | 14 ++++----
.../Senders/Agent/Flows/FlowTriggerTest.cs | 14 ++++----
.../Agent/Flows/FlowUpdateParamsTest.cs | 6 ++--
.../Agent/Flows/FlowUpdateResponseTest.cs | 14 ++++----
.../Services/Senders/Agent/FlowServiceTest.cs | 2 +-
.../Models/Senders/Agent/Flows/FlowTrigger.cs | 32 ++++++++++++++++---
12 files changed, 97 insertions(+), 73 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/src/Zavudev.Tests/Models/Senders/Agent/Flows/AgentFlowTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/AgentFlowTest.cs
index ee49f90..7fada41 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/AgentFlowTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/AgentFlowTest.cs
@@ -35,7 +35,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -64,7 +64,7 @@ public void FieldRoundtrip_Works()
Flows::FlowTrigger expectedTrigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
};
DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
@@ -113,7 +113,7 @@ public void SerializationRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -156,7 +156,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -192,7 +192,7 @@ public void FieldRoundtripThroughSerialization_Works()
Flows::FlowTrigger expectedTrigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
};
DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z");
@@ -241,7 +241,7 @@ public void Validation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -278,7 +278,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -315,7 +315,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -351,7 +351,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -390,7 +390,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -428,7 +428,7 @@ public void CopyConstructor_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateParamsTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateParamsTest.cs
index 5660eea..7bb43d9 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateParamsTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateParamsTest.cs
@@ -41,7 +41,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["info", "pricing", "demo"],
},
Description = "Capture lead information",
@@ -78,7 +78,7 @@ public void FieldRoundtrip_Works()
Flows::FlowTrigger expectedTrigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["info", "pricing", "demo"],
};
string expectedDescription = "Capture lead information";
@@ -132,7 +132,7 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["info", "pricing", "demo"],
},
};
@@ -179,7 +179,7 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["info", "pricing", "demo"],
},
@@ -231,7 +231,7 @@ public void Url_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["info", "pricing", "demo"],
},
};
@@ -277,7 +277,7 @@ public void CopyConstructor_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["info", "pricing", "demo"],
},
Description = "Capture lead information",
diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateResponseTest.cs
index d2363df..ceccad3 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateResponseTest.cs
@@ -37,7 +37,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -69,7 +69,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -108,7 +108,7 @@ public void SerializationRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -154,7 +154,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -193,7 +193,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -232,7 +232,7 @@ public void Validation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -272,7 +272,7 @@ public void CopyConstructor_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowDuplicateResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowDuplicateResponseTest.cs
index ecbcadb..3bc398a 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowDuplicateResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowDuplicateResponseTest.cs
@@ -37,7 +37,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -69,7 +69,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -108,7 +108,7 @@ public void SerializationRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -154,7 +154,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -193,7 +193,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -232,7 +232,7 @@ public void Validation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -272,7 +272,7 @@ public void CopyConstructor_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowListPageResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowListPageResponseTest.cs
index b3ca95a..9bd772c 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowListPageResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowListPageResponseTest.cs
@@ -39,7 +39,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -75,7 +75,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -123,7 +123,7 @@ public void SerializationRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -173,7 +173,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -216,7 +216,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -264,7 +264,7 @@ public void Validation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -308,7 +308,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -352,7 +352,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -395,7 +395,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -441,7 +441,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -486,7 +486,7 @@ public void CopyConstructor_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowRetrieveResponseTest.cs
index dc9a030..db20921 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowRetrieveResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowRetrieveResponseTest.cs
@@ -37,7 +37,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -69,7 +69,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -108,7 +108,7 @@ public void SerializationRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -154,7 +154,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -193,7 +193,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -232,7 +232,7 @@ public void Validation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -272,7 +272,7 @@ public void CopyConstructor_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowTriggerTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowTriggerTest.cs
index 17d4d3b..b5c3397 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowTriggerTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowTriggerTest.cs
@@ -14,12 +14,12 @@ public void FieldRoundtrip_Works()
var model = new FlowTrigger
{
Type = FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
};
ApiEnum expectedType = FlowTriggerType.Keyword;
- string expectedIntent = "intent";
+ string expectedIntent = "quiere saber precios o cotizar";
List expectedKeywords = ["string"];
Assert.Equal(expectedType, model.Type);
@@ -38,7 +38,7 @@ public void SerializationRoundtrip_Works()
var model = new FlowTrigger
{
Type = FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
};
@@ -57,7 +57,7 @@ public void FieldRoundtripThroughSerialization_Works()
var model = new FlowTrigger
{
Type = FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
};
@@ -69,7 +69,7 @@ public void FieldRoundtripThroughSerialization_Works()
Assert.NotNull(deserialized);
ApiEnum expectedType = FlowTriggerType.Keyword;
- string expectedIntent = "intent";
+ string expectedIntent = "quiere saber precios o cotizar";
List expectedKeywords = ["string"];
Assert.Equal(expectedType, deserialized.Type);
@@ -88,7 +88,7 @@ public void Validation_Works()
var model = new FlowTrigger
{
Type = FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
};
@@ -153,7 +153,7 @@ public void CopyConstructor_Works()
var model = new FlowTrigger
{
Type = FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
};
diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateParamsTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateParamsTest.cs
index 8e0008f..336bf5d 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateParamsTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateParamsTest.cs
@@ -34,7 +34,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
};
@@ -61,7 +61,7 @@ public void FieldRoundtrip_Works()
Flows::FlowTrigger expectedTrigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
};
@@ -172,7 +172,7 @@ public void CopyConstructor_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
};
diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateResponseTest.cs
index e430296..6f22241 100644
--- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateResponseTest.cs
+++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateResponseTest.cs
@@ -37,7 +37,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -69,7 +69,7 @@ public void FieldRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -108,7 +108,7 @@ public void SerializationRoundtrip_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -154,7 +154,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -193,7 +193,7 @@ public void FieldRoundtripThroughSerialization_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -232,7 +232,7 @@ public void Validation_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
@@ -272,7 +272,7 @@ public void CopyConstructor_Works()
Trigger = new()
{
Type = Flows::FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["string"],
},
UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"),
diff --git a/src/Zavudev.Tests/Services/Senders/Agent/FlowServiceTest.cs b/src/Zavudev.Tests/Services/Senders/Agent/FlowServiceTest.cs
index b7e0a77..a6f6bf2 100644
--- a/src/Zavudev.Tests/Services/Senders/Agent/FlowServiceTest.cs
+++ b/src/Zavudev.Tests/Services/Senders/Agent/FlowServiceTest.cs
@@ -42,7 +42,7 @@ public async Task Create_Works()
Trigger = new()
{
Type = FlowTriggerType.Keyword,
- Intent = "intent",
+ Intent = "quiere saber precios o cotizar",
Keywords = ["info", "pricing", "demo"],
},
},
diff --git a/src/Zavudev/Models/Senders/Agent/Flows/FlowTrigger.cs b/src/Zavudev/Models/Senders/Agent/Flows/FlowTrigger.cs
index 9e46f7c..83e7385 100644
--- a/src/Zavudev/Models/Senders/Agent/Flows/FlowTrigger.cs
+++ b/src/Zavudev/Models/Senders/Agent/Flows/FlowTrigger.cs
@@ -14,7 +14,13 @@ namespace Zavudev.Models.Senders.Agent.Flows;
public sealed record class FlowTrigger : JsonModel
{
///
- /// 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.
///
public required ApiEnum Type
{
@@ -27,7 +33,17 @@ public required ApiEnum 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.
///
public string? Intent
{
@@ -48,7 +64,9 @@ public string? 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.
///
public IReadOnlyList? Keywords
{
@@ -122,7 +140,13 @@ public FlowTrigger FromRawUnchecked(IReadOnlyDictionary raw
}
///
-/// 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.
///
[JsonConverter(typeof(FlowTriggerTypeConverter))]
public enum FlowTriggerType
From c6817349332d41d675087de9dccf25b340f1886f 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:41:46 +0000
Subject: [PATCH 15/15] release: 0.2.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 18 ++++++++++++++++++
src/Zavudev/Zavudev.csproj | 2 +-
3 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 3d2ac0b..10f3091 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0"
+ ".": "0.2.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be543db..8457837 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelog
+## 0.2.0 (2026-09-20)
+
+Full Changelog: [v0.1.0...v0.2.0](https://github.com/zavudev/sdk-csharp/compare/v0.1.0...v0.2.0)
+
+### Features
+
+* **api:** api update ([5fb8697](https://github.com/zavudev/sdk-csharp/commit/5fb8697f9affa1b561efce67b6be0f56d13677fb))
+* **api:** api update ([da0a2c0](https://github.com/zavudev/sdk-csharp/commit/da0a2c07b9299dcaa0c1ae7c078e5642ca679531))
+* **api:** api update ([d73876e](https://github.com/zavudev/sdk-csharp/commit/d73876e64f5cf89766a28baddb7979576157c928))
+* **api:** api update ([2c961fc](https://github.com/zavudev/sdk-csharp/commit/2c961fc5c02d0de95ec3c4dbed98333f097818f8))
+* **api:** api update ([3e405cc](https://github.com/zavudev/sdk-csharp/commit/3e405cc8b4b01a0e4ac758f7f2d45b9a81d4f80c))
+* **api:** api update ([0d869f1](https://github.com/zavudev/sdk-csharp/commit/0d869f1ea9fd1fc2049483bfd76d6ce8803df627))
+* **api:** api update ([415d31d](https://github.com/zavudev/sdk-csharp/commit/415d31dbddf1211b0ce0fe66310f5c1e453c2de4))
+* **api:** api update ([5456b2c](https://github.com/zavudev/sdk-csharp/commit/5456b2c8998185dff2f91bf699cc506f32c02864))
+* **api:** api update ([01367bb](https://github.com/zavudev/sdk-csharp/commit/01367bbf6af82761f861601a214216e503fdd481))
+* **api:** api update ([710ab9f](https://github.com/zavudev/sdk-csharp/commit/710ab9f1f328f1aeef304b058650140f0f0285db))
+* **api:** api update ([c1e8938](https://github.com/zavudev/sdk-csharp/commit/c1e89384e72a0a05e2a94819a2e8656872a804e2))
+
## 0.1.0 (2026-09-08)
Full Changelog: [v0.0.2...v0.1.0](https://github.com/zavudev/sdk-csharp/compare/v0.0.2...v0.1.0)
diff --git a/src/Zavudev/Zavudev.csproj b/src/Zavudev/Zavudev.csproj
index 17ecca7..be0d841 100644
--- a/src/Zavudev/Zavudev.csproj
+++ b/src/Zavudev/Zavudev.csproj
@@ -3,7 +3,7 @@
Zavudev C#
Zavudev
- 0.1.0
+ 0.2.0
The official .NET library for the Zavudev API.
Library
README.md