Skip to content

Commit 1e6b57c

Browse files
committed
feat: monthly details page, failure rates, pair data
1 parent 3f9d9a4 commit 1e6b57c

18 files changed

+2260
-777
lines changed

BOLTZ_API_DOCS.md

Lines changed: 160 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
1+
# Boltz API Reference (Source of Truth)
2+
3+
Do not maintain endpoint details manually in this repository.
4+
5+
Always use the live Boltz swagger spec:
6+
7+
- https://api.boltz.exchange/swagger-spec.json
8+
9+
If behavior in code and docs diverge, trust the swagger spec and update code
10+
accordingly.
11+
112
# Boltz API Documentation
213

314
## Partner Referral Stats Endpoint
415

516
### Endpoint
17+
618
```
719
GET https://api.boltz.exchange/v2/referral/stats
820
```
921

1022
### Authentication
1123

12-
The Boltz API uses **HMAC-SHA256** signature authentication. Each request must include three headers:
24+
The Boltz API uses **HMAC-SHA256** signature authentication. Each request must
25+
include three headers:
1326

1427
#### Required Headers
28+
1529
- `TS` - Unix timestamp (seconds since epoch)
1630
- `API-KEY` - Your Boltz partner API key
1731
- `API-HMAC` - HMAC signature of the request
@@ -20,44 +34,44 @@ The Boltz API uses **HMAC-SHA256** signature authentication. Each request must i
2034

2135
```javascript
2236
// 1. Create the message to sign
23-
const timestamp = Math.round(Date.now() / 1000)
24-
const method = 'GET'
25-
const path = '/v2/referral/stats'
26-
const message = `${timestamp}${method}${path}`
37+
const timestamp = Math.round(Date.now() / 1000);
38+
const method = "GET";
39+
const path = "/v2/referral/stats";
40+
const message = `${timestamp}${method}${path}`;
2741

2842
// 2. Generate HMAC-SHA256 signature using your API secret
2943
const hmac = crypto
30-
.createHmac('sha256', API_SECRET)
31-
.update(message)
32-
.digest('hex')
44+
.createHmac("sha256", API_SECRET)
45+
.update(message)
46+
.digest("hex");
3347

3448
// 3. Include in request headers
3549
const headers = {
36-
'TS': timestamp.toString(),
37-
'API-KEY': API_KEY,
38-
'API-HMAC': hmac
39-
}
50+
TS: timestamp.toString(),
51+
"API-KEY": API_KEY,
52+
"API-HMAC": hmac,
53+
};
4054
```
4155

4256
#### Example in Browser (Web Crypto API)
4357

4458
```typescript
4559
async function createHmac(message: string, secret: string): Promise<string> {
46-
const encoder = new TextEncoder();
47-
const keyData = encoder.encode(secret);
48-
const messageData = encoder.encode(message);
49-
50-
const cryptoKey = await crypto.subtle.importKey(
51-
'raw',
52-
keyData,
53-
{ name: 'HMAC', hash: 'SHA-256' },
54-
false,
55-
['sign']
56-
);
57-
58-
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
59-
const hashArray = Array.from(new Uint8Array(signature));
60-
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
60+
const encoder = new TextEncoder();
61+
const keyData = encoder.encode(secret);
62+
const messageData = encoder.encode(message);
63+
64+
const cryptoKey = await crypto.subtle.importKey(
65+
"raw",
66+
keyData,
67+
{ name: "HMAC", hash: "SHA-256" },
68+
false,
69+
["sign"],
70+
);
71+
72+
const signature = await crypto.subtle.sign("HMAC", cryptoKey, messageData);
73+
const hashArray = Array.from(new Uint8Array(signature));
74+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
6175
}
6276
```
6377

@@ -76,119 +90,124 @@ The API returns referral statistics organized by year and month:
7690

