@stridge/openapi 0.4.0 → 0.5.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.
package/dist/openapi.json CHANGED
@@ -3,8 +3,8 @@
3
3
  "info": {
4
4
  "title": "Stridge API",
5
5
  "version": "1.0.0",
6
- "summary": "Public REST API for Stridge Universal Deposit Addresses (UDA).",
7
- "description": "The Stridge API lets you allocate **Universal Deposit Addresses (UDA)** — cross-chain deposit addresses that route any incoming deposit to a destination you control — fetch indicative cross-chain/cross-asset quotes, and track the settlements those deposits produce.\n\n## Authentication\n\nAuthenticate by sending your secret API key in the `X-API-Key` header on every request. Two endpoints are public and need no key: `GET /uda/quote` and `GET /uda/supported-assets`. All other endpoints require a key.\n\n``` X-API-Key: sk_live_8f3c2b1a9d4e5f60718293a4b5c6d7e8 ```\n\nKeep your secret key on the server. Never embed it in a browser bundle, mobile app, or any client you ship to users.\n\n## Base URLs\n\n| Environment | Base URL | | --- | --- | | Production | `https://api.stridge.com/v1` | | Sandbox | `https://api.stridge.dev/v1` |\n\nAll paths in this reference are relative to the selected base URL — e.g. `POST /uda` is `POST https://api.stridge.com/v1/uda`.\n\n## Conventions\n\nAmounts are integer strings in the asset's smallest unit (e.g. `wei`, USDC `\"1000000\"` = 1 USDC). List endpoints paginate with `limit` (default 50, max 200) and `offset`, and return a `pagination` object. Errors return a consistent JSON envelope (see the `Error` schema).\n",
6
+ "summary": "Public REST API for Stridge: Universal Deposit Addresses (UDA), vaults and payouts, gas stations, routing policies, and the sandbox test helpers.",
7
+ "description": "The Stridge API lets you allocate **Universal Deposit Addresses (UDA)** — cross-chain deposit addresses that route any incoming deposit to a destination you control — fetch indicative cross-chain/cross-asset quotes, track the settlements those deposits produce, read your vaults and send payouts from them, read your gas stations and routing policies, and, in a sandbox, simulate deposits and payouts with the test helpers.\n\n## Authentication\n\nAuthenticate by sending your secret API key in the `X-API-Key` header on every request. Two endpoints are public and need no key: `GET /uda/quote` and `GET /uda/supported-assets`. All other endpoints require a key.\n\n``` X-API-Key: sk_live_8f3c2b1a9d4e5f60718293a4b5c6d7e8 ```\n\nA missing, unknown, revoked or expired key is answered `401`. A key whose scopes do not cover the operation is answered `403` with `Insufficient permissions`; a key restricted to an IP allowlist or referrers is answered `403` from anywhere else.\n\n## Live and sandbox keys\n\nEvery key belongs to exactly one environment, and the key alone decides which. New live keys start `sk_live_`; sandbox keys start `sk_sbx_` and act on simulated chains with no real funds. Live keys issued before sandbox keys existed have no prefix and stay valid in live. The environment is a property of the key, and no header, parameter or URL changes it. Stridge reads the prefix to route your request (see Base URL); your code should not, and should read `livemode` on the response instead. A keyed request that also sends `X-Stridge-Environment` is refused with `400` and `environment header not accepted`; every keyed operation lists that `400`.\n\nEvery object a keyed request answers with carries `livemode`, so a stored payload says which environment it came from. Webhook deliveries carry `livemode` and `environment_id` in the signed envelope, and sandbox deliveries also carry `reset_epoch` (see Webhooks).\n\nThe gateway (`X-Gateway-Key`, which the Gateway Kit, its SDK and the onramp use) serves live keys only: a sandbox key is answered `403` with `Sandbox API keys cannot be used with the gateway`. While a sandbox is being reset from the dashboard, every request made with its keys is answered `409` with `environment resetting`; retry once the reset is done.\n\nKeep your secret key on the server. Never embed it in a browser bundle, mobile app, or any client you ship to users.\n\n## No step-up for API keys\n\nA request made with an API key is never asked for a step-up: no TOTP code or other second factor, on any operation, in live or in sandbox. Step-up applies only to people signed in to the Stridge dashboard, who confirm a fresh second factor before sensitive live actions such as creating a payout or creating or widening a live key. What a key may do is fixed by the scopes it was created with.\n\n## Test helpers\n\nIn a sandbox, the `/test_helpers` operations simulate what the outside world would do — a deposit to a UDA or vault, how a settlement ends, whether a payout's transaction confirms — so you can exercise your integration end to end, webhooks included. Call them with a sandbox key; a live key is answered `404` with `not a sandbox environment`. Every helper call that pulls a lever requires an `Idempotency-Key` and spends one unit of the sandbox's simulation budget (600 per hour).\n\nThe sandbox simulates Ethereum, Arbitrum, Base, BNB Smart Chain, Optimism, Polygon, Bitcoin, Solana and Tron. Tempo is live only: it is not simulated in the sandbox.\n\n## Base URL\n\nLive and sandbox keys use the same base URL, `https://api.stridge.com/v1`. The key decides where a request is served: Stridge routes a request whose `X-API-Key` starts `sk_sbx_` to your sandbox and every other request to live. Send the key unchanged in `X-API-Key`, never in `Authorization`; a key sent anywhere else reaches live. A key that reaches the other environment's API is refused with `401`, and the error names the key's environment and the fix, so a misroute never acts in the wrong environment. Switching between sandbox and live is changing the key, and nothing else.\n\nAll paths in this reference are relative to the base URL — e.g. `POST /uda` is `POST https://api.stridge.com/v1/uda`.\n\n## Idempotency\n\nEvery keyed `POST` accepts an optional `Idempotency-Key` header; the test helpers require one. Keys are scoped to your workspace and the key's environment, so every API key of one environment shares them (a retry with a rotated key still replays) and a sandbox never replays a live answer. A repeat of the same request with the same key within 24 hours is answered the first response again, refusals included, with `Idempotency-Replay: true`, and is not carried out twice. The same key sent with a different body to the same operation is answered `409` with `Idempotency key reused with different request`. Requests made without a key are never deduplicated.\n\n## Errors and rate limits\n\nErrors return one JSON envelope (see the `Error` schema): `success: false`, the HTTP status in `code`, a message in `error`, and, on the few refusals that need it, a `details` object to act on.\n\n| Status | Meaning |\n| --- | --- |\n| `400` | Malformed request, failed validation, or `environment header not accepted`. |\n| `401` | Missing or invalid key, or a key that reached the other environment's API. |\n| `403` | The key lacks the scope, IP or referrer, or a sandbox key on a live-only surface. |\n| `404` | Not found; on `/test_helpers`, `not a sandbox environment` for a live key. |\n| `409` | A conflict (duplicate `reference_id`, reused `Idempotency-Key`), or `environment resetting`. |\n| `422` | A request the operation cannot carry out. |\n| `429` | Rate limit (`Too many requests`) or the sandbox's simulation budget. |\n| `5xx` | A server error. |\n\nEvery operation is rate limited per API key (per client address on the public endpoints) over a one-minute window. Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (a Unix timestamp in seconds); a `429` adds `Retry-After` in seconds.\n\n## Webhooks\n\nStridge POSTs each event to your webhook endpoints as a signed JSON envelope (see the `webhook-event` webhook): `id`, `version`, `type`, `time`, `livemode`, `environment_id`, `reset_epoch` on sandbox deliveries only, and the event's `payload`. The `webhook-signature` header is the hex HMAC-SHA256 of `webhook-timestamp + \".\" + raw body` with the endpoint's signing secret; verify it over the raw bytes, before parsing.\n\n## Conventions\n\nAmounts are integer strings in the asset's smallest unit (e.g. `wei`, USDC `\"1000000\"` = 1 USDC) unless an operation says otherwise; payouts take and return decimal amounts (e.g. `\"100.50\"`). UDA and settlement lists paginate with `limit` and `offset` and return a `pagination` object; payout, gas-station, routing-policy and simulation lists paginate with `page` and `limit` and return page-based `pagination`.\n\nNetworks are identified by SLIP-44-style `network_id` (e.g. `60` Ethereum, `195` Tron, `501` Solana, `0` Bitcoin, `4217` Tempo) and, where a slug is asked for, by `network_symbol` (e.g. `ethereum`, `tron`, `tempo`). Tempo is an EVM network with no native gas coin: its fees are paid in a USD stablecoin, and a Tempo payout of USDC, USDT or OUSD pays its fee out of the amount sent.\n",
8
8
  "contact": {
9
9
  "name": "Stridge Developer Support",
10
10
  "url": "https://stridge.com"
@@ -13,11 +13,7 @@
13
13
  "servers": [
14
14
  {
15
15
  "url": "https://api.stridge.com/v1",
16
- "description": "Production"
17
- },
18
- {
19
- "url": "https://api.stridge.dev/v1",
20
- "description": "Sandbox"
16
+ "description": "Production (live and sandbox keys)"
21
17
  }
22
18
  ],
23
19
  "tags": [
@@ -36,6 +32,14 @@
36
32
  {
37
33
  "name": "Gas Stations",
38
34
  "description": "Native-gas wallets that fund payout transactions. Stridge refills a vault wallet from a gas station automatically before a transfer when it is short on gas.\n"
35
+ },
36
+ {
37
+ "name": "Test Helpers",
38
+ "description": "**Sandbox only.** Levers that make the simulated world do what a real one would: pay a UDA or a vault, decide how a settlement ends, and choose whether a payout's transaction confirms, fails or is held for you to release or fail. Each call answers a simulation you can read back and wait on; the deposits, settlements, payouts and webhooks it causes are ordinary sandbox objects. Call them with a sandbox key (`sk_sbx_`); a live key is answered `404` with `not a sandbox environment`. Every operation here is marked `x-sandbox-only`.\n"
39
+ },
40
+ {
41
+ "name": "Webhooks",
42
+ "description": "The signed envelope Stridge POSTs to your webhook endpoints for every event, in live and in sandbox. Endpoints are managed in the dashboard.\n"
39
43
  }
40
44
  ],
41
45
  "paths": {
@@ -121,6 +125,15 @@
121
125
  "401": {
122
126
  "$ref": "#/components/responses/Unauthorized"
123
127
  },
128
+ "403": {
129
+ "$ref": "#/components/responses/Forbidden"
130
+ },
131
+ "409": {
132
+ "$ref": "#/components/responses/EnvironmentResetting"
133
+ },
134
+ "429": {
135
+ "$ref": "#/components/responses/TooManyRequests"
136
+ },
124
137
  "500": {
125
138
  "$ref": "#/components/responses/ServerError"
126
139
  }
@@ -138,6 +151,11 @@
138
151
  "ApiKeyAuth": []
139
152
  }
140
153
  ],
154
+ "parameters": [
155
+ {
156
+ "$ref": "#/components/parameters/IdempotencyKeyOptional"
157
+ }
158
+ ],
141
159
  "requestBody": {
142
160
  "required": true,
143
161
  "description": "UDA address configuration.",
@@ -175,6 +193,15 @@
175
193
  },
176
194
  "401": {
177
195
  "$ref": "#/components/responses/Unauthorized"
196
+ },
197
+ "403": {
198
+ "$ref": "#/components/responses/Forbidden"
199
+ },
200
+ "409": {
201
+ "$ref": "#/components/responses/EnvironmentResetting"
202
+ },
203
+ "429": {
204
+ "$ref": "#/components/responses/TooManyRequests"
178
205
  }
179
206
  }
180
207
  }
@@ -241,9 +268,11 @@
241
268
  {
242
269
  "name": "from_address",
243
270
  "in": "query",
244
- "description": "Source (signer) EOA; required for an executable quote.",
271
+ "required": true,
272
+ "description": "Source (signer) address. Required: without it the quote providers cannot price the route and the API answers `503`.\n",
245
273
  "schema": {
246
- "type": "string"
274
+ "type": "string",
275
+ "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
247
276
  }
248
277
  },
249
278
  {
@@ -281,6 +310,9 @@
281
310
  "400": {
282
311
  "$ref": "#/components/responses/BadRequest"
283
312
  },
313
+ "429": {
314
+ "$ref": "#/components/responses/TooManyRequests"
315
+ },
284
316
  "500": {
285
317
  "$ref": "#/components/responses/ServerError"
286
318
  }
@@ -306,6 +338,9 @@
306
338
  }
307
339
  }
308
340
  },
341
+ "429": {
342
+ "$ref": "#/components/responses/TooManyRequests"
343
+ },
309
344
  "500": {
310
345
  "$ref": "#/components/responses/ServerError"
311
346
  }
@@ -353,9 +388,21 @@
353
388
  }
354
389
  }
355
390
  },
391
+ "400": {
392
+ "$ref": "#/components/responses/BadRequest"
393
+ },
356
394
  "401": {
357
395
  "$ref": "#/components/responses/Unauthorized"
358
396
  },
397
+ "403": {
398
+ "$ref": "#/components/responses/Forbidden"
399
+ },
400
+ "409": {
401
+ "$ref": "#/components/responses/EnvironmentResetting"
402
+ },
403
+ "429": {
404
+ "$ref": "#/components/responses/TooManyRequests"
405
+ },
359
406
  "500": {
360
407
  "$ref": "#/components/responses/ServerError"
361
408
  }
@@ -397,11 +444,23 @@
397
444
  }
398
445
  }
399
446
  },
447
+ "400": {
448
+ "$ref": "#/components/responses/BadRequest"
449
+ },
400
450
  "401": {
401
451
  "$ref": "#/components/responses/Unauthorized"
402
452
  },
453
+ "403": {
454
+ "$ref": "#/components/responses/Forbidden"
455
+ },
403
456
  "404": {
404
457
  "$ref": "#/components/responses/NotFound"
458
+ },
459
+ "409": {
460
+ "$ref": "#/components/responses/EnvironmentResetting"
461
+ },
462
+ "429": {
463
+ "$ref": "#/components/responses/TooManyRequests"
405
464
  }
406
465
  }
407
466
  }
@@ -447,6 +506,15 @@
447
506
  "401": {
448
507
  "$ref": "#/components/responses/Unauthorized"
449
508
  },
509
+ "403": {
510
+ "$ref": "#/components/responses/Forbidden"
511
+ },
512
+ "409": {
513
+ "$ref": "#/components/responses/EnvironmentResetting"
514
+ },
515
+ "429": {
516
+ "$ref": "#/components/responses/TooManyRequests"
517
+ },
450
518
  "500": {
451
519
  "$ref": "#/components/responses/ServerError"
452
520
  }
@@ -477,9 +545,21 @@
477
545
  }
478
546
  }
479
547
  },
548
+ "400": {
549
+ "$ref": "#/components/responses/BadRequest"
550
+ },
480
551
  "401": {
481
552
  "$ref": "#/components/responses/Unauthorized"
482
553
  },
554
+ "403": {
555
+ "$ref": "#/components/responses/Forbidden"
556
+ },
557
+ "409": {
558
+ "$ref": "#/components/responses/EnvironmentResetting"
559
+ },
560
+ "429": {
561
+ "$ref": "#/components/responses/TooManyRequests"
562
+ },
483
563
  "500": {
484
564
  "$ref": "#/components/responses/ServerError"
485
565
  }
@@ -521,9 +601,18 @@
521
601
  "401": {
522
602
  "$ref": "#/components/responses/Unauthorized"
523
603
  },
604
+ "403": {
605
+ "$ref": "#/components/responses/Forbidden"
606
+ },
524
607
  "404": {
525
608
  "$ref": "#/components/responses/NotFound"
526
609
  },
610
+ "409": {
611
+ "$ref": "#/components/responses/EnvironmentResetting"
612
+ },
613
+ "429": {
614
+ "$ref": "#/components/responses/TooManyRequests"
615
+ },
527
616
  "500": {
528
617
  "$ref": "#/components/responses/ServerError"
529
618
  }
@@ -551,7 +640,7 @@
551
640
  "name": "network",
552
641
  "in": "path",
553
642
  "required": true,
554
- "description": "Network name (e.g. `ethereum`, `tron`).",
643
+ "description": "Network slug (e.g. `ethereum`, `tron`, `tempo`).",
555
644
  "schema": {
556
645
  "type": "string"
557
646
  }
@@ -574,9 +663,18 @@
574
663
  "401": {
575
664
  "$ref": "#/components/responses/Unauthorized"
576
665
  },
666
+ "403": {
667
+ "$ref": "#/components/responses/Forbidden"
668
+ },
577
669
  "404": {
578
670
  "$ref": "#/components/responses/NotFound"
579
671
  },
672
+ "409": {
673
+ "$ref": "#/components/responses/EnvironmentResetting"
674
+ },
675
+ "429": {
676
+ "$ref": "#/components/responses/TooManyRequests"
677
+ },
580
678
  "500": {
581
679
  "$ref": "#/components/responses/ServerError"
582
680
  }
@@ -634,9 +732,18 @@
634
732
  "401": {
635
733
  "$ref": "#/components/responses/Unauthorized"
636
734
  },
735
+ "403": {
736
+ "$ref": "#/components/responses/Forbidden"
737
+ },
637
738
  "404": {
638
739
  "$ref": "#/components/responses/NotFound"
639
740
  },
741
+ "409": {
742
+ "$ref": "#/components/responses/EnvironmentResetting"
743
+ },
744
+ "429": {
745
+ "$ref": "#/components/responses/TooManyRequests"
746
+ },
640
747
  "500": {
641
748
  "$ref": "#/components/responses/ServerError"
642
749
  }
@@ -686,9 +793,18 @@
686
793
  "401": {
687
794
  "$ref": "#/components/responses/Unauthorized"
688
795
  },
796
+ "403": {
797
+ "$ref": "#/components/responses/Forbidden"
798
+ },
689
799
  "404": {
690
800
  "$ref": "#/components/responses/NotFound"
691
801
  },
802
+ "409": {
803
+ "$ref": "#/components/responses/EnvironmentResetting"
804
+ },
805
+ "429": {
806
+ "$ref": "#/components/responses/TooManyRequests"
807
+ },
692
808
  "500": {
693
809
  "$ref": "#/components/responses/ServerError"
694
810
  }
@@ -737,7 +853,7 @@
737
853
  }
738
854
  },
