stables-mcp-server 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -41
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/build/lib/stables-client.d.ts +214 -126
- package/build/lib/stables-client.d.ts.map +1 -1
- package/build/lib/stables-client.js +54 -19
- package/build/lib/stables-client.js.map +1 -1
- package/build/tools/api-keys.d.ts.map +1 -1
- package/build/tools/api-keys.js +7 -3
- package/build/tools/api-keys.js.map +1 -1
- package/build/tools/customers.d.ts.map +1 -1
- package/build/tools/customers.js +239 -106
- package/build/tools/customers.js.map +1 -1
- package/build/tools/payment-methods.d.ts +12 -0
- package/build/tools/payment-methods.d.ts.map +1 -0
- package/build/tools/payment-methods.js +119 -0
- package/build/tools/payment-methods.js.map +1 -0
- package/build/tools/quotes.d.ts +5 -1
- package/build/tools/quotes.d.ts.map +1 -1
- package/build/tools/quotes.js +103 -59
- package/build/tools/quotes.js.map +1 -1
- package/build/tools/transfers.d.ts +4 -1
- package/build/tools/transfers.d.ts.map +1 -1
- package/build/tools/transfers.js +212 -112
- package/build/tools/transfers.js.map +1 -1
- package/build/tools/virtual-accounts.d.ts.map +1 -1
- package/build/tools/virtual-accounts.js +55 -69
- package/build/tools/virtual-accounts.js.map +1 -1
- package/build/tools/webhooks.d.ts.map +1 -1
- package/build/tools/webhooks.js +37 -19
- package/build/tools/webhooks.js.map +1 -1
- package/package.json +10 -3
- package/build/tools/notifications.d.ts +0 -8
- package/build/tools/notifications.d.ts.map +0 -1
- package/build/tools/notifications.js +0 -125
- package/build/tools/notifications.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Stables MCP Server
|
|
2
2
|
|
|
3
|
-
An MCP (Model Context Protocol) server that exposes the Stables fiat-to-crypto API to AI agents. This allows AI assistants like Claude, ChatGPT, and
|
|
3
|
+
An MCP (Model Context Protocol) server that exposes the Stables fiat-to-crypto API to AI agents. This allows AI assistants like Claude, ChatGPT, Cursor, Codex, and other MCP-compatible clients to manage customers, create USDC and USDT quotes, execute approved transfers, and handle virtual accounts programmatically.
|
|
4
|
+
|
|
5
|
+
Use it to build stablecoin payment workflows for AI agents and agentic commerce: payouts, virtual account deposits, treasury movement, fiat off-ramping, and webhook reconciliation.
|
|
4
6
|
|
|
5
7
|
## What is MCP?
|
|
6
8
|
|
|
@@ -8,7 +10,7 @@ MCP (Model Context Protocol) is an open standard that provides a standardized wa
|
|
|
8
10
|
|
|
9
11
|
## Features
|
|
10
12
|
|
|
11
|
-
This MCP server provides
|
|
13
|
+
This MCP server provides 23 tools across 7 categories:
|
|
12
14
|
|
|
13
15
|
### Customer Management
|
|
14
16
|
- `create_customer` - Create individual or business customers
|
|
@@ -31,10 +33,11 @@ This MCP server provides 25 tools across 7 categories:
|
|
|
31
33
|
- `create_virtual_account` - Create virtual bank accounts for fiat deposits
|
|
32
34
|
- `list_virtual_accounts` - List virtual accounts for a customer
|
|
33
35
|
- `update_virtual_account` - Update virtual account settings
|
|
34
|
-
- `deactivate_virtual_account` - Deactivate a virtual account
|
|
35
|
-
- `reactivate_virtual_account` - Reactivate a deactivated virtual account
|
|
36
36
|
- `get_virtual_account_history` - Get activity history for a virtual account
|
|
37
37
|
|
|
38
|
+
### Payment Methods
|
|
39
|
+
- `validate_payment_method` - Check payout details against a currency's rules before creating a quote or transfer
|
|
40
|
+
|
|
38
41
|
### API Keys
|
|
39
42
|
- `create_api_key` - Create a new API key
|
|
40
43
|
- `list_api_keys` - List all API keys
|
|
@@ -46,9 +49,6 @@ This MCP server provides 25 tools across 7 categories:
|
|
|
46
49
|
- `list_webhooks` - List all webhook subscriptions
|
|
47
50
|
- `delete_webhook` - Delete a webhook subscription
|
|
48
51
|
|
|
49
|
-
### Notifications
|
|
50
|
-
- `send_verification_sms` - Send a KYC verification link to a customer via SMS (requires Twilio)
|
|
51
|
-
|
|
52
52
|
## Installation
|
|
53
53
|
|
|
54
54
|
```bash
|
|
@@ -69,10 +69,26 @@ The server requires the following environment variables:
|
|
|
69
69
|
| Variable | Required | Description |
|
|
70
70
|
|----------|----------|-------------|
|
|
71
71
|
| `STABLES_API_KEY` | Yes | Your Stables API key |
|
|
72
|
-
| `STABLES_API_URL` | No | API base URL (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
| `STABLES_API_URL` | No | API base URL. Defaults to the environment your key belongs to (see below). Must use HTTPS. |
|
|
73
|
+
|
|
74
|
+
### Which environment you're talking to
|
|
75
|
+
|
|
76
|
+
Stables keys carry their environment: `sti_test_…` is a sandbox key, `sti_live_…`
|
|
77
|
+
is a production key, and the API refuses a key that arrives at the wrong
|
|
78
|
+
environment. So you don't have to set a URL — leave `STABLES_API_URL` unset and
|
|
79
|
+
the key decides:
|
|
80
|
+
|
|
81
|
+
| Your key | Where requests go |
|
|
82
|
+
|----------|-------------------|
|
|
83
|
+
| `sti_test_…` | `https://api.sandbox.stables.money` |
|
|
84
|
+
| `sti_live_…` | `https://api.stables.money` — **real money** |
|
|
85
|
+
| `sti_local_…` or anything else | production, unless you set `STABLES_API_URL` |
|
|
86
|
+
|
|
87
|
+
Setting `STABLES_API_URL` always wins, which is how you reach staging, dev or a
|
|
88
|
+
local deployment.
|
|
89
|
+
|
|
90
|
+
**Start with a sandbox key.** An agent holding a live key can move real money on
|
|
91
|
+
your behalf; see [agent safety](https://docs.stables.money/get-started/getting-started/quickstart/building-with-ai/agent-safety).
|
|
76
92
|
|
|
77
93
|
## Usage
|
|
78
94
|
|
|
@@ -97,6 +113,29 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
97
113
|
|
|
98
114
|
Then restart Claude Desktop.
|
|
99
115
|
|
|
116
|
+
### With Cursor, Codex, ChatGPT, or another MCP client
|
|
117
|
+
|
|
118
|
+
Use the same command and environment variables in any MCP-compatible client:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"mcpServers": {
|
|
123
|
+
"stables": {
|
|
124
|
+
"command": "npx",
|
|
125
|
+
"args": ["stables-mcp-server"],
|
|
126
|
+
"env": {
|
|
127
|
+
"STABLES_API_KEY": "your-api-key",
|
|
128
|
+
"STABLES_API_URL": "https://api.sandbox.stables.money"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Agent safety
|
|
136
|
+
|
|
137
|
+
Stables is financial infrastructure. Agents should create quotes, prepare payment objects, and reconcile webhooks, but should require explicit human approval before creating transfers or other money movement. Check customer KYC/KYB status and entitlements before transactional actions, and treat sanctions, unsupported jurisdiction, verification, or compliance failures as hard stops.
|
|
138
|
+
|
|
100
139
|
### With MCP Inspector (for testing)
|
|
101
140
|
|
|
102
141
|
```bash
|
|
@@ -118,7 +157,7 @@ STABLES_API_KEY=your-api-key node build/index.js
|
|
|
118
157
|
|
|
119
158
|
### Creating a customer and getting a quote
|
|
120
159
|
|
|
121
|
-
**User:** "Create a customer for john@example.com and get a quote to convert 1000
|
|
160
|
+
**User:** "Create a customer for john@example.com and get a quote to convert 1000 USDT to EUR"
|
|
122
161
|
|
|
123
162
|
**AI (using MCP tools):**
|
|
124
163
|
1. Calls `create_customer` with email and type
|
|
@@ -179,7 +218,7 @@ stables-mcp-server/
|
|
|
179
218
|
│ ├── virtual-accounts.ts # Virtual account tools (6)
|
|
180
219
|
│ ├── api-keys.ts # API key tools (4)
|
|
181
220
|
│ ├── webhooks.ts # Webhook tools (3)
|
|
182
|
-
│ └──
|
|
221
|
+
│ └── payment-methods.ts # Payment method validation (1)
|
|
183
222
|
├── package.json
|
|
184
223
|
├── tsconfig.json
|
|
185
224
|
├── vitest.config.ts
|
|
@@ -336,22 +375,6 @@ Update virtual account settings.
|
|
|
336
375
|
| virtualAccountId | string | Yes | Virtual account ID |
|
|
337
376
|
| depositHandlingMode | string | Yes | New deposit handling mode |
|
|
338
377
|
|
|
339
|
-
#### deactivate_virtual_account
|
|
340
|
-
Deactivate a virtual account to prevent new deposits.
|
|
341
|
-
|
|
342
|
-
| Parameter | Type | Required | Description |
|
|
343
|
-
|-----------|------|----------|-------------|
|
|
344
|
-
| customerId | string | Yes | Customer ID |
|
|
345
|
-
| virtualAccountId | string | Yes | Virtual account ID |
|
|
346
|
-
|
|
347
|
-
#### reactivate_virtual_account
|
|
348
|
-
Reactivate a previously deactivated virtual account.
|
|
349
|
-
|
|
350
|
-
| Parameter | Type | Required | Description |
|
|
351
|
-
|-----------|------|----------|-------------|
|
|
352
|
-
| customerId | string | Yes | Customer ID |
|
|
353
|
-
| virtualAccountId | string | Yes | Virtual account ID |
|
|
354
|
-
|
|
355
378
|
#### get_virtual_account_history
|
|
356
379
|
Get activity history for a virtual account.
|
|
357
380
|
|
|
@@ -429,18 +452,6 @@ Delete a webhook subscription.
|
|
|
429
452
|
|-----------|------|----------|-------------|
|
|
430
453
|
| webhookId | string | Yes | Webhook subscription ID |
|
|
431
454
|
|
|
432
|
-
### Notification Tools
|
|
433
|
-
|
|
434
|
-
#### send_verification_sms
|
|
435
|
-
Send a KYC verification link to a customer via SMS. Requires Twilio environment variables.
|
|
436
|
-
|
|
437
|
-
| Parameter | Type | Required | Description |
|
|
438
|
-
|-----------|------|----------|-------------|
|
|
439
|
-
| customerId | string | Yes | Customer ID to send verification to |
|
|
440
|
-
| phone | string | No | Override phone number (uses customer's phone if not provided) |
|
|
441
|
-
| botName | string | No | Name of the assistant sending the message |
|
|
442
|
-
| verificationLinkTtlSecs | number | No | Verification link expiry in seconds (default: 1800) |
|
|
443
|
-
|
|
444
455
|
## Security
|
|
445
456
|
|
|
446
457
|
- API keys are only read from environment variables
|
package/build/index.js
CHANGED
|
@@ -32,7 +32,7 @@ import { registerTransferTools } from "./tools/transfers.js";
|
|
|
32
32
|
import { registerVirtualAccountTools } from "./tools/virtual-accounts.js";
|
|
33
33
|
import { registerApiKeyTools } from "./tools/api-keys.js";
|
|
34
34
|
import { registerWebhookTools } from "./tools/webhooks.js";
|
|
35
|
-
import {
|
|
35
|
+
import { registerPaymentMethodTools } from "./tools/payment-methods.js";
|
|
36
36
|
// Validate environment
|
|
37
37
|
const apiKey = process.env.STABLES_API_KEY;
|
|
38
38
|
if (!apiKey) {
|
|
@@ -43,7 +43,7 @@ if (!apiKey) {
|
|
|
43
43
|
// Create the MCP server
|
|
44
44
|
const server = new McpServer({
|
|
45
45
|
name: "stables-mcp-server",
|
|
46
|
-
version: "
|
|
46
|
+
version: "2.0.0",
|
|
47
47
|
description: "Stables fiat-to-crypto API for AI agents - manage customers, quotes, transfers, and virtual accounts",
|
|
48
48
|
});
|
|
49
49
|
// Create the Stables API client
|
|
@@ -55,7 +55,7 @@ registerTransferTools(server, stablesClient);
|
|
|
55
55
|
registerVirtualAccountTools(server, stablesClient);
|
|
56
56
|
registerApiKeyTools(server, stablesClient);
|
|
57
57
|
registerWebhookTools(server, stablesClient);
|
|
58
|
-
|
|
58
|
+
registerPaymentMethodTools(server, stablesClient);
|
|
59
59
|
// Start the server with STDIO transport
|
|
60
60
|
async function main() {
|
|
61
61
|
const transport = new StdioServerTransport();
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAExE,uBAAuB;AACvB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;AAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;IACZ,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,wBAAwB;AACxB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,OAAO;IAChB,WAAW,EACT,sGAAsG;CACzG,CAAC,CAAC;AAEH,gCAAgC;AAChC,MAAM,aAAa,GAAG,mBAAmB,EAAE,CAAC;AAE5C,qBAAqB;AACrB,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC7C,kBAAkB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC1C,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC7C,2BAA2B,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AACnD,mBAAmB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC3C,oBAAoB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC5C,0BAA0B,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAElD,wCAAwC;AACxC,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -8,92 +8,117 @@ export declare class StablesApiError extends Error {
|
|
|
8
8
|
readonly errorBody?: unknown | undefined;
|
|
9
9
|
constructor(message: string, statusCode: number, endpoint: string, errorBody?: unknown | undefined);
|
|
10
10
|
}
|
|
11
|
-
export type CustomerType = "
|
|
12
|
-
export type VerificationStatus = "
|
|
13
|
-
export type VerificationLevel = "
|
|
11
|
+
export type CustomerType = "individual" | "business";
|
|
12
|
+
export type VerificationStatus = "in_progress" | "approved" | "rejected" | "not_started";
|
|
13
|
+
export type VerificationLevel = "individual_base" | "individual_enhanced" | "business_base" | "base_business";
|
|
14
14
|
export interface VerificationLevelResponse {
|
|
15
15
|
level: VerificationLevel;
|
|
16
16
|
status: VerificationStatus;
|
|
17
|
+
sub_status?: string[];
|
|
18
|
+
details?: unknown[];
|
|
17
19
|
}
|
|
20
|
+
/** The five feature entitlements the API accepts. */
|
|
21
|
+
export type EntitlementId = "base_payout" | "virtual_account" | "eur_virtual_account" | "usd_virtual_account" | "aed_local";
|
|
18
22
|
export interface Entitlement {
|
|
19
23
|
name: string;
|
|
20
|
-
status: "
|
|
24
|
+
status: "submitted" | "pending" | "in_progress" | "approved" | "rejected";
|
|
21
25
|
}
|
|
22
26
|
export interface CustomerAddress {
|
|
23
27
|
line1: string;
|
|
24
28
|
line2?: string;
|
|
25
29
|
city: string;
|
|
26
30
|
state?: string;
|
|
27
|
-
|
|
31
|
+
postal_code?: string;
|
|
28
32
|
country: string;
|
|
29
33
|
}
|
|
30
34
|
export interface Customer {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
customer_id: string;
|
|
36
|
+
external_customer_id?: string;
|
|
37
|
+
customer_type: CustomerType;
|
|
34
38
|
email: string;
|
|
35
39
|
phone?: string;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
first_name?: string;
|
|
41
|
+
last_name?: string;
|
|
42
|
+
company_name?: string;
|
|
43
|
+
status?: string;
|
|
44
|
+
compliance_lock?: boolean;
|
|
39
45
|
entitlements?: Entitlement[];
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
created_at: string;
|
|
47
|
+
updated_at: string;
|
|
48
|
+
verification_levels?: VerificationLevelResponse[];
|
|
43
49
|
metadata?: Record<string, string>;
|
|
44
50
|
}
|
|
45
51
|
export interface CreateIndividualCustomerRequest {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
email
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
customer_type: "individual";
|
|
53
|
+
/** Required — the API rejects a customer without one. */
|
|
54
|
+
email: string;
|
|
55
|
+
external_customer_id?: string;
|
|
56
|
+
first_name?: string;
|
|
57
|
+
last_name?: string;
|
|
58
|
+
middle_name?: string;
|
|
52
59
|
phone?: string;
|
|
53
60
|
dob?: string;
|
|
54
61
|
nationality?: string;
|
|
62
|
+
tax_id_number?: string;
|
|
55
63
|
address?: CustomerAddress;
|
|
56
|
-
entitlements?:
|
|
64
|
+
entitlements?: EntitlementId[];
|
|
57
65
|
metadata?: Record<string, string>;
|
|
58
66
|
}
|
|
59
67
|
export interface CreateBusinessCustomerRequest {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
customer_type: "business";
|
|
69
|
+
email: string;
|
|
70
|
+
company_name: string;
|
|
71
|
+
external_customer_id?: string;
|
|
63
72
|
phone?: string;
|
|
64
|
-
companyName: string;
|
|
65
73
|
country?: string;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
registration_number?: string;
|
|
75
|
+
legal_address?: CustomerAddress;
|
|
76
|
+
postal_address?: CustomerAddress;
|
|
77
|
+
incorporated_on?: string;
|
|
69
78
|
type?: string;
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
tax_id?: string;
|
|
80
|
+
registration_location?: string;
|
|
72
81
|
website?: string;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
entitlements?: string[];
|
|
82
|
+
alternative_names?: string[];
|
|
83
|
+
describe_business?: string;
|
|
84
|
+
conduct_money_services?: boolean;
|
|
85
|
+
describe_money_services?: string;
|
|
86
|
+
describe_compliance_controls?: string;
|
|
87
|
+
account_purpose?: string;
|
|
88
|
+
account_purpose_other?: string;
|
|
89
|
+
is_your_business_a_dao?: boolean;
|
|
90
|
+
industry_selection?: string;
|
|
91
|
+
main_source_of_funds?: string;
|
|
92
|
+
source_of_funds?: string;
|
|
93
|
+
source_of_funds_description?: string;
|
|
94
|
+
expected_annual_revenue?: string;
|
|
95
|
+
expected_monthly_payments?: string;
|
|
96
|
+
does_your_business_engage_in_high_risk_activities?: "yes" | "no";
|
|
97
|
+
high_risk_activities?: string[];
|
|
98
|
+
operate_in_prohibited_country?: boolean;
|
|
99
|
+
accept_terms?: boolean;
|
|
100
|
+
how_did_you_come_across_stables?: string;
|
|
101
|
+
entitlements?: EntitlementId[];
|
|
94
102
|
metadata?: Record<string, string>;
|
|
95
103
|
}
|
|
96
104
|
export type CreateCustomerRequest = CreateIndividualCustomerRequest | CreateBusinessCustomerRequest;
|
|
105
|
+
/**
|
|
106
|
+
* Every field optional, so an unknown key is not an error — it is simply
|
|
107
|
+
* dropped. Sending camelCase here parsed to an empty object and returned 200
|
|
108
|
+
* having changed nothing.
|
|
109
|
+
*/
|
|
110
|
+
export interface UpdateCustomerRequest {
|
|
111
|
+
email?: string;
|
|
112
|
+
phone?: string;
|
|
113
|
+
first_name?: string;
|
|
114
|
+
last_name?: string;
|
|
115
|
+
middle_name?: string;
|
|
116
|
+
dob?: string;
|
|
117
|
+
nationality?: string;
|
|
118
|
+
company_name?: string;
|
|
119
|
+
entitlements?: EntitlementId[];
|
|
120
|
+
metadata?: Record<string, string>;
|
|
121
|
+
}
|
|
97
122
|
export interface ListCustomersResponse {
|
|
98
123
|
customers: Customer[];
|
|
99
124
|
}
|
|
@@ -108,64 +133,105 @@ export interface GenerateVerificationLinkRequest {
|
|
|
108
133
|
redirect?: VerificationRedirect;
|
|
109
134
|
}
|
|
110
135
|
export interface GenerateVerificationLinkResponse {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
export type TransferType = "TRANSFER_TYPE_OFFRAMP" | "TRANSFER_TYPE_ONRAMP";
|
|
115
|
-
export type TransferStatus = "CREATED" | "COMPLIANCE_HOLD" | "AWAITING_FUNDS_COLLECTION" | "FUNDS_COLLECTED" | "PAYMENT_SUBMITTED" | "PAYMENT_PROCESSED" | "COMPLETED" | "FAILED" | "CANCELLED" | "EXPIRED";
|
|
116
|
-
export interface BankCodes {
|
|
117
|
-
swiftCode?: string;
|
|
118
|
-
bicCode?: string;
|
|
119
|
-
ifscCode?: string;
|
|
120
|
-
abaCode?: string;
|
|
121
|
-
sortCode?: string;
|
|
122
|
-
branchCode?: string;
|
|
123
|
-
bsbCode?: string;
|
|
124
|
-
bankCode?: string;
|
|
125
|
-
cnaps?: string;
|
|
136
|
+
customer_id: string;
|
|
137
|
+
kyc_link: string;
|
|
126
138
|
}
|
|
127
|
-
export
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
139
|
+
export type TransferType = "offramp" | "onramp";
|
|
140
|
+
export type TransferStatus = "created" | "compliance_hold" | "awaiting_funds_collection" | "funds_collected" | "in_progress" | "payment_submitted" | "payment_processed" | "completed" | "failed" | "cancelled" | "expired" | "unknown";
|
|
141
|
+
/** ISO 3166-1 alpha-2, lowercase, per AddressApiSchema. */
|
|
142
|
+
export interface BeneficiaryAddress {
|
|
143
|
+
street: string;
|
|
144
|
+
city: string;
|
|
145
|
+
state: string;
|
|
146
|
+
postal_code: string;
|
|
147
|
+
country: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Bank payout destination. Bank codes are flat here — the nested `bankCodes`
|
|
151
|
+
* object the API used to take is gone.
|
|
152
|
+
*/
|
|
153
|
+
export interface BankTransferDestination {
|
|
154
|
+
type: "bank";
|
|
155
|
+
account_holder_name: string;
|
|
156
|
+
bank_name: string;
|
|
157
|
+
bank_country: string;
|
|
133
158
|
currency: string;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Required for payouts in AED, CAD, EUR, GBP, MXN and USD, together with
|
|
161
|
+
* `address`, and `date_of_birth` when this is "individual".
|
|
162
|
+
*/
|
|
163
|
+
recipient_type?: "individual" | "business";
|
|
164
|
+
date_of_birth?: string;
|
|
165
|
+
address?: BeneficiaryAddress;
|
|
166
|
+
account_number?: string;
|
|
167
|
+
iban?: string;
|
|
168
|
+
pay_id?: string;
|
|
169
|
+
pay_id_type?: "email" | "phone" | "abn" | "org_id";
|
|
170
|
+
account_type?: "savings" | "checking" | "payment";
|
|
171
|
+
branch_name?: string;
|
|
172
|
+
swift_code?: string;
|
|
173
|
+
bic_code?: string;
|
|
174
|
+
ifsc_code?: string;
|
|
175
|
+
aba_code?: string;
|
|
176
|
+
sort_code?: string;
|
|
177
|
+
branch_code?: string;
|
|
178
|
+
bsb_code?: string;
|
|
179
|
+
bank_code?: string;
|
|
180
|
+
cnaps?: string;
|
|
181
|
+
name_in_local_language?: string;
|
|
182
|
+
national_identification_number?: string;
|
|
183
|
+
}
|
|
184
|
+
export interface PaymentMethodValidationError {
|
|
185
|
+
field?: string;
|
|
186
|
+
message: string;
|
|
187
|
+
code: "UNSUPPORTED_CURRENCY" | "UNSUPPORTED_PAYMENT_METHOD" | "INVALID_FIELDS";
|
|
137
188
|
}
|
|
138
|
-
export interface
|
|
139
|
-
|
|
189
|
+
export interface ValidatePaymentMethodResponse {
|
|
190
|
+
valid: boolean;
|
|
191
|
+
errors?: PaymentMethodValidationError[];
|
|
140
192
|
}
|
|
141
|
-
export interface
|
|
142
|
-
|
|
193
|
+
export interface CryptoTransferDestination {
|
|
194
|
+
type: "crypto";
|
|
143
195
|
currency: string;
|
|
144
196
|
network: string;
|
|
145
|
-
|
|
197
|
+
address: string;
|
|
198
|
+
}
|
|
199
|
+
export type TransferDestination = BankTransferDestination | CryptoTransferDestination;
|
|
200
|
+
/** Where the customer sends funds for an offramp. Was `collectionInstructions`. */
|
|
201
|
+
export interface SourceDepositInstructions {
|
|
202
|
+
wallet_address?: string;
|
|
203
|
+
currency?: string;
|
|
204
|
+
network?: string;
|
|
205
|
+
amount?: string;
|
|
206
|
+
[key: string]: unknown;
|
|
146
207
|
}
|
|
147
208
|
export interface Transfer {
|
|
148
209
|
id: string;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
210
|
+
tenant_id: string;
|
|
211
|
+
customer_id: string;
|
|
212
|
+
quote_id: string;
|
|
152
213
|
type: TransferType;
|
|
153
214
|
status: TransferStatus;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
215
|
+
origin?: "api" | "otc";
|
|
216
|
+
created_at: string;
|
|
217
|
+
updated_at: string;
|
|
218
|
+
source_deposit_instructions?: SourceDepositInstructions;
|
|
219
|
+
destination?: TransferDestination;
|
|
220
|
+
fees?: Record<string, unknown>;
|
|
221
|
+
exchange_rate?: string;
|
|
157
222
|
metadata?: Record<string, string>;
|
|
158
223
|
}
|
|
159
224
|
export interface CreateTransferRequest {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
225
|
+
customer_id: string;
|
|
226
|
+
quote_id: string;
|
|
227
|
+
destination: TransferDestination;
|
|
228
|
+
purpose_code?: string;
|
|
163
229
|
metadata?: Record<string, string>;
|
|
164
230
|
}
|
|
165
231
|
export interface ListTransfersResponse {
|
|
166
232
|
transfers: Transfer[];
|
|
167
233
|
page: {
|
|
168
|
-
|
|
234
|
+
next_page_token: string;
|
|
169
235
|
total: number;
|
|
170
236
|
};
|
|
171
237
|
}
|
|
@@ -235,56 +301,62 @@ export interface VirtualAccountHistoryEvent {
|
|
|
235
301
|
deposit_id?: string;
|
|
236
302
|
created_at: string;
|
|
237
303
|
}
|
|
238
|
-
export type QuoteStatus = "
|
|
239
|
-
|
|
240
|
-
|
|
304
|
+
export type QuoteStatus = "active" | "expired" | "used" | "cancelled" | "preview";
|
|
305
|
+
/**
|
|
306
|
+
* Live blockchain networks. The client used to allow only ethereum and polygon,
|
|
307
|
+
* which blocked six networks the platform supports.
|
|
308
|
+
*/
|
|
309
|
+
export type QuoteNetwork = "arbitrum" | "avalanche" | "base" | "ethereum" | "optimism" | "polygon" | "solana" | "tron";
|
|
241
310
|
export interface CurrencyAmount {
|
|
242
311
|
currency: string;
|
|
243
312
|
amount: string;
|
|
244
313
|
network?: string;
|
|
245
314
|
}
|
|
246
315
|
export interface FeeBreakdown {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
316
|
+
fx_fee?: CurrencyAmount;
|
|
317
|
+
integrator_fee?: CurrencyAmount;
|
|
318
|
+
platform_fee?: CurrencyAmount;
|
|
319
|
+
payment_method_fee?: CurrencyAmount;
|
|
320
|
+
network_fee?: CurrencyAmount;
|
|
321
|
+
total_fee: CurrencyAmount;
|
|
253
322
|
}
|
|
254
323
|
export interface Quote {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
currency: string;
|
|
259
|
-
amount: string;
|
|
260
|
-
network?: string;
|
|
261
|
-
paymentMethodType: PaymentMethodType;
|
|
262
|
-
};
|
|
324
|
+
quote_id: string;
|
|
325
|
+
source: CurrencyAmount;
|
|
326
|
+
destination: CurrencyAmount;
|
|
263
327
|
fees: FeeBreakdown;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
328
|
+
exchange_rate: number;
|
|
329
|
+
expires_at: string;
|
|
330
|
+
created_at: string;
|
|
267
331
|
status: QuoteStatus;
|
|
268
332
|
metadata?: Record<string, string>;
|
|
269
333
|
}
|
|
270
334
|
export interface CreateQuoteRequest {
|
|
271
|
-
|
|
272
|
-
from: {
|
|
335
|
+
source: {
|
|
273
336
|
currency: string;
|
|
274
|
-
|
|
337
|
+
/** Required for offramp (crypto source); omitted for onramp (fiat source). */
|
|
338
|
+
network?: string;
|
|
275
339
|
amount: string;
|
|
276
340
|
};
|
|
277
|
-
|
|
341
|
+
destination: {
|
|
278
342
|
currency: string;
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
343
|
+
/** Required for offramp. */
|
|
344
|
+
country?: string;
|
|
345
|
+
/** Offramp: payment network (swift/bank). Onramp: blockchain network. */
|
|
346
|
+
network?: string;
|
|
347
|
+
/** Required for onramp. */
|
|
348
|
+
address?: string;
|
|
282
349
|
};
|
|
350
|
+
/** Price without persisting the quote. Returns status "preview". */
|
|
351
|
+
preview?: boolean;
|
|
283
352
|
metadata?: Record<string, string>;
|
|
284
353
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
354
|
+
/**
|
|
355
|
+
* The quote endpoints return the quote directly. They used to wrap it in
|
|
356
|
+
* `{ quote }`, and reading the wrapper gave undefined and then a TypeError on
|
|
357
|
+
* the first field access.
|
|
358
|
+
*/
|
|
359
|
+
export type CreateQuoteResponse = Quote;
|
|
288
360
|
export interface ApiKey {
|
|
289
361
|
apiKeyId: string;
|
|
290
362
|
tenantId: string;
|
|
@@ -331,7 +403,7 @@ export declare class StablesApiClient {
|
|
|
331
403
|
listCustomers(): Promise<ListCustomersResponse>;
|
|
332
404
|
getCustomer(customerId: string): Promise<Customer>;
|
|
333
405
|
createCustomer(data: CreateCustomerRequest): Promise<Customer>;
|
|
334
|
-
updateCustomer(customerId: string, data:
|
|
406
|
+
updateCustomer(customerId: string, data: UpdateCustomerRequest): Promise<Customer>;
|
|
335
407
|
updateMetadata(customerId: string, metadata: Record<string, string>): Promise<void>;
|
|
336
408
|
generateVerificationLink(customerId: string, options?: GenerateVerificationLinkRequest): Promise<GenerateVerificationLinkResponse>;
|
|
337
409
|
listAllVirtualAccounts(params?: {
|
|
@@ -346,8 +418,6 @@ export declare class StablesApiClient {
|
|
|
346
418
|
updateVirtualAccount(customerId: string, virtualAccountId: string, data: {
|
|
347
419
|
deposit_handling_mode?: DepositHandlingMode;
|
|
348
420
|
}): Promise<VirtualAccount>;
|
|
349
|
-
deactivateVirtualAccount(customerId: string, virtualAccountId: string): Promise<VirtualAccount>;
|
|
350
|
-
reactivateVirtualAccount(customerId: string, virtualAccountId: string): Promise<VirtualAccount>;
|
|
351
421
|
getVirtualAccountHistory(customerId: string, virtualAccountId: string, params?: {
|
|
352
422
|
limit?: number;
|
|
353
423
|
depositId?: string;
|
|
@@ -367,9 +437,7 @@ export declare class StablesApiClient {
|
|
|
367
437
|
getTransfer(transferId: string): Promise<Transfer>;
|
|
368
438
|
createTransfer(data: CreateTransferRequest): Promise<Transfer>;
|
|
369
439
|
createQuote(data: CreateQuoteRequest): Promise<CreateQuoteResponse>;
|
|
370
|
-
getQuote(quoteId: string): Promise<
|
|
371
|
-
quote: Quote;
|
|
372
|
-
}>;
|
|
440
|
+
getQuote(quoteId: string): Promise<Quote>;
|
|
373
441
|
listApiKeys(params?: {
|
|
374
442
|
pageSize?: number;
|
|
375
443
|
pageToken?: string;
|
|
@@ -381,6 +449,12 @@ export declare class StablesApiClient {
|
|
|
381
449
|
apiKey: ApiKey;
|
|
382
450
|
}>;
|
|
383
451
|
revokeApiKey(apiKeyId: string): Promise<Record<string, never>>;
|
|
452
|
+
/**
|
|
453
|
+
* Check payout details against the per-currency rules without creating
|
|
454
|
+
* anything. Always answers 200 — the outcome is in the body — so an agent can
|
|
455
|
+
* discover what a corridor demands before spending a short-lived quote.
|
|
456
|
+
*/
|
|
457
|
+
validatePaymentMethod(network: string, destination: BankTransferDestination): Promise<ValidatePaymentMethodResponse>;
|
|
384
458
|
listWebhooks(): Promise<{
|
|
385
459
|
subscriptions: WebhookSubscription[];
|
|
386
460
|
}>;
|
|
@@ -389,5 +463,19 @@ export declare class StablesApiClient {
|
|
|
389
463
|
}>;
|
|
390
464
|
deleteWebhook(subscriptionId: string): Promise<Record<string, never>>;
|
|
391
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* Which environment a key belongs to, read from the key itself.
|
|
468
|
+
*
|
|
469
|
+
* Stables keys are `sti_<env>_<prefix>_<secret>` with env one of local, test or
|
|
470
|
+
* live, and the api service refuses a key whose segment does not match the
|
|
471
|
+
* deployment it arrives at. So the key already decides the environment, and a
|
|
472
|
+
* default base URL is only ever a guess at something we can read.
|
|
473
|
+
*
|
|
474
|
+
* Guessing had a cost in both directions: the code defaulted to production while
|
|
475
|
+
* the README promised sandbox, so a test key with no STABLES_API_URL set failed
|
|
476
|
+
* authentication against production and read as "my key is invalid" when the key
|
|
477
|
+
* was fine.
|
|
478
|
+
*/
|
|
479
|
+
export declare function apiUrlForKey(apiKey: string): string | null;
|
|
392
480
|
export declare function createStablesClient(): StablesApiClient;
|
|
393
481
|
//# sourceMappingURL=stables-client.d.ts.map
|