twilio-ruby 7.12.0 → 7.13.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +201 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +2 -2
  5. data/examples/WebhookSignatureValidation.md +61 -0
  6. data/lib/rack/twilio_webhook_authentication.rb +30 -5
  7. data/lib/twilio-ruby/rest/api/v2010/account/call.rb +7 -0
  8. data/lib/twilio-ruby/rest/client.rb +5 -0
  9. data/lib/twilio-ruby/rest/content/v1/content/approval_create.rb +108 -1
  10. data/lib/twilio-ruby/rest/content/v1/content.rb +8 -2
  11. data/lib/twilio-ruby/rest/conversations/v2/configuration.rb +7 -0
  12. data/lib/twilio-ruby/rest/conversations/v2/conversation.rb +194 -7
  13. data/lib/twilio-ruby/rest/memory/v1/bulk.rb +22 -1
  14. data/lib/twilio-ruby/rest/memory/v1/import.rb +21 -7
  15. data/lib/twilio-ruby/rest/memory/v1/store.rb +7 -0
  16. data/lib/twilio-ruby/rest/trusthub/v1/a2p_brand_registration.rb +5 -5
  17. data/lib/twilio-ruby/rest/trusthub/v1/a2p_campaign_registration.rb +4 -4
  18. data/lib/twilio-ruby/rest/trusthub/v1.rb +2 -2
  19. data/lib/twilio-ruby/rest/webhooks/v1/auth_profile.rb +825 -0
  20. data/lib/twilio-ruby/rest/webhooks/v1/edge_zone.rb +320 -0
  21. data/lib/twilio-ruby/rest/webhooks/v1/operation.rb +513 -0
  22. data/lib/twilio-ruby/rest/webhooks/v1/rule.rb +818 -0
  23. data/lib/twilio-ruby/rest/webhooks/v1/setting.rb +1028 -0
  24. data/lib/twilio-ruby/rest/webhooks/v1/shared_key.rb +722 -0
  25. data/lib/twilio-ruby/rest/webhooks/v1/test.rb +228 -0
  26. data/lib/twilio-ruby/rest/webhooks/v1.rb +126 -0
  27. data/lib/twilio-ruby/rest/webhooks.rb +6 -0
  28. data/lib/twilio-ruby/rest/webhooks_base.rb +38 -0
  29. data/lib/twilio-ruby/security/request_validator.rb +54 -8
  30. data/lib/twilio-ruby/twiml/voice_response.rb +20 -5
  31. data/lib/twilio-ruby/version.rb +1 -1
  32. metadata +13 -3
  33. data/lib/twilio-ruby/rest/oauth/v2/oauth_authorization_server.rb +0 -200
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de45c2f4e640d77d1ef3ce66c59c270c8c76a7bf60802a5168c0431f72ab3eb1
4
- data.tar.gz: 319065808ccb2132bc019d456490657bd25a07adcfa783f1b0815ea01b9e512e
3
+ metadata.gz: '0587ac5e14138ede69b18d56ff5965fa106d30ec11e17c08db0ca823a5bc64c7'
4
+ data.tar.gz: b07c0cf304d869f8d700e1ad5c37065b22bf2385340df09d6f2e29870d58e068
5
5
  SHA512:
6
- metadata.gz: f4eca7472b55d315dcf30b819e04754760fb49250151fb72912db9199502325b910b544464a788277c4aa0974c49872fdba45c6f0af96e52396341d0ce1356c2
7
- data.tar.gz: 5c7a1776e780a85d5ecfd9b2b928122baad0cd7ba3ee100cec889ba84a49b68970b38be01f1f72aa491f5fd36e7224cd9e792b5e64986d6eeb09bfe9c04f738a
6
+ metadata.gz: 448b5de7a2cd468f9e7c7029903eeba2ef39d1db284a66660100422a50e9ed60c422ff50ce1078e9e4864a167a6c58f0ead8c6689d305a279d5ca9c01f5fd452
7
+ data.tar.gz: b59a11d26dcd3eb65f27453259da7079fd966aff164261ef9c21ed015ffe0a006f8e493717f9a7c0ec0c40cc45e9358c0d6dc058e172c2b63f7b84104409ec9c
data/CHANGES.md CHANGED
@@ -1,5 +1,206 @@
1
1
  twilio-ruby changelog
2
2
  =====================
