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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +339 -0
- data/lib/hi_energy_ai/client.rb +229 -0
- data/lib/hi_energy_ai/configuration.rb +24 -0
- data/lib/hi_energy_ai/error.rb +44 -0
- data/lib/hi_energy_ai/paginator.rb +27 -0
- data/lib/hi_energy_ai/resource.rb +37 -0
- data/lib/hi_energy_ai/resources/advertisers.rb +39 -0
- data/lib/hi_energy_ai/resources/agencies.rb +15 -0
- data/lib/hi_energy_ai/resources/clicks.rb +11 -0
- data/lib/hi_energy_ai/resources/contacts.rb +19 -0
- data/lib/hi_energy_ai/resources/deals.rb +23 -0
- data/lib/hi_energy_ai/resources/deeplinks.rb +11 -0
- data/lib/hi_energy_ai/resources/domains.rb +11 -0
- data/lib/hi_energy_ai/resources/exports.rb +19 -0
- data/lib/hi_energy_ai/resources/mcp.rb +41 -0
- data/lib/hi_energy_ai/resources/networks.rb +15 -0
- data/lib/hi_energy_ai/resources/opportunities.rb +11 -0
- data/lib/hi_energy_ai/resources/publishers.rb +27 -0
- data/lib/hi_energy_ai/resources/reports.rb +15 -0
- data/lib/hi_energy_ai/resources/schema.rb +11 -0
- data/lib/hi_energy_ai/resources/search.rb +11 -0
- data/lib/hi_energy_ai/resources/status_changes.rb +11 -0
- data/lib/hi_energy_ai/resources/tags.rb +17 -0
- data/lib/hi_energy_ai/resources/tools.rb +11 -0
- data/lib/hi_energy_ai/resources/transactions.rb +15 -0
- data/lib/hi_energy_ai/resources/users.rb +31 -0
- data/lib/hi_energy_ai/resources/verticals.rb +11 -0
- data/lib/hi_energy_ai/response.rb +56 -0
- data/lib/hi_energy_ai/version.rb +5 -0
- data/lib/hi_energy_ai.rb +29 -0
- 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
|
+
[](https://rubygems.org/gems/hi_energy_ai)
|
|
4
|
+
[](https://www.ruby-lang.org/)
|
|
5
|
+
[](LICENSE.txt)
|
|
6
|
+
[](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
|