7791
```json
7892
{
79-
"2024": {
80-
"1": {
81-
"volume": {
82-
"total": "1.23456789",
83-
"BTC/BTC": "0.5",
84-
"L-BTC/BTC": "0.73456789"
85-
},
86-
"trades": {
87-
"total": 150,
88-
"BTC/BTC": 75,
89-
"L-BTC/BTC": 75
90-
},
91-
"groups": {
92-
"partner-id-1": {
93-
"volume": { "total": "0.5" },
94-
"trades": { "total": 50 }
93+
"2024": {
94+
"1": {
95+
"volume": {
96+
"total": "1.23456789",
97+
"BTC/BTC": "0.5",
98+
"L-BTC/BTC": "0.73456789"
99+
},
100+
"trades": {
101+
"total": 150,
102+
"BTC/BTC": 75,
103+
"L-BTC/BTC": 75
104+
},
105+
"groups": {
106+
"partner-id-1": {
107+
"volume": { "total": "0.5" },
108+
"trades": { "total": 50 }
109+
},
110+
"partner-id-2": {
111+
"volume": { "total": "0.73456789" },
112+
"trades": { "total": 100 }
113+
}
114+
}
95115
},
96-
"partner-id-2": {
97-
"volume": { "total": "0.73456789" },
98-
"trades": { "total": 100 }
116+
"2": {
117+
"volume": {
118+
"total": "2.5",
119+
"BTC/BTC": "1.2",
120+
"L-BTC/BTC": "1.3"
121+
},
122+
"trades": {
123+
"total": 200,
124+
"BTC/BTC": 100,
125+
"L-BTC/BTC": 100
126+
},
127+
"groups": {
128+
"partner-id-1": {
129+
"volume": { "total": "1.0" },
130+
"trades": { "total": 80 }
131+
},
132+
"partner-id-2": {
133+
"volume": { "total": "1.5" },
134+
"trades": { "total": 120 }
135+
}
136+
}
99137
}
100-
}
101138
},
102-
"2": {
103-
"volume": {
104-
"total": "2.5",
105-
"BTC/BTC": "1.2",
106-
"L-BTC/BTC": "1.3"
107-
},
108-
"trades": {
109-
"total": 200,
110-
"BTC/BTC": 100,
111-
"L-BTC/BTC": 100
112-
},
113-
"groups": {
114-
"partner-id-1": {
115-
"volume": { "total": "1.0" },
116-
"trades": { "total": 80 }
117-
},
118-
"partner-id-2": {
119-
"volume": { "total": "1.5" },
120-
"trades": { "total": 120 }
139+
"2025": {
140+
"1": {
141+
"volume": {
142+
"total": "3.14159265",
143+
"BTC/BTC": "1.5",
144+
"L-BTC/BTC": "1.64159265"
145+
},
146+
"trades": {
147+
"total": 250,
148+
"BTC/BTC": 120,
149+
"L-BTC/BTC": 130
150+
},
151+
"groups": {
152+
"partner-id-1": {
153+
"volume": { "total": "1.2" },
154+
"trades": { "total": 100 }
155+
},
156+
"partner-id-2": {
157+
"volume": { "total": "1.94159265" },
158+
"trades": { "total": 150 }
159+
}
160+
}
121161
}
122-
}
123162
}
124-
},
125-
"2025": {
126-
"1": {
127-
"volume": {
128-
"total": "3.14159265",
129-
"BTC/BTC": "1.5",
130-
"L-BTC/BTC": "1.64159265"
131-
},
132-
"trades": {
133-
"total": 250,
134-
"BTC/BTC": 120,
135-
"L-BTC/BTC": 130
136-
},
137-
"groups": {
138-
"partner-id-1": {
139-
"volume": { "total": "1.2" },
140-
"trades": { "total": 100 }
141-
},
142-
"partner-id-2": {
143-
"volume": { "total": "1.94159265" },
144-
"trades": { "total": 150 }
145-
}
146-
}
147-
}
148-
}
149163
}
150164
```
151165

152166
### Response Fields
153167

154-
| Field | Type | Description |
155-
|-------|------|-------------|
156-
| `{year}` | Object | Container for all months in that year |
157-
| `{year}.{month}` | Object | Stats for specific month (1-12) |
158-
| `{year}.{month}.volume` | Object | Volume breakdown by trading pair |
159-
| `{year}.{month}.volume.total` | String | Total volume in BTC for the month |
160-
| `{year}.{month}.trades` | Object | Trade count breakdown by trading pair |
161-
| `{year}.{month}.trades.total` | Number | Total number of swaps for the month |
162-
| `{year}.{month}.groups` | Object | Sub-partner breakdown (if applicable) |
163-
| `{year}.{month}.groups.{partnerId}` | Object | Stats for specific sub-partner |
168+
| Field | Type | Description |
169+
| ----------------------------------- | ------ | ------------------------------------- |
170+
| `{year}` | Object | Container for all months in that year |
171+
| `{year}.{month}` | Object | Stats for specific month (1-12) |
172+
| `{year}.{month}.volume` | Object | Volume breakdown by trading pair |
173+
| `{year}.{month}.volume.total` | String | Total volume in BTC for the month |
174+
| `{year}.{month}.trades` | Object | Trade count breakdown by trading pair |
175+
| `{year}.{month}.trades.total` | Number | Total number of swaps for the month |
176+
| `{year}.{month}.groups` | Object | Sub-partner breakdown (if applicable) |
177+
| `{year}.{month}.groups.{partnerId}` | Object | Stats for specific sub-partner |
164178

