rcs 2.0.14 → 2.0.16.pre.rc.1

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.fern/metadata.json +4 -3
  3. data/.rubocop.yml +14 -1
  4. data/lib/pinnacle/brands/client.rb +3 -3
  5. data/lib/pinnacle/campaigns/dlc/client.rb +2 -2
  6. data/lib/pinnacle/campaigns/rcs/client.rb +2 -2
  7. data/lib/pinnacle/campaigns/rcs/types/upsert_rcs_campaign_params.rb +3 -6
  8. data/lib/pinnacle/campaigns/toll_free/client.rb +2 -2
  9. data/lib/pinnacle/client.rb +6 -1
  10. data/lib/pinnacle/conversations/client.rb +1 -1
  11. data/lib/pinnacle/forms/client.rb +217 -0
  12. data/lib/pinnacle/forms/submissions/client.rb +56 -0
  13. data/lib/pinnacle/forms/submissions/types/list_form_submissions_params.rb +15 -0
  14. data/lib/pinnacle/forms/types/forms_send_response.rb +14 -0
  15. data/lib/pinnacle/forms/types/list_forms_params.rb +12 -0
  16. data/lib/pinnacle/forms/types/update_form_params.rb +18 -0
  17. data/lib/pinnacle/internal/http/base_request.rb +14 -0
  18. data/lib/pinnacle/internal/http/raw_client.rb +21 -3
  19. data/lib/pinnacle/internal/json/request.rb +6 -4
  20. data/lib/pinnacle/internal/multipart/multipart_request.rb +6 -4
  21. data/lib/pinnacle/internal/types/union.rb +1 -1
  22. data/lib/pinnacle/internal/types/utils.rb +2 -2
  23. data/lib/pinnacle/messages/client.rb +1 -1
  24. data/lib/pinnacle/messages/schedule/client.rb +1 -1
  25. data/lib/pinnacle/phone_numbers/client.rb +0 -5
  26. data/lib/pinnacle/rcs/client.rb +1 -1
  27. data/lib/pinnacle/rcs/test/client.rb +3 -3
  28. data/lib/pinnacle/status/get/client.rb +5 -5
  29. data/lib/pinnacle/tools/url/client.rb +2 -2
  30. data/lib/pinnacle/types/address_field.rb +14 -0
  31. data/lib/pinnacle/types/attach_webhook_response_webhook.rb +2 -2
  32. data/lib/pinnacle/types/attach_webhook_result.rb +12 -0
  33. data/lib/pinnacle/types/checkbox_field.rb +13 -0
  34. data/lib/pinnacle/types/color_field.rb +8 -0
  35. data/lib/pinnacle/types/create_form_request.rb +23 -0
  36. data/lib/pinnacle/types/date_field.rb +12 -0
  37. data/lib/pinnacle/types/datetime_field.rb +12 -0
  38. data/lib/pinnacle/types/detach_webhook_result.rb +11 -0
  39. data/lib/pinnacle/types/email_field.rb +10 -0
  40. data/lib/pinnacle/types/extended_brand_with_vetting.rb +2 -2
  41. data/lib/pinnacle/types/extended_rcs_campaign.rb +3 -6
  42. data/lib/pinnacle/types/failed_sender.rb +10 -0
  43. data/lib/pinnacle/types/field_base.rb +14 -0
  44. data/lib/pinnacle/types/form.rb +22 -0
  45. data/lib/pinnacle/types/form_background.rb +17 -0
  46. data/lib/pinnacle/types/form_background_gradient.rb +12 -0
  47. data/lib/pinnacle/types/form_background_image.rb +13 -0
  48. data/lib/pinnacle/types/form_background_pattern.rb +10 -0
  49. data/lib/pinnacle/types/form_background_pattern_preset.rb +16 -0
  50. data/lib/pinnacle/types/form_background_solid.rb +34 -0
  51. data/lib/pinnacle/types/form_color_palette.rb +12 -0
  52. data/lib/pinnacle/types/form_definition.rb +15 -0
  53. data/lib/pinnacle/types/form_field.rb +29 -0
  54. data/lib/pinnacle/types/form_field_option.rb +11 -0
  55. data/lib/pinnacle/types/form_gradient.rb +12 -0
  56. data/lib/pinnacle/types/form_gradient_angle.rb +12 -0
  57. data/lib/pinnacle/types/form_id_reference.rb +23 -0
  58. data/lib/pinnacle/types/form_submission.rb +19 -0
  59. data/lib/pinnacle/types/form_submission_answer.rb +20 -0
  60. data/lib/pinnacle/types/form_submission_event.rb +15 -0
  61. data/lib/pinnacle/types/form_submission_event_conversation.rb +20 -0
  62. data/lib/pinnacle/types/form_submission_event_form.rb +12 -0
  63. data/lib/pinnacle/types/form_submission_event_submission.rb +17 -0
  64. data/lib/pinnacle/types/form_submitted_field.rb +13 -0
  65. data/lib/pinnacle/types/form_theme_override.rb +25 -0
  66. data/lib/pinnacle/types/form_theme_override_colors.rb +12 -0
  67. data/lib/pinnacle/types/form_theme_override_content_alignment.rb +13 -0
  68. data/lib/pinnacle/types/form_theme_override_corner_radius.rb +13 -0
  69. data/lib/pinnacle/types/form_theme_override_font_family.rb +15 -0
  70. data/lib/pinnacle/types/form_theme_override_theme_mode.rb +13 -0
  71. data/lib/pinnacle/types/list_form_submissions_response.rb +12 -0
  72. data/lib/pinnacle/types/list_forms_response.rb +12 -0
  73. data/lib/pinnacle/types/number_field.rb +13 -0
  74. data/lib/pinnacle/types/phone_field.rb +11 -0
  75. data/lib/pinnacle/types/radio_field.rb +10 -0
  76. data/lib/pinnacle/types/range_field.rb +12 -0
  77. data/lib/pinnacle/types/rating_field.rb +11 -0
  78. data/lib/pinnacle/types/rcs_campaign.rb +3 -6
  79. data/lib/pinnacle/types/rcs_campaign_summary.rb +0 -1
  80. data/lib/pinnacle/types/rcs_cards_content.rb +1 -1
  81. data/lib/pinnacle/types/rcs_cards_content_cards_item.rb +1 -1
  82. data/lib/pinnacle/types/rcs_media_content.rb +1 -1
  83. data/lib/pinnacle/types/rcs_validate_content_media.rb +1 -1
  84. data/lib/pinnacle/types/rich_card.rb +1 -1
  85. data/lib/pinnacle/types/rich_cards.rb +1 -1
  86. data/lib/pinnacle/types/rich_media_message.rb +1 -1
  87. data/lib/pinnacle/types/rich_text.rb +1 -1
  88. data/lib/pinnacle/types/scheduled_form_send_response_form.rb +10 -0
  89. data/lib/pinnacle/types/scheduled_form_send_response_submission.rb +10 -0
  90. data/lib/pinnacle/types/scheduled_form_send_result.rb +13 -0
  91. data/lib/pinnacle/types/select_field.rb +11 -0
  92. data/lib/pinnacle/types/send_form_options.rb +11 -0
  93. data/lib/pinnacle/types/send_form_options_webview_mode.rb +13 -0
  94. data/lib/pinnacle/types/send_form_params.rb +20 -0
  95. data/lib/pinnacle/types/send_form_result.rb +17 -0
  96. data/lib/pinnacle/types/send_form_via_rcs_params.rb +14 -0
  97. data/lib/pinnacle/types/send_form_via_rcs_request_fallback.rb +10 -0
  98. data/lib/pinnacle/types/send_form_via_rcs_request_form.rb +13 -0
  99. data/lib/pinnacle/types/send_form_via_sms_params.rb +13 -0
  100. data/lib/pinnacle/types/send_form_via_sms_request_form.rb +12 -0
  101. data/lib/pinnacle/types/send_rich_cards_options.rb +5 -0
  102. data/lib/pinnacle/types/send_rich_message_options_4.rb +13 -0
  103. data/lib/pinnacle/types/text_field.rb +13 -0
  104. data/lib/pinnacle/types/textarea_field.rb +13 -0
  105. data/lib/pinnacle/types/time_field.rb +12 -0
  106. data/lib/pinnacle/types/url_field.rb +10 -0
  107. data/lib/pinnacle/types/vcard_content.rb +1 -1
  108. data/lib/pinnacle/types/webhook_event_enum.rb +2 -0
  109. data/lib/pinnacle/types/webhook_summary.rb +1 -0
  110. data/lib/pinnacle/types/webhooks.rb +1 -0
  111. data/lib/pinnacle/version.rb +1 -1
  112. data/lib/pinnacle/webhooks/client.rb +85 -0
  113. data/lib/pinnacle/webhooks/types/attach_webhook_params.rb +16 -0
  114. data/lib/pinnacle/webhooks/types/detach_webhook_params.rb +12 -0
  115. data/lib/pinnacle.rb +74 -13
  116. data/reference.md +1002 -492
  117. metadata +73 -12
  118. data/lib/pinnacle/campaigns/rcs/types/rcs_use_case.rb +0 -15
  119. data/lib/pinnacle/phone_numbers/webhook/client.rb +0 -90
  120. data/lib/pinnacle/types/attach_webhook_by_id_params.rb +0 -11
  121. data/lib/pinnacle/types/attach_webhook_params.rb +0 -15
  122. data/lib/pinnacle/types/configured_webhook.rb +0 -13
  123. data/lib/pinnacle/types/create_and_attach_webhook_by_url_params.rb +0 -12
  124. data/lib/pinnacle/types/detached_webhook_info.rb +0 -11
  125. data/lib/pinnacle/types/rcs_campaign_schema_extra_use_case.rb +0 -11
  126. data/lib/pinnacle/types/rcs_campaign_schema_use_case.rb +0 -11
  127. data/lib/pinnacle/types/rcs_campaign_use_case_enum.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 362ad9ba7c8c47a7e24f2e98ae2b6a4af84b43e66faaa0682622414565a8f5de
