hi_energy_ai 0.1.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 (34) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +13 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +339 -0
  5. data/lib/hi_energy_ai/client.rb +229 -0
  6. data/lib/hi_energy_ai/configuration.rb +24 -0
  7. data/lib/hi_energy_ai/error.rb +44 -0
  8. data/lib/hi_energy_ai/paginator.rb +27 -0
  9. data/lib/hi_energy_ai/resource.rb +37 -0
  10. data/lib/hi_energy_ai/resources/advertisers.rb +39 -0
  11. data/lib/hi_energy_ai/resources/agencies.rb +15 -0
  12. data/lib/hi_energy_ai/resources/clicks.rb +11 -0
  13. data/lib/hi_energy_ai/resources/contacts.rb +19 -0
  14. data/lib/hi_energy_ai/resources/deals.rb +23 -0
  15. data/lib/hi_energy_ai/resources/deeplinks.rb +11 -0
  16. data/lib/hi_energy_ai/resources/domains.rb +11 -0
  17. data/lib/hi_energy_ai/resources/exports.rb +19 -0
  18. data/lib/hi_energy_ai/resources/mcp.rb +41 -0
  19. data/lib/hi_energy_ai/resources/networks.rb +15 -0
  20. data/lib/hi_energy_ai/resources/opportunities.rb +11 -0
  21. data/lib/hi_energy_ai/resources/publishers.rb +27 -0
  22. data/lib/hi_energy_ai/resources/reports.rb +15 -0
  23. data/lib/hi_energy_ai/resources/schema.rb +11 -0
  24. data/lib/hi_energy_ai/resources/search.rb +11 -0
  25. data/lib/hi_energy_ai/resources/status_changes.rb +11 -0
  26. data/lib/hi_energy_ai/resources/tags.rb +17 -0
  27. data/lib/hi_energy_ai/resources/tools.rb +11 -0
  28. data/lib/hi_energy_ai/resources/transactions.rb +15 -0
  29. data/lib/hi_energy_ai/resources/users.rb +31 -0
  30. data/lib/hi_energy_ai/resources/verticals.rb +11 -0
  31. data/lib/hi_energy_ai/response.rb +56 -0
  32. data/lib/hi_energy_ai/version.rb +5 -0
  33. data/lib/hi_energy_ai.rb +29 -0
  34. metadata +164 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b1e97ca461ca7e385a0230330b8630a893e4715141c294f494c296fda9e3c9c8