739
855
  "400": {
740
- "description": "Missing `vault_id` or invalid filters.",
856
+ "description": "Missing `vault_id` or invalid filters, or the request also sent `X-Stridge-Environment`.\n",
741
857
  "content": {
742
858
  "application/json": {
743
859
  "schema": {
@@ -749,6 +865,15 @@
749
865
  "401": {
750
866
  "$ref": "#/components/responses/Unauthorized"
751
867
  },
868
+ "403": {
869
+ "$ref": "#/components/responses/Forbidden"
870
+ },
871
+ "409": {
872
+ "$ref": "#/components/responses/EnvironmentResetting"
873
+ },
874
+ "429": {
875
+ "$ref": "#/components/responses/TooManyRequests"
876
+ },
752
877
  "500": {
753
878
  "$ref": "#/components/responses/ServerError"
754
879
  }
@@ -766,6 +891,11 @@
766
891
  "ApiKeyAuth": []
767
892
  }
768
893
  ],
894
+ "parameters": [
895
+ {
896
+ "$ref": "#/components/parameters/IdempotencyKeyOptional"
897
+ }
898
+ ],
769
899
  "requestBody": {
770
900
  "required": true,
771
901
  "description": "Create payout request.",
@@ -789,7 +919,7 @@
789
919
  }
790
920
  },
791
921
  "400": {
792
- "description": "Validation error or insufficient balance.",
922
+ "description": "Validation error or insufficient balance, or the request also sent `X-Stridge-Environment`.\n",
793
923
  "content": {
794
924
  "application/json": {
795
925
  "schema": {
@@ -801,11 +931,38 @@
801
931
  "401": {
802
932
  "$ref": "#/components/responses/Unauthorized"
803
933
  },
934
+ "403": {
935
+ "$ref": "#/components/responses/Forbidden"
936
+ },
804
937
  "404": {
805
938
  "$ref": "#/components/responses/NotFound"
806
939
  },
807
940
  "409": {
808
- "description": "Duplicate `reference_id`.",
941
+ "description": "The vault already has a payout with this `reference_id`: the body is that payout, at `409`, with `success: true` and `Payout already exists for this reference_id`. Also `duplicate reference_id for this vault` (an error body) when the existing payout cannot be read, or `environment resetting` while the key's sandbox is being reset.\n",
942
+ "content": {
943
+ "application/json": {
944
+ "schema": {
945
+ "oneOf": [
946
+ {
947
+ "allOf": [
948
+ {
949
+ "$ref": "#/components/schemas/SuccessEnvelope"
950
+ },
951
+ {
952
+ "$ref": "#/components/schemas/PayoutResponse"
953
+ }
954
+ ]
955
+ },
956
+ {
957
+ "$ref": "#/components/schemas/Error"
958
+ }
959
+ ]
960
+ }
961
+ }
962
+ }
963
+ },
964
+ "422": {
965
+ "description": "The body failed validation (a missing required field, an invalid `to_address` for the network, or an amount with more decimals than the asset has).\n",
809
966
  "content": {
810
967
  "application/json": {
811
968
  "schema": {
@@ -814,6 +971,9 @@
814
971
  }
815
972
  }
816
973
  },
974
+ "429": {
975
+ "$ref": "#/components/responses/TooManyRequests"
976
+ },
817
977
  "500": {
818
978
  "$ref": "#/components/responses/ServerError"
819
979
  }
@@ -862,7 +1022,7 @@
862
1022
  }
863
1023
  },
864
1024
  "400": {
865
- "description": "Missing `vault_id` or invalid filters.",
1025
+ "description": "Missing `vault_id` or invalid filters, or the request also sent `X-Stridge-Environment`.\n",
866
1026
  "content": {
867
1027
  "application/json": {
868
1028
  "schema": {
@@ -874,6 +1034,15 @@
874
1034
  "401": {
875
1035
  "$ref": "#/components/responses/Unauthorized"
876
1036
  },
1037
+ "403": {
1038
+ "$ref": "#/components/responses/Forbidden"
1039
+ },
1040
+ "409": {
1041
+ "$ref": "#/components/responses/EnvironmentResetting"
1042
+ },
1043
+ "429": {
1044
+ "$ref": "#/components/responses/TooManyRequests"
1045
+ },
877
1046
  "500": {
878
1047
  "$ref": "#/components/responses/ServerError"
879
1048
  }
@@ -915,12 +1084,24 @@
915
1084
  }
916
1085
  }
917
1086
  },
1087
+ "400": {
1088
+ "$ref": "#/components/responses/BadRequest"
1089
+ },
918
1090
  "401": {
919
1091
  "$ref": "#/components/responses/Unauthorized"
920
1092
  },
1093
+ "403": {
1094
+ "$ref": "#/components/responses/Forbidden"
1095
+ },
921
1096
  "404": {
922
1097
  "$ref": "#/components/responses/NotFound"
923
1098
  },
1099
+ "409": {
1100
+ "$ref": "#/components/responses/EnvironmentResetting"
1101
+ },
1102
+ "429": {
1103
+ "$ref": "#/components/responses/TooManyRequests"
1104
+ },
924
1105
  "500": {
925
1106
  "$ref": "#/components/responses/ServerError"
926
1107
  }
@@ -941,6 +1122,9 @@
941
1122
  }
942
1123
  ],
943
1124
  "parameters": [
1125
+ {
1126
+ "$ref": "#/components/parameters/IdempotencyKeyOptional"
1127
+ },
944
1128
  {
945
1129
  "name": "id",
946
1130
  "in": "path",
@@ -962,14 +1146,20 @@
962
1146
  }
963
1147
  }
964
1148
  },
1149
+ "400": {
1150
+ "$ref": "#/components/responses/BadRequest"
1151
+ },
965
1152
  "401": {
966
1153
  "$ref": "#/components/responses/Unauthorized"
967
1154
  },
1155
+ "403": {
1156
+ "$ref": "#/components/responses/Forbidden"
1157
+ },
968
1158
  "404": {
969
1159
  "$ref": "#/components/responses/NotFound"
970
1160
  },
971
1161
  "409": {
972
- "description": "The payout is already final or past pending.",
1162
+ "description": "`payout is already in a final state`, or `environment resetting` while the key's sandbox is being reset.\n",
973
1163
  "content": {
974
1164
  "application/json": {
975
1165
  "schema": {
@@ -978,6 +1168,9 @@
978
1168
  }
979
1169
  }
980
1170
  },
1171
+ "429": {
1172
+ "$ref": "#/components/responses/TooManyRequests"
1173
+ },
981
1174
  "500": {
982
1175
  "$ref": "#/components/responses/ServerError"
983
1176
  }
@@ -1025,9 +1218,18 @@
1025
1218
  "401": {
1026
1219
  "$ref": "#/components/responses/Unauthorized"
1027
1220
  },
1221
+ "403": {
1222
+ "$ref": "#/components/responses/Forbidden"
1223
+ },
1028
1224
  "404": {
1029
1225
  "$ref": "#/components/responses/NotFound"
1030
1226
  },
1227
+ "409": {
1228
+ "$ref": "#/components/responses/EnvironmentResetting"
1229
+ },
1230
+ "429": {
1231
+ "$ref": "#/components/responses/TooManyRequests"
1232
+ },
1031
1233
  "500": {
1032
1234
  "$ref": "#/components/responses/ServerError"
1033
1235
  }
@@ -1075,9 +1277,18 @@
1075
1277
  "401": {
1076
1278
  "$ref": "#/components/responses/Unauthorized"
1077
1279
  },
1280
+ "403": {
1281
+ "$ref": "#/components/responses/Forbidden"
1282
+ },
1078
1283
  "404": {
1079
1284
  "$ref": "#/components/responses/NotFound"
1080
1285
  },
1286
+ "409": {
1287
+ "$ref": "#/components/responses/EnvironmentResetting"
1288
+ },
1289
+ "429": {
1290
+ "$ref": "#/components/responses/TooManyRequests"
1291
+ },
1081
1292
  "500": {
1082
1293
  "$ref": "#/components/responses/ServerError"
1083
1294
  }
@@ -1155,9 +1366,18 @@
1155
1366
  "401": {
1156
1367
  "$ref": "#/components/responses/Unauthorized"
1157
1368
  },
1369
+ "403": {
1370
+ "$ref": "#/components/responses/Forbidden"
1371
+ },
1158
1372
  "404": {
1159
1373
  "$ref": "#/components/responses/NotFound"
1160
1374
  },
1375
+ "409": {
1376
+ "$ref": "#/components/responses/EnvironmentResetting"
1377
+ },
1378
+ "429": {
1379
+ "$ref": "#/components/responses/TooManyRequests"
1380
+ },
1161
1381
  "500": {
1162
1382
  "$ref": "#/components/responses/ServerError"
1163
1383
  }
@@ -1171,7 +1391,7 @@
1171
1391
  "Routing Policies"
1172
1392
  ],
1173
1393
  "summary": "List routing policies",
1174
- "description": "List the routing policies configured for your tenant, including the default policy. Read-only — create and edit policies from the dashboard.\n",
1394
+ "description": "List the routing policies configured for your tenant, including the default policy, newest first. Read-only — create and edit policies from the dashboard.\n",
1175
1395
  "security": [
1176
1396
  {
1177
1397
  "ApiKeyAuth": []
@@ -1179,10 +1399,18 @@
1179
1399
  ],
1180
1400
  "parameters": [
1181
1401
  {
1182
- "$ref": "#/components/parameters/LimitParam"
1402
+ "$ref": "#/components/parameters/PageParam"
1183
1403
  },
1184
1404
  {
1185
- "$ref": "#/components/parameters/OffsetParam"
1405
+ "name": "limit",
1406
+ "in": "query",
1407
+ "description": "Items per page (default 10, max 100).",
1408
+ "schema": {
1409
+ "type": "integer",
1410
+ "default": 10,
1411
+ "minimum": 1,
1412
+ "maximum": 100
1413
+ }
1186
1414
  }
1187
1415
  ],
1188
1416
  "responses": {
@@ -1202,6 +1430,15 @@
1202
1430
  "401": {
1203
1431
  "$ref": "#/components/responses/Unauthorized"
1204
1432
  },
1433
+ "403": {
1434
+ "$ref": "#/components/responses/Forbidden"
1435
+ },
1436
+ "409": {
1437
+ "$ref": "#/components/responses/EnvironmentResetting"
1438
+ },
1439
+ "429": {
1440
+ "$ref": "#/components/responses/TooManyRequests"
1441
+ },
1205
1442
  "500": {
1206
1443
  "$ref": "#/components/responses/ServerError"
1207
1444
  }
@@ -1238,134 +1475,1114 @@
1238
1475
  "content": {
1239
1476
  "application/json": {
1240
1477
  "schema": {
1241
- "$ref": "#/components/schemas/RoutingPolicy"
1478
+ "$ref": "#/components/schemas/RoutingPolicyResponse"
1242
1479
  }
1243
1480
  }
1244
1481
  }
1245
1482
  },
1483
+ "400": {
1484
+ "$ref": "#/components/responses/BadRequest"
1485
+ },
1246
1486
  "401": {
1247
1487
  "$ref": "#/components/responses/Unauthorized"
1248
1488
  },
1489
+ "403": {
1490
+ "$ref": "#/components/responses/Forbidden"
1491
+ },
1249
1492
  "404": {
1250
1493
  "$ref": "#/components/responses/NotFound"
1251
1494
  },
1495
+ "409": {
1496
+ "$ref": "#/components/responses/EnvironmentResetting"
1497
+ },
1498
+ "429": {
1499
+ "$ref": "#/components/responses/TooManyRequests"
1500
+ },
1252
1501
  "500": {
1253
1502
  "$ref": "#/components/responses/ServerError"
1254
1503
  }
1255
1504
  }
1256
1505
  }
1257
- }
1258
- },
1259
- "components": {
1260
- "securitySchemes": {
1261
- "ApiKeyAuth": {
1262
- "type": "apiKey",
1263
- "in": "header",
1264
- "name": "X-API-Key",
1265
- "description": "Secret API key for programmatic access. Send it on every authenticated request. Keep it server-side; never ship it in a client bundle.\n"
1266
- }
1267
- },
1268
- "parameters": {
1269
- "LimitParam": {
1270
- "name": "limit",
1271
- "in": "query",
1272
- "description": "Page size (default 50, max 200).",
1273
- "schema": {
1274
- "type": "integer",
1275
- "default": 50,
1276
- "minimum": 1,
1277
- "maximum": 200
1278
- }
1279
- },
1280
- "OffsetParam": {
1281
- "name": "offset",
1282
- "in": "query",
1283
- "description": "Number of items to skip (default 0).",
1284
- "schema": {
1285
- "type": "integer",
1286
- "default": 0,
1287
- "minimum": 0
1288
- }
1289
- },
1290
- "VaultIdParam": {
1291
- "name": "vault_id",
1292
- "in": "path",
1293
- "required": true,
1294
- "description": "Vault id.",
1295
- "schema": {
1296
- "type": "string"
1297
- }
1298
- },
1299
- "PageParam": {
1300
- "name": "page",
1301
- "in": "query",
1302
- "description": "Page number (default 1).",
1303
- "schema": {
1304
- "type": "integer",
1305
- "default": 1,
1306
- "minimum": 1
1307
- }
1308
- },
1309
- "PageSizeParam": {
1310
- "name": "limit",
1311
- "in": "query",
1312
- "description": "Items per page (default 20, max 100).",
1313
- "schema": {
1314
- "type": "integer",
1315
- "default": 20,
1316
- "minimum": 1,
1317
- "maximum": 100
1318
- }
1319
- }
1320
1506
  },
1321
- "responses": {
1322
- "BadRequest": {
1323
- "description": "The request was malformed or failed validation.",
1324
- "content": {
1325
- "application/json": {
1326
- "schema": {
1327
- "$ref": "#/components/schemas/Error"
1328
- },
1329
- "example": {
1330
- "success": false,
1331
- "code": 400,
1332
- "error": "invalid request"
1333
- }
1507
+ "/test_helpers/options": {
1508
+ "get": {
1509
+ "operationId": "get-test-helper-options",
1510
+ "tags": [
1511
+ "Test Helpers"
1512
+ ],
1513
+ "x-sandbox-only": true,
1514
+ "summary": "Read what the test helpers accept",
1515
+ "description": "Everything a test helper call in your sandbox may name: the UDAs and vaults you can deposit to, the simulated networks with their assets and levers, the deposit presets, the settlement and payout outcomes, the webhook event types a simulation can cause, how fast each simulated network moves, and how much of the simulation budget is left. Pass `uda_id` to narrow the UDA list to that one UDA and expand each preset into the concrete `facts` (network, asset and amount) it would send to it. Spends no simulation budget and needs no `Idempotency-Key`. Sandbox keys only; a live key is answered `404` with `not a sandbox environment`.\n",
1516
+ "security": [
1517
+ {
1518
+ "ApiKeyAuth": []
1334
1519
  }
1335
- }
1336
- },
1337
- "Unauthorized": {
1338
- "description": "The `X-API-Key` header is missing or invalid.",
1339
- "content": {
1340
- "application/json": {
1520
+ ],
1521
+ "parameters": [
1522
+ {
1523
+ "name": "uda_id",
1524
+ "in": "query",
1525
+ "description": "A UDA in your sandbox. The `udas` list then holds only it, and each preset carries its `facts` for it (`null` where the UDA leaves nothing to express the preset with).\n",
1341
1526
  "schema": {
1342
- "$ref": "#/components/schemas/Error"
1343
- },
1344
- "example": {
1345
- "success": false,
1346
- "code": 401,
1347
- "error": "unauthenticated"
1527
+ "type": "string"
1348
1528
  }
1349
1529
  }
1350
- }
1351
- },
1352
- "NotFound": {
1353
- "description": "The requested resource does not exist.",
1354
- "content": {
1355
- "application/json": {
1356
- "schema": {
1357
- "$ref": "#/components/schemas/Error"
1358
- },
1359
- "example": {
1360
- "success": false,
1361
- "code": 404,
1362
- "error": "not found"
1530
+ ],
1531
+ "responses": {
1532
+ "200": {
1533
+ "description": "What the test helpers accept in your sandbox.",
1534
+ "content": {
1535
+ "application/json": {
1536
+ "schema": {
1537
+ "$ref": "#/components/schemas/TestHelperOptionsResponse"
1538
+ }
1539
+ }
1363
1540
  }
1364
- }
1365
- }
1366
- },
1367
- "ServerError": {
1368
- "description": "An unexpected error occurred on the server.",
1541
+ },
1542
+ "400": {
1543
+ "$ref": "#/components/responses/BadRequest"
1544
+ },
1545
+ "401": {
1546
+ "$ref": "#/components/responses/Unauthorized"
1547
+ },
1548
+ "404": {
1549
+ "description": "`not a sandbox environment` (the key is a live key) or `uda not found` (the `uda_id` is not in your sandbox).\n",
1550
+ "content": {
1551
+ "application/json": {
1552
+ "schema": {
1553
+ "$ref": "#/components/schemas/Error"
1554
+ },
1555
+ "example": {
1556
+ "success": false,
1557
+ "code": 404,
1558
+ "error": "not a sandbox environment"
1559
+ }
1560
+ }
1561
+ }
1562
+ },
1563
+ "409": {
1564
+ "$ref": "#/components/responses/SandboxBusy"
1565
+ },
1566
+ "429": {
1567
+ "$ref": "#/components/responses/TooManyRequests"
1568
+ },
1569
+ "500": {
1570
+ "$ref": "#/components/responses/ServerError"
1571
+ }
1572
+ }
1573
+ }
1574
+ },
1575
+ "/test_helpers/uda/deposits": {
1576
+ "post": {
1577
+ "operationId": "simulate-uda-deposit",
1578
+ "tags": [
1579
+ "Test Helpers"
1580
+ ],
1581
+ "x-sandbox-only": true,
1582
+ "summary": "Simulate a deposit to a UDA",
1583
+ "description": "Send a simulated payment to one of your sandbox UDAs and get the simulation back at once. The deposit reaches Stridge through the same observer as a real one, so it produces the deposit, the settlement and the webhooks a real deposit would. Choose how the settlement ends with `settlement`, or name a `preset` for a shaped payment (underpaid, overpaid, wrong network, wrong asset, dust) — the preset only labels the facts you send, which `GET /test_helpers/options` expands for a UDA. A payment in an asset the UDA does not accept is recorded and settled by nothing, so it takes no `settlement` (except `success` with the `wrong_asset` preset). Requires an `Idempotency-Key`. Spends one unit of the simulation budget. Sandbox keys only.\n",
1584
+ "security": [
1585
+ {
1586
+ "ApiKeyAuth": []
1587
+ }
1588
+ ],
1589
+ "parameters": [
1590
+ {
1591
+ "$ref": "#/components/parameters/IdempotencyKeyHeader"
1592
+ }
1593
+ ],
1594
+ "requestBody": {
1595
+ "required": true,
1596
+ "content": {
1597
+ "application/json": {
1598
+ "schema": {
1599
+ "$ref": "#/components/schemas/SimulateUDADepositRequest"
1600
+ }
1601
+ }
1602
+ }
1603
+ },
1604
+ "responses": {
1605
+ "201": {
1606
+ "$ref": "#/components/responses/SimulationCreated"
1607
+ },
1608
+ "400": {
1609
+ "$ref": "#/components/responses/TestHelperBadRequest"
1610
+ },
1611
+ "401": {
1612
+ "$ref": "#/components/responses/Unauthorized"
1613
+ },
1614
+ "404": {
1615
+ "description": "`not a sandbox environment` (the key is a live key), `uda not found`, `network not simulated`, or `uda has no address on this network`.\n",
1616
+ "content": {
1617
+ "application/json": {
1618
+ "schema": {
1619
+ "$ref": "#/components/schemas/Error"
1620
+ },
1621
+ "example": {
1622
+ "success": false,
1623
+ "code": 404,
1624
+ "error": "not a sandbox environment"
1625
+ }
1626
+ }
1627
+ }
1628
+ },
1629
+ "409": {
1630
+ "$ref": "#/components/responses/SandboxBusy"
1631
+ },
1632
+ "422": {
1633
+ "description": "A request the helper cannot carry out: `uda_id, network_id, asset and amount are required`, `unknown asset` (not one the network carries), `malformed amount`, `unknown settlement outcome`, `unknown preset`, `asset not accepted by the uda` (a `settlement` chosen for an asset the UDA does not accept; `details` carries `network_id` and the `accepted_assets`), `bridge outcome needs a cross-chain route` (the bridge lever is not offered in production), or `idempotency key replayed with a different body`.\n",
1634
+ "content": {
1635
+ "application/json": {
1636
+ "schema": {
1637
+ "$ref": "#/components/schemas/Error"
1638
+ },
1639
+ "examples": {
1640
+ "malformed-amount": {
1641
+ "value": {
1642
+ "success": false,
1643
+ "code": 422,
1644
+ "error": "malformed amount"
1645
+ }
1646
+ },
1647
+ "asset-not-accepted": {
1648
+ "value": {
1649
+ "success": false,
1650
+ "code": 422,
1651
+ "error": "asset not accepted by the uda",
1652
+ "details": {
1653
+ "network_id": "60",
1654
+ "accepted_assets": [
1655
+ {
1656
+ "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
1657
+ }
1658
+ ]
1659
+ }
1660
+ }
1661
+ }
1662
+ }
1663
+ }
1664
+ }
1665
+ },
1666
+ "429": {
1667
+ "$ref": "#/components/responses/SimulationBudgetExhausted"
1668
+ },
1669
+ "500": {
1670
+ "$ref": "#/components/responses/ServerError"
1671
+ }
1672
+ }
1673
+ }
1674
+ },
1675
+ "/test_helpers/vault/deposits": {
1676
+ "post": {
1677
+ "operationId": "simulate-vault-deposit",
1678
+ "tags": [
1679
+ "Test Helpers"
1680
+ ],
1681
+ "x-sandbox-only": true,
1682
+ "summary": "Simulate a deposit to a vault",
1683
+ "description": "Send a simulated payment to one of your sandbox vaults on a simulated network and get the simulation back at once. `preset` accepts `dust` only. Requires an `Idempotency-Key`. Spends one unit of the simulation budget. Sandbox keys only.\n",
1684
+ "security": [
1685
+ {
1686
+ "ApiKeyAuth": []
1687
+ }
1688
+ ],
1689
+ "parameters": [
1690
+ {
1691
+ "$ref": "#/components/parameters/IdempotencyKeyHeader"
1692
+ }
1693
+ ],
1694
+ "requestBody": {
1695
+ "required": true,
1696
+ "content": {
1697
+ "application/json": {
1698
+ "schema": {
1699
+ "$ref": "#/components/schemas/SimulateVaultDepositRequest"
1700
+ }
1701
+ }
1702
+ }
1703
+ },
1704
+ "responses": {
1705
+ "201": {
1706
+ "$ref": "#/components/responses/SimulationCreated"
1707
+ },
1708
+ "400": {
1709
+ "$ref": "#/components/responses/TestHelperBadRequest"
1710
+ },
1711
+ "401": {
1712
+ "$ref": "#/components/responses/Unauthorized"
1713
+ },
1714
+ "404": {
1715
+ "description": "`not a sandbox environment` (the key is a live key), `vault not found`, `network not simulated`, or `vault has no wallet on this network`.\n",
1716
+ "content": {
1717
+ "application/json": {
1718
+ "schema": {
1719
+ "$ref": "#/components/schemas/Error"
1720
+ },
1721
+ "example": {
1722
+ "success": false,
1723
+ "code": 404,
1724
+ "error": "not a sandbox environment"
1725
+ }
1726
+ }
1727
+ }
1728
+ },
1729
+ "409": {
1730
+ "$ref": "#/components/responses/SandboxBusy"
1731
+ },
1732
+ "422": {
1733
+ "description": "A request the helper cannot carry out: `vault_id, network_id, asset and amount are required`, `unknown asset`, `malformed amount`, `unknown preset`, or `idempotency key replayed with a different body`.\n",
1734
+ "content": {
1735
+ "application/json": {
1736
+ "schema": {
1737
+ "$ref": "#/components/schemas/Error"
1738
+ },
1739
+ "example": {
1740
+ "success": false,
1741
+ "code": 422,
1742
+ "error": "unknown asset"
1743
+ }
1744
+ }
1745
+ }
1746
+ },
1747
+ "429": {
1748
+ "$ref": "#/components/responses/SimulationBudgetExhausted"
1749
+ },
1750
+ "500": {
1751
+ "$ref": "#/components/responses/ServerError"
1752
+ }
1753
+ }
1754
+ }
1755
+ },
1756
+ "/test_helpers/payouts": {
1757
+ "post": {
1758
+ "operationId": "simulate-payout",
1759
+ "tags": [
1760
+ "Test Helpers"
1761
+ ],
1762
+ "x-sandbox-only": true,
1763
+ "summary": "Simulate a payout",
1764
+ "description": "Create an ordinary sandbox payout — the same body and checks as `POST /transfer/payout` — with the fate of its transaction chosen first: `success` confirms it, `fail` fails it, and `hold` keeps it broadcast but unconfirmed until you release or fail it. Requires the key's `payout` scope, as creating a payout does, and an `Idempotency-Key`. Spends one unit of the simulation budget. Sandbox keys only.\n",
1765
+ "security": [
1766
+ {
1767
+ "ApiKeyAuth": []
1768
+ }
1769
+ ],
1770
+ "parameters": [
1771
+ {
1772
+ "$ref": "#/components/parameters/IdempotencyKeyHeader"
1773
+ }
1774
+ ],
1775
+ "requestBody": {
1776
+ "required": true,
1777
+ "content": {
1778
+ "application/json": {
1779
+ "schema": {
1780
+ "$ref": "#/components/schemas/SimulatePayoutRequest"
1781
+ }
1782
+ }
1783
+ }
1784
+ },
1785
+ "responses": {
1786
+ "201": {
1787
+ "$ref": "#/components/responses/SimulationCreated"
1788
+ },
1789
+ "400": {
1790
+ "$ref": "#/components/responses/TestHelperBadRequest"
1791
+ },
1792
+ "401": {
1793
+ "$ref": "#/components/responses/Unauthorized"
1794
+ },
1795
+ "403": {
1796
+ "$ref": "#/components/responses/PayoutScopeRequired"
1797
+ },
1798
+ "404": {
1799
+ "description": "`not a sandbox environment` (the key is a live key) or `vault not found`.\n",
1800
+ "content": {
1801
+ "application/json": {
1802
+ "schema": {
1803
+ "$ref": "#/components/schemas/Error"
1804
+ },
1805
+ "example": {
1806
+ "success": false,
1807
+ "code": 404,
1808
+ "error": "not a sandbox environment"
1809
+ }
1810
+ }
1811
+ }
1812
+ },
1813
+ "409": {
1814
+ "description": "`environment provisioning` or `environment resetting`, `duplicate reference_id for this vault` (`details` carries the existing `payout_id` and its `status`), `a request with this idempotency key is still in flight`, or `Idempotency key reused with different request`.\n",
1815
+ "content": {
1816
+ "application/json": {
1817
+ "schema": {
1818
+ "$ref": "#/components/schemas/Error"
1819
+ },
1820
+ "examples": {
1821
+ "resetting": {
1822
+ "value": {
1823
+ "success": false,
1824
+ "code": 409,
1825
+ "error": "environment resetting"
1826
+ }
1827
+ },
1828
+ "duplicate-reference": {
1829
+ "value": {
1830
+ "success": false,
1831
+ "code": 409,
1832
+ "error": "duplicate reference_id for this vault",
1833
+ "details": {
1834
+ "payout_id": "550e8400-e29b-41d4-a716-446655440000",
1835
+ "status": "confirmed"
1836
+ }
1837
+ }
1838
+ }
1839
+ }
1840
+ }
1841
+ }
1842
+ },
1843
+ "422": {
1844
+ "description": "`vault_id, reference_id, to_address, network_symbol and asset are required`, `state exactly one of amount and usd_amount`, `invalid type, must be 'normal' or 'forceful'`, `unknown payout outcome`, `invalid network_symbol`, `vault has no wallet on this network`, a payout the payout API refuses (`insufficient balance`, `insufficient gas for transaction`, `invalid destination address`, `invalid amount`, `expired_at must be in the future`, `unable to get price for asset`, `network or asset not supported for this tenant`), or `idempotency key replayed with a different body`.\n",
1845
+ "content": {
1846
+ "application/json": {
1847
+ "schema": {
1848
+ "$ref": "#/components/schemas/Error"
1849
+ },
1850
+ "example": {
1851
+ "success": false,
1852
+ "code": 422,
1853
+ "error": "unknown payout outcome"
1854
+ }
1855
+ }
1856
+ }
1857
+ },
1858
+ "429": {
1859
+ "$ref": "#/components/responses/SimulationBudgetExhausted"
1860
+ },
1861
+ "500": {
1862
+ "$ref": "#/components/responses/ServerError"
1863
+ }
1864
+ }
1865
+ }
1866
+ },
1867
+ "/test_helpers/payouts/{payout_id}/release": {
1868
+ "post": {
1869
+ "operationId": "release-simulated-payout",
1870
+ "tags": [
1871
+ "Test Helpers"
1872
+ ],
1873
+ "x-sandbox-only": true,
1874
+ "summary": "Release a held payout",
1875
+ "description": "Confirm a payout that `POST /test_helpers/payouts` created with `outcome: hold`. No body. Requires the key's `payout` scope and an `Idempotency-Key`. Spends one unit of the simulation budget. Sandbox keys only.\n",
1876
+ "security": [
1877
+ {
1878
+ "ApiKeyAuth": []
1879
+ }
1880
+ ],
1881
+ "parameters": [
1882
+ {
1883
+ "$ref": "#/components/parameters/PayoutIdPathParam"
1884
+ },
1885
+ {
1886
+ "$ref": "#/components/parameters/IdempotencyKeyHeader"
1887
+ }
1888
+ ],
1889
+ "responses": {
1890
+ "201": {
1891
+ "$ref": "#/components/responses/SimulationCreated"
1892
+ },
1893
+ "400": {
1894
+ "$ref": "#/components/responses/TestHelperBadRequest"
1895
+ },
1896
+ "401": {
1897
+ "$ref": "#/components/responses/Unauthorized"
1898
+ },
1899
+ "403": {
1900
+ "$ref": "#/components/responses/PayoutScopeRequired"
1901
+ },
1902
+ "404": {
1903
+ "$ref": "#/components/responses/HeldPayoutNotFound"
1904
+ },
1905
+ "409": {
1906
+ "$ref": "#/components/responses/PayoutNotHeld"
1907
+ },
1908
+ "422": {
1909
+ "$ref": "#/components/responses/IdempotencyKeyReused"
1910
+ },
1911
+ "429": {
1912
+ "$ref": "#/components/responses/SimulationBudgetExhausted"
1913
+ },
1914
+ "500": {
1915
+ "$ref": "#/components/responses/ServerError"
1916
+ }
1917
+ }
1918
+ }
1919
+ },
1920
+ "/test_helpers/payouts/{payout_id}/fail": {
1921
+ "post": {
1922
+ "operationId": "fail-simulated-payout",
1923
+ "tags": [
1924
+ "Test Helpers"
1925
+ ],
1926
+ "x-sandbox-only": true,
1927
+ "summary": "Fail a held payout",
1928
+ "description": "Fail a payout that `POST /test_helpers/payouts` created with `outcome: hold`. No body. Requires the key's `payout` scope and an `Idempotency-Key`. Spends one unit of the simulation budget. Sandbox keys only.\n",
1929
+ "security": [
1930
+ {
1931
+ "ApiKeyAuth": []
1932
+ }
1933
+ ],
1934
+ "parameters": [
1935
+ {
1936
+ "$ref": "#/components/parameters/PayoutIdPathParam"
1937
+ },
1938
+ {
1939
+ "$ref": "#/components/parameters/IdempotencyKeyHeader"
1940
+ }
1941
+ ],
1942
+ "responses": {
1943
+ "201": {
1944
+ "$ref": "#/components/responses/SimulationCreated"
1945
+ },
1946
+ "400": {
1947
+ "$ref": "#/components/responses/TestHelperBadRequest"
1948
+ },
1949
+ "401": {
1950
+ "$ref": "#/components/responses/Unauthorized"
1951
+ },
1952
+ "403": {
1953
+ "$ref": "#/components/responses/PayoutScopeRequired"
1954
+ },
1955
+ "404": {
1956
+ "$ref": "#/components/responses/HeldPayoutNotFound"
1957
+ },
1958
+ "409": {
1959
+ "$ref": "#/components/responses/PayoutNotHeld"
1960
+ },
1961
+ "422": {
1962
+ "$ref": "#/components/responses/IdempotencyKeyReused"
1963
+ },
1964
+ "429": {
1965
+ "$ref": "#/components/responses/SimulationBudgetExhausted"
1966
+ },
1967
+ "500": {
1968
+ "$ref": "#/components/responses/ServerError"
1969
+ }
1970
+ }
1971
+ }
1972
+ },
1973
+ "/test_helpers/simulations": {
1974
+ "get": {
1975
+ "operationId": "list-simulations",
1976
+ "tags": [
1977
+ "Test Helpers"
1978
+ ],
1979
+ "x-sandbox-only": true,
1980
+ "summary": "List simulations",
1981
+ "description": "List the simulations made in your sandbox, newest first. Filter and sort with `filter[]`, `filterOr[]` and `sort[]` on `id`, `kind`, `state`, `preset`, `created_at` and `updated_at`. A reset of the sandbox deletes its simulations. Sandbox keys only.\n",
1982
+ "security": [
1983
+ {
1984
+ "ApiKeyAuth": []
1985
+ }
1986
+ ],
1987
+ "parameters": [
1988
+ {
1989
+ "name": "filter[]",
1990
+ "in": "query",
1991
+ "description": "Filter condition, `field:operator:value`; repeat for more (all must match). Operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `between`, `in`, `nin`, `like`, `ilike`, `inc`, `iinc`, `isnull`, `notnull`.\n",
1992
+ "schema": {
1993
+ "type": "array",
1994
+ "items": {
1995
+ "type": "string"
1996
+ }
1997
+ },
1998
+ "example": [
1999
+ "state:eq:completed"
2000
+ ]
2001
+ },
2002
+ {
2003
+ "name": "filterOr[]",
2004
+ "in": "query",
2005
+ "description": "Filter condition, `field:operator:value`; repeat for more (any may match).",
2006
+ "schema": {
2007
+ "type": "array",
2008
+ "items": {
2009
+ "type": "string"
2010
+ }
2011
+ }
2012
+ },
2013
+ {
2014
+ "name": "sort[]",
2015
+ "in": "query",
2016
+ "description": "Sort field, `field:direction` (`asc` or `desc`). Defaults to `created_at:desc`.",
2017
+ "schema": {
2018
+ "type": "array",
2019
+ "items": {
2020
+ "type": "string"
2021
+ }
2022
+ },
2023
+ "example": [
2024
+ "created_at:desc"
2025
+ ]
2026
+ },
2027
+ {
2028
+ "$ref": "#/components/parameters/PageParam"
2029
+ },
2030
+ {
2031
+ "name": "limit",
2032
+ "in": "query",
2033
+ "description": "Items per page (default 10, max 100).",
2034
+ "schema": {
2035
+ "type": "integer",
2036
+ "default": 10,
2037
+ "minimum": 1,
2038
+ "maximum": 100
2039
+ }
2040
+ }
2041
+ ],
2042
+ "responses": {
2043
+ "200": {
2044
+ "description": "A page of simulations.",
2045
+ "content": {
2046
+ "application/json": {
2047
+ "schema": {
2048
+ "$ref": "#/components/schemas/SimulationList"
2049
+ }
2050
+ }
2051
+ }
2052
+ },
2053
+ "400": {
2054
+ "description": "`invalid query parameters`, or the request also sent `X-Stridge-Environment`.\n",
2055
+ "content": {
2056
+ "application/json": {
2057
+ "schema": {
2058
+ "$ref": "#/components/schemas/Error"
2059
+ }
2060
+ }
2061
+ }
2062
+ },
2063
+ "401": {
2064
+ "$ref": "#/components/responses/Unauthorized"
2065
+ },
2066
+ "404": {
2067
+ "$ref": "#/components/responses/NotASandbox"
2068
+ },
2069
+ "409": {
2070
+ "$ref": "#/components/responses/SandboxBusy"
2071
+ },
2072
+ "429": {
2073
+ "$ref": "#/components/responses/TooManyRequests"
2074
+ },
2075
+ "500": {
2076
+ "$ref": "#/components/responses/ServerError"
2077
+ }
2078
+ }
2079
+ }
2080
+ },
2081
+ "/test_helpers/simulations/{simulation_id}": {
2082
+ "get": {
2083
+ "operationId": "get-simulation",
2084
+ "tags": [
2085
+ "Test Helpers"
2086
+ ],
2087
+ "x-sandbox-only": true,
2088
+ "summary": "Get a simulation",
2089
+ "description": "Read one simulation. With `wait`, the request blocks until the simulation reaches that state (`observed`: Stridge recorded the first object it produced; `completed`: the flow reached its terminal state), stops early (`failed` or `expired`), or `timeout_ms` passes, and then answers `200` with whatever state it reached. Sandbox keys only.\n",
2090
+ "security": [
2091
+ {
2092
+ "ApiKeyAuth": []
2093
+ }
2094
+ ],
2095
+ "parameters": [
2096
+ {
2097
+ "name": "simulation_id",
2098
+ "in": "path",
2099
+ "required": true,
2100
+ "description": "Simulation id.",
2101
+ "schema": {
2102
+ "type": "string"
2103
+ }
2104
+ },
2105
+ {
2106
+ "name": "wait",
2107
+ "in": "query",
2108
+ "description": "Block until the simulation is `observed` or `completed`.",
2109
+ "schema": {
2110
+ "type": "string",
2111
+ "enum": [
2112
+ "observed",
2113
+ "completed"
2114
+ ]
2115
+ }
2116
+ },
2117
+ {
2118
+ "name": "timeout_ms",
2119
+ "in": "query",
2120
+ "description": "How long `wait` may block, in milliseconds (default 10000). Values above 30000 are capped at 30000.\n",
2121
+ "schema": {
2122
+ "type": "integer",
2123
+ "default": 10000,
2124
+ "minimum": 0
2125
+ }
2126
+ }
2127
+ ],
2128
+ "responses": {
2129
+ "200": {
2130
+ "description": "The simulation.",
2131
+ "content": {
2132
+ "application/json": {
2133
+ "schema": {
2134
+ "$ref": "#/components/schemas/SimulationResponse"
2135
+ }
2136
+ }
2137
+ }
2138
+ },
2139
+ "400": {
2140
+ "description": "`unknown wait` or `malformed timeout_ms`, or the request also sent `X-Stridge-Environment`.\n",
2141
+ "content": {
2142
+ "application/json": {
2143
+ "schema": {
2144
+ "$ref": "#/components/schemas/Error"
2145
+ }
2146
+ }
2147
+ }
2148
+ },
2149
+ "401": {
2150
+ "$ref": "#/components/responses/Unauthorized"
2151
+ },
2152
+ "404": {
2153
+ "description": "`not a sandbox environment` (the key is a live key) or `simulation not found`.\n",
2154
+ "content": {
2155
+ "application/json": {
2156
+ "schema": {
2157
+ "$ref": "#/components/schemas/Error"
2158
+ },
2159
+ "example": {
2160
+ "success": false,
2161
+ "code": 404,
2162
+ "error": "simulation not found"
2163
+ }
2164
+ }
2165
+ }
2166
+ },
2167
+ "409": {
2168
+ "$ref": "#/components/responses/SandboxBusy"
2169
+ },
2170
+ "429": {
2171
+ "$ref": "#/components/responses/TooManyRequests"
2172
+ },
2173
+ "500": {
2174
+ "$ref": "#/components/responses/ServerError"
2175
+ }
2176
+ }
2177
+ }
2178
+ }
2179
+ },
2180
+ "webhooks": {
2181
+ "webhook-event": {
2182
+ "post": {
2183
+ "operationId": "receive-webhook-event",
2184
+ "tags": [
2185
+ "Webhooks"
2186
+ ],
2187
+ "summary": "Receive an event",
2188
+ "security": [],
2189
+ "description": "What Stridge POSTs to each of your webhook endpoints (https only) for every event it subscribes to, from live and from sandbox alike. The body is the signed envelope below; `livemode` and `environment_id` say which environment the event belongs to, and sandbox deliveries also carry `reset_epoch`. Verify `webhook-signature` over the raw body before parsing it: it is the hex HMAC-SHA256, keyed with the endpoint's signing secret, of `webhook-timestamp + \".\" + raw body`. A receiver that re-serialises the body before verifying, or validates the envelope against a schema that refuses unknown fields, breaks.\n",
2190
+ "parameters": [
2191
+ {
2192
+ "name": "webhook-id",
2193
+ "in": "header",
2194
+ "required": true,
2195
+ "description": "The delivery's idempotency key; the same on every retry of the event.",
2196
+ "schema": {
2197
+ "type": "string"
2198
+ }
2199
+ },
2200
+ {
2201
+ "name": "webhook-timestamp",
2202
+ "in": "header",
2203
+ "required": true,
2204
+ "description": "When the delivery was sent, as a Unix timestamp in seconds. Reject stale timestamps to prevent replays.",
2205
+ "schema": {
2206
+ "type": "string",
2207
+ "example": "1792913022"
2208
+ }
2209
+ },
2210
+ {
2211
+ "name": "webhook-signature",
2212
+ "in": "header",
2213
+ "required": true,
2214
+ "description": "Hex HMAC-SHA256 of `webhook-timestamp + \".\" + raw body`, keyed with the endpoint's signing secret.",
2215
+ "schema": {
2216
+ "type": "string"
2217
+ }
2218
+ }
2219
+ ],
2220
+ "requestBody": {
2221
+ "required": true,
2222
+ "content": {
2223
+ "application/json": {
2224
+ "schema": {
2225
+ "$ref": "#/components/schemas/WebhookEnvelope"
2226
+ }
2227
+ }
2228
+ }
2229
+ },
2230
+ "responses": {
2231
+ "200": {
2232
+ "description": "Any `2xx` acknowledges the delivery, within 10 seconds. Anything else, or no answer, is retried with backoff: in live up to 6 attempts, from 10 seconds apart up to 10 minutes; in a sandbox fewer, 30 seconds apart.\n"
2233
+ }
2234
+ }
2235
+ }
2236
+ }
2237
+ },
2238
+ "components": {
2239
+ "securitySchemes": {
2240
+ "ApiKeyAuth": {
2241
+ "type": "apiKey",
2242
+ "in": "header",
2243
+ "name": "X-API-Key",
2244
+ "description": "Secret API key for programmatic access. Send it on every authenticated request. Keep it server-side; never ship it in a client bundle. New live keys start `sk_live_` and sandbox keys `sk_sbx_`; live keys issued before sandbox keys existed have no prefix. The key's own environment is the one the request acts in, whatever its prefix. Live and sandbox keys share one base URL: send the key unchanged in this header, which is what routes a sandbox key's request to the sandbox. A keyed request is never asked for a step-up (TOTP or any second factor); step-up applies only to people signed in to the dashboard.\n"
2245
+ }
2246
+ },
2247
+ "parameters": {
2248
+ "LimitParam": {
2249
+ "name": "limit",
2250
+ "in": "query",
2251
+ "description": "Page size (default 50, max 200).",
2252
+ "schema": {
2253
+ "type": "integer",
2254
+ "default": 50,
2255
+ "minimum": 1,
2256
+ "maximum": 200
2257
+ }
2258
+ },
2259
+ "OffsetParam": {
2260
+ "name": "offset",
2261
+ "in": "query",
2262
+ "description": "Number of items to skip (default 0).",
2263
+ "schema": {
2264
+ "type": "integer",
2265
+ "default": 0,
2266
+ "minimum": 0
2267
+ }
2268
+ },
2269
+ "VaultIdParam": {
2270
+ "name": "vault_id",
2271
+ "in": "path",
2272
+ "required": true,
2273
+ "description": "Vault id.",
2274
+ "schema": {
2275
+ "type": "string"
2276
+ }
2277
+ },
2278
+ "PageParam": {
2279
+ "name": "page",
2280
+ "in": "query",
2281
+ "description": "Page number (default 1).",
2282
+ "schema": {
2283
+ "type": "integer",
2284
+ "default": 1,
2285
+ "minimum": 1
2286
+ }
2287
+ },
2288
+ "PageSizeParam": {
2289
+ "name": "limit",
2290
+ "in": "query",
2291
+ "description": "Items per page (default 20, max 100).",
2292
+ "schema": {
2293
+ "type": "integer",
2294
+ "default": 20,
2295
+ "minimum": 1,
2296
+ "maximum": 100
2297
+ }
2298
+ },
2299
+ "IdempotencyKeyHeader": {
2300
+ "name": "Idempotency-Key",
2301
+ "in": "header",
2302
+ "required": true,
2303
+ "description": "Required on every test helper call that pulls a lever (at most 255 characters; without one the call is answered `400 idempotency key required`). A repeat with the same key and body is answered the first response again, refusals included, with `Idempotency-Replay: true`, and never pulls the lever twice. The same key with another body on the same helper is answered `409 Idempotency key reused with different request`; the same key on another helper, or for another payout, is answered `422 idempotency key replayed with a different body`. Keys are remembered for 24 hours per sandbox, and a reset of the sandbox starts them afresh.\n",
2304
+ "schema": {
2305
+ "type": "string",
2306
+ "maxLength": 255
2307
+ },
2308
+ "example": "sim-2f1c9a7e"
2309
+ },
2310
+ "IdempotencyKeyOptional": {
2311
+ "name": "Idempotency-Key",
2312
+ "in": "header",
2313
+ "required": false,
2314
+ "description": "Optional. A unique key for this request, such as a UUID. A repeat of the same request with the same key within 24 hours is answered the first response again, refusals included, with `Idempotency-Replay: true`, and is not carried out twice. Keys are scoped to your workspace and the key's environment, so every API key of that environment shares them. The same key with a different body is answered `409` with `Idempotency key reused with different request`; a repeat that arrives while the first is still running waits for it, and is answered `408` with `Request processing timeout` if it does not finish in time.\n",
2315
+ "schema": {
2316
+ "type": "string"
2317
+ },
2318
+ "example": "6f1c2e7a-4b9d-4c1e-9a7f-0d3b5e8c2a14"
2319
+ },
2320
+ "PayoutIdPathParam": {
2321
+ "name": "payout_id",
2322
+ "in": "path",
2323
+ "required": true,
2324
+ "description": "The held payout's id.",
2325
+ "schema": {
2326
+ "type": "string"
2327
+ }
2328
+ }
2329
+ },
2330
+ "responses": {
2331
+ "BadRequest": {
2332
+ "description": "The request was malformed or failed validation, or a keyed request also sent `X-Stridge-Environment`.\n",
2333
+ "content": {
2334
+ "application/json": {
2335
+ "schema": {
2336
+ "$ref": "#/components/schemas/Error"
2337
+ },
2338
+ "example": {
2339
+ "success": false,
2340
+ "code": 400,
2341
+ "error": "invalid request"
2342
+ }
2343
+ }
2344
+ }
2345
+ },
2346
+ "Unauthorized": {
2347
+ "description": "The `X-API-Key` header is missing (`X-API-Key header is required`; a key sent as a bearer token is told `send the API key in X-API-Key, not in Authorization`), the key is unknown, revoked or expired (`Invalid API key`, `API key is not active`), or the key reached the other environment's API (a sandbox key sent to live, or a live key sent to the sandbox). Send the key unchanged in `X-API-Key` to the same base URL; the error says which.\n",
2348
+ "content": {
2349
+ "application/json": {
2350
+ "schema": {
2351
+ "$ref": "#/components/schemas/Error"
2352
+ },
2353
+ "example": {
2354
+ "success": false,
2355
+ "code": 401,
2356
+ "error": "Invalid API key"
2357
+ }
2358
+ }
2359
+ }
2360
+ },
2361
+ "Forbidden": {
2362
+ "description": "The key may not make this request: its scopes do not cover the operation (`Insufficient permissions`), or it is restricted to an IP allowlist or to referrers and the request came from elsewhere (`API key not allowed from this IP`, `Referrer not allowed`).\n",
2363
+ "content": {
2364
+ "application/json": {
2365
+ "schema": {
2366
+ "$ref": "#/components/schemas/Error"
2367
+ },
2368
+ "example": {
2369
+ "success": false,
2370
+ "code": 403,
2371
+ "error": "Insufficient permissions"
2372
+ }
2373
+ }
2374
+ }
2375
+ },
2376
+ "EnvironmentResetting": {
2377
+ "description": "`environment resetting`: the key's sandbox is being reset from the dashboard, and its keys act in nothing until the reset is done. Retry shortly. Live keys never get it.\n",
2378
+ "content": {
2379
+ "application/json": {
2380
+ "schema": {
2381
+ "$ref": "#/components/schemas/Error"
2382
+ },
2383
+ "example": {
2384
+ "success": false,
2385
+ "code": 409,
2386
+ "error": "environment resetting"
2387
+ }
2388
+ }
2389
+ }
2390
+ },
2391
+ "TooManyRequests": {
2392
+ "description": "`Too many requests`: the rate limit for this operation was reached for the current one-minute window. Wait `Retry-After` seconds.\n",
2393
+ "headers": {
2394
+ "X-RateLimit-Limit": {
2395
+ "$ref": "#/components/headers/X-RateLimit-Limit"
2396
+ },
2397
+ "X-RateLimit-Remaining": {
2398
+ "$ref": "#/components/headers/X-RateLimit-Remaining"
2399
+ },
2400
+ "X-RateLimit-Reset": {
2401
+ "$ref": "#/components/headers/X-RateLimit-Reset"
2402
+ },
2403
+ "Retry-After": {
2404
+ "$ref": "#/components/headers/Retry-After"
2405
+ }
2406
+ },
2407
+ "content": {
2408
+ "application/json": {
2409
+ "schema": {
2410
+ "$ref": "#/components/schemas/Error"
2411
+ },
2412
+ "example": {
2413
+ "success": false,
2414
+ "code": 429,
2415
+ "error": "Too many requests"
2416
+ }
2417
+ }
2418
+ }
2419
+ },
2420
+ "NotFound": {
2421
+ "description": "The requested resource does not exist.",
2422
+ "content": {
2423
+ "application/json": {
2424
+ "schema": {
2425
+ "$ref": "#/components/schemas/Error"
2426
+ },
2427
+ "example": {
2428
+ "success": false,
2429
+ "code": 404,
2430
+ "error": "not found"
2431
+ }
2432
+ }
2433
+ }
2434
+ },
2435
+ "ServerError": {
2436
+ "description": "An unexpected error occurred on the server.",
2437
+ "content": {
2438
+ "application/json": {
2439
+ "schema": {
2440
+ "$ref": "#/components/schemas/Error"
2441
+ },
2442
+ "example": {
2443
+ "success": false,
2444
+ "code": 500,
2445
+ "error": "internal server error"
2446
+ }
2447
+ }
2448
+ }
2449
+ },
2450
+ "NotASandbox": {
2451
+ "description": "`not a sandbox environment`: the key is a live key. Every test helper answers a live key this way.\n",
2452
+ "content": {
2453
+ "application/json": {
2454
+ "schema": {
2455
+ "$ref": "#/components/schemas/Error"
2456
+ },
2457
+ "example": {
2458
+ "success": false,
2459
+ "code": 404,
2460
+ "error": "not a sandbox environment"
2461
+ }
2462
+ }
2463
+ }
2464
+ },
2465
+ "SandboxBusy": {
2466
+ "description": "`environment provisioning` or `environment resetting`: the sandbox is not ready yet or is being reset; retry shortly. A helper call may also answer `a request with this idempotency key is still in flight`, or `Idempotency key reused with different request` for a key first sent with another body.\n",
2467
+ "content": {
2468
+ "application/json": {
2469
+ "schema": {
2470
+ "$ref": "#/components/schemas/Error"
2471
+ },
2472
+ "example": {
2473
+ "success": false,
2474
+ "code": 409,
2475
+ "error": "environment resetting"
2476
+ }
2477
+ }
2478
+ }
2479
+ },
2480
+ "TestHelperBadRequest": {
2481
+ "description": "`idempotency key required`, `idempotency key too long` (over 255 characters) or `invalid request body`, or the request also sent `X-Stridge-Environment` (`environment header not accepted`).\n",
2482
+ "content": {
2483
+ "application/json": {
2484
+ "schema": {
2485
+ "$ref": "#/components/schemas/Error"
2486
+ },
2487
+ "example": {
2488
+ "success": false,
2489
+ "code": 400,
2490
+ "error": "idempotency key required"
2491
+ }
2492
+ }
2493
+ }
2494
+ },
2495
+ "PayoutScopeRequired": {
2496
+ "description": "The key lacks the `payout` scope, which the payout helpers require as creating a payout does.\n",
2497
+ "content": {
2498
+ "application/json": {
2499
+ "schema": {
2500
+ "$ref": "#/components/schemas/Error"
2501
+ },
2502
+ "example": {
2503
+ "success": false,
2504
+ "code": 403,
2505
+ "error": "Insufficient permissions"
2506
+ }
2507
+ }
2508
+ }
2509
+ },
2510
+ "HeldPayoutNotFound": {
2511
+ "description": "`not a sandbox environment` (the key is a live key) or `payout not found`.\n",
2512
+ "content": {
2513
+ "application/json": {
2514
+ "schema": {
2515
+ "$ref": "#/components/schemas/Error"
2516
+ },
2517
+ "example": {
2518
+ "success": false,
2519
+ "code": 404,
2520
+ "error": "payout not found"
2521
+ }
2522
+ }
2523
+ }
2524
+ },
2525
+ "PayoutNotHeld": {
2526
+ "description": "`payout not held`: no payout helper holds this payout, or it is already final; `details.state` is the payout's status. Also `environment provisioning`, `environment resetting`, or `a request with this idempotency key is still in flight`.\n",
2527
+ "content": {
2528
+ "application/json": {
2529
+ "schema": {
2530
+ "$ref": "#/components/schemas/Error"
2531
+ },
2532
+ "example": {
2533
+ "success": false,
2534
+ "code": 409,
2535
+ "error": "payout not held",
2536
+ "details": {
2537
+ "state": "confirmed"
2538
+ }
2539
+ }
2540
+ }
2541
+ }
2542
+ },
2543
+ "IdempotencyKeyReused": {
2544
+ "description": "`idempotency key replayed with a different body`: the key was first used on another helper or for another payout.\n",
2545
+ "content": {
2546
+ "application/json": {
2547
+ "schema": {
2548
+ "$ref": "#/components/schemas/Error"
2549
+ },
2550
+ "example": {
2551
+ "success": false,
2552
+ "code": 422,
2553
+ "error": "idempotency key replayed with a different body"
2554
+ }
2555
+ }
2556
+ }
2557
+ },
2558
+ "SimulationBudgetExhausted": {
2559
+ "description": "`simulation budget exhausted`: the sandbox spent its simulations for the current window (600 per hour). `details` carries `limit`, `window_s` and `retry_after_s`, and the `X-RateLimit-*` headers describe the simulation budget. The operation's own rate limit answers `Too many requests` instead, with the same headers describing that limit.\n",
2560
+ "headers": {
2561
+ "X-RateLimit-Limit": {
2562
+ "description": "Simulations allowed per window.",
2563
+ "schema": {
2564
+ "type": "integer"
2565
+ }
2566
+ },
2567
+ "X-RateLimit-Remaining": {
2568
+ "description": "Simulations left in the current window.",
2569
+ "schema": {
2570
+ "type": "integer"
2571
+ }
2572
+ },
2573
+ "X-RateLimit-Reset": {
2574
+ "description": "When the window resets, as a Unix timestamp in seconds.",
2575
+ "schema": {
2576
+ "type": "integer"
2577
+ }
2578
+ },
2579
+ "Retry-After": {
2580
+ "description": "Seconds until a simulation can be made again.",
2581
+ "schema": {
2582
+ "type": "integer"
2583
+ }
2584
+ }
2585
+ },
1369
2586
  "content": {
1370
2587
  "application/json": {
1371
2588
  "schema": {
@@ -1373,14 +2590,173 @@
1373
2590
  },
1374
2591
  "example": {
1375
2592
  "success": false,
1376
- "code": 500,
1377
- "error": "internal server error"
2593
+ "code": 429,
2594
+ "error": "simulation budget exhausted",
2595
+ "details": {
2596
+ "limit": 600,
2597
+ "window_s": 3600,
2598
+ "retry_after_s": 1200
2599
+ }
2600
+ }
2601
+ }
2602
+ }
2603
+ },
2604
+ "SimulationCreated": {
2605
+ "description": "The lever was pulled; the simulation is answered at once, usually `running`. Read it back with `GET /test_helpers/simulations/{simulation_id}`.\n",
2606
+ "headers": {
2607
+ "Location": {
2608
+ "description": "Where to read the simulation, relative to the base URL.",
2609
+ "schema": {
2610
+ "type": "string",
2611
+ "example": "/test_helpers/simulations/0192f4c8-7a1e-7c3b-9d2a-5e6f7a8b9c0d"
2612
+ }
2613
+ },
2614
+ "Idempotency-Replay": {
2615
+ "description": "`true` when this answer is the replay of an earlier request with the same `Idempotency-Key`.",
2616
+ "schema": {
2617
+ "type": "string",
2618
+ "enum": [
2619
+ "true"
2620
+ ]
2621
+ }
2622
+ }
2623
+ },
2624
+ "content": {
2625
+ "application/json": {
2626
+ "schema": {
2627
+ "$ref": "#/components/schemas/SimulationResponse"
1378
2628
  }
1379
2629
  }
1380
2630
  }
1381
2631
  }
1382
2632
  },
2633
+ "headers": {
2634
+ "X-RateLimit-Limit": {
2635
+ "description": "Requests allowed in the current window.",
2636
+ "schema": {
2637
+ "type": "integer",
2638
+ "example": 720
2639
+ }
2640
+ },
2641
+ "X-RateLimit-Remaining": {
2642
+ "description": "Requests left in the current window.",
2643
+ "schema": {
2644
+ "type": "integer",
2645
+ "example": 0
2646
+ }
2647
+ },
2648
+ "X-RateLimit-Reset": {
2649
+ "description": "When the window resets, as a Unix timestamp in seconds.",
2650
+ "schema": {
2651
+ "type": "integer",
2652
+ "example": 1792913022
2653
+ }
2654
+ },
2655
+ "Retry-After": {
2656
+ "description": "Seconds to wait before retrying.",
2657
+ "schema": {
2658
+ "type": "integer",
2659
+ "example": 42
2660
+ }
2661
+ }
2662
+ },
1383
2663
  "schemas": {
2664
+ "Livemode": {
2665
+ "type": "boolean",
2666
+ "description": "`true` when the API key that made the request acts in a live environment, `false` in a sandbox. It is on every object a keyed request answers with — the resource itself, each item of a list, and each object inside a list a resource carries — but not on the `success`/`message` envelope or on an object nested directly in another (such as `destination`, `fees` or `pagination`), so a payload read on its own still says which side of the boundary it came from. The two public endpoints answer without it. It is derived from the key at the time of the response and is never stored.\n",
2667
+ "example": true
2668
+ },
2669
+ "WebhookEnvelope": {
2670
+ "type": "object",
2671
+ "description": "The signed body of every webhook delivery. Fields appear in this order.\n",
2672
+ "properties": {
2673
+ "id": {
2674
+ "type": "string",
2675
+ "description": "The event's id.",
2676
+ "example": "7401d9c7-e29d-4374-8952-af40f05168b7"
2677
+ },
2678
+ "version": {
2679
+ "type": "string",
2680
+ "description": "The envelope format version.",
2681
+ "example": "1"
2682
+ },
2683
+ "type": {
2684
+ "type": "string",
2685
+ "description": "The event type.",
2686
+ "enum": [
2687
+ "wallet.address.assigned",
2688
+ "transaction.event",
2689
+ "deposit.confirmed",
2690
+ "vault.deposit.detected",
2691
+ "vault.created",
2692
+ "gas_station.created",
2693
+ "payout.confirmed",
2694
+ "payout.failed",
2695
+ "payout.expired",
2696
+ "payout.retrying",
2697
+ "uda.settlement.created",
2698
+ "uda.settlement.completed",
2699
+ "uda.settlement.retrying",
2700
+ "uda.settlement.awaiting_credit",
2701
+ "uda.settlement.failed",
2702
+ "uda.address.created",
2703
+ "treasury.created",
2704
+ "treasury.rotated",
2705
+ "treasury.deleted",
2706
+ "webhook_endpoint.ping"
2707
+ ],
2708
+ "example": "uda.settlement.completed"
2709
+ },
2710
+ "time": {
2711
+ "type": "string",
2712
+ "description": "When the event happened, in UTC (`2006-01-02T15:04:05Z`).",
2713
+ "example": "2026-10-24T11:59:25Z"
2714
+ },
2715
+ "livemode": {
2716
+ "type": "boolean",
2717
+ "description": "`true` for a live environment's event, `false` for a sandbox's.",
2718
+ "example": true
2719
+ },
2720
+ "environment_id": {
2721
+ "type": "string",
2722
+ "description": "The environment the event belongs to. A live environment's id is its workspace's id.\n",
2723
+ "example": "3f2c1d9e-8a47-4b6e-9c11-2d5e7f8a9b04"
2724
+ },
2725
+ "reset_epoch": {
2726
+ "type": "integer",
2727
+ "description": "Sandbox deliveries only: how many times the sandbox had been reset when the event was delivered. Absent on live deliveries. A delivery whose `reset_epoch` is lower than the sandbox's current one describes a world a reset has since emptied.\n",
2728
+ "example": 2
2729
+ },
2730
+ "payload": {
2731
+ "type": "object",
2732
+ "description": "The event's data; its shape depends on `type`.",
2733
+ "additionalProperties": true
2734
+ }
2735
+ },
2736
+ "required": [
2737
+ "id",
2738
+ "version",
2739
+ "type",
2740
+ "time",
2741
+ "livemode",
2742
+ "environment_id",
2743
+ "payload"
2744
+ ],
2745
+ "example": {
2746
+ "id": "7401d9c7-e29d-4374-8952-af40f05168b7",
2747
+ "version": "1",
2748
+ "type": "uda.settlement.completed",
2749
+ "time": "2026-10-24T11:59:25Z",
2750
+ "livemode": false,
2751
+ "environment_id": "3f2c1d9e-8a47-4b6e-9c11-2d5e7f8a9b04",
2752
+ "reset_epoch": 2,
2753
+ "payload": {
2754
+ "id": "stl_71829304a5b6c7d8",
2755
+ "uda_id": "uda_2f8c1a9d4e5f6071",
2756
+ "status": "completed"
2757
+ }
2758
+ }
2759
+ },
1384
2760
  "Error": {
1385
2761
  "type": "object",
1386
2762
  "description": "Canonical error envelope returned by every endpoint on failure.",
@@ -1398,6 +2774,11 @@
1398
2774
  "type": "string",
1399
2775
  "description": "Human-readable error message.",
1400
2776
  "example": "invalid request"
2777
+ },
2778
+ "details": {
2779
+ "type": "object",
2780
+ "description": "What a client needs to act on one particular refusal (for example `state` on `payout not held`, `payout_id` and `status` on a duplicate `reference_id`, or `limit`, `window_s` and `retry_after_s` on `simulation budget exhausted`). Absent from every error that has none.\n",
2781
+ "additionalProperties": true
1401
2782
  }
1402
2783
  },
1403
2784
  "required": [
@@ -1425,49 +2806,243 @@
1425
2806
  "Pagination": {
1426
2807
  "type": "object",
1427
2808
  "properties": {
1428
- "count": {
1429
- "type": "integer",
1430
- "description": "Number of items in this page.",
1431
- "example": 20
2809
+ "count": {
2810
+ "type": "integer",
2811
+ "description": "Number of items in this page.",
2812
+ "example": 20
2813
+ },
2814
+ "limit": {
2815
+ "type": "integer",
2816
+ "example": 50
2817
+ },
2818
+ "offset": {
2819
+ "type": "integer",
2820
+ "example": 0
2821
+ }
2822
+ }
2823
+ },
2824
+ "DestinationInput": {
2825
+ "type": "object",
2826
+ "description": "Where settlements for this UDA should be delivered.",
2827
+ "properties": {
2828
+ "address": {
2829
+ "type": "string",
2830
+ "description": "Destination wallet address.",
2831
+ "example": "0x3a1f8b2c9d4e5f60718293a4b5c6d7e8f9012345"
2832
+ },
2833
+ "network_id": {
2834
+ "type": "string",
2835
+ "description": "Destination SLIP-44 network id.",
2836
+ "example": "60"
2837
+ },
2838
+ "asset_symbol": {
2839
+ "type": "string",
2840
+ "description": "Destination asset symbol.",
2841
+ "example": "USDC"
2842
+ }
2843
+ },
2844
+ "required": [
2845
+ "address",
2846
+ "network_id"
2847
+ ]
2848
+ },
2849
+ "RoutingRuleMatchInput": {
2850
+ "type": "object",
2851
+ "description": "Conditions a deposit must meet for this routing rule to apply.",
2852
+ "properties": {
2853
+ "source_network_id": {
2854
+ "type": "string",
2855
+ "example": "60"
2856
+ },
2857
+ "source_asset_symbol": {
2858
+ "type": "string",
2859
+ "example": "USDT"
2860
+ }
2861
+ },
2862
+ "required": [
2863
+ "source_network_id"
2864
+ ]
2865
+ },
2866
+ "RoutingRuleDestinationInput": {
2867
+ "type": "object",
2868
+ "properties": {
2869
+ "address": {
2870
+ "type": "string",
2871
+ "example": "0x3a1f8b2c9d4e5f60718293a4b5c6d7e8f9012345"
2872
+ },
2873
+ "network_id": {
2874
+ "type": "string",
2875
+ "example": "60"
2876
+ },
2877
+ "asset_symbol": {
2878
+ "type": "string",
2879
+ "example": "USDC"
2880
+ }
2881
+ },
2882
+ "required": [
2883
+ "address",
2884
+ "network_id"
2885
+ ]
2886
+ },
2887
+ "RoutingRuleInput": {
2888
+ "type": "object",
2889
+ "properties": {
2890
+ "match": {
2891
+ "$ref": "#/components/schemas/RoutingRuleMatchInput"
2892
+ },
2893
+ "destination": {
2894
+ "$ref": "#/components/schemas/RoutingRuleDestinationInput"
2895
+ }
2896
+ },
2897
+ "required": [
2898
+ "match",
2899
+ "destination"
2900
+ ]
2901
+ },
2902
+ "CreateUDAAddressRequest": {
2903
+ "type": "object",
2904
+ "properties": {
2905
+ "owner": {
2906
+ "type": "string",
2907
+ "description": "The owner the UDA is allocated for.",
2908
+ "example": "0x9f8e7d6c5b4a39281706f5e4d3c2b1a098765432"
2909
+ },
2910
+ "destination": {
2911
+ "$ref": "#/components/schemas/DestinationInput"
2912
+ },
2913
+ "accepted_assets": {
2914
+ "type": "array",
2915
+ "items": {
2916
+ "type": "string"
2917
+ },
2918
+ "description": "Asset symbols this UDA accepts; empty means every asset on every UDA-supported chain.",
2919
+ "example": [
2920
+ "USDC",
2921
+ "USDT"
2922
+ ]
2923
+ },
2924
+ "routing_rules": {
2925
+ "type": "array",
2926
+ "items": {
2927
+ "$ref": "#/components/schemas/RoutingRuleInput"
2928
+ },
2929
+ "description": "Optional; max 3. Specificity-wins precedence: a rule pinning both `source_network_id` and `source_asset_symbol` beats a network-only rule. When nothing matches, settlements use the default destination.\n"
2930
+ },
2931
+ "routing_policy_id": {
2932
+ "type": "string",
2933
+ "description": "Routing policy to attach to this UDA. Omit or send an empty string to attach the tenant's default policy. Per-UDA inline `routing_rules` still take precedence over the policy.\n",
2934
+ "example": "rpol_2f8c1a9d4e5f6071"
2935
+ }
2936
+ },
2937
+ "required": [
2938
+ "owner",
2939
+ "destination"
2940
+ ],
2941
+ "example": {
2942
+ "owner": "0x9f8e7d6c5b4a39281706f5e4d3c2b1a098765432",
2943
+ "destination": {
2944
+ "address": "0x3a1f8b2c9d4e5f60718293a4b5c6d7e8f9012345",
2945
+ "network_id": "60",
2946
+ "asset_symbol": "USDC"
2947
+ },
2948
+ "accepted_assets": [
2949
+ "USDC",
2950
+ "USDT"
2951
+ ]
2952
+ }
2953
+ },
2954
+ "DepositAsset": {
2955
+ "type": "object",
2956
+ "properties": {
2957
+ "livemode": {
2958
+ "$ref": "#/components/schemas/Livemode"
2959
+ },
2960
+ "symbol": {
2961
+ "type": "string",
2962
+ "example": "USDC"
2963
+ },
2964
+ "address": {
2965
+ "type": "string",
2966
+ "description": "Token contract address (zero address for native).",
2967
+ "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
2968
+ },
2969
+ "decimals": {
2970
+ "type": "integer",
2971
+ "example": 6
2972
+ },
2973
+ "logo": {
2974
+ "type": "string",
2975
+ "example": "https://assets.stridge.com/tokens/usdc.png"
2976
+ }
2977
+ }
2978
+ },
2979
+ "DepositAddress": {
2980
+ "type": "object",
2981
+ "description": "A per-chain address a depositor can send funds to.",
2982
+ "properties": {
2983
+ "livemode": {
2984
+ "$ref": "#/components/schemas/Livemode"
1432
2985
  },
1433
- "limit": {
1434
- "type": "integer",
1435
- "example": 50
2986
+ "address": {
2987
+ "type": "string",
2988
+ "example": "0x7c2d9a1b3e4f5061728394a5b6c7d8e9f0a1b2c3"
1436
2989
  },
1437
- "offset": {
1438
- "type": "integer",
1439
- "example": 0
2990
+ "network_id": {
2991
+ "type": "string",
2992
+ "example": "60"
2993
+ },
2994
+ "eip155_id": {
2995
+ "type": "string",
2996
+ "example": "1"
2997
+ },
2998
+ "network_name": {
2999
+ "type": "string",
3000
+ "example": "Ethereum"
3001
+ },
3002
+ "accepted_assets": {
3003
+ "type": "array",
3004
+ "items": {
3005
+ "$ref": "#/components/schemas/DepositAsset"
3006
+ }
1440
3007
  }
1441
3008
  }
1442
3009
  },
1443
- "DestinationInput": {
3010
+ "Destination": {
1444
3011
  "type": "object",
1445
- "description": "Where settlements for this UDA should be delivered.",
3012
+ "description": "Resolved destination (symbol plus the resolved contract address).",
1446
3013
  "properties": {
1447
3014
  "address": {
1448
3015
  "type": "string",
1449
- "description": "Destination wallet address.",
1450
3016
  "example": "0x3a1f8b2c9d4e5f60718293a4b5c6d7e8f9012345"
1451
3017
  },
1452
3018
  "network_id": {
1453
3019
  "type": "string",
1454
- "description": "Destination SLIP-44 network id.",
1455
3020
  "example": "60"
1456
3021
  },
3022
+ "eip155_id": {
3023
+ "type": "string",
3024
+ "example": "1"
3025
+ },
3026
+ "network_name": {
3027
+ "type": "string",
3028
+ "example": "Ethereum"
3029
+ },
1457
3030
  "asset_symbol": {
1458
3031
  "type": "string",
1459
- "description": "Destination asset symbol.",
1460
3032
  "example": "USDC"
3033
+ },
3034
+ "asset_address": {
3035
+ "type": "string",
3036
+ "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
3037
+ },
3038
+ "asset_decimals": {
3039
+ "type": "integer",
3040
+ "example": 6
1461
3041
  }
1462
- },
1463
- "required": [
1464
- "address",
1465
- "network_id"
1466
- ]
3042
+ }
1467
3043
  },
1468
- "RoutingRuleMatchInput": {
3044
+ "RoutingRuleMatch": {
1469
3045
  "type": "object",
1470
- "description": "Conditions a deposit must meet for this routing rule to apply.",
1471
3046
  "properties": {
1472
3047
  "source_network_id": {
1473
3048
  "type": "string",
@@ -1476,13 +3051,14 @@
1476
3051
  "source_asset_symbol": {
1477
3052
  "type": "string",
1478
3053
  "example": "USDT"
3054
+ },
3055
+ "source_token_address": {
3056
+ "type": "string",
3057
+ "example": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
1479
3058
  }
1480
- },
1481
- "required": [
1482
- "source_network_id"
1483
- ]
3059
+ }
1484
3060
  },
1485
- "RoutingRuleDestinationInput": {
3061
+ "RoutingRuleDestination": {
1486
3062
  "type": "object",
1487
3063
  "properties": {
1488
3064
  "address": {
@@ -1493,152 +3069,297 @@
1493
3069
  "type": "string",
1494
3070
  "example": "60"
1495
3071
  },
3072
+ "eip155_id": {
3073
+ "type": "string",
3074
+ "example": "1"
3075
+ },
3076
+ "network_name": {
3077
+ "type": "string",
3078
+ "example": "Ethereum"
3079
+ },
1496
3080
  "asset_symbol": {
1497
3081
  "type": "string",
1498
3082
  "example": "USDC"
3083
+ },
3084
+ "asset_address": {
3085
+ "type": "string",
3086
+ "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
3087
+ },
3088
+ "asset_decimals": {
3089
+ "type": "integer",
3090
+ "example": 6
1499
3091
  }
1500
- },
1501
- "required": [
1502
- "address",
1503
- "network_id"
1504
- ]
3092
+ }
1505
3093
  },
1506
- "RoutingRuleInput": {
3094
+ "RoutingRule": {
1507
3095
  "type": "object",
3096
+ "description": "An echoed routing rule in dual form (symbol plus resolved contract address).",
1508
3097
  "properties": {
3098
+ "livemode": {
3099
+ "$ref": "#/components/schemas/Livemode"
3100
+ },
1509
3101
  "match": {
1510
- "$ref": "#/components/schemas/RoutingRuleMatchInput"
3102
+ "$ref": "#/components/schemas/RoutingRuleMatch"
1511
3103
  },
1512
3104
  "destination": {
1513
- "$ref": "#/components/schemas/RoutingRuleDestinationInput"
3105
+ "$ref": "#/components/schemas/RoutingRuleDestination"
1514
3106
  }
1515
- },
1516
- "required": [
1517
- "match",
1518
- "destination"
1519
- ]
3107
+ }
1520
3108
  },
1521
- "CreateUDAAddressRequest": {
3109
+ "UDAAddress": {
1522
3110
  "type": "object",
1523
3111
  "properties": {
3112
+ "livemode": {
3113
+ "$ref": "#/components/schemas/Livemode"
3114
+ },
3115
+ "id": {
3116
+ "type": "string",
3117
+ "example": "uda_2f8c1a9d4e5f6071"
3118
+ },
1524
3119
  "owner": {
1525
3120
  "type": "string",
1526
- "description": "The owner the UDA is allocated for.",
1527
3121
  "example": "0x9f8e7d6c5b4a39281706f5e4d3c2b1a098765432"
1528
3122
  },
3123
+ "status": {
3124
+ "type": "string",
3125
+ "enum": [
3126
+ "active",
3127
+ "retired"
3128
+ ],
3129
+ "example": "active"
3130
+ },
3131
+ "created_at": {
3132
+ "type": "string",
3133
+ "format": "date-time",
3134
+ "example": "2026-05-20T14:03:12Z"
3135
+ },
1529
3136
  "destination": {
1530
- "$ref": "#/components/schemas/DestinationInput"
3137
+ "$ref": "#/components/schemas/Destination"
1531
3138
  },
1532
- "accepted_assets": {
3139
+ "deposit_addresses": {
1533
3140
  "type": "array",
1534
3141
  "items": {
1535
- "type": "string"
1536
- },
1537
- "description": "Asset symbols this UDA accepts; empty means every asset on every UDA-supported chain.",
1538
- "example": [
1539
- "USDC",
1540
- "USDT"
1541
- ]
3142
+ "$ref": "#/components/schemas/DepositAddress"
3143
+ }
1542
3144
  },
1543
3145
  "routing_rules": {
1544
3146
  "type": "array",
3147
+ "description": "Empty when no rules are configured.",
1545
3148
  "items": {
1546
- "$ref": "#/components/schemas/RoutingRuleInput"
1547
- },
1548
- "description": "Optional; max 3. Specificity-wins precedence: a rule pinning both `source_network_id` and `source_asset_symbol` beats a network-only rule. When nothing matches, settlements use the default destination.\n"
3149
+ "$ref": "#/components/schemas/RoutingRule"
3150
+ }
1549
3151
  },
1550
3152
  "routing_policy_id": {
1551
3153
  "type": "string",
1552
- "description": "Routing policy to attach to this UDA. Omit or send an empty string to attach the tenant's default policy. Per-UDA inline `routing_rules` still take precedence over the policy.\n",
3154
+ "description": "The routing policy attached to this UDA, when set. Omitted for legacy UDAs that inherit the tenant default policy.\n",
1553
3155
  "example": "rpol_2f8c1a9d4e5f6071"
1554
3156
  }
1555
- },
1556
- "required": [
1557
- "owner",
1558
- "destination"
1559
- ],
1560
- "example": {
1561
- "owner": "0x9f8e7d6c5b4a39281706f5e4d3c2b1a098765432",
1562
- "destination": {
1563
- "address": "0x3a1f8b2c9d4e5f60718293a4b5c6d7e8f9012345",
1564
- "network_id": "60",
1565
- "asset_symbol": "USDC"
3157
+ }
3158
+ },
3159
+ "RoutingPolicy": {
3160
+ "type": "object",
3161
+ "description": "A tenant-scoped, ordered routing-policy. Rules are evaluated first-match-wins; an empty rule set falls through to the UDA's default destination.\n",
3162
+ "properties": {
3163
+ "livemode": {
3164
+ "$ref": "#/components/schemas/Livemode"
1566
3165
  },
1567
- "accepted_assets": [
1568
- "USDC",
1569
- "USDT"
1570
- ]
3166
+ "id": {
3167
+ "type": "string",
3168
+ "example": "rpol_2f8c1a9d4e5f6071"
3169
+ },
3170
+ "name": {
3171
+ "type": "string",
3172
+ "example": "vip_tier"
3173
+ },
3174
+ "is_default": {
3175
+ "type": "boolean",
3176
+ "description": "Whether this is the tenant's default policy (one per tenant).",
3177
+ "example": false
3178
+ },
3179
+ "rules": {
3180
+ "type": "array",
3181
+ "description": "Ordered, first-match-wins routing rules (dual form).",
3182
+ "items": {
3183
+ "$ref": "#/components/schemas/RoutingRule"
3184
+ }
3185
+ },
3186
+ "created_at": {
3187
+ "type": "string",
3188
+ "format": "date-time",
3189
+ "example": "2026-05-20T14:03:12Z"
3190
+ },
3191
+ "updated_at": {
3192
+ "type": "string",
3193
+ "format": "date-time",
3194
+ "example": "2026-05-20T14:03:12Z"
3195
+ }
3196
+ }
3197
+ },
3198
+ "RoutingPolicyResponse": {
3199
+ "type": "object",
3200
+ "properties": {
3201
+ "data": {
3202
+ "$ref": "#/components/schemas/RoutingPolicy"
3203
+ }
3204
+ }
3205
+ },
3206
+ "RoutingPolicyList": {
3207
+ "type": "object",
3208
+ "properties": {
3209
+ "data": {
3210
+ "type": "array",
3211
+ "items": {
3212
+ "$ref": "#/components/schemas/RoutingPolicy"
3213
+ }
3214
+ },
3215
+ "pagination": {
3216
+ "$ref": "#/components/schemas/PagePagination"
3217
+ }
3218
+ }
3219
+ },
3220
+ "UDAAddressList": {
3221
+ "type": "object",
3222
+ "properties": {
3223
+ "livemode": {
3224
+ "$ref": "#/components/schemas/Livemode"
3225
+ },
3226
+ "data": {
3227
+ "type": "array",
3228
+ "items": {
3229
+ "$ref": "#/components/schemas/UDAAddress"
3230
+ }
3231
+ },
3232
+ "pagination": {
3233
+ "$ref": "#/components/schemas/Pagination"
3234
+ }
3235
+ }
3236
+ },
3237
+ "QuoteSide": {
3238
+ "type": "object",
3239
+ "properties": {
3240
+ "network_id": {
3241
+ "type": "integer",
3242
+ "description": "EIP-155 chain id for this side.",
3243
+ "example": 1
3244
+ },
3245
+ "asset_address": {
3246
+ "type": "string",
3247
+ "example": "0x0000000000000000000000000000000000000000"
3248
+ },
3249
+ "amount": {
3250
+ "type": "string",
3251
+ "description": "Amount in the asset's smallest unit.",
3252
+ "example": "1000000000000000000"
3253
+ }
1571
3254
  }
1572
3255
  },
1573
- "DepositAsset": {
3256
+ "QuoteFeeItem": {
1574
3257
  "type": "object",
1575
3258
  "properties": {
1576
- "symbol": {
3259
+ "kind": {
1577
3260
  "type": "string",
1578
- "example": "USDC"
3261
+ "example": "platform"
1579
3262
  },
1580
- "address": {
3263
+ "amount": {
1581
3264
  "type": "string",
1582
- "description": "Token contract address (zero address for native).",
1583
- "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
3265
+ "example": "2500000000000000"
1584
3266
  },
1585
- "decimals": {
1586
- "type": "integer",
1587
- "example": 6
3267
+ "source": {
3268
+ "type": "string",
3269
+ "example": "stridge"
1588
3270
  },
1589
- "logo": {
3271
+ "recipient": {
1590
3272
  "type": "string",
1591
- "example": "https://assets.stridge.com/tokens/usdc.png"
3273
+ "example": "0xfee0000000000000000000000000000000000000"
1592
3274
  }
1593
3275
  }
1594
3276
  },
1595
- "DepositAddress": {
3277
+ "QuoteFees": {
1596
3278
  "type": "object",
1597
- "description": "A per-chain address a depositor can send funds to.",
1598
3279
  "properties": {
1599
- "address": {
3280
+ "total_fee": {
1600
3281
  "type": "string",
1601
- "example": "0x7c2d9a1b3e4f5061728394a5b6c7d8e9f0a1b2c3"
3282
+ "example": "3500000000000000"
1602
3283
  },
1603
- "network_id": {
3284
+ "gas_fee": {
1604
3285
  "type": "string",
1605
- "example": "60"
3286
+ "example": "1000000000000000"
1606
3287
  },
1607
- "eip155_id": {
3288
+ "network_reserve": {
1608
3289
  "type": "string",
1609
- "example": "1"
3290
+ "description": "Chain-level reserve (e.g. TRON bandwidth).",
3291
+ "example": "0"
1610
3292
  },
1611
- "network_name": {
3293
+ "platform_fee": {
1612
3294
  "type": "string",
1613
- "example": "Ethereum"
3295
+ "example": "2500000000000000"
1614
3296
  },
1615
- "accepted_assets": {
3297
+ "provider_fee": {
3298
+ "type": "string",
3299
+ "description": "Third-party (e.g. LiFi / Relay) fee.",
3300
+ "example": "0"
3301
+ },
3302
+ "items": {
1616
3303
  "type": "array",
1617
3304
  "items": {
1618
- "$ref": "#/components/schemas/DepositAsset"
3305
+ "$ref": "#/components/schemas/QuoteFeeItem"
1619
3306
  }
1620
3307
  }
1621
3308
  }
1622
3309
  },
1623
- "Destination": {
3310
+ "QuoteRoute": {
1624
3311
  "type": "object",
1625
- "description": "Resolved destination (symbol plus the resolved contract address).",
1626
3312
  "properties": {
1627
- "address": {
3313
+ "provider": {
1628
3314
  "type": "string",
1629
- "example": "0x3a1f8b2c9d4e5f60718293a4b5c6d7e8f9012345"
3315
+ "example": "lifi"
1630
3316
  },
1631
- "network_id": {
3317
+ "scenario": {
1632
3318
  "type": "string",
1633
- "example": "60"
3319
+ "example": "cross_chain_swap"
1634
3320
  },
1635
- "eip155_id": {
3321
+ "estimated_time_seconds": {
3322
+ "type": "integer",
3323
+ "example": 45
3324
+ }
3325
+ }
3326
+ },
3327
+ "Quote": {
3328
+ "type": "object",
3329
+ "properties": {
3330
+ "from": {
3331
+ "$ref": "#/components/schemas/QuoteSide"
3332
+ },
3333
+ "to": {
3334
+ "$ref": "#/components/schemas/QuoteSide"
3335
+ },
3336
+ "exchange_rate": {
1636
3337
  "type": "string",
1637
- "example": "1"
3338
+ "example": "0.9985"
1638
3339
  },
1639
- "network_name": {
3340
+ "expires_at": {
1640
3341
  "type": "string",
1641
- "example": "Ethereum"
3342
+ "format": "date-time",
3343
+ "example": "2026-05-29T12:05:00Z"
3344
+ },
3345
+ "fees": {
3346
+ "$ref": "#/components/schemas/QuoteFees"
3347
+ },
3348
+ "route": {
3349
+ "$ref": "#/components/schemas/QuoteRoute"
3350
+ }
3351
+ }
3352
+ },
3353
+ "SettlementDeposit": {
3354
+ "type": "object",
3355
+ "properties": {
3356
+ "address": {
3357
+ "type": "string",
3358
+ "example": "0x7c2d9a1b3e4f5061728394a5b6c7d8e9f0a1b2c3"
3359
+ },
3360
+ "amount": {
3361
+ "type": "string",
3362
+ "example": "1000000"
1642
3363
  },
1643
3364
  "asset_symbol": {
1644
3365
  "type": "string",
@@ -1648,47 +3369,35 @@
1648
3369
  "type": "string",
1649
3370
  "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
1650
3371
  },
1651
- "asset_decimals": {
3372
+ "network_id": {
1652
3373
  "type": "integer",
1653
- "example": 6
1654
- }
1655
- }
1656
- },
1657
- "RoutingRuleMatch": {
1658
- "type": "object",
1659
- "properties": {
1660
- "source_network_id": {
3374
+ "example": 1
3375
+ },
3376
+ "network_name": {
1661
3377
  "type": "string",
1662
- "example": "60"
3378
+ "example": "Ethereum"
1663
3379
  },
1664
- "source_asset_symbol": {
3380
+ "tx_hash": {
1665
3381
  "type": "string",
1666
- "example": "USDT"
3382
+ "example": "0xab12cd34ef56...7890"
1667
3383
  },
1668
- "source_token_address": {
3384
+ "confirmed_at": {
1669
3385
  "type": "string",
1670
- "example": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
3386
+ "format": "date-time",
3387
+ "example": "2026-05-29T11:58:40Z"
1671
3388
  }
1672
3389
  }
1673
3390
  },
1674
- "RoutingRuleDestination": {
3391
+ "SettlementOutbound": {
1675
3392
  "type": "object",
1676
3393
  "properties": {
1677
3394
  "address": {
1678
3395
  "type": "string",
1679
3396
  "example": "0x3a1f8b2c9d4e5f60718293a4b5c6d7e8f9012345"
1680
3397
  },
1681
- "network_id": {
1682
- "type": "string",
1683
- "example": "60"
1684
- },
1685
- "eip155_id": {
1686
- "type": "string",
1687
- "example": "1"
1688
- },
1689
- "network_name": {
3398
+ "amount": {
1690
3399
  "type": "string",
1691
- "example": "Ethereum"
3400
+ "example": "998500"
1692
3401
  },
1693
3402
  "asset_symbol": {
1694
3403
  "type": "string",
@@ -1696,130 +3405,130 @@
1696
3405
  },
1697
3406
  "asset_address": {
1698
3407
  "type": "string",
1699
- "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
3408
+ "example": "0x55d398326f99059fF775485246999027B3197955"
1700
3409
  },
1701
- "asset_decimals": {
3410
+ "network_id": {
1702
3411
  "type": "integer",
1703
- "example": 6
1704
- }
1705
- }
1706
- },
1707
- "RoutingRule": {
1708
- "type": "object",
1709
- "description": "An echoed routing rule in dual form (symbol plus resolved contract address).",
1710
- "properties": {
1711
- "match": {
1712
- "$ref": "#/components/schemas/RoutingRuleMatch"
3412
+ "example": 56
1713
3413
  },
1714
- "destination": {
1715
- "$ref": "#/components/schemas/RoutingRuleDestination"
3414
+ "network_name": {
3415
+ "type": "string",
3416
+ "example": "BNB Smart Chain"
3417
+ },
3418
+ "tx_hash": {
3419
+ "type": "string",
3420
+ "example": "0xcd34ef5678...90ab"
3421
+ },
3422
+ "settled_at": {
3423
+ "type": "string",
3424
+ "format": "date-time",
3425
+ "example": "2026-05-29T11:59:25Z"
1716
3426
  }
1717
3427
  }
1718
3428
  },
1719
- "UDAAddress": {
3429
+ "SettlementFees": {
1720
3430
  "type": "object",
1721
3431
  "properties": {
1722
- "id": {
3432
+ "total": {
1723
3433
  "type": "string",
1724
- "example": "uda_2f8c1a9d4e5f6071"
3434
+ "example": "1500"
1725
3435
  },
1726
- "owner": {
3436
+ "platform": {
1727
3437
  "type": "string",
1728
- "example": "0x9f8e7d6c5b4a39281706f5e4d3c2b1a098765432"
3438
+ "example": "1500"
1729
3439
  },
1730
- "status": {
3440
+ "provider": {
1731
3441
  "type": "string",
1732
- "enum": [
1733
- "active",
1734
- "retired"
1735
- ],
1736
- "example": "active"
3442
+ "example": "0"
1737
3443
  },
1738
- "created_at": {
3444
+ "fee_amount": {
1739
3445
  "type": "string",
1740
- "format": "date-time",
1741
- "example": "2026-05-20T14:03:12Z"
1742
- },
1743
- "destination": {
1744
- "$ref": "#/components/schemas/Destination"
1745
- },
1746
- "deposit_addresses": {
1747
- "type": "array",
1748
- "items": {
1749
- "$ref": "#/components/schemas/DepositAddress"
1750
- }
1751
- },
1752
- "routing_rules": {
1753
- "type": "array",
1754
- "description": "Empty when no rules are configured.",
1755
- "items": {
1756
- "$ref": "#/components/schemas/RoutingRule"
1757
- }
3446
+ "description": "Equal to `total` (back-compat field).",
3447
+ "example": "1500"
3448
+ }
3449
+ }
3450
+ },
3451
+ "SettlementRoute": {
3452
+ "type": "object",
3453
+ "properties": {
3454
+ "provider": {
3455
+ "type": "string",
3456
+ "example": "lifi"
1758
3457
  },
1759
- "routing_policy_id": {
3458
+ "type": {
1760
3459
  "type": "string",
1761
- "description": "The routing policy attached to this UDA, when set. Omitted for legacy UDAs that inherit the tenant default policy.\n",
1762
- "example": "rpol_2f8c1a9d4e5f6071"
3460
+ "example": "cross_chain_swap"
3461
+ },
3462
+ "time_ms": {
3463
+ "type": "integer",
3464
+ "example": 45000
1763
3465
  }
1764
3466
  }
1765
3467
  },
1766
- "RoutingPolicy": {
3468
+ "Settlement": {
1767
3469
  "type": "object",
1768
- "description": "A tenant-scoped, ordered routing-policy. Rules are evaluated first-match-wins; an empty rule set falls through to the UDA's default destination.\n",
1769
3470
  "properties": {
3471
+ "livemode": {
3472
+ "$ref": "#/components/schemas/Livemode"
3473
+ },
1770
3474
  "id": {
1771
3475
  "type": "string",
1772
- "example": "rpol_2f8c1a9d4e5f6071"
3476
+ "example": "stl_71829304a5b6c7d8"
1773
3477
  },
1774
- "name": {
3478
+ "uda_id": {
1775
3479
  "type": "string",
1776
- "example": "vip_tier"
1777
- },
1778
- "is_default": {
1779
- "type": "boolean",
1780
- "description": "Whether this is the tenant's default policy (one per tenant).",
1781
- "example": false
3480
+ "example": "uda_2f8c1a9d4e5f6071"
1782
3481
  },
1783
- "rules": {
1784
- "type": "array",
1785
- "description": "Ordered, first-match-wins routing rules (dual form).",
1786
- "items": {
1787
- "$ref": "#/components/schemas/RoutingRule"
1788
- }
3482
+ "status": {
3483
+ "type": "string",
3484
+ "enum": [
3485
+ "created",
3486
+ "routing",
3487
+ "completed",
3488
+ "failed",
3489
+ "retrying"
3490
+ ],
3491
+ "example": "completed"
1789
3492
  },
1790
3493
  "created_at": {
1791
3494
  "type": "string",
1792
3495
  "format": "date-time",
1793
- "example": "2026-05-20T14:03:12Z"
3496
+ "example": "2026-05-29T11:58:41Z"
1794
3497
  },
1795
3498
  "updated_at": {
1796
3499
  "type": "string",
1797
3500
  "format": "date-time",
1798
- "example": "2026-05-20T14:03:12Z"
1799
- }
1800
- }
1801
- },
1802
- "RoutingPolicyList": {
1803
- "type": "object",
1804
- "properties": {
1805
- "data": {
1806
- "type": "array",
1807
- "items": {
1808
- "$ref": "#/components/schemas/RoutingPolicy"
1809
- }
3501
+ "example": "2026-05-29T11:59:25Z"
1810
3502
  },
1811
- "pagination": {
1812
- "$ref": "#/components/schemas/Pagination"
3503
+ "error": {
3504
+ "type": "string",
3505
+ "description": "Failure reason when `status` is `failed`; empty otherwise.",
3506
+ "example": ""
3507
+ },
3508
+ "deposit": {
3509
+ "$ref": "#/components/schemas/SettlementDeposit"
3510
+ },
3511
+ "settlement": {
3512
+ "$ref": "#/components/schemas/SettlementOutbound"
3513
+ },
3514
+ "fees": {
3515
+ "$ref": "#/components/schemas/SettlementFees"
3516
+ },
3517
+ "route": {
3518
+ "$ref": "#/components/schemas/SettlementRoute"
1813
3519
  }
1814
3520
  }
1815
3521
  },
1816
- "UDAAddressList": {
3522
+ "SettlementList": {
1817
3523
  "type": "object",
1818
3524
  "properties": {
3525
+ "livemode": {
3526
+ "$ref": "#/components/schemas/Livemode"
3527
+ },
1819
3528
  "data": {
1820
3529
  "type": "array",
1821
3530
  "items": {
1822
- "$ref": "#/components/schemas/UDAAddress"
3531
+ "$ref": "#/components/schemas/Settlement"
1823
3532
  }
1824
3533
  },
1825
3534
  "pagination": {
@@ -1827,514 +3536,595 @@
1827
3536
  }
1828
3537
  }
1829
3538
  },
1830
- "QuoteSide": {
1831
- "type": "object",
1832
- "properties": {
1833
- "network_id": {
1834
- "type": "integer",
1835
- "description": "EIP-155 chain id for this side.",
1836
- "example": 1
1837
- },
1838
- "asset_address": {
1839
- "type": "string",
1840
- "example": "0x0000000000000000000000000000000000000000"
1841
- },
1842
- "amount": {
1843
- "type": "string",
1844
- "description": "Amount in the asset's smallest unit.",
1845
- "example": "1000000000000000000"
1846
- }
1847
- }
1848
- },
1849
- "QuoteFeeItem": {
3539
+ "SettlementsByReference": {
1850
3540
  "type": "object",
1851
3541
  "properties": {
1852
- "kind": {
1853
- "type": "string",
1854
- "example": "platform"
3542
+ "livemode": {
3543
+ "$ref": "#/components/schemas/Livemode"
1855
3544
  },
1856
- "amount": {
3545
+ "reference": {
1857
3546
  "type": "string",
1858
- "example": "2500000000000000"
3547
+ "description": "The reference that was looked up.",
3548
+ "example": "0xab12cd34ef56...7890"
1859
3549
  },
1860
- "source": {
1861
- "type": "string",
1862
- "example": "stridge"
3550
+ "count": {
3551
+ "type": "integer",
3552
+ "example": 1
1863
3553
  },
1864
- "recipient": {
1865
- "type": "string",
1866
- "example": "0xfee0000000000000000000000000000000000000"
3554
+ "data": {
3555
+ "type": "array",
3556
+ "items": {
3557
+ "$ref": "#/components/schemas/Settlement"
3558
+ }
1867
3559
  }
1868
3560
  }
1869
3561
  },
1870
- "QuoteFees": {
3562
+ "NativeCurrency": {
1871
3563
  "type": "object",
1872
3564
  "properties": {
1873
- "total_fee": {
3565
+ "symbol": {
1874
3566
  "type": "string",
1875
- "example": "3500000000000000"
3567
+ "example": "ETH"
1876
3568
  },
1877
- "gas_fee": {
3569
+ "name": {
1878
3570
  "type": "string",
1879
- "example": "1000000000000000"
3571
+ "example": "Ether"
1880
3572
  },
1881
- "network_reserve": {
3573
+ "decimals": {
3574
+ "type": "integer",
3575
+ "example": 18
3576
+ },
3577
+ "logo": {
1882
3578
  "type": "string",
1883
- "description": "Chain-level reserve (e.g. TRON bandwidth).",
1884
- "example": "0"
3579
+ "example": "https://assets.stridge.com/tokens/eth.png"
1885
3580
  },
1886
- "platform_fee": {
3581
+ "min_deposit_usd": {
1887
3582
  "type": "string",
1888
- "example": "2500000000000000"
3583
+ "example": "1.00"
1889
3584
  },
1890
- "provider_fee": {
3585
+ "price_impact": {
1891
3586
  "type": "string",
1892
- "description": "Third-party (e.g. LiFi / Relay) fee.",
1893
3587
  "example": "0"
1894
- },
1895
- "items": {
1896
- "type": "array",
1897
- "items": {
1898
- "$ref": "#/components/schemas/QuoteFeeItem"
1899
- }
1900
3588
  }
1901
3589
  }
1902
3590
  },
1903
- "QuoteRoute": {
3591
+ "AssetInfo": {
1904
3592
  "type": "object",
1905
3593
  "properties": {
1906
- "provider": {
3594
+ "symbol": {
1907
3595
  "type": "string",
1908
- "example": "lifi"
3596
+ "example": "USDC"
1909
3597
  },
1910
- "scenario": {
3598
+ "name": {
1911
3599
  "type": "string",
1912
- "example": "cross_chain_swap"
3600
+ "example": "USD Coin"
1913
3601
  },
1914
- "estimated_time_seconds": {
1915
- "type": "integer",
1916
- "example": 45
1917
- }
1918
- }
1919
- },
1920
- "Quote": {
1921
- "type": "object",
1922
- "properties": {
1923
- "from": {
1924
- "$ref": "#/components/schemas/QuoteSide"
3602
+ "address": {
3603
+ "type": "string",
3604
+ "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
1925
3605
  },
1926
- "to": {
1927
- "$ref": "#/components/schemas/QuoteSide"
3606
+ "decimals": {
3607
+ "type": "integer",
3608
+ "example": 6
1928
3609
  },
1929
- "exchange_rate": {
3610
+ "logo": {
1930
3611
  "type": "string",
1931
- "example": "0.9985"
3612
+ "example": "https://assets.stridge.com/tokens/usdc.png"
1932
3613
  },
1933
- "expires_at": {
3614
+ "min_deposit_usd": {
1934
3615
  "type": "string",
1935
- "format": "date-time",
1936
- "example": "2026-05-29T12:05:00Z"
1937
- },
1938
- "fees": {
1939
- "$ref": "#/components/schemas/QuoteFees"
3616
+ "example": "1.00"
1940
3617
  },
1941
- "route": {
1942
- "$ref": "#/components/schemas/QuoteRoute"
3618
+ "price_impact": {
3619
+ "type": "string",
3620
+ "example": "0"
1943
3621
  }
1944
3622
  }
1945
3623
  },
1946
- "SettlementDeposit": {
3624
+ "SupportedAssetChain": {
1947
3625
  "type": "object",
1948
3626
  "properties": {
1949
- "address": {
1950
- "type": "string",
1951
- "example": "0x7c2d9a1b3e4f5061728394a5b6c7d8e9f0a1b2c3"
1952
- },
1953
- "amount": {
3627
+ "network_id": {
1954
3628
  "type": "string",
1955
- "example": "1000000"
3629
+ "example": "60"
1956
3630
  },
1957
- "asset_symbol": {
3631
+ "network_name": {
1958
3632
  "type": "string",
1959
- "example": "USDC"
3633
+ "example": "Ethereum"
1960
3634
  },
1961
- "asset_address": {
3635
+ "network_symbol": {
1962
3636
  "type": "string",
1963
- "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
3637
+ "example": "ETH"
1964
3638
  },
1965
- "network_id": {
3639
+ "eip155_id": {
1966
3640
  "type": "integer",
3641
+ "description": "Chain-specific numeric id: the EIP-155 chain id for EVM networks (Tempo is `4217`), or the analogous numeric chain id Stridge uses for non-EVM networks (TVM, SVM, Bitcoin). Always present.\n",
1967
3642
  "example": 1
1968
3643
  },
1969
- "network_name": {
3644
+ "chain_type": {
1970
3645
  "type": "string",
1971
- "example": "Ethereum"
3646
+ "enum": [
3647
+ "EVM",
3648
+ "TVM",
3649
+ "SVM",
3650
+ "BITCOIN"
3651
+ ],
3652
+ "description": "The network's chain family: `EVM` (Ethereum, BNB Smart Chain, Polygon, Arbitrum, Base, Optimism, Tempo), `TVM` (Tron), `SVM` (Solana) or `BITCOIN` (Bitcoin).\n",
3653
+ "example": "EVM"
1972
3654
  },
1973
- "tx_hash": {
1974
- "type": "string",
1975
- "example": "0xab12cd34ef56...7890"
3655
+ "native_currency": {
3656
+ "$ref": "#/components/schemas/NativeCurrency"
1976
3657
  },
1977
- "confirmed_at": {
1978
- "type": "string",
1979
- "format": "date-time",
1980
- "example": "2026-05-29T11:58:40Z"
3658
+ "assets": {
3659
+ "type": "array",
3660
+ "items": {
3661
+ "$ref": "#/components/schemas/AssetInfo"
3662
+ }
1981
3663
  }
1982
3664
  }
1983
3665
  },
1984
- "SettlementOutbound": {
3666
+ "SupportedAssetsResponse": {
1985
3667
  "type": "object",
1986
3668
  "properties": {
1987
- "address": {
1988
- "type": "string",
1989
- "example": "0x3a1f8b2c9d4e5f60718293a4b5c6d7e8f9012345"
1990
- },
1991
- "amount": {
1992
- "type": "string",
1993
- "example": "998500"
1994
- },
1995
- "asset_symbol": {
1996
- "type": "string",
1997
- "example": "USDC"
3669
+ "assets": {
3670
+ "type": "array",
3671
+ "items": {
3672
+ "$ref": "#/components/schemas/SupportedAssetChain"
3673
+ }
3674
+ }
3675
+ }
3676
+ },
3677
+ "PagePagination": {
3678
+ "type": "object",
3679
+ "description": "Page-based pagination metadata returned by the payout, gas-station, routing-policy and simulation lists.",
3680
+ "properties": {
3681
+ "page": {
3682
+ "type": "integer",
3683
+ "example": 1
1998
3684
  },
1999
- "asset_address": {
2000
- "type": "string",
2001
- "example": "0x55d398326f99059fF775485246999027B3197955"
3685
+ "limit": {
3686
+ "type": "integer",
3687
+ "example": 20
2002
3688
  },
2003
- "network_id": {
3689
+ "total_items": {
2004
3690
  "type": "integer",
2005
- "example": 56
3691
+ "example": 100
2006
3692
  },
2007
- "network_name": {
2008
- "type": "string",
2009
- "example": "BNB Smart Chain"
3693
+ "total_pages": {
3694
+ "type": "integer",
3695
+ "example": 5
2010
3696
  },
2011
- "tx_hash": {
2012
- "type": "string",
2013
- "example": "0xcd34ef5678...90ab"
3697
+ "has_next": {
3698
+ "type": "boolean",
3699
+ "example": true
2014
3700
  },
2015
- "settled_at": {
2016
- "type": "string",
2017
- "format": "date-time",
2018
- "example": "2026-05-29T11:59:25Z"
3701
+ "has_prev": {
3702
+ "type": "boolean",
3703
+ "example": false
2019
3704
  }
2020
3705
  }
2021
3706
  },
2022
- "SettlementFees": {
3707
+ "MoneyValue": {
2023
3708
  "type": "object",
3709
+ "description": "A single balance rendered in human, raw, and USD forms.",
2024
3710
  "properties": {
2025
- "total": {
2026
- "type": "string",
2027
- "example": "1500"
2028
- },
2029
- "platform": {
3711
+ "amount": {
2030
3712
  "type": "string",
2031
- "example": "1500"
3713
+ "description": "Human-readable amount (already divided by decimals).",
3714
+ "example": "1.5"
2032
3715
  },
2033
- "provider": {
3716
+ "raw": {
2034
3717
  "type": "string",
2035
- "example": "0"
3718
+ "description": "Amount in the asset's smallest unit.",
3719
+ "example": "1000000000000000000"
2036
3720
  },
2037
- "fee_amount": {
3721
+ "usd": {
2038
3722
  "type": "string",
2039
- "description": "Equal to `total` (back-compat field).",
2040
- "example": "1500"
3723
+ "description": "Indicative USD value.",
3724
+ "example": "3500.00"
2041
3725
  }
2042
3726
  }
2043
3727
  },
2044
- "SettlementRoute": {
3728
+ "Vault": {
2045
3729
  "type": "object",
2046
3730
  "properties": {
2047
- "provider": {
2048
- "type": "string",
2049
- "example": "lifi"
3731
+ "livemode": {
3732
+ "$ref": "#/components/schemas/Livemode"
2050
3733
  },
2051
- "type": {
3734
+ "id": {
2052
3735
  "type": "string",
2053
- "example": "cross_chain_swap"
3736
+ "example": "550e8400-e29b-41d4-a716-446655440000"
2054
3737
  },
2055
- "time_ms": {
2056
- "type": "integer",
2057
- "example": 45000
3738
+ "label": {
3739
+ "type": "string",
3740
+ "description": "Human-readable vault name.",
3741
+ "example": "hot"
2058
3742
  }
2059
3743
  }
2060
3744
  },
2061
- "Settlement": {
3745
+ "VaultList": {
2062
3746
  "type": "object",
2063
3747
  "properties": {
2064
- "id": {
3748
+ "data": {
3749
+ "type": "array",
3750
+ "items": {
3751
+ "$ref": "#/components/schemas/Vault"
3752
+ }
3753
+ }
3754
+ }
3755
+ },
3756
+ "VaultAddress": {
3757
+ "type": "object",
3758
+ "properties": {
3759
+ "livemode": {
3760
+ "$ref": "#/components/schemas/Livemode"
3761
+ },
3762
+ "address": {
2065
3763
  "type": "string",
2066
- "example": "stl_71829304a5b6c7d8"
3764
+ "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD58"
2067
3765
  },
2068
- "uda_id": {
3766
+ "network_id": {
2069
3767
  "type": "string",
2070
- "example": "uda_2f8c1a9d4e5f6071"
3768
+ "example": "60"
2071
3769
  },
2072
- "status": {
3770
+ "network_symbol": {
2073
3771
  "type": "string",
2074
- "enum": [
2075
- "created",
2076
- "routing",
2077
- "completed",
2078
- "failed",
2079
- "retrying"
2080
- ],
2081
- "example": "completed"
3772
+ "example": "ETH"
2082
3773
  },
2083
3774
  "created_at": {
2084
3775
  "type": "string",
2085
3776
  "format": "date-time",
2086
- "example": "2026-05-29T11:58:41Z"
3777
+ "example": "2024-01-01T00:00:00Z"
3778
+ }
3779
+ }
3780
+ },
3781
+ "VaultAddressList": {
3782
+ "type": "object",
3783
+ "properties": {
3784
+ "data": {
3785
+ "type": "object",
3786
+ "properties": {
3787
+ "livemode": {
3788
+ "$ref": "#/components/schemas/Livemode"
3789
+ },
3790
+ "addresses": {
3791
+ "type": "array",
3792
+ "items": {
3793
+ "$ref": "#/components/schemas/VaultAddress"
3794
+ }
3795
+ }
3796
+ }
3797
+ }
3798
+ }
3799
+ },
3800
+ "VaultAddressResponse": {
3801
+ "type": "object",
3802
+ "properties": {
3803
+ "data": {
3804
+ "$ref": "#/components/schemas/VaultAddress"
3805
+ }
3806
+ }
3807
+ },
3808
+ "VaultBalance": {
3809
+ "type": "object",
3810
+ "properties": {
3811
+ "livemode": {
3812
+ "$ref": "#/components/schemas/Livemode"
2087
3813
  },
2088
- "updated_at": {
3814
+ "asset": {
2089
3815
  "type": "string",
2090
- "format": "date-time",
2091
- "example": "2026-05-29T11:59:25Z"
3816
+ "example": "Ethereum"
2092
3817
  },
2093
- "error": {
3818
+ "network_id": {
2094
3819
  "type": "string",
2095
- "description": "Failure reason when `status` is `failed`; empty otherwise.",
2096
- "example": ""
3820
+ "example": "60"
2097
3821
  },
2098
- "deposit": {
2099
- "$ref": "#/components/schemas/SettlementDeposit"
3822
+ "network_symbol": {
3823
+ "type": "string",
3824
+ "example": "ETH"
2100
3825
  },
2101
- "settlement": {
2102
- "$ref": "#/components/schemas/SettlementOutbound"
3826
+ "raw_value": {
3827
+ "type": "string",
3828
+ "description": "Balance in the asset's smallest unit.",
3829
+ "example": "1000000000000000000"
2103
3830
  },
2104
- "fees": {
2105
- "$ref": "#/components/schemas/SettlementFees"
3831
+ "raw_value_decimals": {
3832
+ "type": "integer",
3833
+ "example": 18
2106
3834
  },
2107
- "route": {
2108
- "$ref": "#/components/schemas/SettlementRoute"
3835
+ "display_values": {
3836
+ "$ref": "#/components/schemas/MoneyValue"
2109
3837
  }
2110
3838
  }
2111
3839
  },
2112
- "SettlementList": {
3840
+ "VaultBalancesResponse": {
2113
3841
  "type": "object",
2114
3842
  "properties": {
2115
3843
  "data": {
2116
- "type": "array",
2117
- "items": {
2118
- "$ref": "#/components/schemas/Settlement"
3844
+ "type": "object",
3845
+ "properties": {
3846
+ "livemode": {
3847
+ "$ref": "#/components/schemas/Livemode"
3848
+ },
3849
+ "balances": {
3850
+ "type": "array",
3851
+ "items": {
3852
+ "$ref": "#/components/schemas/VaultBalance"
3853
+ }
3854
+ }
2119
3855
  }
2120
- },
2121
- "pagination": {
2122
- "$ref": "#/components/schemas/Pagination"
2123
3856
  }
2124
3857
  }
2125
3858
  },
2126
- "SettlementsByReference": {
3859
+ "VaultTotal": {
2127
3860
  "type": "object",
2128
3861
  "properties": {
2129
- "reference": {
2130
- "type": "string",
2131
- "description": "The reference that was looked up.",
2132
- "example": "0xab12cd34ef56...7890"
3862
+ "livemode": {
3863
+ "$ref": "#/components/schemas/Livemode"
2133
3864
  },
2134
- "count": {
2135
- "type": "integer",
2136
- "example": 1
3865
+ "total_usd": {
3866
+ "type": "string",
3867
+ "description": "Combined USD value of every asset in the vault.",
3868
+ "example": "25000.00"
2137
3869
  },
2138
- "data": {
3870
+ "included_currency": {
2139
3871
  "type": "array",
2140
3872
  "items": {
2141
- "$ref": "#/components/schemas/Settlement"
2142
- }
3873
+ "type": "string"
3874
+ },
3875
+ "example": [
3876
+ "ETH",
3877
+ "USDT"
3878
+ ]
2143
3879
  }
2144
3880
  }
2145
3881
  },
2146
- "NativeCurrency": {
3882
+ "VaultTotalResponse": {
2147
3883
  "type": "object",
2148
3884
  "properties": {
2149
- "symbol": {
3885
+ "data": {
3886
+ "$ref": "#/components/schemas/VaultTotal"
3887
+ }
3888
+ }
3889
+ },
3890
+ "CreatePayoutRequest": {
3891
+ "type": "object",
3892
+ "description": "A payout from a vault to an external address. Provide either `amount` or `usd_amount`, not both.\n",
3893
+ "properties": {
3894
+ "vault_id": {
2150
3895
  "type": "string",
2151
- "example": "ETH"
3896
+ "description": "The vault the funds leave from.",
3897
+ "example": "550e8400-e29b-41d4-a716-446655440001"
2152
3898
  },
2153
- "name": {
3899
+ "asset": {
2154
3900
  "type": "string",
2155
- "example": "Ether"
3901
+ "description": "Asset symbol to send.",
3902
+ "example": "USDT"
2156
3903
  },
2157
- "decimals": {
2158
- "type": "integer",
2159
- "example": 18
3904
+ "network_symbol": {
3905
+ "type": "string",
3906
+ "description": "Network slug (e.g. `ethereum`, `tron`, `solana`, `bitcoin`, `tempo`).",
3907
+ "example": "ethereum"
2160
3908
  },
2161
- "logo": {
3909
+ "to_address": {
2162
3910
  "type": "string",
2163
- "example": "https://assets.stridge.com/tokens/eth.png"
3911
+ "description": "External destination address.",
3912
+ "example": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
2164
3913
  },
2165
- "min_deposit_usd": {
3914
+ "reference_id": {
2166
3915
  "type": "string",
2167
- "example": "1.00"
3916
+ "description": "Your idempotency key — unique per vault.",
3917
+ "example": "order-12345"
2168
3918
  },
2169
- "price_impact": {
3919
+ "amount": {
2170
3920
  "type": "string",
2171
- "example": "0"
3921
+ "description": "Crypto amount; provide either `amount` or `usd_amount`.",
3922
+ "example": "100.50"
3923
+ },
3924
+ "usd_amount": {
3925
+ "type": "string",
3926
+ "description": "USD amount; converted to crypto at the current rate.",
3927
+ "example": "50.00"
3928
+ },
3929
+ "remark": {
3930
+ "type": "string",
3931
+ "description": "Free-form note stored on the payout.",
3932
+ "example": "Payment for order #12345"
3933
+ },
3934
+ "type": {
3935
+ "type": "string",
3936
+ "enum": [
3937
+ "normal",
3938
+ "forceful"
3939
+ ],
3940
+ "default": "normal",
3941
+ "description": "Payout strategy. `normal` (default) refills gas when needed and retries until `expired_at`. `forceful` still refills when needed but is single-attempt — the refill and the transfer are each tried once, with no retry and no `expired_at`; it fails immediately if either can't complete.\n"
3942
+ },
3943
+ "expired_at": {
3944
+ "type": "string",
3945
+ "format": "date-time",
3946
+ "description": "Cutoff after which a non-confirmed payout expires. Defaults to 1 hour from creation; capped at 12 hours ahead. Ignored for `forceful` payouts.\n",
3947
+ "example": "2026-01-01T01:00:00Z"
2172
3948
  }
3949
+ },
3950
+ "required": [
3951
+ "vault_id",
3952
+ "asset",
3953
+ "network_symbol",
3954
+ "to_address",
3955
+ "reference_id"
3956
+ ],
3957
+ "example": {
3958
+ "vault_id": "550e8400-e29b-41d4-a716-446655440001",
3959
+ "asset": "USDT",
3960
+ "network_symbol": "ethereum",
3961
+ "to_address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
3962
+ "reference_id": "order-12345",
3963
+ "amount": "100.50",
3964
+ "remark": "Payment for order #12345",
3965
+ "type": "normal",
3966
+ "expired_at": "2026-01-01T01:00:00Z"
2173
3967
  }
2174
3968
  },
2175
- "AssetInfo": {
3969
+ "Payout": {
2176
3970
  "type": "object",
2177
3971
  "properties": {
2178
- "symbol": {
3972
+ "livemode": {
3973
+ "$ref": "#/components/schemas/Livemode"
3974
+ },
3975
+ "id": {
2179
3976
  "type": "string",
2180
- "example": "USDC"
3977
+ "example": "550e8400-e29b-41d4-a716-446655440000"
2181
3978
  },
2182
- "name": {
3979
+ "vault_id": {
2183
3980
  "type": "string",
2184
- "example": "USD Coin"
3981
+ "example": "550e8400-e29b-41d4-a716-446655440001"
2185
3982
  },
2186
- "address": {
3983
+ "asset": {
2187
3984
  "type": "string",
2188
- "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
3985
+ "example": "USDT"
2189
3986
  },
2190
- "decimals": {
2191
- "type": "integer",
2192
- "example": 6
3987
+ "network_symbol": {
3988
+ "type": "string",
3989
+ "example": "ethereum"
2193
3990
  },
2194
- "logo": {
3991
+ "amount": {
2195
3992
  "type": "string",
2196
- "example": "https://assets.stridge.com/tokens/usdc.png"
3993
+ "description": "Crypto amount sent.",
3994
+ "example": "100.50"
2197
3995
  },
2198
- "min_deposit_usd": {
3996
+ "usd_amount": {
2199
3997
  "type": "string",
2200
- "example": "1.00"
3998
+ "description": "USD value at submit time.",
3999
+ "example": "100.50"
2201
4000
  },
2202
- "price_impact": {
4001
+ "rate": {
2203
4002
  "type": "string",
2204
- "example": "0"
2205
- }
2206
- }
2207
- },
2208
- "SupportedAssetChain": {
2209
- "type": "object",
2210
- "properties": {
2211
- "network_id": {
4003
+ "description": "Conversion rate applied when `usd_amount` was used.",
4004
+ "example": "1.0001"
4005
+ },
4006
+ "to_address": {
2212
4007
  "type": "string",
2213
- "example": "60"
4008
+ "example": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
2214
4009
  },
2215
- "network_name": {
4010
+ "reference_id": {
2216
4011
  "type": "string",
2217
- "example": "Ethereum"
4012
+ "example": "order-12345"
2218
4013
  },
2219
- "network_symbol": {
4014
+ "remark": {
2220
4015
  "type": "string",
2221
- "example": "ETH"
4016
+ "example": "Payment for order #12345"
2222
4017
  },
2223
- "eip155_id": {
2224
- "type": "integer",
2225
- "description": "Chain-specific numeric id: the EIP-155 chain id for EVM networks, or the analogous numeric chain id Stridge uses for non-EVM (TVM, SVM) networks. Always present.\n",
2226
- "example": 1
4018
+ "type": {
4019
+ "type": "string",
4020
+ "enum": [
4021
+ "normal",
4022
+ "forceful"
4023
+ ],
4024
+ "description": "Payout strategy the payout was created with.",
4025
+ "example": "normal"
2227
4026
  },
2228
- "chain_type": {
4027
+ "status": {
2229
4028
  "type": "string",
2230
4029
  "enum": [
2231
- "EVM",
2232
- "TVM",
2233
- "SVM"
4030
+ "queued",
4031
+ "processing",
4032
+ "confirmed",
4033
+ "failed",
4034
+ "expired"
2234
4035
  ],
2235
- "example": "EVM"
4036
+ "example": "confirmed"
2236
4037
  },
2237
- "native_currency": {
2238
- "$ref": "#/components/schemas/NativeCurrency"
4038
+ "tx_id": {
4039
+ "type": "string",
4040
+ "description": "On-chain transaction hash; empty until broadcast.",
4041
+ "example": "0xabc123def456789"
2239
4042
  },
2240
- "assets": {
2241
- "type": "array",
2242
- "items": {
2243
- "$ref": "#/components/schemas/AssetInfo"
2244
- }
4043
+ "error": {
4044
+ "type": "string",
4045
+ "description": "Failure reason when `status` is `failed`; empty otherwise.",
4046
+ "example": ""
4047
+ },
4048
+ "created_at": {
4049
+ "type": "string",
4050
+ "format": "date-time",
4051
+ "example": "2026-01-01T00:00:00Z"
4052
+ },
4053
+ "confirmed_at": {
4054
+ "type": "string",
4055
+ "format": "date-time",
4056
+ "example": "2026-01-01T00:05:00Z"
4057
+ },
4058
+ "expired_at": {
4059
+ "type": "string",
4060
+ "format": "date-time",
4061
+ "example": "2026-01-01T01:00:00Z"
2245
4062
  }
2246
4063
  }
2247
4064
  },
2248
- "SupportedAssetsResponse": {
4065
+ "PayoutResponse": {
2249
4066
  "type": "object",
2250
4067
  "properties": {
2251
- "assets": {
4068
+ "data": {
4069
+ "$ref": "#/components/schemas/Payout"
4070
+ }
4071
+ }
4072
+ },
4073
+ "PayoutList": {
4074
+ "type": "object",
4075
+ "properties": {
4076
+ "data": {
2252
4077
  "type": "array",
2253
4078
  "items": {
2254
- "$ref": "#/components/schemas/SupportedAssetChain"
4079
+ "$ref": "#/components/schemas/Payout"
2255
4080
  }
4081
+ },
4082
+ "pagination": {
4083
+ "$ref": "#/components/schemas/PagePagination"
2256
4084
  }
2257
4085
  }
2258
4086
  },
2259
- "PagePagination": {
4087
+ "GasStationBalance": {
2260
4088
  "type": "object",
2261
- "description": "Page-based pagination metadata returned by payout and gas-station list endpoints.",
2262
4089
  "properties": {
2263
- "page": {
2264
- "type": "integer",
2265
- "example": 1
4090
+ "livemode": {
4091
+ "$ref": "#/components/schemas/Livemode"
2266
4092
  },
2267
- "limit": {
2268
- "type": "integer",
2269
- "example": 20
4093
+ "asset": {
4094
+ "type": "string",
4095
+ "example": "Ethereum"
2270
4096
  },
2271
- "total_items": {
2272
- "type": "integer",
2273
- "example": 100
4097
+ "symbol": {
4098
+ "type": "string",
4099
+ "example": "ETH"
2274
4100
  },
2275
- "total_pages": {
4101
+ "decimal": {
2276
4102
  "type": "integer",
2277
- "example": 5
2278
- },
2279
- "has_next": {
2280
- "type": "boolean",
2281
- "example": true
4103
+ "example": 18
2282
4104
  },
2283
- "has_prev": {
2284
- "type": "boolean",
2285
- "example": false
2286
- }
2287
- }
2288
- },
2289
- "MoneyValue": {
2290
- "type": "object",
2291
- "description": "A single balance rendered in human, raw, and USD forms.",
2292
- "properties": {
2293
- "amount": {
4105
+ "network_id": {
2294
4106
  "type": "string",
2295
- "description": "Human-readable amount (already divided by decimals).",
2296
- "example": "1.5"
4107
+ "example": "60"
2297
4108
  },
2298
- "raw": {
4109
+ "network_symbol": {
2299
4110
  "type": "string",
2300
- "description": "Amount in the asset's smallest unit.",
2301
- "example": "1000000000000000000"
4111
+ "example": "ETH"
2302
4112
  },
2303
- "usd": {
2304
- "type": "string",
2305
- "description": "Indicative USD value.",
2306
- "example": "3500.00"
4113
+ "balance": {
4114
+ "$ref": "#/components/schemas/MoneyValue"
2307
4115
  }
2308
4116
  }
2309
4117
  },
2310
- "Vault": {
4118
+ "GasStation": {
2311
4119
  "type": "object",
2312
4120
  "properties": {
4121
+ "livemode": {
4122
+ "$ref": "#/components/schemas/Livemode"
4123
+ },
2313
4124
  "id": {
2314
4125
  "type": "string",
2315
4126
  "example": "550e8400-e29b-41d4-a716-446655440000"
2316
4127
  },
2317
- "label": {
2318
- "type": "string",
2319
- "description": "Human-readable vault name.",
2320
- "example": "hot"
2321
- }
2322
- }
2323
- },
2324
- "VaultList": {
2325
- "type": "object",
2326
- "properties": {
2327
- "data": {
2328
- "type": "array",
2329
- "items": {
2330
- "$ref": "#/components/schemas/Vault"
2331
- }
2332
- }
2333
- }
2334
- },
2335
- "VaultAddress": {
2336
- "type": "object",
2337
- "properties": {
2338
4128
  "address": {
2339
4129
  "type": "string",
2340
4130
  "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD58"
@@ -2347,43 +4137,35 @@
2347
4137
  "type": "string",
2348
4138
  "example": "ETH"
2349
4139
  },
2350
- "created_at": {
2351
- "type": "string",
2352
- "format": "date-time",
2353
- "example": "2024-01-01T00:00:00Z"
2354
- }
2355
- }
2356
- },
2357
- "VaultAddressList": {
2358
- "type": "object",
2359
- "properties": {
2360
- "data": {
2361
- "type": "object",
2362
- "properties": {
2363
- "addresses": {
2364
- "type": "array",
2365
- "items": {
2366
- "$ref": "#/components/schemas/VaultAddress"
2367
- }
2368
- }
4140
+ "balances": {
4141
+ "type": "array",
4142
+ "items": {
4143
+ "$ref": "#/components/schemas/GasStationBalance"
2369
4144
  }
2370
4145
  }
2371
4146
  }
2372
4147
  },
2373
- "VaultAddressResponse": {
4148
+ "GasStationResponse": {
2374
4149
  "type": "object",
2375
4150
  "properties": {
2376
4151
  "data": {
2377
- "$ref": "#/components/schemas/VaultAddress"
4152
+ "$ref": "#/components/schemas/GasStation"
2378
4153
  }
2379
4154
  }
2380
4155
  },
2381
- "VaultBalance": {
4156
+ "GasStationWallet": {
2382
4157
  "type": "object",
2383
4158
  "properties": {
2384
- "asset": {
4159
+ "livemode": {
4160
+ "$ref": "#/components/schemas/Livemode"
4161
+ },
4162
+ "id": {
2385
4163
  "type": "string",
2386
- "example": "Ethereum"
4164
+ "example": "550e8400-e29b-41d4-a716-446655440000"
4165
+ },
4166
+ "address": {
4167
+ "type": "string",
4168
+ "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD58"
2387
4169
  },
2388
4170
  "network_id": {
2389
4171
  "type": "string",
@@ -2391,132 +4173,176 @@
2391
4173
  },
2392
4174
  "network_symbol": {
2393
4175
  "type": "string",
2394
- "example": "ETH"
2395
- },
2396
- "raw_value": {
2397
- "type": "string",
2398
- "description": "Balance in the asset's smallest unit.",
2399
- "example": "1000000000000000000"
2400
- },
2401
- "raw_value_decimals": {
2402
- "type": "integer",
2403
- "example": 18
4176
+ "example": "ETH"
2404
4177
  },
2405
- "display_values": {
2406
- "$ref": "#/components/schemas/MoneyValue"
4178
+ "created_at": {
4179
+ "type": "string",
4180
+ "format": "date-time",
4181
+ "example": "2024-01-01T00:00:00Z"
2407
4182
  }
2408
4183
  }
2409
4184
  },
2410
- "VaultBalancesResponse": {
4185
+ "GasStationWalletList": {
2411
4186
  "type": "object",
2412
4187
  "properties": {
2413
4188
  "data": {
2414
- "type": "object",
2415
- "properties": {
2416
- "balances": {
2417
- "type": "array",
2418
- "items": {
2419
- "$ref": "#/components/schemas/VaultBalance"
2420
- }
2421
- }
2422
- }
2423
- }
2424
- }
2425
- },
2426
- "VaultTotal": {
2427
- "type": "object",
2428
- "properties": {
2429
- "total_usd": {
2430
- "type": "string",
2431
- "description": "Combined USD value of every asset in the vault.",
2432
- "example": "25000.00"
2433
- },
2434
- "included_currency": {
2435
4189
  "type": "array",
2436
4190
  "items": {
2437
- "type": "string"
2438
- },
2439
- "example": [
2440
- "ETH",
2441
- "USDT"
2442
- ]
4191
+ "$ref": "#/components/schemas/GasStationWallet"
4192
+ }
4193
+ },
4194
+ "pagination": {
4195
+ "$ref": "#/components/schemas/PagePagination"
2443
4196
  }
2444
4197
  }
2445
4198
  },
2446
- "VaultTotalResponse": {
4199
+ "SimulationAsset": {
2447
4200
  "type": "object",
4201
+ "description": "The asset a simulated deposit pays in: the network's native coin (`native: true`) or a token named by its contract (`token`). State exactly one.\n",
2448
4202
  "properties": {
2449
- "data": {
2450
- "$ref": "#/components/schemas/VaultTotal"
4203
+ "native": {
4204
+ "type": "boolean",
4205
+ "description": "The network's native coin.",
4206
+ "example": true
4207
+ },
4208
+ "token": {
4209
+ "type": "string",
4210
+ "description": "The token's contract address.",
4211
+ "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
2451
4212
  }
2452
4213
  }
2453
4214
  },
2454
- "CreatePayoutRequest": {
4215
+ "SimulateUDADepositRequest": {
2455
4216
  "type": "object",
2456
- "description": "A payout from a vault to an external address. Provide either `amount` or `usd_amount`, not both.\n",
4217
+ "description": "A simulated payment to one of your sandbox UDAs.",
2457
4218
  "properties": {
2458
- "vault_id": {
4219
+ "uda_id": {
2459
4220
  "type": "string",
2460
- "description": "The vault the funds leave from.",
2461
- "example": "550e8400-e29b-41d4-a716-446655440001"
4221
+ "description": "The UDA to pay.",
4222
+ "example": "uda_01J9Z3K4M5N6P7Q8R9S0T1U2V3"
2462
4223
  },
2463
- "asset": {
4224
+ "network_id": {
2464
4225
  "type": "string",
2465
- "description": "Asset symbol to send.",
2466
- "example": "USDT"
4226
+ "description": "A simulated network the UDA is payable on (see `GET /test_helpers/options`).",
4227
+ "example": "60"
2467
4228
  },
2468
- "network_symbol": {
4229
+ "asset": {
4230
+ "$ref": "#/components/schemas/SimulationAsset"
4231
+ },
4232
+ "amount": {
2469
4233
  "type": "string",
2470
- "description": "Network slug.",
2471
- "example": "ethereum"
4234
+ "description": "A positive whole number of the asset's base units.",
4235
+ "example": "25000000"
2472
4236
  },
2473
- "to_address": {
4237
+ "settlement": {
2474
4238
  "type": "string",
2475
- "description": "External destination address.",
2476
- "example": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
4239
+ "enum": [
4240
+ "success",
4241
+ "fail",
4242
+ "retry_then_success"
4243
+ ],
4244
+ "description": "How the settlement ends (default `success`).",
4245
+ "example": "success"
2477
4246
  },
2478
- "reference_id": {
4247
+ "preset": {
2479
4248
  "type": "string",
2480
- "description": "Your idempotency key — unique per vault.",
2481
- "example": "order-12345"
4249
+ "enum": [
4250
+ "underpaid",
4251
+ "overpaid",
4252
+ "wrong_network",
4253
+ "wrong_asset",
4254
+ "dust"
4255
+ ],
4256
+ "description": "Labels the payment with a preset. It does not change what is sent: send the `facts` the options expand for the UDA.\n",
4257
+ "example": "underpaid"
4258
+ }
4259
+ },
4260
+ "required": [
4261
+ "uda_id",
4262
+ "network_id",
4263
+ "asset",
4264
+ "amount"
4265
+ ],
4266
+ "example": {
4267
+ "uda_id": "uda_01J9Z3K4M5N6P7Q8R9S0T1U2V3",
4268
+ "network_id": "60",
4269
+ "asset": {
4270
+ "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
2482
4271
  },
2483
- "amount": {
4272
+ "amount": "25000000",
4273
+ "settlement": "success"
4274
+ }
4275
+ },
4276
+ "SimulateVaultDepositRequest": {
4277
+ "type": "object",
4278
+ "description": "A simulated payment to one of your sandbox vaults.",
4279
+ "properties": {
4280
+ "vault_id": {
2484
4281
  "type": "string",
2485
- "description": "Crypto amount; provide either `amount` or `usd_amount`.",
2486
- "example": "100.50"
4282
+ "description": "The vault to pay.",
4283
+ "example": "550e8400-e29b-41d4-a716-446655440001"
2487
4284
  },
2488
- "usd_amount": {
4285
+ "network_id": {
2489
4286
  "type": "string",
2490
- "description": "USD amount; converted to crypto at the current rate.",
2491
- "example": "50.00"
4287
+ "description": "A simulated network.",
4288
+ "example": "60"
2492
4289
  },
2493
- "remark": {
4290
+ "asset": {
4291
+ "$ref": "#/components/schemas/SimulationAsset"
4292
+ },
4293
+ "amount": {
2494
4294
  "type": "string",
2495
- "description": "Free-form note stored on the payout.",
2496
- "example": "Payment for order #12345"
4295
+ "description": "A positive whole number of the asset's base units.",
4296
+ "example": "1000000000000000000"
2497
4297
  },
2498
- "type": {
4298
+ "preset": {
2499
4299
  "type": "string",
2500
4300
  "enum": [
2501
- "normal",
2502
- "forceful"
4301
+ "dust"
2503
4302
  ],
2504
- "default": "normal",
2505
- "description": "Payout strategy. `normal` (default) refills gas when needed and retries until `expired_at`. `forceful` still refills when needed but is single-attempt — the refill and the transfer are each tried once, with no retry and no `expired_at`; it fails immediately if either can't complete.\n"
2506
- },
2507
- "expired_at": {
2508
- "type": "string",
2509
- "format": "date-time",
2510
- "description": "Cutoff after which a non-confirmed payout expires. Defaults to 1 hour from creation; capped at 12 hours ahead. Ignored for `forceful` payouts.\n",
2511
- "example": "2026-01-01T01:00:00Z"
4303
+ "description": "Labels the payment as dust, below the network's minimum deposit.",
4304
+ "example": "dust"
2512
4305
  }
2513
4306
  },
2514
4307
  "required": [
2515
4308
  "vault_id",
4309
+ "network_id",
2516
4310
  "asset",
2517
- "network_symbol",
2518
- "to_address",
2519
- "reference_id"
4311
+ "amount"
4312
+ ],
4313
+ "example": {
4314
+ "vault_id": "550e8400-e29b-41d4-a716-446655440001",
4315
+ "network_id": "60",
4316
+ "asset": {
4317
+ "native": true
4318
+ },
4319
+ "amount": "1000000000000000000"
4320
+ }
4321
+ },
4322
+ "SimulatePayoutRequest": {
4323
+ "description": "The body of `POST /transfer/payout` plus the `outcome` its transaction meets.\n",
4324
+ "allOf": [
4325
+ {
4326
+ "$ref": "#/components/schemas/CreatePayoutRequest"
4327
+ },
4328
+ {
4329
+ "type": "object",
4330
+ "properties": {
4331
+ "outcome": {
4332
+ "type": "string",
4333
+ "enum": [
4334
+ "success",
4335
+ "fail",
4336
+ "hold"
4337
+ ],
4338
+ "description": "`success` confirms the payout's transaction, `fail` fails it, and `hold` leaves it broadcast but unconfirmed until `POST /test_helpers/payouts/{payout_id}/release` or `/fail`.\n",
4339
+ "example": "hold"
4340
+ }
4341
+ },
4342
+ "required": [
4343
+ "outcome"
4344
+ ]
4345
+ }
2520
4346
  ],
2521
4347
  "example": {
2522
4348
  "vault_id": "550e8400-e29b-41d4-a716-446655440001",
@@ -2525,228 +4351,647 @@
2525
4351
  "to_address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
2526
4352
  "reference_id": "order-12345",
2527
4353
  "amount": "100.50",
2528
- "remark": "Payment for order #12345",
2529
- "type": "normal",
2530
- "expired_at": "2026-01-01T01:00:00Z"
4354
+ "outcome": "hold"
2531
4355
  }
2532
4356
  },
2533
- "Payout": {
4357
+ "SimulationChain": {
4358
+ "type": [
4359
+ "object",
4360
+ "null"
4361
+ ],
4362
+ "description": "The simulated transaction the simulation sent, once there is one.",
4363
+ "properties": {
4364
+ "network_id": {
4365
+ "type": "string",
4366
+ "example": "60"
4367
+ },
4368
+ "hash": {
4369
+ "type": "string",
4370
+ "example": "0x9f2c4e1b7a3d5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcd"
4371
+ }
4372
+ }
4373
+ },
4374
+ "SimulationCause": {
2534
4375
  "type": "object",
4376
+ "description": "A Stridge object the simulation produced, as it stood when last seen.",
2535
4377
  "properties": {
4378
+ "livemode": {
4379
+ "$ref": "#/components/schemas/Livemode"
4380
+ },
4381
+ "object": {
4382
+ "type": "string",
4383
+ "enum": [
4384
+ "deposit",
4385
+ "settlement",
4386
+ "routing",
4387
+ "payout"
4388
+ ],
4389
+ "example": "settlement"
4390
+ },
2536
4391
  "id": {
2537
4392
  "type": "string",
2538
- "example": "550e8400-e29b-41d4-a716-446655440000"
4393
+ "example": "stl_01J9Z3K4M5N6P7Q8R9S0T1U2V3"
2539
4394
  },
2540
- "vault_id": {
4395
+ "status": {
4396
+ "type": [
4397
+ "string",
4398
+ "null"
4399
+ ],
4400
+ "description": "The object's status.",
4401
+ "example": "completed"
4402
+ },
4403
+ "event_type": {
4404
+ "type": [
4405
+ "string",
4406
+ "null"
4407
+ ],
4408
+ "description": "The webhook event type it last caused.",
4409
+ "example": "uda.settlement.completed"
4410
+ }
4411
+ }
4412
+ },
4413
+ "Simulation": {
4414
+ "type": "object",
4415
+ "description": "One test helper call and how far the flow it started has got. A reset of the sandbox expires the simulations made before it.\n",
4416
+ "properties": {
4417
+ "id": {
2541
4418
  "type": "string",
2542
- "example": "550e8400-e29b-41d4-a716-446655440001"
4419
+ "example": "0192f4c8-7a1e-7c3b-9d2a-5e6f7a8b9c0d"
2543
4420
  },
2544
- "asset": {
4421
+ "object": {
2545
4422
  "type": "string",
2546
- "example": "USDT"
4423
+ "enum": [
4424
+ "simulation"
4425
+ ],
4426
+ "example": "simulation"
2547
4427
  },
2548
- "network_symbol": {
4428
+ "environment_id": {
2549
4429
  "type": "string",
2550
- "example": "ethereum"
4430
+ "description": "The sandbox environment the simulation ran in.",
4431
+ "example": "3f2c1d9e-8a47-4b6e-9c11-2d5e7f8a9b04"
2551
4432
  },
2552
- "amount": {
4433
+ "livemode": {
4434
+ "$ref": "#/components/schemas/Livemode"
4435
+ },
4436
+ "kind": {
2553
4437
  "type": "string",
2554
- "description": "Crypto amount sent.",
2555
- "example": "100.50"
4438
+ "enum": [
4439
+ "uda_deposit",
4440
+ "vault_deposit",
4441
+ "payout",
4442
+ "payout_release",
4443
+ "payout_fail"
4444
+ ],
4445
+ "example": "uda_deposit"
2556
4446
  },
2557
- "usd_amount": {
4447
+ "preset": {
4448
+ "type": [
4449
+ "string",
4450
+ "null"
4451
+ ],
4452
+ "example": null
4453
+ },
4454
+ "request": {
4455
+ "type": "object",
4456
+ "description": "The request body the helper was called with; for a release or fail, `{ \"payout_id\": \"…\" }`.\n",
4457
+ "additionalProperties": true
4458
+ },
4459
+ "state": {
2558
4460
  "type": "string",
2559
- "description": "USD value at submit time.",
2560
- "example": "100.50"
4461
+ "enum": [
4462
+ "running",
4463
+ "observed",
4464
+ "completed",
4465
+ "failed",
4466
+ "expired"
4467
+ ],
4468
+ "description": "`running`: the lever is pulled and nothing recorded yet. `observed`: Stridge recorded the first object it produced. `completed`: the flow reached its terminal state — a failure you chose still completes. `failed`: the simulation itself could not finish (see `error`). `expired`: the sandbox was reset since.\n",
4469
+ "example": "completed"
2561
4470
  },
2562
- "rate": {
4471
+ "chain": {
4472
+ "$ref": "#/components/schemas/SimulationChain"
4473
+ },
4474
+ "caused": {
4475
+ "type": "array",
4476
+ "items": {
4477
+ "$ref": "#/components/schemas/SimulationCause"
4478
+ }
4479
+ },
4480
+ "epoch": {
4481
+ "type": "integer",
4482
+ "description": "The sandbox's reset count when the simulation was made.",
4483
+ "example": 0
4484
+ },
4485
+ "error": {
4486
+ "type": [
4487
+ "string",
4488
+ "null"
4489
+ ],
4490
+ "description": "Why a `failed` simulation stopped: `world refused`, or `timed out` after 10 minutes.",
4491
+ "example": null
4492
+ },
4493
+ "outcome": {
4494
+ "type": [
4495
+ "string",
4496
+ "null"
4497
+ ],
4498
+ "enum": [
4499
+ "asset not accepted",
4500
+ "not settled",
4501
+ "not recorded",
4502
+ null
4503
+ ],
4504
+ "description": "Why a `completed` simulation stopped short of a settlement: `asset not accepted` (a UDA deposit in an asset the UDA does not accept, recorded and settled by nothing), `not settled` (a UDA deposit Stridge recorded and did not settle for another reason) or `not recorded` (a dust deposit Stridge never recorded). `null` where the flow ran its course.\n",
4505
+ "example": null
4506
+ },
4507
+ "created_at": {
2563
4508
  "type": "string",
2564
- "description": "Conversion rate applied when `usd_amount` was used.",
2565
- "example": "1.0001"
4509
+ "format": "date-time",
4510
+ "example": "2026-10-10T12:00:00Z"
2566
4511
  },
2567
- "to_address": {
4512
+ "updated_at": {
2568
4513
  "type": "string",
2569
- "example": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
4514
+ "format": "date-time",
4515
+ "example": "2026-10-10T12:00:07Z"
4516
+ }
4517
+ }
4518
+ },
4519
+ "SimulationResponse": {
4520
+ "allOf": [
4521
+ {
4522
+ "$ref": "#/components/schemas/SuccessEnvelope"
2570
4523
  },
2571
- "reference_id": {
4524
+ {
4525
+ "type": "object",
4526
+ "properties": {
4527
+ "data": {
4528
+ "$ref": "#/components/schemas/Simulation"
4529
+ }
4530
+ }
4531
+ }
4532
+ ]
4533
+ },
4534
+ "SimulationList": {
4535
+ "allOf": [
4536
+ {
4537
+ "$ref": "#/components/schemas/SuccessEnvelope"
4538
+ },
4539
+ {
4540
+ "type": "object",
4541
+ "properties": {
4542
+ "data": {
4543
+ "type": "array",
4544
+ "items": {
4545
+ "$ref": "#/components/schemas/Simulation"
4546
+ }
4547
+ },
4548
+ "pagination": {
4549
+ "$ref": "#/components/schemas/PagePagination"
4550
+ }
4551
+ }
4552
+ }
4553
+ ]
4554
+ },
4555
+ "TestHelperPresetFacts": {
4556
+ "type": [
4557
+ "object",
4558
+ "null"
4559
+ ],
4560
+ "description": "The payment a preset comes to for the UDA named by `uda_id`; `null` without one, or where the UDA leaves nothing to express it with.\n",
4561
+ "properties": {
4562
+ "network_id": {
2572
4563
  "type": "string",
2573
- "example": "order-12345"
4564
+ "example": "60"
2574
4565
  },
2575
- "remark": {
4566
+ "asset": {
4567
+ "$ref": "#/components/schemas/SimulationAsset"
4568
+ },
4569
+ "amount": {
2576
4570
  "type": "string",
2577
- "example": "Payment for order #12345"
4571
+ "example": "1000000"
4572
+ }
4573
+ }
4574
+ },
4575
+ "TestHelperPreset": {
4576
+ "type": "object",
4577
+ "properties": {
4578
+ "livemode": {
4579
+ "$ref": "#/components/schemas/Livemode"
2578
4580
  },
2579
- "type": {
4581
+ "id": {
2580
4582
  "type": "string",
2581
4583
  "enum": [
2582
- "normal",
2583
- "forceful"
4584
+ "underpaid",
4585
+ "overpaid",
4586
+ "wrong_network",
4587
+ "wrong_asset",
4588
+ "dust"
2584
4589
  ],
2585
- "description": "Payout strategy the payout was created with.",
2586
- "example": "normal"
4590
+ "example": "underpaid"
2587
4591
  },
2588
- "status": {
4592
+ "label": {
2589
4593
  "type": "string",
2590
- "enum": [
2591
- "queued",
2592
- "processing",
2593
- "confirmed",
2594
- "failed",
2595
- "expired"
2596
- ],
2597
- "example": "confirmed"
4594
+ "example": "Underpaid"
2598
4595
  },
2599
- "tx_id": {
4596
+ "description": {
2600
4597
  "type": "string",
2601
- "description": "On-chain transaction hash; empty until broadcast.",
2602
- "example": "0xabc123def456789"
4598
+ "example": "Less than the address usually receives, in an asset it accepts."
2603
4599
  },
2604
- "error": {
2605
- "type": "string",
2606
- "description": "Failure reason when `status` is `failed`; empty otherwise.",
2607
- "example": ""
4600
+ "applies_to": {
4601
+ "type": "array",
4602
+ "description": "The helpers that accept the preset.",
4603
+ "items": {
4604
+ "type": "string",
4605
+ "enum": [
4606
+ "uda_deposit",
4607
+ "vault_deposit"
4608
+ ]
4609
+ },
4610
+ "example": [
4611
+ "uda_deposit"
4612
+ ]
2608
4613
  },
2609
- "created_at": {
2610
- "type": "string",
2611
- "format": "date-time",
2612
- "example": "2026-01-01T00:00:00Z"
4614
+ "facts": {
4615
+ "$ref": "#/components/schemas/TestHelperPresetFacts"
4616
+ }
4617
+ }
4618
+ },
4619
+ "TestHelperCrossChainRoute": {
4620
+ "type": "object",
4621
+ "description": "A payment to the UDA on another chain than it settles on, which the bridge lever applies to. The bridge lever is not offered in production, so the list is empty there.\n",
4622
+ "properties": {
4623
+ "livemode": {
4624
+ "$ref": "#/components/schemas/Livemode"
2613
4625
  },
2614
- "confirmed_at": {
4626
+ "network_id": {
2615
4627
  "type": "string",
2616
- "format": "date-time",
2617
- "example": "2026-01-01T00:05:00Z"
4628
+ "example": "137"
2618
4629
  },
2619
- "expired_at": {
2620
- "type": "string",
2621
- "format": "date-time",
2622
- "example": "2026-01-01T01:00:00Z"
4630
+ "asset": {
4631
+ "$ref": "#/components/schemas/SimulationAsset"
2623
4632
  }
2624
4633
  }
2625
4634
  },
2626
- "PayoutResponse": {
4635
+ "TestHelperAcceptedAsset": {
2627
4636
  "type": "object",
4637
+ "description": "An asset of a simulated network that Stridge settles a deposit of to this UDA.",
2628
4638
  "properties": {
2629
- "data": {
2630
- "$ref": "#/components/schemas/Payout"
4639
+ "livemode": {
4640
+ "$ref": "#/components/schemas/Livemode"
4641
+ },
4642
+ "network_id": {
4643
+ "type": "string",
4644
+ "example": "60"
4645
+ },
4646
+ "asset": {
4647
+ "$ref": "#/components/schemas/SimulationAsset"
2631
4648
  }
2632
4649
  }
2633
4650
  },
2634
- "PayoutList": {
4651
+ "TestHelperUDA": {
2635
4652
  "type": "object",
2636
4653
  "properties": {
2637
- "data": {
4654
+ "livemode": {
4655
+ "$ref": "#/components/schemas/Livemode"
4656
+ },
4657
+ "id": {
4658
+ "type": "string",
4659
+ "example": "uda_01J9Z3K4M5N6P7Q8R9S0T1U2V3"
4660
+ },
4661
+ "network_ids": {
2638
4662
  "type": "array",
4663
+ "description": "The simulated networks the UDA is payable on.",
2639
4664
  "items": {
2640
- "$ref": "#/components/schemas/Payout"
2641
- }
4665
+ "type": "string"
4666
+ },
4667
+ "example": [
4668
+ "60",
4669
+ "137"
4670
+ ]
2642
4671
  },
2643
- "pagination": {
2644
- "$ref": "#/components/schemas/PagePagination"
4672
+ "cross_chain": {
4673
+ "type": "boolean",
4674
+ "description": "Whether any `cross_chain_routes` exist; `false` in production.",
4675
+ "example": false
4676
+ },
4677
+ "cross_chain_routes": {
4678
+ "type": "array",
4679
+ "items": {
4680
+ "$ref": "#/components/schemas/TestHelperCrossChainRoute"
4681
+ },
4682
+ "example": []
4683
+ },
4684
+ "accepted_assets": {
4685
+ "type": "array",
4686
+ "description": "Every asset of the simulated networks the UDA is payable on that Stridge settles a deposit of, spelled out even where the UDA accepts every asset. A deposit of any other asset is recorded and settled by nothing.\n",
4687
+ "items": {
4688
+ "$ref": "#/components/schemas/TestHelperAcceptedAsset"
4689
+ }
2645
4690
  }
2646
4691
  }
2647
4692
  },
2648
- "GasStationBalance": {
4693
+ "TestHelperVault": {
2649
4694
  "type": "object",
2650
4695
  "properties": {
2651
- "asset": {
4696
+ "livemode": {
4697
+ "$ref": "#/components/schemas/Livemode"
4698
+ },
4699
+ "id": {
2652
4700
  "type": "string",
2653
- "example": "Ethereum"
4701
+ "example": "550e8400-e29b-41d4-a716-446655440001"
2654
4702
  },
2655
- "symbol": {
4703
+ "label": {
2656
4704
  "type": "string",
2657
- "example": "ETH"
4705
+ "example": "hot"
2658
4706
  },
2659
- "decimal": {
2660
- "type": "integer",
2661
- "example": 18
4707
+ "network_ids": {
4708
+ "type": "array",
4709
+ "description": "The simulated networks the vault has an address on, which a deposit to it can be made on.",
4710
+ "items": {
4711
+ "type": "string"
4712
+ },
4713
+ "example": [
4714
+ "60",
4715
+ "195"
4716
+ ]
4717
+ }
4718
+ }
4719
+ },
4720
+ "TestHelperNetworkAsset": {
4721
+ "type": "object",
4722
+ "properties": {
4723
+ "livemode": {
4724
+ "$ref": "#/components/schemas/Livemode"
2662
4725
  },
2663
- "network_id": {
4726
+ "symbol": {
2664
4727
  "type": "string",
2665
- "example": "60"
4728
+ "example": "USDC"
2666
4729
  },
2667
- "network_symbol": {
2668
- "type": "string",
2669
- "example": "ETH"
4730
+ "token": {
4731
+ "type": [
4732
+ "string",
4733
+ "null"
4734
+ ],
4735
+ "description": "The token's contract; `null` for the native coin.",
4736
+ "example": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
2670
4737
  },
2671
- "balance": {
2672
- "$ref": "#/components/schemas/MoneyValue"
4738
+ "decimals": {
4739
+ "type": "integer",
4740
+ "example": 6
2673
4741
  }
2674
4742
  }
2675
4743
  },
2676
- "GasStation": {
4744
+ "TestHelperNetwork": {
2677
4745
  "type": "object",
2678
4746
  "properties": {
2679
- "id": {
2680
- "type": "string",
2681
- "example": "550e8400-e29b-41d4-a716-446655440000"
2682
- },
2683
- "address": {
2684
- "type": "string",
2685
- "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD58"
4747
+ "livemode": {
4748
+ "$ref": "#/components/schemas/Livemode"
2686
4749
  },
2687
4750
  "network_id": {
2688
4751
  "type": "string",
2689
4752
  "example": "60"
2690
4753
  },
2691
- "network_symbol": {
4754
+ "name": {
2692
4755
  "type": "string",
2693
- "example": "ETH"
4756
+ "example": "Ethereum"
2694
4757
  },
2695
- "balances": {
4758
+ "family": {
4759
+ "type": "string",
4760
+ "enum": [
4761
+ "evm",
4762
+ "tvm",
4763
+ "utxo",
4764
+ "svm"
4765
+ ],
4766
+ "example": "evm"
4767
+ },
4768
+ "assets": {
2696
4769
  "type": "array",
2697
4770
  "items": {
2698
- "$ref": "#/components/schemas/GasStationBalance"
4771
+ "$ref": "#/components/schemas/TestHelperNetworkAsset"
4772
+ }
4773
+ },
4774
+ "levers": {
4775
+ "type": "object",
4776
+ "description": "What a simulation on this network can be made to do.",
4777
+ "properties": {
4778
+ "deposit": {
4779
+ "type": "boolean",
4780
+ "example": true
4781
+ },
4782
+ "payout_outcomes": {
4783
+ "type": "array",
4784
+ "items": {
4785
+ "type": "string",
4786
+ "enum": [
4787
+ "success",
4788
+ "fail",
4789
+ "hold"
4790
+ ]
4791
+ },
4792
+ "example": [
4793
+ "success",
4794
+ "fail",
4795
+ "hold"
4796
+ ]
4797
+ },
4798
+ "bridge": {
4799
+ "type": "boolean",
4800
+ "description": "Whether the bridge lever is offered; `false` in production.",
4801
+ "example": false
4802
+ }
2699
4803
  }
2700
4804
  }
2701
4805
  }
2702
4806
  },
2703
- "GasStationResponse": {
4807
+ "TestHelperNetworkTiming": {
2704
4808
  "type": "object",
4809
+ "description": "How fast one simulated network moves.",
2705
4810
  "properties": {
2706
- "data": {
2707
- "$ref": "#/components/schemas/GasStation"
4811
+ "network_id": {
4812
+ "type": "string",
4813
+ "example": "60"
4814
+ },
4815
+ "block_time_ms": {
4816
+ "type": "integer",
4817
+ "description": "How often the network seals a block while a transaction waits.",
4818
+ "example": 1000
4819
+ },
4820
+ "observable_blocks": {
4821
+ "type": "integer",
4822
+ "description": "How many blocks after inclusion Stridge can first read a transaction.",
4823
+ "example": 1
4824
+ },
4825
+ "confirmation_blocks": {
4826
+ "type": "integer",
4827
+ "description": "How many blocks Stridge waits on top of a transaction before it counts it as confirmed.",
4828
+ "example": 12
4829
+ },
4830
+ "confirmation_delay_ms": {
4831
+ "type": "integer",
4832
+ "description": "How long Stridge waits after sending a transaction before it first checks its confirmations.",
4833
+ "example": 30000
2708
4834
  }
2709
4835
  }
2710
4836
  },
2711
- "GasStationWallet": {
4837
+ "TestHelperTiming": {
2712
4838
  "type": "object",
4839
+ "description": "How fast the sandbox plays a simulation out.",
2713
4840
  "properties": {
2714
- "id": {
2715
- "type": "string",
2716
- "example": "550e8400-e29b-41d4-a716-446655440000"
4841
+ "networks": {
4842
+ "type": "array",
4843
+ "items": {
4844
+ "$ref": "#/components/schemas/TestHelperNetworkTiming"
4845
+ }
2717
4846
  },
2718
- "address": {
2719
- "type": "string",
2720
- "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD58"
4847
+ "settlement_retry_interval_ms": {
4848
+ "type": "integer",
4849
+ "example": 60000
2721
4850
  },
2722
- "network_id": {
2723
- "type": "string",
2724
- "example": "60"
4851
+ "settlement_retry_min_age_ms": {
4852
+ "type": "integer",
4853
+ "example": 60000
2725
4854
  },
2726
- "network_symbol": {
2727
- "type": "string",
2728
- "example": "ETH"
4855
+ "settlement_max_retries": {
4856
+ "type": "integer",
4857
+ "example": 3
2729
4858
  },
2730
- "created_at": {
2731
- "type": "string",
2732
- "format": "date-time",
2733
- "example": "2024-01-01T00:00:00Z"
4859
+ "bridge_poll_interval_ms": {
4860
+ "type": "integer",
4861
+ "example": 30000
4862
+ },
4863
+ "non_settling_grace_ms": {
4864
+ "type": "integer",
4865
+ "description": "How long a recorded deposit waits for a settlement before the simulation reads it as one Stridge does not settle.",
4866
+ "example": 60000
4867
+ },
4868
+ "unrecorded_grace_ms": {
4869
+ "type": "integer",
4870
+ "description": "How long a sent deposit waits for Stridge to record it before the simulation reads it as one Stridge does not record.",
4871
+ "example": 60000
2734
4872
  }
2735
4873
  }
2736
4874
  },
2737
- "GasStationWalletList": {
4875
+ "TestHelperOptions": {
2738
4876
  "type": "object",
2739
4877
  "properties": {
2740
- "data": {
4878
+ "livemode": {
4879
+ "$ref": "#/components/schemas/Livemode"
4880
+ },
4881
+ "budget": {
4882
+ "type": "object",
4883
+ "description": "The simulation budget, a fixed window per sandbox that every lever-pulling helper call spends from.",
4884
+ "properties": {
4885
+ "limit": {
4886
+ "type": "integer",
4887
+ "example": 600
4888
+ },
4889
+ "window_s": {
4890
+ "type": "integer",
4891
+ "example": 3600
4892
+ },
4893
+ "remaining": {
4894
+ "type": "integer",
4895
+ "example": 597
4896
+ }
4897
+ }
4898
+ },
4899
+ "udas": {
2741
4900
  "type": "array",
2742
4901
  "items": {
2743
- "$ref": "#/components/schemas/GasStationWallet"
4902
+ "$ref": "#/components/schemas/TestHelperUDA"
2744
4903
  }
2745
4904
  },
2746
- "pagination": {
2747
- "$ref": "#/components/schemas/PagePagination"
4905
+ "vaults": {
4906
+ "type": "array",
4907
+ "items": {
4908
+ "$ref": "#/components/schemas/TestHelperVault"
4909
+ }
4910
+ },
4911
+ "networks": {
4912
+ "type": "array",
4913
+ "items": {
4914
+ "$ref": "#/components/schemas/TestHelperNetwork"
4915
+ }
4916
+ },
4917
+ "presets": {
4918
+ "type": "array",
4919
+ "items": {
4920
+ "$ref": "#/components/schemas/TestHelperPreset"
4921
+ }
4922
+ },
4923
+ "settlement_outcomes": {
4924
+ "type": "array",
4925
+ "items": {
4926
+ "type": "string"
4927
+ },
4928
+ "example": [
4929
+ "success",
4930
+ "fail",
4931
+ "retry_then_success"
4932
+ ]
4933
+ },
4934
+ "bridge_outcomes": {
4935
+ "type": "array",
4936
+ "description": "The bridge lever's outcomes. The lever is not offered in production (every network's `levers.bridge` is `false`).",
4937
+ "items": {
4938
+ "type": "string"
4939
+ },
4940
+ "example": [
4941
+ "complete",
4942
+ "fail",
4943
+ "refund",
4944
+ "pending"
4945
+ ]
4946
+ },
4947
+ "payout_outcomes": {
4948
+ "type": "array",
4949
+ "items": {
4950
+ "type": "string"
4951
+ },
4952
+ "example": [
4953
+ "success",
4954
+ "fail",
4955
+ "hold"
4956
+ ]
4957
+ },
4958
+ "event_types": {
4959
+ "type": "array",
4960
+ "description": "The webhook event types a simulation can cause.",
4961
+ "items": {
4962
+ "type": "string"
4963
+ },
4964
+ "example": [
4965
+ "deposit.confirmed",
4966
+ "vault.deposit.detected",
4967
+ "uda.settlement.created",
4968
+ "uda.settlement.retrying",
4969
+ "uda.settlement.completed",
4970
+ "uda.settlement.failed",
4971
+ "payout.confirmed",
4972
+ "payout.failed",
4973
+ "payout.expired"
4974
+ ]
4975
+ },
4976
+ "timing": {
4977
+ "$ref": "#/components/schemas/TestHelperTiming"
2748
4978
  }
2749
4979
  }
4980
+ },
4981
+ "TestHelperOptionsResponse": {
4982
+ "allOf": [
4983
+ {
4984
+ "$ref": "#/components/schemas/SuccessEnvelope"
4985
+ },
4986
+ {
4987
+ "type": "object",
4988
+ "properties": {
4989
+ "data": {
4990
+ "$ref": "#/components/schemas/TestHelperOptions"
4991
+ }
4992
+ }
4993
+ }
4994
+ ]
2750
4995
  }
2751
4996
  }
2752
4997
  }