huntrecht-sdk 0.1.4

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2e4418b148d7b59618906b0442001104999f4360ca3db315780d71a90091ff70
4
+ data.tar.gz: 47c6491f815275c4264bc80168bfe41b9be706efdeafbfd8ffecf63cdf5746ae
5
+ SHA512:
6
+ metadata.gz: ecd9cb91da37da60a6c29796d3787257b8521d9f0ac8aa4b21652608c467ca3fbc6e2d1b1bb1aa39824e84632e6eb46adb6aed5bbee5c88a6e95d1f7e6ad31ee
7
+ data.tar.gz: d32a334f3e8dea757f1e782927d06eba5bafac6f58e56d2433fe63e860adb01f6d2eeabf8e9628e53b55df8f6d68df954b8afc0b70e9b6309a059bb3dd412b23
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Huntrecht SDK for Ruby
4
+
5
+ Official Ruby client for the Huntrecht Platform API v1.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ gem install huntrecht-sdk
11
+ ```
12
+
13
+ Or in your Gemfile:
14
+
15
+ ```ruby
16
+ gem "huntrecht-sdk"
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ require "huntrecht"
23
+
24
+ client = Huntrecht::Client.new(
25
+ client_id: "hnt_your_client_id",
26
+ client_secret: "your_secret"
27
+ )
28
+
29
+ orders = client.orders.list(status: "pending")
30
+ ```
31
+
32
+ See the [Ruby SDK docs](https://huntrecht.com/docs/sdks/ruby) for the full reference.
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "json"
6
+
7
+ require_relative "errors"
8
+ require_relative "version"
9
+
10
+ module Huntrecht
11
+ API_VERSION = "v1"
12
+ DEFAULT_BASE_URL = "https://api.huntrecht.com"
13
+ USER_AGENT = "huntrecht-sdk-ruby/#{VERSION}"
14
+
15
+ # Client for the Huntrecht Platform API v1.
16
+ #
17
+ # client = Huntrecht::Client.new(
18
+ # client_id: "hnt_abc123",
19
+ # client_secret: "secret"
20
+ # )
21
+ # client.auth.token # authenticate
22
+ # orders = client.orders.list
23
+ class Client
24
+ attr_reader :base_url, :client_id, :timeout, :max_retries, :retry_backoff
25
+ attr_accessor :access_token
26
+
27
+ def initialize(base_url: nil, client_id: nil, client_secret: nil,
28
+ access_token: nil, timeout: 30, max_retries: 3,
29
+ retry_backoff: 1.0)
30
+ @base_url = (base_url || ENV["HUNTRECHT_BASE_URL"] || DEFAULT_BASE_URL).sub(%r{/+\z}, "")
31
+ @client_id = client_id || ENV["HUNTRECHT_CLIENT_ID"] || ""
32
+ @client_secret = client_secret || ENV["HUNTRECHT_CLIENT_SECRET"] || ""
33
+ @access_token = access_token
34
+ @refresh_token = nil
35
+ @token_expires_at = Time.at(0)
36
+ @timeout = timeout
37
+ @max_retries = max_retries
38
+ @retry_backoff = retry_backoff
39
+
40
+ require_relative "resources"
41
+ @auth = Resources::AuthAPI.new(self)
42
+ @clients = Resources::ClientsAPI.new(self)
43
+ @orders = Resources::OrdersAPI.new(self)
44
+ @payments = Resources::PaymentsAPI.new(self)
45
+ @subscriptions = Resources::SubscriptionsAPI.new(self)
46
+ @credit = Resources::CreditAPI.new(self)
47
+ @credit_risk = Resources::CreditRiskAPI.new(self)
48
+ @kyc = Resources::KycAPI.new(self)
49
+ @quotes = Resources::QuotesAPI.new(self)
50
+ @users = Resources::UsersAPI.new(self)
51
+ @storefront = Resources::StorefrontAPI.new(self)
52
+ @price_drops = Resources::PriceDropsAPI.new(self)
53
+ @app_proxy = Resources::AppProxyAPI.new(self)
54
+ @linked_payments = Resources::LinkedPaymentsAPI.new(self)
55
+ end
56
+
57
+ attr_reader :auth, :clients, :orders, :payments, :subscriptions,
58
+ :credit, :credit_risk, :kyc, :quotes, :users, :storefront,
59
+ :price_drops, :app_proxy, :linked_payments
60
+
61
+ def client_secret
62
+ @client_secret
63
+ end
64
+
65
+ def refresh_token
66
+ @refresh_token
67
+ end
68
+
69
+ def store_tokens(data)
70
+ @access_token = data["access_token"]
71
+ @refresh_token = data["refresh_token"]
72
+ @token_expires_at = Time.now + (data["expires_in"] || 1800).to_i - 60
73
+ end
74
+
75
+ # Make an API request with automatic auth, retry, and rate-limit handling.
76
+ def request(method, path, params: nil, json: nil, headers: nil, auth_required: true)
77
+ ensure_token if auth_required
78
+
79
+ last_error = nil
80
+ (0..@max_retries).each do |attempt|
81
+ begin
82
+ return perform(method, path, params: params, json: json,
83
+ headers: headers, auth_required: auth_required)
84
+ rescue RateLimitError => e
85
+ last_error = e
86
+ raise if attempt >= @max_retries
87
+
88
+ wait = e.retry_after.positive? ? e.retry_after : @retry_backoff * (2**attempt)
89
+ sleep(wait)
90
+ rescue APIError
91
+ raise
92
+ rescue StandardError => e
93
+ last_error = APIError.new("HTTP error: #{e.message}")
94
+ raise last_error if attempt >= @max_retries
95
+
96
+ sleep(@retry_backoff * (2**attempt))
97
+ end
98
+ end
99
+ raise(last_error || APIError.new("Request failed"))
100
+ end
101
+
102
+ # Build the full URI for a path + query params (no network).
103
+ def build_uri(path, params = nil)
104
+ uri = URI("#{@base_url}/api/#{API_VERSION}#{path}")
105
+ clean = (params || {}).reject { |_k, v| v.nil? }
106
+ unless clean.empty?
107
+ uri.query = URI.encode_www_form(clean.map { |k, v| [k.to_s, v.to_s] })
108
+ end
109
+ uri
110
+ end
111
+
112
+ private
113
+
114
+ def ensure_token
115
+ return if @access_token && Time.now < @token_expires_at
116
+
117
+ if !@client_id.empty? && !@client_secret.empty?
118
+ data = @auth.token
119
+ store_tokens(data)
120
+ else
121
+ raise AuthenticationError, "No access token and no client credentials. " \
122
+ "Set HUNTRECHT_CLIENT_ID and HUNTRECHT_CLIENT_SECRET, " \
123
+ "or pass them to Huntrecht::Client.new."
124
+ end
125
+ end
126
+
127
+ def perform(method, path, params:, json:, headers:, auth_required:)
128
+ uri = build_uri(path, params)
129
+ http = Net::HTTP.new(uri.host, uri.port)
130
+ http.use_ssl = uri.scheme == "https"
131
+ http.open_timeout = @timeout
132
+ http.read_timeout = @timeout
133
+
134
+ req_class = {
135
+ "GET" => Net::HTTP::Get, "POST" => Net::HTTP::Post,
136
+ "PUT" => Net::HTTP::Put, "PATCH" => Net::HTTP::Patch,
137
+ "DELETE" => Net::HTTP::Delete
138
+ }.fetch(method.upcase) { raise APIError, "Unsupported method #{method}" }
139
+
140
+ req = req_class.new(uri.request_uri)
141
+ req["Accept"] = "application/json"
142
+ req["User-Agent"] = USER_AGENT
143
+ req["Authorization"] = "Bearer #{@access_token}" if auth_required && @access_token
144
+ (headers || {}).each { |k, v| req[k] = v }
145
+ if json
146
+ req["Content-Type"] = "application/json"
147
+ req.body = JSON.generate(json)
148
+ end
149
+
150
+ handle_response(http.request(req))
151
+ end
152
+
153
+ def handle_response(resp)
154
+ return {} if resp.code == "204"
155
+
156
+ data = begin
157
+ JSON.parse(resp.body || "")
158
+ rescue JSON::ParserError
159
+ { "raw" => resp.body.to_s }
160
+ end
161
+
162
+ case resp.code.to_i
163
+ when 200..299
164
+ data
165
+ when 401
166
+ @access_token = nil
167
+ raise AuthenticationError.new(data["error_description"] || "Authentication failed",
168
+ status_code: 401, response: data)
169
+ when 403
170
+ raise PermissionError.new(data["error_description"] || "Insufficient permissions",
171
+ status_code: 403, response: data)
172
+ when 404
173
+ raise NotFoundError.new(data["error_description"] || "Resource not found",
174
+ status_code: 404, response: data)
175
+ when 422
176
+ raise ValidationError.new(data["error_description"] || "Validation failed",
177
+ status_code: 422, response: data)
178
+ when 429
179
+ retry_after = resp["Retry-After"].to_i
180
+ retry_after = 60 if retry_after <= 0
181
+ raise RateLimitError.new(data["error_description"] || "Rate limit exceeded",
182
+ status_code: 429, retry_after: retry_after, response: data)
183
+ else
184
+ raise APIError.new(data["error_description"] || "HTTP #{resp.code}",
185
+ status_code: resp.code.to_i, response: data)
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Huntrecht
4
+ # Base error for all Huntrecht API failures.
5
+ class APIError < StandardError
6
+ attr_reader :status_code, :response
7
+
8
+ def initialize(message = nil, status_code: nil, response: nil)
9
+ super(message)
10
+ @status_code = status_code
11
+ @response = response
12
+ end
13
+ end
14
+
15
+ # 401 — missing, expired, or invalid credentials.
16
+ class AuthenticationError < APIError; end
17
+
18
+ # 403 — authenticated but not permitted.
19
+ class PermissionError < APIError; end
20
+
21
+ # 404 — resource not found.
22
+ class NotFoundError < APIError; end
23
+
24
+ # 422 — request validation failed.
25
+ class ValidationError < APIError; end
26
+
27
+ # 429 — rate limited. Carries the Retry-After seconds.
28
+ class RateLimitError < APIError
29
+ attr_reader :retry_after
30
+
31
+ def initialize(message = nil, status_code: 429, retry_after: 60, response: nil)
32
+ super(message, status_code: status_code, response: response)
33
+ @retry_after = retry_after
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,325 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Huntrecht
4
+ module Resources
5
+ # Base class for all API resources.
6
+ class Base
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ private
12
+
13
+ def request(method, path, **kwargs)
14
+ @client.request(method, path, **kwargs)
15
+ end
16
+
17
+ def page_params(page, per_page, status = nil)
18
+ params = { page: page, per_page: per_page }
19
+ params[:status] = status if status
20
+ params
21
+ end
22
+
23
+ # Accept a body as positional hash, kwargs, or both (Ruby 3
24
+ # separates the two at call sites, so support each style).
25
+ def body_for(data = nil, **kwargs)
26
+ body = {}
27
+ body.merge!(data) if data
28
+ kwargs.each { |k, v| body[k.to_s] = v }
29
+ body
30
+ end
31
+ end
32
+
33
+ # OAuth2 token lifecycle.
34
+ class AuthAPI < Base
35
+ def token(grant_type: "client_credentials", client_id: nil, client_secret: nil,
36
+ refresh_token: nil, scope: nil)
37
+ body = { "grant_type" => grant_type }
38
+ if grant_type == "client_credentials"
39
+ body["client_id"] = client_id || @client.client_id
40
+ body["client_secret"] = client_secret || @client.client_secret
41
+ body["scope"] = scope if scope
42
+ elsif grant_type == "refresh_token"
43
+ body["refresh_token"] = refresh_token || @client.refresh_token || ""
44
+ end
45
+ data = request("POST", "/auth/token", json: body, auth_required: false)
46
+ @client.store_tokens(data)
47
+ data
48
+ end
49
+
50
+ def revoke(token = nil)
51
+ tok = token || @client.access_token || ""
52
+ request("POST", "/auth/revoke", params: { token: tok }, auth_required: false)
53
+ end
54
+ end
55
+
56
+ # OAuth2 API clients.
57
+ class ClientsAPI < Base
58
+ def list(user_id)
59
+ request("GET", "/clients", params: { user_id: user_id })
60
+ end
61
+
62
+ def create(user_id, data = nil, **kwargs)
63
+ request("POST", "/clients", params: { user_id: user_id },
64
+ json: body_for(data, **kwargs))
65
+ end
66
+
67
+ def update(user_id, client_id, data = nil, **kwargs)
68
+ request("PATCH", "/clients/#{client_id}", params: { user_id: user_id },
69
+ json: body_for(data, **kwargs))
70
+ end
71
+
72
+ def rotate_secret(user_id, client_id)
73
+ request("POST", "/clients/#{client_id}/rotate", params: { user_id: user_id })
74
+ end
75
+
76
+ def delete(user_id, client_id)
77
+ request("DELETE", "/clients/#{client_id}", params: { user_id: user_id })
78
+ end
79
+ end
80
+
81
+ # B2B trade orders.
82
+ class OrdersAPI < Base
83
+ def list(page: 1, per_page: 20, status: nil)
84
+ request("GET", "/orders", params: page_params(page, per_page, status))
85
+ end
86
+
87
+ def get(order_id)
88
+ request("GET", "/orders/#{order_id}")
89
+ end
90
+
91
+ def create(commodity:, quantity:, delivery_terms: "FOB", destination: nil, currency: "USD")
92
+ params = { commodity: commodity, quantity: quantity,
93
+ delivery_terms: delivery_terms, currency: currency }
94
+ params[:destination] = destination if destination
95
+ request("POST", "/orders", params: params)
96
+ end
97
+ end
98
+
99
+ # Payments.
100
+ class PaymentsAPI < Base
101
+ def list(page: 1, per_page: 20, status: nil)
102
+ request("GET", "/payments", params: page_params(page, per_page, status))
103
+ end
104
+
105
+ def get(payment_id)
106
+ request("GET", "/payments/#{payment_id}")
107
+ end
108
+
109
+ def create(data = nil, **kwargs)
110
+ request("POST", "/payments", json: body_for(data, **kwargs))
111
+ end
112
+ end
113
+
114
+ # B2B subscriptions.
115
+ class SubscriptionsAPI < Base
116
+ def list(page: 1, per_page: 20, status: nil, include_payment_history: false)
117
+ params = page_params(page, per_page, status)
118
+ params[:include_payment_history] = include_payment_history
119
+ request("GET", "/subscriptions", params: params)
120
+ end
121
+
122
+ def get(subscription_id)
123
+ request("GET", "/subscriptions/#{subscription_id}")
124
+ end
125
+ end
126
+
127
+ # Basic credit scoring.
128
+ class CreditAPI < Base
129
+ def assess(data = nil, **kwargs)
130
+ request("POST", "/credit/assess", json: body_for(data, **kwargs))
131
+ end
132
+
133
+ def score(customer_email)
134
+ request("GET", "/credit/score/#{customer_email}")
135
+ end
136
+ end
137
+
138
+ # Risk scoring, company assessments, improvement, credit history.
139
+ class CreditRiskAPI < Base
140
+ def get_score(customer_id)
141
+ request("GET", "/credit-risk/score", params: { customer_id: customer_id })
142
+ end
143
+
144
+ def assess(data = nil, **kwargs)
145
+ request("POST", "/credit-risk/assess", json: body_for(data, **kwargs))
146
+ end
147
+
148
+ def get_history(customer_id, limit: 20)
149
+ request("GET", "/data-connect/credit-history",
150
+ params: { customer_id: customer_id, limit: limit })
151
+ end
152
+
153
+ def get_assessment(user_id)
154
+ request("GET", "/company/credit-assessment", params: { user_id: user_id })
155
+ end
156
+
157
+ def request_assessment(data = nil, **kwargs)
158
+ request("POST", "/company/credit-assessment/request", json: body_for(data, **kwargs))
159
+ end
160
+
161
+ def get_improvement_options
162
+ request("GET", "/credit-improvement/available-options")
163
+ end
164
+
165
+ def connect_wallet(data = nil, **kwargs)
166
+ request("POST", "/credit-improvement/connect-wallet", json: body_for(data, **kwargs))
167
+ end
168
+
169
+ def apply_boosts(data = nil, **kwargs)
170
+ request("POST", "/credit-improvement/apply-boosts", json: body_for(data, **kwargs))
171
+ end
172
+ end
173
+
174
+ # KYC submissions.
175
+ class KycAPI < Base
176
+ def list(page: 1, per_page: 20, status: nil)
177
+ request("GET", "/kyc", params: page_params(page, per_page, status))
178
+ end
179
+
180
+ def get(submission_id)
181
+ request("GET", "/kyc/#{submission_id}")
182
+ end
183
+
184
+ def submit(data = nil, **kwargs)
185
+ request("POST", "/kyc", json: body_for(data, **kwargs))
186
+ end
187
+ end
188
+
189
+ # Commodity quotes.
190
+ class QuotesAPI < Base
191
+ def list(page: 1, per_page: 20)
192
+ request("GET", "/quotes", params: { page: page, per_page: per_page })
193
+ end
194
+
195
+ def get(quote_id)
196
+ request("GET", "/quotes/#{quote_id}")
197
+ end
198
+
199
+ def create(data = nil, **kwargs)
200
+ request("POST", "/quotes", json: body_for(data, **kwargs))
201
+ end
202
+ end
203
+
204
+ # User profiles.
205
+ class UsersAPI < Base
206
+ def me
207
+ request("GET", "/users/me")
208
+ end
209
+
210
+ def get(user_id)
211
+ request("GET", "/users/#{user_id}")
212
+ end
213
+ end
214
+
215
+ # Shopify-backed catalog.
216
+ class StorefrontAPI < Base
217
+ def collections(first: 20, include_products: false)
218
+ first = 100 if first > 100
219
+ request("GET", "/storefront/collections",
220
+ params: { first: first, include_products: include_products })
221
+ end
222
+
223
+ def collection(handle, products_first: 20)
224
+ products_first = 250 if products_first > 250
225
+ request("GET", "/storefront/collections/#{handle}",
226
+ params: { products_first: products_first })
227
+ end
228
+
229
+ def products(first: 20, after: nil, b2b_only: false)
230
+ first = 100 if first > 100
231
+ params = { first: first, b2b_only: b2b_only }
232
+ params[:after] = after if after
233
+ request("GET", "/storefront/products", params: params)
234
+ end
235
+
236
+ def product(handle)
237
+ request("GET", "/storefront/products/#{handle}")
238
+ end
239
+
240
+ def search(query, first: 10, b2b_only: false)
241
+ first = 50 if first > 50
242
+ request("GET", "/storefront/search",
243
+ params: { query: query, first: first, b2b_only: b2b_only })
244
+ end
245
+ end
246
+
247
+ # Price-drop events (public).
248
+ class PriceDropsAPI < Base
249
+ def list(limit: 10, min_discount: 5.0, days: 7)
250
+ limit = 50 if limit > 50
251
+ request("GET", "/price-drops",
252
+ params: { limit: limit, min_discount: min_discount, days: days },
253
+ auth_required: false)
254
+ end
255
+
256
+ def featured(limit: 10)
257
+ limit = 10 if limit > 10
258
+ request("GET", "/price-drops/featured", params: { limit: limit },
259
+ auth_required: false)
260
+ end
261
+ end
262
+
263
+ # Theme-safe proxy endpoints (public).
264
+ class AppProxyAPI < Base
265
+ def collections(first: 20, signature: nil)
266
+ first = 100 if first > 100
267
+ params = { first: first }
268
+ params[:signature] = signature if signature
269
+ request("GET", "/app-proxy/collections", params: params, auth_required: false)
270
+ end
271
+
272
+ def collection(handle, first: 20, signature: nil)
273
+ first = 250 if first > 250
274
+ params = { first: first }
275
+ params[:signature] = signature if signature
276
+ request("GET", "/app-proxy/collections/#{handle}", params: params,
277
+ auth_required: false)
278
+ end
279
+
280
+ def price_drops(limit: 10, min_discount: 5.0)
281
+ limit = 20 if limit > 20
282
+ request("GET", "/app-proxy/price-drops",
283
+ params: { limit: limit, min_discount: min_discount },
284
+ auth_required: false)
285
+ end
286
+
287
+ def payment_methods(customer_id: nil, product_price: 0, b2b_exclusive: false,
288
+ signature: nil)
289
+ params = { product_price: product_price, b2b_exclusive: b2b_exclusive }
290
+ params[:customer_id] = customer_id if customer_id
291
+ params[:signature] = signature if signature
292
+ request("GET", "/app-proxy/payment-methods", params: params,
293
+ auth_required: false)
294
+ end
295
+ end
296
+
297
+ # Linked wallets and bank accounts.
298
+ class LinkedPaymentsAPI < Base
299
+ def check_eligibility(customer_id, product_price: 0, b2b_exclusive: false)
300
+ request("GET", "/linked-payments/check-eligibility",
301
+ params: { customer_id: customer_id, product_price: product_price,
302
+ b2b_exclusive: b2b_exclusive },
303
+ auth_required: false)
304
+ end
305
+
306
+ def link_wallet(customer_id, wallet_address: nil, wallet_provider: nil)
307
+ body = { "customer_id" => customer_id }
308
+ body["wallet_address"] = wallet_address if wallet_address
309
+ body["wallet_provider"] = wallet_provider if wallet_provider
310
+ request("POST", "/linked-payments/link-wallet", json: body)
311
+ end
312
+
313
+ def link_bank(customer_id, plaid_access_token: nil, account_id: nil)
314
+ body = { "customer_id" => customer_id }
315
+ body["plaid_access_token"] = plaid_access_token if plaid_access_token
316
+ body["account_id"] = account_id if account_id
317
+ request("POST", "/linked-payments/link-bank", json: body)
318
+ end
319
+
320
+ def linked_accounts(customer_id)
321
+ request("GET", "/linked-payments/linked-accounts/#{customer_id}")
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Huntrecht
4
+ VERSION = "0.1.4"
5
+ end
data/lib/huntrecht.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Huntrecht Platform SDK for Ruby.
4
+ #
5
+ # Official client for the Huntrecht Platform API v1.
6
+ #
7
+ # require "huntrecht"
8
+ #
9
+ # client = Huntrecht::Client.new(
10
+ # client_id: "hnt_your_client_id",
11
+ # client_secret: "your_secret"
12
+ # )
13
+ # orders = client.orders.list
14
+ require_relative "huntrecht/version"
15
+ require_relative "huntrecht/errors"
16
+ require_relative "huntrecht/client"
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: huntrecht-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Huntrecht
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-09-17 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: B2B commerce, credit risk, KYC, quotes, storefront, and payments for
14
+ the Huntrecht Platform API v1.
15
+ email:
16
+ - dev@huntrecht.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - lib/huntrecht.rb
23
+ - lib/huntrecht/client.rb
24
+ - lib/huntrecht/errors.rb
25
+ - lib/huntrecht/resources.rb
26
+ - lib/huntrecht/version.rb
27
+ homepage: https://github.com/huntrecht/sdks
28
+ licenses:
29
+ - MIT
30
+ metadata:
31
+ homepage_uri: https://github.com/huntrecht/sdks
32
+ source_code_uri: https://github.com/huntrecht/sdks
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '3.0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubygems_version: 3.5.22
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: Official Ruby SDK for the Huntrecht Platform API v1
52
+ test_files: []