3
+ [2026-11-05] Version 7.13.0
4
+ ---------------------------
5
+ **Library - Chore**
6
+ - [PR #820](https://github.com/twilio/twilio-ruby/pull/820): support HMAC-SHA256 webhook signatures in RequestValidator. Thanks to [@sbansla](https://github.com/sbansla)!
7
+
8
+ **Twiml**
9
+ - Add ElevenLabs voices to `<Say>` verb.
10
+ - Add `partialPromptOnHangup`, `eagerEotThreshold`, `asrSmartFormat`, `ttsTextNormalization`, `ttsLanguageTextNormalization`, `elevenlabsLanguageTextNormalization`, `includeInPrompt`, `customerName`, `applicationReconnect`, `echoPromptFiltering`, `turnDetection` attributes to `<ConversationRelay>`, and `hints`, `asrSmartFormat`, `ttsTextNormalization`, `ttsLanguageTextNormalization` attributes to `<Language>`
11
+
12
+ **Api**
13
+ - # API Changes
14
+ - ## 2026-09-22
15
+ - Simplified the `criteria` field description.
16
+ - ## 2026-09-18
17
+ - Moved `readOnly` on the snapshot count fields (`profileCount`, `partitionCount`) from the shared schema to the response usage sites, so the counts stay read-only in responses (no change for API consumers).
18
+ - ## 2026-09-17
19
+ - Renamed 3 `Get*` operations to `Fetch*` to match the operationId standard: `FetchCohort`, `FetchSnapshot`, `FetchOperation`.
20
+ - Removed the unused `ExpressionId` schema and made the operation error example cohort-native, so no `aud_` identifiers remain in the spec.
21
+ - Kept `libraryVisibility: hidden` across the whole surface (info, path items, and operations).
22
+ - ## 2026-09-08
23
+ - Removed the deprecated header-based-versioning unified specs, consolidating audiences onto the path-based `api.twilio.com/v1` surface.
24
+ - ## 2026-08-31
25
+ - Published the unified audiences spec at `api.twilio.com/v1`.
26
+ - ## 2026-08-26
27
+ - Fixed the Cohort operation response mapping in the unified audiences API.
28
+ - ## 2026-08-19
29
+ - Migrated the unified audiences spec to the Cohort surface (Audiences renamed to Cohorts).
30
+ - Switched `downstreamServiceName` to `audiences-domain`.
31
+ - ## 2026-08-05
32
+ - Added the `api.unified.version.preview` beta feature.
33
+ - Made the unified APIs available in prod us1.
34
+ - ## 2026-07-30
35
+ - Fixed example values.
36
+ - ## 2026-07-29
37
+ - Added the initial version of the audiences API to the unified APIs.
38
+ - ## 2026-07-20
39
+ - Deployed the audiences-domain OAS to the dev edge.
40
+ - ## 2026-07-06
41
+ - Added the prod-us1 realm to the audiences public API.
42
+ - ## 2026-06-23
43
+ - Changed the `storeType` enum from `MEMORA` to `TWILIO`.
44
+ - ## 2026-05-04
45
+ - Added the stage-us1 realm and `x-rate-limit` to the audiences API.
46
+ - ## 2026-04-08
47
+ - Added the initial audiences endpoints.
48
+ - # API Changes
49
+ - ## 2026-10-28
50
+ - Enabled the Redaction Configurations and Redaction Catalog resources in prod-us1.
51
+ - ## 2026-10-27
52
+ - Enabled the Redaction Configurations and Redaction Catalog resources in stage-us1.
53
+ - ## 2026-09-28
54
+ - Added the Redaction Configurations resource (`/v1/RedactionConfigurations`) as a private beta in dev-us1, gated by the `api.redaction.enabled` beta feature.
55
+ - Added the Redaction Catalog resource (`/v1/RedactionCatalog`), under the same beta feature. It is restricted to One Console.
56
+ - Add `ApplicationSid` parameter to TwimlSession and TwimlProcess Create endpoints
57
+
58
+ **Audiences**
59
+ - ## 2026-10-23
60
+ - **Content updates**:
61
+ - Updated description for `AdminFetchRecord`
62
+ - Updated description for `AdminListCohorts`
63
+ - Updated description for `AdminListCohortSnapshots`
64
+ - Updated description for `AdminFetchCohortOperation`
65
+ - Updated description for `AdminListSnapshotOperations`
66
+ - Updated schema description for `OperationStatus`
67
+ - Added properties to `Cohort`: tags
68
+ - Removed properties from `Cohort`: labels
69
+ - Added properties to `CohortSnapshot`: tags
70
+ - Removed properties from `CohortSnapshot`: labels
71
+ - ## 2026-10-23
72
+ - **Content updates**:
73
+ - Updated description for `ListCohorts`
74
+ - Updated description for `CreateCohort`
75
+ - Updated description for `UpdateCohort`
76
+ - Updated description for `ListCohortSnapshots`
77
+ - Updated description for `CreateCohortSnapshot`
78
+ - Updated description for `FetchCohortOperation`
79
+ - Updated schema description for `OperationStatus`
80
+ - Added properties to `Cohort`: tags
81
+ - Removed properties from `Cohort`: labels
82
+ - Added properties to `CohortSnapshot`: tags
83
+ - ...and 8 more changes
84
+ - ## 2026-10-14
85
+ - Minor updates (formatting, metadata)
86
+ - ## 2026-10-07
87
+ - Minor updates (formatting, metadata)
88
+
89
+ **Conversations**
90
+ - Enable the Imports endpoints (list, create, fetch, cancel) and the Import Outcomes list endpoint in prod-us1
91
+ - Add GET /v2/Imports/{ImportId}/Outcomes to list an import's per-resource outcomes (dev-us1 and stage-us1)
92
+ - Add `EMAIL` to the Conversations v2 Communication channel enum.
93
+
94
+ **Deletions**
95
+ - ## 2026-10-28
96
+ - Enable the User Data Deletions API in the `stage-us1` realm.
97
+
98
+ **Destinations**
99
+ - ## 2026-11-03
100
+ - Enable Replay admin APIs for prod-us1 and prod-ie1 realms
101
+
102
+ **Iam**
103
+ - Raised the most permissions in a POST /v1/Account/AuthorizedApps section, and in a part of the PUT /v1/Organization/AgentsRegistration/Permissions request, to 2000 for MCP agents; the limit of other consents is unchanged
104
+ - Lowered the most permissions in a POST /v1/Account/AuthorizedApps section to 150, which is what an XMS policy holds
105
+ - Made the GET and PUT /v1/Organization/AgentsRegistration/Permissions routes available in stage-us1 and prod-us1
106
+ - Added the MCP agent consent fields to GET /v1/Account/AuthorizedApps: agent, account, organization, sections (the offered permissions per context) and redirect, and a 401 body with redirect when the account's organization has not registered or enabled the agent (code 25110, with organization_sid) or the account has no organization (code 25111). oauth_app_sid, client_sid, consent_state and app_permissions are now nullable, since an MCP agent response does not use them
107
+ - Added sections (at least 1) with permissionSelectionMode (ALL_READ, ALL_READ_WRITE, SPECIFIC) and permissions (at most 200) to the POST /v1/Account/AuthorizedApps request, and response_mode and form_params to its 201 response
108
+ - Made the /v1/Organization/AgentsRegistration routes available in stage-us1 and prod-us1
109
+ - Added created_by (the name of the user who registered the agent) and created_by_sid to the /v1/Organization/AgentsRegistration responses; both are null for a registration made before the user was recorded
110
+ - Moved the POST /v1/actions/post_login (PostLoginActionRequest) webhook payload to camelCase (accountSid, nodeId, actionId, userContext, authenticationContext, requestContext, ipAddress, userAgent, requestId, clientId, identifierType); replaced timestamp with the RFC 3339 sentAt, made authorizationDetails a JSON array, made completedAt an RFC 3339 string, and added userContext (isFirstLogin, user) with the CIAM id at userContext.user.userId
111
+ - Added the optional type, agent, account, organization, sections, last_used and expires_at to the iam.v1.consent responses of the GET /v1/Account/AuthorizedApps and GET /v1/Organization/AuthorizedApps lists, for MCP connections. The list shows an MCP connection beside the OAuth applications, and deleting it revokes the whole connection
112
+ - Added a 404 to PUT /v1/Account/AuthorizedApps/{consentSid} for an MCP connection, which cannot be updated
113
+ - Added the GET and PUT /v1/Organization/AgentsRegistration/Permissions routes for what any agent may do in the caller's organization (dev-us1)
114
+ - Renamed the properties of the /v1/Organization/AgentsRegistration responses to snake_case (registration_sid, friendly_name, client_url, date_created, date_updated), as the other IAM responses are; the request bodies keep clientUrl and enabled
115
+ - Fixed the pagination meta of the GET /v1/Organization/AgentsRegistration response: it is mapped from the service's snake_case fields, as in the other IAM list routes, so key, pageSize and the tokens are no longer null
116
+ - Added the /v1/Organization/AgentsRegistration routes (list, register, fetch, enable or disable, remove) for the MCP agents an organization lets its users connect (dev-us1)
117
+
118
+ **Instrumentation**
119
+ - # API Changes
120
+ - ## 2026-10-12
121
+ - Initial read-only admin-api spec for the Behavioral Events (events-domain) One Admin console: event producers (list, fetch), write keys (list), event schemas (list, fetch). dev-us1 and stage-us1 only.
122
+ - ## 2026-10-27
123
+ - **Control Plane create, update and attach are synchronous** (`dev-us1`), matching the deployed service:
124
+ - CreateEventProducer returns `201` with the event producer (`ETag`, `Idempotent-Replayed`), not `202` with an operation
125
+ - PatchEventProducer returns `200` with the event producer (`ETag`), not `202`; `If-Match` is its only header parameter
126
+ - AttachEventProducerSchemaVersion returns `201` with the attachment (`Idempotent-Replayed`), not `202`
127
+ - ## 2026-10-21
128
+ - **Replaced the Control Plane with the private-beta API** (`/v1/ControlPlane/…`), enabled in `dev-us1` only:
129
+ - Removed the EventSources, Datasets, EventSources EventSchemas and AutoInstrumentationRules operations
130
+ - Added `/v1/ControlPlane/EventProducers` (CreateEventProducer, ListEventProducers)
131
+ - Added `/v1/ControlPlane/EventProducers/{producerId}` (FetchEventProducer, PatchEventProducer, DeleteEventProducer)
132
+ - Added `/v1/ControlPlane/EventProducers/{producerId}/WriteKeys` (CreateWriteKey, ListWriteKeys)
133
+ - Added `/v1/ControlPlane/EventProducers/{producerId}/WriteKeys/{writeKey}` (DeleteWriteKey)
134
+ - Added `/v1/ControlPlane/EventProducers/{producerId}/EventSchemas` (AttachEventProducerSchemaVersion, ListEventProducerSchemaAttachments)
135
+ - Added `/v1/ControlPlane/EventProducers/{producerId}/EventSchemas/{schemaId}/Versions/{version}` (DetachEventProducerSchemaVersion)
136
+ - Added `/v1/ControlPlane/EventSchemas` (CreateEventSchema, ListEventSchemas)
137
+ - Added `/v1/ControlPlane/EventSchemas/{schemaId}` (FetchEventSchema, PatchEventSchema, DeleteEventSchema)
138
+ - Added `/v1/ControlPlane/EventSchemas/{schemaId}/Versions` (ListEventSchemaVersions)
139
+ - Added `/v1/ControlPlane/EventSchemas/{schemaId}/Versions/{version}` (FetchEventSchemaVersion, DeleteEventSchemaVersion)
140
+ - Updated `/v1/ControlPlane/Operations/{operationId}` (FetchControlPlaneOperationStatus) to the new operation shape; now `dev-us1` only
141
+
142
+ **Knowledge**
143
+ - ## 2026-10-27
144
+ - **Added 1 new path(s)**:
145
+ - `/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Limits` (AdminFetchKnowledgeMigrationLimits)
146
+ - ## 2026-10-21
147
+ - **Content updates**:
148
+ - `AdminListKnowledgeMigrations`: the `state` query parameter is optional; without it the list returns every account with knowledge bases
149
+ - Added properties to `MigrationSummary`: knowledgeBaseCount
150
+ - ## 2026-10-19
151
+ - **Added 7 new path(s)**:
152
+ - `/v2/ControlPlane/KnowledgeMigrations` (AdminListKnowledgeMigrations)
153
+ - `/v2/ControlPlane/KnowledgeMigrations/{accountSid}` (AdminFetchKnowledgeMigration)
154
+ - `/v2/ControlPlane/KnowledgeMigrations/{accountSid}/History` (AdminListKnowledgeMigrationHistory)
155
+ - `/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Readiness` (AdminFetchKnowledgeMigrationReadiness)
156
+ - `/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Runs` (AdminListKnowledgeMigrationRuns)
157
+ - `/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Transitions` (AdminListKnowledgeMigrationTransitions, AdminCreateKnowledgeMigrationTransition)
158
+ - `/v2/ControlPlane/KnowledgeMigrations/{accountSid}/Transitions/{transitionId}` (AdminFetchKnowledgeMigrationTransition)
159
+
160
+ **Lookups**
161
+ - Add test api support for Lookup Bulk Jobs endpoints
162
+
163
+ **Memory**
164
+ - ## 2026-10-14
165
+ - No path changes (schema only)
166
+ - Added optional `dataAsOf` to the `Store` schema returned by `AdminFetchStore`.
167
+ - ## 2026-10-14
168
+ - Added optional `dataAsOf` to the `Store` schema returned by `FetchStore`.
169
+ - ## 2026-10-08
170
+ - Added `FetchProfileOperation` (`GET /v1/ProfileOperations/{operationId}`) to check the status and
171
+ - progress of an asynchronous profile operation. The response includes a `Retry-After` header with
172
+ - the minimum number of seconds to wait before polling again. The endpoint is hidden from the
173
+ - helper libraries and gated behind the `api.memory.2026Q3` beta feature.
174
+ - The `202` responses of `UpdateProfilesBulk` and `CreateProfilesImportV2` now return `operationId`,
175
+ - `createdAt` and `statusUrl` (behind the `api.memory.2026Q3` beta feature), plus the `Operation-Id`,
176
+ - `Location` and `Retry-After` response headers. Use `statusUrl` with `FetchProfileOperation` to
177
+ - track the operation.
178
+ - Additive and backwards compatible: existing request and response fields are unchanged.
179
+
180
+ **Messaging**
181
+ - Add data access policy admin endpoints (`GET/PUT /v1/Accounts/{AccountSid}/DataAccessPolicy`) to the messaging admin-api, backed by `postflight-messaging-api` (dev-us1 and stage-us1 only)
182
+ - Enable data access policy admin endpoints (`GET/PUT /v1/Accounts/{AccountSid}/DataAccessPolicy`) in prod-us1 and prod-ie1
183
+
184
+ **Segment**
185
+ - # API Changes
186
+ - ## 2026-11-04
187
+ - Add read-only Cob routes: `GET /v1/Segment/Cob/Whoami`, `/Records`, `/Audits`, `/Audits/{Family}/{Name}`
188
+ - ## 2026-10-30
189
+ - Add `GET /v1/Whoami` (One Admin identity check for the Segment oneadmin-gateway)
190
+
191
+ **Trusthub**
192
+ - Make id, sessionId and sessionToken required and non-nullable in the A2P embedded session response. **(breaking change)**
193
+
194
+ **Voice**
195
+ - Add internal console ProvisioningStatus endpoint.
196
+
197
+ **Webhooks**
198
+ - ## 2026-10-28
199
+ - **Changed**: Rule `prefix` and `value` are limited to 1000 characters. A `prefix` must end with `/` and a `value` must not.
200
+ - ## 2026-10-13
201
+ - API maturity level changed from `beta` to `ga`. Library visibility changed from `private` to `public`.
202
+
203
+
3
204
  [2026-10-12] Version 7.12.0
4
205
  ---------------------------
5
206
  **Audiences**
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- twilio-ruby (7.12.0)
4
+ twilio-ruby (7.13.0)
5
5
  faraday (>= 2.0, < 3.0)
6
6
  jwt (>= 1.5, < 4.0)
7
7
  nokogiri (>= 1.6, < 2.0)
@@ -144,7 +144,7 @@ CHECKSUMS
144
144
  rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
145
145
  ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
146
146
  simplecov (1.0.3) sha256=38ef0514f16ae7562f0d0f4df02610071115103d301b6de7dacbcc000082e39b
147
- twilio-ruby (7.12.0)
147
+ twilio-ruby (7.13.0)
148
148
  unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
149
149
  unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
150
150
  uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
data/README.md CHANGED
@@ -39,13 +39,13 @@ This library supports the following Ruby implementations:
39
39
  To install using [Bundler][bundler] grab the latest stable version:
40
40
 
41
41
  ```ruby
42
- gem 'twilio-ruby', '~> 7.12.0'
42
+ gem 'twilio-ruby', '~> 7.13.0'
43
43
  ```
44
44
 
45
45
  To manually install `twilio-ruby` via [Rubygems][rubygems] simply gem install:
46
46
 
47
47
  ```bash
48
- gem install twilio-ruby -v 7.12.0
48
+ gem install twilio-ruby -v 7.13.0
49
49
  ```
50
50
 
51
51
  To build and install the development branch yourself from the latest source:
@@ -0,0 +1,61 @@
1
+ # Validating webhook signatures (HMAC-SHA1 and HMAC-SHA256)
2
+
3
+ By default Twilio signs webhooks with your Auth Token using HMAC-SHA1. If a
4
+ [Webhooks Rule](https://www.twilio.com/docs/usage/webhooks/webhook-rules) matches the webhook URL, Twilio signs
5
+ with the Setting's SharedKey and algorithm instead, and adds this header:
6
+
7
+ ```
8
+ X-Twilio-Webhooks-Signature-Settings: key=<sharedKeyId>,algorithm=HMAC_SHA256
9
+ ```
10
+
11
+ Only the hash changes. The signed string is still the full URL followed by the sorted POST parameters.
12
+
13
+ ## Option 1: Rack middleware
14
+
15
+ `Rack::TwilioWebhookAuthentication` reads the header for you. Pass the SharedKey secret next to the Auth Token:
16
+
17
+ ```ruby
18
+ require 'rack'
19
+
20
+ use Rack::TwilioWebhookAuthentication, ENV['AUTH_TOKEN'], /\/voice/,
21
+ shared_key_secret: ENV['SHARED_KEY_SECRET']
22
+ ```
23
+
24
+ - Header absent: validates with the Auth Token and HMAC-SHA1.
25
+ - Header present: validates with `shared_key_secret` and the header's algorithm.
26
+ - Invalid signature or unsupported algorithm: responds 403.
27
+ - Header present but `shared_key_secret` not set: responds 500.
28
+
29
+ ## Option 2: Validate manually
30
+
31
+ ```ruby
32
+ validator_class = Twilio::Security::RequestValidator
33
+
34
+ signature = request.env['HTTP_X_TWILIO_SIGNATURE']
35
+ settings = request.env['HTTP_X_TWILIO_WEBHOOKS_SIGNATURE_SETTINGS']
36
+
37
+ # No header means the Auth Token and HMAC_SHA1
38
+ secret = settings ? shared_key_secret : auth_token
39
+ algorithm = validator_class.signature_algorithm(settings)
40
+
41
+ validator = validator_class.new(secret)
42
+
43
+ # Form-encoded body
44
+ valid = validator.validate(url, params, signature, algorithm)
45
+
46
+ # JSON body (the URL carries bodySHA256)
47
+ valid_json = validator.validate(url, raw_body, signature, algorithm)
48
+ ```
49
+
50
+ If you use more than one SharedKey, look up the secret by the `key=` value in the header.
51
+
52
+ Twilio signs empty parameters too, so keep them when you parse the form body (for example `URI.decode_www_form`).
53
+
54
+ ## Existing code
55
+
56
+ The `algorithm` argument is optional and defaults to HMAC-SHA1, so code that validates with the Auth Token needs no change:
57
+
58
+ ```ruby
59
+ Twilio::Security::RequestValidator.new(auth_token).validate(url, params, signature)
60
+ use Rack::TwilioWebhookAuthentication, ENV['AUTH_TOKEN'], /\/voice/
61
+ ```
@@ -20,15 +20,28 @@ module Rack
20
20
  # the ENV and only against paths that match /\/messages/. If the request
21
21
  # validates then it gets passed on to the action as normal. If the request
22
22
  # doesn't validate then the middleware responds immediately with a 403 status.
23
+ #
24
+ # Webhooks signed with a SharedKey (see the Webhooks Rules API) carry an
25
+ # X-Twilio-Webhooks-Signature-Settings header naming the signing algorithm, and
26
+ # are signed with the SharedKey secret instead of the auth token. Pass it with
27
+ # the shared_key_secret option:
28
+ #
29
+ # use Rack::TwilioWebhookAuthentication, ENV['AUTH_TOKEN'], /\/voice/, shared_key_secret: ENV['SHARED_KEY_SECRET']
30
+ #
31
+ # Requests without that header are still validated with the auth token and
32
+ # HMAC-SHA1. A request with the header but no shared_key_secret configured gets
33
+ # a 500, as the middleware cannot validate it.
23
34
 
24
35
  class TwilioWebhookAuthentication
25
36
  # Rack's FORM_DATA_MEDIA_TYPES can be modified to taste, so we're slightly
26
37
  # more conservative in what we consider form data.
27
38
  FORM_URLENCODED_MEDIA_TYPE = Rack::MediaType.type('application/x-www-form-urlencoded')
39
+ SIGNATURE_SETTINGS_ENV_KEY = 'HTTP_X_TWILIO_WEBHOOKS_SIGNATURE_SETTINGS'
28
40
 
29
- def initialize(app, auth_token, *paths, &auth_token_lookup)
41
+ def initialize(app, auth_token, *paths, shared_key_secret: nil, &auth_token_lookup)
30
42
  @app = app
31
43
  @auth_token = auth_token
44
+ @shared_key_secret = shared_key_secret
32
45
  define_singleton_method(:get_auth_token, auth_token_lookup) if block_given?
33
46
  @path_regex = Regexp.union(paths)
34
47
  end
@@ -38,10 +51,14 @@ module Rack
38
51
  request = Rack::Request.new(env)
39
52
  original_url = request.url
40
53
  params = extract_params!(request)
41
- auth_token = @auth_token || get_auth_token(params['AccountSid'])
42
- validator = Twilio::Security::RequestValidator.new(auth_token)
54
+ signature_settings = env[SIGNATURE_SETTINGS_ENV_KEY]
55
+ secret = signature_settings ? @shared_key_secret : (@auth_token || get_auth_token(params['AccountSid']))
56
+ return missing_shared_key_secret_response if secret.nil?
57
+
58
+ validator = Twilio::Security::RequestValidator.new(secret)
43
59
  signature = env['HTTP_X_TWILIO_SIGNATURE'] || ''
44
- if validator.validate(original_url, params, signature)
60
+ algorithm = Twilio::Security::RequestValidator.signature_algorithm(signature_settings)
61
+ if validator.validate(original_url, params, signature, algorithm)
45
62
  @app.call(env)
46
63
  else
47
64
  [
@@ -52,6 +69,14 @@ module Rack
52
69
  end
53
70
  end
54
71
 
72
+ def missing_shared_key_secret_response
73
+ [
74
+ 500,
75
+ { 'Content-Type' => 'text/plain' },
76
+ ['Twilio Request Validation Failed: no SharedKey secret configured for a SharedKey-signed request.']
77
+ ]
78
+ end
79
+
55
80
  # Decide whether a request path needs its signature validated.
56
81
  #
57
82
  # PATH_INFO arrives percent-encoded, but the routers we sit in front of
@@ -110,6 +135,6 @@ module Rack
110
135
  end
111
136
  end
112
137
 
113
- private :extract_params!, :protected_path?, :decode, :remove_dot_segments
138
+ private :missing_shared_key_secret_response, :extract_params!, :protected_path?, :decode, :remove_dot_segments
114
139
  end
115
140
  end
@@ -1078,6 +1078,7 @@ module Twilio
1078
1078
  'group_sid' => payload['group_sid'],
1079
1079
  'caller_name' => payload['caller_name'],
1080
1080
  'queue_time' => payload['queue_time'],
1081
+ 'stir_verstat' => payload['stir_verstat'],
1081
1082
  'trunk_sid' => payload['trunk_sid'],
1082
1083
  'uri' => payload['uri'],
1083
1084
  'subresource_uris' => payload['subresource_uris'],
@@ -1237,6 +1238,12 @@ module Twilio
1237
1238
  @properties['queue_time']
1238
1239
  end
1239
1240
 
1241
+ ##
1242
+ # @return [String] The STIR/SHAKEN verification status of the call, for example `TN-Validation-Passed-A`. Empty if the incoming call had no STIR/SHAKEN information.
1243
+ def stir_verstat
1244
+ @properties['stir_verstat']
1245
+ end
1246
+
1240
1247
  ##
1241
1248
  # @return [String] The unique identifier of the trunk resource that was used for this call. The field is empty if the call was not made using a SIP trunk or if the call is not terminated.
1242
1249
  def trunk_sid
@@ -202,6 +202,11 @@ module Twilio
202
202
  @voice ||= Voice.new self
203
203
  end
204
204
  ##
205
+ # Access the Webhooks Twilio Domain
206
+ def webhooks
207
+ @webhooks ||= Webhooks.new self
208
+ end
209
+ ##
205
210
  # Access the Wireless Twilio Domain
206
211
  def wireless
207
212
  @wireless ||= Wireless.new self
@@ -25,17 +25,69 @@ module Twilio
25
25
  # @param [name]: [String] Name of the template.
26
26
  # @param [category]: [String] A WhatsApp recognized template category.
27
27
  # @param [send_ttl_seconds]: [String] Time-to-live in seconds for attempting to send a message with this Content
28
- attr_accessor :name, :category, :send_ttl_seconds
28
+ # @param [bid_amount]: [String] Maximum price per 1,000 message deliveries, expressed in the WABA currency's smallest unit (cents for USD). Only valid for MARKETING templates onboarded to Max Pricing.
29
+ attr_accessor :name, :category, :send_ttl_seconds, :bid_amount
29
30
  def initialize(payload)
30
31
  @name = payload["name"]
31
32
  @category = payload["category"]
32
33
  @send_ttl_seconds = payload["send_ttl_seconds"]
34
+ @bid_amount = payload["bid_amount"]
33
35
  end
34
36
  def to_json(options = {})
35
37
  {
36
38
  "name": @name,
37
39
  "category": @category,
38
40
  "send_ttl_seconds": @send_ttl_seconds,
41
+ "bid_amount": @bid_amount,
42
+ }.to_json(options)
43
+ end
44
+ end
45
+
46
+ class ContentApprovalUpdateRequest
47
+ # @param [bid_amount]: [String] Maximum price per 1,000 message deliveries, expressed in the WABA currency's smallest unit (cents for USD). Only valid for MARKETING templates onboarded to Max Pricing.
48
+ attr_accessor :bid_amount
49
+ def initialize(payload)
50
+ @bid_amount = payload["bid_amount"]
51
+ end
52
+ def to_json(options = {})
53
+ {
54
+ "bid_amount": @bid_amount,
55
+ }.to_json(options)
56
+ end
57
+ end
58
+
59
+
60
+ class ContentApprovalRequest
61
+ # @param [name]: [String] Name of the template.
62
+ # @param [category]: [String] A WhatsApp recognized template category.
63
+ # @param [send_ttl_seconds]: [String] Time-to-live in seconds for attempting to send a message with this Content
64
+ # @param [bid_amount]: [String] Maximum price per 1,000 message deliveries, expressed in the WABA currency's smallest unit (cents for USD). Only valid for MARKETING templates onboarded to Max Pricing.
65
+ attr_accessor :name, :category, :send_ttl_seconds, :bid_amount
66
+ def initialize(payload)
67
+ @name = payload["name"]
68
+ @category = payload["category"]
69
+ @send_ttl_seconds = payload["send_ttl_seconds"]
70
+ @bid_amount = payload["bid_amount"]
71
+ end
72
+ def to_json(options = {})
73
+ {
74
+ "name": @name,
75
+ "category": @category,
76
+ "send_ttl_seconds": @send_ttl_seconds,
77
+ "bid_amount": @bid_amount,
78
+ }.to_json(options)
79
+ end
80
+ end
81
+
82
+ class ContentApprovalUpdateRequest
83
+ # @param [bid_amount]: [String] Maximum price per 1,000 message deliveries, expressed in the WABA currency's smallest unit (cents for USD). Only valid for MARKETING templates onboarded to Max Pricing.
84
+ attr_accessor :bid_amount
85
+ def initialize(payload)
86
+ @bid_amount = payload["bid_amount"]
87
+ end
88
+ def to_json(options = {})
89
+ {
90
+ "bid_amount": @bid_amount,
39
91
  }.to_json(options)
40
92
  end
41
93
  end
@@ -101,6 +153,54 @@ module Twilio
101
153
  )
102
154
  end
103
155
 
156
+ ##
157
+ # Update the ApprovalCreateInstance
158
+ # @param [ContentApprovalUpdateRequest] content_approval_update_request
159
+ # @return [ApprovalCreateInstance] Updated ApprovalCreateInstance
160
+ def update(content_approval_update_request: nil
161
+ )
162
+
163
+ headers = Twilio::Values.of({'Content-Type' => 'application/x-www-form-urlencoded', })
164
+ headers['Content-Type'] = 'application/json'
165
+
166
+
167
+
168
+
169
+ payload = @version.update('PATCH', @uri, headers: headers, data: content_approval_update_request.to_json)
170
+ ApprovalCreateInstance.new(
171
+ @version,
172
+ payload,
173
+ content_sid: @solution[:content_sid],
174
+ )
175
+ end
176
+
177
+ ##
178
+ # Update the ApprovalCreateInstanceMetadata
179
+ # @param [ContentApprovalUpdateRequest] content_approval_update_request
180
+ # @return [ApprovalCreateInstance] Updated ApprovalCreateInstance
181
+ def update_with_metadata(content_approval_update_request: nil
182
+ )
183
+
184
+ headers = Twilio::Values.of({'Content-Type' => 'application/x-www-form-urlencoded', })
185
+ headers['Content-Type'] = 'application/json'
186
+
187
+
188
+
189
+
190
+ response = @version.update_with_metadata('PATCH', @uri, headers: headers, data: content_approval_update_request.to_json)
191
+ approval_create_instance = ApprovalCreateInstance.new(
192
+ @version,
193
+ response.body,
194
+ content_sid: @solution[:content_sid],
195
+ )
196
+ ApprovalCreateInstanceMetadata.new(
197
+ @version,
198
+ approval_create_instance,
199
+ response.headers,
200
+ response.status_code
201
+ )
202
+ end
203
+
104
204
 
105
205
 
106
206
 
@@ -163,6 +263,7 @@ module Twilio
163
263
  'rejection_reason' => payload['rejection_reason'],
164
264
  'allow_category_change' => payload['allow_category_change'],
165
265
  'send_ttl_seconds' => payload['send_ttl_seconds'],
266
+ 'bid_amount' => payload['bid_amount'],
166
267
  }
167
268
  end
168
269
 
@@ -209,6 +310,12 @@ module Twilio
209
310
  @properties['send_ttl_seconds']
210
311
  end
211
312
 
313
+ ##
314
+ # @return [String] Maximum price per 1,000 message deliveries, expressed in the WABA currency's smallest unit (cents for USD). Only set for MARKETING templates onboarded to Max Pricing.
315
+ def bid_amount
316
+ @properties['bid_amount']
317
+ end
318
+
212
319
  ##
213
320
  # Provide a user friendly representation
214
321
  def to_s
@@ -71,7 +71,8 @@ module Twilio
71
71
  # @param [id]: [String]
72
72
  # @param [code]: [String]
73
73
  # @param [webview_size]: [WebviewSizeType]
74
- attr_accessor :type, :title, :url, :phone, :id, :code, :webview_size
74
+ # @param [digitable_line]: [String]
75
+ attr_accessor :type, :title, :url, :phone, :id, :code, :webview_size, :digitable_line
75
76
  def initialize(payload)
76
77
  @type = payload["type"]
77
78
  @title = payload["title"]
@@ -80,6 +81,7 @@ module Twilio
80
81
  @id = payload["id"]
81
82
  @code = payload["code"]
82
83
  @webview_size = payload["webview_size"]
84
+ @digitable_line = payload["digitable_line"]
83
85
  end
84
86
  def to_json(options = {})
85
87
  {
@@ -90,6 +92,7 @@ module Twilio
90
92
  "id": @id,
91
93
  "code": @code,
92
94
  "webview_size": @webview_size,
95
+ "digitable_line": @digitable_line,
93
96
  }.to_json(options)
94
97
  end
95
98
  end
@@ -693,7 +696,8 @@ module Twilio
693
696
  # @param [id]: [String]
694
697
  # @param [code]: [String]
695
698
  # @param [webview_size]: [WebviewSizeType]
696
- attr_accessor :type, :title, :url, :phone, :id, :code, :webview_size
699
+ # @param [digitable_line]: [String]
700
+ attr_accessor :type, :title, :url, :phone, :id, :code, :webview_size, :digitable_line
697
701
  def initialize(payload)
698
702
  @type = payload["type"]
699
703
  @title = payload["title"]
@@ -702,6 +706,7 @@ module Twilio
702
706
  @id = payload["id"]
703
707
  @code = payload["code"]
704
708
  @webview_size = payload["webview_size"]
709
+ @digitable_line = payload["digitable_line"]
705
710
  end
706
711
  def to_json(options = {})
707
712
  {
@@ -712,6 +717,7 @@ module Twilio
712
717
  "id": @id,
713
718
  "code": @code,
714
719
  "webview_size": @webview_size,
720
+ "digitable_line": @digitable_line,
715
721
  }.to_json(options)
716
722
  end
717
723
  end
@@ -1587,6 +1587,7 @@ module Twilio
1587
1587
  'trait_extraction_strategy_ids' => payload['trait_extraction_strategy_ids'],
1588
1588
  'observation_extraction_strategy_ids' => payload['observation_extraction_strategy_ids'],
1589
1589
  'memory_extraction_enabled' => payload['memory_extraction_enabled'],
1590
+ 'redaction_configuration_id' => payload['redaction_configuration_id'],
1590
1591
  'conversations_v1_bridge' => payload['conversations_v1_bridge'],
1591
1592
  'created_at' => Twilio.deserialize_iso8601_datetime(payload['created_at']),
1592
1593
  'updated_at' => Twilio.deserialize_iso8601_datetime(payload['updated_at']),
@@ -1687,6 +1688,12 @@ module Twilio
1687
1688
  @properties['memory_extraction_enabled']
1688
1689
  end
1689
1690
 
1691
+ ##
1692
+ # @return [String] The ID of the redaction configuration applied to communications in this configuration's conversations.
1693
+ def redaction_configuration_id
1694
+ @properties['redaction_configuration_id']
1695
+ end
1696
+
1690
1697
  ##
1691
1698
  # @return [ConversationsV2ConversationsV1Bridge]
1692
1699
  def conversations_v1_bridge