165179
### Notes
166180

167-
1. **Volume Format**: All volume values are in BTC as strings (to preserve precision)
168-
2. **Trading Pairs**: The response includes breakdowns by trading pair (e.g., "BTC/BTC", "L-BTC/BTC")
181+
1. **Volume Format**: All volume values are in BTC as strings (to preserve
182+
precision)
183+
2. **Trading Pairs**: The response includes breakdowns by trading pair (e.g.,
184+
"BTC/BTC", "L-BTC/BTC")
169185
3. **Groups**: Only present if you have sub-partners under your referral program
170186
4. **Month Numbers**: Months are numbered 1-12 (1 = January, 12 = December)
171187

172188
### Error Responses
173189

174190
#### 401 Unauthorized
191+
175192
```json
176193
{
177-
"error": "Invalid API key or signature"
194+
"error": "Invalid API key or signature"
178195
}
179196
```
180197

181198
#### 403 Forbidden
199+
182200
```json
183201
{
184-
"error": "Access denied"
202+
"error": "Access denied"
185203
}
186204
```
187205

188206
#### 429 Rate Limited
207+
189208
```json
190209
{
191-
"error": "Too many requests"
210+
"error": "Too many requests"
192211
}
193212
```
194213

@@ -201,52 +220,59 @@ The API returns referral statistics organized by year and month:
201220

202221
```typescript
203222
interface MonthlyStats {
204-
month: string;
205-
year: number;
206-
volumeBtc: number;
207-
tradeCount: number;
208-
avgTradeSize: number;
223+
month: string;
224+
year: number;
225+
volumeBtc: number;
226+
tradeCount: number;
227+
avgTradeSize: number;
209228
}
210229

211230
function processStatsData(data: any): MonthlyStats[] {
212-
const monthly: MonthlyStats[] = [];
213-
214-
Object.entries(data).forEach(([year, yearData]: [string, any]) => {
215-
Object.entries(yearData).forEach(([month, monthData]: [string, any]) => {
216-
const volumeBtc = parseFloat(monthData.volume?.total || '0');
217-
const tradeCount = monthData.trades?.total || 0;
218-
219-
monthly.push({
220-
month: getMonthName(parseInt(month)),
221-
year: parseInt(year),
222-
volumeBtc,
223-
tradeCount,
224-
avgTradeSize: tradeCount > 0
225-
? Math.round((volumeBtc * 100_000_000) / tradeCount)
226-
: 0,
227-
});
231+
const monthly: MonthlyStats[] = [];
232+
233+
Object.entries(data).forEach(([year, yearData]: [string, any]) => {
234+
Object.entries(yearData).forEach(
235+
([month, monthData]: [string, any]) => {
236+
const volumeBtc = parseFloat(monthData.volume?.total || "0");
237+
const tradeCount = monthData.trades?.total || 0;
238+
239+
monthly.push({
240+
month: getMonthName(parseInt(month)),
241+
year: parseInt(year),
242+
volumeBtc,
243+
tradeCount,
244+
avgTradeSize:
245+
tradeCount > 0
246+
? Math.round((volumeBtc * 100_000_000) / tradeCount)
247+
: 0,
248+
});
249+
},
250+
);
251+
});
252+
253+
return monthly.sort((a, b) => {
254+
if (a.year !== b.year) return a.year - b.year;
255+
return getMonthIndex(a.month) - getMonthIndex(b.month);
228256
});
229-
});
230-
231-
return monthly.sort((a, b) => {
232-
if (a.year !== b.year) return a.year - b.year;
233-
return getMonthIndex(a.month) - getMonthIndex(b.month);
234-
});
235257
}
236258
```
237259

238260
## Additional Endpoints
239261

240262
### Get Partner Fees
263+
241264
```
242265
GET https://api.boltz.exchange/v2/referral/fees
243266
```
267+
244268
Returns fee earnings breakdown by currency and time period.
245269

246270
### Get Referral Info
271+
247272
```
248273
GET https://api.boltz.exchange/v2/referral
249274
```
275+
250276
Returns general information about your referral program.
251277

252278
---

0 commit comments

Comments
 (0)