4
- data.tar.gz: eb18c0b9268ce71ae35a20a2b6a8a68409f80637fa495faac8a93a541f59bd3b
3
+ metadata.gz: d2cac8fd3c68928a102bb892cd100493eb70f36a326b1c0eff8d80e9e09654a4
4
+ data.tar.gz: 702ed3fc0bcafd3836774adcca8cf3f8feb54bcb6120b13c1c9523a5836d5550
5
5
  SHA512:
6
- metadata.gz: af1d17f92bbd2f7e9835f1b9360dc4bb93d5b88140951da73ef943bcc83b38cc2189d7e914dee005bf61101cf17feb643814bd7695acd41d20ce996e1af47a9b
7
- data.tar.gz: c89f1854e0650dfcb4a01a46c88f4016007550e8c3d642f4578e1835cc3b744c0cc61cb6e662625e6e2e9a562c3d41a95d0934c26dacce2ac535e6fd583d2749
6
+ metadata.gz: fbeabb07e1998a0f02d5cf7b02faf556e7d2595089daaf0e9f1c4be52a20eb20e130e7b631fb4ed2892fb3beddac5b07f84ddc8b11f782afbcbbd7f85adb0b80
7
+ data.tar.gz: 1db2d564b8c37db520453732791d53875eff850db2bc8cf1dd5350805f696f3e482eb344d3724a434bc590903f1b49d32e937faa275d87b9239467d34071836b
data/.fern/metadata.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
- "cliVersion": "3.51.3",
2
+ "cliVersion": "4.58.0",
3
3
  "generatorName": "fernapi/fern-ruby-sdk",
