flexops 2.0.0 → 2.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 +4 -4
- data/README.md +39 -15
- data/lib/flexops/errors.rb +5 -2
- data/lib/flexops/http.rb +2 -1
- data/lib/flexops/resources/returns.rb +17 -0
- data/lib/flexops/resources/shipping.rb +25 -0
- data/lib/flexops/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0176ab2f4e6ee84a35449cc7c3cc2b0d13708b62fdef7552013db7cce25bff08
|
|
4
|
+
data.tar.gz: d6c47bc34a6f23aa44bb799c4a1165c87eab2a592439a8e8a10bd39774f5e6f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cf5f9b203dbf64a94243192fd7db18fc131275b0da8ceff6a5165bc88fb64b85e26efb0d71062e030a1acf45cec9e5a3717da17833636ab6685ddc010dcb6b62
|
|
7
|
+
data.tar.gz: 570d832da16f3449283d3d064197efe2eeba19e6fad36183179490564829a1217f488038bd81970357a67bfef685be2d3dd06b1a3ced0e6eb0ac1272cc48a752
|
data/README.md
CHANGED
|
@@ -29,8 +29,8 @@ require "flexops"
|
|
|
29
29
|
|
|
30
30
|
# API key authentication (recommended for server-to-server)
|
|
31
31
|
client = FlexOps::Client.new(
|
|
32
|
-
api_key: "
|
|
33
|
-
workspace_id: "
|
|
32
|
+
api_key: "live_...",
|
|
33
|
+
workspace_id: "your-workspace-id"
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
# Get shipping rates from all carriers
|
|
@@ -90,7 +90,7 @@ separate contracts. The SDK never automatically confirms a preview.
|
|
|
90
90
|
### API key (recommended)
|
|
91
91
|
|
|
92
92
|
```ruby
|
|
93
|
-
client = FlexOps::Client.new(api_key: "
|
|
93
|
+
client = FlexOps::Client.new(api_key: "live_...", workspace_id: "your-workspace-id")
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
### Email / password
|
|
@@ -99,15 +99,15 @@ client = FlexOps::Client.new(api_key: "fxk_live_...", workspace_id: "ws_abc123")
|
|
|
99
99
|
client = FlexOps::Client.new(base_url: "https://gateway.flexops.io")
|
|
100
100
|
session = client.auth.login(email: "user@example.com", password: "password")
|
|
101
101
|
client.set_access_token(session["data"]["token"])
|
|
102
|
-
client.workspace_id = "
|
|
102
|
+
client.workspace_id = "your-workspace-id"
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
## Sandbox / test keys
|
|
106
106
|
|
|
107
|
-
Use `
|
|
107
|
+
Use `test_...` (instead of `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.
|
|
108
108
|
|
|
109
109
|
```ruby
|
|
110
|
-
client = FlexOps::Client.new(api_key: "
|
|
110
|
+
client = FlexOps::Client.new(api_key: "test_...", workspace_id: "your-workspace-id")
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
## Carrier-specific calls
|
|
@@ -209,6 +209,30 @@ client.email_templates.upsert(eventType: 3, subjectTemplate: "Delivered: {{track
|
|
|
209
209
|
client.email_templates.preview(subject_template: "Hi {{recipient_name}}", html_body_template: "<p>{{status}}</p>")
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
+
## Multi-package labels
|
|
213
|
+
|
|
214
|
+
One order shipped in several boxes with the same carrier and service; each box gets its own label and
|
|
215
|
+
a box that fails does not stop the others. Multi-package labels cannot carry HAZMAT: send
|
|
216
|
+
`FlexOps::HazmatClassification::NONE` or leave `hazmatClassification` out.
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
begin
|
|
220
|
+
shipment = client.shipping.create_multi_package(
|
|
221
|
+
{ orderId: 12345, carrierCode: "USPS", serviceCode: "PRIORITY",
|
|
222
|
+
hazmatClassification: FlexOps::HazmatClassification::NONE,
|
|
223
|
+
packages: [{ weightInOunces: 16, lengthInInches: 10, widthInInches: 8, heightInInches: 4, packageReference: "Box 1 of 2" },
|
|
224
|
+
{ weightInOunces: 8, lengthInInches: 6, widthInInches: 6, heightInInches: 6, packageReference: "Box 2 of 2" }] },
|
|
225
|
+
idempotency_key: "order-12345-boxes" # optional; reuse it when you retry
|
|
226
|
+
)
|
|
227
|
+
rescue FlexOps::Error => e
|
|
228
|
+
# No label was bought: 422 AllPackagesFailed, or 409 OutcomeUnknown when a label may exist
|
|
229
|
+
# (check the order before buying again). e.data holds the per-box results.
|
|
230
|
+
end
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`client.returns.get` reads a received return by its numeric return id; read an RMA with
|
|
234
|
+
`client.returns.get_rma("RMA-1001")` or page through them with `client.returns.list_rmas`.
|
|
235
|
+
|
|
212
236
|
## Wallet balance
|
|
213
237
|
|
|
214
238
|
```ruby
|
|
@@ -287,7 +311,7 @@ Every SDK method is a thin wrapper around the FlexOps REST API. If you want to v
|
|
|
287
311
|
```bash
|
|
288
312
|
# Shop rates across all connected carriers
|
|
289
313
|
curl -X POST https://gateway.flexops.io/api/shipping/rates \
|
|
290
|
-
-H "X-API-Key:
|
|
314
|
+
-H "X-API-Key: live_..." \
|
|
291
315
|
-H "Content-Type: application/json" \
|
|
292
316
|
-d '{
|
|
293
317
|
"origin": {"addressLine1": "123 Main St", "city": "New York", "stateProvince": "NY", "postalCode": "10001", "countryCode": "US"},
|
|
@@ -298,7 +322,7 @@ curl -X POST https://gateway.flexops.io/api/shipping/rates \
|
|
|
298
322
|
# Preview a live label; this does not purchase it.
|
|
299
323
|
# hazmatClassification 0 = not hazardous; required when there is no orderId.
|
|
300
324
|
curl -X POST https://gateway.flexops.io/api/shipping/labels \
|
|
301
|
-
-H "X-API-Key:
|
|
325
|
+
-H "X-API-Key: live_..." \
|
|
302
326
|
-H "Content-Type: application/json" \
|
|
303
327
|
-d '{
|
|
304
328
|
"carrierCode": "USPS", "serviceCode": "GROUND_ADVANTAGE",
|
|
@@ -315,14 +339,14 @@ curl -X POST https://gateway.flexops.io/api/shipping/labels \
|
|
|
315
339
|
|
|
316
340
|
# Track a shipment
|
|
317
341
|
curl https://gateway.flexops.io/api/shipping/track/9400111899223456789012 \
|
|
318
|
-
-H "X-API-Key:
|
|
342
|
+
-H "X-API-Key: live_..."
|
|
319
343
|
|
|
320
344
|
# Cancel a label
|
|
321
345
|
curl -X DELETE "https://gateway.flexops.io/api/shipping/labels/lbl_123?carrierCode=USPS" \
|
|
322
|
-
-H "X-API-Key:
|
|
346
|
+
-H "X-API-Key: live_..."
|
|
323
347
|
```
|
|
324
348
|
|
|
325
|
-
Use
|
|
349
|
+
Use a `test_...` key instead of `live_...` to hit the sandbox environment; mock carriers respond, no real charges, no real labels.
|
|
326
350
|
|
|
327
351
|
## Resources
|
|
328
352
|
|
|
@@ -330,12 +354,12 @@ Use an `fxk_test_...` key instead of `fxk_live_...` to hit the sandbox environme
|
|
|
330
354
|
|----------|-------------|
|
|
331
355
|
| `client.auth` | Login, token refresh, logout |
|
|
332
356
|
| `client.workspaces` | List and read workspaces; list, invite and remove members, change roles |
|
|
333
|
-
| `client.shipping` | Rate shopping, labels, tracking, address validation, batch, cancel |
|
|
357
|
+
| `client.shipping` | Rate shopping, labels, multi-package labels, tracking, address validation, batch, cancel |
|
|
334
358
|
| `client.carriers` | USPS, UPS, FedEx and DHL calls through the legacy proxy; writes are deprecated and need a user token |
|
|
335
359
|
| `client.webhooks` | Subscription create/update/delete, secret rotation, test events, delivery logs, signature verification |
|
|
336
360
|
| `client.wallet` | Workspace balance |
|
|
337
361
|
| `client.insurance` | Providers, quotes, policies, void, claims |
|
|
338
|
-
| `client.returns` | List and read returns; create RMAs
|
|
362
|
+
| `client.returns` | List and read received returns; create, read and list RMAs; return labels; receive |
|
|
339
363
|
| `client.analytics` | Shipments, orders, carrier performance (legacy proxy reads) |
|
|
340
364
|
| `client.orders` | Order reads (legacy proxy) |
|
|
341
365
|
| `client.inventory` | Inventory reads (legacy proxy) |
|
|
@@ -355,8 +379,8 @@ API keys, workspace settings and account management are managed in the FlexOps p
|
|
|
355
379
|
```ruby
|
|
356
380
|
client = FlexOps::Client.new(
|
|
357
381
|
base_url: "https://gateway.flexops.io", # API base URL
|
|
358
|
-
api_key: "
|
|
359
|
-
workspace_id: "
|
|
382
|
+
api_key: "live_...", # API key auth
|
|
383
|
+
workspace_id: "your-workspace-id", # Default workspace
|
|
360
384
|
timeout: 30 # Request timeout (seconds)
|
|
361
385
|
)
|
|
362
386
|
```
|
data/lib/flexops/errors.rb
CHANGED
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
module FlexOps
|
|
10
10
|
class Error < StandardError
|
|
11
|
-
|
|
11
|
+
# data is the envelope's "data" on an error reply, when the Gateway sends one. A
|
|
12
|
+
# multi-package 422 AllPackagesFailed or 409 OutcomeUnknown carries the per-box results there.
|
|
13
|
+
attr_reader :status, :code, :errors, :data
|
|
12
14
|
|
|
13
|
-
def initialize(message, status: 0, code: nil, errors: nil)
|
|
15
|
+
def initialize(message, status: 0, code: nil, errors: nil, data: nil)
|
|
14
16
|
super(message)
|
|
15
17
|
@status = status
|
|
16
18
|
@code = code
|
|
17
19
|
@errors = errors
|
|
20
|
+
@data = data
|
|
18
21
|
end
|
|
19
22
|
end
|
|
20
23
|
|
data/lib/flexops/http.rb
CHANGED
|
@@ -117,7 +117,8 @@ module FlexOps
|
|
|
117
117
|
error_body["message"] || "HTTP #{status}: #{response.message}",
|
|
118
118
|
status: status,
|
|
119
119
|
code: error_body["errorCode"] || error_body["code"] || (status == 403 ? "FORBIDDEN" : nil),
|
|
120
|
-
errors: error_body["errors"]
|
|
120
|
+
errors: error_body["errors"],
|
|
121
|
+
data: error_body["data"]
|
|
121
122
|
)
|
|
122
123
|
|
|
123
124
|
raise error unless retryable?(method, status, error.code)
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
|
|
7
7
|
# ***********************************************************************
|
|
8
8
|
|
|
9
|
+
require "erb"
|
|
10
|
+
|
|
9
11
|
module FlexOps
|
|
10
12
|
module Resources
|
|
11
13
|
class Returns
|
|
@@ -14,15 +16,30 @@ module FlexOps
|
|
|
14
16
|
@ws_id = ws_id_proc
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# Lists received returns (start_date and end_date are ISO 8601; the Gateway defaults to the
|
|
20
|
+
# last 30 days). To list return authorizations, use list_rmas.
|
|
17
21
|
def list(page: nil, page_size: nil, start_date: nil, end_date: nil)
|
|
18
22
|
query = { page: page, pageSize: page_size, startDate: start_date, endDate: end_date }.compact
|
|
19
23
|
@http.get("#{ws_path}/returns", query: query.empty? ? nil : query)
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
# Reads a received return by its numeric return id ("returnId" on a listed return or an RMA).
|
|
27
|
+
# An RMA number such as "RMA-1001" is not a return id and matches no Gateway route; use get_rma.
|
|
22
28
|
def get(return_id)
|
|
23
29
|
@http.get("#{ws_path}/returns/#{return_id}")
|
|
24
30
|
end
|
|
25
31
|
|
|
32
|
+
# Lists return authorizations (RMAs), one page at a time.
|
|
33
|
+
def list_rmas(page: nil, page_size: nil)
|
|
34
|
+
query = { page: page, pageSize: page_size }.compact
|
|
35
|
+
@http.get("#{ws_path}/returns/rma", query: query.empty? ? nil : query)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Reads a return authorization by its RMA number, for example "RMA-1001".
|
|
39
|
+
def get_rma(rma_number)
|
|
40
|
+
@http.get("#{ws_path}/returns/rma/#{ERB::Util.url_encode(rma_number.to_s)}")
|
|
41
|
+
end
|
|
42
|
+
|
|
26
43
|
# Creates an RMA and returns the enveloped RMA (201).
|
|
27
44
|
#
|
|
28
45
|
# request:: { orderId: (a number), orderNumber: (optional), reason:,
|
|
@@ -60,6 +60,31 @@ module FlexOps
|
|
|
60
60
|
@http.post("/api/addresses/validate", body: to_validation_address(address))
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
# Buys labels for one order shipped in 1 to 99 boxes with the same carrier and service
|
|
64
|
+
# (POST /api/shipping/multi-package, the Gateway MultiPackageShipmentRequest):
|
|
65
|
+
#
|
|
66
|
+
# { orderId: 12345, carrierCode: "USPS", serviceCode: (optional), labelFormat: (optional),
|
|
67
|
+
# shipDate: (optional), hazmatClassification: (optional),
|
|
68
|
+
# packages: [{ weightInOunces:, lengthInInches:, widthInInches:, heightInInches:,
|
|
69
|
+
# deliveryConfirmationRequired: (optional), signatureConfirmationRequired: (optional),
|
|
70
|
+
# insuranceCoverageRequested: (optional), insuredValue: (optional),
|
|
71
|
+
# packageReference: (optional) }] }
|
|
72
|
+
#
|
|
73
|
+
# Weight is in ounces and every dimension in inches; all four are required. Multi-package
|
|
74
|
+
# labels cannot carry HAZMAT yet: send HazmatClassification::NONE or leave hazmatClassification
|
|
75
|
+
# out (any other class is refused with 400 HazmatNotSupported).
|
|
76
|
+
#
|
|
77
|
+
# Returns the envelope when at least one label was bought (check data["failedPackages"] and
|
|
78
|
+
# each box in data["labels"]). When none was, the Gateway answers 422 AllPackagesFailed, or 409
|
|
79
|
+
# OutcomeUnknown when a box's carrier outcome is unknown and a label may exist: both raise
|
|
80
|
+
# FlexOps::Error whose data holds the per-box results. After OutcomeUnknown, check the order
|
|
81
|
+
# before buying again. Pass your own idempotency_key (and reuse it) to make a retry you run
|
|
82
|
+
# yourself safe.
|
|
83
|
+
def create_multi_package(request, idempotency_key: nil)
|
|
84
|
+
@http.post("/api/shipping/multi-package", body: request,
|
|
85
|
+
headers: idempotency_key.nil? ? {} : { "Idempotency-Key" => idempotency_key })
|
|
86
|
+
end
|
|
87
|
+
|
|
63
88
|
# The payload is sent as given. Set hazmatClassification on an item only when you have a declaration.
|
|
64
89
|
def create_batch(request)
|
|
65
90
|
@http.post("#{ws_path}/labels/batch", body: request)
|
data/lib/flexops/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flexops
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- FlexOps, LLC
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|