griffnode 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.
Files changed (154) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/LICENSE +17 -0
  4. data/README.md +24 -0
  5. data/Rakefile +10 -0
  6. data/docs/Account.md +22 -0
  7. data/docs/AccountApi.md +339 -0
  8. data/docs/AccountLimits.md +22 -0
  9. data/docs/AccountPlan.md +24 -0
  10. data/docs/AccountUsage.md +22 -0
  11. data/docs/Balance.md +24 -0
  12. data/docs/BillingApi.md +75 -0
  13. data/docs/CreateBillingCheckoutRequest.md +22 -0
  14. data/docs/CreateDetailedTransactionRequest.md +32 -0
  15. data/docs/CreateTransactionRequest.md +30 -0
  16. data/docs/CryptoSymbol.md +15 -0
  17. data/docs/Cryptocurrency.md +26 -0
  18. data/docs/DefaultApi.md +78 -0
  19. data/docs/Error.md +22 -0
  20. data/docs/FiatCurrency.md +15 -0
  21. data/docs/GetAccount200Response.md +20 -0
  22. data/docs/GetHealth200Response.md +22 -0
  23. data/docs/GetPrices200Response.md +20 -0
  24. data/docs/GetPrices200ResponseData.md +22 -0
  25. data/docs/GetStats200Response.md +20 -0
  26. data/docs/GetStats200ResponseData.md +24 -0
  27. data/docs/InlineObject.md +24 -0
  28. data/docs/InlineObjectAllOfExistingTransaction.md +22 -0
  29. data/docs/Invoice.md +34 -0
  30. data/docs/LineItem.md +22 -0
  31. data/docs/ListBalances200Response.md +20 -0
  32. data/docs/ListBalances200ResponseData.md +18 -0
  33. data/docs/ListCryptocurrencies200Response.md +20 -0
  34. data/docs/ListCryptocurrencies200ResponseData.md +20 -0
  35. data/docs/ListInvoices200Response.md +20 -0
  36. data/docs/ListInvoices200ResponseData.md +20 -0
  37. data/docs/ListPlans200Response.md +20 -0
  38. data/docs/ListPlans200ResponseData.md +20 -0
  39. data/docs/ListTransactions200Response.md +20 -0
  40. data/docs/ListTransactions200ResponseData.md +20 -0
  41. data/docs/MarketDataApi.md +202 -0
  42. data/docs/Pagination.md +22 -0
  43. data/docs/PaymentSplit.md +28 -0
  44. data/docs/Plan.md +38 -0
  45. data/docs/PlanTier.md +15 -0
  46. data/docs/SystemApi.md +139 -0
  47. data/docs/Transaction.md +66 -0
  48. data/docs/TransactionEnvelope.md +20 -0
  49. data/docs/TransactionStatus.md +15 -0
  50. data/docs/TransactionsApi.md +295 -0
  51. data/docs/WebhookPayload.md +40 -0
  52. data/git_push.sh +57 -0
  53. data/griffnode.gemspec +43 -0
  54. data/lib/griffnode/api/account_api.rb +315 -0
  55. data/lib/griffnode/api/billing_api.rb +88 -0
  56. data/lib/griffnode/api/default_api.rb +84 -0
  57. data/lib/griffnode/api/market_data_api.rb +187 -0
  58. data/lib/griffnode/api/system_api.rb +158 -0
  59. data/lib/griffnode/api/transactions_api.rb +300 -0
  60. data/lib/griffnode/api_client.rb +394 -0
  61. data/lib/griffnode/api_error.rb +58 -0
  62. data/lib/griffnode/configuration.rb +308 -0
  63. data/lib/griffnode/models/account.rb +232 -0
  64. data/lib/griffnode/models/account_limits.rb +232 -0
  65. data/lib/griffnode/models/account_plan.rb +264 -0
  66. data/lib/griffnode/models/account_usage.rb +234 -0
  67. data/lib/griffnode/models/balance.rb +241 -0
  68. data/lib/griffnode/models/create_billing_checkout_request.rb +284 -0
  69. data/lib/griffnode/models/create_detailed_transaction_request.rb +349 -0
  70. data/lib/griffnode/models/create_transaction_request.rb +313 -0
  71. data/lib/griffnode/models/crypto_symbol.rb +53 -0
  72. data/lib/griffnode/models/cryptocurrency.rb +284 -0
  73. data/lib/griffnode/models/error.rb +251 -0
  74. data/lib/griffnode/models/fiat_currency.rb +42 -0
  75. data/lib/griffnode/models/get_account200_response.rb +233 -0
  76. data/lib/griffnode/models/get_health200_response.rb +232 -0
  77. data/lib/griffnode/models/get_prices200_response.rb +233 -0
  78. data/lib/griffnode/models/get_prices200_response_data.rb +238 -0
  79. data/lib/griffnode/models/get_stats200_response.rb +233 -0
  80. data/lib/griffnode/models/get_stats200_response_data.rb +241 -0
  81. data/lib/griffnode/models/inline_object.rb +267 -0
  82. data/lib/griffnode/models/inline_object_all_of_existing_transaction.rb +254 -0
  83. data/lib/griffnode/models/invoice.rb +311 -0
  84. data/lib/griffnode/models/line_item.rb +267 -0
  85. data/lib/griffnode/models/list_balances200_response.rb +233 -0
  86. data/lib/griffnode/models/list_balances200_response_data.rb +216 -0
  87. data/lib/griffnode/models/list_cryptocurrencies200_response.rb +233 -0
  88. data/lib/griffnode/models/list_cryptocurrencies200_response_data.rb +225 -0
  89. data/lib/griffnode/models/list_invoices200_response.rb +233 -0
  90. data/lib/griffnode/models/list_invoices200_response_data.rb +225 -0
  91. data/lib/griffnode/models/list_plans200_response.rb +233 -0
  92. data/lib/griffnode/models/list_plans200_response_data.rb +227 -0
  93. data/lib/griffnode/models/list_transactions200_response.rb +233 -0
  94. data/lib/griffnode/models/list_transactions200_response_data.rb +239 -0
  95. data/lib/griffnode/models/pagination.rb +253 -0
  96. data/lib/griffnode/models/payment_split.rb +262 -0
  97. data/lib/griffnode/models/plan.rb +330 -0
  98. data/lib/griffnode/models/plan_tier.rb +42 -0
  99. data/lib/griffnode/models/transaction.rb +535 -0
  100. data/lib/griffnode/models/transaction_envelope.rb +233 -0
  101. data/lib/griffnode/models/transaction_status.rb +44 -0
  102. data/lib/griffnode/models/webhook_payload.rb +387 -0
  103. data/lib/griffnode/version.rb +15 -0
  104. data/lib/griffnode.rb +84 -0
  105. data/openapi.yaml +791 -0
  106. data/openapitools.json +15 -0
  107. data/spec/api/account_api_spec.rb +87 -0
  108. data/spec/api/billing_api_spec.rb +46 -0
  109. data/spec/api/default_api_spec.rb +47 -0
  110. data/spec/api/market_data_api_spec.rb +65 -0
  111. data/spec/api/system_api_spec.rb +60 -0
  112. data/spec/api/transactions_api_spec.rb +84 -0
  113. data/spec/models/account_limits_spec.rb +48 -0
  114. data/spec/models/account_plan_spec.rb +54 -0
  115. data/spec/models/account_spec.rb +48 -0
  116. data/spec/models/account_usage_spec.rb +48 -0
  117. data/spec/models/balance_spec.rb +54 -0
  118. data/spec/models/create_billing_checkout_request_spec.rb +48 -0
  119. data/spec/models/create_detailed_transaction_request_spec.rb +78 -0
  120. data/spec/models/create_transaction_request_spec.rb +72 -0
  121. data/spec/models/crypto_symbol_spec.rb +30 -0
  122. data/spec/models/cryptocurrency_spec.rb +64 -0
  123. data/spec/models/error_spec.rb +48 -0
  124. data/spec/models/fiat_currency_spec.rb +30 -0
  125. data/spec/models/get_account200_response_spec.rb +42 -0
  126. data/spec/models/get_health200_response_spec.rb +48 -0
  127. data/spec/models/get_prices200_response_data_spec.rb +48 -0
  128. data/spec/models/get_prices200_response_spec.rb +42 -0
  129. data/spec/models/get_stats200_response_data_spec.rb +54 -0
  130. data/spec/models/get_stats200_response_spec.rb +42 -0
  131. data/spec/models/inline_object_all_of_existing_transaction_spec.rb +48 -0
  132. data/spec/models/inline_object_spec.rb +54 -0
  133. data/spec/models/invoice_spec.rb +84 -0
  134. data/spec/models/line_item_spec.rb +48 -0
  135. data/spec/models/list_balances200_response_data_spec.rb +36 -0
  136. data/spec/models/list_balances200_response_spec.rb +42 -0
  137. data/spec/models/list_cryptocurrencies200_response_data_spec.rb +42 -0
  138. data/spec/models/list_cryptocurrencies200_response_spec.rb +42 -0
  139. data/spec/models/list_invoices200_response_data_spec.rb +42 -0
  140. data/spec/models/list_invoices200_response_spec.rb +42 -0
  141. data/spec/models/list_plans200_response_data_spec.rb +42 -0
  142. data/spec/models/list_plans200_response_spec.rb +42 -0
  143. data/spec/models/list_transactions200_response_data_spec.rb +42 -0
  144. data/spec/models/list_transactions200_response_spec.rb +42 -0
  145. data/spec/models/pagination_spec.rb +48 -0
  146. data/spec/models/payment_split_spec.rb +66 -0
  147. data/spec/models/plan_spec.rb +96 -0
  148. data/spec/models/plan_tier_spec.rb +30 -0
  149. data/spec/models/transaction_envelope_spec.rb +42 -0
  150. data/spec/models/transaction_spec.rb +184 -0
  151. data/spec/models/transaction_status_spec.rb +30 -0
  152. data/spec/models/webhook_payload_spec.rb +106 -0
  153. data/spec/spec_helper.rb +111 -0
  154. metadata +286 -0