4
+ data.tar.gz: b11f51535e0ea7b9ab64d972c6f8572f02c95c7802556c68505a56dd97cafacb
5
+ SHA512:
6
+ metadata.gz: fcff37287e8139af46061d328a0bab3b89797b628f0b50ccaf409363b7093dd8149b82134117cbba5f928df07ba09f3d2d3ab46426e8d5bf86a40ee1f038f2a2
7
+ data.tar.gz: c322f67afd5c2330745a94f99a5ad6e68945d1eebe20be245c0f1e72dc937fa23957f850adbfb159efdb225ea678b679d16dc6ec7c98cbfba6a9bb029962363a
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-05-18
4
+
5
+ ### Added
6
+
7
+ - Initial RubyGems release
8
+ - Faraday client for Hi Energy AI API v1 (`https://app.hienergy.ai/api/v1`)
9
+ - Resource accessors for advertisers, deals, contacts, transactions, clicks, reports, publishers, search, schema, tools, and more
10
+ - MCP helpers (`GET /mcp`, `POST /mcp` JSON-RPC, `GET /mcp/integration.json`)
11
+ - `HiEnergyAi::Error` with API error codes and `request_id`
12
+ - Offset pagination helpers on `HiEnergyAi::Response`
13
+ - Optional global `dry_run` query parameter support
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Patrick Karsh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,339 @@
1
+ # Hi Energy AI Ruby Gem — Official Affiliate Marketing API Client
2
+
3
+ [![Gem Version](https://img.shields.io/gem/v/hi_energy_ai)](https://rubygems.org/gems/hi_energy_ai)
4
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.2-red)](https://www.ruby-lang.org/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.txt)
6
+ [![API Docs](https://img.shields.io/badge/docs-app.hienergy.ai-api_documentation-0d6efd)](https://app.hienergy.ai/api_documentation)
7
+
8
+ **`hi_energy_ai`** is the official **Ruby client** for the [Hi Energy AI API](https://app.hienergy.ai/api_documentation) — a production REST API for **affiliate marketing data**, **coupon deals**, **advertiser discovery**, **commission transactions**, **click reporting**, and **publisher analytics**.
9
+
10
+ Use this gem to integrate Hi Energy AI into Ruby on Rails apps, background jobs, data pipelines, AI agents, and MCP-compatible tools without hand-rolling HTTP calls.
11
+
12
+ | | |
13
+ |---|---|
14
+ | **RubyGems** | [`hi_energy_ai`](https://rubygems.org/gems/hi_energy_ai) |
15
+ | **API base URL** | `https://app.hienergy.ai/api/v1` |
16
+ | **Documentation** | [app.hienergy.ai/api_documentation](https://app.hienergy.ai/api_documentation) |
17
+ | **OpenAPI** | [OpenAPI reference](https://app.hienergy.ai/api_documentation/openapi) |
18
+ | **Repository** | [github.com/HiEnergyAgency/hi_energy_api](https://github.com/HiEnergyAgency/hi_energy_api) |
19
+
20
+ ---
21
+
22
+ ## Table of contents
23
+
24
+ - [What is the Hi Energy AI API?](#what-is-the-hi-energy-ai-api)
25
+ - [Why use the hi_energy_ai gem?](#why-use-the-hi_energy_ai-gem)
26
+ - [Installation](#installation)
27
+ - [Quick start](#quick-start)
28
+ - [Authentication](#authentication)
29
+ - [API endpoints covered](#api-endpoints-covered)
30
+ - [Affiliate networks and data sources](#affiliate-networks-and-data-sources)
31
+ - [MCP and AI agent integration](#mcp-and-ai-agent-integration)
32
+ - [Pagination, dry run, and rate limits](#pagination-dry-run-and-rate-limits)
33
+ - [Configuration](#configuration)
34
+ - [Error handling](#error-handling)
35
+ - [Frequently asked questions](#frequently-asked-questions)
36
+ - [Development](#development)
37
+ - [License](#license)
38
+
39
+ ---
40
+
41
+ ## What is the Hi Energy AI API?
42
+
43
+ [Hi Energy AI](https://app.hienergy.ai) aggregates and normalizes affiliate program data from major networks — including **FlexOffers**, **Commission Junction (CJ)**, **Rakuten**, **Impact**, **Awin**, **Partnerize**, **Pepperjam**, and **ShareASale** — and exposes it through a single, **AI-friendly JSON API**.
44
+
45
+ The platform is built for:
46
+
47
+ - **Affiliate marketers** searching deals and advertisers by domain, category, or keyword
48
+ - **Publishers** tracking transactions, clicks, commissions, and status changes
49
+ - **Developers** building dashboards, ETL jobs, or internal tools on structured JSON
50
+ - **AI agents** using MCP, OpenAPI, and predictable pagination metadata
51
+
52
+ Full reference: **[Hi Energy AI API Documentation](https://app.hienergy.ai/api_documentation)**
53
+
54
+ ---
55
+
56
+ ## Why use the hi_energy_ai gem?
57
+
58
+ | Benefit | Description |
59
+ |---------|-------------|
60
+ | **Official client** | Matches the public API playground and OpenAPI schema |
61
+ | **Typed Ruby API** | Resource objects (`client.deals`, `client.advertisers`, …) instead of raw URLs |
62
+ | **Auth built in** | `X-Api-Key` and OAuth bearer support |
63
+ | **Pagination helpers** | Auto-follow `meta.next_page` on list endpoints |
64
+ | **MCP support** | Bootstrap and JSON-RPC against `/mcp` on the app origin |
65
+ | **Dry run mode** | Validate requests with `dry_run=true` without live data |
66
+ | **MIT licensed** | Free to use in commercial Ruby projects |
67
+
68
+ ---
69
+
70
+ ## Installation
71
+
72
+ Add to your `Gemfile`:
73
+
74
+ ```ruby
75
+ gem "hi_energy_ai"
76
+ ```
77
+
78
+ Or install from RubyGems:
79
+
80
+ ```bash
81
+ gem install hi_energy_ai
82
+ ```
83
+
84
+ **Requirements:** Ruby **3.2+**
85
+
86
+ ---
87
+
88
+ ## Quick start
89
+
90
+ 1. Sign in at [Hi Energy AI](https://app.hienergy.ai) and create an API key on the [API Key page](https://app.hienergy.ai/api_documentation/api_key).
91
+ 2. Install the gem and call the API:
92
+
93
+ ```ruby
94
+ require "hi_energy_ai"
95
+
96
+ client = HiEnergyAi.new(api_key: ENV["HI_ENERGY_API_KEY"])
97
+
98
+ # Affiliate advertisers
99
+ client.advertisers.list(limit: 5)
100
+
101
+ # Coupon / promo deals
102
+ client.deals.list(active: true, country: "US")
103
+
104
+ # Universal search (advertisers, deals, contacts, …)
105
+ client.search.query(q: "nike", types: "advertisers,deals", per_type_limit: 5)
106
+
107
+ # Commission and sales reports
108
+ client.reports.find("top_advertisers_by_sales", period: "last_90_days", limit: 10)
109
+ ```
110
+
111
+ **Equivalent curl** (same API the gem calls):
112
+
113
+ ```bash
114
+ curl -H "X-Api-Key: YOUR_API_KEY" \
115
+ "https://app.hienergy.ai/api/v1/advertisers?limit=5"
116
+ ```
117
+
118
+ ```bash
119
+ curl -H "X-Api-Key: YOUR_API_KEY" \
120
+ "https://app.hienergy.ai/api/v1/deals?active=true&country=US"
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Authentication
126
+
127
+ The Hi Energy AI API accepts API keys via header (recommended):
128
+
129
+ ```http
130
+ X-Api-Key: YOUR_API_KEY
131
+ ```
132
+
133
+ ```ruby
134
+ client = HiEnergyAi.new(api_key: "your_integration_key")
135
+ ```
136
+
137
+ OAuth **Bearer** tokens for signed-in user sessions:
138
+
139
+ ```ruby
140
+ client = HiEnergyAi.new(bearer_token: ENV["AUTH0_ACCESS_TOKEN"])
141
+ ```
142
+
143
+ Get your key: [API Documentation → API Key](https://app.hienergy.ai/api_documentation/api_key)
144
+
145
+ ---
146
+
147
+ ## API endpoints covered
148
+
149
+ Every method maps to the [API playground](https://app.hienergy.ai/api_documentation):
150
+
151
+ | Ruby client | HTTP | Use case |
152
+ |-------------|------|----------|
153
+ | `search` | `GET /api/v1/search` | Universal Searchkick search |
154
+ | `deals` | `GET /api/v1/deals` | List and filter affiliate deals / coupons |
155
+ | `advertisers` | `GET /api/v1/advertisers` | Advertiser discovery and filters |
156
+ | `advertisers.search_by_domain` | `GET /api/v1/advertisers/search_by_domain` | Lookup by domain |
157
+ | `advertisers.by_domain` | `GET /api/v1/advertisers?domain=` | Domain filter shorthand |
158
+ | `contacts` | `GET`, `POST /api/v1/contacts` | Contact search and create |
159
+ | `transactions` | `GET /api/v1/transactions` | Affiliate sales and commissions |
160
+ | `clicks` | `GET /api/v1/clicks` | Click-level reporting (date range required) |
161
+ | `opportunities` | `GET /api/v1/opportunities` | Publisher opportunity advertisers |
162
+ | `verticals` | `GET /api/v1/verticals` | Industry / category list |
163
+ | `tags` | `GET /api/v1/tags` | Tag search and tagged advertisers |
164
+ | `publishers` | CRUD `/api/v1/publishers` | Publisher accounts and credentials |
165
+ | `agencies` | `GET /api/v1/agencies` | Agency directory |
166
+ | `networks` | `GET /api/v1/networks` | Affiliate network list |
167
+ | `reports` | `GET /api/v1/reports` | Prebuilt analytics reports |
168
+ | `users` | `/api/v1/users` | Scoped user management |
169
+ | `status_changes` | `GET /api/v1/status_changes` | Advertiser approval history |
170
+ | `deeplinks` | `POST /api/v1/deeplinks/generate` | Tracking link generator |
171
+ | `domains` | `GET /api/v1/domains/search` | Domain search |
172
+ | `tools` | `GET /api/v1/tools` | MCP tool catalog (JSON Schema) |
173
+ | `schema` | `GET /api/v1/schema` | OpenAPI 3.0 download |
174
+ | `exports` | `/api/v1/exports` | Async report exports |
175
+ | `mcp` | `GET`, `POST /mcp` | MCP bootstrap and JSON-RPC |
176
+
177
+ ---
178
+
179
+ ## Affiliate networks and data sources
180
+
181
+ Hi Energy AI normalizes feeds from:
182
+
183
+ FlexOffers · CJ (Commission Junction) · Rakuten Advertising · Impact · Awin · Partnerize · Pepperjam · ShareASale · proprietary enrichment
184
+
185
+ One API key, one JSON schema — no per-network SDK required in Ruby.
186
+
187
+ ---
188
+
189
+ ## MCP and AI agent integration
190
+
191
+ The API is designed for **AI agents**, **chatbots**, and **MCP clients**. MCP routes run on the app origin (`https://app.hienergy.ai`), not under `/api/v1`:
192
+
193
+ ```ruby
194
+ client.mcp.bootstrap
195
+ client.mcp.integration
196
+ client.mcp.initialize_session
197
+ client.mcp.call("tools/list")
198
+ ```
199
+
200
+ **Initialize MCP via curl:**
201
+
202
+ ```bash
203
+ curl -X POST https://app.hienergy.ai/mcp \
204
+ -H "X-Api-Key: YOUR_API_KEY" \
205
+ -H "Content-Type: application/json" \
206
+ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25"}}'
207
+ ```
208
+
209
+ See also: [MCP Server documentation](https://app.hienergy.ai/api_documentation) · [OpenAPI reference](https://app.hienergy.ai/api_documentation/openapi)
210
+
211
+ ---
212
+
213
+ ## Pagination, dry run, and rate limits
214
+
215
+ ### Pagination
216
+
217
+ List responses include `data` and `meta` (`current_page`, `next_page`, `per_page`, `has_more`):
218
+
219
+ ```ruby
220
+ client.paginate("/deals", params: { limit: 50 }).each do |page|
221
+ page.data.each { |deal| process(deal) }
222
+ end
223
+ ```
224
+
225
+ ### Dry run
226
+
227
+ Test integration wiring without live data:
228
+
229
+ ```ruby
230
+ HiEnergyAi.new(api_key: key, dry_run: true).deals.list(active: true)
231
+ ```
232
+
233
+ ### Rate limits
234
+
235
+ | Account type | Limit |
236
+ |--------------|-------|
237
+ | Hi Energy publisher | 1,000 requests / hour |
238
+ | Other publishers | 10,000 requests / hour |
239
+
240
+ Headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
241
+
242
+ ---
243
+
244
+ ## Configuration
245
+
246
+ ```ruby
247
+ HiEnergyAi.configure do |config|
248
+ config.api_key = ENV["HI_ENERGY_API_KEY"]
249
+ config.base_url = HiEnergyAi::Configuration::API_BASE_URL # https://app.hienergy.ai/api/v1
250
+ config.app_origin = HiEnergyAi::Configuration::APP_ORIGIN # https://app.hienergy.ai
251
+ config.timeout = 60
252
+ end
253
+
254
+ client = HiEnergyAi.new
255
+ ```
256
+
257
+ | Setting | Default |
258
+ |---------|---------|
259
+ | `base_url` | `https://app.hienergy.ai/api/v1` |
260
+ | `app_origin` | `https://app.hienergy.ai` |
261
+ | `timeout` | `30` seconds |
262
+
263
+ ---
264
+
265
+ ## Error handling
266
+
267
+ ```ruby
268
+ begin
269
+ client.advertisers.find(999)
270
+ rescue HiEnergyAi::Error => e
271
+ e.code # API error code
272
+ e.message # Human-readable message
273
+ e.request_id # Support / debugging
274
+ e.status # HTTP status
275
+ end
276
+ ```
277
+
278
+ ---
279
+
280
+ ## Frequently asked questions
281
+
282
+ ### What is the difference between hi_energy_ai and calling the REST API directly?
283
+
284
+ The gem wraps the same endpoints documented at [app.hienergy.ai/api_documentation](https://app.hienergy.ai/api_documentation). It adds Ruby resource methods, authentication headers, JSON parsing, pagination iterators, and structured errors — so you do not maintain Faraday or Net::HTTP code in every project.
285
+
286
+ ### Is there a Python or JavaScript SDK?
287
+
288
+ This repository is the **official Ruby gem**. Other languages can use the REST API or OpenAPI spec at `GET /api/v1/schema`.
289
+
290
+ ### How do I search advertisers by website domain?
291
+
292
+ ```ruby
293
+ client.advertisers.by_domain("amazon.com")
294
+ # or
295
+ client.advertisers.search_by_domain(domain: "amazon.com")
296
+ ```
297
+
298
+ ### Does the gem support OpenClaw or custom AI tools?
299
+
300
+ Yes. Use `client.tools.list` for the MCP tool catalog, `client.schema.fetch` for OpenAPI, and `client.mcp` for JSON-RPC against `/mcp`.
301
+
302
+ ### Where do I get an API key?
303
+
304
+ Sign in at [app.hienergy.ai](https://app.hienergy.ai) and visit [API Documentation → API Key](https://app.hienergy.ai/api_documentation/api_key).
305
+
306
+ ---
307
+
308
+ ## Development
309
+
310
+ ```bash
311
+ git clone https://github.com/HiEnergyAgency/hi_energy_api.git
312
+ cd hi_energy_api
313
+ bundle install
314
+ bundle exec rspec
315
+ ```
316
+
317
+ Interactive console:
318
+
319
+ ```bash
320
+ bin/console
321
+ ```
322
+
323
+ ---
324
+
325
+ ## Related links
326
+
327
+ - [Hi Energy AI API Documentation](https://app.hienergy.ai/api_documentation) — full endpoint reference and playground
328
+ - [OpenAPI / Swagger](https://app.hienergy.ai/api_documentation/openapi)
329
+ - [RubyGems: hi_energy_ai](https://rubygems.org/gems/hi_energy_ai)
330
+ - [GitHub: HiEnergyAgency/hi_energy_api](https://github.com/HiEnergyAgency/hi_energy_api)
331
+ - [CHANGELOG](CHANGELOG.md)
332
+
333
+ ---
334
+
335
+ ## License
336
+
337
+ MIT — see [LICENSE.txt](LICENSE.txt).
338
+
339
+ API access requires a Hi Energy AI account and API key. Platform terms apply at [app.hienergy.ai](https://app.hienergy.ai).
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "faraday"
5
+ require "faraday/net_http"
6
+
7
+ module HiEnergyAi
8
+ class Client
9
+ attr_reader :config
10
+
11
+ def self.configure
12
+ yield(configuration)
13
+ configuration
14
+ end
15
+
16
+ def self.configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+
20
+ def initialize(api_key: nil, bearer_token: nil, base_url: nil, app_origin: nil, timeout: nil, user_agent: nil, dry_run: nil)
21
+ @config = self.class.configuration.dup
22
+ @config.api_key = api_key if api_key
23
+ @config.bearer_token = bearer_token if bearer_token
24
+ @config.base_url = base_url if base_url
25
+ @config.app_origin = app_origin if app_origin
26
+ @config.timeout = timeout if timeout
27
+ @config.user_agent = user_agent if user_agent
28
+ @config.dry_run = dry_run unless dry_run.nil?
29
+
30
+ raise ArgumentError, "api_key or bearer_token is required" unless @config.credentials_present?
31
+ end
32
+
33
+ def get(path, params: {})
34
+ request(:get, path, params: params)
35
+ end
36
+
37
+ def post(path, params: {}, body: nil)
38
+ request(:post, path, params: params, body: body)
39
+ end
40
+
41
+ def patch(path, params: {}, body: nil)
42
+ request(:patch, path, params: params, body: body)
43
+ end
44
+
45
+ def delete(path, params: {})
46
+ request(:delete, path, params: params)
47
+ end
48
+
49
+ def app_get(path, params: {})
50
+ app_request(:get, path, params: params)
51
+ end
52
+
53
+ def app_post(path, params: {}, body: nil)
54
+ app_request(:post, path, params: params, body: body)
55
+ end
56
+
57
+ def request(method, path, params: {}, body: nil)
58
+ perform_request(api_connection, method, path, params: params, body: body)
59
+ end
60
+
61
+ def app_request(method, path, params: {}, body: nil)
62
+ perform_request(app_connection, method, path, params: params, body: body)
63
+ end
64
+
65
+ def paginate(path, params: {})
66
+ Paginator.new(client: self, path: path, params: params)
67
+ end
68
+
69
+ def tools
70
+ @tools ||= Resources::Tools.new(self)
71
+ end
72
+
73
+ def schema
74
+ @schema ||= Resources::Schema.new(self)
75
+ end
76
+
77
+ def search
78
+ @search ||= Resources::Search.new(self)
79
+ end
80
+
81
+ def advertisers
82
+ @advertisers ||= Resources::Advertisers.new(self)
83
+ end
84
+
85
+ def deals
86
+ @deals ||= Resources::Deals.new(self)
87
+ end
88
+
89
+ def contacts
90
+ @contacts ||= Resources::Contacts.new(self)
91
+ end
92
+
93
+ def transactions
94
+ @transactions ||= Resources::Transactions.new(self)
95
+ end
96
+
97
+ def clicks
98
+ @clicks ||= Resources::Clicks.new(self)
99
+ end
100
+
101
+ def opportunities
102
+ @opportunities ||= Resources::Opportunities.new(self)
103
+ end
104
+
105
+ def reports
106
+ @reports ||= Resources::Reports.new(self)
107
+ end
108
+
109
+ def publishers
110
+ @publishers ||= Resources::Publishers.new(self)
111
+ end
112
+
113
+ def agencies
114
+ @agencies ||= Resources::Agencies.new(self)
115
+ end
116
+
117
+ def networks
118
+ @networks ||= Resources::Networks.new(self)
119
+ end
120
+
121
+ def status_changes
122
+ @status_changes ||= Resources::StatusChanges.new(self)
123
+ end
124
+
125
+ def tags
126
+ @tags ||= Resources::Tags.new(self)
127
+ end
128
+
129
+ def users
130
+ @users ||= Resources::Users.new(self)
131
+ end
132
+
133
+ def verticals
134
+ @verticals ||= Resources::Verticals.new(self)
135
+ end
136
+
137
+ def domains
138
+ @domains ||= Resources::Domains.new(self)
139
+ end
140
+
141
+ def deeplinks
142
+ @deeplinks ||= Resources::Deeplinks.new(self)
143
+ end
144
+
145
+ def exports
146
+ @exports ||= Resources::Exports.new(self)
147
+ end
148
+
149
+ def mcp
150
+ @mcp ||= Resources::Mcp.new(self)
151
+ end
152
+
153
+ private
154
+
155
+ def api_connection
156
+ @api_connection ||= build_connection(config.base_url)
157
+ end
158
+
159
+ def app_connection
160
+ @app_connection ||= build_connection(config.app_origin)
161
+ end
162
+
163
+ def build_connection(base_url)
164
+ Faraday.new(url: base_url) do |faraday|
165
+ faraday.request :json
166
+ faraday.response :json, content_type: /\bjson$/
167
+ faraday.options.timeout = config.timeout
168
+ faraday.options.open_timeout = config.timeout
169
+ faraday.headers["Accept"] = "application/json"
170
+ faraday.headers["User-Agent"] = config.user_agent
171
+ faraday.adapter Faraday.default_adapter
172
+ end
173
+ end
174
+
175
+ def perform_request(connection, method, path, params: {}, body: nil)
176
+ response = connection.run_request(method, normalize_path(path), body, nil) do |req|
177
+ req.params.update(compact_params(params)) if params.any? || config.dry_run
178
+ apply_auth!(req)
179
+ end
180
+
181
+ handle_response(response)
182
+ end
183
+
184
+ def apply_auth!(req)
185
+ if !config.api_key.to_s.empty?
186
+ req.headers["X-Api-Key"] = config.api_key
187
+ elsif !config.bearer_token.to_s.empty?
188
+ req.headers["Authorization"] = "Bearer #{config.bearer_token}"
189
+ end
190
+ end
191
+
192
+ def normalize_path(path)
193
+ path.to_s.delete_prefix("/")
194
+ end
195
+
196
+ def compact_params(params)
197
+ merged = params.each_with_object({}) do |(key, value), memo|
198
+ next if value.nil?
199
+
200
+ memo[key] = value
201
+ end
202
+
203
+ merged[:dry_run] = true if config.dry_run && !merged.key?(:dry_run) && !merged.key?("dry_run")
204
+ merged
205
+ end
206
+
207
+ def handle_response(response)
208
+ status = response.status
209
+ body = normalize_body(response.body)
210
+
211
+ return Response.new(status: status, headers: response.headers, body: body) if success_status?(status)
212
+
213
+ raise Error.from_response(status, body)
214
+ end
215
+
216
+ def normalize_body(body)
217
+ return body if body.is_a?(Hash) || body.is_a?(Array)
218
+ return {} if body.nil? || body.to_s.strip.empty?
219
+
220
+ JSON.parse(body)
221
+ rescue JSON::ParserError
222
+ body
223
+ end
224
+
225
+ def success_status?(status)
226
+ status >= 200 && status < 300
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HiEnergyAi
4
+ class Configuration
5
+ APP_ORIGIN = "https://app.hienergy.ai"
6
+ API_BASE_URL = "https://app.hienergy.ai/api/v1"
7
+ DOCUMENTATION_URL = "https://app.hienergy.ai/api_documentation"
8
+ DEFAULT_TIMEOUT = 30
9
+
10
+ attr_accessor :api_key, :bearer_token, :base_url, :app_origin, :timeout, :user_agent, :dry_run
11
+
12
+ def initialize
13
+ @app_origin = APP_ORIGIN
14
+ @base_url = API_BASE_URL
15
+ @timeout = DEFAULT_TIMEOUT
16
+ @dry_run = false
17
+ @user_agent = "hi_energy_ai/#{HiEnergyAi::VERSION} (Ruby #{RUBY_VERSION})"
18
+ end
19
+
20
+ def credentials_present?
21
+ !api_key.to_s.empty? || !bearer_token.to_s.empty?
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HiEnergyAi
4
+ class Error < StandardError
5
+ attr_reader :status, :code, :request_id, :details, :response_body
6
+
7
+ def initialize(message = nil, status: nil, code: nil, request_id: nil, details: nil, response_body: nil)
8
+ super(message)
9
+ @status = status
10
+ @code = code
11
+ @request_id = request_id
12
+ @details = details
13
+ @response_body = response_body
14
+ end
15
+
16
+ def self.from_response(status, body)
17
+ payload = parse_json(body)
18
+ error = payload.is_a?(Hash) ? payload["error"] || payload[:error] : nil
19
+
20
+ if error.is_a?(Hash)
21
+ new(
22
+ error["message"] || error[:message] || "API request failed",
23
+ status: status,
24
+ code: error["code"] || error[:code],
25
+ request_id: error["request_id"] || error[:request_id],
26
+ details: error["details"] || error[:details],
27
+ response_body: body
28
+ )
29
+ else
30
+ new("API request failed with status #{status}", status: status, response_body: body)
31
+ end
32
+ end
33
+
34
+ def self.parse_json(body)
35
+ return body if body.is_a?(Hash) || body.is_a?(Array)
36
+ return {} if body.nil? || body.to_s.strip.empty?
37
+
38
+ JSON.parse(body)
39
+ rescue JSON::ParserError
40
+ {}
41
+ end
42
+ private_class_method :parse_json
43
+ end
44
+ end