@outlayer/sdk 0.1.0-alpha.1

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.
@@ -0,0 +1,1648 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: OutLayer API
4
+ version: 0.1.0-alpha.1
5
+ summary: HTTP API for OutLayer custody wallets and policy enforcement
6
+ description: |
7
+ OutLayer Agent Custody — multi-chain wallets for AI agents with TEE-enforced
8
+ policy, multisig approvals, audit log, and gasless cross-chain transfers via
9
+ NEAR Intents.
10
+
11
+ Private keys live exclusively inside an Intel TDX TEE and are derived from
12
+ NEAR MPC. The wallet owner sets policy (spending limits, whitelists,
13
+ multisig, freeze) — all enforced inside the TEE before signing.
14
+
15
+ See [CUSTODY docs](https://outlayer.fastnear.com/docs/agent-custody) for the
16
+ architectural overview and [@outlayer/sdk](https://www.npmjs.com/package/@outlayer/sdk)
17
+ for the TypeScript client.
18
+ license:
19
+ name: MIT
20
+ identifier: MIT
21
+ contact:
22
+ name: OutLayer
23
+ url: https://outlayer.fastnear.com
24
+
25
+ servers:
26
+ - url: https://api.outlayer.fastnear.com
27
+ description: Production
28
+
29
+ security:
30
+ - BearerAuth: []
31
+
32
+ tags:
33
+ - name: Registration
34
+ description: Create a new wallet and obtain an API key.
35
+ - name: Wallet
36
+ description: Multi-chain custody operations — transfer, withdraw, swap, balance, signing.
37
+ - name: Policy
38
+ description: Encrypted on-chain policy lifecycle (read, encrypt, sign, cache).
39
+ - name: Approvals
40
+ description: Multisig approval workflows for actions that exceed policy thresholds.
41
+ - name: Audit
42
+ description: Event history for the wallet.
43
+ - name: Requests
44
+ description: Async operation status tracking.
45
+ - name: Meta
46
+ description: Token catalog and helpers.
47
+
48
+ paths:
49
+ # ============================================================================
50
+ # Registration
51
+ # ============================================================================
52
+ /register:
53
+ post:
54
+ tags: [Registration]
55
+ operationId: registerWallet
56
+ summary: Register a new wallet
57
+ description: |
58
+ Creates a new multi-chain wallet and returns an API key. For most
59
+ callers, send `{}` — an anonymous wallet is created and an API key is
60
+ returned in `api_key` (shown once, not retrievable later).
61
+
62
+ Optional fields enable advanced registration paths:
63
+ - `account_id` + `pubkey` + `message` + `signature`: bind the wallet to
64
+ a NEAR account via NEP-413 proof-of-ownership.
65
+ - `vault_id`: bind to a customer-owned vault for sovereign custody (see
66
+ vault docs).
67
+
68
+ Rate-limited: 30 requests per minute per IP.
69
+ security: []
70
+ requestBody:
71
+ required: false
72
+ content:
73
+ application/json:
74
+ schema:
75
+ $ref: '#/components/schemas/RegisterRequest'
76
+ examples:
77
+ anonymous:
78
+ summary: Anonymous wallet (most common)
79
+ value: {}
80
+ bound_to_near_account:
81
+ summary: Bind to NEAR account via NEP-413
82
+ value:
83
+ account_id: alice.near
84
+ pubkey: ed25519:7BcBZ9Z...
85
+ message: "register:1716200000"
86
+ signature: ed25519:5K3F...
87
+ responses:
88
+ '200':
89
+ description: Wallet created
90
+ content:
91
+ application/json:
92
+ schema:
93
+ $ref: '#/components/schemas/RegisterResponse'
94
+ examples:
95
+ anonymous:
96
+ value:
97
+ wallet_id: 9c3c9e10-1c1f-4f5e-9c4a-1d7b9a8f3c20
98
+ api_key: wk_2a8b1f3c4d5e6789abcdef0123456789
99
+ near_account_id: 9c3c9e101c1f4f5e9c4a1d7b9a8f3c20
100
+ handoff_url: https://outlayer.fastnear.com/wallet?api_key=wk_2a8b...
101
+ trial:
102
+ calls_remaining: 100
103
+ expires_at: '2026-06-20T00:00:00Z'
104
+ limits:
105
+ max_instructions: 100000000
106
+ max_execution_seconds: 30
107
+ max_memory_mb: 64
108
+ '400':
109
+ $ref: '#/components/responses/BadRequest'
110
+ '429':
111
+ $ref: '#/components/responses/RateLimited'
112
+ '500':
113
+ $ref: '#/components/responses/InternalError'
114
+
115
+ # ============================================================================
116
+ # Wallet — read
117
+ # ============================================================================
118
+ /wallet/v1/address:
119
+ get:
120
+ tags: [Wallet]
121
+ operationId: getAddress
122
+ summary: Derive wallet address for a chain
123
+ description: |
124
+ Returns the deterministically-derived address for the wallet on the
125
+ requested chain. Same `wallet_id` always produces the same addresses
126
+ across chains.
127
+ parameters:
128
+ - name: chain
129
+ in: query
130
+ required: true
131
+ schema:
132
+ $ref: '#/components/schemas/Chain'
133
+ responses:
134
+ '200':
135
+ description: Address derived
136
+ content:
137
+ application/json:
138
+ schema:
139
+ $ref: '#/components/schemas/AddressResponse'
140
+ example:
141
+ wallet_id: 9c3c9e10-1c1f-4f5e-9c4a-1d7b9a8f3c20
142
+ chain: near
143
+ address: 9c3c9e101c1f4f5e9c4a1d7b9a8f3c20
144
+ public_key: ed25519:7BcBZ9Z...
145
+ vault_id: null
146
+ '400':
147
+ $ref: '#/components/responses/BadRequest'
148
+ '401':
149
+ $ref: '#/components/responses/Unauthorized'
150
+ '500':
151
+ $ref: '#/components/responses/InternalError'
152
+
153
+ /wallet/v1/balance:
154
+ get:
155
+ tags: [Wallet]
156
+ operationId: getBalance
157
+ summary: Read balance
158
+ description: |
159
+ Returns the balance for a token on the requested chain. Currently only
160
+ `chain=near` is supported for native balance reads; cross-chain balances
161
+ should be read from the upstream chain directly.
162
+
163
+ Set `source=intents` to read the wallet's intents.near balance
164
+ (`mt_balance_of`) instead of the on-chain account balance.
165
+ parameters:
166
+ - name: chain
167
+ in: query
168
+ required: false
169
+ schema:
170
+ $ref: '#/components/schemas/Chain'
171
+ default: near
172
+ - name: token
173
+ in: query
174
+ required: false
175
+ schema:
176
+ type: string
177
+ description: Token ID (`nep141:<contract>` or `native`).
178
+ - name: source
179
+ in: query
180
+ required: false
181
+ schema:
182
+ type: string
183
+ enum: [chain, intents]
184
+ default: chain
185
+ responses:
186
+ '200':
187
+ description: Balance
188
+ content:
189
+ application/json:
190
+ schema:
191
+ $ref: '#/components/schemas/BalanceResponse'
192
+ example:
193
+ balance: '1000000000000000000000000'
194
+ token: NEAR
195
+ account_id: 9c3c9e101c1f4f5e9c4a1d7b9a8f3c20
196
+ '400':
197
+ $ref: '#/components/responses/BadRequest'
198
+ '401':
199
+ $ref: '#/components/responses/Unauthorized'
200
+ '500':
201
+ $ref: '#/components/responses/InternalError'
202
+
203
+ /wallet/v1/tokens:
204
+ get:
205
+ tags: [Meta]
206
+ operationId: listTokens
207
+ summary: List supported tokens
208
+ description: |
209
+ Returns the token catalog supported for swaps and cross-chain withdraws
210
+ via NEAR Intents. Token IDs are typed as `nep141:<contract>` and map to
211
+ each chain via `defuse_asset_id`.
212
+ responses:
213
+ '200':
214
+ description: Token list
215
+ content:
216
+ application/json:
217
+ schema:
218
+ $ref: '#/components/schemas/TokensResponse'
219
+ '401':
220
+ $ref: '#/components/responses/Unauthorized'
221
+ '500':
222
+ $ref: '#/components/responses/InternalError'
223
+
224
+ # ============================================================================
225
+ # Wallet — write
226
+ # ============================================================================
227
+ /wallet/v1/call:
228
+ post:
229
+ tags: [Wallet]
230
+ operationId: call
231
+ summary: Sign and broadcast a NEAR function call
232
+ description: |
233
+ Native NEAR contract call. Policy is enforced before signing; if the
234
+ call exceeds limits, the response carries `status=pending_approval`
235
+ and an `approval_id` for the multisig flow.
236
+ parameters:
237
+ - $ref: '#/components/parameters/IdempotencyKey'
238
+ requestBody:
239
+ required: true
240
+ content:
241
+ application/json:
242
+ schema:
243
+ $ref: '#/components/schemas/CallRequest'
244
+ example:
245
+ receiver_id: usdt.tether-token.near
246
+ method_name: ft_transfer
247
+ args:
248
+ receiver_id: bob.near
249
+ amount: '1000000'
250
+ gas: '30000000000000'
251
+ deposit: '1'
252
+ responses:
253
+ '200':
254
+ description: Call submitted (or queued for approval)
255
+ content:
256
+ application/json:
257
+ schema:
258
+ $ref: '#/components/schemas/CallResponse'
259
+ '400':
260
+ $ref: '#/components/responses/BadRequest'
261
+ '401':
262
+ $ref: '#/components/responses/Unauthorized'
263
+ '403':
264
+ $ref: '#/components/responses/Forbidden'
265
+ '429':
266
+ $ref: '#/components/responses/RateLimited'
267
+ '500':
268
+ $ref: '#/components/responses/InternalError'
269
+
270
+ /wallet/v1/transfer:
271
+ post:
272
+ tags: [Wallet]
273
+ operationId: transfer
274
+ summary: Native chain transfer
275
+ description: |
276
+ Chain-agnostic native transfer (NEAR yoctoNEAR units). Currently only
277
+ `chain=near` is supported; other chains return `unsupported_chain`.
278
+ parameters:
279
+ - $ref: '#/components/parameters/IdempotencyKey'
280
+ requestBody:
281
+ required: true
282
+ content:
283
+ application/json:
284
+ schema:
285
+ $ref: '#/components/schemas/TransferRequest'
286
+ example:
287
+ chain: near
288
+ receiver_id: bob.near
289
+ amount: '1000000000000000000000000'
290
+ responses:
291
+ '200':
292
+ description: Transfer submitted
293
+ content:
294
+ application/json:
295
+ schema:
296
+ $ref: '#/components/schemas/CallResponse'
297
+ '400':
298
+ $ref: '#/components/responses/BadRequest'
299
+ '401':
300
+ $ref: '#/components/responses/Unauthorized'
301
+ '403':
302
+ $ref: '#/components/responses/Forbidden'
303
+ '500':
304
+ $ref: '#/components/responses/InternalError'
305
+
306
+ /wallet/v1/intents/deposit:
307
+ post:
308
+ tags: [Wallet]
309
+ operationId: intentsDeposit
310
+ summary: Deposit FT into intents.near
311
+ description: |
312
+ Wraps `ft_transfer_call` to `intents.near`, auto-handling storage
313
+ deposit if required. After this call, the wallet's intents.near balance
314
+ increases by `amount` of `token` and is available for swaps and
315
+ gasless cross-chain withdrawals.
316
+ parameters:
317
+ - $ref: '#/components/parameters/IdempotencyKey'
318
+ requestBody:
319
+ required: true
320
+ content:
321
+ application/json:
322
+ schema:
323
+ $ref: '#/components/schemas/IntentsDepositRequest'
324
+ example:
325
+ token: wrap.near
326
+ amount: '5000000000000000000000000'
327
+ responses:
328
+ '200':
329
+ description: Deposit submitted
330
+ content:
331
+ application/json:
332
+ schema:
333
+ $ref: '#/components/schemas/IntentsDepositResponse'
334
+ '400':
335
+ $ref: '#/components/responses/BadRequest'
336
+ '401':
337
+ $ref: '#/components/responses/Unauthorized'
338
+ '403':
339
+ $ref: '#/components/responses/Forbidden'
340
+ '500':
341
+ $ref: '#/components/responses/InternalError'
342
+
343
+ /wallet/v1/intents/withdraw:
344
+ post:
345
+ tags: [Wallet]
346
+ operationId: intentsWithdraw
347
+ summary: Withdraw via NEAR Intents (gasless cross-chain)
348
+ description: |
349
+ Gasless cross-chain withdrawal through NEAR Intents solver relay. The
350
+ wallet's intents.near balance is debited; the receiver gets funds on
351
+ the destination chain (NEAR, Ethereum, Solana, Bitcoin) without paying
352
+ gas on either side.
353
+
354
+ If the policy requires approval, returns `status=pending_approval` with
355
+ an `approval_id`.
356
+ parameters:
357
+ - $ref: '#/components/parameters/IdempotencyKey'
358
+ requestBody:
359
+ required: true
360
+ content:
361
+ application/json:
362
+ schema:
363
+ $ref: '#/components/schemas/WithdrawRequest'
364
+ example:
365
+ chain: ethereum
366
+ to: '0x742d35Cc6634C0532925a3b844Bc9e7595f8b4f5'
367
+ amount: '1000000'
368
+ token: nep141:usdt.tether-token.near
369
+ responses:
370
+ '200':
371
+ description: Withdraw submitted (or queued for approval)
372
+ content:
373
+ application/json:
374
+ schema:
375
+ $ref: '#/components/schemas/WithdrawResponse'
376
+ '400':
377
+ $ref: '#/components/responses/BadRequest'
378
+ '401':
379
+ $ref: '#/components/responses/Unauthorized'
380
+ '403':
381
+ $ref: '#/components/responses/Forbidden'
382
+ '500':
383
+ $ref: '#/components/responses/InternalError'
384
+
385
+ /wallet/v1/intents/withdraw/dry-run:
386
+ post:
387
+ tags: [Wallet]
388
+ operationId: intentsWithdrawDryRun
389
+ summary: Simulate a withdraw (policy + balance check, no execution)
390
+ description: |
391
+ Runs the full policy and balance check for a withdraw without signing
392
+ or broadcasting. Useful for showing the user whether an action would
393
+ succeed before they confirm.
394
+ requestBody:
395
+ required: true
396
+ content:
397
+ application/json:
398
+ schema:
399
+ $ref: '#/components/schemas/WithdrawRequest'
400
+ responses:
401
+ '200':
402
+ description: Dry-run result
403
+ content:
404
+ application/json:
405
+ schema:
406
+ $ref: '#/components/schemas/DryRunResponse'
407
+ '400':
408
+ $ref: '#/components/responses/BadRequest'
409
+ '401':
410
+ $ref: '#/components/responses/Unauthorized'
411
+ '500':
412
+ $ref: '#/components/responses/InternalError'
413
+
414
+ /wallet/v1/intents/swap:
415
+ post:
416
+ tags: [Wallet]
417
+ operationId: intentsSwap
418
+ summary: Swap via NEAR Intents (1Click)
419
+ description: |
420
+ Cross-chain swap via the 1Click solver. Quote → deposit to intents.near
421
+ → mt_transfer → poll. Returns when the swap settles or fails.
422
+ parameters:
423
+ - $ref: '#/components/parameters/IdempotencyKey'
424
+ requestBody:
425
+ required: true
426
+ content:
427
+ application/json:
428
+ schema:
429
+ $ref: '#/components/schemas/SwapRequest'
430
+ example:
431
+ token_in: nep141:wrap.near
432
+ token_out: nep141:usdt.tether-token.near
433
+ amount_in: '5000000000000000000000000'
434
+ min_amount_out: '24000000'
435
+ responses:
436
+ '200':
437
+ description: Swap submitted
438
+ content:
439
+ application/json:
440
+ schema:
441
+ $ref: '#/components/schemas/SwapResponse'
442
+ '400':
443
+ $ref: '#/components/responses/BadRequest'
444
+ '401':
445
+ $ref: '#/components/responses/Unauthorized'
446
+ '403':
447
+ $ref: '#/components/responses/Forbidden'
448
+ '500':
449
+ $ref: '#/components/responses/InternalError'
450
+
451
+ /wallet/v1/intents/swap/quote:
452
+ post:
453
+ tags: [Wallet]
454
+ operationId: intentsSwapQuote
455
+ summary: Get a swap quote (no execution)
456
+ description: |
457
+ Returns the indicative output and deadline for a swap without executing
458
+ it. Use this before calling `/intents/swap` to show the user expected
459
+ output and allow them to set `min_amount_out`.
460
+ requestBody:
461
+ required: true
462
+ content:
463
+ application/json:
464
+ schema:
465
+ $ref: '#/components/schemas/SwapRequest'
466
+ responses:
467
+ '200':
468
+ description: Quote
469
+ content:
470
+ application/json:
471
+ schema:
472
+ $ref: '#/components/schemas/SwapQuoteResponse'
473
+ '400':
474
+ $ref: '#/components/responses/BadRequest'
475
+ '401':
476
+ $ref: '#/components/responses/Unauthorized'
477
+ '500':
478
+ $ref: '#/components/responses/InternalError'
479
+
480
+ /wallet/v1/sign-message:
481
+ post:
482
+ tags: [Wallet]
483
+ operationId: signMessage
484
+ summary: Sign an arbitrary message (NEP-413 or raw)
485
+ description: |
486
+ Signs a message with the wallet's NEAR key. Use `format=nep413` (default)
487
+ for the standard NEP-413 signing scheme; use `format=raw` for raw byte
488
+ signing.
489
+
490
+ Policy and freeze still apply — a frozen wallet cannot sign.
491
+ requestBody:
492
+ required: true
493
+ content:
494
+ application/json:
495
+ schema:
496
+ $ref: '#/components/schemas/SignMessageRequest'
497
+ example:
498
+ message: 'login:myapp:1716200000'
499
+ recipient: myapp.example
500
+ format: nep413
501
+ responses:
502
+ '200':
503
+ description: Signature
504
+ content:
505
+ application/json:
506
+ schema:
507
+ $ref: '#/components/schemas/SignMessageResponse'
508
+ '400':
509
+ $ref: '#/components/responses/BadRequest'
510
+ '401':
511
+ $ref: '#/components/responses/Unauthorized'
512
+ '403':
513
+ $ref: '#/components/responses/Forbidden'
514
+ '500':
515
+ $ref: '#/components/responses/InternalError'
516
+
517
+ # ============================================================================
518
+ # Requests (async tracking)
519
+ # ============================================================================
520
+ /wallet/v1/requests:
521
+ get:
522
+ tags: [Requests]
523
+ operationId: listRequests
524
+ summary: List async wallet requests
525
+ parameters:
526
+ - name: type
527
+ in: query
528
+ required: false
529
+ schema:
530
+ $ref: '#/components/schemas/RequestType'
531
+ - name: status
532
+ in: query
533
+ required: false
534
+ schema:
535
+ $ref: '#/components/schemas/RequestStatus'
536
+ - name: limit
537
+ in: query
538
+ required: false
539
+ schema:
540
+ type: integer
541
+ default: 50
542
+ maximum: 100
543
+ - name: offset
544
+ in: query
545
+ required: false
546
+ schema:
547
+ type: integer
548
+ default: 0
549
+ responses:
550
+ '200':
551
+ description: Request list
552
+ content:
553
+ application/json:
554
+ schema:
555
+ $ref: '#/components/schemas/RequestListResponse'
556
+ '401':
557
+ $ref: '#/components/responses/Unauthorized'
558
+ '500':
559
+ $ref: '#/components/responses/InternalError'
560
+
561
+ /wallet/v1/requests/{id}:
562
+ get:
563
+ tags: [Requests]
564
+ operationId: getRequest
565
+ summary: Get a single request's status
566
+ parameters:
567
+ - name: id
568
+ in: path
569
+ required: true
570
+ schema:
571
+ type: string
572
+ format: uuid
573
+ responses:
574
+ '200':
575
+ description: Request status
576
+ content:
577
+ application/json:
578
+ schema:
579
+ $ref: '#/components/schemas/RequestStatusResponse'
580
+ '401':
581
+ $ref: '#/components/responses/Unauthorized'
582
+ '404':
583
+ $ref: '#/components/responses/NotFound'
584
+ '500':
585
+ $ref: '#/components/responses/InternalError'
586
+
587
+ # ============================================================================
588
+ # Policy
589
+ # ============================================================================
590
+ /wallet/v1/policy:
591
+ get:
592
+ tags: [Policy]
593
+ operationId: getPolicy
594
+ summary: Get current decrypted policy
595
+ description: |
596
+ Returns the wallet's current policy (decrypted by the keystore TEE),
597
+ plus the current usage counters used by velocity-limit checks.
598
+ responses:
599
+ '200':
600
+ description: Current policy + usage
601
+ content:
602
+ application/json:
603
+ schema:
604
+ $ref: '#/components/schemas/PolicyResponse'
605
+ '401':
606
+ $ref: '#/components/responses/Unauthorized'
607
+ '500':
608
+ $ref: '#/components/responses/InternalError'
609
+
610
+ /wallet/v1/encrypt-policy:
611
+ post:
612
+ tags: [Policy]
613
+ operationId: encryptPolicy
614
+ summary: Encrypt a policy for on-chain storage
615
+ description: |
616
+ Sends a policy JSON to the keystore TEE for encryption. The returned
617
+ `encrypted_base64` is what gets stored in NEAR via
618
+ `store_wallet_policy(...)` after the keystore signs it.
619
+ requestBody:
620
+ required: true
621
+ content:
622
+ application/json:
623
+ schema:
624
+ $ref: '#/components/schemas/EncryptPolicyRequest'
625
+ responses:
626
+ '200':
627
+ description: Encrypted policy
628
+ content:
629
+ application/json:
630
+ schema:
631
+ $ref: '#/components/schemas/EncryptPolicyResponse'
632
+ '400':
633
+ $ref: '#/components/responses/BadRequest'
634
+ '401':
635
+ $ref: '#/components/responses/Unauthorized'
636
+ '500':
637
+ $ref: '#/components/responses/InternalError'
638
+
639
+ /wallet/v1/sign-policy:
640
+ post:
641
+ tags: [Policy]
642
+ operationId: signPolicy
643
+ summary: Sign an encrypted policy (for on-chain submission)
644
+ description: |
645
+ The keystore signs `SHA256(encrypted_data)` with the wallet's key. The
646
+ signature + encrypted data + public key form the payload for the
647
+ on-chain `store_wallet_policy(...)` call.
648
+ requestBody:
649
+ required: true
650
+ content:
651
+ application/json:
652
+ schema:
653
+ $ref: '#/components/schemas/SignPolicyRequest'
654
+ responses:
655
+ '200':
656
+ description: Signature
657
+ content:
658
+ application/json:
659
+ schema:
660
+ $ref: '#/components/schemas/SignPolicyResponse'
661
+ '400':
662
+ $ref: '#/components/responses/BadRequest'
663
+ '401':
664
+ $ref: '#/components/responses/Unauthorized'
665
+ '500':
666
+ $ref: '#/components/responses/InternalError'
667
+
668
+ /wallet/v1/invalidate-cache:
669
+ post:
670
+ tags: [Policy]
671
+ operationId: invalidatePolicyCache
672
+ summary: Invalidate negative policy cache
673
+ description: |
674
+ Forces the coordinator to drop its cached `no_policy` flag for a wallet.
675
+ Call this after submitting `store_wallet_policy(...)` on-chain so that
676
+ subsequent operations pick up the new policy without waiting for the
677
+ 5-minute TTL to expire.
678
+ security: []
679
+ requestBody:
680
+ required: true
681
+ content:
682
+ application/json:
683
+ schema:
684
+ $ref: '#/components/schemas/InvalidateCacheRequest'
685
+ responses:
686
+ '200':
687
+ description: Cache cleared
688
+ content:
689
+ application/json:
690
+ schema:
691
+ type: object
692
+ properties:
693
+ ok:
694
+ type: boolean
695
+ '400':
696
+ $ref: '#/components/responses/BadRequest'
697
+ '500':
698
+ $ref: '#/components/responses/InternalError'
699
+
700
+ # ============================================================================
701
+ # Approvals (multisig)
702
+ # ============================================================================
703
+ /wallet/v1/pending_approvals:
704
+ get:
705
+ tags: [Approvals]
706
+ operationId: listPendingApprovals
707
+ summary: List pending multisig approvals
708
+ responses:
709
+ '200':
710
+ description: Pending approvals
711
+ content:
712
+ application/json:
713
+ schema:
714
+ $ref: '#/components/schemas/PendingApprovalsResponse'
715
+ '401':
716
+ $ref: '#/components/responses/Unauthorized'
717
+ '500':
718
+ $ref: '#/components/responses/InternalError'
719
+
720
+ /wallet/v1/approve/{id}:
721
+ post:
722
+ tags: [Approvals]
723
+ operationId: approveRequest
724
+ summary: Approve a pending action (NEP-413 signed)
725
+ description: |
726
+ Submits an approver's NEP-413 signature for a pending action. When the
727
+ approval threshold is reached, the action auto-executes. No API key
728
+ required — auth is established by the NEP-413 signature in the body.
729
+ security: []
730
+ parameters:
731
+ - name: id
732
+ in: path
733
+ required: true
734
+ schema:
735
+ type: string
736
+ format: uuid
737
+ requestBody:
738
+ required: true
739
+ content:
740
+ application/json:
741
+ schema:
742
+ $ref: '#/components/schemas/Nep413Auth'
743
+ responses:
744
+ '200':
745
+ description: Approval recorded
746
+ content:
747
+ application/json:
748
+ schema:
749
+ $ref: '#/components/schemas/ApproveResponse'
750
+ '403':
751
+ $ref: '#/components/responses/Forbidden'
752
+ '404':
753
+ $ref: '#/components/responses/NotFound'
754
+ '500':
755
+ $ref: '#/components/responses/InternalError'
756
+
757
+ /wallet/v1/reject/{id}:
758
+ post:
759
+ tags: [Approvals]
760
+ operationId: rejectRequest
761
+ summary: Reject a pending action (NEP-413 signed)
762
+ security: []
763
+ parameters:
764
+ - name: id
765
+ in: path
766
+ required: true
767
+ schema:
768
+ type: string
769
+ format: uuid
770
+ requestBody:
771
+ required: true
772
+ content:
773
+ application/json:
774
+ schema:
775
+ $ref: '#/components/schemas/RejectRequest'
776
+ responses:
777
+ '200':
778
+ description: Rejection recorded
779
+ content:
780
+ application/json:
781
+ schema:
782
+ $ref: '#/components/schemas/ApproveResponse'
783
+ '403':
784
+ $ref: '#/components/responses/Forbidden'
785
+ '404':
786
+ $ref: '#/components/responses/NotFound'
787
+ '500':
788
+ $ref: '#/components/responses/InternalError'
789
+
790
+ # ============================================================================
791
+ # Audit
792
+ # ============================================================================
793
+ /wallet/v1/audit:
794
+ get:
795
+ tags: [Audit]
796
+ operationId: listAuditEvents
797
+ summary: Event history for the wallet
798
+ parameters:
799
+ - name: limit
800
+ in: query
801
+ required: false
802
+ schema:
803
+ type: integer
804
+ default: 50
805
+ maximum: 100
806
+ - name: offset
807
+ in: query
808
+ required: false
809
+ schema:
810
+ type: integer
811
+ default: 0
812
+ responses:
813
+ '200':
814
+ description: Event list
815
+ content:
816
+ application/json:
817
+ schema:
818
+ $ref: '#/components/schemas/AuditResponse'
819
+ '401':
820
+ $ref: '#/components/responses/Unauthorized'
821
+ '500':
822
+ $ref: '#/components/responses/InternalError'
823
+
824
+ # ==============================================================================
825
+ # Components
826
+ # ==============================================================================
827
+ components:
828
+ securitySchemes:
829
+ BearerAuth:
830
+ type: http
831
+ scheme: bearer
832
+ bearerFormat: wk_<32-hex-chars>
833
+ description: |
834
+ API key returned by `POST /register`. Format `wk_<hex>`. Pass as
835
+ `Authorization: Bearer wk_...`.
836
+
837
+ parameters:
838
+ IdempotencyKey:
839
+ name: Idempotency-Key
840
+ in: header
841
+ required: false
842
+ schema:
843
+ type: string
844
+ description: |
845
+ Optional idempotency token. Resubmitting a write request with the same
846
+ key returns the original result without re-execution. Recommended for
847
+ clients that retry on network failure.
848
+
849
+ responses:
850
+ BadRequest:
851
+ description: Invalid request
852
+ content:
853
+ application/json:
854
+ schema:
855
+ $ref: '#/components/schemas/ErrorResponse'
856
+ examples:
857
+ bad_request:
858
+ value: { error: bad_request, message: "amount must be positive" }
859
+ invalid_address:
860
+ value: { error: invalid_address }
861
+ unsupported_chain:
862
+ value: { error: unsupported_chain }
863
+ unsupported_token:
864
+ value: { error: unsupported_token }
865
+ insufficient_balance:
866
+ value: { error: insufficient_balance }
867
+ Unauthorized:
868
+ description: Missing or invalid credentials
869
+ content:
870
+ application/json:
871
+ schema:
872
+ $ref: '#/components/schemas/ErrorResponse'
873
+ examples:
874
+ missing_auth:
875
+ value: { error: missing_auth }
876
+ invalid_api_key:
877
+ value: { error: invalid_api_key }
878
+ timestamp_expired:
879
+ value: { error: timestamp_expired }
880
+ Forbidden:
881
+ description: Action blocked by policy or wallet state
882
+ content:
883
+ application/json:
884
+ schema:
885
+ $ref: '#/components/schemas/ErrorResponse'
886
+ examples:
887
+ policy_denied:
888
+ value: { error: policy_denied, message: "daily limit exceeded" }
889
+ wallet_frozen:
890
+ value: { error: wallet_frozen }
891
+ not_approver:
892
+ value: { error: not_approver }
893
+ NotFound:
894
+ description: Resource does not exist
895
+ content:
896
+ application/json:
897
+ schema:
898
+ $ref: '#/components/schemas/ErrorResponse'
899
+ examples:
900
+ request_not_found:
901
+ value: { error: request_not_found }
902
+ approval_not_found:
903
+ value: { error: approval_not_found }
904
+ RateLimited:
905
+ description: Too many requests
906
+ content:
907
+ application/json:
908
+ schema:
909
+ $ref: '#/components/schemas/ErrorResponse'
910
+ examples:
911
+ rate_limited:
912
+ value: { error: rate_limited }
913
+ InternalError:
914
+ description: Server error
915
+ content:
916
+ application/json:
917
+ schema:
918
+ $ref: '#/components/schemas/ErrorResponse'
919
+ examples:
920
+ internal_error:
921
+ value: { error: internal_error }
922
+
923
+ schemas:
924
+ # ----- Primitives -----
925
+ Chain:
926
+ type: string
927
+ enum: [near, ethereum, solana, bitcoin]
928
+ description: Supported chain identifier.
929
+
930
+ RequestType:
931
+ type: string
932
+ enum: [call, transfer, withdraw, deposit, swap]
933
+
934
+ RequestStatus:
935
+ type: string
936
+ enum:
937
+ - processing
938
+ - success
939
+ - failed
940
+ - pending_approval
941
+ - approved
942
+ - rejected
943
+
944
+ ErrorCode:
945
+ type: string
946
+ enum:
947
+ - missing_auth
948
+ - invalid_api_key
949
+ - missing_wallet_id
950
+ - missing_signature
951
+ - timestamp_expired
952
+ - wallet_frozen
953
+ - policy_denied
954
+ - not_approver
955
+ - insufficient_balance
956
+ - invalid_address
957
+ - rate_limited
958
+ - unsupported_chain
959
+ - unsupported_token
960
+ - request_not_found
961
+ - approval_not_found
962
+ - bad_request
963
+ - duplicate_idempotency_key
964
+ - internal_error
965
+
966
+ ErrorResponse:
967
+ type: object
968
+ required: [error]
969
+ properties:
970
+ error:
971
+ $ref: '#/components/schemas/ErrorCode'
972
+ message:
973
+ type: string
974
+ details:
975
+ type: object
976
+ additionalProperties: {}
977
+
978
+ # ----- Registration -----
979
+ RegisterRequest:
980
+ type: object
981
+ description: |
982
+ For the common case (anonymous wallet on OutLayer's shared master),
983
+ send empty `{}`. Optional fields enable advanced flows:
984
+
985
+ - `vault_id`: bind this wallet to an existing customer-owned vault,
986
+ so the wallet's master is derived through the vault's per-customer
987
+ MPC-derived master instead of OutLayer's shared default. The vault
988
+ must already be deployed and verified — **vault deployment is not
989
+ done through this endpoint**. Use the dashboard
990
+ (https://outlayer.fastnear.com/vault) or the CLI
991
+ (`outlayer vault init`) to deploy. Reference:
992
+ https://outlayer.fastnear.com/docs/vaults
993
+ - `account_id` + `pubkey` + `message` + `signature`: bind the wallet
994
+ to a verified NEAR account via NEP-413 proof-of-ownership. Used
995
+ by clients that want operational keys (`wk_...`) tied to a real
996
+ NEAR account.
997
+ properties:
998
+ account_id:
999
+ type: string
1000
+ description: NEAR account ID to bind the wallet to (requires NEP-413 signature).
1001
+ seed:
1002
+ type: string
1003
+ description: Reserved for deterministic-wallet derivation. Most callers leave unset.
1004
+ pubkey:
1005
+ type: string
1006
+ description: NEP-413 signer pubkey (paired with `account_id` + `signature`).
1007
+ message:
1008
+ type: string
1009
+ description: NEP-413 message that was signed.
1010
+ signature:
1011
+ type: string
1012
+ description: NEP-413 signature over `message`.
1013
+ vault_id:
1014
+ type: string
1015
+ description: |
1016
+ ID of a deployed and verified customer vault (e.g. `vault.alice.near`).
1017
+ Binding is permanent — the resulting API key is tied to this vault
1018
+ for its lifetime. All subsequent operations on the API key
1019
+ automatically derive keys through the per-vault master; you do
1020
+ NOT pass `vault_id` on every call.
1021
+
1022
+ RegisterResponse:
1023
+ type: object
1024
+ required: [wallet_id, near_account_id]
1025
+ properties:
1026
+ wallet_id:
1027
+ type: string
1028
+ format: uuid
1029
+ api_key:
1030
+ type: string
1031
+ description: Shown once. Not retrievable later — save it now.
1032
+ near_account_id:
1033
+ type: string
1034
+ handoff_url:
1035
+ type: string
1036
+ format: uri
1037
+ description: Dashboard URL where the controller sets policy.
1038
+ trial:
1039
+ $ref: '#/components/schemas/TrialInfo'
1040
+
1041
+ TrialInfo:
1042
+ type: object
1043
+ properties:
1044
+ calls_remaining:
1045
+ type: integer
1046
+ expires_at:
1047
+ type: string
1048
+ format: date-time
1049
+ limits:
1050
+ type: object
1051
+ properties:
1052
+ max_instructions:
1053
+ type: integer
1054
+ max_execution_seconds:
1055
+ type: integer
1056
+ max_memory_mb:
1057
+ type: integer
1058
+
1059
+ # ----- Wallet read -----
1060
+ AddressResponse:
1061
+ type: object
1062
+ required: [wallet_id, chain, address, public_key]
1063
+ properties:
1064
+ wallet_id:
1065
+ type: string
1066
+ chain:
1067
+ $ref: '#/components/schemas/Chain'
1068
+ address:
1069
+ type: string
1070
+ public_key:
1071
+ type: string
1072
+ vault_id:
1073
+ type: [string, 'null']
1074
+
1075
+ BalanceResponse:
1076
+ type: object
1077
+ required: [balance, token, account_id]
1078
+ properties:
1079
+ balance:
1080
+ type: string
1081
+ description: Amount in token's smallest unit.
1082
+ token:
1083
+ type: string
1084
+ account_id:
1085
+ type: string
1086
+
1087
+ TokenInfo:
1088
+ type: object
1089
+ required: [id, symbol, chains, decimals]
1090
+ properties:
1091
+ id:
1092
+ type: string
1093
+ symbol:
1094
+ type: string
1095
+ chains:
1096
+ type: array
1097
+ items:
1098
+ $ref: '#/components/schemas/Chain'
1099
+ decimals:
1100
+ type: integer
1101
+ defuse_asset_id:
1102
+ type: string
1103
+
1104
+ TokensResponse:
1105
+ type: object
1106
+ required: [tokens]
1107
+ properties:
1108
+ tokens:
1109
+ type: array
1110
+ items:
1111
+ $ref: '#/components/schemas/TokenInfo'
1112
+
1113
+ # ----- Wallet write -----
1114
+ CallRequest:
1115
+ type: object
1116
+ required: [receiver_id, method_name]
1117
+ properties:
1118
+ receiver_id:
1119
+ type: string
1120
+ method_name:
1121
+ type: string
1122
+ args:
1123
+ type: object
1124
+ additionalProperties: {}
1125
+ description: Method arguments as a JSON object. Mutually exclusive with `args_base64`.
1126
+ args_base64:
1127
+ type: string
1128
+ description: Pre-encoded base64 args. Use for non-JSON encodings.
1129
+ gas:
1130
+ type: string
1131
+ description: Gas budget in TGas as a string. Defaults to `30000000000000` (30 TGas) server-side.
1132
+ deposit:
1133
+ type: string
1134
+ description: Attached NEAR deposit in yoctoNEAR. Defaults to `0` server-side.
1135
+
1136
+ CallResponse:
1137
+ type: object
1138
+ required: [request_id, status]
1139
+ properties:
1140
+ request_id:
1141
+ type: string
1142
+ format: uuid
1143
+ status:
1144
+ $ref: '#/components/schemas/RequestStatus'
1145
+ tx_hash:
1146
+ type: [string, 'null']
1147
+ result:
1148
+ type: [object, 'null']
1149
+ additionalProperties: {}
1150
+ approval_id:
1151
+ type: [string, 'null']
1152
+ format: uuid
1153
+ required:
1154
+ type: [integer, 'null']
1155
+ approved:
1156
+ type: [integer, 'null']
1157
+
1158
+ TransferRequest:
1159
+ type: object
1160
+ required: [receiver_id, amount]
1161
+ properties:
1162
+ chain:
1163
+ allOf:
1164
+ - $ref: '#/components/schemas/Chain'
1165
+ description: Chain to transfer on. Defaults to `near` server-side.
1166
+ receiver_id:
1167
+ type: string
1168
+ amount:
1169
+ type: string
1170
+
1171
+ IntentsDepositRequest:
1172
+ type: object
1173
+ required: [token, amount]
1174
+ properties:
1175
+ token:
1176
+ type: string
1177
+ amount:
1178
+ type: string
1179
+
1180
+ IntentsDepositResponse:
1181
+ type: object
1182
+ required: [request_id, status]
1183
+ properties:
1184
+ request_id:
1185
+ type: string
1186
+ format: uuid
1187
+ status:
1188
+ $ref: '#/components/schemas/RequestStatus'
1189
+ tx_hash:
1190
+ type: [string, 'null']
1191
+
1192
+ WithdrawRequest:
1193
+ type: object
1194
+ required: [chain, amount]
1195
+ properties:
1196
+ chain:
1197
+ $ref: '#/components/schemas/Chain'
1198
+ to:
1199
+ type: string
1200
+ description: Destination address on the target chain.
1201
+ amount:
1202
+ type: string
1203
+ token:
1204
+ type: string
1205
+ description: Token ID, e.g. `nep141:usdt.tether-token.near`. Omit for native asset.
1206
+
1207
+ WithdrawResponse:
1208
+ type: object
1209
+ required: [request_id, status]
1210
+ properties:
1211
+ request_id:
1212
+ type: string
1213
+ format: uuid
1214
+ status:
1215
+ $ref: '#/components/schemas/RequestStatus'
1216
+ approval_id:
1217
+ type: [string, 'null']
1218
+ format: uuid
1219
+ required:
1220
+ type: [integer, 'null']
1221
+ approved:
1222
+ type: [integer, 'null']
1223
+
1224
+ DryRunResponse:
1225
+ type: object
1226
+ properties:
1227
+ would_succeed:
1228
+ type: boolean
1229
+ reason:
1230
+ type: string
1231
+ message:
1232
+ type: string
1233
+ estimated_fee:
1234
+ type: string
1235
+ fee_token:
1236
+ type: string
1237
+ policy_check:
1238
+ type: object
1239
+ properties:
1240
+ decision:
1241
+ type: string
1242
+ enum: [allowed, denied, requires_approval, frozen, no_policy_allow]
1243
+ reason:
1244
+ type: string
1245
+ required_approvals:
1246
+ type: integer
1247
+
1248
+ SwapRequest:
1249
+ type: object
1250
+ required: [token_in, token_out, amount_in]
1251
+ properties:
1252
+ token_in:
1253
+ type: string
1254
+ token_out:
1255
+ type: string
1256
+ amount_in:
1257
+ type: string
1258
+ min_amount_out:
1259
+ type: string
1260
+
1261
+ SwapResponse:
1262
+ type: object
1263
+ required: [request_id, status]
1264
+ properties:
1265
+ request_id:
1266
+ type: string
1267
+ format: uuid
1268
+ status:
1269
+ $ref: '#/components/schemas/RequestStatus'
1270
+ amount_out:
1271
+ type: string
1272
+ intent_hash:
1273
+ type: string
1274
+
1275
+ SwapQuoteResponse:
1276
+ type: object
1277
+ properties:
1278
+ amount_out:
1279
+ type: string
1280
+ min_amount_out:
1281
+ type: string
1282
+ deadline:
1283
+ type: string
1284
+ format: date-time
1285
+ time_estimate_seconds:
1286
+ type: integer
1287
+
1288
+ SignMessageRequest:
1289
+ type: object
1290
+ required: [message, recipient]
1291
+ properties:
1292
+ message:
1293
+ type: string
1294
+ recipient:
1295
+ type: string
1296
+ nonce:
1297
+ type: string
1298
+ description: Base64-encoded 32-byte nonce. Auto-generated if omitted.
1299
+ format:
1300
+ type: string
1301
+ enum: [nep413, raw]
1302
+ default: nep413
1303
+
1304
+ SignMessageResponse:
1305
+ type: object
1306
+ properties:
1307
+ account_id:
1308
+ type: string
1309
+ signature:
1310
+ type: string
1311
+ signature_base64:
1312
+ type: string
1313
+ public_key:
1314
+ type: string
1315
+ nonce:
1316
+ type: string
1317
+
1318
+ # ----- Policy -----
1319
+ PolicyRules:
1320
+ type: object
1321
+ properties:
1322
+ limits:
1323
+ $ref: '#/components/schemas/PolicyLimits'
1324
+ addresses:
1325
+ $ref: '#/components/schemas/AddressList'
1326
+ transaction_types:
1327
+ type: array
1328
+ items:
1329
+ $ref: '#/components/schemas/RequestType'
1330
+ time_restrictions:
1331
+ $ref: '#/components/schemas/TimeRestrictions'
1332
+ rate_limit:
1333
+ $ref: '#/components/schemas/RateLimit'
1334
+
1335
+ PolicyLimits:
1336
+ type: object
1337
+ description: |
1338
+ Per-period spending limits. Each field is a map of `token_id → amount`.
1339
+ Use `"*"` as the key to match all tokens.
1340
+ properties:
1341
+ per_transaction:
1342
+ type: object
1343
+ additionalProperties:
1344
+ type: string
1345
+ hourly:
1346
+ type: object
1347
+ additionalProperties:
1348
+ type: string
1349
+ daily:
1350
+ type: object
1351
+ additionalProperties:
1352
+ type: string
1353
+ monthly:
1354
+ type: object
1355
+ additionalProperties:
1356
+ type: string
1357
+
1358
+ AddressList:
1359
+ type: object
1360
+ required: [mode, list]
1361
+ properties:
1362
+ mode:
1363
+ type: string
1364
+ enum: [whitelist, blacklist]
1365
+ list:
1366
+ type: array
1367
+ items:
1368
+ type: string
1369
+
1370
+ TimeRestrictions:
1371
+ type: object
1372
+ properties:
1373
+ timezone:
1374
+ type: string
1375
+ description: IANA timezone name, e.g. `UTC` or `Europe/Berlin`.
1376
+ allowed_hours:
1377
+ type: array
1378
+ items:
1379
+ type: integer
1380
+ minimum: 0
1381
+ maximum: 23
1382
+ allowed_days:
1383
+ type: array
1384
+ items:
1385
+ type: integer
1386
+ minimum: 0
1387
+ maximum: 6
1388
+ description: 0 = Sunday, 6 = Saturday.
1389
+
1390
+ RateLimit:
1391
+ type: object
1392
+ properties:
1393
+ max_per_hour:
1394
+ type: integer
1395
+
1396
+ ApprovalConfig:
1397
+ type: object
1398
+ properties:
1399
+ threshold:
1400
+ type: object
1401
+ required: [required, of]
1402
+ properties:
1403
+ required:
1404
+ type: integer
1405
+ of:
1406
+ type: integer
1407
+ above_usd:
1408
+ type: number
1409
+ description: Approval gate triggers when transaction value exceeds this USD amount.
1410
+ approvers:
1411
+ type: array
1412
+ items:
1413
+ $ref: '#/components/schemas/Approver'
1414
+
1415
+ Approver:
1416
+ type: object
1417
+ required: [id, role]
1418
+ properties:
1419
+ id:
1420
+ type: string
1421
+ description: Public key, e.g. `ed25519:...`.
1422
+ role:
1423
+ type: string
1424
+ enum: [admin, signer]
1425
+
1426
+ AdminQuorum:
1427
+ type: object
1428
+ required: [required, admins]
1429
+ properties:
1430
+ required:
1431
+ type: integer
1432
+ admins:
1433
+ type: array
1434
+ items:
1435
+ type: string
1436
+
1437
+ PolicyResponse:
1438
+ type: object
1439
+ required: [wallet_id, controller, frozen]
1440
+ properties:
1441
+ wallet_id:
1442
+ type: string
1443
+ controller:
1444
+ type: string
1445
+ description: NEAR account that owns the policy.
1446
+ frozen:
1447
+ type: boolean
1448
+ rules:
1449
+ $ref: '#/components/schemas/PolicyRules'
1450
+ approval:
1451
+ $ref: '#/components/schemas/ApprovalConfig'
1452
+ authorized_key_hashes:
1453
+ type: array
1454
+ items:
1455
+ type: string
1456
+ usage:
1457
+ type: object
1458
+ additionalProperties: {}
1459
+ description: |
1460
+ Current accumulated usage per token, per period. Same shape as
1461
+ `PolicyLimits` — used for client-side velocity-limit visualization.
1462
+
1463
+ EncryptPolicyRequest:
1464
+ type: object
1465
+ required: [wallet_id, rules]
1466
+ properties:
1467
+ wallet_id:
1468
+ type: string
1469
+ rules:
1470
+ $ref: '#/components/schemas/PolicyRules'
1471
+ approval:
1472
+ $ref: '#/components/schemas/ApprovalConfig'
1473
+ admin_quorum:
1474
+ $ref: '#/components/schemas/AdminQuorum'
1475
+ webhook_url:
1476
+ type: string
1477
+ format: uri
1478
+ authorized_key_hashes:
1479
+ type: array
1480
+ items:
1481
+ type: string
1482
+
1483
+ EncryptPolicyResponse:
1484
+ type: object
1485
+ required: [encrypted_base64, wallet_pubkey]
1486
+ properties:
1487
+ encrypted_base64:
1488
+ type: string
1489
+ wallet_pubkey:
1490
+ type: string
1491
+
1492
+ SignPolicyRequest:
1493
+ type: object
1494
+ required: [encrypted_data]
1495
+ properties:
1496
+ encrypted_data:
1497
+ type: string
1498
+
1499
+ SignPolicyResponse:
1500
+ type: object
1501
+ required: [signature_hex, public_key_hex]
1502
+ properties:
1503
+ signature_hex:
1504
+ type: string
1505
+ public_key_hex:
1506
+ type: string
1507
+
1508
+ InvalidateCacheRequest:
1509
+ type: object
1510
+ required: [wallet_id]
1511
+ properties:
1512
+ wallet_id:
1513
+ type: string
1514
+
1515
+ # ----- Approvals -----
1516
+ PendingApproval:
1517
+ type: object
1518
+ required: [approval_id, type, request_data, required, approved, expires_at]
1519
+ properties:
1520
+ approval_id:
1521
+ type: string
1522
+ format: uuid
1523
+ request_id:
1524
+ type: [string, 'null']
1525
+ format: uuid
1526
+ type:
1527
+ $ref: '#/components/schemas/RequestType'
1528
+ request_data:
1529
+ type: object
1530
+ additionalProperties: {}
1531
+ required:
1532
+ type: integer
1533
+ approved:
1534
+ type: integer
1535
+ expires_at:
1536
+ type: string
1537
+ format: date-time
1538
+
1539
+ PendingApprovalsResponse:
1540
+ type: object
1541
+ required: [approvals]
1542
+ properties:
1543
+ approvals:
1544
+ type: array
1545
+ items:
1546
+ $ref: '#/components/schemas/PendingApproval'
1547
+
1548
+ Nep413Auth:
1549
+ type: object
1550
+ description: |
1551
+ NEP-413 signed authentication payload. The signed message is
1552
+ `approve:{approval_id}:{request_hash}` for approve or
1553
+ `reject:{approval_id}:{request_hash}` for reject.
1554
+ required: [signature, public_key, account_id, nonce]
1555
+ properties:
1556
+ signature:
1557
+ type: string
1558
+ public_key:
1559
+ type: string
1560
+ account_id:
1561
+ type: string
1562
+ nonce:
1563
+ type: string
1564
+
1565
+ RejectRequest:
1566
+ allOf:
1567
+ - $ref: '#/components/schemas/Nep413Auth'
1568
+ - type: object
1569
+ properties:
1570
+ reason:
1571
+ type: string
1572
+
1573
+ ApproveResponse:
1574
+ type: object
1575
+ required: [approval_id, status, approved, required]
1576
+ properties:
1577
+ approval_id:
1578
+ type: string
1579
+ format: uuid
1580
+ status:
1581
+ type: string
1582
+ enum: [pending, approved, rejected, expired]
1583
+ approved:
1584
+ type: integer
1585
+ required:
1586
+ type: integer
1587
+ request_id:
1588
+ type: [string, 'null']
1589
+ format: uuid
1590
+
1591
+ # ----- Audit & requests -----
1592
+ AuditEvent:
1593
+ type: object
1594
+ required: [type, details, at]
1595
+ properties:
1596
+ type:
1597
+ type: string
1598
+ request_id:
1599
+ type: [string, 'null']
1600
+ status:
1601
+ type: [string, 'null']
1602
+ details:
1603
+ type: object
1604
+ additionalProperties: {}
1605
+ at:
1606
+ type: string
1607
+ format: date-time
1608
+
1609
+ AuditResponse:
1610
+ type: object
1611
+ required: [events]
1612
+ properties:
1613
+ events:
1614
+ type: array
1615
+ items:
1616
+ $ref: '#/components/schemas/AuditEvent'
1617
+
1618
+ RequestStatusResponse:
1619
+ type: object
1620
+ required: [request_id, type, status, created_at]
1621
+ properties:
1622
+ request_id:
1623
+ type: string
1624
+ format: uuid
1625
+ type:
1626
+ $ref: '#/components/schemas/RequestType'
1627
+ status:
1628
+ $ref: '#/components/schemas/RequestStatus'
1629
+ result:
1630
+ type: [object, 'null']
1631
+ additionalProperties: {}
1632
+ created_at:
1633
+ type: string
1634
+ format: date-time
1635
+ updated_at:
1636
+ type: [string, 'null']
1637
+ format: date-time
1638
+
1639
+ RequestListResponse:
1640
+ type: object
1641
+ required: [requests, total]
1642
+ properties:
1643
+ requests:
1644
+ type: array
1645
+ items:
1646
+ $ref: '#/components/schemas/RequestStatusResponse'
1647
+ total:
1648
+ type: integer