4
- "generatorVersion": "1.0.0-rc84",
4
+ "generatorVersion": "1.1.13",
5
5
  "generatorConfig": {
6
6
  "clientModuleName": "PinnacleBaseClient",
7
7
  "flattenModuleStructure": true,
8
8
  "useProvidedDefaults": true
9
9
  },
10
- "sdkVersion": "2.0.14"
10
+ "originGitCommit": "fb028ee5e2c6395c9680eb66d1173e55015384c0",
11
+ "sdkVersion": "2.0.16.pre.rc.1"
11
12
  }
data/.rubocop.yml CHANGED
@@ -45,7 +45,11 @@ Layout/LineLength:
45
45
  Enabled: false
46
46
 
47
47
  Naming/VariableNumber:
48
- EnforcedStyle: snake_case
48
+ # Fern's ruby-sdk generator emits symbols like :gsm_7 / :utf_16 / :code_3
49
+ # (snake_case) in generated type classes while shipping a template that
50
+ # enforces `normalcase`. The two are inconsistent, so disable the cop
51
+ # until the generator resolves it upstream.
52
+ Enabled: false
49
53
 
50
54
  Style/Documentation:
51
55
  Enabled: false
@@ -58,3 +62,12 @@ Minitest/MultipleAssertions:
58
62
 
59
63
  Minitest/UselessAssertion:
60
64
  Enabled: false
65
+
66
+ # Dynamic snippets are code samples for documentation, not standalone Ruby files.
67
+ Style/FrozenStringLiteralComment:
68
+ Exclude:
69
+ - "dynamic-snippets/**/*"
70
+
71
+ Layout/FirstHashElementIndentation:
72
+ Exclude:
73
+ - "dynamic-snippets/**/*"
@@ -108,7 +108,7 @@ module Pinnacle
108
108
  request = Pinnacle::Internal::JSON::Request.new(
109
109
  base_url: request_options[:base_url],
110
110
  method: "GET",
111
- path: "brands/#{params[:id]}",
111
+ path: "brands/#{URI.encode_uri_component(params[:id].to_s)}",
112
112
  query: query_params,
113
113
  request_options: request_options
114
114
  )
@@ -143,7 +143,7 @@ module Pinnacle
143
143
  request = Pinnacle::Internal::JSON::Request.new(
144
144
  base_url: request_options[:base_url],
145
145
  method: "POST",
146
- path: "brands/#{params[:brand_id]}/submit",
146
+ path: "brands/#{URI.encode_uri_component(params[:brand_id].to_s)}/submit",
147
147
  request_options: request_options
148
148
  )
149
149
  begin
@@ -216,7 +216,7 @@ module Pinnacle
216
216
  request = Pinnacle::Internal::JSON::Request.new(
217
217
  base_url: request_options[:base_url],
218
218
  method: "POST",
219
- path: "brands/#{params[:brand_id]}/vet",
219
+ path: "brands/#{URI.encode_uri_component(params[:brand_id].to_s)}/vet",
220
220
  body: body,
221
221
  request_options: request_options
222
222
  )
@@ -62,7 +62,7 @@ module Pinnacle
62
62
  request = Pinnacle::Internal::JSON::Request.new(
63
63
  base_url: request_options[:base_url],
64
64
  method: "GET",
65
- path: "campaigns/dlc/#{params[:campaign_id]}",
65
+ path: "campaigns/dlc/#{URI.encode_uri_component(params[:campaign_id].to_s)}",
66
66
  request_options: request_options
67
67
  )
68
68
  begin
