sentdm 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb2a3192ac350b8b789c39c41c5c98b4dcd1df0b714d0d3f0540e88205a0b57d
4
- data.tar.gz: 5d97dab3605f998f583ea137599413f498de456598e57d257591df8afb173e14
3
+ metadata.gz: d680fc6fd1a90e7cfbb949b51b2318a9a5be626e7f5629eac8e94aa891efb518
4
+ data.tar.gz: 51a7542758dea2afa3873d000f47d003c4212ad54808c9a1ee6b97551f14a93d
5
5
  SHA512:
6
- metadata.gz: 65df0bbbe671b490469b171d4b241b1d4fc5d088b271390c3a727227f046fddb138c61be3bee853f2a8b26a8e910aae0a41c0f0b46ef01ffedee44e5fc1002b6
7
- data.tar.gz: 0fc42b03c73246deaf5cb6711e0431a4469b6eb172c6d1a3cf2a8d7f63621aa4cda7708d7054d61afa042d571d07377b9e1b94437cc2e65aed7099a16eadd77c
6
+ metadata.gz: 1fd26499de108ec871956cbc8f6219506a2c08cb6ce1f9aa105c2509518faf6b99eda7cfd5dda5781f2b584fbbeca3618692667ac6fe46b1cc2a4e033114f6ee
7
+ data.tar.gz: 3bf07498b8d470a63f9f543b4bf0e68b88141ae345c7a7700994fb6beaf89731a01a0ddc41198275d0d64f1e8ffa8026487b14c74a6e9f9194c4b3659462223d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.37.0](https://github.com/sentdm/sent-dm-ruby/compare/v0.36.0...v0.37.0) (2026-10-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * **api:** sync generated SDKs from the committed spec ([c38a90b](https://github.com/sentdm/sent-dm-ruby/commit/c38a90bc1459c97d364720a4d37db3a30f5ea396))
9
+ * **api:** sync OpenAPI spec from production ([6697158](https://github.com/sentdm/sent-dm-ruby/commit/66971588cd75f6b148a1e63e9c43f6d54abb0680))
10
+
11
+ ## [0.36.0](https://github.com/sentdm/sent-dm-ruby/compare/v0.35.0...v0.36.0) (2026-10-06)
12
+
13
+
14
+ ### Features
15
+
16
+ * **api:** sync generated SDKs from the committed spec ([fc29692](https://github.com/sentdm/sent-dm-ruby/commit/fc29692de765a72b716a46c4920c603e2b617a95))
17
+ * **api:** sync OpenAPI spec from production ([989362a](https://github.com/sentdm/sent-dm-ruby/commit/989362a3142f3d926e68793f163d21e85d3cf346))
18
+
3
19
  ## [0.35.0](https://github.com/sentdm/sent-dm-ruby/compare/v0.34.0...v0.35.0) (2026-10-01)
4
20
 
5
21
 
data/README.md CHANGED
@@ -17,7 +17,7 @@ To use this gem, install via Bundler by adding the following to your application
17
17
  <!-- x-release-please-start-version -->
18
18
 
19
19
  ```ruby
20
- gem "sentdm", "~> 0.35.0"
20
+ gem "sentdm", "~> 0.37.0"
21
21
  ```
22
22
 
23
23
  <!-- x-release-please-end -->
data/lib/sentdm/client.rb CHANGED
@@ -89,6 +89,11 @@ module Sentdm
89
89
  # the send pipeline. It is a new attempt, not a free retry: every policy runs
90
90
  # again, the message is billed again, and its status webhooks fire again. A
91
91
  # FILTERED message is never resendable.
92
+ #
93
+ # **A scheduled message can be called off.** `POST /v3/messages/{id}/cancel`
94
+ # cancels a send you scheduled with `scheduled_at`, as long as it has not been
95
+ # released yet. Cancelling is free, fires `message.cancelled`, and is final — a
96
+ # cancelled message cannot be resent.
92
97
  # @return [Sentdm::Resources::Messages]
93
98
  attr_reader :messages
94
99
 
@@ -39,8 +39,9 @@ module Sentdm
39
39
 
40
40
  # @!attribute failure_reason
41
41
  # Why the call did not complete: callback_timeout, invalid_answer,
42
- # insufficient_balance, destination_blocked, rejected or no_answer. Null while the
43
- # call is live, when it completed, and when it failed without a recorded reason
42
+ # insufficient_balance, destination_blocked, callback_not_configured, rejected or
43
+ # no_answer. Null while the call is live, when it completed, and when it failed
44
+ # without a recorded reason
44
45
  #
45
46
  # @return [String, nil]
46
47
  optional :failure_reason, String, nil?: true
@@ -77,7 +78,7 @@ module Sentdm
77
78
  optional :started_at, Time
78
79
 
79
80
  # @!attribute status
80
- # initiated, ringing, answered, completed, failed, no_answer or rejected
81
+ # INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED
81
82
  #
82
83
  # @return [String, nil]
83
84
  optional :status, String
@@ -123,7 +124,7 @@ module Sentdm
123
124
  #
124
125
  # @param started_at [Time] When the call was placed (UTC)
125
126
  #
126
- # @param status [String] initiated, ringing, answered, completed, failed, no_answer or rejected
127
+ # @param status [String] INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED
127
128
  #
128
129
  # @param timeline [Array<Sentdm::Models::CallTimelineEntry>, nil] When the call entered each status, oldest first. Only returned when reading one
129
130
  #
@@ -4,7 +4,7 @@ module Sentdm
4
4
  module Models
5
5
  class CallTimelineEntry < Sentdm::Internal::Type::BaseModel
6
6
  # @!attribute status
7
- # initiated, ringing, answered, completed, failed, no_answer or rejected
7
+ # INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED
8
8
  #
9
9
  # @return [String, nil]
10
10
  optional :status, String
@@ -18,7 +18,7 @@ module Sentdm
18
18
  # @!method initialize(status: nil, timestamp: nil)
19
19
  # When a call entered a status
20
20
  #
21
- # @param status [String] initiated, ringing, answered, completed, failed, no_answer or rejected
21
+ # @param status [String] INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED
22
22
  #
23
23
  # @param timestamp [Time] When the call entered this status (UTC)
24
24
  end
@@ -72,14 +72,19 @@ module Sentdm
72
72
  # @!attribute schedule_reason
73
73
  # message.scheduled only: why the message is held, either because you scheduled it
74
74
  # or because the recipient is inside a protected quiet-hours window. Omitted on
75
- # every other event.
75
+ # every other event, including message.cancelled — that is a property of the hold,
76
+ # not of the cancellation, and repeating it there would read as "why was this
77
+ # cancelled", which it does not answer.
76
78
  #
77
79
  # @return [String, nil]
78
80
  optional :schedule_reason, String, nil?: true
79
81
 
80
82
  # @!attribute scheduled_at
81
- # message.scheduled only: when the held message will be released for delivery, in
82
- # UTC (yyyy-MM-ddTHH:mm:ssZ). Omitted on every other event.
83
+ # message.scheduled and message.cancelled only, in UTC (yyyy-MM-ddTHH:mm:ssZ): on
84
+ # message.scheduled it is when the held message will be released for delivery, on
85
+ # message.cancelled the release instant that was called off — the same instant,
86
+ # before and after. A consumer that recorded a future send from the first event
87
+ # has what it needs to un-record it from the second. Omitted on every other event.
83
88
  #
84
89
  # @return [String, nil]
85
90
  optional :scheduled_at, String, nil?: true
@@ -131,7 +136,7 @@ module Sentdm
131
136
  #
132
137
  # @param schedule_reason [String, nil] message.scheduled only: why the message is held, either because you scheduled it
133
138
  #
134
- # @param scheduled_at [String, nil] message.scheduled only: when the held message will be released for delivery, in
139
+ # @param scheduled_at [String, nil] message.scheduled and message.cancelled only, in UTC (yyyy-MM-ddTHH:mm:ssZ): on
135
140
  #
136
141
  # @param template_id [String, nil] The template the message was sent from, when it was sent from one.
137
142
  #
@@ -116,10 +116,12 @@ module Sentdm
116
116
  optional :reason_code, String, nil?: true
117
117
 
118
118
  # @!attribute scheduled_at
119
- # SCHEDULED activities only: when the held message will be released for delivery,
120
- # in UTC. Same wire name as on the send response, the message and the webhook.
121
- # Omitted on every other activity. A message that quiet hours moved at release has
122
- # two SCHEDULED entries, each carrying the instant as it stood at that moment.
119
+ # SCHEDULED and CANCELLED activities only, in UTC: on a SCHEDULED entry, when the
120
+ # held message will be released for delivery; on a CANCELLED entry, the instant
121
+ # that was called off. Same wire name as on the send response, the message and the
122
+ # webhook. Omitted on every other activity. A message that quiet hours moved at
123
+ # release has two SCHEDULED entries, each carrying the instant as it stood at that
124
+ # moment.
123
125
  #
124
126
  # @return [Time, nil]
125
127
  optional :scheduled_at, Time, nil?: true
@@ -161,7 +163,7 @@ module Sentdm
161
163
  # @param reason_code [String, nil] Why the message reached this status, as a stable platform code such as
162
164
  # DELIVERY\_
163
165
  #
164
- # @param scheduled_at [Time, nil] SCHEDULED activities only: when the held message will be released for delivery,
166
+ # @param scheduled_at [Time, nil] SCHEDULED and CANCELLED activities only, in UTC: on a SCHEDULED entry, when the
165
167
  #
166
168
  # @param status [String] Activity status. Outbound: QUEUED, PROCESSED, ROUTED, SCHEDULED, SENT, DELIVERED
167
169
  #
@@ -15,6 +15,33 @@ module Sentdm
15
15
  # @return [Array<String>, nil]
16
16
  optional :channel, Sentdm::Internal::Type::ArrayOf[String], nil?: true
17
17
 
18
+ # @!attribute channels
19
+ # Which of your own numbers to send from, keyed by channel, each channel holding a
20
+ # list of entries: {"sms": [{"country": "US", "from": ["+12125550000",
21
+ # "+14155550000"]}, {"from": ["+447700800001"]}]}. Any real channel may be a key;
22
+ # sent, which is auto-detect rather than a channel, is rejected. country and
23
+ # strategy are accepted and stored but not acted on yet: every entry's numbers
24
+ # apply to every recipient on that channel.
25
+ #
26
+ # This does not choose channels — Channel does, and the two combine: "channel":
27
+ # ["sms"] with an sms list sends on SMS from those numbers. Each list only narrows
28
+ # which of its own channel's routes may win, so with Channel left at auto-detect a
29
+ # recipient best served by a channel with no list still goes out on it. Routing
30
+ # itself is unchanged: the same rules are scored and ranked the same way, with
31
+ # routes pinned to numbers you did not list removed from the running.
32
+ #
33
+ # Every number must be an active sender on your account. The request itself is
34
+ # still accepted (202) if one is not — like every other send-time rule, that is
35
+ # decided per message, so each affected message is recorded BLOCKED with error
36
+ # code BUSINESS_029 and reported on GET /v3/messages and the status webhook.
37
+ #
38
+ # @return [Hash{Symbol=>Array<Sentdm::Models::MessageSendParams::Channel>}, nil]
39
+ optional :channels,
40
+ -> {
41
+ Sentdm::Internal::Type::HashOf[Sentdm::Internal::Type::ArrayOf[Sentdm::MessageSendParams::Channel]]
42
+ },
43
+ nil?: true
44
+
18
45
  # @!attribute media_urls
19
46
  # Attachments for this send, as publicly fetchable https URLs. Used by the MMS
20
47
  # channel and ignored by every other one.
@@ -93,12 +120,14 @@ module Sentdm
93
120
  # @return [String, nil]
94
121
  optional :x_profile_id, String
95
122
 
96
- # @!method initialize(channel: nil, media_urls: nil, sandbox: nil, scheduled_at: nil, subject: nil, template: nil, text: nil, to: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
123
+ # @!method initialize(channel: nil, channels: nil, media_urls: nil, sandbox: nil, scheduled_at: nil, subject: nil, template: nil, text: nil, to: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
97
124
  # Some parameter documentations has been truncated, see
98
125
  # {Sentdm::Models::MessageSendParams} for more details.
99
126
  #
100
127
  # @param channel [Array<String>, nil] Channels to broadcast on, e.g. ["whatsapp", "sms"].
101
128
  #
129
+ # @param channels [Hash{Symbol=>Array<Sentdm::Models::MessageSendParams::Channel>}, nil] Which of your own numbers to send from, keyed by channel, each channel holding a
130
+ #
102
131
  # @param media_urls [Array<String>, nil] Attachments for this send, as publicly fetchable https URLs. Used by the MMS cha
103
132
  #
104
133
  # @param sandbox [Boolean] Sandbox flag - when true, the operation is simulated without side effects
@@ -119,6 +148,44 @@ module Sentdm
119
148
  #
120
149
  # @param request_options [Sentdm::RequestOptions, Hash{Symbol=>Object}]
121
150
 
151
+ class Channel < Sentdm::Internal::Type::BaseModel
152
+ # @!attribute country
153
+ # Recipient country this entry is meant for (ISO 3166-1 alpha-2, e.g. US).
154
+ # Optional. Accepted and stored, not acted on yet.
155
+ #
156
+ # @return [String, nil]
157
+ optional :country, String, nil?: true
158
+
159
+ # @!attribute from
160
+ # Sender numbers in E.164. Each must be an active sender on your account for this
161
+ # channel. That is account state rather than request shape, so it is decided per
162
+ # message: the request is accepted with 202 and a message naming an unusable
163
+ # number is recorded BLOCKED with error code BUSINESS_029.
164
+ #
165
+ # @return [Array<String>, nil]
166
+ optional :from, Sentdm::Internal::Type::ArrayOf[String], nil?: true
167
+
168
+ # @!attribute strategy
169
+ # How to pick a number from From, e.g. sticky or geo. Optional. Accepted and
170
+ # stored, not acted on yet.
171
+ #
172
+ # @return [String, nil]
173
+ optional :strategy, String, nil?: true
174
+
175
+ # @!method initialize(country: nil, from: nil, strategy: nil)
176
+ # Some parameter documentations has been truncated, see
177
+ # {Sentdm::Models::MessageSendParams::Channel} for more details.
178
+ #
179
+ # One entry of a channel's list in Channels, e.g. {"country": "US", "from":
180
+ # ["+15559990002", "+15559990003"], "strategy": "sticky"}.
181
+ #
182
+ # @param country [String, nil] Recipient country this entry is meant for (ISO 3166-1 alpha-2, e.g. US). Optiona
183
+ #
184
+ # @param from [Array<String>, nil] Sender numbers in E.164. Each must be an active sender on your account for this
185
+ #
186
+ # @param strategy [String, nil] How to pick a number from From, e.g. sticky or geo. Optional. Accepted
187
+ end
188
+
122
189
  class Template < Sentdm::Internal::Type::BaseModel
123
190
  # @!attribute id
124
191
  # Template ID (mutually exclusive with name)
@@ -106,11 +106,11 @@ module Sentdm
106
106
  # conference room, and answers with the participant's own call record. The
107
107
  # participant is a call of their own: it has its own id, can be looked up and hung
108
108
  # up, and is billed and reported through call.completed and call.failed like any
109
- # other call. A phone participant is called from caller_id, which must be one of
110
- # your numbers, or from the call's owning number when omitted, and needs a
111
- # destination you may call and a positive balance. Only a call your answer
112
- # connected to a conference can take participants: a call connected to a user or a
113
- # number answers 409.
109
+ # other call. Every participant needs a positive balance. A phone participant is
110
+ # called from caller_id, which must be one of your numbers, or from the call's
111
+ # owning number when omitted, and needs a destination you may call. Only a call
112
+ # your answer connected to a conference can take participants: a call connected to
113
+ # a user or a number answers 409.
114
114
  #
115
115
  # @overload add(id, caller_id: nil, sandbox: nil, to: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
116
116
  #
@@ -118,10 +118,11 @@ module Sentdm
118
118
  # Some parameter documentations has been truncated, see
119
119
  # {Sentdm::Models::CallHangupParams} for more details.
120
120
  #
121
- # Ends one of your live calls. The call then ends the way any other call does: its
122
- # status moves to completed and call.completed is sent once the disconnect is
123
- # reported. A call that has already ended answers 409, and so does a call with no
124
- # phone leg, such as one between two app users.
121
+ # Ends one of your live calls. The call then ends the way any other call does once
122
+ # the disconnect is reported: an answered call as COMPLETED with call.completed, a
123
+ # call still ringing as NO_ANSWER, REJECTED or FAILED with call.failed. A call
124
+ # that has already ended answers 409, and so does a call with no phone leg, such
125
+ # as one between two app users.
125
126
  #
126
127
  # @overload hangup(id, sandbox: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
127
128
  #
@@ -19,6 +19,11 @@ module Sentdm
19
19
  # the send pipeline. It is a new attempt, not a free retry: every policy runs
20
20
  # again, the message is billed again, and its status webhooks fire again. A
21
21
  # FILTERED message is never resendable.
22
+ #
23
+ # **A scheduled message can be called off.** `POST /v3/messages/{id}/cancel`
24
+ # cancels a send you scheduled with `scheduled_at`, as long as it has not been
25
+ # released yet. Cancelling is free, fires `message.cancelled`, and is final — a
26
+ # cancelled message cannot be resent.
22
27
  class Messages
23
28
  # Some parameter documentations has been truncated, see
24
29
  # {Sentdm::Models::MessageRetrieveActivitiesParams} for more details.
@@ -55,10 +60,11 @@ module Sentdm
55
60
  #
56
61
  # Retrieves the current status and details of a message by ID. Includes delivery
57
62
  # status, timestamps, and error information if applicable. A message that is or
58
- # was held for a later time (a send you scheduled with scheduled_at, or a
59
- # quiet-hours hold) is returned as a ScheduledMessageResponse: the same fields
60
- # plus scheduled_at, the release instant in UTC. A message sent immediately has no
61
- # scheduled_at key.
63
+ # was held for a later time (a send you scheduled with scheduled_at, a quiet-hours
64
+ # hold, or a message you cancelled while it was held) is returned as a
65
+ # ScheduledMessageResponse: the same fields plus scheduled_at, the instant it is
66
+ # held for in UTC — or, on a CANCELLED message, the instant that was called off. A
67
+ # message sent immediately has no scheduled_at key.
62
68
  #
63
69
  # @overload retrieve_status(id, x_profile_id: nil, request_options: {})
64
70
  #
@@ -88,13 +94,27 @@ module Sentdm
88
94
  # Sends a message to one or more recipients using a template. Supports
89
95
  # multi-channel broadcast — when multiple channels are specified (e.g. ["sms",
90
96
  # "whatsapp"]), a separate message is created for each (recipient, channel) pair.
91
- # Returns immediately with per-recipient message IDs for async tracking via
92
- # webhooks or the GET /messages/{id} endpoint. Sends gated before any delivery
93
- # attempt do not reject the request — an account-level precondition such as
94
- # insufficient balance, a template not approved for sending, or free-form content
95
- # with no open conversation with the contact. The send is accepted with 202 and
96
- # the affected messages are reported as BLOCKED on GET /messages/{id} and the
97
- # message.blocked webhook. To send later, set scheduled_at (ISO-8601 with an
97
+ # To choose which of your own numbers a send goes out from, use 'channels':
98
+ # {"sms": [{"from": ["+12125550000", "+14155550000"]}]}. Each channel holds a list
99
+ # of entries, each with 'from' and optionally 'country' and 'strategy'; 'country'
100
+ # and 'strategy' are stored but not acted on yet, so every entry's numbers apply
101
+ # to every recipient on that channel. Every number listed must be an active sender
102
+ # on your account. Like the other account-level preconditions below, that is
103
+ # checked per message rather than when the request is received: the request is
104
+ # still accepted with 202, and each affected message is reported as BLOCKED with
105
+ # error code BUSINESS_029 on GET /messages/{id} and the message.blocked webhook.
106
+ # Each channel's numbers restrict which numbers that channel may use; it does not
107
+ # choose channels — 'channel' does, and the two can be combined. With 'channel'
108
+ # left at auto-detect, a recipient best served by a channel you listed no numbers
109
+ # for still goes out on it. Where several of the listed numbers could serve a
110
+ # recipient, routing prefers the one whose area code matches theirs. Keys: sms,
111
+ # whatsapp, rcs, mms. Returns immediately with per-recipient message IDs for async
112
+ # tracking via webhooks or the GET /messages/{id} endpoint. Sends gated before any
113
+ # delivery attempt do not reject the request — an account-level precondition such
114
+ # as insufficient balance, a template not approved for sending, or free-form
115
+ # content with no open conversation with the contact. The send is accepted with
116
+ # 202 and the affected messages are reported as BLOCKED on GET /messages/{id} and
117
+ # the message.blocked webhook. To send later, set scheduled_at (ISO-8601 with an
98
118
  # explicit UTC offset; a value without one is rejected) between 1 minute and 30
99
119
  # days ahead: the response is a ScheduledSendMessageResponse (the same fields plus
100
120
  # scheduled_at; status is still QUEUED), each message then moves to SCHEDULED, is
@@ -106,10 +126,12 @@ module Sentdm
106
126
  # second message.scheduled webhook reports the new scheduled_at. An account may
107
127
  # hold at most 1,000,000 scheduled messages at once (429 LIMIT_001).
108
128
  #
109
- # @overload send_(channel: nil, media_urls: nil, sandbox: nil, scheduled_at: nil, subject: nil, template: nil, text: nil, to: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
129
+ # @overload send_(channel: nil, channels: nil, media_urls: nil, sandbox: nil, scheduled_at: nil, subject: nil, template: nil, text: nil, to: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
110
130
  #
111
131
  # @param channel [Array<String>, nil] Body param: Channels to broadcast on, e.g. ["whatsapp", "sms"].
112
132
  #
133
+ # @param channels [Hash{Symbol=>Array<Sentdm::Models::MessageSendParams::Channel>}, nil] Body param: Which of your own numbers to send from, keyed by channel, each chann
134
+ #
113
135
  # @param media_urls [Array<String>, nil] Body param: Attachments for this send, as publicly fetchable https URLs. Used by
114
136
  #
115
137
  # @param sandbox [Boolean] Body param: Sandbox flag - when true, the operation is simulated without side ef
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentdm
4
- VERSION = "0.35.0"
4
+ VERSION = "0.37.0"
5
5
  end
@@ -84,6 +84,11 @@ module Sentdm
84
84
  # the send pipeline. It is a new attempt, not a free retry: every policy runs
85
85
  # again, the message is billed again, and its status webhooks fire again. A
86
86
  # FILTERED message is never resendable.
87
+ #
88
+ # **A scheduled message can be called off.** `POST /v3/messages/{id}/cancel`
89
+ # cancels a send you scheduled with `scheduled_at`, as long as it has not been
90
+ # released yet. Cancelling is free, fires `message.cancelled`, and is final — a
91
+ # cancelled message cannot be resent.
87
92
  sig { returns(Sentdm::Resources::Messages) }
88
93
  attr_reader :messages
89
94
 
@@ -35,8 +35,9 @@ module Sentdm
35
35
  attr_accessor :ended_at
36
36
 
37
37
  # Why the call did not complete: callback_timeout, invalid_answer,
38
- # insufficient_balance, destination_blocked, rejected or no_answer. Null while the
39
- # call is live, when it completed, and when it failed without a recorded reason
38
+ # insufficient_balance, destination_blocked, callback_not_configured, rejected or
39
+ # no_answer. Null while the call is live, when it completed, and when it failed
40
+ # without a recorded reason
40
41
  sig { returns(T.nilable(String)) }
41
42
  attr_accessor :failure_reason
42
43
 
@@ -73,7 +74,7 @@ module Sentdm
73
74
  sig { params(started_at: Time).void }
74
75
  attr_writer :started_at
75
76
 
76
- # initiated, ringing, answered, completed, failed, no_answer or rejected
77
+ # INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED
77
78
  sig { returns(T.nilable(String)) }
78
79
  attr_reader :status
79
80
 
@@ -126,8 +127,9 @@ module Sentdm
126
127
  # When the call ended (UTC). Null while the call is live
127
128
  ended_at: nil,
128
129
  # Why the call did not complete: callback_timeout, invalid_answer,
129
- # insufficient_balance, destination_blocked, rejected or no_answer. Null while the
130
- # call is live, when it completed, and when it failed without a recorded reason
130
+ # insufficient_balance, destination_blocked, callback_not_configured, rejected or
131
+ # no_answer. Null while the call is live, when it completed, and when it failed
132
+ # without a recorded reason
131
133
  failure_reason: nil,
132
134
  # One end of a call
133
135
  from: nil,
@@ -140,7 +142,7 @@ module Sentdm
140
142
  recording_available: nil,
141
143
  # When the call was placed (UTC)
142
144
  started_at: nil,
143
- # initiated, ringing, answered, completed, failed, no_answer or rejected
145
+ # INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED
144
146
  status: nil,
145
147
  # When the call entered each status, oldest first. Only returned when reading one
146
148
  # call
@@ -8,7 +8,7 @@ module Sentdm
8
8
  T.any(Sentdm::CallTimelineEntry, Sentdm::Internal::AnyHash)
9
9
  end
10
10
 
11
- # initiated, ringing, answered, completed, failed, no_answer or rejected
11
+ # INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED
12
12
  sig { returns(T.nilable(String)) }
13
13
  attr_reader :status
14
14
 
@@ -25,7 +25,7 @@ module Sentdm
25
25
  # When a call entered a status
26
26
  sig { params(status: String, timestamp: Time).returns(T.attached_class) }
27
27
  def self.new(
28
- # initiated, ringing, answered, completed, failed, no_answer or rejected
28
+ # INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED
29
29
  status: nil,
30
30
  # When the call entered this status (UTC)
31
31
  timestamp: nil
@@ -70,12 +70,17 @@ module Sentdm
70
70
 
71
71
  # message.scheduled only: why the message is held, either because you scheduled it
72
72
  # or because the recipient is inside a protected quiet-hours window. Omitted on
73
- # every other event.
73
+ # every other event, including message.cancelled — that is a property of the hold,
74
+ # not of the cancellation, and repeating it there would read as "why was this
75
+ # cancelled", which it does not answer.
74
76
  sig { returns(T.nilable(String)) }
75
77
  attr_accessor :schedule_reason
76
78
 
77
- # message.scheduled only: when the held message will be released for delivery, in
78
- # UTC (yyyy-MM-ddTHH:mm:ssZ). Omitted on every other event.
79
+ # message.scheduled and message.cancelled only, in UTC (yyyy-MM-ddTHH:mm:ssZ): on
80
+ # message.scheduled it is when the held message will be released for delivery, on
81
+ # message.cancelled the release instant that was called off — the same instant,
82
+ # before and after. A consumer that recorded a future send from the first event
83
+ # has what it needs to un-record it from the second. Omitted on every other event.
79
84
  sig { returns(T.nilable(String)) }
80
85
  attr_accessor :scheduled_at
81
86
 
@@ -149,10 +154,15 @@ module Sentdm
149
154
  reason_code: nil,
150
155
  # message.scheduled only: why the message is held, either because you scheduled it
151
156
  # or because the recipient is inside a protected quiet-hours window. Omitted on
152
- # every other event.
157
+ # every other event, including message.cancelled — that is a property of the hold,
158
+ # not of the cancellation, and repeating it there would read as "why was this
159
+ # cancelled", which it does not answer.
153
160
  schedule_reason: nil,
154
- # message.scheduled only: when the held message will be released for delivery, in
155
- # UTC (yyyy-MM-ddTHH:mm:ssZ). Omitted on every other event.
161
+ # message.scheduled and message.cancelled only, in UTC (yyyy-MM-ddTHH:mm:ssZ): on
162
+ # message.scheduled it is when the held message will be released for delivery, on
163
+ # message.cancelled the release instant that was called off — the same instant,
164
+ # before and after. A consumer that recorded a future send from the first event
165
+ # has what it needs to un-record it from the second. Omitted on every other event.
156
166
  scheduled_at: nil,
157
167
  # The template the message was sent from, when it was sent from one.
158
168
  template_id: nil,
@@ -216,10 +216,12 @@ module Sentdm
216
216
  sig { returns(T.nilable(String)) }
217
217
  attr_accessor :reason_code
218
218
 
219
- # SCHEDULED activities only: when the held message will be released for delivery,
220
- # in UTC. Same wire name as on the send response, the message and the webhook.
221
- # Omitted on every other activity. A message that quiet hours moved at release has
222
- # two SCHEDULED entries, each carrying the instant as it stood at that moment.
219
+ # SCHEDULED and CANCELLED activities only, in UTC: on a SCHEDULED entry, when the
220
+ # held message will be released for delivery; on a CANCELLED entry, the instant
221
+ # that was called off. Same wire name as on the send response, the message and the
222
+ # webhook. Omitted on every other activity. A message that quiet hours moved at
223
+ # release has two SCHEDULED entries, each carrying the instant as it stood at that
224
+ # moment.
223
225
  sig { returns(T.nilable(Time)) }
224
226
  attr_accessor :scheduled_at
225
227
 
@@ -278,10 +280,12 @@ module Sentdm
278
280
  # rather than on reason: the code is stable, the wording may be improved. Same
279
281
  # wire name and vocabulary as on the message and the webhook.
280
282
  reason_code: nil,
281
- # SCHEDULED activities only: when the held message will be released for delivery,
282
- # in UTC. Same wire name as on the send response, the message and the webhook.
283
- # Omitted on every other activity. A message that quiet hours moved at release has
284
- # two SCHEDULED entries, each carrying the instant as it stood at that moment.
283
+ # SCHEDULED and CANCELLED activities only, in UTC: on a SCHEDULED entry, when the
284
+ # held message will be released for delivery; on a CANCELLED entry, the instant
285
+ # that was called off. Same wire name as on the send response, the message and the
286
+ # webhook. Omitted on every other activity. A message that quiet hours moved at
287
+ # release has two SCHEDULED entries, each carrying the instant as it stood at that
288
+ # moment.
285
289
  scheduled_at: nil,
286
290
  # Activity status. Outbound: QUEUED, PROCESSED, ROUTED, SCHEDULED, SENT,
287
291
  # DELIVERED, READ, FAILED. Inbound (from contact): RECEIVED (terminal).
@@ -17,6 +17,33 @@ module Sentdm
17
17
  sig { returns(T.nilable(T::Array[String])) }
18
18
  attr_accessor :channel
19
19
 
20
+ # Which of your own numbers to send from, keyed by channel, each channel holding a
21
+ # list of entries: {"sms": [{"country": "US", "from": ["+12125550000",
22
+ # "+14155550000"]}, {"from": ["+447700800001"]}]}. Any real channel may be a key;
23
+ # sent, which is auto-detect rather than a channel, is rejected. country and
24
+ # strategy are accepted and stored but not acted on yet: every entry's numbers
25
+ # apply to every recipient on that channel.
26
+ #
27
+ # This does not choose channels — Channel does, and the two combine: "channel":
28
+ # ["sms"] with an sms list sends on SMS from those numbers. Each list only narrows
29
+ # which of its own channel's routes may win, so with Channel left at auto-detect a
30
+ # recipient best served by a channel with no list still goes out on it. Routing
31
+ # itself is unchanged: the same rules are scored and ranked the same way, with
32
+ # routes pinned to numbers you did not list removed from the running.
33
+ #
34
+ # Every number must be an active sender on your account. The request itself is
35
+ # still accepted (202) if one is not — like every other send-time rule, that is
36
+ # decided per message, so each affected message is recorded BLOCKED with error
37
+ # code BUSINESS_029 and reported on GET /v3/messages and the status webhook.
38
+ sig do
39
+ returns(
40
+ T.nilable(
41
+ T::Hash[Symbol, T::Array[Sentdm::MessageSendParams::Channel]]
42
+ )
43
+ )
44
+ end
45
+ attr_accessor :channels
46
+
20
47
  # Attachments for this send, as publicly fetchable https URLs. Used by the MMS
21
48
  # channel and ignored by every other one.
22
49
  #
@@ -99,6 +126,13 @@ module Sentdm
99
126
  sig do
100
127
  params(
101
128
  channel: T.nilable(T::Array[String]),
129
+ channels:
130
+ T.nilable(
131
+ T::Hash[
132
+ Symbol,
133
+ T::Array[Sentdm::MessageSendParams::Channel::OrHash]
134
+ ]
135
+ ),
102
136
  media_urls: T.nilable(T::Array[String]),
103
137
  sandbox: T::Boolean,
104
138
  scheduled_at: T.nilable(Time),
@@ -116,6 +150,25 @@ module Sentdm
116
150
  # separate message per recipient. "sent" = auto-detect. Defaults to ["sent"]
117
151
  # (auto-detect) if omitted.
118
152
  channel: nil,
153
+ # Which of your own numbers to send from, keyed by channel, each channel holding a
154
+ # list of entries: {"sms": [{"country": "US", "from": ["+12125550000",
155
+ # "+14155550000"]}, {"from": ["+447700800001"]}]}. Any real channel may be a key;
156
+ # sent, which is auto-detect rather than a channel, is rejected. country and
157
+ # strategy are accepted and stored but not acted on yet: every entry's numbers
158
+ # apply to every recipient on that channel.
159
+ #
160
+ # This does not choose channels — Channel does, and the two combine: "channel":
161
+ # ["sms"] with an sms list sends on SMS from those numbers. Each list only narrows
162
+ # which of its own channel's routes may win, so with Channel left at auto-detect a
163
+ # recipient best served by a channel with no list still goes out on it. Routing
164
+ # itself is unchanged: the same rules are scored and ranked the same way, with
165
+ # routes pinned to numbers you did not list removed from the running.
166
+ #
167
+ # Every number must be an active sender on your account. The request itself is
168
+ # still accepted (202) if one is not — like every other send-time rule, that is
169
+ # decided per message, so each affected message is recorded BLOCKED with error
170
+ # code BUSINESS_029 and reported on GET /v3/messages and the status webhook.
171
+ channels: nil,
119
172
  # Attachments for this send, as publicly fetchable https URLs. Used by the MMS
120
173
  # channel and ignored by every other one.
121
174
  #
@@ -166,6 +219,10 @@ module Sentdm
166
219
  override.returns(
167
220
  {
168
221
  channel: T.nilable(T::Array[String]),
222
+ channels:
223
+ T.nilable(
224
+ T::Hash[Symbol, T::Array[Sentdm::MessageSendParams::Channel]]
225
+ ),
169
226
  media_urls: T.nilable(T::Array[String]),
170
227
  sandbox: T::Boolean,
171
228
  scheduled_at: T.nilable(Time),
@@ -182,6 +239,66 @@ module Sentdm
182
239
  def to_hash
183
240
  end
184
241
 
242
+ class Channel < Sentdm::Internal::Type::BaseModel
243
+ OrHash =
244
+ T.type_alias do
245
+ T.any(Sentdm::MessageSendParams::Channel, Sentdm::Internal::AnyHash)
246
+ end
247
+
248
+ # Recipient country this entry is meant for (ISO 3166-1 alpha-2, e.g. US).
249
+ # Optional. Accepted and stored, not acted on yet.
250
+ sig { returns(T.nilable(String)) }
251
+ attr_accessor :country
252
+
253
+ # Sender numbers in E.164. Each must be an active sender on your account for this
254
+ # channel. That is account state rather than request shape, so it is decided per
255
+ # message: the request is accepted with 202 and a message naming an unusable
256
+ # number is recorded BLOCKED with error code BUSINESS_029.
257
+ sig { returns(T.nilable(T::Array[String])) }
258
+ attr_accessor :from
259
+
260
+ # How to pick a number from From, e.g. sticky or geo. Optional. Accepted and
261
+ # stored, not acted on yet.
262
+ sig { returns(T.nilable(String)) }
263
+ attr_accessor :strategy
264
+
265
+ # One entry of a channel's list in Channels, e.g. {"country": "US", "from":
266
+ # ["+15559990002", "+15559990003"], "strategy": "sticky"}.
267
+ sig do
268
+ params(
269
+ country: T.nilable(String),
270
+ from: T.nilable(T::Array[String]),
271
+ strategy: T.nilable(String)
272
+ ).returns(T.attached_class)
273
+ end
274
+ def self.new(
275
+ # Recipient country this entry is meant for (ISO 3166-1 alpha-2, e.g. US).
276
+ # Optional. Accepted and stored, not acted on yet.
277
+ country: nil,
278
+ # Sender numbers in E.164. Each must be an active sender on your account for this
279
+ # channel. That is account state rather than request shape, so it is decided per
280
+ # message: the request is accepted with 202 and a message naming an unusable
281
+ # number is recorded BLOCKED with error code BUSINESS_029.
282
+ from: nil,
283
+ # How to pick a number from From, e.g. sticky or geo. Optional. Accepted and
284
+ # stored, not acted on yet.
285
+ strategy: nil
286
+ )
287
+ end
288
+
289
+ sig do
290
+ override.returns(
291
+ {
292
+ country: T.nilable(String),
293
+ from: T.nilable(T::Array[String]),
294
+ strategy: T.nilable(String)
295
+ }
296
+ )
297
+ end
298
+ def to_hash
299
+ end
300
+ end
301
+
185
302
  class Template < Sentdm::Internal::Type::BaseModel
186
303
  OrHash =
187
304
  T.type_alias do
@@ -90,11 +90,11 @@ module Sentdm
90
90
  # conference room, and answers with the participant's own call record. The
91
91
  # participant is a call of their own: it has its own id, can be looked up and hung
92
92
  # up, and is billed and reported through call.completed and call.failed like any
93
- # other call. A phone participant is called from caller_id, which must be one of
94
- # your numbers, or from the call's owning number when omitted, and needs a
95
- # destination you may call and a positive balance. Only a call your answer
96
- # connected to a conference can take participants: a call connected to a user or a
97
- # number answers 409.
93
+ # other call. Every participant needs a positive balance. A phone participant is
94
+ # called from caller_id, which must be one of your numbers, or from the call's
95
+ # owning number when omitted, and needs a destination you may call. Only a call
96
+ # your answer connected to a conference can take participants: a call connected to
97
+ # a user or a number answers 409.
98
98
  sig do
99
99
  params(
100
100
  id: String,
@@ -104,10 +104,11 @@ module Sentdm
104
104
  )
105
105
  end
106
106
 
107
- # Ends one of your live calls. The call then ends the way any other call does: its
108
- # status moves to completed and call.completed is sent once the disconnect is
109
- # reported. A call that has already ended answers 409, and so does a call with no
110
- # phone leg, such as one between two app users.
107
+ # Ends one of your live calls. The call then ends the way any other call does once
108
+ # the disconnect is reported: an answered call as COMPLETED with call.completed, a
109
+ # call still ringing as NO_ANSWER, REJECTED or FAILED with call.failed. A call
110
+ # that has already ended answers 409, and so does a call with no phone leg, such
111
+ # as one between two app users.
111
112
  sig do
112
113
  params(
113
114
  id: String,
@@ -19,6 +19,11 @@ module Sentdm
19
19
  # the send pipeline. It is a new attempt, not a free retry: every policy runs
20
20
  # again, the message is billed again, and its status webhooks fire again. A
21
21
  # FILTERED message is never resendable.
22
+ #
23
+ # **A scheduled message can be called off.** `POST /v3/messages/{id}/cancel`
24
+ # cancels a send you scheduled with `scheduled_at`, as long as it has not been
25
+ # released yet. Cancelling is free, fires `message.cancelled`, and is final — a
26
+ # cancelled message cannot be resent.
22
27
  class Messages
23
28
  # Retrieves the activity log for a specific message. Activities track the message
24
29
  # lifecycle including acceptance, processing, sending, delivery, and any errors. A
@@ -43,10 +48,11 @@ module Sentdm
43
48
 
44
49
  # Retrieves the current status and details of a message by ID. Includes delivery
45
50
  # status, timestamps, and error information if applicable. A message that is or
46
- # was held for a later time (a send you scheduled with scheduled_at, or a
47
- # quiet-hours hold) is returned as a ScheduledMessageResponse: the same fields
48
- # plus scheduled_at, the release instant in UTC. A message sent immediately has no
49
- # scheduled_at key.
51
+ # was held for a later time (a send you scheduled with scheduled_at, a quiet-hours
52
+ # hold, or a message you cancelled while it was held) is returned as a
53
+ # ScheduledMessageResponse: the same fields plus scheduled_at, the instant it is
54
+ # held for in UTC — or, on a CANCELLED message, the instant that was called off. A
55
+ # message sent immediately has no scheduled_at key.
50
56
  sig do
51
57
  params(
52
58
  id: String,
@@ -67,13 +73,27 @@ module Sentdm
67
73
  # Sends a message to one or more recipients using a template. Supports
68
74
  # multi-channel broadcast — when multiple channels are specified (e.g. ["sms",
69
75
  # "whatsapp"]), a separate message is created for each (recipient, channel) pair.
70
- # Returns immediately with per-recipient message IDs for async tracking via
71
- # webhooks or the GET /messages/{id} endpoint. Sends gated before any delivery
72
- # attempt do not reject the request — an account-level precondition such as
73
- # insufficient balance, a template not approved for sending, or free-form content
74
- # with no open conversation with the contact. The send is accepted with 202 and
75
- # the affected messages are reported as BLOCKED on GET /messages/{id} and the
76
- # message.blocked webhook. To send later, set scheduled_at (ISO-8601 with an
76
+ # To choose which of your own numbers a send goes out from, use 'channels':
77
+ # {"sms": [{"from": ["+12125550000", "+14155550000"]}]}. Each channel holds a list
78
+ # of entries, each with 'from' and optionally 'country' and 'strategy'; 'country'
79
+ # and 'strategy' are stored but not acted on yet, so every entry's numbers apply
80
+ # to every recipient on that channel. Every number listed must be an active sender
81
+ # on your account. Like the other account-level preconditions below, that is
82
+ # checked per message rather than when the request is received: the request is
83
+ # still accepted with 202, and each affected message is reported as BLOCKED with
84
+ # error code BUSINESS_029 on GET /messages/{id} and the message.blocked webhook.
85
+ # Each channel's numbers restrict which numbers that channel may use; it does not
86
+ # choose channels — 'channel' does, and the two can be combined. With 'channel'
87
+ # left at auto-detect, a recipient best served by a channel you listed no numbers
88
+ # for still goes out on it. Where several of the listed numbers could serve a
89
+ # recipient, routing prefers the one whose area code matches theirs. Keys: sms,
90
+ # whatsapp, rcs, mms. Returns immediately with per-recipient message IDs for async
91
+ # tracking via webhooks or the GET /messages/{id} endpoint. Sends gated before any
92
+ # delivery attempt do not reject the request — an account-level precondition such
93
+ # as insufficient balance, a template not approved for sending, or free-form
94
+ # content with no open conversation with the contact. The send is accepted with
95
+ # 202 and the affected messages are reported as BLOCKED on GET /messages/{id} and
96
+ # the message.blocked webhook. To send later, set scheduled_at (ISO-8601 with an
77
97
  # explicit UTC offset; a value without one is rejected) between 1 minute and 30
78
98
  # days ahead: the response is a ScheduledSendMessageResponse (the same fields plus
79
99
  # scheduled_at; status is still QUEUED), each message then moves to SCHEDULED, is
@@ -87,6 +107,13 @@ module Sentdm
87
107
  sig do
88
108
  params(
89
109
  channel: T.nilable(T::Array[String]),
110
+ channels:
111
+ T.nilable(
112
+ T::Hash[
113
+ Symbol,
114
+ T::Array[Sentdm::MessageSendParams::Channel::OrHash]
115
+ ]
116
+ ),
90
117
  media_urls: T.nilable(T::Array[String]),
91
118
  sandbox: T::Boolean,
92
119
  scheduled_at: T.nilable(Time),
@@ -104,6 +131,25 @@ module Sentdm
104
131
  # produces a separate message per recipient. "sent" = auto-detect. Defaults to
105
132
  # ["sent"] (auto-detect) if omitted.
106
133
  channel: nil,
134
+ # Body param: Which of your own numbers to send from, keyed by channel, each
135
+ # channel holding a list of entries: {"sms": [{"country": "US", "from":
136
+ # ["+12125550000", "+14155550000"]}, {"from": ["+447700800001"]}]}. Any real
137
+ # channel may be a key; sent, which is auto-detect rather than a channel, is
138
+ # rejected. country and strategy are accepted and stored but not acted on yet:
139
+ # every entry's numbers apply to every recipient on that channel.
140
+ #
141
+ # This does not choose channels — Channel does, and the two combine: "channel":
142
+ # ["sms"] with an sms list sends on SMS from those numbers. Each list only narrows
143
+ # which of its own channel's routes may win, so with Channel left at auto-detect a
144
+ # recipient best served by a channel with no list still goes out on it. Routing
145
+ # itself is unchanged: the same rules are scored and ranked the same way, with
146
+ # routes pinned to numbers you did not list removed from the running.
147
+ #
148
+ # Every number must be an active sender on your account. The request itself is
149
+ # still accepted (202) if one is not — like every other send-time rule, that is
150
+ # decided per message, so each affected message is recorded BLOCKED with error
151
+ # code BUSINESS_029 and reported on GET /v3/messages and the status webhook.
152
+ channels: nil,
107
153
  # Body param: Attachments for this send, as publicly fetchable https URLs. Used by
108
154
  # the MMS channel and ignored by every other one.
109
155
  #
@@ -3,6 +3,7 @@ module Sentdm
3
3
  type message_send_params =
4
4
  {
5
5
  channel: ::Array[String]?,
6
+ channels: ::Hash[Symbol, ::Array[Sentdm::MessageSendParams::Channel]]?,
6
7
  media_urls: ::Array[String]?,
7
8
  sandbox: bool,
8
9
  scheduled_at: Time?,
@@ -21,6 +22,8 @@ module Sentdm
21
22
 
22
23
  attr_accessor channel: ::Array[String]?
23
24
 
25
+ attr_accessor channels: ::Hash[Symbol, ::Array[Sentdm::MessageSendParams::Channel]]?
26
+
24
27
  attr_accessor media_urls: ::Array[String]?
25
28
 
26
29
  attr_reader sandbox: bool?
@@ -49,6 +52,7 @@ module Sentdm
49
52
 
50
53
  def initialize: (
51
54
  ?channel: ::Array[String]?,
55
+ ?channels: ::Hash[Symbol, ::Array[Sentdm::MessageSendParams::Channel]]?,
52
56
  ?media_urls: ::Array[String]?,
53
57
  ?sandbox: bool,
54
58
  ?scheduled_at: Time?,
@@ -63,6 +67,7 @@ module Sentdm
63
67
 
64
68
  def to_hash: -> {
65
69
  channel: ::Array[String]?,
70
+ channels: ::Hash[Symbol, ::Array[Sentdm::MessageSendParams::Channel]]?,
66
71
  media_urls: ::Array[String]?,
67
72
  sandbox: bool,
68
73
  scheduled_at: Time?,
@@ -75,6 +80,29 @@ module Sentdm
75
80
  request_options: Sentdm::RequestOptions
76
81
  }
77
82
 
83
+ type channel =
84
+ { country: String?, from: ::Array[String]?, strategy: String? }
85
+
86
+ class Channel < Sentdm::Internal::Type::BaseModel
87
+ attr_accessor country: String?
88
+
89
+ attr_accessor from: ::Array[String]?
90
+
91
+ attr_accessor strategy: String?
92
+
93
+ def initialize: (
94
+ ?country: String?,
95
+ ?from: ::Array[String]?,
96
+ ?strategy: String?
97
+ ) -> void
98
+
99
+ def to_hash: -> {
100
+ country: String?,
101
+ from: ::Array[String]?,
102
+ strategy: String?
103
+ }
104
+ end
105
+
78
106
  type template =
79
107
  { id: String?, name: String?, parameters: ::Hash[Symbol, String]? }
80
108
 
@@ -15,6 +15,7 @@ module Sentdm
15
15
 
16
16
  def send_: (
17
17
  ?channel: ::Array[String]?,
18
+ ?channels: ::Hash[Symbol, ::Array[Sentdm::MessageSendParams::Channel]]?,
18
19
  ?media_urls: ::Array[String]?,
19
20
  ?sandbox: bool,
20
21
  ?scheduled_at: Time?,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentdm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.35.0
4
+ version: 0.37.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sent
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-01 00:00:00.000000000 Z
11
+ date: 2026-10-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cgi