flexops 1.1.0 → 2.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 +4 -4
- data/README.md +189 -45
- data/lib/flexops/client.rb +6 -7
- data/lib/flexops/errors.rb +9 -0
- data/lib/flexops/hazmat_classification.rb +34 -0
- data/lib/flexops/http.rb +75 -44
- data/lib/flexops/resources/auth.rb +8 -29
- data/lib/flexops/resources/carbon_offsets.rb +60 -0
- data/lib/flexops/resources/carriers.rb +95 -8
- data/lib/flexops/resources/email_templates.rb +27 -17
- data/lib/flexops/resources/hs_codes.rb +10 -11
- data/lib/flexops/resources/insurance.rb +19 -9
- data/lib/flexops/resources/inventory.rb +0 -8
- data/lib/flexops/resources/orders.rb +0 -8
- data/lib/flexops/resources/pickups.rb +14 -5
- data/lib/flexops/resources/recurring_shipments.rb +30 -15
- data/lib/flexops/resources/report_schedules.rb +63 -0
- data/lib/flexops/resources/returns.rb +37 -23
- data/lib/flexops/resources/rules.rb +18 -2
- data/lib/flexops/resources/shipping.rb +47 -9
- data/lib/flexops/resources/wallet.rb +4 -20
- data/lib/flexops/resources/webhooks.rb +132 -9
- data/lib/flexops/resources/workspaces.rb +8 -13
- data/lib/flexops/version.rb +1 -1
- data/lib/flexops/webhook_event_type.rb +25 -0
- data/lib/flexops/workspace_role.rb +32 -0
- data/lib/flexops.rb +5 -3
- metadata +7 -5
- data/lib/flexops/resources/api_keys.rb +0 -40
- data/lib/flexops/resources/offsets.rb +0 -38
- data/lib/flexops/resources/reports.rb +0 -46
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# ***********************************************************************
|
|
2
|
+
# Package : flexops
|
|
3
|
+
# Author : FlexOps, LLC
|
|
4
|
+
# Created : 2026-10-19
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
|
|
7
|
+
# ***********************************************************************
|
|
8
|
+
|
|
9
|
+
module FlexOps
|
|
10
|
+
module Resources
|
|
11
|
+
# Carbon offsets for shipments. Answers use the { success, data, message, errorCode } envelope.
|
|
12
|
+
class CarbonOffsets
|
|
13
|
+
def initialize(http, ws_id_proc)
|
|
14
|
+
@http = http
|
|
15
|
+
@ws_id = ws_id_proc
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def quote(weight_lbs:, distance_miles:)
|
|
19
|
+
@http.post("#{ws_path}/quote", body: { weightLbs: weight_lbs, distanceMiles: distance_miles })
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Buys an offset for a shipment and debits the workspace wallet.
|
|
23
|
+
# Pass an idempotency_key you store with the purchase to make your own retry safe: after an
|
|
24
|
+
# error or a timeout, retry with the same key and the Gateway returns the first result instead
|
|
25
|
+
# of buying again. Without one, the SDK's own retries still share a key, but a new call gets a new key.
|
|
26
|
+
def purchase(tracking_number:, weight_lbs:, distance_miles:, idempotency_key: nil)
|
|
27
|
+
@http.post("#{ws_path}/purchase",
|
|
28
|
+
body: { trackingNumber: tracking_number, weightLbs: weight_lbs, distanceMiles: distance_miles },
|
|
29
|
+
headers: idempotency_key.nil? ? {} : { "Idempotency-Key" => idempotency_key })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def list(page: nil, page_size: nil)
|
|
33
|
+
query = { page: page, pageSize: page_size }.compact
|
|
34
|
+
@http.get(ws_path, query: query.empty? ? nil : query)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def get(offset_id)
|
|
38
|
+
@http.get("#{ws_path}/#{offset_id}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def summary
|
|
42
|
+
@http.get("#{ws_path}/summary")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def get_settings
|
|
46
|
+
@http.get("#{ws_path}/settings")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def update_settings(auto_offset_enabled:)
|
|
50
|
+
@http.put("#{ws_path}/settings", body: { autoOffsetEnabled: auto_offset_enabled })
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def ws_path
|
|
56
|
+
"/api/workspaces/#{@ws_id.call}/carbon-offsets"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
module FlexOps
|
|
10
10
|
module Resources
|
|
11
|
+
# Carrier-specific calls through the legacy /api/ApiProxy passthrough (sunset 2027-06-30).
|
|
12
|
+
# Lookups (GET) work with an API key or a user token. The writes (POST/PUT/DELETE) are
|
|
13
|
+
# deprecated and need a user token (auth.login): the Gateway refuses API-key writes on the
|
|
14
|
+
# proxy with 403 API_KEY_SCOPE_REQUIRED. Label generation through the proxy is closed (410 Gone);
|
|
15
|
+
# buy, cancel and schedule through client.shipping, client.pickups and client.scan_forms.
|
|
11
16
|
class Carriers
|
|
12
17
|
attr_reader :usps, :ups, :fedex, :dhl
|
|
13
18
|
|
|
@@ -30,20 +35,37 @@ module FlexOps
|
|
|
30
35
|
def validate_address(params) = @http.get("#{PROXY}/api/v3/AddressValidation/getUspsValidateAndCorrectAddress", query: params)
|
|
31
36
|
def city_state_lookup(zip_code) = @http.get("#{PROXY}/api/v3/AddressValidation/getUspsCityStateLookupByZipCode", query: { zipCode: zip_code })
|
|
32
37
|
def zip_code_lookup(params) = @http.get("#{PROXY}/api/v3/AddressValidation/getUspsZipCodeLookupByAddress", query: params)
|
|
38
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
39
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
33
40
|
def get_domestic_rates(body) = @http.post("#{PROXY}/api/v3/RateCalculator/postUspsSearchDomesticBaseRates", body: body)
|
|
41
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
42
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
34
43
|
def get_domestic_products(body) = @http.post("#{PROXY}/api/v3/RateCalculator/postUspsSearchEligibleDomesticProducts", body: body)
|
|
44
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
45
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
35
46
|
def get_domestic_prices(body) = @http.post("#{PROXY}/api/v3/RateCalculator/postUspsSearchEligibleDomesticPrices", body: body)
|
|
47
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
48
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
36
49
|
def get_international_rates(body) = @http.post("#{PROXY}/api/v3/RateCalculator/postUspsSearchInternationalBaseRates", body: body)
|
|
50
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
51
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
37
52
|
def get_international_prices(body) = @http.post("#{PROXY}/api/v3/RateCalculator/postUspsSearchEligibleInternationalPrices", body: body)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def create_international_label(body) = @http.post("#{PROXY}/api/v3/Shipping/postUspsGenerateInternationalShippingLabel", body: body)
|
|
53
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
54
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
41
55
|
def cancel_domestic_label = @http.delete("#{PROXY}/api/v3/Shipping/cancelUspsDomesticShipmentLabel")
|
|
56
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
57
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
42
58
|
def cancel_international_label = @http.delete("#{PROXY}/api/v3/Shipping/cancelUspsInternationalShipmentLabel")
|
|
43
59
|
def track_summary(params) = @http.get("#{PROXY}/api/v3/Tracking/getUspsTrackingSummaryInformation", query: params)
|
|
44
60
|
def track_detail(params) = @http.get("#{PROXY}/api/v3/Tracking/getUspsTrackingDetailInformation", query: params)
|
|
61
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
62
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
45
63
|
def create_pickup(body) = @http.post("#{PROXY}/api/v3/CarrierPickup/postUspsCreateCarrierPickupSchedule", body: body)
|
|
64
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
65
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
46
66
|
def cancel_pickup = @http.delete("#{PROXY}/api/v3/CarrierPickup/cancelUspsCarrierPickupSchedule")
|
|
67
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
68
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
47
69
|
def create_scan_form(body) = @http.post("#{PROXY}/api/v3/ScanForm/postUspsCreateScanFormLabelShipment", body: body)
|
|
48
70
|
def delivery_standards(params) = @http.get("#{PROXY}/api/v3/ServiceStandards/getUspsGetDeliveryStandardsEstimates", query: params)
|
|
49
71
|
def find_drop_off_locations(params) = @http.get("#{PROXY}/api/v3/LocationSearch/getUspsFindValidDropOffLocations", query: params)
|
|
@@ -57,17 +79,36 @@ module FlexOps
|
|
|
57
79
|
@http = http
|
|
58
80
|
end
|
|
59
81
|
|
|
82
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
83
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
60
84
|
def validate_address(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsVerifyAddress", body: body)
|
|
85
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
86
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
61
87
|
def get_rates(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsRateCheck", body: body)
|
|
62
|
-
def create_label(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/generateNewUpsShipLabel", body: body)
|
|
63
88
|
def track(params) = @http.get("#{PROXY}/api/v2/ShippingLabel/getSingleUpsTrackingDetail", query: params)
|
|
89
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
90
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
64
91
|
def create_pickup(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsCreatePickup", body: body)
|
|
92
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
93
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
65
94
|
def cancel_pickup = @http.delete("#{PROXY}/api/v2/ShippingLabel/deleteUpsPickup")
|
|
95
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
96
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
66
97
|
def get_transit_times(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsGetTransitTimes", body: body)
|
|
98
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
99
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
67
100
|
def get_landed_cost(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsGetLandedCostQuote", body: body)
|
|
101
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
102
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
68
103
|
def search_locations(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsSearchLocations", body: body)
|
|
104
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
105
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
69
106
|
def upload_document(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsUploadPaperlessDocument", body: body)
|
|
107
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
108
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
70
109
|
def create_freight_shipment(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsCreateFreightShipment", body: body)
|
|
110
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
111
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
71
112
|
def get_freight_rate(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postUpsGetFreightRate", body: body)
|
|
72
113
|
end
|
|
73
114
|
|
|
@@ -78,26 +119,62 @@ module FlexOps
|
|
|
78
119
|
@http = http
|
|
79
120
|
end
|
|
80
121
|
|
|
122
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
123
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
81
124
|
def validate_address(body) = @http.post("#{PROXY}/api/v3/AddressValidation/postFedExValidateAndCorrectDomesticAddress", body: body)
|
|
125
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
126
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
82
127
|
def validate_postal_code(body) = @http.post("#{PROXY}/api/v3/AddressValidation/postFedExValidatePostalCode", body: body)
|
|
128
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
129
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
83
130
|
def get_rates(body) = @http.post("#{PROXY}/api/v3/RateCalculator/postRetrieveFedExRateAndTransitTimesAsync", body: body)
|
|
84
|
-
|
|
131
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
132
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
85
133
|
def cancel_shipment(body) = @http.put("#{PROXY}/api/v3/Shipping/putFedExCancelShipment", body: body)
|
|
134
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
135
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
86
136
|
def validate_shipment(body) = @http.post("#{PROXY}/api/v3/Shipping/postFedExValidateShipment", body: body)
|
|
87
|
-
|
|
137
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
138
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
88
139
|
def track(body) = @http.post("#{PROXY}/api/v3/Tracking/postFedExRetrieveTrackingInfoByTrackingNumber", body: body)
|
|
140
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
141
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
89
142
|
def track_multi_piece(body) = @http.post("#{PROXY}/api/v3/Tracking/postFedExRetrieveTrackingInfoForMultiPieceShipment", body: body)
|
|
143
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
144
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
90
145
|
def register_tracking_notification(body) = @http.post("#{PROXY}/api/v3/Tracking/postFedExRegisterForTrackingNotification", body: body)
|
|
146
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
147
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
91
148
|
def create_pickup(body) = @http.post("#{PROXY}/api/v3/CarrierPickup/postFedExCreateCarrierPickupRequest", body: body)
|
|
149
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
150
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
92
151
|
def cancel_pickup(body) = @http.put("#{PROXY}/api/v3/CarrierPickup/putFedExCancelCarrierPickupRequest", body: body)
|
|
152
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
153
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
93
154
|
def search_locations(body) = @http.post("#{PROXY}/api/v3/LocationSearch/postFedExSearchValidLocations", body: body)
|
|
155
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
156
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
94
157
|
def get_service_standards(body) = @http.post("#{PROXY}/api/v3/ServiceStandards/postFedExRetrieveServicesAndTransitTimes", body: body)
|
|
158
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
159
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
95
160
|
def get_freight_rate(body) = @http.post("#{PROXY}/api/v3/Freight/postFedExGetFreightRateQuote", body: body)
|
|
161
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
162
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
96
163
|
def create_freight_shipment(body) = @http.post("#{PROXY}/api/v3/Freight/postFedExCreateFreightShipment", body: body)
|
|
164
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
165
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
97
166
|
def ground_close(body) = @http.post("#{PROXY}/api/v3/GroundClose/postFedExCloseWithDocuments", body: body)
|
|
167
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
168
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
98
169
|
def upload_trade_documents(body) = @http.post("#{PROXY}/api/v3/Trade/postFedExUploadTradeDocuments", body: body)
|
|
170
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
171
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
99
172
|
def create_open_shipment(body) = @http.post("#{PROXY}/api/v3/OpenShip/postFedExCreateOpenShipment", body: body)
|
|
173
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
174
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
100
175
|
def add_packages_to_open_shipment(body) = @http.post("#{PROXY}/api/v3/OpenShip/postFedExAddPackagesToOpenShipment", body: body)
|
|
176
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
177
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
101
178
|
def confirm_open_shipment(body) = @http.post("#{PROXY}/api/v3/OpenShip/postFedExConfirmOpenShipment", body: body)
|
|
102
179
|
end
|
|
103
180
|
|
|
@@ -110,16 +187,26 @@ module FlexOps
|
|
|
110
187
|
|
|
111
188
|
def validate_address(params) = @http.get("#{PROXY}/api/v2/ShippingLabel/getDhlValidateAddress", query: params)
|
|
112
189
|
def get_rates(params) = @http.get("#{PROXY}/api/v2/ShippingLabel/getDhlRates", query: params)
|
|
190
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
191
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
113
192
|
def get_multi_piece_rates(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postDhlMultiPieceRates", body: body)
|
|
114
193
|
def get_products(params) = @http.get("#{PROXY}/api/v2/ShippingLabel/getDhlProducts", query: params)
|
|
115
|
-
def create_shipment(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postDhlCreateShipment", body: body)
|
|
116
194
|
def track(params) = @http.get("#{PROXY}/api/v2/ShippingLabel/getDhlTrackSingleShipment", query: params)
|
|
117
195
|
def track_multiple(params) = @http.get("#{PROXY}/api/v2/ShippingLabel/getDhlTrackMultipleShipments", query: params)
|
|
196
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
197
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
118
198
|
def create_pickup(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postDhlCreatePickup", body: body)
|
|
119
|
-
|
|
199
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
200
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
120
201
|
def cancel_pickup = @http.delete("#{PROXY}/api/v2/ShippingLabel/deleteDhlPickup")
|
|
202
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
203
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
121
204
|
def calculate_landed_cost(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postDhlCalculateLandedCost", body: body)
|
|
205
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
206
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
122
207
|
def screen_shipment(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postDhlScreenShipment", body: body)
|
|
208
|
+
# @deprecated Legacy carrier proxy (sunset 2027-06-30). Needs a user token: API keys get 403
|
|
209
|
+
# API_KEY_SCOPE_REQUIRED. Use client.shipping / client.pickups / client.scan_forms where they cover this.
|
|
123
210
|
def upload_invoice(body) = @http.post("#{PROXY}/api/v2/ShippingLabel/postDhlUploadInvoice", body: body)
|
|
124
211
|
def get_proof_of_delivery(params) = @http.get("#{PROXY}/api/v2/ShippingLabel/getDhlElectronicProofOfDelivery", query: params)
|
|
125
212
|
def get_reference_data(params) = @http.get("#{PROXY}/api/v2/ShippingLabel/getDhlReferenceData", query: params)
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
# ***********************************************************************
|
|
2
2
|
# Package : flexops
|
|
3
3
|
# Author : FlexOps, LLC
|
|
4
|
-
# Created : 2026-
|
|
4
|
+
# Created : 2026-10-19
|
|
5
5
|
#
|
|
6
6
|
# Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
|
|
7
7
|
# ***********************************************************************
|
|
8
8
|
|
|
9
|
-
# frozen_string_literal: true
|
|
10
|
-
|
|
11
9
|
module FlexOps
|
|
12
10
|
module Resources
|
|
11
|
+
# Branded tracking email templates, one per tracking event. Answers are bare JSON (no
|
|
12
|
+
# envelope); ids are GUID strings.
|
|
13
|
+
#
|
|
14
|
+
# Reads work with any API key or a user token. Writes (upsert, delete, preview) need a user
|
|
15
|
+
# token (auth.login) or an API key granted the opt-in "email-templates:write" scope; a key
|
|
16
|
+
# without it gets 403 API_KEY_SCOPE_REQUIRED.
|
|
17
|
+
#
|
|
18
|
+
# +eventType+ is the Gateway TrackingEmailEvent number: LabelCreated 0, InTransit 1,
|
|
19
|
+
# OutForDelivery 2, Delivered 3, Exception 4, ReturnToSender 5.
|
|
13
20
|
class EmailTemplates
|
|
14
21
|
def initialize(http, ws_id_proc)
|
|
15
22
|
@http = http
|
|
@@ -17,33 +24,36 @@ module FlexOps
|
|
|
17
24
|
end
|
|
18
25
|
|
|
19
26
|
def list
|
|
20
|
-
@http.get(
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def get(id)
|
|
24
|
-
@http.get("#{ws_path}/shipment-email-templates/#{id}")
|
|
27
|
+
@http.get(ws_path)
|
|
25
28
|
end
|
|
26
29
|
|
|
27
|
-
def
|
|
28
|
-
@http.
|
|
30
|
+
def get(template_id)
|
|
31
|
+
@http.get("#{ws_path}/#{template_id}")
|
|
29
32
|
end
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
# Creates or replaces the template for request's +eventType+.
|
|
35
|
+
#
|
|
36
|
+
# request:: { eventType: (0-5), subjectTemplate: (<= 500 chars), htmlBodyTemplate:,
|
|
37
|
+
# isEnabled: (default true) }. Returns the template.
|
|
38
|
+
def upsert(request)
|
|
39
|
+
@http.put(ws_path, body: request)
|
|
33
40
|
end
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
# Returns nil (204).
|
|
43
|
+
def delete(template_id)
|
|
44
|
+
@http.delete("#{ws_path}/#{template_id}")
|
|
37
45
|
end
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
# Renders a draft with sample placeholder values. Returns { renderedSubject, renderedHtmlBody }.
|
|
48
|
+
def preview(subject_template:, html_body_template:)
|
|
49
|
+
@http.post("#{ws_path}/preview",
|
|
50
|
+
body: { subjectTemplate: subject_template, htmlBodyTemplate: html_body_template })
|
|
41
51
|
end
|
|
42
52
|
|
|
43
53
|
private
|
|
44
54
|
|
|
45
55
|
def ws_path
|
|
46
|
-
"/api/workspaces/#{@ws_id.call}"
|
|
56
|
+
"/api/workspaces/#{@ws_id.call}/email-templates"
|
|
47
57
|
end
|
|
48
58
|
end
|
|
49
59
|
end
|
|
@@ -8,31 +8,30 @@
|
|
|
8
8
|
|
|
9
9
|
# frozen_string_literal: true
|
|
10
10
|
|
|
11
|
+
require "erb"
|
|
12
|
+
|
|
11
13
|
module FlexOps
|
|
12
14
|
module Resources
|
|
15
|
+
# HS code lookup and landed cost. These routes are not workspace-scoped; each returns a bare
|
|
16
|
+
# (not enveloped) answer.
|
|
13
17
|
class HsCodes
|
|
14
|
-
def initialize(http
|
|
18
|
+
def initialize(http)
|
|
15
19
|
@http = http
|
|
16
|
-
@ws_id = ws_id_proc
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
def search(query, destination_country: nil, max_results: 10)
|
|
20
23
|
params = { query: query, destinationCountry: destination_country, maxResults: max_results }.compact
|
|
21
|
-
@http.get("
|
|
24
|
+
@http.get("/api/shipping/hs-codes/search", query: params)
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
def lookup(code)
|
|
25
|
-
@http.get("
|
|
28
|
+
@http.get("/api/shipping/hs-codes/#{ERB::Util.url_encode(code.to_s)}")
|
|
26
29
|
end
|
|
27
30
|
|
|
31
|
+
# request:: { destinationCountry:, items: [{ description:, quantity:, value:, weightOz:,
|
|
32
|
+
# hsCode: (optional), originCountry:, skuOrProductId: (optional) }], shippingCost: }
|
|
28
33
|
def estimate_landed_cost(request)
|
|
29
|
-
@http.post("
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
private
|
|
33
|
-
|
|
34
|
-
def ws_path
|
|
35
|
-
"/api/workspaces/#{@ws_id.call}"
|
|
34
|
+
@http.post("/api/shipping/landed-cost", body: request)
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
end
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
module FlexOps
|
|
10
10
|
module Resources
|
|
11
|
+
# Shipping insurance. Answers use the { success, data, message, errorCode } envelope.
|
|
11
12
|
class Insurance
|
|
12
13
|
def initialize(http, ws_id_proc)
|
|
13
14
|
@http = http
|
|
@@ -18,24 +19,33 @@ module FlexOps
|
|
|
18
19
|
@http.get("#{ws_path}/insurance/providers")
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
def get_quote(
|
|
22
|
-
body = {
|
|
23
|
-
|
|
22
|
+
def get_quote(declared_value:, carrier_code:, tracking_number: nil, provider: nil)
|
|
23
|
+
body = { declaredValue: declared_value, carrierCode: carrier_code,
|
|
24
|
+
trackingNumber: tracking_number, provider: provider }.compact
|
|
24
25
|
@http.post("#{ws_path}/insurance/quote", body: body)
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
# Buys a policy and charges the premium to the workspace wallet. A shipment that already has a
|
|
29
|
+
# policy in force is refused with 409 ALREADY_EXISTS.
|
|
30
|
+
# Pass an idempotency_key you store with the purchase to make your own retry safe: after an
|
|
31
|
+
# error or a timeout, retry with the same key and the Gateway returns the first result instead
|
|
32
|
+
# of buying again. Without one, the SDK's own retries still share a key, but a new call gets a new key.
|
|
33
|
+
def purchase(tracking_number:, carrier_code:, declared_value:, coverage_tier: nil, provider: nil,
|
|
34
|
+
idempotency_key: nil)
|
|
35
|
+
body = { trackingNumber: tracking_number, carrierCode: carrier_code, declaredValue: declared_value,
|
|
36
|
+
coverageTier: coverage_tier, provider: provider }.compact
|
|
37
|
+
@http.post("#{ws_path}/insurance/policies", body: body,
|
|
38
|
+
headers: idempotency_key.nil? ? {} : { "Idempotency-Key" => idempotency_key })
|
|
31
39
|
end
|
|
32
40
|
|
|
33
41
|
def void(policy_id)
|
|
34
42
|
@http.delete("#{ws_path}/insurance/policies/#{policy_id}")
|
|
35
43
|
end
|
|
36
44
|
|
|
37
|
-
def file_claim(policy_id
|
|
38
|
-
|
|
45
|
+
def file_claim(policy_id:, claim_reason:, claim_amount:, evidence_json: nil)
|
|
46
|
+
body = { policyId: policy_id, claimReason: claim_reason, claimAmount: claim_amount,
|
|
47
|
+
evidenceJson: evidence_json }.compact
|
|
48
|
+
@http.post("#{ws_path}/insurance/claims", body: body)
|
|
39
49
|
end
|
|
40
50
|
|
|
41
51
|
private
|
|
@@ -15,10 +15,6 @@ module FlexOps
|
|
|
15
15
|
@http = http
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def post_asn_receipt(receipt)
|
|
19
|
-
@http.post("#{PROXY}/api/v1/Inventory/postNewAsnReceipt", body: receipt)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
18
|
def get_warehouse_snapshot(params = nil)
|
|
23
19
|
@http.get("#{PROXY}/api/v1/Inventory/getWarehouseInventorySnapshot", query: params)
|
|
24
20
|
end
|
|
@@ -30,10 +26,6 @@ module FlexOps
|
|
|
30
26
|
def get_part_numbers(params = nil)
|
|
31
27
|
@http.get("#{PROXY}/api/v1/Inventory/getPartNumberList", query: params)
|
|
32
28
|
end
|
|
33
|
-
|
|
34
|
-
def update_inventory(data)
|
|
35
|
-
@http.post("#{PROXY}/api/v2/Inventory/postCustomerInventoryUpdate", body: data)
|
|
36
|
-
end
|
|
37
29
|
end
|
|
38
30
|
end
|
|
39
31
|
end
|
|
@@ -15,10 +15,6 @@ module FlexOps
|
|
|
15
15
|
@http = http
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def create(order)
|
|
19
|
-
@http.post("#{BASE}/postNewOrder", body: order)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
18
|
def get_new_orders(params = nil)
|
|
23
19
|
@http.get("#{BASE}/getNewOrderList", query: params)
|
|
24
20
|
end
|
|
@@ -39,10 +35,6 @@ module FlexOps
|
|
|
39
35
|
@http.get("#{BASE}/getIndividualOrderStatusByOrderNumber", query: { orderNumber: order_number })
|
|
40
36
|
end
|
|
41
37
|
|
|
42
|
-
def cancel(order_number)
|
|
43
|
-
@http.post("#{BASE}/cancelOrderByOrderNumber", body: { orderNumber: order_number })
|
|
44
|
-
end
|
|
45
|
-
|
|
46
38
|
def get_items(order_number)
|
|
47
39
|
@http.get("#{BASE}/getAllOrderItemsByOrderNumber", query: { orderNumber: order_number })
|
|
48
40
|
end
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
|
|
7
7
|
# ***********************************************************************
|
|
8
8
|
|
|
9
|
+
require "uri"
|
|
10
|
+
|
|
9
11
|
module FlexOps
|
|
10
12
|
module Resources
|
|
11
13
|
class Pickups
|
|
@@ -14,20 +16,27 @@ module FlexOps
|
|
|
14
16
|
@ws_id = ws_id_proc
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# request:: { carrierCode:, pickupDate:, street:, city:, state:, postalCode:,
|
|
20
|
+
# country: (optional, "US"), packageCount:, totalWeightOz: (optional),
|
|
21
|
+
# specialInstructions:, readyTime:, closeTime:, contactName:, contactPhone: (all optional) }
|
|
17
22
|
def schedule(request)
|
|
18
23
|
@http.post("#{ws_path}/pickups", body: request)
|
|
19
24
|
end
|
|
20
25
|
|
|
26
|
+
# Lists the workspace's scheduled pickups.
|
|
21
27
|
def list
|
|
22
|
-
@http.get("#{ws_path}/pickups")
|
|
28
|
+
@http.get("#{ws_path}/pickups/scheduled")
|
|
23
29
|
end
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
# The Gateway uses "usps" when carrier_code is left out.
|
|
32
|
+
def get(pickup_id, carrier_code: nil)
|
|
33
|
+
@http.get("#{ws_path}/pickups/#{pickup_id}", query: carrier_code.nil? ? nil : { carrierCode: carrier_code })
|
|
27
34
|
end
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
# The Gateway uses "usps" when carrier_code is left out.
|
|
37
|
+
def cancel(pickup_id, carrier_code: nil)
|
|
38
|
+
query = carrier_code.nil? ? "" : "?#{URI.encode_www_form(carrierCode: carrier_code)}"
|
|
39
|
+
@http.delete("#{ws_path}/pickups/#{pickup_id}#{query}")
|
|
31
40
|
end
|
|
32
41
|
|
|
33
42
|
private
|
|
@@ -1,58 +1,73 @@
|
|
|
1
1
|
# ***********************************************************************
|
|
2
2
|
# Package : flexops
|
|
3
3
|
# Author : FlexOps, LLC
|
|
4
|
-
# Created : 2026-
|
|
4
|
+
# Created : 2026-10-19
|
|
5
5
|
#
|
|
6
6
|
# Copyright (c) 2021-2026 by FlexOps, LLC. All rights reserved.
|
|
7
7
|
# ***********************************************************************
|
|
8
8
|
|
|
9
|
-
# frozen_string_literal: true
|
|
10
|
-
|
|
11
9
|
module FlexOps
|
|
12
10
|
module Resources
|
|
11
|
+
# Recurring shipment schedules. Answers are bare JSON (no envelope); ids are GUID strings.
|
|
12
|
+
#
|
|
13
|
+
# Reads work with any API key or a user token. Writes (create, update, delete, pause, resume,
|
|
14
|
+
# trigger) need a user token (auth.login) or an API key granted the opt-in
|
|
15
|
+
# "recurring-shipments:write" scope; a key without it gets 403 API_KEY_SCOPE_REQUIRED.
|
|
16
|
+
#
|
|
17
|
+
# +interval+ is the Gateway RecurrenceInterval number: Daily 0, Weekly 1, Biweekly 2,
|
|
18
|
+
# Monthly 3, Custom 4 (Custom runs on +cronExpression+).
|
|
13
19
|
class RecurringShipments
|
|
14
20
|
def initialize(http, ws_id_proc)
|
|
15
21
|
@http = http
|
|
16
22
|
@ws_id = ws_id_proc
|
|
17
23
|
end
|
|
18
24
|
|
|
19
|
-
def list
|
|
20
|
-
|
|
21
|
-
@http.get("#{ws_path}/recurring-shipments/", query: query.empty? ? nil : query)
|
|
25
|
+
def list
|
|
26
|
+
@http.get(ws_path)
|
|
22
27
|
end
|
|
23
28
|
|
|
24
29
|
def get(id)
|
|
25
|
-
@http.get("#{ws_path}
|
|
30
|
+
@http.get("#{ws_path}/#{id}")
|
|
26
31
|
end
|
|
27
32
|
|
|
33
|
+
# request:: { name:, firstRunUtc: (ISO 8601), interval: (0-4), presetId:, addressId:,
|
|
34
|
+
# cronExpression:, maxFailures: (1-100, default 3) }. Each run needs a destination
|
|
35
|
+
# +addressId+; a +presetId+ supplies the package and service. Returns the schedule.
|
|
28
36
|
def create(request)
|
|
29
|
-
@http.post(
|
|
37
|
+
@http.post(ws_path, body: request)
|
|
30
38
|
end
|
|
31
39
|
|
|
40
|
+
# request:: any of name:, presetId:, addressId:, interval:, cronExpression:, maxFailures:.
|
|
32
41
|
def update(id, request)
|
|
33
|
-
@http.put("#{ws_path}
|
|
42
|
+
@http.put("#{ws_path}/#{id}", body: request)
|
|
34
43
|
end
|
|
35
44
|
|
|
45
|
+
# Returns nil (204).
|
|
36
46
|
def delete(id)
|
|
37
|
-
@http.delete("#{ws_path}
|
|
47
|
+
@http.delete("#{ws_path}/#{id}")
|
|
38
48
|
end
|
|
39
49
|
|
|
50
|
+
# Returns nil (204).
|
|
40
51
|
def pause(id)
|
|
41
|
-
@http.post("#{ws_path}
|
|
52
|
+
@http.post("#{ws_path}/#{id}/pause")
|
|
42
53
|
end
|
|
43
54
|
|
|
55
|
+
# Returns nil (204).
|
|
44
56
|
def resume(id)
|
|
45
|
-
@http.post("#{ws_path}
|
|
57
|
+
@http.post("#{ws_path}/#{id}/resume")
|
|
46
58
|
end
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
# Runs the schedule now, which creates an order. Pass the same +idempotency_key+ when you
|
|
61
|
+
# retry a trigger yourself so the Gateway does not create a second order. Returns nil (204).
|
|
62
|
+
def trigger(id, idempotency_key: nil)
|
|
63
|
+
@http.post("#{ws_path}/#{id}/trigger",
|
|
64
|
+
headers: idempotency_key.nil? ? {} : { "Idempotency-Key" => idempotency_key })
|
|
50
65
|
end
|
|
51
66
|
|
|
52
67
|
private
|
|
53
68
|
|
|
54
69
|
def ws_path
|
|
55
|
-
"/api/workspaces/#{@ws_id.call}"
|
|
70
|
+
"/api/workspaces/#{@ws_id.call}/recurring-shipments"
|
|
56
71
|
end
|
|
57
72
|
end
|
|
58
73
|
end
|