@@ -96,7 +96,7 @@ module Pinnacle
96
96
  request = Pinnacle::Internal::JSON::Request.new(
97
97
  base_url: request_options[:base_url],
98
98
  method: "POST",
99
- path: "campaigns/dlc/submit/#{params[:campaign_id]}",
99
+ path: "campaigns/dlc/submit/#{URI.encode_uri_component(params[:campaign_id].to_s)}",
100
100
  request_options: request_options
101
101
  )
102
102
  begin
@@ -62,7 +62,7 @@ module Pinnacle
62
62
  request = Pinnacle::Internal::JSON::Request.new(
63
63
  base_url: request_options[:base_url],
64
64
  method: "GET",
65
- path: "campaigns/rcs/#{params[:campaign_id]}",
65
+ path: "campaigns/rcs/#{URI.encode_uri_component(params[:campaign_id].to_s)}",
66
66
  request_options: request_options
67
67
  )
68
68
  begin
@@ -96,7 +96,7 @@ module Pinnacle
96
96
  request = Pinnacle::Internal::JSON::Request.new(
97
97
  base_url: request_options[:base_url],
98
98
  method: "POST",
99
- path: "campaigns/rcs/submit/#{params[:campaign_id]}",
99
+ path: "campaigns/rcs/submit/#{URI.encode_uri_component(params[:campaign_id].to_s)}",
100
100
  request_options: request_options
101
101
  )
102
102
  begin
@@ -10,15 +10,12 @@ module Pinnacle
10
10
  field :campaign_id, -> { String }, optional: true, nullable: false, api_name: "campaignId"
11
11
  field :expected_agent_responses, -> { Internal::Types::Array[String] }, optional: true, nullable: false, api_name: "expectedAgentResponses"
12
12
  field :links, -> { Pinnacle::Campaigns::Rcs::Types::RcsLinks }, optional: true, nullable: false
13
- field :use_case, -> { Pinnacle::Campaigns::Rcs::Types::RcsUseCase }, optional: true, nullable: false, api_name: "useCase"
14
- field :opt_in_terms_and_conditions, -> { String }, optional: true, nullable: false, api_name: "optInTermsAndConditions"
13
+ field :use_case_description, -> { String }, optional: true, nullable: false, api_name: "useCaseDescription"
15
14
  field :messaging_type, -> { Pinnacle::Types::RcsMessagingTypeEnum }, optional: true, nullable: false, api_name: "messagingType"
16
- field :carrier_description, -> { String }, optional: true, nullable: false, api_name: "carrierDescription"
15
+ field :cta_media, -> { String }, optional: true, nullable: false, api_name: "ctaMedia"
16
+ field :opt_in_method, -> { String }, optional: true, nullable: false, api_name: "optInMethod"
17
17
  field :keywords, -> { Pinnacle::Campaigns::Rcs::Types::RcsCampaignKeywords }, optional: true, nullable: false
18
18
  field :traffic, -> { Pinnacle::Campaigns::Rcs::Types::RcsCampaignTraffic }, optional: true, nullable: false
19
- field :agent_triggers, -> { String }, optional: true, nullable: false, api_name: "agentTriggers"
20
- field :interaction_description, -> { String }, optional: true, nullable: false, api_name: "interactionDescription"
21
- field :is_conversational, -> { Internal::Types::Boolean }, optional: true, nullable: false, api_name: "isConversational"
22
19
  field :cta_language, -> { String }, optional: true, nullable: false, api_name: "ctaLanguage"
23
20
  field :demo_trigger, -> { String }, optional: true, nullable: false, api_name: "demoTrigger"
24
21
  end
@@ -62,7 +62,7 @@ module Pinnacle
62
62
  request = Pinnacle::Internal::JSON::Request.new(
63
63
  base_url: request_options[:base_url],
64
64
  method: "GET",
65
- path: "campaigns/toll-free/#{params[:campaign_id]}",
65
+ path: "campaigns/toll-free/#{URI.encode_uri_component(params[:campaign_id].to_s)}",
66
66
  request_options: request_options
67
67
  )
68
68
  begin
@@ -96,7 +96,7 @@ module Pinnacle
96
96
  request = Pinnacle::Internal::JSON::Request.new(
97
97
  base_url: request_options[:base_url],
98
98
  method: "POST",
99
- path: "campaigns/toll-free/submit/#{params[:campaign_id]}",
99
+ path: "campaigns/toll-free/submit/#{URI.encode_uri_component(params[:campaign_id].to_s)}",
100
100
  request_options: request_options
101
101
  )
102
102
  begin
