Skip to content

Commit c1ca98a

Browse files
author
stlc-bot
committed
sdk: declare the channel and contact delivered-event models
Stainless-Generated-From: a3be0928eeed4ba84a319da96ba26c2b8c93ceb9
1 parent ec2272b commit c1ca98a

7 files changed

Lines changed: 320 additions & 298 deletions

File tree

‎api.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Types:
66
from sent_dm.types import (
77
APIMeta,
88
APIResponseWebhook,
9+
ChannelEvent,
10+
ChannelEventPayload,
11+
ContactEvent,
12+
ContactEventPayload,
913
ErrorDetail,
1014
InboundMessageEvent,
1115
InboundMessageEventPayload,

‎src/sent_dm/types/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from .template import Template as Template
99
from .error_detail import ErrorDetail as ErrorDetail
1010
from .tcr_vertical import TcrVertical as TcrVertical
11+
from .channel_event import ChannelEvent as ChannelEvent
12+
from .contact_event import ContactEvent as ContactEvent
1113
from .message_event import MessageEvent as MessageEvent
1214
from .user_response import UserResponse as UserResponse
1315
from .profile_detail import ProfileDetail as ProfileDetail
@@ -31,8 +33,10 @@
3133
from .me_retrieve_response import MeRetrieveResponse as MeRetrieveResponse
3234
from .template_list_params import TemplateListParams as TemplateListParams
3335
from .api_response_template import APIResponseTemplate as APIResponseTemplate
36+
from .channel_event_payload import ChannelEventPayload as ChannelEventPayload
3437
from .contact_create_params import ContactCreateParams as ContactCreateParams
3538
from .contact_delete_params import ContactDeleteParams as ContactDeleteParams
39+
from .contact_event_payload import ContactEventPayload as ContactEventPayload
3640
from .contact_update_params import ContactUpdateParams as ContactUpdateParams
3741
from .inbound_message_event import InboundMessageEvent as InboundMessageEvent
3842
from .message_event_payload import MessageEventPayload as MessageEventPayload

‎src/sent_dm/types/channel_event.py‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from typing import Optional
4+
5+
from .._models import BaseModel
6+
from .channel_event_payload import ChannelEventPayload
7+
8+
__all__ = ["ChannelEvent"]
9+
10+
11+
class ChannelEvent(BaseModel):
12+
"""The envelope Sent POSTs to a subscribed webhook endpoint.
13+
14+
Every event shares this shape and
15+
varies only in Payload.
16+
"""
17+
18+
event: Optional[str] = None
19+
"""
20+
The specific event within the family, for example message.delivered,
21+
message.received or contact.opt_out. Absent on events that have no subtype, so
22+
treat it as optional.
23+
"""
24+
25+
field: Optional[str] = None
26+
"""The event family, for example message, templates or contact.
27+
28+
Route on this first, then on event for the specific change.
29+
"""
30+
31+
payload: Optional[ChannelEventPayload] = None
32+
"""
33+
Body of a channel event: where one of the customer's channels stands in
34+
provisioning and compliance. Delivered when a milestone moves — a registration
35+
filed, a verdict returned, a resubmission asked for, a sender gone live — so a
36+
customer's own onboarding UI does not have to poll GET /v3/channels.
37+
38+
The subject is one item, never the account. A customer's "SMS channel" has no
39+
status; a market does. Country, NumberType and SenderValue name which one, so a
40+
customer terminating only to Kosovo never receives an event about US 10DLC.
41+
42+
Status is the stable half of the contract. It is the same four-value set GET
43+
/v3/channels publishes, computed through the same code, so an event and a read
44+
of the same market cannot disagree. A subscriber that reads nothing but the
45+
status and the subject fields is a correct subscriber. The sub-type on the
46+
envelope names the specific milestone and is additive — that vocabulary comes
47+
from registries and carriers, which are parties Sent does not control.
48+
49+
Status means provisioning and compliance are complete, not that a send will
50+
succeed right now. An account can be suspended, or a destination blocked by a
51+
routing rule, without either showing up here. Those are separate surfaces and
52+
deliberately not modelled on this payload.
53+
"""
54+
55+
request_id: Optional[str] = None
56+
"""The event-specific body."""
57+
58+
timestamp: Optional[str] = None
59+
"""When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ).
60+
61+
This is the emission time, not the time the underlying change happened. Use the
62+
timestamp inside the payload for the latter.
63+
"""
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from typing import Optional
4+
5+
from .._models import BaseModel
6+
7+
__all__ = ["ChannelEventPayload"]
8+
9+
10+
class ChannelEventPayload(BaseModel):
11+
"""
12+
Body of a channel event: where one of the customer's channels stands in provisioning and
13+
compliance. Delivered when a milestone moves — a registration filed, a verdict returned, a
14+
resubmission asked for, a sender gone live — so a customer's own onboarding UI does not have to
15+
poll GET /v3/channels.
16+
17+
The subject is one item, never the account. A customer's "SMS channel" has no
18+
status; a market does. Country, NumberType and
19+
SenderValue name which one, so a customer terminating only to Kosovo never
20+
receives an event about US 10DLC.
21+
22+
Status is the stable half of the contract. It is the same four-value
23+
set GET /v3/channels publishes, computed through the same code, so an event and a read of
24+
the same market cannot disagree. A subscriber that reads nothing but the status and the subject
25+
fields is a correct subscriber. The sub-type on the envelope names the specific milestone and is
26+
additive — that vocabulary comes from registries and carriers, which are parties Sent does not
27+
control.
28+
29+
Status means provisioning and compliance are complete, not that a send
30+
will succeed right now. An account can be suspended, or a destination blocked by a routing
31+
rule, without either showing up here. Those are separate surfaces and deliberately not modelled
32+
on this payload.
33+
"""
34+
35+
country: str
36+
"""The market's destination country as an ISO 3166-1 alpha-2 code, for example XK.
37+
38+
Always present, and the property that identifies this payload among the
39+
delivered envelopes — see DeliveredWebhookEvents. Every event in this family
40+
reports one market, and a market has a country.
41+
"""
42+
43+
account_id: Optional[str] = None
44+
"""The account whose market this is, named as on every other family.
45+
46+
When an organization receives an event for one of its sender profiles this is
47+
the profile, so a reseller compares it with its own id and anything different is
48+
one of its profiles.
49+
"""
50+
51+
channel: Optional[str] = None
52+
"""The channel this market belongs to: sms, whatsapp, or rcs.
53+
54+
Never sent — that value belongs to message events, where it names the
55+
smart-routing brand rather than a channel that can be provisioned.
56+
"""
57+
58+
number_type: Optional[str] = None
59+
"""The kind of sender the market uses, for example TEN_DLC, LOCAL, or ALPHANUMERIC.
60+
61+
Omitted when the subject has no sender type of its own.
62+
"""
63+
64+
reason: Optional[str] = None
65+
"""
66+
Why the market reached this state, when a reason was given — a correction
67+
explained, or a campaign lapse. Free text, passed through from the registry or
68+
carrier that wrote it, so treat it as a message to show a human rather than a
69+
value to branch on.
70+
"""
71+
72+
sender_value: Optional[str] = None
73+
"""The sender itself — a number in E.164, or an alphanumeric sender ID.
74+
75+
Always present, and null until a sender exists. The key is on every delivery so
76+
a subscriber reads one shape rather than branching on whether the field arrived
77+
— the same choice template_id makes on the message payload.
78+
79+
It can carry a value at any point in the lifecycle, not only once the market is
80+
live: a number ordered and not yet active at the carrier is already known during
81+
PROVISIONING, and an alphanumeric sender the customer chose themselves is known
82+
before anything is filed. It is null while the market is still waiting on a
83+
number, which for a US 10DLC registration is every event up to
84+
channel.activated.
85+
"""
86+
87+
status: Optional[str] = None
88+
"""
89+
Where the market stands: PENDING_REVIEW, ACTION_NEEDED, PROVISIONING, ACTIVE or
90+
INACTIVE. PENDING_REVIEW means a registry or a carrier holds it and the wait is
91+
theirs; ACTION_NEEDED means it is yours; PROVISIONING means the verdict is in
92+
and Sent is acquiring the sender; INACTIVE means it had a working sender and no
93+
longer does.
94+
95+
Each event name is the transition into one of these, but the two are separate
96+
fields and may legitimately differ. A resubmission filed against a market whose
97+
sender is already live is channel.submitted carrying ACTIVE: a correction is
98+
with the registry and the sender keeps working. Read both.
99+
"""
100+
101+
updated_at: Optional[str] = None
102+
"""When the transition happened, in UTC (yyyy-MM-ddTHH:mm:ssZ)."""

‎src/sent_dm/types/contact_event.py‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from typing import Optional
4+
5+
from .._models import BaseModel
6+
from .contact_event_payload import ContactEventPayload
7+
8+
__all__ = ["ContactEvent"]
9+
10+
11+
class ContactEvent(BaseModel):
12+
"""The envelope Sent POSTs to a subscribed webhook endpoint.
13+
14+
Every event shares this shape and
15+
varies only in Payload.
16+
"""
17+
18+
event: Optional[str] = None
19+
"""
20+
The specific event within the family, for example message.delivered,
21+
message.received or contact.opt_out. Absent on events that have no subtype, so
22+
treat it as optional.
23+
"""
24+
25+
field: Optional[str] = None
26+
"""The event family, for example message, templates or contact.
27+
28+
Route on this first, then on event for the specific change.
29+
"""
30+
31+
payload: Optional[ContactEventPayload] = None
32+
"""Body of a contact.opt_in, contact.opt_out or contact.help event.
33+
34+
Delivered when a contact signals a consent change or asks for help.
35+
36+
These events state the signal outright, so you do not have to recognise keywords
37+
in the text of a message.received event. They also cover cases that produce no
38+
inbound message at all, such as a network handling an opt-out on your behalf.
39+
40+
Fields are ordered identity → resulting state → provenance → join key. Nothing
41+
here restates the envelope: which of the three signals occurred is the
42+
envelope's event, and when it was emitted is its timestamp. Retries carry the
43+
same X-Webhook-Event-ID header, which is what to deduplicate on.
44+
"""
45+
46+
request_id: Optional[str] = None
47+
"""The event-specific body."""
48+
49+
timestamp: Optional[str] = None
50+
"""When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ).
51+
52+
This is the emission time, not the time the underlying change happened. Use the
53+
timestamp inside the payload for the latter.
54+
"""
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from typing import Optional
4+
5+
from .._models import BaseModel
6+
7+
__all__ = ["ContactEventPayload"]
8+
9+
10+
class ContactEventPayload(BaseModel):
11+
"""Body of a contact.opt_in, contact.opt_out or contact.help event.
12+
13+
Delivered
14+
when a contact signals a consent change or asks for help.
15+
16+
These events state the signal outright, so you do not have to recognise keywords in the
17+
text of a message.received event. They also cover cases that produce no inbound message
18+
at all, such as a network handling an opt-out on your behalf.
19+
20+
Fields are ordered identity → resulting state → provenance → join key. Nothing here
21+
restates the envelope: which of the three signals occurred is the envelope's event, and
22+
when it was emitted is its timestamp. Retries carry the same X-Webhook-Event-ID
23+
header, which is what to deduplicate on.
24+
"""
25+
26+
opt_out: bool
27+
"""
28+
Whether the contact is opted out after this signal — the state to write to your
29+
own record. Same meaning as opt_out on the contact resource. On contact.help
30+
this reports the contact's existing state, which help does not change.
31+
32+
Two signals from the same contact can arrive out of order, because each one is
33+
queued on its own rather than against the contact. Compare the envelope's
34+
timestamp before you overwrite a newer state with an older one. That timestamp
35+
is second-precision, so treat two signals stamped in the same second as
36+
unordered and read the contact resource to settle them.
37+
"""
38+
39+
source: str
40+
"""How the signal reached us.
41+
42+
INBOUND_KEYWORD means the contact sent a message whose text matched one of the
43+
keywords; PROVIDER_SIGNAL means the network reported it. A provider signal
44+
usually carries no message_id or text, so read both for null rather than
45+
inferring them from this field.
46+
"""
47+
48+
account_id: Optional[str] = None
49+
"""The account the contact belongs to.
50+
51+
Present so one endpoint can serve several accounts.
52+
"""
53+
54+
channel: Optional[str] = None
55+
"""The channel the signal arrived on, for example sms or whatsapp."""
56+
57+
contact_id: Optional[str] = None
58+
"""The contact who raised the signal.
59+
60+
Always populated, including for contact.help from a number you have not messaged
61+
before — the contact is created if it does not exist yet, so this identifier is
62+
always resolvable against the contacts API.
63+
"""
64+
65+
message_id: Optional[str] = None
66+
"""
67+
The inbound message that carried the signal, matching message_id on the
68+
corresponding message.received event so the two can be joined.
69+
70+
Sent as null when the signal did not arrive as a message — for example when a
71+
network processed an opt-out on your behalf — and also when the message belongs
72+
to a different account than this event, which can happen on a shared WhatsApp
73+
number. The field is always present, so read it and check for null rather than
74+
checking whether the key exists.
75+
"""
76+
77+
phone_number: Optional[str] = None
78+
"""The contact's number in E.164 format.
79+
80+
Same value as phone_number on the contact resource.
81+
"""
82+
83+
text: Optional[str] = None
84+
"""The text the contact sent, for example STOP or UNSUBSCRIBE.
85+
86+
Sent as null when the signal did not arrive as text. The field is always
87+
present, so read it and check for null rather than checking whether the key
88+
exists.
89+
"""

0 commit comments

Comments
 (0)