data/openapi.yaml ADDED
@@ -0,0 +1,791 @@
1
+ openapi: 3.1.0
2
+
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # GriffNode Public Merchant API — SINGLE SOURCE OF TRUTH
5
+ #
6
+ # This file is CANONICAL. Code, docs, SDKs, and plugins are reconciled TO it.
7
+ # Governance: docs/sdk/api-contract-governance.md (the contract is FROZEN).
8
+ #
9
+ # This spec describes the cleaned-up v1 TARGET (decided: clean break + trim + /v1):
10
+ # - transaction_id everywhere (the old `txid` on create/get is removed)
11
+ # - currency_fiat everywhere (the old `currency` on create/get is removed)
12
+ # - one pagination style: limit + offset, with a { limit, offset, total } block
13
+ # - one curated Transaction schema for create/get/list (capability tokens,
14
+ # client_ip, address_index, and other internal fields are NOT exposed)
15
+ # - all paths under /v1
16
+ # Migration status of the Go code → this spec is tracked in api-inconsistencies.md.
17
+ # ─────────────────────────────────────────────────────────────────────────────
18
+
19
+ info:
20
+ title: GriffNode API
21
+ version: "1.0.1"
22
+ description: >
23
+ Accept Bitcoin, Litecoin, Dogecoin, Dash, Ethereum and ERC-20 tokens.
24
+ Server-to-server, authenticated with a secret API key
25
+ (`Authorization: Bearer sk_live_…` for live, or `sk_test_…` for test mode — same base URL).
26
+ All monetary amounts in API responses are JSON numbers; **webhook** amounts are
27
+ strings to preserve decimal precision (see the `webhooks` section).
28
+
29
+
30
+ **Rate limits.** Every request is rate-limited per API key in two windows — per minute
31
+ and per hour — by plan tier (min/hour): starter 30/500, business 100/2000, professional
32
+ 300/5000, enterprise 1000/20000. Every response carries `X-RateLimit-Limit`,
33
+ `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix seconds) for the per-minute window.
34
+ On `429` the body is `error: "RATE_LIMIT_EXCEEDED"` (either window) with a `Retry-After`
35
+ header (seconds) — clients should retry after it. A separate `429` with
36
+ `error: "MONTHLY_LIMIT_REACHED"` means the plan's monthly transaction quota (not the
37
+ request rate).
38
+ license:
39
+ name: Apache-2.0
40
+ contact:
41
+ name: GriffNode Developers
42
+ url: https://griffnode.com
43
+
44
+ servers:
45
+ - url: https://api.griffnode.com/v1
46
+ description: >
47
+ Single base URL for both live and test traffic. A live secret key (`sk_live_`)
48
+ settles real funds on mainnet; a test secret key (`sk_test_`) runs in test mode
49
+ with no real funds. There is no separate sandbox host.
50
+
51
+ security:
52
+ - SecretKey: []
53
+
54
+ tags:
55
+ - name: Transactions
56
+ description: Create and read merchant payment transactions.
57
+ - name: Market Data
58
+ description: Live prices and the supported-currency catalogue.
59
+ - name: Account
60
+ description: Merchant plan, usage, balances, stats and invoices.
61
+ - name: Billing
62
+ description: Plan upgrades and account top-ups.
63
+ - name: System
64
+ description: Health and hosted-checkout redirect.
65
+
66
+ # ═════════════════════════════════════════════════════════════════════════════
67
+ paths:
68
+
69
+ /health:
70
+ get:
71
+ operationId: getHealth
72
+ tags: [System]
73
+ summary: API health check
74
+ security: []
75
+ responses:
76
+ "200":
77
+ description: Service status (intentionally NOT wrapped in the success envelope).
78
+ content:
79
+ application/json:
80
+ schema:
81
+ type: object
82
+ properties:
83
+ status: { type: string, examples: ["operational"] }
84
+ service: { type: string, examples: ["GriffNode API"] }
85
+ version: { type: string, examples: ["1.0.0"] }
86
+
87
+ /pay:
88
+ get:
89
+ operationId: hostedCheckoutRedirect
90
+ tags: [System]
91
+ summary: Hosted-checkout redirect (browser flow, publishable key)
92
+ description: >
93
+ Browser-facing redirect to the hosted payment page, authenticated by a
94
+ **publishable** key in the query string (safe to expose client-side).
95
+ Not used by the server-side SDKs — included for completeness.
96
+ security: []
97
+ parameters:
98
+ - { name: pk, in: query, required: true, schema: { type: string }, description: "Publishable key, pk_live_… / pk_test_…" }
99
+ - { name: amount, in: query, required: true, schema: { type: string }, description: "Fiat amount (≥ 1.00 USD equivalent)." }
100
+ - { name: crypto, in: query, required: true, schema: { $ref: "#/components/schemas/CryptoSymbol" } }
101
+ - { name: link, in: query, required: false, schema: { type: string }, description: "Payment-link slug for attribution." }
102
+ responses:
103
+ "302":
104
+ description: Redirect to https://griffnode.com/transaction/{transaction_id}
105
+ "400": { $ref: "#/components/responses/BadRequest" }
106
+
107
+ /transactions/create:
108
+ post:
109
+ operationId: createTransaction
110
+ tags: [Transactions]
111
+ summary: Create a payment transaction
112
+ parameters:
113
+ - $ref: "#/components/parameters/IdempotencyKey"
114
+ requestBody:
115
+ required: true
116
+ content:
117
+ application/json:
118
+ schema: { $ref: "#/components/schemas/CreateTransactionRequest" }
119
+ responses:
120
+ "201":
121
+ description: Transaction created.
122
+ content:
123
+ application/json:
124
+ schema: { $ref: "#/components/schemas/TransactionEnvelope" }
125
+ "400": { $ref: "#/components/responses/BadRequest" }
126
+ "401": { $ref: "#/components/responses/Unauthorized" }
127
+ "402": { $ref: "#/components/responses/InsufficientBalance" }
128
+ "403": { $ref: "#/components/responses/Forbidden" }
129
+ "409": { $ref: "#/components/responses/ActiveTransactionExists" }
130
+ "429": { $ref: "#/components/responses/TooManyRequests" }
131
+
132
+ /transactions/create-detailed:
133
+ post:
134
+ operationId: createDetailedTransaction
135
+ tags: [Transactions]
136
+ summary: Create an itemized transaction (Professional/Enterprise plans)
137
+ parameters:
138
+ - $ref: "#/components/parameters/IdempotencyKey"
139
+ requestBody:
140
+ required: true
141
+ content:
142
+ application/json:
143
+ schema: { $ref: "#/components/schemas/CreateDetailedTransactionRequest" }
144
+ responses:
145
+ "201":
146
+ description: Transaction created.
147
+ content:
148
+ application/json:
149
+ schema: { $ref: "#/components/schemas/TransactionEnvelope" }
150
+ "400": { $ref: "#/components/responses/BadRequest" }
151
+ "401": { $ref: "#/components/responses/Unauthorized" }
152
+ "402": { $ref: "#/components/responses/InsufficientBalance" }
153
+ "403": { $ref: "#/components/responses/Forbidden" }
154
+ "429": { $ref: "#/components/responses/TooManyRequests" }
155
+
156
+ /transactions/list:
157
+ get:
158
+ operationId: listTransactions
159
+ tags: [Transactions]
160
+ summary: List the merchant's transactions (newest first)
161
+ parameters:
162
+ - $ref: "#/components/parameters/Limit"
163
+ - $ref: "#/components/parameters/Offset"
164
+ - { name: status, in: query, required: false, schema: { $ref: "#/components/schemas/TransactionStatus" } }
165
+ - { name: crypto, in: query, required: false, schema: { $ref: "#/components/schemas/CryptoSymbol" } }
166
+ responses:
167
+ "200":
168
+ description: A page of transactions.
169
+ content:
170
+ application/json:
171
+ schema:
172
+ type: object
173
+ required: [success, data]
174
+ properties:
175
+ success: { const: true }
176
+ data:
177
+ type: object
178
+ required: [transactions, pagination]
179
+ properties:
180
+ transactions:
181
+ type: array
182
+ items: { $ref: "#/components/schemas/Transaction" }
183
+ pagination: { $ref: "#/components/schemas/Pagination" }
184
+ "401": { $ref: "#/components/responses/Unauthorized" }
185
+ "403": { $ref: "#/components/responses/Forbidden" }
186
+
187
+ /transactions/{transaction_id}:
188
+ get:
189
+ operationId: getTransaction
190
+ tags: [Transactions]
191
+ summary: Retrieve a single transaction
192
+ parameters:
193
+ - name: transaction_id
194
+ in: path
195
+ required: true
196
+ schema: { type: string, examples: ["MTX-A1B2C3D4"] }
197
+ responses:
198
+ "200":
199
+ description: The transaction.
200
+ content:
201
+ application/json:
202
+ schema: { $ref: "#/components/schemas/TransactionEnvelope" }
203
+ "401": { $ref: "#/components/responses/Unauthorized" }
204
+ "403": { $ref: "#/components/responses/Forbidden" }
205
+ "404": { $ref: "#/components/responses/NotFound" }
206
+
207
+ /prices:
208
+ get:
209
+ operationId: getPrices
210
+ tags: [Market Data]
211
+ summary: Current crypto and fiat exchange rates (USD-denominated)
212
+ responses:
213
+ "200":
214
+ description: Live rates.
215
+ content:
216
+ application/json:
217
+ schema:
218
+ type: object
219
+ required: [success, data]
220
+ properties:
221
+ success: { const: true }
222
+ data:
223
+ type: object
224
+ properties:
225
+ crypto:
226
+ type: object
227
+ additionalProperties: { type: number }
228
+ description: USD price per unit, keyed by crypto symbol (active coins only).
229
+ fiat:
230
+ type: object
231
+ additionalProperties: { type: number }
232
+ description: USD value of one unit of each fiat currency.
233
+ fetched_at: { type: string, format: date-time }
234
+ "401": { $ref: "#/components/responses/Unauthorized" }
235
+ "500": { $ref: "#/components/responses/ServerError" }
236
+
237
+ /cryptos/list:
238
+ get:
239
+ operationId: listCryptocurrencies
240
+ tags: [Market Data]
241
+ summary: All supported cryptocurrencies and tokens
242
+ responses:
243
+ "200":
244
+ description: Supported-currency catalogue.
245
+ content:
246
+ application/json:
247
+ schema:
248
+ type: object
249
+ required: [success, data]
250
+ properties:
251
+ success: { const: true }
252
+ data:
253
+ type: object
254
+ properties:
255
+ cryptocurrencies:
256
+ type: array
257
+ items: { $ref: "#/components/schemas/Cryptocurrency" }
258
+ total: { type: integer }
259
+ "401": { $ref: "#/components/responses/Unauthorized" }
260
+
261
+ /merchant/cryptos:
262
+ get:
263
+ operationId: listMerchantCryptocurrencies
264
+ tags: [Market Data]
265
+ summary: Cryptocurrencies this merchant has wallets configured for
266
+ responses:
267
+ "200":
268
+ description: The merchant's configured currencies.
269
+ content:
270
+ application/json:
271
+ schema:
272
+ type: object
273
+ required: [success, data]
274
+ properties:
275
+ success: { const: true }
276
+ data:
277
+ type: object
278
+ properties:
279
+ cryptocurrencies:
280
+ type: array
281
+ items: { $ref: "#/components/schemas/Cryptocurrency" }
282
+ total: { type: integer }
283
+ "401": { $ref: "#/components/responses/Unauthorized" }
284
+
285
+ /account:
286
+ get:
287
+ operationId: getAccount
288
+ tags: [Account]
289
+ summary: Merchant plan, usage and limits
290
+ responses:
291
+ "200":
292
+ description: Account details.
293
+ content:
294
+ application/json:
295
+ schema:
296
+ type: object
297
+ required: [success, data]
298
+ properties:
299
+ success: { const: true }
300
+ data: { $ref: "#/components/schemas/Account" }
301
+ "401": { $ref: "#/components/responses/Unauthorized" }
302
+ "403": { $ref: "#/components/responses/Forbidden" }
303
+
304
+ /plans:
305
+ get:
306
+ operationId: listPlans
307
+ tags: [Account]
308
+ summary: Plan catalogue and pricing
309
+ responses:
310
+ "200":
311
+ description: Plans.
312
+ content:
313
+ application/json:
314
+ schema:
315
+ type: object
316
+ required: [success, data]
317
+ properties:
318
+ success: { const: true }
319
+ data:
320
+ type: object
321
+ properties:
322
+ plans:
323
+ type: array
324
+ items: { $ref: "#/components/schemas/Plan" }
325
+ billing_cycles:
326
+ type: array
327
+ items: { type: string }
328
+ examples: [["monthly", "quarterly", "biannual", "yearly", "biennial"]]
329
+ "401": { $ref: "#/components/responses/Unauthorized" }
330
+ "403": { $ref: "#/components/responses/Forbidden" }
331
+
332
+ /balances:
333
+ get:
334
+ operationId: listBalances
335
+ tags: [Account]
336
+ summary: On-platform balances (for overage/top-up; NOT crypto settlement)
337
+ responses:
338
+ "200":
339
+ description: Balances.
340
+ content:
341
+ application/json:
342
+ schema:
343
+ type: object
344
+ required: [success, data]
345
+ properties:
346
+ success: { const: true }
347
+ data:
348
+ type: object
349
+ properties:
350
+ balances:
351
+ type: array
352
+ items: { $ref: "#/components/schemas/Balance" }
353
+ "401": { $ref: "#/components/responses/Unauthorized" }
354
+ "403": { $ref: "#/components/responses/Forbidden" }
355
+
356
+ /stats:
357
+ get:
358
+ operationId: getStats
359
+ tags: [Account]
360
+ summary: Merchant transaction analytics
361
+ responses:
362
+ "200":
363
+ description: Stats.
364
+ content:
365
+ application/json:
366
+ schema:
367
+ type: object
368
+ required: [success, data]
369
+ properties:
370
+ success: { const: true }
371
+ data:
372
+ type: object
373
+ properties:
374
+ total_transactions: { type: integer }
375
+ monthly_transactions: { type: integer }
376
+ total_volume_fiat: { type: number }
377
+ unique_customers: { type: integer }
378
+ "401": { $ref: "#/components/responses/Unauthorized" }
379
+ "403": { $ref: "#/components/responses/Forbidden" }
380
+
381
+ /invoices:
382
+ get:
383
+ operationId: listInvoices
384
+ tags: [Account]
385
+ summary: GriffNode billing invoices (platform ↔ merchant)
386
+ parameters:
387
+ - $ref: "#/components/parameters/Limit"
388
+ - $ref: "#/components/parameters/Offset"
389
+ responses:
390
+ "200":
391
+ description: Invoices.
392
+ content:
393
+ application/json:
394
+ schema:
395
+ type: object
396
+ required: [success, data]
397
+ properties:
398
+ success: { const: true }
399
+ data:
400
+ type: object
401
+ properties:
402
+ invoices:
403
+ type: array
404
+ items: { $ref: "#/components/schemas/Invoice" }
405
+ pagination: { $ref: "#/components/schemas/Pagination" }
406
+ "401": { $ref: "#/components/responses/Unauthorized" }
407
+ "403": { $ref: "#/components/responses/Forbidden" }
408
+
409
+ /billing/checkout:
410
+ post:
411
+ operationId: createBillingCheckout
412
+ tags: [Billing]
413
+ summary: Start a plan upgrade or account top-up
414
+ requestBody:
415
+ required: true
416
+ content:
417
+ application/json:
418
+ schema:
419
+ type: object
420
+ required: [tier]
421
+ properties:
422
+ tier: { $ref: "#/components/schemas/PlanTier" }
423
+ billing_months: { type: integer, minimum: 1, default: 1 }
424
+ payment_method: { type: string, default: "crypto" }
425
+ responses:
426
+ "201":
427
+ description: Billing transaction created.
428
+ content:
429
+ application/json:
430
+ schema: { $ref: "#/components/schemas/TransactionEnvelope" }
431
+ "401": { $ref: "#/components/responses/Unauthorized" }
432
+ "402": { $ref: "#/components/responses/InsufficientBalance" }
433
+ "403": { $ref: "#/components/responses/Forbidden" }
434
+
435
+ # ═════════════════════════════════════════════════════════════════════════════
436
+ # Webhooks (OpenAPI 3.1 top-level). Every SDK ships a verify() helper for the
437
+ # signature scheme below. NOTE: webhook amounts are STRINGS (precision-safe).
438
+ webhooks:
439
+ paymentEvent:
440
+ post:
441
+ operationId: paymentWebhook
442
+ summary: Payment lifecycle event delivered to the merchant's webhook URL
443
+ description: >
444
+ Signed with HMAC-SHA256 over the RAW request body. Verify by comparing
445
+ `X-GriffNode-Signature: sha256=<hex>` to
446
+ `hex(hmac_sha256(webhook_secret, raw_body))` using a constant-time compare.
447
+ Also sent: `X-GriffNode-Event` (the event type) and `X-Webhook-ID`
448
+ (unique delivery id — use for idempotency).
449
+ requestBody:
450
+ content:
451
+ application/json:
452
+ schema: { $ref: "#/components/schemas/WebhookPayload" }
453
+ responses:
454
+ "200":
455
+ description: Acknowledge within 15s. Non-2xx is retried with backoff.
456
+
457
+ # ═════════════════════════════════════════════════════════════════════════════
458
+ components:
459
+
460
+ securitySchemes:
461
+ SecretKey:
462
+ type: http
463
+ scheme: bearer
464
+ description: "Secret API key: `Authorization: Bearer sk_live_…` (or `sk_test_…`)."
465
+
466
+ parameters:
467
+ Limit:
468
+ name: limit
469
+ in: query
470
+ required: false
471
+ schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
472
+ Offset:
473
+ name: offset
474
+ in: query
475
+ required: false
476
+ schema: { type: integer, minimum: 0, default: 0 }
477
+ IdempotencyKey:
478
+ name: X-Idempotency-Key
479
+ in: header
480
+ required: false
481
+ schema: { type: string }
482
+ description: >
483
+ Optional unique key for a create request (e.g. a UUID). A retried create with the
484
+ same key returns the original transaction instead of creating a duplicate — send it
485
+ on every create so a network retry can't double-charge the customer.
486
+
487
+ schemas:
488
+
489
+ CryptoSymbol:
490
+ type: string
491
+ description: Supported coin/token symbol. /cryptos/list is authoritative at runtime.
492
+ enum: [BTC, LTC, DOGE, DASH, ETH, USDT, USDC, DAI, WBTC, SHIB, PEPE, LINK, UNI, AAVE, MKR]
493
+
494
+ FiatCurrency:
495
+ type: string
496
+ enum: [USD, PLN, EUR, GBP]
497
+ default: USD
498
+
499
+ TransactionStatus:
500
+ type: string
501
+ description: Public transaction status (internal sub-states are mapped to these).
502
+ enum: [pending, awaiting_confirmation, completed, partial, expired, failed]
503
+
504
+ PlanTier:
505
+ type: string
506
+ enum: [starter, business, professional, enterprise]
507
+
508
+ Metadata:
509
+ type: [object, "null"]
510
+ description: Free-form key/value (≤20 keys, string values ≤500 chars, ≤4 KB total).
511
+ additionalProperties: { type: string }
512
+
513
+ LineItem:
514
+ type: object
515
+ required: [name, price]
516
+ properties:
517
+ name: { type: string }
518
+ quantity: { type: integer, minimum: 1, default: 1 }
519
+ price: { type: number }
520
+
521
+ CreateTransactionRequest:
522
+ type: object
523
+ required: [crypto, amount]
524
+ properties:
525
+ crypto: { $ref: "#/components/schemas/CryptoSymbol" }
526
+ amount: { type: number, description: "Fiat amount (≥ 1.00 USD equivalent)." }
527
+ currency_fiat: { $ref: "#/components/schemas/FiatCurrency" }
528
+ metadata: { $ref: "#/components/schemas/Metadata" }
529
+ customer_email: { type: [string, "null"], format: email }
530
+ success_url: { type: [string, "null"], format: uri }
531
+ cancel_url: { type: [string, "null"], format: uri }
532
+
533
+ CreateDetailedTransactionRequest:
534
+ type: object
535
+ required: [crypto, items, order_id]
536
+ properties:
537
+ crypto: { $ref: "#/components/schemas/CryptoSymbol" }
538
+ currency_fiat: { $ref: "#/components/schemas/FiatCurrency" }
539
+ items:
540
+ type: array
541
+ minItems: 1
542
+ items: { $ref: "#/components/schemas/LineItem" }
543
+ order_id: { type: string }
544
+ metadata: { $ref: "#/components/schemas/Metadata" }
545
+ customer_email: { type: [string, "null"], format: email }
546
+ success_url: { type: [string, "null"], format: uri }
547
+ cancel_url: { type: [string, "null"], format: uri }
548
+
549
+ PaymentSplit:
550
+ type: object
551
+ description: One on-chain payment toward the transaction.
552
+ properties:
553
+ txid: { type: string, description: "On-chain transaction hash (blockchain id, NOT the GriffNode transaction_id)." }
554
+ amount_crypto: { type: number }
555
+ confirmations: { type: integer }
556
+ status: { type: string }
557
+ detected_at: { type: string, format: date-time }
558
+ confirmed_at: { type: [string, "null"], format: date-time }
559
+
560
+ Transaction:
561
+ type: object
562
+ description: >
563
+ The canonical, curated public view of a transaction (same shape from
564
+ create, get and list). Internal fields (capability tokens, client_ip,
565
+ address_index, derivation/db internals) are deliberately NOT exposed.
566
+ required: [transaction_id, status, crypto, amount_fiat, amount_usd, currency_fiat, created_at, expires_at]
567
+ properties:
568
+ transaction_id: { type: string, examples: ["MTX-A1B2C3D4"] }
569
+ status: { $ref: "#/components/schemas/TransactionStatus" }
570
+ type: { type: string, enum: [basic, detailed] }
571
+ crypto: { $ref: "#/components/schemas/CryptoSymbol" }
572
+ deposit_address: { type: string }
573
+ amount_crypto: { type: number }
574
+ amount_fiat: { type: number }
575
+ amount_usd: { type: number }
576
+ amount_paid: { type: number }
577
+ amount_remaining: { type: number }
578
+ currency_fiat: { $ref: "#/components/schemas/FiatCurrency" }
579
+ fiat_to_usd_rate: { type: number }
580
+ exchange_rate: { type: number, description: "USD per unit of crypto, locked at creation." }
581
+ confirmations_required: { type: integer }
582
+ payment_url: { type: string, format: uri }
583
+ order_id: { type: [string, "null"] }
584
+ customer_email: { type: [string, "null"] }
585
+ items:
586
+ type: [array, "null"]
587
+ items: { $ref: "#/components/schemas/LineItem" }
588
+ payments:
589
+ type: array
590
+ description: On-chain payments detected toward this transaction.
591
+ items: { $ref: "#/components/schemas/PaymentSplit" }
592
+ metadata: { $ref: "#/components/schemas/Metadata" }
593
+ success_url: { type: [string, "null"] }
594
+ cancel_url: { type: [string, "null"] }
595
+ created_at: { type: string, format: date-time }
596
+ updated_at: { type: string, format: date-time }
597
+ expires_at: { type: string, format: date-time }
598
+
599
+ TransactionEnvelope:
600
+ type: object
601
+ required: [success, data]
602
+ properties:
603
+ success: { const: true }
604
+ data: { $ref: "#/components/schemas/Transaction" }
605
+
606
+ Cryptocurrency:
607
+ type: object
608
+ properties:
609
+ symbol: { $ref: "#/components/schemas/CryptoSymbol" }
610
+ name: { type: string }
611
+ blockchain: { type: string, examples: ["bitcoin", "ethereum"] }
612
+ network: { type: string, examples: ["mainnet"] }
613
+ type: { type: string, enum: [coin, token] }
614
+
615
+ Account:
616
+ type: object
617
+ properties:
618
+ plan:
619
+ type: object
620
+ properties:
621
+ tier: { $ref: "#/components/schemas/PlanTier" }
622
+ billing_cycle: { type: string }
623
+ auto_renewal: { type: boolean }
624
+ expires_at: { type: [string, "null"], format: date-time }
625
+ usage:
626
+ type: object
627
+ properties:
628
+ monthly_transactions_used: { type: integer }
629
+ monthly_transaction_limit: { type: [integer, "null"], description: "null = unlimited." }
630
+ overage_cost_per_tx: { type: number }
631
+ limits:
632
+ type: object
633
+ properties:
634
+ api_rate_limit_per_minute: { type: integer }
635
+ api_rate_limit_per_hour: { type: integer }
636
+ webhook_support: { type: boolean }
637
+
638
+ Plan:
639
+ type: object
640
+ properties:
641
+ tier: { $ref: "#/components/schemas/PlanTier" }
642
+ monthly_price_usd: { type: [number, "null"] }
643
+ yearly_price_usd: { type: [number, "null"] }
644
+ monthly_transaction_limit: { type: [integer, "null"] }
645
+ overage_cost_per_tx: { type: number }
646
+ webhook_support: { type: boolean }
647
+ webhook_limit: { type: [integer, "null"] }
648
+ ip_whitelist: { type: boolean }
649
+ api_rate_limit_per_minute: { type: integer }
650
+ api_rate_limit_per_hour: { type: integer }
651
+ custom_pricing: { type: boolean }
652
+
653
+ Balance:
654
+ type: object
655
+ properties:
656
+ currency: { type: string, examples: ["USD"] }
657
+ available_balance: { type: number }
658
+ pending_balance: { type: number }
659
+ updated_at: { type: string, format: date-time }
660
+
661
+ Invoice:
662
+ type: object
663
+ properties:
664
+ transaction_id: { type: string, examples: ["STX-..."] }
665
+ purchase_type: { type: string, examples: ["plan_purchase", "topup"] }
666
+ amount_fiat: { type: number }
667
+ currency_fiat: { $ref: "#/components/schemas/FiatCurrency" }
668
+ status: { type: string }
669
+ crypto_symbol: { $ref: "#/components/schemas/CryptoSymbol" }
670
+ created_at: { type: string, format: date-time }
671
+ confirmed_at: { type: [string, "null"], format: date-time }
672
+ expires_at: { type: string, format: date-time }
673
+
674
+ Pagination:
675
+ type: object
676
+ required: [limit, offset, total]
677
+ properties:
678
+ limit: { type: integer }
679
+ offset: { type: integer }
680
+ total: { type: integer }
681
+
682
+ WebhookPayload:
683
+ type: object
684
+ description: >
685
+ Payment-event payload. AMOUNTS ARE STRINGS (e.g. "100.00", "0.00109462")
686
+ to preserve decimal precision — parse before arithmetic.
687
+ required: [event, timestamp, transaction_id, status]
688
+ properties:
689
+ event:
690
+ type: string
691
+ enum:
692
+ - payment.created
693
+ - payment.pending
694
+ - payment.completed
695
+ - payment.overpaid
696
+ - payment.partial
697
+ - payment.expired
698
+ - payment.failed
699
+ timestamp: { type: string, format: date-time }
700
+ transaction_id: { type: string }
701
+ status: { $ref: "#/components/schemas/TransactionStatus" }
702
+ currency_crypto: { $ref: "#/components/schemas/CryptoSymbol" }
703
+ currency_fiat: { $ref: "#/components/schemas/FiatCurrency" }
704
+ amount_fiat: { type: string, description: "Decimal string." }
705
+ amount_usd: { type: string, description: "Decimal string." }
706
+ amount_crypto: { type: string, description: "Decimal string." }
707
+ order_id: { type: [string, "null"] }
708
+ receipt_url: { type: [string, "null"], format: uri, description: "Present on completed/overpaid." }
709
+ metadata: { $ref: "#/components/schemas/Metadata" }
710
+
711
+ Error:
712
+ type: object
713
+ required: [success, error, message]
714
+ properties:
715
+ success: { const: false }
716
+ error: { type: string, description: "Machine-readable code.", examples: ["AMOUNT_TOO_LOW"] }
717
+ message: { type: string, description: "Human-readable description." }
718
+
719
+ headers:
720
+ X-RateLimit-Limit:
721
+ description: Requests allowed per minute for this API key's plan tier.
722
+ schema: { type: integer }
723
+ X-RateLimit-Remaining:
724
+ description: Requests remaining in the current one-minute window.
725
+ schema: { type: integer }
726
+ X-RateLimit-Reset:
727
+ description: Unix time (seconds) when the current rate-limit window resets.
728
+ schema: { type: integer, format: int64 }
729
+ Retry-After:
730
+ description: Seconds to wait before retrying (sent on 429 responses).
731
+ schema: { type: integer }
732
+
733
+ responses:
734
+ BadRequest:
735
+ description: Validation error (INVALID_REQUEST, INVALID_CRYPTO, INVALID_AMOUNT, AMOUNT_TOO_LOW, INVALID_CURRENCY, INVALID_METADATA, WALLET_NOT_CONFIGURED, MISSING_ITEMS, MISSING_ORDER_ID, …).
736
+ content:
737
+ application/json:
738
+ schema: { $ref: "#/components/schemas/Error" }
739
+ Unauthorized:
740
+ description: Missing or invalid API key (UNAUTHORIZED).
741
+ content:
742
+ application/json:
743
+ schema: { $ref: "#/components/schemas/Error" }
744
+ Forbidden:
745
+ description: Key lacks permission — e.g. a publishable key on a secret-only endpoint, or plan too low (FORBIDDEN, USE_PAY_ENDPOINT, PLAN_UPGRADE_REQUIRED, NO_ACTIVE_PLAN).
746
+ content:
747
+ application/json:
748
+ schema: { $ref: "#/components/schemas/Error" }
749
+ NotFound:
750
+ description: Resource not found (TRANSACTION_NOT_FOUND).
751
+ content:
752
+ application/json:
753
+ schema: { $ref: "#/components/schemas/Error" }
754
+ InsufficientBalance:
755
+ description: Merchant platform balance too low for overage fees (INSUFFICIENT_BALANCE).
756
+ content:
757
+ application/json:
758
+ schema: { $ref: "#/components/schemas/Error" }
759
+ ActiveTransactionExists:
760
+ description: This client IP already has an active transaction (ACTIVE_TRANSACTION_EXISTS). Body includes an `existing_transaction` object.
761
+ content:
762
+ application/json:
763
+ schema:
764
+ allOf:
765
+ - { $ref: "#/components/schemas/Error" }
766
+ - type: object
767
+ properties:
768
+ existing_transaction:
769
+ type: object
770
+ properties:
771
+ transaction_id: { type: string }
772
+ status: { $ref: "#/components/schemas/TransactionStatus" }
773
+ payment_url: { type: string, format: uri }
774
+ TooManyRequests:
775
+ description: >
776
+ Rate limit or quota exceeded. `error` is `RATE_LIMIT_EXCEEDED` (per-minute request
777
+ rate — honour `Retry-After`) or `MONTHLY_LIMIT_REACHED` (the plan's monthly
778
+ transaction quota). Rate-limit headers accompany the `RATE_LIMIT_EXCEEDED` case.
779
+ headers:
780
+ X-RateLimit-Limit: { $ref: "#/components/headers/X-RateLimit-Limit" }
781
+ X-RateLimit-Remaining: { $ref: "#/components/headers/X-RateLimit-Remaining" }
782
+ X-RateLimit-Reset: { $ref: "#/components/headers/X-RateLimit-Reset" }
783
+ Retry-After: { $ref: "#/components/headers/Retry-After" }
784
+ content:
785
+ application/json:
786
+ schema: { $ref: "#/components/schemas/Error" }
787
+ ServerError:
788
+ description: Internal error (RATE_FETCH_FAILED, ADDRESS_GENERATION_FAILED, UPSTREAM_ERROR, …).
789
+ content:
790
+ application/json:
791
+ schema: { $ref: "#/components/schemas/Error" }