flexops 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: de7a8ef21d6a80d52efbbb2f7254fb5184c875ab84a09834204d4757736040ce
4
+ data.tar.gz: 741a555c559275fe53124fa44d5cd76d3d9dbf57031b12346dfecb63e9c26c73
5
+ SHA512:
6
+ metadata.gz: e32d5a2172b700037fe706867b7111a5f6af66d099bf8f759b916fb38134b7760a3212d2f44c6eec83a10b243cf602c2fadd20792884871fbdcc878b84306f8c
7
+ data.tar.gz: 4446a9a607aba3ccc55b694f4438a63d6d20422825a7bd476da37dad4021885e60d2b93e65f6cf72ef65eb9ac4b930f2afd0a4fc26361a651f877be185ed6a7f
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FlexOps, LLC
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,196 @@
1
+ # FlexOps Ruby SDK
2
+
3
+ Official Ruby SDK for the [FlexOps](https://flexops.io) multi-carrier shipping platform. Supports USPS, UPS, FedEx, DHL, OnTrac, Australia Post, Canada Post, Royal Mail, and LSO with rate shopping, label generation, tracking, webhooks, wallet, insurance, returns, and more.
4
+
5
+ ## Installation
6
+
7
+ Add to your Gemfile:
8
+
9
+ ```ruby
10
+ gem "flexops"
11
+ ```
12
+
13
+ Then:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Or install directly:
20
+
21
+ ```bash
22
+ gem install flexops
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```ruby
28
+ require "flexops"
29
+
30
+ # API key authentication (recommended for server-to-server)
31
+ client = FlexOps::Client.new(
32
+ api_key: "fxk_live_...",
33
+ workspace_id: "ws_abc123"
34
+ )
35
+
36
+ # Get shipping rates from all carriers
37
+ rates = client.shipping.get_rates(
38
+ from_address: {street1: "123 Main St", city: "New York", state: "NY", zip: "10001", country: "US"},
39
+ to_address: {street1: "456 Oak Ave", city: "Los Angeles", state: "CA", zip: "90210", country: "US"},
40
+ parcel: {weight: 16, weight_unit: "oz"}
41
+ )
42
+
43
+ # Create a label with the cheapest rate
44
+ cheapest = rates["data"].min_by { |r| r["totalCost"].to_f }
45
+ label = client.shipping.create_label(
46
+ carrier: cheapest["carrier"],
47
+ service: cheapest["service"],
48
+ from_address: {name: "Warehouse", street1: "123 Main St", city: "New York", state: "NY", zip: "10001", country: "US"},
49
+ to_address: {name: "Customer", street1: "456 Oak Ave", city: "Los Angeles", state: "CA", zip: "90210", country: "US"},
50
+ parcel: {weight: 16, weight_unit: "oz"}
51
+ )
52
+
53
+ puts "Label URL: #{label["data"]["labelUrl"]}"
54
+ puts "Tracking: #{label["data"]["trackingNumber"]}"
55
+
56
+ # Track a shipment
57
+ info = client.shipping.track("9400111899223456789012")
58
+ ```
59
+
60
+ ## Authentication
61
+
62
+ ### API key (recommended)
63
+
64
+ ```ruby
65
+ client = FlexOps::Client.new(api_key: "fxk_live_...", workspace_id: "ws_abc123")
66
+ ```
67
+
68
+ ### Email / password
69
+
70
+ ```ruby
71
+ client = FlexOps::Client.new(base_url: "https://gateway.flexops.io")
72
+ client.auth.login(email: "user@example.com", password: "password")
73
+ client.workspace_id = "ws_abc123"
74
+ ```
75
+
76
+ ## Sandbox / test keys
77
+
78
+ Use `fxk_test_...` (instead of `fxk_live_...`) to route to the sandbox environment. Mock carriers respond, nothing hits real carrier APIs, no charges, no real labels. Perfect for CI and integration tests.
79
+
80
+ ```ruby
81
+ client = FlexOps::Client.new(api_key: "fxk_test_...", workspace_id: "ws_abc123")
82
+ ```
83
+
84
+ ## Direct carrier operations
85
+
86
+ Access carrier-specific endpoints when you need full control:
87
+
88
+ ```ruby
89
+ # USPS domestic label
90
+ label = client.carriers.usps.create_domestic_label(
91
+ image_type: "PDF",
92
+ mail_class: "PRIORITY_MAIL",
93
+ weight_in_ounces: 16
94
+ )
95
+
96
+ # FedEx rate quote
97
+ rates = client.carriers.fedex.get_rates(...)
98
+
99
+ # UPS tracking
100
+ info = client.carriers.ups.track(tracking_number: "1Z999AA10123456784")
101
+
102
+ # DHL shipment
103
+ shipment = client.carriers.dhl.create_shipment(...)
104
+ ```
105
+
106
+ ## Webhook verification
107
+
108
+ ```ruby
109
+ valid = FlexOps::Resources::Webhooks.verify_signature(
110
+ payload: request.body.read,
111
+ signature: request.headers["X-FlexOps-Signature"],
112
+ secret: "whsec_..."
113
+ )
114
+ ```
115
+
116
+ ## Curl quickstart
117
+
118
+ Every SDK method is a thin wrapper around the FlexOps REST API. If you want to verify the API before committing to the SDK — or you're integrating from a language we don't ship a SDK for — these curl invocations hit the same endpoints:
119
+
120
+ ```bash
121
+ # Shop rates across all connected carriers
122
+ curl -X POST https://gateway.flexops.io/api/workspaces/ws_abc123/shipping/rates \
123
+ -H "X-API-Key: fxk_live_..." \
124
+ -H "Content-Type: application/json" \
125
+ -d '{
126
+ "fromAddress": {"street1": "123 Main St", "city": "New York", "state": "NY", "zip": "10001", "country": "US"},
127
+ "toAddress": {"street1": "456 Oak Ave", "city": "Los Angeles", "state": "CA", "zip": "90210", "country": "US"},
128
+ "parcel": {"weight": 16, "weightUnit": "oz"}
129
+ }'
130
+
131
+ # Create a label
132
+ curl -X POST https://gateway.flexops.io/api/workspaces/ws_abc123/shipping/labels \
133
+ -H "X-API-Key: fxk_live_..." \
134
+ -H "Content-Type: application/json" \
135
+ -d '{
136
+ "carrier": "USPS",
137
+ "service": "PRIORITY_MAIL",
138
+ "fromAddress": {"name": "Warehouse", "street1": "123 Main St", "city": "New York", "state": "NY", "zip": "10001", "country": "US"},
139
+ "toAddress": {"name": "Customer", "street1": "456 Oak Ave", "city": "Los Angeles", "state": "CA", "zip": "90210", "country": "US"},
140
+ "parcel": {"weight": 16, "weightUnit": "oz"}
141
+ }'
142
+
143
+ # Track a shipment
144
+ curl https://gateway.flexops.io/api/workspaces/ws_abc123/shipping/track/9400111899223456789012 \
145
+ -H "X-API-Key: fxk_live_..."
146
+
147
+ # Cancel a label (via the unified carrier-agnostic endpoint)
148
+ curl -X DELETE https://gateway.flexops.io/api/v3.0/shipping/Usps/cancel/9400111899223456789012 \
149
+ -H "X-API-Key: fxk_live_..."
150
+ ```
151
+
152
+ Use an `fxk_test_...` key instead of `fxk_live_...` to hit the sandbox environment; mock carriers respond, no real charges, no real labels.
153
+
154
+ ## Resources
155
+
156
+ | Resource | Description |
157
+ |----------|-------------|
158
+ | `client.auth` | Login, register, password management |
159
+ | `client.workspaces` | Workspace CRUD, membership, branding |
160
+ | `client.shipping` | Rate shopping, labels, tracking, batch, cancel |
161
+ | `client.carriers` | USPS, UPS, FedEx, DHL direct endpoints |
162
+ | `client.webhooks` | Subscription CRUD, signature verification, delivery logs |
163
+ | `client.wallet` | Balance, transactions, auto-reload |
164
+ | `client.insurance` | Quotes, purchase, claims (first-party + U-PIC) |
165
+ | `client.returns` | RMA lifecycle: create, batch, QR codes, photo upload, cost recovery |
166
+ | `client.api_keys` | Key creation, rotation, revocation |
167
+ | `client.analytics` | Shipments, orders, carrier performance |
168
+ | `client.orders` | Order management |
169
+ | `client.inventory` | Warehouse inventory |
170
+ | `client.pickups` | Carrier pickup scheduling |
171
+ | `client.scan_forms` | USPS scan forms |
172
+ | `client.rules` | Shipping automation rules |
173
+ | `client.offsets` | Carbon offset purchases |
174
+ | `client.hs_codes` | HS code lookup for international customs |
175
+ | `client.recurring_shipments` | Scheduled recurring shipments |
176
+ | `client.email_templates` | Branded post-purchase email templates |
177
+ | `client.reports` | Report generation and scheduled delivery |
178
+
179
+ ## Configuration
180
+
181
+ ```ruby
182
+ client = FlexOps::Client.new(
183
+ base_url: "https://gateway.flexops.io", # API base URL
184
+ api_key: "fxk_live_...", # API key auth
185
+ workspace_id: "ws_abc123", # Default workspace
186
+ timeout: 30 # Request timeout (seconds)
187
+ )
188
+ ```
189
+
190
+ ## Requirements
191
+
192
+ - Ruby 3.0+
193
+
194
+ ## License
195
+
196
+ Proprietary — FlexOps, LLC
@@ -0,0 +1,53 @@
1
+ # ***********************************************************************
2
+ # Package : flexops
3
+ # Author : FlexOps, LLC
4
+ # Created : 2026-03-08
5
+ #
6
+ # Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
7
+ # ***********************************************************************
8
+
9
+ module FlexOps
10
+ class Client
11
+ attr_accessor :workspace_id
12
+ attr_reader :auth, :workspaces, :shipping, :carriers, :webhooks, :wallet,
13
+ :insurance, :returns, :api_keys, :analytics, :orders, :inventory,
14
+ :pickups, :scan_forms, :rules, :offsets, :hs_codes,
15
+ :recurring_shipments, :email_templates, :reports
16
+
17
+ def initialize(api_key: nil, access_token: nil, base_url: "https://gateway.flexops.io", workspace_id: nil, timeout: 30)
18
+ @http = HttpClient.new(base_url: base_url, api_key: api_key, access_token: access_token, timeout: timeout)
19
+ @workspace_id = workspace_id
20
+
21
+ ws_id_proc = -> { @workspace_id }
22
+
23
+ @auth = Resources::Auth.new(@http)
24
+ @workspaces = Resources::Workspaces.new(@http, ws_id_proc)
25
+ @shipping = Resources::Shipping.new(@http, ws_id_proc)
26
+ @carriers = Resources::Carriers.new(@http)
27
+ @webhooks = Resources::Webhooks.new(@http, ws_id_proc)
28
+ @wallet = Resources::Wallet.new(@http, ws_id_proc)
29
+ @insurance = Resources::Insurance.new(@http, ws_id_proc)
30
+ @returns = Resources::Returns.new(@http, ws_id_proc)
31
+ @api_keys = Resources::ApiKeys.new(@http, ws_id_proc)
32
+ @analytics = Resources::Analytics.new(@http)
33
+ @orders = Resources::Orders.new(@http)
34
+ @inventory = Resources::Inventory.new(@http)
35
+ @pickups = Resources::Pickups.new(@http, ws_id_proc)
36
+ @scan_forms = Resources::ScanForms.new(@http, ws_id_proc)
37
+ @rules = Resources::Rules.new(@http, ws_id_proc)
38
+ @offsets = Resources::Offsets.new(@http, ws_id_proc)
39
+ @hs_codes = Resources::HsCodes.new(@http, ws_id_proc)
40
+ @recurring_shipments = Resources::RecurringShipments.new(@http, ws_id_proc)
41
+ @email_templates = Resources::EmailTemplates.new(@http, ws_id_proc)
42
+ @reports = Resources::Reports.new(@http, ws_id_proc)
43
+ end
44
+
45
+ def set_access_token(token)
46
+ @http.set_access_token(token)
47
+ end
48
+
49
+ def set_api_key(key)
50
+ @http.set_api_key(key)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ # ***********************************************************************
2
+ # Package : flexops
3
+ # Author : FlexOps, LLC
4
+ # Created : 2026-03-08
5
+ #
6
+ # Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
7
+ # ***********************************************************************
8
+
9
+ module FlexOps
10
+ class Error < StandardError
11
+ attr_reader :status, :code, :errors
12
+
13
+ def initialize(message, status: 0, code: nil, errors: nil)
14
+ super(message)
15
+ @status = status
16
+ @code = code
17
+ @errors = errors
18
+ end
19
+ end
20
+
21
+ class AuthError < Error
22
+ def initialize(message = "Authentication required. Check your access token or API key.")
23
+ super(message, status: 401, code: "UNAUTHORIZED")
24
+ end
25
+ end
26
+
27
+ class RateLimitError < Error
28
+ attr_reader :retry_after
29
+
30
+ def initialize(retry_after: 0)
31
+ super("Rate limited. Retry after #{retry_after}s", status: 429, code: "RATE_LIMITED")
32
+ @retry_after = retry_after
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,173 @@
1
+ # ***********************************************************************
2
+ # Package : flexops
3
+ # Author : FlexOps, LLC
4
+ # Created : 2026-03-08
5
+ #
6
+ # Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
7
+ # ***********************************************************************
8
+
9
+ require "net/http"
10
+ require "uri"
11
+ require "json"
12
+
13
+ module FlexOps
14
+ class HttpClient
15
+ DEFAULT_BASE_URL = "https://gateway.flexops.io"
16
+ DEFAULT_TIMEOUT = 30
17
+ MAX_RETRIES = 3
18
+ RETRYABLE_STATUSES = [429, 500, 502, 503, 504].freeze
19
+
20
+ def initialize(base_url: DEFAULT_BASE_URL, api_key: nil, access_token: nil, timeout: DEFAULT_TIMEOUT)
21
+ @base_url = base_url.chomp("/")
22
+ @api_key = api_key
23
+ @access_token = access_token
24
+ @timeout = timeout
25
+ end
26
+
27
+ def set_access_token(token)
28
+ @access_token = token
29
+ @api_key = nil
30
+ end
31
+
32
+ def set_api_key(key)
33
+ @api_key = key
34
+ @access_token = nil
35
+ end
36
+
37
+ def get(path, query: nil)
38
+ request(:get, path, query: query)
39
+ end
40
+
41
+ def post(path, body: nil, query: nil)
42
+ request(:post, path, body: body, query: query)
43
+ end
44
+
45
+ def put(path, body: nil)
46
+ request(:put, path, body: body)
47
+ end
48
+
49
+ def patch(path, body: nil)
50
+ request(:patch, path, body: body)
51
+ end
52
+
53
+ def delete(path)
54
+ request(:delete, path)
55
+ end
56
+
57
+ private
58
+
59
+ def request(method, path, body: nil, query: nil)
60
+ uri = build_uri(path, query)
61
+ last_error = nil
62
+
63
+ (0..MAX_RETRIES).each do |attempt|
64
+ if attempt > 0
65
+ sleep(calculate_backoff(attempt))
66
+ end
67
+
68
+ req = build_request(method, uri, body)
69
+ begin
70
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https",
71
+ open_timeout: @timeout, read_timeout: @timeout) do |http|
72
+ http.request(req)
73
+ end
74
+
75
+ status = response.code.to_i
76
+
77
+ if status >= 200 && status < 300
78
+ return parse_response(response)
79
+ end
80
+
81
+ if status == 401
82
+ raise AuthError
83
+ end
84
+
85
+ if status == 403
86
+ raise Error.new("Access denied. Check your plan tier and feature entitlements.", status: 403, code: "FORBIDDEN")
87
+ end
88
+
89
+ if status == 429
90
+ retry_after = (response["retry-after"] || "0").to_i
91
+ last_error = RateLimitError.new(retry_after: retry_after)
92
+ next if RETRYABLE_STATUSES.include?(429)
93
+ raise last_error
94
+ end
95
+
96
+ error_body = parse_error_body(response)
97
+ error = Error.new(
98
+ error_body["message"] || "HTTP #{status}: #{response.message}",
99
+ status: status,
100
+ errors: error_body["errors"]
101
+ )
102
+
103
+ if RETRYABLE_STATUSES.include?(status)
104
+ last_error = error
105
+ next
106
+ end
107
+
108
+ raise error
109
+ rescue Error
110
+ raise
111
+ rescue StandardError => e
112
+ last_error = e
113
+ next if attempt < MAX_RETRIES
114
+ end
115
+ end
116
+
117
+ raise last_error || Error.new("Request failed after retries", code: "RETRY_EXHAUSTED")
118
+ end
119
+
120
+ def build_uri(path, query)
121
+ url = "#{@base_url}#{path.start_with?("/") ? path : "/#{path}"}"
122
+ uri = URI.parse(url)
123
+ if query
124
+ params = URI.encode_www_form(query.compact)
125
+ uri.query = params unless params.empty?
126
+ end
127
+ uri
128
+ end
129
+
130
+ def build_request(method, uri, body)
131
+ klass = case method
132
+ when :get then Net::HTTP::Get
133
+ when :post then Net::HTTP::Post
134
+ when :put then Net::HTTP::Put
135
+ when :patch then Net::HTTP::Patch
136
+ when :delete then Net::HTTP::Delete
137
+ end
138
+
139
+ req = klass.new(uri)
140
+ req["Content-Type"] = "application/json"
141
+ req["Accept"] = "application/json"
142
+
143
+ if @api_key
144
+ req["X-Api-Key"] = @api_key
145
+ elsif @access_token
146
+ req["Authorization"] = "Bearer #{@access_token}"
147
+ end
148
+
149
+ req.body = body.to_json if body
150
+ req
151
+ end
152
+
153
+ def parse_response(response)
154
+ content_type = response["content-type"] || ""
155
+ if content_type.include?("application/json")
156
+ JSON.parse(response.body)
157
+ else
158
+ response.body
159
+ end
160
+ end
161
+
162
+ def parse_error_body(response)
163
+ JSON.parse(response.body)
164
+ rescue StandardError
165
+ {}
166
+ end
167
+
168
+ def calculate_backoff(attempt)
169
+ jitter = rand(0.85..1.15)
170
+ [1.0 * (2**(attempt - 1)) * jitter, 30.0].min
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,85 @@
1
+ # ***********************************************************************
2
+ # Package : flexops
3
+ # Author : FlexOps, LLC
4
+ # Created : 2026-03-08
5
+ #
6
+ # Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
7
+ # ***********************************************************************
8
+
9
+ module FlexOps
10
+ module Resources
11
+ class Analytics
12
+ BASE = "/api/ApiProxy/api/v4/Analytics"
13
+
14
+ def initialize(http)
15
+ @http = http
16
+ end
17
+
18
+ def shipments_trend(start_date: nil, end_date: nil)
19
+ @http.get("#{BASE}/ShipmentsTrend", query: date_query(start_date, end_date))
20
+ end
21
+
22
+ def carrier_summary(start_date: nil, end_date: nil)
23
+ @http.get("#{BASE}/CarrierSummary", query: date_query(start_date, end_date))
24
+ end
25
+
26
+ def top_destinations(start_date: nil, end_date: nil, limit: nil)
27
+ @http.get("#{BASE}/TopDestinations", query: date_query(start_date, end_date).merge(limit: limit).compact)
28
+ end
29
+
30
+ def inventory_metrics
31
+ @http.get("#{BASE}/InventoryMetrics")
32
+ end
33
+
34
+ def stock_by_warehouse
35
+ @http.get("#{BASE}/StockByWarehouse")
36
+ end
37
+
38
+ def order_metrics(start_date: nil, end_date: nil)
39
+ @http.get("#{BASE}/OrderMetrics", query: date_query(start_date, end_date))
40
+ end
41
+
42
+ def order_trend(start_date: nil, end_date: nil)
43
+ @http.get("#{BASE}/OrderTrend", query: date_query(start_date, end_date))
44
+ end
45
+
46
+ def top_selling_products(start_date: nil, end_date: nil, limit: nil)
47
+ @http.get("#{BASE}/TopSellingProducts", query: date_query(start_date, end_date).merge(limit: limit).compact)
48
+ end
49
+
50
+ def returns_metrics(start_date: nil, end_date: nil)
51
+ @http.get("#{BASE}/ReturnsMetrics", query: date_query(start_date, end_date))
52
+ end
53
+
54
+ def returns_trend(start_date: nil, end_date: nil)
55
+ @http.get("#{BASE}/ReturnsTrend", query: date_query(start_date, end_date))
56
+ end
57
+
58
+ def return_reasons(start_date: nil, end_date: nil)
59
+ @http.get("#{BASE}/ReturnReasons", query: date_query(start_date, end_date))
60
+ end
61
+
62
+ def performance_metrics(start_date: nil, end_date: nil)
63
+ @http.get("#{BASE}/PerformanceMetrics", query: date_query(start_date, end_date))
64
+ end
65
+
66
+ def carrier_performance(start_date: nil, end_date: nil)
67
+ @http.get("#{BASE}/CarrierPerformance", query: date_query(start_date, end_date))
68
+ end
69
+
70
+ def shipping_cost_analytics(start_date: nil, end_date: nil)
71
+ @http.get("#{BASE}/ShippingCostAnalytics", query: date_query(start_date, end_date))
72
+ end
73
+
74
+ def delivery_performance(start_date: nil, end_date: nil)
75
+ @http.get("#{BASE}/DeliveryPerformance", query: date_query(start_date, end_date))
76
+ end
77
+
78
+ private
79
+
80
+ def date_query(start_date, end_date)
81
+ { startDate: start_date, endDate: end_date }.compact
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,40 @@
1
+ # ***********************************************************************
2
+ # Package : flexops
3
+ # Author : FlexOps, LLC
4
+ # Created : 2026-03-08
5
+ #
6
+ # Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
7
+ # ***********************************************************************
8
+
9
+ module FlexOps
10
+ module Resources
11
+ class ApiKeys
12
+ def initialize(http, ws_id_proc)
13
+ @http = http
14
+ @ws_id = ws_id_proc
15
+ end
16
+
17
+ def list
18
+ @http.get("#{ws_path}/api-keys")
19
+ end
20
+
21
+ def create(request)
22
+ @http.post("#{ws_path}/api-keys", body: request)
23
+ end
24
+
25
+ def revoke(key_id)
26
+ @http.delete("#{ws_path}/api-keys/#{key_id}")
27
+ end
28
+
29
+ def rotate(key_id)
30
+ @http.post("#{ws_path}/api-keys/#{key_id}/rotate")
31
+ end
32
+
33
+ private
34
+
35
+ def ws_path
36
+ "/api/workspaces/#{@ws_id.call}"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,57 @@
1
+ # ***********************************************************************
2
+ # Package : flexops
3
+ # Author : FlexOps, LLC
4
+ # Created : 2026-03-08
5
+ #
6
+ # Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
7
+ # ***********************************************************************
8
+
9
+ module FlexOps
10
+ module Resources
11
+ class Auth
12
+ def initialize(http)
13
+ @http = http
14
+ end
15
+
16
+ def login(email:, password:)
17
+ @http.post("/api/Account/login", body: { email: email, password: password })
18
+ end
19
+
20
+ def register(request)
21
+ @http.post("/api/Account/register", body: request)
22
+ end
23
+
24
+ def refresh_token(refresh_token)
25
+ @http.post("/api/Account/refresh-token", body: { refreshToken: refresh_token })
26
+ end
27
+
28
+ def logout
29
+ @http.post("/api/Account/logout")
30
+ end
31
+
32
+ def get_profile
33
+ @http.get("/api/Account/profile")
34
+ end
35
+
36
+ def update_profile(data)
37
+ @http.put("/api/Account/profile", body: data)
38
+ end
39
+
40
+ def change_password(current_password:, new_password:)
41
+ @http.post("/api/Account/change-password", body: { currentPassword: current_password, newPassword: new_password })
42
+ end
43
+
44
+ def forgot_password(email:)
45
+ @http.post("/api/Account/forgot-password", body: { email: email })
46
+ end
47
+
48
+ def reset_password(token:, new_password:)
49
+ @http.post("/api/Account/reset-password", body: { token: token, newPassword: new_password })
50
+ end
51
+
52
+ def verify_email(token:)
53
+ @http.post("/api/Account/verify-email", body: { token: token })
54
+ end
55
+ end
56
+ end
57
+ end