@@ -10,7 +10,7 @@ module Pinnacle
10
10
  @raw_client = Pinnacle::Internal::Http::RawClient.new(
11
11
  base_url: base_url || Pinnacle::Environment::DEFAULT,
12
12
  headers: {
13
- "User-Agent" => "rcs/2.0.14",
13
+ "User-Agent" => "rcs/2.0.16.pre.rc.1",
14
14
  "X-Fern-Language" => "Ruby",
15
15
  "PINNACLE-API-KEY" => api_key.to_s
16
16
  }
@@ -57,6 +57,11 @@ module Pinnacle
57
57
  @webhooks ||= Pinnacle::Webhooks::Client.new(client: @raw_client)
58
58
  end
59
59
 
60
+ # @return [Pinnacle::Forms::Client]
61
+ def forms
62
+ @forms ||= Pinnacle::Forms::Client.new(client: @raw_client)
63
+ end
64
+
60
65
  # @return [Pinnacle::Campaigns::Client]
61
66
  def campaigns
62
67
  @campaigns ||= Pinnacle::Campaigns::Client.new(client: @raw_client)
@@ -147,7 +147,7 @@ module Pinnacle
147
147
  request = Pinnacle::Internal::JSON::Request.new(
148
148
  base_url: request_options[:base_url],
149
149
  method: "POST",
150
- path: "conversations/#{params[:id]}/messages",
150
+ path: "conversations/#{URI.encode_uri_component(params[:id].to_s)}/messages",
151
151
  query: query_params,
152
152
  request_options: request_options
153
153
  )
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pinnacle
4
+ module Forms
5
+ class Client
6
+ # @param client [Pinnacle::Internal::Http::RawClient]
7
+ #
8
+ # @return [void]
9
+ def initialize(client:)
10
+ @client = client
11
+ end
12
+
13
+ # Retrieve a form by id. Includes submission count, last submission timestamp, and archive state.
14
+ #
15
+ # @param request_options [Hash]
16
+ # @param params [Hash]
17
+ # @option request_options [String] :base_url
18
+ # @option request_options [Hash{String => Object}] :additional_headers
19
+ # @option request_options [Hash{String => Object}] :additional_query_parameters
20
+ # @option request_options [Hash{String => Object}] :additional_body_parameters
21
+ # @option request_options [Integer] :timeout_in_seconds
22
+ # @option params [String] :id
23
+ #
24
+ # @return [Pinnacle::Types::Form]
25
+ def get(request_options: {}, **params)
26
+ params = Pinnacle::Internal::Types::Utils.normalize_keys(params)
27
+ request = Pinnacle::Internal::JSON::Request.new(
28
+ base_url: request_options[:base_url],
29
+ method: "GET",
30
+ path: "forms/#{URI.encode_uri_component(params[:id].to_s)}",
31
+ request_options: request_options
32
+ )
33
+ begin
34
+ response = @client.send(request)
35
+ rescue Net::HTTPRequestTimeout
36
+ raise Pinnacle::Errors::TimeoutError
37
+ end
38
+ code = response.code.to_i
39
+ if code.between?(200, 299)
40
+ Pinnacle::Types::Form.load(response.body)
41
+ else
42
+ error_class = Pinnacle::Errors::ResponseError.subclass_for_code(code)
43
+ raise error_class.new(response.body, code: code)
44
+ end
45
+ end
46
+
47
+ # Partial update. Only keys present in the body are applied. Archived forms (non-null `archived_at`) cannot be
48
+ # updated — restore the form by setting `archived_at: null` in a PATCH first.
49
+ #
50
+ # @param request_options [Hash]
51
+ # @param params [Pinnacle::Forms::Types::UpdateFormParams]
52
+ # @option request_options [String] :base_url
53
+ # @option request_options [Hash{String => Object}] :additional_headers
54
+ # @option request_options [Hash{String => Object}] :additional_query_parameters
55
+ # @option request_options [Hash{String => Object}] :additional_body_parameters
56
+ # @option request_options [Integer] :timeout_in_seconds
57
+ # @option params [String] :id
58
+ #
59
+ # @return [Pinnacle::Types::Form]
60
+ def update(request_options: {}, **params)
61
+ params = Pinnacle::Internal::Types::Utils.normalize_keys(params)
62
+ request_data = Pinnacle::Forms::Types::UpdateFormParams.new(params).to_h
63
+ non_body_param_names = ["id"]
64
+ body = request_data.except(*non_body_param_names)
65
+
66
+ request = Pinnacle::Internal::JSON::Request.new(
67
+ base_url: request_options[:base_url],
68
+ method: "PATCH",
69
+ path: "forms/#{URI.encode_uri_component(params[:id].to_s)}",
70
+ body: body,
71
+ request_options: request_options
72
+ )
73
+ begin
74
+ response = @client.send(request)
75
+ rescue Net::HTTPRequestTimeout
76
+ raise Pinnacle::Errors::TimeoutError
77
+ end
78
+ code = response.code.to_i
79
+ if code.between?(200, 299)
80
+ Pinnacle::Types::Form.load(response.body)
81
+ else
82
+ error_class = Pinnacle::Errors::ResponseError.subclass_for_code(code)
83
+ raise error_class.new(response.body, code: code)
84
+ end
85
+ end
86
+
87
+ # Paginated list of forms on your team, sorted by creation date (newest first). Includes archived forms.
88
+ #
89
+ # @param request_options [Hash]
90
+ # @param params [Pinnacle::Forms::Types::ListFormsParams]
91
+ # @option request_options [String] :base_url
92
+ # @option request_options [Hash{String => Object}] :additional_headers
93
+ # @option request_options [Hash{String => Object}] :additional_query_parameters
94
+ # @option request_options [Hash{String => Object}] :additional_body_parameters
95
+ # @option request_options [Integer] :timeout_in_seconds
96
+ #
97
+ # @return [Pinnacle::Types::ListFormsResponse]
98
+ def list(request_options: {}, **params)
99
+ params = Pinnacle::Internal::Types::Utils.normalize_keys(params)
100
+ request = Pinnacle::Internal::JSON::Request.new(
101
+ base_url: request_options[:base_url],
102
+ method: "POST",
103
+ path: "forms/list",
104
+ body: Pinnacle::Forms::Types::ListFormsParams.new(params).to_h,
105
+ request_options: request_options
106
+ )
107
+ begin
108
+ response = @client.send(request)
109
+ rescue Net::HTTPRequestTimeout
110
+ raise Pinnacle::Errors::TimeoutError
111
+ end
112
+ code = response.code.to_i
113
+ if code.between?(200, 299)
114
+ Pinnacle::Types::ListFormsResponse.load(response.body)
115
+ else
116
+ error_class = Pinnacle::Errors::ResponseError.subclass_for_code(code)
117
+ raise error_class.new(response.body, code: code)
118
+ end
119
+ end
120
+
121
+ # Create a hosted form without sending it. <br>
122
+ #
123
+ # Returns the form object including its public URL — `https://forms.pinnacle.sh/{form_id}`. <br>
124
+ #
125
+ # To also deliver the URL to a recipient over SMS or RCS in a single call, use [`POST
126
+ # /forms/send`](/api-reference/forms/send-form).
127
+ #
128
+ # @param request_options [Hash]
129
+ # @param params [Pinnacle::Types::CreateFormRequest]
130
+ # @option request_options [String] :base_url
131
+ # @option request_options [Hash{String => Object}] :additional_headers
132
+ # @option request_options [Hash{String => Object}] :additional_query_parameters
133
+ # @option request_options [Hash{String => Object}] :additional_body_parameters
134
+ # @option request_options [Integer] :timeout_in_seconds
135
+ #
136
+ # @return [Pinnacle::Types::Form]
137
+ def create(request_options: {}, **params)
138
+ params = Pinnacle::Internal::Types::Utils.normalize_keys(params)
139
+ request = Pinnacle::Internal::JSON::Request.new(
140
+ base_url: request_options[:base_url],
141
+ method: "POST",
142
+ path: "forms",
143
+ body: params,
144
+ request_options: request_options
145
+ )
146
+ begin
147
+ response = @client.send(request)
148
+ rescue Net::HTTPRequestTimeout
149
+ raise Pinnacle::Errors::TimeoutError
150
+ end
151
+ code = response.code.to_i
152
+ if code.between?(200, 299)
153
+ Pinnacle::Types::Form.load(response.body)
154
+ else
155
+ error_class = Pinnacle::Errors::ResponseError.subclass_for_code(code)
156
+ raise error_class.new(response.body, code: code)
157
+ end
158
+ end
159
+
160
+ # Send a form to a recipient over SMS or RCS, or mint a standalone submission URL.
161
+ #
162
+ # Pass `form` as either an existing form id (`form_*`) or an inline `{ fields, ... }` definition to mint a new
163
+ # form for this send.
164
+ #
165
+ # The delivery channel is inferred from `from`:
166
+ # - `from: "agent_*"` → RCS (with optional SMS `fallback`)
167
+ # - `from: "+E.164"` → SMS
168
+ #
169
+ # When `to` is provided, Pinnacle dispatches a message whose body contains the submission URL and the recipient is
170
+ # recorded on the response: `submission.to` echoes the same E.164 number and `message_id` is the id of the
171
+ # outbound SMS/RCS.
172
+ #
173
+ # When `to` is omitted, no message is sent — `submission.to` and `message_id` are both `null` — which is useful
174
+ # for embedding the URL in your own outreach.
175
+ #
176
+ # On completion, a `FORM.SUBMISSION` webhook event is delivered to webhooks subscribed to the sender. See
177
+ # [Receiving Messages and User Events](/guides/messages/receiving).
178
+ #
179
+ # @param request_options [Hash]
180
+ # @param params [Pinnacle::Types::SendFormParams]
181
+ # @option request_options [String] :base_url
182
+ # @option request_options [Hash{String => Object}] :additional_headers
183
+ # @option request_options [Hash{String => Object}] :additional_query_parameters
184
+ # @option request_options [Hash{String => Object}] :additional_body_parameters
185
+ # @option request_options [Integer] :timeout_in_seconds
186
+ #
187
+ # @return [Pinnacle::Forms::Types::FormsSendResponse]
188
+ def send_(request_options: {}, **params)
189
+ params = Pinnacle::Internal::Types::Utils.normalize_keys(params)
190
+ request = Pinnacle::Internal::JSON::Request.new(
191
+ base_url: request_options[:base_url],
192
+ method: "POST",
193
+ path: "forms/send",
194
+ body: Pinnacle::Types::SendFormParams.new(params).to_h,
195
+ request_options: request_options
196
+ )
197
+ begin
198
+ response = @client.send(request)
199
+ rescue Net::HTTPRequestTimeout
200
+ raise Pinnacle::Errors::TimeoutError
201
+ end
202
+ code = response.code.to_i
203
+ if code.between?(200, 299)
204
+ Pinnacle::Forms::Types::FormsSendResponse.load(response.body)
205
+ else
206
+ error_class = Pinnacle::Errors::ResponseError.subclass_for_code(code)
207
+ raise error_class.new(response.body, code: code)
208
+ end
209
+ end
210
+
211
+ # @return [Pinnacle::Submissions::Client]
212
+ def submissions
213
+ @submissions ||= Pinnacle::Forms::Submissions::Client.new(client: @client)
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pinnacle
4
+ module Forms
5
+ module Submissions
6
+ class Client
7
+ # @param client [Pinnacle::Internal::Http::RawClient]
8
+ #
9
+ # @return [void]
10
+ def initialize(client:)
11
+ @client = client
12
+ end
13
+
14
+ # Paginated list of completed submissions for a form, newest first. Each row includes the submitted `data` keyed
15
+ # by field `key`, the sender/recipient, IP, user-agent, and timestamps.
16
+ #
17
+ # @param request_options [Hash]
18
+ # @param params [Pinnacle::Forms::Submissions::Types::ListFormSubmissionsParams]
19
+ # @option request_options [String] :base_url
20
+ # @option request_options [Hash{String => Object}] :additional_headers
21
+ # @option request_options [Hash{String => Object}] :additional_query_parameters
22
+ # @option request_options [Hash{String => Object}] :additional_body_parameters
23
+ # @option request_options [Integer] :timeout_in_seconds
24
+ # @option params [String] :id
25
+ #
26
+ # @return [Pinnacle::Types::ListFormSubmissionsResponse]
27
+ def list(request_options: {}, **params)
28
+ params = Pinnacle::Internal::Types::Utils.normalize_keys(params)
29
+ request_data = Pinnacle::Forms::Submissions::Types::ListFormSubmissionsParams.new(params).to_h
30
+ non_body_param_names = ["id"]
31
+ body = request_data.except(*non_body_param_names)
32
+
33
+ request = Pinnacle::Internal::JSON::Request.new(
34
+ base_url: request_options[:base_url],
35
+ method: "POST",
36
+ path: "forms/#{URI.encode_uri_component(params[:id].to_s)}/submissions/list",
37
+ body: body,
38
+ request_options: request_options
39
+ )
40
+ begin
41
+ response = @client.send(request)
42
+ rescue Net::HTTPRequestTimeout
43
+ raise Pinnacle::Errors::TimeoutError
44
+ end
45
+ code = response.code.to_i
46
+ if code.between?(200, 299)
47
+ Pinnacle::Types::ListFormSubmissionsResponse.load(response.body)
48
+ else
49
+ error_class = Pinnacle::Errors::ResponseError.subclass_for_code(code)
50
+ raise error_class.new(response.body, code: code)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pinnacle
4
+ module Forms
5
+ module Submissions
6
+ module Types
7
+ class ListFormSubmissionsParams < Internal::Types::Model
8
+ field :id, -> { String }, optional: false, nullable: false
9
+ field :page_index, -> { Integer }, optional: true, nullable: false, api_name: "pageIndex"
10
+ field :page_size, -> { Integer }, optional: true, nullable: false, api_name: "pageSize"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pinnacle
4
+ module Forms
5
+ module Types
6
+ class FormsSendResponse < Internal::Types::Model
7
+ extend Pinnacle::Internal::Types::Union
8
+
9
+ member -> { Pinnacle::Types::SendFormResult }
10
+ member -> { Pinnacle::Types::ScheduledFormSendResult }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pinnacle
4
+ module Forms
5
+ module Types
6
+ class ListFormsParams < Internal::Types::Model
7
+ field :page_index, -> { Integer }, optional: true, nullable: false, api_name: "pageIndex"
8
+ field :page_size, -> { Integer }, optional: true, nullable: false, api_name: "pageSize"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pinnacle
4
+ module Forms
5
+ module Types
6
+ class UpdateFormParams < Internal::Types::Model
7
+ field :id, -> { String }, optional: false, nullable: false
8
+ field :name, -> { String }, optional: true, nullable: false
9
+ field :description, -> { String }, optional: true, nullable: false
10
+ field :fields, -> { Internal::Types::Array[Pinnacle::Types::FormField] }, optional: true, nullable: false
11
+ field :can_update, -> { Internal::Types::Boolean }, optional: true, nullable: false
12
+ field :expires_at, -> { String }, optional: true, nullable: false
13
+ field :theme_override, -> { Pinnacle::Types::FormThemeOverride }, optional: true, nullable: false
14
+ field :archived_at, -> { String }, optional: true, nullable: false
15
+ end
16
+ end
17
+ end
18
+ end
@@ -31,6 +31,20 @@ module Pinnacle
31
31
  # Child classes should implement:
32
32
  # - encode_headers: Returns the encoded HTTP request headers.
33
33
  # - encode_body: Returns the encoded HTTP request body.
34
+
35
+ private
36
+
37
+ # Merges additional_headers from request_options into sdk_headers, filtering out
38
+ # any keys that collide with SDK-set or client-protected headers (case-insensitive).
39
+ # @param sdk_headers [Hash] Headers set by the SDK for this request type.
40
+ # @param protected_keys [Array<String>] Additional header keys that must not be overridden.
41
+ # @return [Hash] The merged headers.
42
+ def merge_additional_headers(sdk_headers, protected_keys: [])
43
+ additional_headers = @request_options&.dig(:additional_headers) || @request_options&.dig("additional_headers") || {}
44
+ all_protected = (sdk_headers.keys + protected_keys).to_set { |k| k.to_s.downcase }
45
+ filtered = additional_headers.reject { |key, _| all_protected.include?(key.to_s.downcase) }
46
+ sdk_headers.merge(filtered)
47
+ end
34
48
  end
35
49
  end
36
50
  end
@@ -43,7 +43,7 @@ module Pinnacle
43
43
  http_request = build_http_request(
44
44
  url:,
45
45
  method: request.method,
46
- headers: request.encode_headers,
46
+ headers: request.encode_headers(protected_keys: @default_headers.keys),
47
47
  body: request.encode_body
48
48
  )
49
49
 
@@ -120,6 +120,8 @@ module Pinnacle
120
120
  [delay + jitter, 0].max
121
121
  end
122
122
 
123
+ LOCALHOST_HOSTS = %w[localhost 127.0.0.1 [::1]].freeze
124
+
123
125
  # @param request [Pinnacle::Internal::Http::BaseRequest] The HTTP request.
124
126
  # @return [URI::Generic] The URL.
125
127
  def build_url(request)
@@ -129,14 +131,29 @@ module Pinnacle
129
131
  if request.path.start_with?("http://", "https://")
130
132
  url = request.path
131
133
  url = "#{url}?#{encode_query(encoded_query)}" if encoded_query&.any?
132
- return URI.parse(url)
134
+ parsed = URI.parse(url)
135
+ validate_https!(parsed)
136
+ return parsed
133
137
  end
134
138
 
135
139
  path = request.path.start_with?("/") ? request.path[1..] : request.path
136
140
  base = request.base_url || @base_url
137
141
  url = "#{base.chomp("/")}/#{path}"
138
142
  url = "#{url}?#{encode_query(encoded_query)}" if encoded_query&.any?
139
- URI.parse(url)
143
+ parsed = URI.parse(url)
144
+ validate_https!(parsed)
145
+ parsed
146
+ end
147
+
148
+ # Raises if the URL uses http:// for a non-localhost host, which would
149
+ # send authentication credentials in plaintext.
150
+ # @param url [URI::Generic] The parsed URL.
151
+ def validate_https!(url)
152
+ return if url.scheme != "http"
153
+ return if LOCALHOST_HOSTS.include?(url.host)
154
+
155
+ raise ArgumentError, "Refusing to send request to non-HTTPS URL: #{url}. " \
156
+ "HTTP is only allowed for localhost. Use HTTPS or pass a localhost URL."
140
157
  end
141
158
 
142
159
  # @param url [URI::Generic] The url to the resource.
@@ -180,6 +197,7 @@ module Pinnacle
180
197
 
181
198
  http = Net::HTTP.new(url.host, port)
182
199
  http.use_ssl = is_https
200
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER if is_https
183
201
  # NOTE: We handle retries at the application level with HTTP status code awareness,
184
202
  # so we set max_retries to 0 to disable Net::HTTP's built-in network-level retries.
185
203
  http.max_retries = 0
@@ -21,12 +21,14 @@ module Pinnacle
21
21
  end
22
22
 
23
23
  # @return [Hash] The encoded HTTP request headers.
24
- def encode_headers
25
- additional_headers = @request_options&.dig(:additional_headers) || @request_options&.dig("additional_headers") || {}
26
- {
24
+ # @param protected_keys [Array<String>] Header keys set by the SDK client (e.g. auth, metadata)
25
+ # that must not be overridden by additional_headers from request_options.
26
+ def encode_headers(protected_keys: [])
27
+ sdk_headers = {
27
28
  "Content-Type" => "application/json",
28
29
  "Accept" => "application/json"
29
- }.merge(@headers).merge(additional_headers)
30
+ }.merge(@headers)
31
+ merge_additional_headers(sdk_headers, protected_keys:)
30
32
  end
31
33
 
32
34
  # @return [String, nil] The encoded HTTP request body.
@@ -21,11 +21,13 @@ module Pinnacle
21
21
  end
22
22
 
23
23
  # @return [Hash] The encoded HTTP request headers.
24
- def encode_headers
25
- additional_headers = @request_options&.dig(:additional_headers) || @request_options&.dig("additional_headers") || {}
26
- {
24
+ # @param protected_keys [Array<String>] Header keys set by the SDK client (e.g. auth, metadata)
25
+ # that must not be overridden by additional_headers from request_options.
26
+ def encode_headers(protected_keys: [])
27
+ sdk_headers = {
27
28
  "Content-Type" => @body.content_type
28
- }.merge(@headers).merge(additional_headers)
29
+ }.merge(@headers)
30
+ merge_additional_headers(sdk_headers, protected_keys:)
29
31
  end
30
32
 
31
33
  # @return [String, nil] The encoded HTTP request body.
@@ -21,7 +21,7 @@ module Pinnacle
21
21
  self
22
22
  end
23
23
 
24
- def member?(type)
24
+ def type_member?(type)
25
25
  members.any? { |_key, type_fn| type == type_fn.call }
26
26
  end
27
27