@quicknode/sdk 3.0.0-alpha.5 → 3.0.0-alpha.6

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 ADDED
@@ -0,0 +1,3534 @@
1
+ # Quicknode SDK
2
+
3
+ A unified SDK for building on QuickNode.
4
+
5
+ Rust SDK with Python, Node.js, and Ruby bindings.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Project Structure](#project-structure)
10
+ - [Installation](#installation)
11
+ - [Quick Start](#quick-start)
12
+ - [Configuration](#configuration)
13
+ - [Option A — Pass config directly](#option-a--pass-config-directly)
14
+ - [Option B — Load from environment (`from_env()`)](#option-b--load-from-environment-from_env)
15
+ - [API Reference](#api-reference)
16
+ - [Language conventions](#language-conventions)
17
+ - [Admin Client](#admin-client)
18
+ - [Endpoints](#endpoints)
19
+ - [Endpoint Tags](#endpoint-tags)
20
+ - [Teams](#teams)
21
+ - [Usage](#usage)
22
+ - [Logs](#logs)
23
+ - [Endpoint Security](#endpoint-security)
24
+ - [Security Options](#security-options)
25
+ - [Tokens](#tokens)
26
+ - [Referrers](#referrers)
27
+ - [IPs](#ips)
28
+ - [Domain Masks](#domain-masks)
29
+ - [JWTs](#jwts)
30
+ - [Request Filters](#request-filters)
31
+ - [Multichain](#multichain)
32
+ - [IP Custom Headers](#ip-custom-headers)
33
+ - [Method Rate Limits](#method-rate-limits)
34
+ - [Endpoint Rate Limits](#endpoint-rate-limits)
35
+ - [Metrics](#metrics)
36
+ - [Chains](#chains)
37
+ - [Billing](#billing)
38
+ - [Bulk Operations](#bulk-operations)
39
+ - [Account Tags](#account-tags)
40
+ - [Streams Client](#streams-client)
41
+ - [Datasets, Regions, and Destinations](#datasets-regions-and-destinations)
42
+ - [Streams methods](#streams-methods)
43
+ - [Webhooks Client](#webhooks-client)
44
+ - [Templates and destination](#templates-and-destination)
45
+ - [Webhooks methods](#webhooks-methods)
46
+ - [KV Store Client](#kv-store-client)
47
+ - [Sets](#sets)
48
+ - [Lists](#lists)
49
+ - [Error Handling](#error-handling)
50
+ - [Development](#development)
51
+ - [License](#license)
52
+
53
+ ## Project Structure
54
+
55
+ ```
56
+ sdk/
57
+ ├── crates/
58
+ │ ├── core/ # Pure Rust business logic
59
+ │ ├── python/ # PyO3 bindings
60
+ │ ├── node/ # napi-rs bindings
61
+ │ └── ruby/ # magnus bindings
62
+ ├── python/sdk/ # Python package with type hints
63
+ ├── npm/ # Node.js package with TypeScript types
64
+ ├── ruby/ # Ruby package
65
+ └── pyproject.toml # maturin build config
66
+ ```
67
+
68
+ ## Installation
69
+
70
+ **Python:** `uv add quicknode-sdk`
71
+
72
+ **Node.js:** `npm install quicknode-sdk`
73
+
74
+ **Ruby:** `gem install quicknode-sdk` _(not yet published — see Development below)_
75
+
76
+ ## Quick Start
77
+
78
+ Construct the SDK once, then reach into the four sub-clients (`admin`, `streams`, `webhooks`, `kvstore`). Subsequent API Reference snippets assume you have a `qn` handle from one of these blocks.
79
+
80
+ ```rust
81
+ // Rust
82
+ use quicknode_sdk::{QuickNodeSdk, SdkFullConfig};
83
+
84
+ #[tokio::main]
85
+ async fn main() -> Result<(), Box<dyn std::error::Error>> {
86
+ let qn = QuickNodeSdk::from_env()?;
87
+ let resp = qn.admin.get_endpoints(&Default::default()).await?;
88
+ println!("{} endpoints", resp.data.len());
89
+ Ok(())
90
+ }
91
+ ```
92
+
93
+ ```python
94
+ # Python
95
+ import asyncio
96
+ from sdk import QuickNodeSdk
97
+
98
+ async def main():
99
+ qn = QuickNodeSdk.from_env()
100
+ resp = await qn.admin.get_endpoints()
101
+ print(f"{len(resp.data)} endpoints")
102
+
103
+ asyncio.run(main())
104
+ ```
105
+
106
+ ```typescript
107
+ // Node.js
108
+ import { QuickNodeSdk } from "quicknode-sdk";
109
+
110
+ const qn = QuickNodeSdk.fromEnv();
111
+ const resp = await qn.admin.getEndpoints();
112
+ console.log(`${resp.data.length} endpoints`);
113
+ ```
114
+
115
+ ```ruby
116
+ # Ruby
117
+ require "json"
118
+ require "quicknode_sdk"
119
+
120
+ qn = QuickNodeSdk::SDK.from_env
121
+ resp = JSON.parse(qn.admin.get_endpoints({}))
122
+ puts "#{resp["data"].length} endpoints"
123
+ ```
124
+
125
+ ## Configuration
126
+
127
+ There are two ways to configure the SDK.
128
+
129
+ ### Option A — Pass config directly
130
+
131
+ ```python
132
+ # Python
133
+ from sdk import QuickNodeSdk, SdkFullConfig, HttpConfig
134
+ qn = QuickNodeSdk(SdkFullConfig(api_key="your-key", http=HttpConfig(timeout_secs=30)))
135
+ ```
136
+
137
+ ```typescript
138
+ // Node.js
139
+ import { QuickNodeSdk } from "quicknode-sdk";
140
+ const qn = new QuickNodeSdk({ apiKey: "your-key", http: { timeoutSecs: 30 } });
141
+ ```
142
+
143
+ ```rust
144
+ // Rust
145
+ let qn = QuickNodeSdk::new(&SdkFullConfig::builder().api_key("your-key").build())?;
146
+ ```
147
+
148
+ ### Option B — Load from environment (`from_env()`)
149
+
150
+ ```python
151
+ # Python
152
+ qn = QuickNodeSdk.from_env()
153
+ ```
154
+ ```typescript
155
+ // Node.js
156
+ const qn = QuickNodeSdk.fromEnv();
157
+ ```
158
+ ```ruby
159
+ # Ruby
160
+ qn = QuickNodeSdk::SDK.from_env
161
+ ```
162
+ ```rust
163
+ // Rust
164
+ let qn = QuickNodeSdk::from_env()?;
165
+ ```
166
+
167
+ Environment variables (prefix `QN_SDK__`, separator `__`):
168
+
169
+ | Variable | Required | Default | Description |
170
+ |----------|----------|---------|-------------|
171
+ | `QN_SDK__API_KEY` | yes | — | Your QuickNode API key |
172
+ | `QN_SDK__HTTP__TIMEOUT_SECS` | no | 30 | HTTP request timeout in seconds |
173
+ | `QN_SDK__HTTP__POOL_MAX_IDLE_PER_HOST` | no | — | Max idle HTTP connections per host |
174
+ | `QN_SDK__ADMIN__BASE_URL` | no | `https://api.quicknode.com/v0/` | Override admin API base URL (HTTPS, must end with `/`) |
175
+ | `QN_SDK__STREAMS__BASE_URL` | no | `https://api.quicknode.com/streams/rest/v1/` | Override streams base URL |
176
+ | `QN_SDK__WEBHOOKS__BASE_URL` | no | `https://api.quicknode.com/webhooks/rest/v1/` | Override webhooks base URL |
177
+ | `QN_SDK__KVSTORE__BASE_URL` | no | `https://api.quicknode.com/kv/rest/v1/` | Override KV store base URL |
178
+
179
+ ## API Reference
180
+
181
+ Each method below shows the call pattern in Rust, Python, Node.js, and Ruby in that order. Snippets assume `qn` was already constructed via the Quick Start. Optional parameters are skipped unless showing one is needed to illustrate usage.
182
+
183
+ ### Language conventions
184
+
185
+ - **Rust**: methods are `async` and return `Result<T, SdkError>`. Request structs use the [`bon`](https://docs.rs/bon) builder pattern via `::builder()`.
186
+ - **Python**: methods are `async` — call with `await`. Parameters are kwargs; responses are native `pyclass` objects with attribute access.
187
+ - **Node.js**: methods are `async` and take a single options object with camelCase keys.
188
+ - **Ruby**: methods are **blocking** (not async). Parameters are a single Hash with symbol keys. Responses that carry data are returned as **JSON strings** — wrap calls with `JSON.parse`. Unknown keys raise `ArgumentError`.
189
+
190
+ ---
191
+
192
+ ### Admin Client
193
+
194
+ Accessed as `qn.admin`. Manages endpoints, tags, teams, billing, usage, metrics, security, and rate limits. Backed by `https://api.quicknode.com/v0/`.
195
+
196
+ #### Endpoints
197
+
198
+ ##### `get_endpoints` / `getEndpoints`
199
+
200
+ Returns a paginated list of endpoints on the account with optional search, filters (networks, statuses, labels, tags, dedicated, flat-rate), sorting, and pagination.
201
+
202
+ **Parameters** (all optional): `limit` (i32), `offset` (i32), `search` (string), `sort_by` (string), `sort_direction` (`"asc"` | `"desc"`), `networks` (string[]), `statuses` (string[]), `labels` (string[]), `dedicated` (bool), `is_flat_rate` (bool), `tag_ids` (i32[]), `tag_labels` (string[]).
203
+
204
+ **Returns**: `GetEndpointsResponse` — `{ data: Endpoint[], pagination?: Pagination }`.
205
+
206
+ ```rust
207
+ // Rust
208
+ let params = GetEndpointsRequest::builder()
209
+ .limit(20)
210
+ .sort_by("created_at".to_string())
211
+ .sort_direction("desc".to_string())
212
+ .build();
213
+ let resp = qn.admin.get_endpoints(&params).await?;
214
+ ```
215
+
216
+ ```python
217
+ # Python
218
+ resp = await qn.admin.get_endpoints(limit=20, sort_by="created_at", sort_direction="desc")
219
+ ```
220
+
221
+ ```typescript
222
+ // Node.js
223
+ const resp = await qn.admin.getEndpoints({
224
+ limit: 20,
225
+ sortBy: "created_at",
226
+ sortDirection: "desc",
227
+ });
228
+ ```
229
+
230
+ ```ruby
231
+ # Ruby
232
+ resp = JSON.parse(qn.admin.get_endpoints(limit: 20, sort_by: "created_at", sort_direction: "desc"))
233
+ ```
234
+
235
+ ##### `create_endpoint` / `createEndpoint`
236
+
237
+ Creates a new endpoint for the given blockchain and network.
238
+
239
+ **Parameters**: `chain` (string, optional), `network` (string, optional).
240
+
241
+ **Returns**: `CreateEndpointResponse` with `data: SingleEndpoint`.
242
+
243
+ ```rust
244
+ // Rust
245
+ let params = CreateEndpointRequest::builder()
246
+ .chain("ethereum".to_string())
247
+ .network("mainnet".to_string())
248
+ .build();
249
+ let resp = qn.admin.create_endpoint(&params).await?;
250
+ ```
251
+
252
+ ```python
253
+ # Python
254
+ resp = await qn.admin.create_endpoint(chain="ethereum", network="mainnet")
255
+ ```
256
+
257
+ ```typescript
258
+ // Node.js
259
+ const resp = await qn.admin.createEndpoint({ chain: "ethereum", network: "mainnet" });
260
+ ```
261
+
262
+ ```ruby
263
+ # Ruby
264
+ resp = JSON.parse(qn.admin.create_endpoint(chain: "ethereum", network: "mainnet"))
265
+ ```
266
+
267
+ ##### `show_endpoint` / `showEndpoint`
268
+
269
+ Fetches a single endpoint by id, including its full security configuration and rate limits.
270
+
271
+ **Parameters**: `id` (string, required).
272
+
273
+ **Returns**: `ShowEndpointResponse` with `data: SingleEndpoint`.
274
+
275
+ ```rust
276
+ // Rust
277
+ let resp = qn.admin.show_endpoint("ep-123").await?;
278
+ ```
279
+
280
+ ```python
281
+ # Python
282
+ resp = await qn.admin.show_endpoint("ep-123")
283
+ ```
284
+
285
+ ```typescript
286
+ // Node.js
287
+ const resp = await qn.admin.showEndpoint("ep-123");
288
+ ```
289
+
290
+ ```ruby
291
+ # Ruby
292
+ resp = JSON.parse(qn.admin.show_endpoint(id: "ep-123"))
293
+ ```
294
+
295
+ ##### `update_endpoint` / `updateEndpoint`
296
+
297
+ Updates editable fields on an endpoint. Currently supports `label`.
298
+
299
+ **Parameters**: `id` (string, required); body: `label` (string, optional).
300
+
301
+ **Returns**: nothing.
302
+
303
+ ```rust
304
+ // Rust
305
+ let params = UpdateEndpointRequest::builder().label("my label".to_string()).build();
306
+ qn.admin.update_endpoint("ep-123", &params).await?;
307
+ ```
308
+
309
+ ```python
310
+ # Python
311
+ await qn.admin.update_endpoint("ep-123", label="my label")
312
+ ```
313
+
314
+ ```typescript
315
+ // Node.js
316
+ await qn.admin.updateEndpoint("ep-123", { label: "my label" });
317
+ ```
318
+
319
+ ```ruby
320
+ # Ruby
321
+ qn.admin.update_endpoint(id: "ep-123", label: "my label")
322
+ ```
323
+
324
+ ##### `archive_endpoint` / `archiveEndpoint`
325
+
326
+ Archives an endpoint. The HTTP verb is `DELETE` but the effect is archival, not permanent deletion.
327
+
328
+ **Parameters**: `id` (string, required).
329
+
330
+ **Returns**: nothing.
331
+
332
+ ```rust
333
+ // Rust
334
+ qn.admin.archive_endpoint("ep-123").await?;
335
+ ```
336
+
337
+ ```python
338
+ # Python
339
+ await qn.admin.archive_endpoint("ep-123")
340
+ ```
341
+
342
+ ```typescript
343
+ // Node.js
344
+ await qn.admin.archiveEndpoint("ep-123");
345
+ ```
346
+
347
+ ```ruby
348
+ # Ruby
349
+ qn.admin.archive_endpoint(id: "ep-123")
350
+ ```
351
+
352
+ ##### `update_endpoint_status` / `updateEndpointStatus`
353
+
354
+ Pauses or unpauses an endpoint.
355
+
356
+ **Parameters**: `id` (string, required); body: `status` (string, required — `"active"` or `"paused"`).
357
+
358
+ **Returns**: `UpdateEndpointStatusResponse`.
359
+
360
+ ```rust
361
+ // Rust
362
+ let params = UpdateEndpointStatusRequest::builder().status("paused".to_string()).build();
363
+ qn.admin.update_endpoint_status("ep-123", &params).await?;
364
+ ```
365
+
366
+ ```python
367
+ # Python
368
+ await qn.admin.update_endpoint_status("ep-123", status="paused")
369
+ ```
370
+
371
+ ```typescript
372
+ // Node.js
373
+ await qn.admin.updateEndpointStatus("ep-123", { status: "paused" });
374
+ ```
375
+
376
+ ```ruby
377
+ # Ruby
378
+ JSON.parse(qn.admin.update_endpoint_status(id: "ep-123", status: "paused"))
379
+ ```
380
+
381
+ #### Endpoint Tags
382
+
383
+ Per-endpoint tag add/remove. For account-wide tag management see [Account Tags](#account-tags).
384
+
385
+ ##### `create_tag` / `createTag`
386
+
387
+ Tags an endpoint with the given label. Creates the tag on the account if it does not exist.
388
+
389
+ **Parameters**: `id` (string, required); body: `label` (string, optional).
390
+
391
+ **Returns**: nothing.
392
+
393
+ ```rust
394
+ // Rust
395
+ let params = CreateTagRequest::builder().label("prod".to_string()).build();
396
+ qn.admin.create_tag("ep-123", &params).await?;
397
+ ```
398
+
399
+ ```python
400
+ # Python
401
+ await qn.admin.create_tag("ep-123", label="prod")
402
+ ```
403
+
404
+ ```typescript
405
+ // Node.js
406
+ await qn.admin.createTag("ep-123", { label: "prod" });
407
+ ```
408
+
409
+ ```ruby
410
+ # Ruby
411
+ qn.admin.create_tag(id: "ep-123", label: "prod")
412
+ ```
413
+
414
+ ##### `delete_tag` / `deleteTag`
415
+
416
+ Removes a tag from a specific endpoint.
417
+
418
+ **Parameters**: `id` (endpoint id, string, required), `tag_id` (string, required).
419
+
420
+ **Returns**: nothing.
421
+
422
+ ```rust
423
+ // Rust
424
+ qn.admin.delete_tag("ep-123", "42").await?;
425
+ ```
426
+
427
+ ```python
428
+ # Python
429
+ await qn.admin.delete_tag("ep-123", "42")
430
+ ```
431
+
432
+ ```typescript
433
+ // Node.js
434
+ await qn.admin.deleteTag("ep-123", "42");
435
+ ```
436
+
437
+ ```ruby
438
+ # Ruby
439
+ qn.admin.delete_tag(id: "ep-123", tag_id: "42")
440
+ ```
441
+
442
+ #### Teams
443
+
444
+ ##### `list_teams` / `listTeams`
445
+
446
+ Lists all teams on the account.
447
+
448
+ **Parameters**: none.
449
+
450
+ **Returns**: `ListTeamsResponse` with `data: TeamSummary[]`.
451
+
452
+ ```rust
453
+ // Rust
454
+ let resp = qn.admin.list_teams().await?;
455
+ ```
456
+
457
+ ```python
458
+ # Python
459
+ resp = await qn.admin.list_teams()
460
+ ```
461
+
462
+ ```typescript
463
+ // Node.js
464
+ const resp = await qn.admin.listTeams();
465
+ ```
466
+
467
+ ```ruby
468
+ # Ruby
469
+ resp = JSON.parse(qn.admin.list_teams)
470
+ ```
471
+
472
+ ##### `create_team` / `createTeam`
473
+
474
+ Creates a new team.
475
+
476
+ **Parameters**: `name` (string, required).
477
+
478
+ **Returns**: `CreateTeamResponse` with `data: CreateTeamData`.
479
+
480
+ ```rust
481
+ // Rust
482
+ let params = CreateTeamRequest::builder().name("Payments".to_string()).build();
483
+ let resp = qn.admin.create_team(&params).await?;
484
+ ```
485
+
486
+ ```python
487
+ # Python
488
+ resp = await qn.admin.create_team(name="Payments")
489
+ ```
490
+
491
+ ```typescript
492
+ // Node.js
493
+ const resp = await qn.admin.createTeam({ name: "Payments" });
494
+ ```
495
+
496
+ ```ruby
497
+ # Ruby
498
+ resp = JSON.parse(qn.admin.create_team(name: "Payments"))
499
+ ```
500
+
501
+ ##### `get_team` / `getTeam`
502
+
503
+ Fetches team detail including pending invites.
504
+
505
+ **Parameters**: `id` (i64, required).
506
+
507
+ **Returns**: `GetTeamResponse` with `data: TeamDetail`.
508
+
509
+ ```rust
510
+ // Rust
511
+ let resp = qn.admin.get_team(42).await?;
512
+ ```
513
+
514
+ ```python
515
+ # Python
516
+ resp = await qn.admin.get_team(42)
517
+ ```
518
+
519
+ ```typescript
520
+ // Node.js
521
+ const resp = await qn.admin.getTeam(42);
522
+ ```
523
+
524
+ ```ruby
525
+ # Ruby
526
+ resp = JSON.parse(qn.admin.get_team(id: 42))
527
+ ```
528
+
529
+ ##### `delete_team` / `deleteTeam`
530
+
531
+ Deletes a team.
532
+
533
+ **Parameters**: `id` (i64, required).
534
+
535
+ **Returns**: `DeleteTeamResponse`.
536
+
537
+ ```rust
538
+ // Rust
539
+ qn.admin.delete_team(42).await?;
540
+ ```
541
+
542
+ ```python
543
+ # Python
544
+ await qn.admin.delete_team(42)
545
+ ```
546
+
547
+ ```typescript
548
+ // Node.js
549
+ await qn.admin.deleteTeam(42);
550
+ ```
551
+
552
+ ```ruby
553
+ # Ruby
554
+ qn.admin.delete_team(id: 42)
555
+ ```
556
+
557
+ ##### `list_team_endpoints` / `listTeamEndpoints`
558
+
559
+ Lists endpoints accessible to a team.
560
+
561
+ **Parameters**: `id` (i64, required).
562
+
563
+ **Returns**: `ListTeamEndpointsResponse` with `data: TeamEndpoint[]`.
564
+
565
+ ```rust
566
+ // Rust
567
+ let resp = qn.admin.list_team_endpoints(42).await?;
568
+ ```
569
+
570
+ ```python
571
+ # Python
572
+ resp = await qn.admin.list_team_endpoints(42)
573
+ ```
574
+
575
+ ```typescript
576
+ // Node.js
577
+ const resp = await qn.admin.listTeamEndpoints(42);
578
+ ```
579
+
580
+ ```ruby
581
+ # Ruby
582
+ resp = JSON.parse(qn.admin.list_team_endpoints(id: 42))
583
+ ```
584
+
585
+ ##### `update_team_endpoints` / `updateTeamEndpoints`
586
+
587
+ Replaces the set of endpoints associated with a team. Pass an empty array to remove all.
588
+
589
+ **Parameters**: `id` (i64, required); body: `endpoint_ids` (string[], required).
590
+
591
+ **Returns**: `UpdateTeamEndpointsResponse`.
592
+
593
+ ```rust
594
+ // Rust
595
+ let params = UpdateTeamEndpointsRequest::builder()
596
+ .endpoint_ids(vec!["ep-123".to_string(), "ep-456".to_string()])
597
+ .build();
598
+ qn.admin.update_team_endpoints(42, &params).await?;
599
+ ```
600
+
601
+ ```python
602
+ # Python
603
+ await qn.admin.update_team_endpoints(42, endpoint_ids=["ep-123", "ep-456"])
604
+ ```
605
+
606
+ ```typescript
607
+ // Node.js
608
+ await qn.admin.updateTeamEndpoints(42, { endpointIds: ["ep-123", "ep-456"] });
609
+ ```
610
+
611
+ ```ruby
612
+ # Ruby
613
+ qn.admin.update_team_endpoints(id: 42, endpoint_ids: ["ep-123", "ep-456"])
614
+ ```
615
+
616
+ ##### `invite_team_member` / `inviteTeamMember`
617
+
618
+ Invites a user to a team. Existing users only need `email`; new users require `full_name` and `role`.
619
+
620
+ **Parameters**: `id` (i64, required); body: `email` (string, required), `full_name` (string, optional), `role` (string, optional — `admin` | `viewer` | `billing`).
621
+
622
+ **Returns**: `InviteTeamMemberResponse`.
623
+
624
+ ```rust
625
+ // Rust
626
+ let params = InviteTeamMemberRequest::builder()
627
+ .email("alice@example.com".to_string())
628
+ .role("viewer".to_string())
629
+ .build();
630
+ qn.admin.invite_team_member(42, &params).await?;
631
+ ```
632
+
633
+ ```python
634
+ # Python
635
+ await qn.admin.invite_team_member(42, email="alice@example.com", role="viewer")
636
+ ```
637
+
638
+ ```typescript
639
+ // Node.js
640
+ await qn.admin.inviteTeamMember(42, { email: "alice@example.com", role: "viewer" });
641
+ ```
642
+
643
+ ```ruby
644
+ # Ruby
645
+ qn.admin.invite_team_member(id: 42, email: "alice@example.com", role: "viewer")
646
+ ```
647
+
648
+ ##### `remove_team_member` / `removeTeamMember`
649
+
650
+ Removes a user from a team.
651
+
652
+ **Parameters**: `id` (team id, i64, required), `user_id` (i64, required).
653
+
654
+ **Returns**: `RemoveTeamMemberResponse`.
655
+
656
+ ```rust
657
+ // Rust
658
+ qn.admin.remove_team_member(42, 7).await?;
659
+ ```
660
+
661
+ ```python
662
+ # Python
663
+ await qn.admin.remove_team_member(42, 7)
664
+ ```
665
+
666
+ ```typescript
667
+ // Node.js
668
+ await qn.admin.removeTeamMember(42, 7);
669
+ ```
670
+
671
+ ```ruby
672
+ # Ruby
673
+ qn.admin.remove_team_member(id: 42, user_id: 7)
674
+ ```
675
+
676
+ ##### `resend_team_invite` / `resendTeamInvite`
677
+
678
+ Re-sends a pending team invitation.
679
+
680
+ **Parameters**: `id` (team id, i64, required), `user_id` (i64, required).
681
+
682
+ **Returns**: `ResendTeamInviteResponse`.
683
+
684
+ ```rust
685
+ // Rust
686
+ qn.admin.resend_team_invite(42, 7).await?;
687
+ ```
688
+
689
+ ```python
690
+ # Python
691
+ await qn.admin.resend_team_invite(42, 7)
692
+ ```
693
+
694
+ ```typescript
695
+ // Node.js
696
+ await qn.admin.resendTeamInvite(42, 7);
697
+ ```
698
+
699
+ ```ruby
700
+ # Ruby
701
+ qn.admin.resend_team_invite(id: 42, user_id: 7)
702
+ ```
703
+
704
+ #### Usage
705
+
706
+ All usage methods accept optional `start_time` and `end_time` Unix timestamps. Omit both for account-to-date totals.
707
+
708
+ ##### `get_usage` / `getUsage`
709
+
710
+ Aggregate account usage for a time window.
711
+
712
+ **Returns**: `GetUsageResponse` with `data: UsageData` (`credits_used`, `credits_remaining`, `limit`, `overages`, `start_time`, `end_time`).
713
+
714
+ ```rust
715
+ // Rust
716
+ let resp = qn.admin.get_usage(&GetUsageRequest::default()).await?;
717
+ ```
718
+
719
+ ```python
720
+ # Python
721
+ resp = await qn.admin.get_usage()
722
+ ```
723
+
724
+ ```typescript
725
+ // Node.js
726
+ const resp = await qn.admin.getUsage();
727
+ ```
728
+
729
+ ```ruby
730
+ # Ruby
731
+ resp = JSON.parse(qn.admin.get_usage({}))
732
+ ```
733
+
734
+ ##### `get_usage_by_endpoint` / `getUsageByEndpoint`
735
+
736
+ Per-endpoint usage breakdown.
737
+
738
+ **Returns**: `GetUsageByEndpointResponse` with `data.endpoints: EndpointUsage[]`.
739
+
740
+ ```rust
741
+ // Rust
742
+ let resp = qn.admin.get_usage_by_endpoint(&GetUsageRequest::default()).await?;
743
+ ```
744
+
745
+ ```python
746
+ # Python
747
+ resp = await qn.admin.get_usage_by_endpoint()
748
+ ```
749
+
750
+ ```typescript
751
+ // Node.js
752
+ const resp = await qn.admin.getUsageByEndpoint();
753
+ ```
754
+
755
+ ```ruby
756
+ # Ruby
757
+ resp = JSON.parse(qn.admin.get_usage_by_endpoint({}))
758
+ ```
759
+
760
+ ##### `get_usage_by_method` / `getUsageByMethod`
761
+
762
+ Per-RPC-method usage breakdown.
763
+
764
+ **Returns**: `GetUsageByMethodResponse` with `data.methods: MethodUsage[]`.
765
+
766
+ ```rust
767
+ // Rust
768
+ let resp = qn.admin.get_usage_by_method(&GetUsageRequest::default()).await?;
769
+ ```
770
+
771
+ ```python
772
+ # Python
773
+ resp = await qn.admin.get_usage_by_method()
774
+ ```
775
+
776
+ ```typescript
777
+ // Node.js
778
+ const resp = await qn.admin.getUsageByMethod();
779
+ ```
780
+
781
+ ```ruby
782
+ # Ruby
783
+ resp = JSON.parse(qn.admin.get_usage_by_method({}))
784
+ ```
785
+
786
+ ##### `get_usage_by_chain` / `getUsageByChain`
787
+
788
+ Per-chain usage breakdown.
789
+
790
+ **Returns**: `GetUsageByChainResponse` with `data.chains: ChainUsage[]`.
791
+
792
+ ```rust
793
+ // Rust
794
+ let resp = qn.admin.get_usage_by_chain(&GetUsageRequest::default()).await?;
795
+ ```
796
+
797
+ ```python
798
+ # Python
799
+ resp = await qn.admin.get_usage_by_chain()
800
+ ```
801
+
802
+ ```typescript
803
+ // Node.js
804
+ const resp = await qn.admin.getUsageByChain();
805
+ ```
806
+
807
+ ```ruby
808
+ # Ruby
809
+ resp = JSON.parse(qn.admin.get_usage_by_chain({}))
810
+ ```
811
+
812
+ ##### `get_usage_by_tag` / `getUsageByTag`
813
+
814
+ Per-tag usage breakdown.
815
+
816
+ **Returns**: `GetUsageByTagResponse` with `data.tags: TagUsage[]`.
817
+
818
+ ```rust
819
+ // Rust
820
+ let resp = qn.admin.get_usage_by_tag(&GetUsageRequest::default()).await?;
821
+ ```
822
+
823
+ ```python
824
+ # Python
825
+ resp = await qn.admin.get_usage_by_tag()
826
+ ```
827
+
828
+ ```typescript
829
+ // Node.js
830
+ const resp = await qn.admin.getUsageByTag();
831
+ ```
832
+
833
+ ```ruby
834
+ # Ruby
835
+ resp = JSON.parse(qn.admin.get_usage_by_tag({}))
836
+ ```
837
+
838
+ #### Logs
839
+
840
+ ##### `get_endpoint_logs` / `getEndpointLogs`
841
+
842
+ Fetches a page of request logs for an endpoint. Set `include_details=true` for full request/response payloads (truncated at 2 KB each).
843
+
844
+ **Parameters**: `id` (endpoint id, required); body: `from` (string timestamp, required), `to` (string timestamp, required), `include_details` (bool, optional), `limit` (i32, optional), `next_at` (string cursor, optional).
845
+
846
+ **Returns**: `GetEndpointLogsResponse` — `{ data: EndpointLog[], next_at?: string }`.
847
+
848
+ ```rust
849
+ // Rust
850
+ let params = GetEndpointLogsRequest::builder()
851
+ .from("2026-04-01T00:00:00Z".to_string())
852
+ .to("2026-04-02T00:00:00Z".to_string())
853
+ .limit(100)
854
+ .build();
855
+ let resp = qn.admin.get_endpoint_logs("ep-123", &params).await?;
856
+ ```
857
+
858
+ ```python
859
+ # Python
860
+ resp = await qn.admin.get_endpoint_logs(
861
+ "ep-123",
862
+ from_time="2026-04-01T00:00:00Z",
863
+ to_time="2026-04-02T00:00:00Z",
864
+ limit=100,
865
+ )
866
+ ```
867
+
868
+ ```typescript
869
+ // Node.js
870
+ const resp = await qn.admin.getEndpointLogs("ep-123", {
871
+ from: "2026-04-01T00:00:00Z",
872
+ to: "2026-04-02T00:00:00Z",
873
+ limit: 100,
874
+ });
875
+ ```
876
+
877
+ ```ruby
878
+ # Ruby
879
+ resp = JSON.parse(qn.admin.get_endpoint_logs(
880
+ id: "ep-123",
881
+ from_time: "2026-04-01T00:00:00Z",
882
+ to_time: "2026-04-02T00:00:00Z",
883
+ limit: 100
884
+ ))
885
+ ```
886
+
887
+ ##### `get_log_details` / `getLogDetails`
888
+
889
+ Returns the full request/response payloads for a single log entry.
890
+
891
+ **Parameters**: `id` (endpoint id, required), `request_id` (log request uuid, required).
892
+
893
+ **Returns**: `GetLogDetailsResponse` with `data: LogDetails`.
894
+
895
+ ```rust
896
+ // Rust
897
+ let resp = qn.admin.get_log_details("ep-123", "req-abc").await?;
898
+ ```
899
+
900
+ ```python
901
+ # Python
902
+ resp = await qn.admin.get_log_details("ep-123", "req-abc")
903
+ ```
904
+
905
+ ```typescript
906
+ // Node.js
907
+ const resp = await qn.admin.getLogDetails("ep-123", "req-abc");
908
+ ```
909
+
910
+ ```ruby
911
+ # Ruby
912
+ resp = JSON.parse(qn.admin.get_log_details(id: "ep-123", request_id: "req-abc"))
913
+ ```
914
+
915
+ #### Endpoint Security
916
+
917
+ ##### `get_endpoint_security` / `getEndpointSecurity`
918
+
919
+ Returns the full security configuration for an endpoint: tokens, JWTs, referrers, domain masks, IPs, request filters, and their per-feature toggles.
920
+
921
+ **Parameters**: `id` (string, required).
922
+
923
+ **Returns**: `GetEndpointSecurityResponse` with `data: EndpointSecurity`.
924
+
925
+ ```rust
926
+ // Rust
927
+ let resp = qn.admin.get_endpoint_security("ep-123").await?;
928
+ ```
929
+
930
+ ```python
931
+ # Python
932
+ resp = await qn.admin.get_endpoint_security("ep-123")
933
+ ```
934
+
935
+ ```typescript
936
+ // Node.js
937
+ const resp = await qn.admin.getEndpointSecurity("ep-123");
938
+ ```
939
+
940
+ ```ruby
941
+ # Ruby
942
+ resp = JSON.parse(qn.admin.get_endpoint_security(id: "ep-123"))
943
+ ```
944
+
945
+ #### Security Options
946
+
947
+ ##### `get_security_options` / `getSecurityOptions`
948
+
949
+ Returns the list of security features and their enabled state for an endpoint.
950
+
951
+ **Parameters**: `id` (string, required).
952
+
953
+ **Returns**: `GetSecurityOptionsResponse` with `data: SecurityOption[]`.
954
+
955
+ ```rust
956
+ // Rust
957
+ let resp = qn.admin.get_security_options("ep-123").await?;
958
+ ```
959
+
960
+ ```python
961
+ # Python
962
+ resp = await qn.admin.get_security_options("ep-123")
963
+ ```
964
+
965
+ ```typescript
966
+ // Node.js
967
+ const resp = await qn.admin.getSecurityOptions("ep-123");
968
+ ```
969
+
970
+ ```ruby
971
+ # Ruby
972
+ resp = JSON.parse(qn.admin.get_security_options(id: "ep-123"))
973
+ ```
974
+
975
+ ##### `update_security_options` / `updateSecurityOptions`
976
+
977
+ Enables or disables individual security features. Each field accepts `"enabled"` or `"disabled"`.
978
+
979
+ **Parameters**: `id` (string, required); `options`: `SecurityOptionsUpdate` (`tokens`, `referrers`, `jwts`, `ips`, `domain_masks`, `hsts`, `cors`, `request_filters`, `ip_custom_header`).
980
+
981
+ **Returns**: `UpdateSecurityOptionsResponse` with updated `SecurityOption[]`.
982
+
983
+ ```rust
984
+ // Rust
985
+ let options = SecurityOptionsUpdate::builder()
986
+ .tokens("enabled".to_string())
987
+ .jwts("disabled".to_string())
988
+ .build();
989
+ let params = UpdateSecurityOptionsRequest { options };
990
+ qn.admin.update_security_options("ep-123", &params).await?;
991
+ ```
992
+
993
+ ```python
994
+ # Python
995
+ await qn.admin.update_security_options(
996
+ "ep-123",
997
+ tokens="enabled",
998
+ jwts="disabled",
999
+ )
1000
+ ```
1001
+
1002
+ ```typescript
1003
+ // Node.js
1004
+ await qn.admin.updateSecurityOptions("ep-123", {
1005
+ options: { tokens: "enabled", jwts: "disabled" },
1006
+ });
1007
+ ```
1008
+
1009
+ ```ruby
1010
+ # Ruby
1011
+ qn.admin.update_security_options(id: "ep-123", tokens: "enabled", jwts: "disabled")
1012
+ ```
1013
+
1014
+ #### Tokens
1015
+
1016
+ ##### `create_token` / `createToken`
1017
+
1018
+ Generates a new auth token on an endpoint.
1019
+
1020
+ **Parameters**: `id` (endpoint id, required).
1021
+
1022
+ **Returns**: nothing.
1023
+
1024
+ ```rust
1025
+ // Rust
1026
+ qn.admin.create_token("ep-123").await?;
1027
+ ```
1028
+
1029
+ ```python
1030
+ # Python
1031
+ await qn.admin.create_token("ep-123")
1032
+ ```
1033
+
1034
+ ```typescript
1035
+ // Node.js
1036
+ await qn.admin.createToken("ep-123");
1037
+ ```
1038
+
1039
+ ```ruby
1040
+ # Ruby
1041
+ qn.admin.create_token(id: "ep-123")
1042
+ ```
1043
+
1044
+ ##### `delete_token` / `deleteToken`
1045
+
1046
+ Revokes a token on an endpoint.
1047
+
1048
+ **Parameters**: `id` (endpoint id, required), `token_id` (string, required).
1049
+
1050
+ **Returns**: nothing.
1051
+
1052
+ ```rust
1053
+ // Rust
1054
+ qn.admin.delete_token("ep-123", "tok-1").await?;
1055
+ ```
1056
+
1057
+ ```python
1058
+ # Python
1059
+ await qn.admin.delete_token("ep-123", "tok-1")
1060
+ ```
1061
+
1062
+ ```typescript
1063
+ // Node.js
1064
+ await qn.admin.deleteToken("ep-123", "tok-1");
1065
+ ```
1066
+
1067
+ ```ruby
1068
+ # Ruby
1069
+ qn.admin.delete_token(id: "ep-123", token_id: "tok-1")
1070
+ ```
1071
+
1072
+ #### Referrers
1073
+
1074
+ ##### `create_referrer` / `createReferrer`
1075
+
1076
+ Whitelists a referrer URL or domain on an endpoint.
1077
+
1078
+ **Parameters**: `id` (endpoint id, required); body: `referrer` (string, optional).
1079
+
1080
+ **Returns**: nothing.
1081
+
1082
+ ```rust
1083
+ // Rust
1084
+ let params = CreateReferrerRequest::builder().referrer("example.com".to_string()).build();
1085
+ qn.admin.create_referrer("ep-123", &params).await?;
1086
+ ```
1087
+
1088
+ ```python
1089
+ # Python
1090
+ await qn.admin.create_referrer("ep-123", referrer="example.com")
1091
+ ```
1092
+
1093
+ ```typescript
1094
+ // Node.js
1095
+ await qn.admin.createReferrer("ep-123", { referrer: "example.com" });
1096
+ ```
1097
+
1098
+ ```ruby
1099
+ # Ruby
1100
+ qn.admin.create_referrer(id: "ep-123", referrer: "example.com")
1101
+ ```
1102
+
1103
+ ##### `delete_referrer` / `deleteReferrer`
1104
+
1105
+ Removes a referrer from the whitelist.
1106
+
1107
+ **Parameters**: `id` (endpoint id, required), `referrer_id` (string, required).
1108
+
1109
+ **Returns**: nothing.
1110
+
1111
+ ```rust
1112
+ // Rust
1113
+ qn.admin.delete_referrer("ep-123", "ref-1").await?;
1114
+ ```
1115
+
1116
+ ```python
1117
+ # Python
1118
+ await qn.admin.delete_referrer("ep-123", "ref-1")
1119
+ ```
1120
+
1121
+ ```typescript
1122
+ // Node.js
1123
+ await qn.admin.deleteReferrer("ep-123", "ref-1");
1124
+ ```
1125
+
1126
+ ```ruby
1127
+ # Ruby
1128
+ qn.admin.delete_referrer(id: "ep-123", referrer_id: "ref-1")
1129
+ ```
1130
+
1131
+ #### IPs
1132
+
1133
+ ##### `create_ip` / `createIp`
1134
+
1135
+ Whitelists an IP address on an endpoint.
1136
+
1137
+ **Parameters**: `id` (endpoint id, required); body: `ip` (string, optional).
1138
+
1139
+ **Returns**: nothing.
1140
+
1141
+ ```rust
1142
+ // Rust
1143
+ let params = CreateIpRequest::builder().ip("198.51.100.7".to_string()).build();
1144
+ qn.admin.create_ip("ep-123", &params).await?;
1145
+ ```
1146
+
1147
+ ```python
1148
+ # Python
1149
+ await qn.admin.create_ip("ep-123", ip="198.51.100.7")
1150
+ ```
1151
+
1152
+ ```typescript
1153
+ // Node.js
1154
+ await qn.admin.createIp("ep-123", { ip: "198.51.100.7" });
1155
+ ```
1156
+
1157
+ ```ruby
1158
+ # Ruby
1159
+ qn.admin.create_ip(id: "ep-123", ip: "198.51.100.7")
1160
+ ```
1161
+
1162
+ ##### `delete_ip` / `deleteIp`
1163
+
1164
+ Removes an IP from the whitelist.
1165
+
1166
+ **Parameters**: `id` (endpoint id, required), `ip_id` (string, required).
1167
+
1168
+ **Returns**: `DeleteBoolResponse`.
1169
+
1170
+ ```rust
1171
+ // Rust
1172
+ qn.admin.delete_ip("ep-123", "ip-1").await?;
1173
+ ```
1174
+
1175
+ ```python
1176
+ # Python
1177
+ await qn.admin.delete_ip("ep-123", "ip-1")
1178
+ ```
1179
+
1180
+ ```typescript
1181
+ // Node.js
1182
+ await qn.admin.deleteIp("ep-123", "ip-1");
1183
+ ```
1184
+
1185
+ ```ruby
1186
+ # Ruby
1187
+ resp = JSON.parse(qn.admin.delete_ip(id: "ep-123", ip_id: "ip-1"))
1188
+ ```
1189
+
1190
+ #### Domain Masks
1191
+
1192
+ ##### `create_domain_mask` / `createDomainMask`
1193
+
1194
+ Adds a custom domain mask to an endpoint.
1195
+
1196
+ **Parameters**: `id` (endpoint id, required); body: `domain_mask` (string, optional).
1197
+
1198
+ **Returns**: nothing.
1199
+
1200
+ ```rust
1201
+ // Rust
1202
+ let params = CreateDomainMaskRequest::builder()
1203
+ .domain_mask("rpc.example.com".to_string())
1204
+ .build();
1205
+ qn.admin.create_domain_mask("ep-123", &params).await?;
1206
+ ```
1207
+
1208
+ ```python
1209
+ # Python
1210
+ await qn.admin.create_domain_mask("ep-123", domain_mask="rpc.example.com")
1211
+ ```
1212
+
1213
+ ```typescript
1214
+ // Node.js
1215
+ await qn.admin.createDomainMask("ep-123", { domainMask: "rpc.example.com" });
1216
+ ```
1217
+
1218
+ ```ruby
1219
+ # Ruby
1220
+ qn.admin.create_domain_mask(id: "ep-123", domain_mask: "rpc.example.com")
1221
+ ```
1222
+
1223
+ ##### `delete_domain_mask` / `deleteDomainMask`
1224
+
1225
+ Removes a domain mask.
1226
+
1227
+ **Parameters**: `id` (endpoint id, required), `domain_mask_id` (string, required).
1228
+
1229
+ **Returns**: nothing.
1230
+
1231
+ ```rust
1232
+ // Rust
1233
+ qn.admin.delete_domain_mask("ep-123", "dm-1").await?;
1234
+ ```
1235
+
1236
+ ```python
1237
+ # Python
1238
+ await qn.admin.delete_domain_mask("ep-123", "dm-1")
1239
+ ```
1240
+
1241
+ ```typescript
1242
+ // Node.js
1243
+ await qn.admin.deleteDomainMask("ep-123", "dm-1");
1244
+ ```
1245
+
1246
+ ```ruby
1247
+ # Ruby
1248
+ qn.admin.delete_domain_mask(id: "ep-123", domain_mask_id: "dm-1")
1249
+ ```
1250
+
1251
+ #### JWTs
1252
+
1253
+ ##### `create_jwt` / `createJwt`
1254
+
1255
+ Configures JWT validation on an endpoint.
1256
+
1257
+ **Parameters**: `id` (endpoint id, required); body: `public_key` (string, optional), `kid` (string, optional), `name` (string, optional).
1258
+
1259
+ **Returns**: nothing.
1260
+
1261
+ ```rust
1262
+ // Rust
1263
+ let params = CreateJwtRequest::builder()
1264
+ .public_key("-----BEGIN PUBLIC KEY-----\n...".to_string())
1265
+ .kid("key-1".to_string())
1266
+ .name("primary".to_string())
1267
+ .build();
1268
+ qn.admin.create_jwt("ep-123", &params).await?;
1269
+ ```
1270
+
1271
+ ```python
1272
+ # Python
1273
+ await qn.admin.create_jwt(
1274
+ "ep-123",
1275
+ public_key="-----BEGIN PUBLIC KEY-----\n...",
1276
+ kid="key-1",
1277
+ name="primary",
1278
+ )
1279
+ ```
1280
+
1281
+ ```typescript
1282
+ // Node.js
1283
+ await qn.admin.createJwt("ep-123", {
1284
+ publicKey: "-----BEGIN PUBLIC KEY-----\n...",
1285
+ kid: "key-1",
1286
+ name: "primary",
1287
+ });
1288
+ ```
1289
+
1290
+ ```ruby
1291
+ # Ruby
1292
+ qn.admin.create_jwt(
1293
+ id: "ep-123",
1294
+ public_key: "-----BEGIN PUBLIC KEY-----\n...",
1295
+ kid: "key-1",
1296
+ name: "primary"
1297
+ )
1298
+ ```
1299
+
1300
+ ##### `delete_jwt` / `deleteJwt`
1301
+
1302
+ Removes a JWT configuration.
1303
+
1304
+ **Parameters**: `id` (endpoint id, required), `jwt_id` (string, required).
1305
+
1306
+ **Returns**: nothing.
1307
+
1308
+ ```rust
1309
+ // Rust
1310
+ qn.admin.delete_jwt("ep-123", "jwt-1").await?;
1311
+ ```
1312
+
1313
+ ```python
1314
+ # Python
1315
+ await qn.admin.delete_jwt("ep-123", "jwt-1")
1316
+ ```
1317
+
1318
+ ```typescript
1319
+ // Node.js
1320
+ await qn.admin.deleteJwt("ep-123", "jwt-1");
1321
+ ```
1322
+
1323
+ ```ruby
1324
+ # Ruby
1325
+ qn.admin.delete_jwt(id: "ep-123", jwt_id: "jwt-1")
1326
+ ```
1327
+
1328
+ #### Request Filters
1329
+
1330
+ Whitelist specific RPC methods on an endpoint. Requests for methods not on the list are blocked when the feature is enabled.
1331
+
1332
+ ##### `create_request_filter` / `createRequestFilter`
1333
+
1334
+ **Parameters**: `id` (endpoint id, required); body: `method` (string[], optional). Ruby's Hash key is `methods` (plural).
1335
+
1336
+ **Returns**: `CreateRequestFilterResponse` with `data.id`.
1337
+
1338
+ ```rust
1339
+ // Rust
1340
+ let params = CreateRequestFilterRequest::builder()
1341
+ .method(vec!["eth_blockNumber".to_string(), "eth_getBalance".to_string()])
1342
+ .build();
1343
+ let resp = qn.admin.create_request_filter("ep-123", &params).await?;
1344
+ ```
1345
+
1346
+ ```python
1347
+ # Python
1348
+ resp = await qn.admin.create_request_filter(
1349
+ "ep-123",
1350
+ method=["eth_blockNumber", "eth_getBalance"],
1351
+ )
1352
+ ```
1353
+
1354
+ ```typescript
1355
+ // Node.js
1356
+ const resp = await qn.admin.createRequestFilter("ep-123", {
1357
+ method: ["eth_blockNumber", "eth_getBalance"],
1358
+ });
1359
+ ```
1360
+
1361
+ ```ruby
1362
+ # Ruby
1363
+ resp = JSON.parse(qn.admin.create_request_filter(
1364
+ id: "ep-123",
1365
+ methods: ["eth_blockNumber", "eth_getBalance"]
1366
+ ))
1367
+ ```
1368
+
1369
+ ##### `update_request_filter` / `updateRequestFilter`
1370
+
1371
+ **Parameters**: `id` (endpoint id, required), `request_filter_id` (string, required); body: `method` (string[], optional). Ruby's Hash keys are `request_filter_id` and `methods` (plural).
1372
+
1373
+ **Returns**: nothing.
1374
+
1375
+ ```rust
1376
+ // Rust
1377
+ let params = UpdateRequestFilterRequest::builder()
1378
+ .method(vec!["eth_call".to_string()])
1379
+ .build();
1380
+ qn.admin.update_request_filter("ep-123", "f-1", &params).await?;
1381
+ ```
1382
+
1383
+ ```python
1384
+ # Python
1385
+ await qn.admin.update_request_filter("ep-123", "f-1", method=["eth_call"])
1386
+ ```
1387
+
1388
+ ```typescript
1389
+ // Node.js
1390
+ await qn.admin.updateRequestFilter("ep-123", "f-1", { method: ["eth_call"] });
1391
+ ```
1392
+
1393
+ ```ruby
1394
+ # Ruby
1395
+ qn.admin.update_request_filter(id: "ep-123", request_filter_id: "f-1", methods: ["eth_call"])
1396
+ ```
1397
+
1398
+ ##### `delete_request_filter` / `deleteRequestFilter`
1399
+
1400
+ **Parameters**: `id` (endpoint id, required), `request_filter_id` (string, required).
1401
+
1402
+ **Returns**: nothing.
1403
+
1404
+ ```rust
1405
+ // Rust
1406
+ qn.admin.delete_request_filter("ep-123", "f-1").await?;
1407
+ ```
1408
+
1409
+ ```python
1410
+ # Python
1411
+ await qn.admin.delete_request_filter("ep-123", "f-1")
1412
+ ```
1413
+
1414
+ ```typescript
1415
+ // Node.js
1416
+ await qn.admin.deleteRequestFilter("ep-123", "f-1");
1417
+ ```
1418
+
1419
+ ```ruby
1420
+ # Ruby
1421
+ qn.admin.delete_request_filter(id: "ep-123", request_filter_id: "f-1")
1422
+ ```
1423
+
1424
+ #### Multichain
1425
+
1426
+ ##### `enable_multichain` / `enableMultichain`
1427
+
1428
+ Enables multichain on an endpoint.
1429
+
1430
+ **Parameters**: `id` (endpoint id, required).
1431
+
1432
+ **Returns**: nothing.
1433
+
1434
+ ```rust
1435
+ // Rust
1436
+ qn.admin.enable_multichain("ep-123").await?;
1437
+ ```
1438
+
1439
+ ```python
1440
+ # Python
1441
+ await qn.admin.enable_multichain("ep-123")
1442
+ ```
1443
+
1444
+ ```typescript
1445
+ // Node.js
1446
+ await qn.admin.enableMultichain("ep-123");
1447
+ ```
1448
+
1449
+ ```ruby
1450
+ # Ruby
1451
+ qn.admin.enable_multichain(id: "ep-123")
1452
+ ```
1453
+
1454
+ ##### `disable_multichain` / `disableMultichain`
1455
+
1456
+ Disables multichain on an endpoint.
1457
+
1458
+ **Parameters**: `id` (endpoint id, required).
1459
+
1460
+ **Returns**: nothing.
1461
+
1462
+ ```rust
1463
+ // Rust
1464
+ qn.admin.disable_multichain("ep-123").await?;
1465
+ ```
1466
+
1467
+ ```python
1468
+ # Python
1469
+ await qn.admin.disable_multichain("ep-123")
1470
+ ```
1471
+
1472
+ ```typescript
1473
+ // Node.js
1474
+ await qn.admin.disableMultichain("ep-123");
1475
+ ```
1476
+
1477
+ ```ruby
1478
+ # Ruby
1479
+ qn.admin.disable_multichain(id: "ep-123")
1480
+ ```
1481
+
1482
+ #### IP Custom Headers
1483
+
1484
+ ##### `create_or_update_ip_custom_header` / `createOrUpdateIpCustomHeader`
1485
+
1486
+ Sets the custom header used to identify the client IP (e.g. when traffic is proxied).
1487
+
1488
+ **Parameters**: `id` (endpoint id, required); body: `header_name` (string, required).
1489
+
1490
+ **Returns**: `CreateOrUpdateIpCustomHeaderResponse` with `data.header_name`.
1491
+
1492
+ ```rust
1493
+ // Rust
1494
+ let params = CreateOrUpdateIpCustomHeaderRequest::builder()
1495
+ .header_name("X-Forwarded-For".to_string())
1496
+ .build();
1497
+ qn.admin.create_or_update_ip_custom_header("ep-123", &params).await?;
1498
+ ```
1499
+
1500
+ ```python
1501
+ # Python
1502
+ await qn.admin.create_or_update_ip_custom_header("ep-123", header_name="X-Forwarded-For")
1503
+ ```
1504
+
1505
+ ```typescript
1506
+ // Node.js
1507
+ await qn.admin.createOrUpdateIpCustomHeader("ep-123", { headerName: "X-Forwarded-For" });
1508
+ ```
1509
+
1510
+ ```ruby
1511
+ # Ruby
1512
+ JSON.parse(qn.admin.create_or_update_ip_custom_header(
1513
+ id: "ep-123",
1514
+ header_name: "X-Forwarded-For"
1515
+ ))
1516
+ ```
1517
+
1518
+ ##### `delete_ip_custom_header` / `deleteIpCustomHeader`
1519
+
1520
+ Removes the custom IP header configuration.
1521
+
1522
+ **Parameters**: `id` (endpoint id, required).
1523
+
1524
+ **Returns**: `DeleteBoolResponse`.
1525
+
1526
+ ```rust
1527
+ // Rust
1528
+ qn.admin.delete_ip_custom_header("ep-123").await?;
1529
+ ```
1530
+
1531
+ ```python
1532
+ # Python
1533
+ await qn.admin.delete_ip_custom_header("ep-123")
1534
+ ```
1535
+
1536
+ ```typescript
1537
+ // Node.js
1538
+ await qn.admin.deleteIpCustomHeader("ep-123");
1539
+ ```
1540
+
1541
+ ```ruby
1542
+ # Ruby
1543
+ JSON.parse(qn.admin.delete_ip_custom_header(id: "ep-123"))
1544
+ ```
1545
+
1546
+ #### Method Rate Limits
1547
+
1548
+ ##### `get_method_rate_limits` / `getMethodRateLimits`
1549
+
1550
+ Lists method-level rate limiters configured on an endpoint.
1551
+
1552
+ **Parameters**: `id` (endpoint id, required).
1553
+
1554
+ **Returns**: `GetMethodRateLimitsResponse` with `data.rate_limiters: MethodRateLimiter[]`.
1555
+
1556
+ ```rust
1557
+ // Rust
1558
+ let resp = qn.admin.get_method_rate_limits("ep-123").await?;
1559
+ ```
1560
+
1561
+ ```python
1562
+ # Python
1563
+ resp = await qn.admin.get_method_rate_limits("ep-123")
1564
+ ```
1565
+
1566
+ ```typescript
1567
+ // Node.js
1568
+ const resp = await qn.admin.getMethodRateLimits("ep-123");
1569
+ ```
1570
+
1571
+ ```ruby
1572
+ # Ruby
1573
+ resp = JSON.parse(qn.admin.get_method_rate_limits(id: "ep-123"))
1574
+ ```
1575
+
1576
+ ##### `create_method_rate_limit` / `createMethodRateLimit`
1577
+
1578
+ Creates a new method-level rate limiter.
1579
+
1580
+ **Parameters**: `id` (endpoint id, required); body: `interval` (string, e.g. `"second"`), `methods` (string[]), `rate` (i32).
1581
+
1582
+ **Returns**: `CreateMethodRateLimitResponse` with `data: MethodRateLimiter`.
1583
+
1584
+ ```rust
1585
+ // Rust
1586
+ let params = CreateMethodRateLimitRequest::builder()
1587
+ .interval("second".to_string())
1588
+ .methods(vec!["eth_call".to_string()])
1589
+ .rate(10)
1590
+ .build();
1591
+ let resp = qn.admin.create_method_rate_limit("ep-123", &params).await?;
1592
+ ```
1593
+
1594
+ ```python
1595
+ # Python
1596
+ resp = await qn.admin.create_method_rate_limit(
1597
+ "ep-123",
1598
+ interval="second",
1599
+ methods=["eth_call"],
1600
+ rate=10,
1601
+ )
1602
+ ```
1603
+
1604
+ ```typescript
1605
+ // Node.js
1606
+ const resp = await qn.admin.createMethodRateLimit("ep-123", {
1607
+ interval: "second",
1608
+ methods: ["eth_call"],
1609
+ rate: 10,
1610
+ });
1611
+ ```
1612
+
1613
+ ```ruby
1614
+ # Ruby
1615
+ resp = JSON.parse(qn.admin.create_method_rate_limit(
1616
+ id: "ep-123",
1617
+ interval: "second",
1618
+ methods: ["eth_call"],
1619
+ rate: 10
1620
+ ))
1621
+ ```
1622
+
1623
+ ##### `update_method_rate_limit` / `updateMethodRateLimit`
1624
+
1625
+ Updates an existing rate limiter. Only provided fields change.
1626
+
1627
+ **Parameters**: `id` (endpoint id, required), `method_rate_limit_id` (string, required); body: `methods` (string[], optional), `status` (`"enabled"` | `"disabled"`, optional), `rate` (i32, optional).
1628
+
1629
+ **Returns**: `UpdateMethodRateLimitResponse`.
1630
+
1631
+ ```rust
1632
+ // Rust
1633
+ let params = UpdateMethodRateLimitRequest::builder().rate(50).build();
1634
+ qn.admin.update_method_rate_limit("ep-123", "rl-1", &params).await?;
1635
+ ```
1636
+
1637
+ ```python
1638
+ # Python
1639
+ await qn.admin.update_method_rate_limit("ep-123", "rl-1", rate=50)
1640
+ ```
1641
+
1642
+ ```typescript
1643
+ // Node.js
1644
+ await qn.admin.updateMethodRateLimit("ep-123", "rl-1", { rate: 50 });
1645
+ ```
1646
+
1647
+ ```ruby
1648
+ # Ruby
1649
+ JSON.parse(qn.admin.update_method_rate_limit(id: "ep-123", method_rate_limit_id: "rl-1", rate: 50))
1650
+ ```
1651
+
1652
+ ##### `delete_method_rate_limit` / `deleteMethodRateLimit`
1653
+
1654
+ Deletes a rate limiter.
1655
+
1656
+ **Parameters**: `id` (endpoint id, required), `method_rate_limit_id` (string, required).
1657
+
1658
+ **Returns**: nothing.
1659
+
1660
+ ```rust
1661
+ // Rust
1662
+ qn.admin.delete_method_rate_limit("ep-123", "rl-1").await?;
1663
+ ```
1664
+
1665
+ ```python
1666
+ # Python
1667
+ await qn.admin.delete_method_rate_limit("ep-123", "rl-1")
1668
+ ```
1669
+
1670
+ ```typescript
1671
+ // Node.js
1672
+ await qn.admin.deleteMethodRateLimit("ep-123", "rl-1");
1673
+ ```
1674
+
1675
+ ```ruby
1676
+ # Ruby
1677
+ qn.admin.delete_method_rate_limit(id: "ep-123", method_rate_limit_id: "rl-1")
1678
+ ```
1679
+
1680
+ #### Endpoint Rate Limits
1681
+
1682
+ ##### `update_rate_limits` / `updateRateLimits`
1683
+
1684
+ Updates the endpoint-level RPS / RPM / RPD caps.
1685
+
1686
+ **Parameters**: `id` (endpoint id, required); `rate_limits`: `RateLimitSettings` (`rps`, `rpm`, `rpd`, all optional).
1687
+
1688
+ **Returns**: nothing.
1689
+
1690
+ ```rust
1691
+ // Rust
1692
+ let rate_limits = RateLimitSettings::builder().rps(100).rpm(5000).build();
1693
+ let params = UpdateRateLimitsRequest { rate_limits };
1694
+ qn.admin.update_rate_limits("ep-123", &params).await?;
1695
+ ```
1696
+
1697
+ ```python
1698
+ # Python
1699
+ await qn.admin.update_rate_limits("ep-123", rps=100, rpm=5000)
1700
+ ```
1701
+
1702
+ ```typescript
1703
+ // Node.js
1704
+ await qn.admin.updateRateLimits("ep-123", { rateLimits: { rps: 100, rpm: 5000 } });
1705
+ ```
1706
+
1707
+ ```ruby
1708
+ # Ruby
1709
+ qn.admin.update_rate_limits(id: "ep-123", rps: 100, rpm: 5000)
1710
+ ```
1711
+
1712
+ #### Metrics
1713
+
1714
+ ##### `get_endpoint_metrics` / `getEndpointMetrics`
1715
+
1716
+ Returns metric series for an endpoint over a time period.
1717
+
1718
+ **Parameters**: `id` (endpoint id, required); body: `period` (`"hour"` | `"day"` | `"week"` | `"month"`), `metric` (e.g. `"method_calls_over_time"`, `"response_status_breakdown"`).
1719
+
1720
+ **Returns**: `GetEndpointMetricsResponse` with `data: EndpointMetric[]`.
1721
+
1722
+ ```rust
1723
+ // Rust
1724
+ let params = GetEndpointMetricsRequest {
1725
+ period: "day".to_string(),
1726
+ metric: "method_calls_over_time".to_string(),
1727
+ };
1728
+ let resp = qn.admin.get_endpoint_metrics("ep-123", &params).await?;
1729
+ ```
1730
+
1731
+ ```python
1732
+ # Python
1733
+ resp = await qn.admin.get_endpoint_metrics(
1734
+ "ep-123",
1735
+ period="day",
1736
+ metric="method_calls_over_time",
1737
+ )
1738
+ ```
1739
+
1740
+ ```typescript
1741
+ // Node.js
1742
+ const resp = await qn.admin.getEndpointMetrics("ep-123", {
1743
+ period: "day",
1744
+ metric: "method_calls_over_time",
1745
+ });
1746
+ ```
1747
+
1748
+ ```ruby
1749
+ # Ruby
1750
+ resp = JSON.parse(qn.admin.get_endpoint_metrics(
1751
+ id: "ep-123",
1752
+ period: "day",
1753
+ metric: "method_calls_over_time"
1754
+ ))
1755
+ ```
1756
+
1757
+ ##### `get_account_metrics` / `getAccountMetrics`
1758
+
1759
+ Returns account-level metric series. Supports an optional `percentile` (e.g. `"p50"`, `"p95"`, `"p99"`) for latency metrics.
1760
+
1761
+ **Parameters**: `period` (required), `metric` (required), `percentile` (string, optional).
1762
+
1763
+ **Returns**: `GetAccountMetricsResponse` with `data: EndpointMetric[]`.
1764
+
1765
+ ```rust
1766
+ // Rust
1767
+ let params = GetAccountMetricsRequest {
1768
+ period: "day".to_string(),
1769
+ metric: "credits_over_time".to_string(),
1770
+ percentile: None,
1771
+ };
1772
+ let resp = qn.admin.get_account_metrics(&params).await?;
1773
+ ```
1774
+
1775
+ ```python
1776
+ # Python
1777
+ resp = await qn.admin.get_account_metrics(period="day", metric="credits_over_time")
1778
+ ```
1779
+
1780
+ ```typescript
1781
+ // Node.js
1782
+ const resp = await qn.admin.getAccountMetrics({
1783
+ period: "day",
1784
+ metric: "credits_over_time",
1785
+ });
1786
+ ```
1787
+
1788
+ ```ruby
1789
+ # Ruby
1790
+ resp = JSON.parse(qn.admin.get_account_metrics(period: "day", metric: "credits_over_time"))
1791
+ ```
1792
+
1793
+ #### Chains
1794
+
1795
+ ##### `list_chains` / `listChains`
1796
+
1797
+ Lists the blockchains supported by QuickNode along with their networks.
1798
+
1799
+ **Parameters**: none.
1800
+
1801
+ **Returns**: `ListChainsResponse` with `data: Chain[]`.
1802
+
1803
+ ```rust
1804
+ // Rust
1805
+ let resp = qn.admin.list_chains().await?;
1806
+ ```
1807
+
1808
+ ```python
1809
+ # Python
1810
+ resp = await qn.admin.list_chains()
1811
+ ```
1812
+
1813
+ ```typescript
1814
+ // Node.js
1815
+ const resp = await qn.admin.listChains();
1816
+ ```
1817
+
1818
+ ```ruby
1819
+ # Ruby
1820
+ resp = JSON.parse(qn.admin.list_chains)
1821
+ ```
1822
+
1823
+ #### Billing
1824
+
1825
+ ##### `list_invoices` / `listInvoices`
1826
+
1827
+ Lists invoices on the account.
1828
+
1829
+ **Parameters**: none.
1830
+
1831
+ **Returns**: `ListInvoicesResponse` with `data.invoices: Invoice[]`.
1832
+
1833
+ ```rust
1834
+ // Rust
1835
+ let resp = qn.admin.list_invoices().await?;
1836
+ ```
1837
+
1838
+ ```python
1839
+ # Python
1840
+ resp = await qn.admin.list_invoices()
1841
+ ```
1842
+
1843
+ ```typescript
1844
+ // Node.js
1845
+ const resp = await qn.admin.listInvoices();
1846
+ ```
1847
+
1848
+ ```ruby
1849
+ # Ruby
1850
+ resp = JSON.parse(qn.admin.list_invoices)
1851
+ ```
1852
+
1853
+ ##### `list_payments` / `listPayments`
1854
+
1855
+ Lists payments on the account.
1856
+
1857
+ **Parameters**: none.
1858
+
1859
+ **Returns**: `ListPaymentsResponse` with `data.payments: Payment[]`.
1860
+
1861
+ ```rust
1862
+ // Rust
1863
+ let resp = qn.admin.list_payments().await?;
1864
+ ```
1865
+
1866
+ ```python
1867
+ # Python
1868
+ resp = await qn.admin.list_payments()
1869
+ ```
1870
+
1871
+ ```typescript
1872
+ // Node.js
1873
+ const resp = await qn.admin.listPayments();
1874
+ ```
1875
+
1876
+ ```ruby
1877
+ # Ruby
1878
+ resp = JSON.parse(qn.admin.list_payments)
1879
+ ```
1880
+
1881
+ #### Bulk Operations
1882
+
1883
+ ##### `bulk_update_endpoint_status` / `bulkUpdateEndpointStatus`
1884
+
1885
+ Activates or pauses many endpoints at once.
1886
+
1887
+ **Parameters**: `ids` (string[], required), `status` (`"active"` | `"paused"`, required).
1888
+
1889
+ **Returns**: `BulkUpdateEndpointStatusResponse` with per-endpoint `results`.
1890
+
1891
+ ```rust
1892
+ // Rust
1893
+ let params = BulkUpdateEndpointStatusRequest::builder()
1894
+ .ids(vec!["ep-1".to_string(), "ep-2".to_string()])
1895
+ .status("paused".to_string())
1896
+ .build();
1897
+ let resp = qn.admin.bulk_update_endpoint_status(&params).await?;
1898
+ ```
1899
+
1900
+ ```python
1901
+ # Python
1902
+ resp = await qn.admin.bulk_update_endpoint_status(ids=["ep-1", "ep-2"], status="paused")
1903
+ ```
1904
+
1905
+ ```typescript
1906
+ // Node.js
1907
+ const resp = await qn.admin.bulkUpdateEndpointStatus({
1908
+ ids: ["ep-1", "ep-2"],
1909
+ status: "paused",
1910
+ });
1911
+ ```
1912
+
1913
+ ```ruby
1914
+ # Ruby
1915
+ resp = JSON.parse(qn.admin.bulk_update_endpoint_status(ids: ["ep-1", "ep-2"], status: "paused"))
1916
+ ```
1917
+
1918
+ ##### `bulk_add_tag` / `bulkAddTag`
1919
+
1920
+ Applies a tag (created if missing) to many endpoints at once.
1921
+
1922
+ **Parameters**: `ids` (string[], required), `label` (string, required).
1923
+
1924
+ **Returns**: `BulkAddTagResponse`.
1925
+
1926
+ ```rust
1927
+ // Rust
1928
+ let params = BulkAddTagRequest::builder()
1929
+ .ids(vec!["ep-1".to_string(), "ep-2".to_string()])
1930
+ .label("prod".to_string())
1931
+ .build();
1932
+ let resp = qn.admin.bulk_add_tag(&params).await?;
1933
+ ```
1934
+
1935
+ ```python
1936
+ # Python
1937
+ resp = await qn.admin.bulk_add_tag(ids=["ep-1", "ep-2"], label="prod")
1938
+ ```
1939
+
1940
+ ```typescript
1941
+ // Node.js
1942
+ const resp = await qn.admin.bulkAddTag({ ids: ["ep-1", "ep-2"], label: "prod" });
1943
+ ```
1944
+
1945
+ ```ruby
1946
+ # Ruby
1947
+ resp = JSON.parse(qn.admin.bulk_add_tag(ids: ["ep-1", "ep-2"], label: "prod"))
1948
+ ```
1949
+
1950
+ ##### `bulk_remove_tag` / `bulkRemoveTag`
1951
+
1952
+ Removes a tag from many endpoints at once.
1953
+
1954
+ **Parameters**: `ids` (string[], required), `tag_id` (i32, required).
1955
+
1956
+ **Returns**: `BulkRemoveTagResponse`.
1957
+
1958
+ ```rust
1959
+ // Rust
1960
+ let params = BulkRemoveTagRequest::builder()
1961
+ .ids(vec!["ep-1".to_string(), "ep-2".to_string()])
1962
+ .tag_id(42)
1963
+ .build();
1964
+ let resp = qn.admin.bulk_remove_tag(&params).await?;
1965
+ ```
1966
+
1967
+ ```python
1968
+ # Python
1969
+ resp = await qn.admin.bulk_remove_tag(ids=["ep-1", "ep-2"], tag_id=42)
1970
+ ```
1971
+
1972
+ ```typescript
1973
+ // Node.js
1974
+ const resp = await qn.admin.bulkRemoveTag({ ids: ["ep-1", "ep-2"], tagId: 42 });
1975
+ ```
1976
+
1977
+ ```ruby
1978
+ # Ruby
1979
+ resp = JSON.parse(qn.admin.bulk_remove_tag(ids: ["ep-1", "ep-2"], tag_id: 42))
1980
+ ```
1981
+
1982
+ #### Account Tags
1983
+
1984
+ ##### `list_tags` / `listTags`
1985
+
1986
+ Lists every tag on the account along with usage counts.
1987
+
1988
+ **Parameters**: none.
1989
+
1990
+ **Returns**: `ListTagsResponse` with `data.tags: AccountTag[]`.
1991
+
1992
+ ```rust
1993
+ // Rust
1994
+ let resp = qn.admin.list_tags().await?;
1995
+ ```
1996
+
1997
+ ```python
1998
+ # Python
1999
+ resp = await qn.admin.list_tags()
2000
+ ```
2001
+
2002
+ ```typescript
2003
+ // Node.js
2004
+ const resp = await qn.admin.listTags();
2005
+ ```
2006
+
2007
+ ```ruby
2008
+ # Ruby
2009
+ resp = JSON.parse(qn.admin.list_tags)
2010
+ ```
2011
+
2012
+ ##### `rename_tag` / `renameTag`
2013
+
2014
+ Renames an account-level tag.
2015
+
2016
+ **Parameters**: `tag_id` (i32, required); body: `label` (string, required).
2017
+
2018
+ **Returns**: `RenameTagResponse` with updated `AccountTag`.
2019
+
2020
+ ```rust
2021
+ // Rust
2022
+ let params = RenameTagRequest::builder().label("staging".to_string()).build();
2023
+ let resp = qn.admin.rename_tag(42, &params).await?;
2024
+ ```
2025
+
2026
+ ```python
2027
+ # Python
2028
+ resp = await qn.admin.rename_tag(42, label="staging")
2029
+ ```
2030
+
2031
+ ```typescript
2032
+ // Node.js
2033
+ const resp = await qn.admin.renameTag(42, { label: "staging" });
2034
+ ```
2035
+
2036
+ ```ruby
2037
+ # Ruby
2038
+ resp = JSON.parse(qn.admin.rename_tag(tag_id: 42, label: "staging"))
2039
+ ```
2040
+
2041
+ ##### `delete_account_tag` / `deleteAccountTag`
2042
+
2043
+ Deletes a tag from the account. The tag must first be removed from any endpoints using it.
2044
+
2045
+ **Parameters**: `id` (i32, required).
2046
+
2047
+ **Returns**: `DeleteAccountTagResponse`.
2048
+
2049
+ ```rust
2050
+ // Rust
2051
+ qn.admin.delete_account_tag(42).await?;
2052
+ ```
2053
+
2054
+ ```python
2055
+ # Python
2056
+ await qn.admin.delete_account_tag(42)
2057
+ ```
2058
+
2059
+ ```typescript
2060
+ // Node.js
2061
+ await qn.admin.deleteAccountTag(42);
2062
+ ```
2063
+
2064
+ ```ruby
2065
+ # Ruby
2066
+ JSON.parse(qn.admin.delete_account_tag(id: 42))
2067
+ ```
2068
+
2069
+ ---
2070
+
2071
+ ### Streams Client
2072
+
2073
+ Accessed as `qn.streams`. Creates and manages blockchain data streams that deliver filtered on-chain events to configured destinations. Backed by `https://api.quicknode.com/streams/rest/v1/`.
2074
+
2075
+ #### Datasets, Regions, and Destinations
2076
+
2077
+ Enums used across stream methods:
2078
+
2079
+ - **`StreamRegion`**: `UsaEast`, `EuropeCentral`, `AsiaEast` (wire values: `usa_east`, `europe_central`, `asia_east`).
2080
+ - **`StreamDataset`**: `Block`, `BlockWithReceipts`, `Transactions`, `Logs`, `Receipts`, `TraceBlocks`, `DebugTraces`, `BlockWithReceiptsDebugTrace`, `BlockWithReceiptsTraceBlock`, `BlobSidecars`, `ProgramsWithLogs`, `Ledger`, `Events`, `Orders`, `Trades`, `BookUpdates`, `Twap`, `WriterActions`.
2081
+ - **`StreamStatus`**: `Active`, `Paused`, `Terminated`, `Completed`, `Blocked`.
2082
+ - **`FilterLanguage`**: `Javascript`, `Go`, `Wasm`.
2083
+ - **`StreamMetadataLocation`**: `Body`, `Header`, `None`.
2084
+
2085
+ Destinations are expressed via `DestinationAttributes`. Each variant wraps an attribute struct:
2086
+
2087
+ | Variant | Struct | Key fields |
2088
+ |---|---|---|
2089
+ | `Webhook` | `WebhookAttributes` | `url`, `max_retry`, `retry_interval_sec`, `post_timeout_sec`, `compression`, `security_token?` |
2090
+ | `S3` | `S3Attributes` | `endpoint`, `access_key`, `secret_key`, `bucket`, `object_prefix`, `compression`, `file_type`, `max_retry`, `retry_interval_sec`, `use_ssl?` |
2091
+ | `Azure` | `AzureAttributes` | `storage_account`, `sas_token`, `container`, `compression`, `file_type`, `max_retry`, `retry_interval_sec`, `blob_prefix?` |
2092
+ | `Postgres` | `PostgresAttributes` | `host`, `port`, `username`, `password`, `database`, `schema`, `table`, `max_retry`, `retry_interval_sec`, `use_ssl?` |
2093
+ | `Mysql` | `MysqlAttributes` | `host`, `port`, `username`, `password`, `database`, `table`, `max_retry`, `retry_interval_sec`, `use_ssl?` |
2094
+ | `Mongo` | `MongoAttributes` | `connection_string`, `database`, `collection`, `max_retry`, `retry_interval_sec` |
2095
+ | `Clickhouse` | `ClickhouseAttributes` | `host`, `port`, `username`, `password`, `database`, `table`, `max_retry`, `retry_interval_sec`, `use_ssl?` |
2096
+ | `Snowflake` | `SnowflakeAttributes` | `account`, `warehouse`, `database`, `schema`, `table`, `username`, `private_key`, `max_retry`, `retry_interval_sec` |
2097
+ | `Kafka` | `KafkaAttributes` | `bootstrap_servers`, `topic`, `compression`, `max_retry`, `retry_interval_sec` |
2098
+ | `Redis` | `RedisAttributes` | `host`, `port`, `username`, `password`, `key`, `max_retry`, `retry_interval_sec`, `use_ssl?` |
2099
+
2100
+ Wrapper naming per language:
2101
+
2102
+ - **Rust**: `DestinationAttributes::Webhook(WebhookAttributes { .. })` etc.
2103
+ - **Python**: `StreamWebhookDestination(WebhookAttributes(...))`, `StreamS3Destination(S3Attributes(...))`, etc.
2104
+ - **Node.js**: a discriminated object `{ destination: "webhook", attributes: { ... } }` using string discriminators.
2105
+ - **Ruby**: factory methods on `QuickNodeSdk::DestinationAttributes`, e.g. `QuickNodeSdk::DestinationAttributes.webhook(url: ..., ...)`.
2106
+
2107
+ #### Streams methods
2108
+
2109
+ ##### `create_stream` / `createStream`
2110
+
2111
+ Creates a new stream that delivers filtered data to the configured destination. Start from a specific block for backfills or from the tip for real-time streaming. Supports filters, reorg handling, distance-from-tip, elastic batching, notification emails, and extra destinations.
2112
+
2113
+ **Parameters**: `CreateStreamParams` — required: `name`, `region`, `network`, `dataset`, `start_range` (i64), `end_range` (i64, `-1` = follow tip), `destination_attributes`, `plan`, `threshold_fetch_buffer`. Common optional fields: `dataset_batch_size`, `include_stream_metadata`, `fix_block_reorgs`, `keep_distance_from_tip`, `elastic_batch_enabled`, `filter_function`, `filter_language`, `status`, `notification_email`, `extra_destinations`.
2114
+
2115
+ **Returns**: `Stream`.
2116
+
2117
+ ```rust
2118
+ // Rust
2119
+ let params = CreateStreamParams::builder()
2120
+ .name("My Stream".to_string())
2121
+ .region(StreamRegion::UsaEast)
2122
+ .network("ethereum-mainnet".to_string())
2123
+ .dataset(StreamDataset::Block)
2124
+ .start_range(24691804)
2125
+ .end_range(24691904)
2126
+ .destination_attributes(DestinationAttributes::Webhook(WebhookAttributes {
2127
+ url: "https://webhook.site/...".to_string(),
2128
+ max_retry: 3,
2129
+ retry_interval_sec: 1,
2130
+ post_timeout_sec: 10,
2131
+ compression: "none".to_string(),
2132
+ security_token: None,
2133
+ }))
2134
+ .plan("growth_plan".to_string())
2135
+ .threshold_fetch_buffer(1000)
2136
+ .status(StreamStatus::Active)
2137
+ .build();
2138
+ let stream = qn.streams.create_stream(&params).await?;
2139
+ ```
2140
+
2141
+ ```python
2142
+ # Python
2143
+ from sdk import WebhookAttributes, StreamWebhookDestination
2144
+
2145
+ stream = await qn.streams.create_stream(
2146
+ name="My Stream",
2147
+ network="ethereum-mainnet",
2148
+ dataset="block",
2149
+ region="usa_east",
2150
+ start_range=24691804,
2151
+ end_range=24691904,
2152
+ destination_attributes=StreamWebhookDestination(
2153
+ WebhookAttributes(
2154
+ url="https://webhook.site/...",
2155
+ max_retry=3,
2156
+ retry_interval_sec=1,
2157
+ post_timeout_sec=10,
2158
+ compression="none",
2159
+ )
2160
+ ),
2161
+ plan="growth_plan",
2162
+ threshold_fetch_buffer=1000,
2163
+ status="active",
2164
+ )
2165
+ ```
2166
+
2167
+ ```typescript
2168
+ // Node.js
2169
+ import { StreamDataset, StreamRegion, StreamStatus } from "quicknode-sdk";
2170
+
2171
+ const stream = await qn.streams.createStream({
2172
+ name: "My Stream",
2173
+ network: "ethereum-mainnet",
2174
+ dataset: StreamDataset.Block,
2175
+ region: StreamRegion.UsaEast,
2176
+ startRange: 24691804,
2177
+ endRange: 24691904,
2178
+ destinationAttributes: {
2179
+ destination: "webhook",
2180
+ attributes: {
2181
+ url: "https://webhook.site/...",
2182
+ maxRetry: 3,
2183
+ retryIntervalSec: 1,
2184
+ postTimeoutSec: 10,
2185
+ compression: "none",
2186
+ },
2187
+ },
2188
+ plan: "growth_plan",
2189
+ thresholdFetchBuffer: 1000,
2190
+ status: StreamStatus.Active,
2191
+ });
2192
+ ```
2193
+
2194
+ ```ruby
2195
+ # Ruby
2196
+ dest = QuickNodeSdk::DestinationAttributes.webhook(
2197
+ url: "https://webhook.site/...",
2198
+ max_retry: 3,
2199
+ retry_interval_sec: 1,
2200
+ post_timeout_sec: 10,
2201
+ compression: "none"
2202
+ )
2203
+ stream = JSON.parse(qn.streams.create_stream(
2204
+ name: "My Stream",
2205
+ network: "ethereum-mainnet",
2206
+ dataset: "block",
2207
+ region: "usa_east",
2208
+ start_range: 24691804,
2209
+ end_range: 24691904,
2210
+ destination_attributes: dest,
2211
+ plan: "growth_plan",
2212
+ threshold_fetch_buffer: 1000,
2213
+ status: "active"
2214
+ ))
2215
+ ```
2216
+
2217
+ ##### `list_streams` / `listStreams`
2218
+
2219
+ Paginated list of streams on the account.
2220
+
2221
+ **Parameters** (all optional): `offset` (i64), `limit` (i64), `order_by` (string), `order_direction` (`"asc"` | `"desc"`), `stream_type` (string).
2222
+
2223
+ **Returns**: `ListStreamsResponse` with `data: Stream[]` and `page_info`.
2224
+
2225
+ ```rust
2226
+ // Rust
2227
+ let resp = qn.streams.list_streams(&ListStreamsParams::default()).await?;
2228
+ ```
2229
+
2230
+ ```python
2231
+ # Python
2232
+ resp = await qn.streams.list_streams()
2233
+ ```
2234
+
2235
+ ```typescript
2236
+ // Node.js
2237
+ const resp = await qn.streams.listStreams();
2238
+ ```
2239
+
2240
+ ```ruby
2241
+ # Ruby
2242
+ resp = JSON.parse(qn.streams.list_streams({}))
2243
+ ```
2244
+
2245
+ ##### `get_stream` / `getStream`
2246
+
2247
+ Fetches one stream by id.
2248
+
2249
+ **Parameters**: `id` (string, required).
2250
+
2251
+ **Returns**: `Stream`.
2252
+
2253
+ ```rust
2254
+ // Rust
2255
+ let stream = qn.streams.get_stream("stream-id").await?;
2256
+ ```
2257
+
2258
+ ```python
2259
+ # Python
2260
+ stream = await qn.streams.get_stream("stream-id")
2261
+ ```
2262
+
2263
+ ```typescript
2264
+ // Node.js
2265
+ const stream = await qn.streams.getStream("stream-id");
2266
+ ```
2267
+
2268
+ ```ruby
2269
+ # Ruby
2270
+ stream = JSON.parse(qn.streams.get_stream(id: "stream-id"))
2271
+ ```
2272
+
2273
+ ##### `update_stream` / `updateStream`
2274
+
2275
+ Partially updates a stream. Omitted fields are left unchanged.
2276
+
2277
+ **Parameters**: `id` (string, required); body: any field from `CreateStreamParams` (all optional).
2278
+
2279
+ **Returns**: updated `Stream`.
2280
+
2281
+ ```rust
2282
+ // Rust
2283
+ let params = UpdateStreamParams {
2284
+ name: Some("Renamed".to_string()),
2285
+ ..Default::default()
2286
+ };
2287
+ let stream = qn.streams.update_stream("stream-id", &params).await?;
2288
+ ```
2289
+
2290
+ ```python
2291
+ # Python
2292
+ stream = await qn.streams.update_stream("stream-id", name="Renamed")
2293
+ ```
2294
+
2295
+ ```typescript
2296
+ // Node.js
2297
+ const stream = await qn.streams.updateStream("stream-id", { name: "Renamed" });
2298
+ ```
2299
+
2300
+ ```ruby
2301
+ # Ruby
2302
+ stream = JSON.parse(qn.streams.update_stream(id: "stream-id", name: "Renamed"))
2303
+ ```
2304
+
2305
+ ##### `delete_stream` / `deleteStream`
2306
+
2307
+ Deletes one stream by id.
2308
+
2309
+ **Parameters**: `id` (string, required).
2310
+
2311
+ **Returns**: nothing.
2312
+
2313
+ ```rust
2314
+ // Rust
2315
+ qn.streams.delete_stream("stream-id").await?;
2316
+ ```
2317
+
2318
+ ```python
2319
+ # Python
2320
+ await qn.streams.delete_stream("stream-id")
2321
+ ```
2322
+
2323
+ ```typescript
2324
+ // Node.js
2325
+ await qn.streams.deleteStream("stream-id");
2326
+ ```
2327
+
2328
+ ```ruby
2329
+ # Ruby
2330
+ qn.streams.delete_stream(id: "stream-id")
2331
+ ```
2332
+
2333
+ ##### `delete_all_streams` / `deleteAllStreams`
2334
+
2335
+ Deletes every stream on the account. Destructive and takes no arguments.
2336
+
2337
+ **Parameters**: none.
2338
+
2339
+ **Returns**: nothing.
2340
+
2341
+ ```rust
2342
+ // Rust
2343
+ qn.streams.delete_all_streams().await?;
2344
+ ```
2345
+
2346
+ ```python
2347
+ # Python
2348
+ await qn.streams.delete_all_streams()
2349
+ ```
2350
+
2351
+ ```typescript
2352
+ // Node.js
2353
+ await qn.streams.deleteAllStreams();
2354
+ ```
2355
+
2356
+ ```ruby
2357
+ # Ruby
2358
+ qn.streams.delete_all_streams
2359
+ ```
2360
+
2361
+ ##### `activate_stream` / `activateStream`
2362
+
2363
+ Resumes delivery on a stream from its current position.
2364
+
2365
+ **Parameters**: `id` (string, required).
2366
+
2367
+ **Returns**: nothing.
2368
+
2369
+ ```rust
2370
+ // Rust
2371
+ qn.streams.activate_stream("stream-id").await?;
2372
+ ```
2373
+
2374
+ ```python
2375
+ # Python
2376
+ await qn.streams.activate_stream("stream-id")
2377
+ ```
2378
+
2379
+ ```typescript
2380
+ // Node.js
2381
+ await qn.streams.activateStream("stream-id");
2382
+ ```
2383
+
2384
+ ```ruby
2385
+ # Ruby
2386
+ qn.streams.activate_stream(id: "stream-id")
2387
+ ```
2388
+
2389
+ ##### `pause_stream` / `pauseStream`
2390
+
2391
+ Halts delivery on a stream.
2392
+
2393
+ **Parameters**: `id` (string, required).
2394
+
2395
+ **Returns**: nothing.
2396
+
2397
+ ```rust
2398
+ // Rust
2399
+ qn.streams.pause_stream("stream-id").await?;
2400
+ ```
2401
+
2402
+ ```python
2403
+ # Python
2404
+ await qn.streams.pause_stream("stream-id")
2405
+ ```
2406
+
2407
+ ```typescript
2408
+ // Node.js
2409
+ await qn.streams.pauseStream("stream-id");
2410
+ ```
2411
+
2412
+ ```ruby
2413
+ # Ruby
2414
+ qn.streams.pause_stream(id: "stream-id")
2415
+ ```
2416
+
2417
+ ##### `test_filter` / `testFilter`
2418
+
2419
+ Runs a filter function against a block so it can be validated before being attached to a live stream.
2420
+
2421
+ **Parameters**: `network` (string, required), `dataset` (`StreamDataset`, required), `block` (string, required), `filter_function` (string, optional), `filter_language` (`FilterLanguage`, optional), `address_book_config` (optional).
2422
+
2423
+ **Returns**: `TestFilterResponse` with `result` and `logs`.
2424
+
2425
+ ```rust
2426
+ // Rust
2427
+ let params = TestFilterParams {
2428
+ network: "ethereum-mainnet".to_string(),
2429
+ dataset: StreamDataset::Block,
2430
+ block: "17811625".to_string(),
2431
+ filter_function: None,
2432
+ filter_language: None,
2433
+ address_book_config: None,
2434
+ };
2435
+ let resp = qn.streams.test_filter(&params).await?;
2436
+ ```
2437
+
2438
+ ```python
2439
+ # Python
2440
+ resp = await qn.streams.test_filter(
2441
+ network="ethereum-mainnet",
2442
+ dataset="block",
2443
+ block="17811625",
2444
+ )
2445
+ ```
2446
+
2447
+ ```typescript
2448
+ // Node.js
2449
+ import { StreamDataset } from "quicknode-sdk";
2450
+
2451
+ const resp = await qn.streams.testFilter({
2452
+ network: "ethereum-mainnet",
2453
+ dataset: StreamDataset.Block,
2454
+ block: "17811625",
2455
+ });
2456
+ ```
2457
+
2458
+ ```ruby
2459
+ # Ruby
2460
+ resp = JSON.parse(qn.streams.test_filter(
2461
+ network: "ethereum-mainnet",
2462
+ dataset: "block",
2463
+ block: "17811625"
2464
+ ))
2465
+ ```
2466
+
2467
+ ##### `get_enabled_count` / `getEnabledCount`
2468
+
2469
+ Counts currently enabled (active) streams, optionally filtered by type.
2470
+
2471
+ **Parameters**: `stream_type` (string, optional).
2472
+
2473
+ **Returns**: `EnabledCountResponse` with `total`.
2474
+
2475
+ ```rust
2476
+ // Rust
2477
+ let resp = qn.streams.get_enabled_count(None).await?;
2478
+ ```
2479
+
2480
+ ```python
2481
+ # Python
2482
+ resp = await qn.streams.get_enabled_count()
2483
+ ```
2484
+
2485
+ ```typescript
2486
+ // Node.js
2487
+ const resp = await qn.streams.getEnabledCount();
2488
+ ```
2489
+
2490
+ ```ruby
2491
+ # Ruby
2492
+ resp = JSON.parse(qn.streams.get_enabled_count({}))
2493
+ ```
2494
+
2495
+ ---
2496
+
2497
+ ### Webhooks Client
2498
+
2499
+ Accessed as `qn.webhooks`. Creates webhooks from filter templates and manages their lifecycle. Backed by `https://api.quicknode.com/webhooks/rest/v1/`.
2500
+
2501
+ #### Templates and destination
2502
+
2503
+ `WebhookTemplateId` identifies the filter template:
2504
+
2505
+ | Variant | Wire value |
2506
+ |---|---|
2507
+ | `EvmWalletFilter` | `evmWalletFilter` |
2508
+ | `EvmContractEvents` | `evmContractEvents` |
2509
+ | `EvmAbiFilter` | `evmAbiFilter` |
2510
+ | `SolanaWalletFilter` | `solanaWalletFilter` |
2511
+ | `BitcoinWalletFilter` | `bitcoinWalletFilter` |
2512
+ | `XrplWalletFilter` | `xrplWalletFilter` |
2513
+ | `HyperliquidWalletEventsFilter` | `hyperliquidWalletEventsFilter` |
2514
+ | `StellarWalletTransactionsSourceAccountFilter` | `stellarWalletTransactionsSourceAccountFilter` |
2515
+
2516
+ `TemplateArgs` carries the arguments; construct one per template via the factory methods:
2517
+
2518
+ | Factory | Argument struct | Fields |
2519
+ |---|---|---|
2520
+ | `evm_wallet_filter` | `EvmWalletFilterTemplate` | `wallets: string[]` |
2521
+ | `evm_contract_events` | `EvmContractEventsTemplate` | `contracts: string[]`, `event_hashes?: string[]` |
2522
+ | `evm_abi_filter` | `EvmAbiFilterTemplate` | `abi: string` (JSON), `contracts: string[]` |
2523
+ | `solana_wallet_filter` | `SolanaWalletFilterTemplate` | `accounts: string[]` |
2524
+ | `bitcoin_wallet_filter` | `BitcoinWalletFilterTemplate` | `wallets: string[]` |
2525
+ | `xrpl_wallet_filter` | `XrplWalletFilterTemplate` | `wallets: string[]` |
2526
+ | `hyperliquid_wallet_events_filter` | `HyperliquidWalletEventsFilterTemplate` | `wallets: string[]` |
2527
+ | `stellar_wallet_transactions_filter` | `StellarWalletTransactionsFilterTemplate` | `source_accounts: string[]` |
2528
+
2529
+ `WebhookDestinationAttributes`: `url` (required), `security_token` (optional — auto-generated if omitted), `compression` (optional — `"none"` | `"gzip"`).
2530
+
2531
+ `WebhookStartFrom`: `Last` (resume from last delivered block) or `Latest` (start from newest).
2532
+
2533
+ In Ruby, `template_args` is passed as a JSON string under the key `template_args_json`; destination is passed as a JSON string under `destination_attributes_json`.
2534
+
2535
+ #### Webhooks methods
2536
+
2537
+ ##### `list_webhooks` / `listWebhooks`
2538
+
2539
+ Paginated list of webhooks.
2540
+
2541
+ **Parameters** (all optional): `limit` (i64), `offset` (i64).
2542
+
2543
+ **Returns**: `ListWebhooksResponse` with `data: Webhook[]` and `pageInfo`.
2544
+
2545
+ ```rust
2546
+ // Rust
2547
+ let resp = qn.webhooks.list_webhooks(&GetWebhooksParams::default()).await?;
2548
+ ```
2549
+
2550
+ ```python
2551
+ # Python
2552
+ resp = await qn.webhooks.list_webhooks()
2553
+ ```
2554
+
2555
+ ```typescript
2556
+ // Node.js
2557
+ const resp = await qn.webhooks.listWebhooks();
2558
+ ```
2559
+
2560
+ ```ruby
2561
+ # Ruby
2562
+ resp = JSON.parse(qn.webhooks.list_webhooks({}))
2563
+ ```
2564
+
2565
+ ##### `get_webhook` / `getWebhook`
2566
+
2567
+ Fetches a webhook by id.
2568
+
2569
+ **Parameters**: `id` (string, required).
2570
+
2571
+ **Returns**: `Webhook`.
2572
+
2573
+ ```rust
2574
+ // Rust
2575
+ let webhook = qn.webhooks.get_webhook("wh-1").await?;
2576
+ ```
2577
+
2578
+ ```python
2579
+ # Python
2580
+ webhook = await qn.webhooks.get_webhook("wh-1")
2581
+ ```
2582
+
2583
+ ```typescript
2584
+ // Node.js
2585
+ const webhook = await qn.webhooks.getWebhook("wh-1");
2586
+ ```
2587
+
2588
+ ```ruby
2589
+ # Ruby
2590
+ webhook = JSON.parse(qn.webhooks.get_webhook(id: "wh-1"))
2591
+ ```
2592
+
2593
+ ##### `create_webhook_from_template` / `createWebhookFromTemplate`
2594
+
2595
+ Creates a webhook from a predefined filter template.
2596
+
2597
+ **Parameters**: `name` (required), `network` (required), `destination_attributes` (`WebhookDestinationAttributes`, required), `template_args` (`TemplateArgs`, required), `notification_email` (optional).
2598
+
2599
+ **Returns**: `Webhook`.
2600
+
2601
+ ```rust
2602
+ // Rust
2603
+ let template_args = TemplateArgs::evm_wallet_filter(&EvmWalletFilterTemplate {
2604
+ wallets: vec!["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48".to_string()],
2605
+ })?;
2606
+ let params = CreateWebhookFromTemplateParams {
2607
+ name: "Wallet Webhook".to_string(),
2608
+ network: "ethereum-mainnet".to_string(),
2609
+ notification_email: None,
2610
+ destination_attributes: WebhookDestinationAttributes {
2611
+ url: "https://webhook.site/...".to_string(),
2612
+ security_token: None,
2613
+ compression: None,
2614
+ },
2615
+ template_args,
2616
+ };
2617
+ let webhook = qn.webhooks.create_webhook_from_template(&params).await?;
2618
+ ```
2619
+
2620
+ ```python
2621
+ # Python
2622
+ from sdk import EvmWalletFilterTemplate, TemplateArgs, WebhookDestinationAttributes
2623
+
2624
+ webhook = await qn.webhooks.create_webhook_from_template(
2625
+ name="Wallet Webhook",
2626
+ network="ethereum-mainnet",
2627
+ destination_attributes=WebhookDestinationAttributes(url="https://webhook.site/..."),
2628
+ template_args=TemplateArgs.evm_wallet_filter(
2629
+ EvmWalletFilterTemplate(wallets=["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"])
2630
+ ),
2631
+ )
2632
+ ```
2633
+
2634
+ ```typescript
2635
+ // Node.js
2636
+ import { TemplateArgs } from "quicknode-sdk";
2637
+
2638
+ const webhook = await qn.webhooks.createWebhookFromTemplate({
2639
+ name: "Wallet Webhook",
2640
+ network: "ethereum-mainnet",
2641
+ destinationAttributes: { url: "https://webhook.site/..." },
2642
+ templateArgs: TemplateArgs.evmWalletFilter({
2643
+ wallets: ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"],
2644
+ }),
2645
+ });
2646
+ ```
2647
+
2648
+ ```ruby
2649
+ # Ruby
2650
+ destination_attributes = JSON.generate({
2651
+ url: "https://webhook.site/...",
2652
+ compression: "none"
2653
+ })
2654
+ template_args = JSON.generate({
2655
+ template_id: "evmWalletFilter",
2656
+ value: JSON.generate({ wallets: ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"] })
2657
+ })
2658
+ webhook = JSON.parse(qn.webhooks.create_webhook_from_template(
2659
+ name: "Wallet Webhook",
2660
+ network: "ethereum-mainnet",
2661
+ destination_attributes_json: destination_attributes,
2662
+ template_args_json: template_args
2663
+ ))
2664
+ ```
2665
+
2666
+ ##### `update_webhook` / `updateWebhook`
2667
+
2668
+ Partially updates a webhook's name, notification email, and/or destination. If `destination_attributes` is supplied without `security_token`, a new token is generated automatically.
2669
+
2670
+ **Parameters**: `id` (required); body — all optional: `name`, `notification_email`, `destination_attributes`. In Ruby, `destination_attributes` is passed as a JSON string under the key `destination_attributes_json`.
2671
+
2672
+ **Returns**: updated `Webhook`.
2673
+
2674
+ ```rust
2675
+ // Rust
2676
+ let params = UpdateWebhookParams {
2677
+ name: Some("Renamed Webhook".to_string()),
2678
+ ..Default::default()
2679
+ };
2680
+ let webhook = qn.webhooks.update_webhook("wh-1", &params).await?;
2681
+ ```
2682
+
2683
+ ```python
2684
+ # Python
2685
+ webhook = await qn.webhooks.update_webhook("wh-1", name="Renamed Webhook")
2686
+ ```
2687
+
2688
+ ```typescript
2689
+ // Node.js
2690
+ const webhook = await qn.webhooks.updateWebhook("wh-1", { name: "Renamed Webhook" });
2691
+ ```
2692
+
2693
+ ```ruby
2694
+ # Ruby
2695
+ webhook = JSON.parse(qn.webhooks.update_webhook(id: "wh-1", name: "Renamed Webhook"))
2696
+ ```
2697
+
2698
+ ##### `update_webhook_template` / `updateWebhookTemplate`
2699
+
2700
+ Updates the template args (and optionally name, email, destination) on an existing template-backed webhook.
2701
+
2702
+ **Parameters**: `webhook_id` (required), `template_args` (required); optional: `name`, `notification_email`, `destination_attributes`.
2703
+
2704
+ **Returns**: updated `Webhook`.
2705
+
2706
+ ```rust
2707
+ // Rust
2708
+ let template_args = TemplateArgs::evm_wallet_filter(&EvmWalletFilterTemplate {
2709
+ wallets: vec!["0xnewwallet".to_string()],
2710
+ })?;
2711
+ let params = UpdateWebhookTemplateParams {
2712
+ name: None,
2713
+ notification_email: None,
2714
+ destination_attributes: None,
2715
+ template_args,
2716
+ };
2717
+ let webhook = qn.webhooks.update_webhook_template("wh-1", &params).await?;
2718
+ ```
2719
+
2720
+ ```python
2721
+ # Python
2722
+ webhook = await qn.webhooks.update_webhook_template(
2723
+ "wh-1",
2724
+ template_args=TemplateArgs.evm_wallet_filter(
2725
+ EvmWalletFilterTemplate(wallets=["0xnewwallet"])
2726
+ ),
2727
+ )
2728
+ ```
2729
+
2730
+ ```typescript
2731
+ // Node.js
2732
+ const webhook = await qn.webhooks.updateWebhookTemplate("wh-1", {
2733
+ templateArgs: TemplateArgs.evmWalletFilter({ wallets: ["0xnewwallet"] }),
2734
+ });
2735
+ ```
2736
+
2737
+ ```ruby
2738
+ # Ruby
2739
+ template_args = JSON.generate({
2740
+ template_id: "evmWalletFilter",
2741
+ value: JSON.generate({ wallets: ["0xnewwallet"] })
2742
+ })
2743
+ webhook = JSON.parse(qn.webhooks.update_webhook_template(
2744
+ webhook_id: "wh-1",
2745
+ template_args_json: template_args
2746
+ ))
2747
+ ```
2748
+
2749
+ ##### `delete_webhook` / `deleteWebhook`
2750
+
2751
+ Deletes a webhook.
2752
+
2753
+ **Parameters**: `id` (required).
2754
+
2755
+ **Returns**: nothing.
2756
+
2757
+ ```rust
2758
+ // Rust
2759
+ qn.webhooks.delete_webhook("wh-1").await?;
2760
+ ```
2761
+
2762
+ ```python
2763
+ # Python
2764
+ await qn.webhooks.delete_webhook("wh-1")
2765
+ ```
2766
+
2767
+ ```typescript
2768
+ // Node.js
2769
+ await qn.webhooks.deleteWebhook("wh-1");
2770
+ ```
2771
+
2772
+ ```ruby
2773
+ # Ruby
2774
+ qn.webhooks.delete_webhook(id: "wh-1")
2775
+ ```
2776
+
2777
+ ##### `delete_all_webhooks` / `deleteAllWebhooks`
2778
+
2779
+ Deletes every webhook on the account. Destructive and takes no arguments.
2780
+
2781
+ **Parameters**: none.
2782
+
2783
+ **Returns**: nothing.
2784
+
2785
+ ```rust
2786
+ // Rust
2787
+ qn.webhooks.delete_all_webhooks().await?;
2788
+ ```
2789
+
2790
+ ```python
2791
+ # Python
2792
+ await qn.webhooks.delete_all_webhooks()
2793
+ ```
2794
+
2795
+ ```typescript
2796
+ // Node.js
2797
+ await qn.webhooks.deleteAllWebhooks();
2798
+ ```
2799
+
2800
+ ```ruby
2801
+ # Ruby
2802
+ qn.webhooks.delete_all_webhooks
2803
+ ```
2804
+
2805
+ ##### `pause_webhook` / `pauseWebhook`
2806
+
2807
+ Pauses a webhook so it stops delivering events.
2808
+
2809
+ **Parameters**: `id` (required).
2810
+
2811
+ **Returns**: nothing.
2812
+
2813
+ ```rust
2814
+ // Rust
2815
+ qn.webhooks.pause_webhook("wh-1").await?;
2816
+ ```
2817
+
2818
+ ```python
2819
+ # Python
2820
+ await qn.webhooks.pause_webhook("wh-1")
2821
+ ```
2822
+
2823
+ ```typescript
2824
+ // Node.js
2825
+ await qn.webhooks.pauseWebhook("wh-1");
2826
+ ```
2827
+
2828
+ ```ruby
2829
+ # Ruby
2830
+ qn.webhooks.pause_webhook(id: "wh-1")
2831
+ ```
2832
+
2833
+ ##### `activate_webhook` / `activateWebhook`
2834
+
2835
+ Activates a paused or new webhook so it resumes delivering events. `start_from` determines where processing resumes.
2836
+
2837
+ **Parameters**: `id` (required), `start_from` (`WebhookStartFrom`, required — `Last` or `Latest`).
2838
+
2839
+ **Returns**: nothing.
2840
+
2841
+ ```rust
2842
+ // Rust
2843
+ let params = ActivateWebhookParams { start_from: WebhookStartFrom::Latest };
2844
+ qn.webhooks.activate_webhook("wh-1", &params).await?;
2845
+ ```
2846
+
2847
+ ```python
2848
+ # Python
2849
+ await qn.webhooks.activate_webhook("wh-1", start_from="latest")
2850
+ ```
2851
+
2852
+ ```typescript
2853
+ // Node.js
2854
+ import { WebhookStartFrom } from "quicknode-sdk";
2855
+
2856
+ await qn.webhooks.activateWebhook("wh-1", { startFrom: WebhookStartFrom.Latest });
2857
+ ```
2858
+
2859
+ ```ruby
2860
+ # Ruby
2861
+ qn.webhooks.activate_webhook(id: "wh-1", start_from: "latest")
2862
+ ```
2863
+
2864
+ ##### `get_enabled_count` / `getEnabledCount`
2865
+
2866
+ Counts currently enabled webhooks.
2867
+
2868
+ **Parameters**: none.
2869
+
2870
+ **Returns**: `WebhookEnabledCountResponse` with `total`.
2871
+
2872
+ ```rust
2873
+ // Rust
2874
+ let resp = qn.webhooks.get_enabled_count().await?;
2875
+ ```
2876
+
2877
+ ```python
2878
+ # Python
2879
+ resp = await qn.webhooks.get_enabled_count()
2880
+ ```
2881
+
2882
+ ```typescript
2883
+ // Node.js
2884
+ const resp = await qn.webhooks.getEnabledCount();
2885
+ ```
2886
+
2887
+ ```ruby
2888
+ # Ruby
2889
+ resp = JSON.parse(qn.webhooks.get_enabled_count)
2890
+ ```
2891
+
2892
+ ---
2893
+
2894
+ ### KV Store Client
2895
+
2896
+ Accessed as `qn.kvstore`. Provides two primitives — **sets** (single string values under a key) and **lists** (ordered collections of strings under a key). Backed by `https://api.quicknode.com/kv/rest/v1/`.
2897
+
2898
+ #### Sets
2899
+
2900
+ ##### `create_set` / `createSet`
2901
+
2902
+ Stores a single string value under a key.
2903
+
2904
+ **Parameters**: `key` (string, required), `value` (string, required).
2905
+
2906
+ **Returns**: nothing.
2907
+
2908
+ ```rust
2909
+ // Rust
2910
+ qn.kvstore.create_set(&CreateSetParams {
2911
+ key: "my-key".to_string(),
2912
+ value: "hello".to_string(),
2913
+ }).await?;
2914
+ ```
2915
+
2916
+ ```python
2917
+ # Python
2918
+ await qn.kvstore.create_set(key="my-key", value="hello")
2919
+ ```
2920
+
2921
+ ```typescript
2922
+ // Node.js
2923
+ await qn.kvstore.createSet({ key: "my-key", value: "hello" });
2924
+ ```
2925
+
2926
+ ```ruby
2927
+ # Ruby
2928
+ qn.kvstore.create_set(key: "my-key", value: "hello")
2929
+ ```
2930
+
2931
+ ##### `get_sets` / `getSets`
2932
+
2933
+ Paginated page of key/value entries.
2934
+
2935
+ **Parameters** (all optional): `limit` (i64), `cursor` (string).
2936
+
2937
+ **Returns**: `GetSetsResponse` — `{ data: KvSetEntry[], cursor: string }`.
2938
+
2939
+ ```rust
2940
+ // Rust
2941
+ let resp = qn.kvstore.get_sets(&Default::default()).await?;
2942
+ ```
2943
+
2944
+ ```python
2945
+ # Python
2946
+ resp = await qn.kvstore.get_sets()
2947
+ ```
2948
+
2949
+ ```typescript
2950
+ // Node.js
2951
+ const resp = await qn.kvstore.getSets();
2952
+ ```
2953
+
2954
+ ```ruby
2955
+ # Ruby
2956
+ resp = JSON.parse(qn.kvstore.get_sets({}))
2957
+ ```
2958
+
2959
+ ##### `get_set` / `getSet`
2960
+
2961
+ Returns the value stored under a key.
2962
+
2963
+ **Parameters**: `key` (string, required).
2964
+
2965
+ **Returns**: `GetSetResponse` with `value`.
2966
+
2967
+ ```rust
2968
+ // Rust
2969
+ let resp = qn.kvstore.get_set("my-key").await?;
2970
+ ```
2971
+
2972
+ ```python
2973
+ # Python
2974
+ resp = await qn.kvstore.get_set("my-key")
2975
+ ```
2976
+
2977
+ ```typescript
2978
+ // Node.js
2979
+ const resp = await qn.kvstore.getSet("my-key");
2980
+ ```
2981
+
2982
+ ```ruby
2983
+ # Ruby
2984
+ resp = JSON.parse(qn.kvstore.get_set(key: "my-key"))
2985
+ ```
2986
+
2987
+ ##### `bulk_sets` / `bulkSets`
2988
+
2989
+ Adds and/or deletes multiple sets in a single request.
2990
+
2991
+ **Parameters** (at least one required): `add_sets` (map<string,string>, optional), `delete_sets` (string[], optional).
2992
+
2993
+ **Returns**: nothing.
2994
+
2995
+ ```rust
2996
+ // Rust
2997
+ use std::collections::HashMap;
2998
+
2999
+ let mut add_sets = HashMap::new();
3000
+ add_sets.insert("k1".to_string(), "v1".to_string());
3001
+ qn.kvstore.bulk_sets(&BulkSetsParams {
3002
+ add_sets: Some(add_sets),
3003
+ delete_sets: Some(vec!["old-key".to_string()]),
3004
+ }).await?;
3005
+ ```
3006
+
3007
+ ```python
3008
+ # Python
3009
+ await qn.kvstore.bulk_sets(
3010
+ add_sets={"k1": "v1"},
3011
+ delete_sets=["old-key"],
3012
+ )
3013
+ ```
3014
+
3015
+ ```typescript
3016
+ // Node.js
3017
+ await qn.kvstore.bulkSets({
3018
+ addSets: { k1: "v1" },
3019
+ deleteSets: ["old-key"],
3020
+ });
3021
+ ```
3022
+
3023
+ ```ruby
3024
+ # Ruby
3025
+ qn.kvstore.bulk_sets(add_sets: { "k1" => "v1" }, delete_sets: ["old-key"])
3026
+ ```
3027
+
3028
+ ##### `delete_set` / `deleteSet`
3029
+
3030
+ Deletes a single set.
3031
+
3032
+ **Parameters**: `key` (string, required).
3033
+
3034
+ **Returns**: nothing.
3035
+
3036
+ ```rust
3037
+ // Rust
3038
+ qn.kvstore.delete_set("my-key").await?;
3039
+ ```
3040
+
3041
+ ```python
3042
+ # Python
3043
+ await qn.kvstore.delete_set("my-key")
3044
+ ```
3045
+
3046
+ ```typescript
3047
+ // Node.js
3048
+ await qn.kvstore.deleteSet("my-key");
3049
+ ```
3050
+
3051
+ ```ruby
3052
+ # Ruby
3053
+ qn.kvstore.delete_set(key: "my-key")
3054
+ ```
3055
+
3056
+ #### Lists
3057
+
3058
+ ##### `create_list` / `createList`
3059
+
3060
+ Creates a list under a key, seeded with the initial items.
3061
+
3062
+ **Parameters**: `key` (string, required), `items` (string[], required).
3063
+
3064
+ **Returns**: nothing.
3065
+
3066
+ ```rust
3067
+ // Rust
3068
+ qn.kvstore.create_list(&CreateListParams {
3069
+ key: "my-list".to_string(),
3070
+ items: vec!["0xabc".to_string(), "0xdef".to_string()],
3071
+ }).await?;
3072
+ ```
3073
+
3074
+ ```python
3075
+ # Python
3076
+ await qn.kvstore.create_list(key="my-list", items=["0xabc", "0xdef"])
3077
+ ```
3078
+
3079
+ ```typescript
3080
+ // Node.js
3081
+ await qn.kvstore.createList({ key: "my-list", items: ["0xabc", "0xdef"] });
3082
+ ```
3083
+
3084
+ ```ruby
3085
+ # Ruby
3086
+ qn.kvstore.create_list(key: "my-list", items: ["0xabc", "0xdef"])
3087
+ ```
3088
+
3089
+ ##### `get_lists` / `getLists`
3090
+
3091
+ Paginated page of list keys.
3092
+
3093
+ **Parameters** (all optional): `limit` (i64), `cursor` (string).
3094
+
3095
+ **Returns**: `GetListsResponse` — `{ data: { keys: string[] }, cursor: string }`.
3096
+
3097
+ ```rust
3098
+ // Rust
3099
+ let resp = qn.kvstore.get_lists(&Default::default()).await?;
3100
+ ```
3101
+
3102
+ ```python
3103
+ # Python
3104
+ resp = await qn.kvstore.get_lists()
3105
+ ```
3106
+
3107
+ ```typescript
3108
+ // Node.js
3109
+ const resp = await qn.kvstore.getLists();
3110
+ ```
3111
+
3112
+ ```ruby
3113
+ # Ruby
3114
+ resp = JSON.parse(qn.kvstore.get_lists({}))
3115
+ ```
3116
+
3117
+ ##### `get_list` / `getList`
3118
+
3119
+ Paginated page of items for a specific list.
3120
+
3121
+ **Parameters**: `key` (string, required); optional `limit` (i64), `cursor` (string).
3122
+
3123
+ **Returns**: `GetListResponse` — `{ data: { items: string[] }, cursor: string }`.
3124
+
3125
+ ```rust
3126
+ // Rust
3127
+ let resp = qn.kvstore.get_list("my-list", &Default::default()).await?;
3128
+ ```
3129
+
3130
+ ```python
3131
+ # Python
3132
+ resp = await qn.kvstore.get_list("my-list")
3133
+ ```
3134
+
3135
+ ```typescript
3136
+ // Node.js
3137
+ const resp = await qn.kvstore.getList("my-list");
3138
+ ```
3139
+
3140
+ ```ruby
3141
+ # Ruby
3142
+ resp = JSON.parse(qn.kvstore.get_list(key: "my-list"))
3143
+ ```
3144
+
3145
+ ##### `update_list` / `updateList`
3146
+
3147
+ Adds and/or removes items in a single operation.
3148
+
3149
+ **Parameters**: `key` (string, required); optional: `add_items` (string[]), `remove_items` (string[]).
3150
+
3151
+ **Returns**: nothing.
3152
+
3153
+ ```rust
3154
+ // Rust
3155
+ qn.kvstore.update_list(
3156
+ "my-list",
3157
+ &UpdateListParams {
3158
+ add_items: Some(vec!["0x456".to_string()]),
3159
+ remove_items: Some(vec!["0xabc".to_string()]),
3160
+ },
3161
+ ).await?;
3162
+ ```
3163
+
3164
+ ```python
3165
+ # Python
3166
+ await qn.kvstore.update_list(
3167
+ "my-list",
3168
+ add_items=["0x456"],
3169
+ remove_items=["0xabc"],
3170
+ )
3171
+ ```
3172
+
3173
+ ```typescript
3174
+ // Node.js
3175
+ await qn.kvstore.updateList("my-list", {
3176
+ addItems: ["0x456"],
3177
+ removeItems: ["0xabc"],
3178
+ });
3179
+ ```
3180
+
3181
+ ```ruby
3182
+ # Ruby
3183
+ qn.kvstore.update_list(key: "my-list", add_items: ["0x456"], remove_items: ["0xabc"])
3184
+ ```
3185
+
3186
+ ##### `add_list_item` / `addListItem`
3187
+
3188
+ Appends a single item to a list.
3189
+
3190
+ **Parameters**: `key` (string, required), `item` (string, required).
3191
+
3192
+ **Returns**: nothing.
3193
+
3194
+ ```rust
3195
+ // Rust
3196
+ qn.kvstore.add_list_item(
3197
+ "my-list",
3198
+ &AddListItemParams { item: "0x123".to_string() },
3199
+ ).await?;
3200
+ ```
3201
+
3202
+ ```python
3203
+ # Python
3204
+ await qn.kvstore.add_list_item("my-list", "0x123")
3205
+ ```
3206
+
3207
+ ```typescript
3208
+ // Node.js
3209
+ await qn.kvstore.addListItem("my-list", { item: "0x123" });
3210
+ ```
3211
+
3212
+ ```ruby
3213
+ # Ruby
3214
+ qn.kvstore.add_list_item(key: "my-list", item: "0x123")
3215
+ ```
3216
+
3217
+ ##### `list_contains_item` / `listContainsItem`
3218
+
3219
+ Checks whether a list contains a specific item.
3220
+
3221
+ **Parameters**: `key` (string, required), `item` (string, required).
3222
+
3223
+ **Returns**: `ListContainsItemResponse` with `exists: bool`.
3224
+
3225
+ ```rust
3226
+ // Rust
3227
+ let resp = qn.kvstore.list_contains_item("my-list", "0x123").await?;
3228
+ ```
3229
+
3230
+ ```python
3231
+ # Python
3232
+ resp = await qn.kvstore.list_contains_item("my-list", "0x123")
3233
+ ```
3234
+
3235
+ ```typescript
3236
+ // Node.js
3237
+ const resp = await qn.kvstore.listContainsItem("my-list", "0x123");
3238
+ ```
3239
+
3240
+ ```ruby
3241
+ # Ruby
3242
+ resp = JSON.parse(qn.kvstore.list_contains_item(key: "my-list", item: "0x123"))
3243
+ ```
3244
+
3245
+ ##### `delete_list_item` / `deleteListItem`
3246
+
3247
+ Removes a single item from a list.
3248
+
3249
+ **Parameters**: `key` (string, required), `item` (string, required).
3250
+
3251
+ **Returns**: nothing.
3252
+
3253
+ ```rust
3254
+ // Rust
3255
+ qn.kvstore.delete_list_item("my-list", "0x123").await?;
3256
+ ```
3257
+
3258
+ ```python
3259
+ # Python
3260
+ await qn.kvstore.delete_list_item("my-list", "0x123")
3261
+ ```
3262
+
3263
+ ```typescript
3264
+ // Node.js
3265
+ await qn.kvstore.deleteListItem("my-list", "0x123");
3266
+ ```
3267
+
3268
+ ```ruby
3269
+ # Ruby
3270
+ qn.kvstore.delete_list_item(key: "my-list", item: "0x123")
3271
+ ```
3272
+
3273
+ ##### `delete_list` / `deleteList`
3274
+
3275
+ Deletes a list and all of its items.
3276
+
3277
+ **Parameters**: `key` (string, required).
3278
+
3279
+ **Returns**: nothing.
3280
+
3281
+ ```rust
3282
+ // Rust
3283
+ qn.kvstore.delete_list("my-list").await?;
3284
+ ```
3285
+
3286
+ ```python
3287
+ # Python
3288
+ await qn.kvstore.delete_list("my-list")
3289
+ ```
3290
+
3291
+ ```typescript
3292
+ // Node.js
3293
+ await qn.kvstore.deleteList("my-list");
3294
+ ```
3295
+
3296
+ ```ruby
3297
+ # Ruby
3298
+ qn.kvstore.delete_list(key: "my-list")
3299
+ ```
3300
+
3301
+ ## Error Handling
3302
+
3303
+ Every binding exposes a typed exception hierarchy derived from the core `SdkError`
3304
+ enum (`crates/core/src/errors.rs`). Catch the base class (`QuickNodeError` /
3305
+ `QuickNodeSdk::Error` / `SdkError`) for any SDK-originated failure, or a specific
3306
+ subclass to branch on transport vs. API semantics.
3307
+
3308
+ | Logical class | When it fires | Extra fields |
3309
+ |----------------------|-------------------------------------------------------------|----------------------|
3310
+ | `QuickNodeError` | base class; catches everything below | — |
3311
+ | `ConfigError` | invalid config or URL surfaced at construction time | — |
3312
+ | `HttpError` | transport failure that isn't a timeout/connect | — |
3313
+ | `TimeoutError` | request timed out (subclass of `HttpError`) | — |
3314
+ | `ConnectionError` | connection refused / DNS / TLS (subclass of `HttpError`) | — |
3315
+ | `ApiError` | non-2xx HTTP response | `status`, `body` |
3316
+ | `DecodeError` | 2xx response but JSON parse failed | `body` |
3317
+
3318
+ Per-language names:
3319
+
3320
+ - **Rust** — pattern-match on `SdkError { Http, Api, Decode, UrlParse, Config }`; use `err.http_kind()` to classify `Http` into `Timeout`, `Connect`, or `Other`.
3321
+ - **Python** — `QuickNodeError`, `ConfigError`, `HttpError`, `TimeoutError`, `ConnectionError`, `ApiError`, `DecodeError` (importable from `sdk`).
3322
+ - **Node.js** — same class names, importable from `@quicknode/sdk`, all extend `Error`.
3323
+ - **Ruby** — `QuickNodeSdk::Error`, `QuickNodeSdk::ConfigError`, `QuickNodeSdk::HttpError`, `QuickNodeSdk::TimeoutError`, `QuickNodeSdk::ConnectionError`, `QuickNodeSdk::ApiError`, `QuickNodeSdk::DecodeError`; all extend `StandardError`. Hash-key validation still raises `ArgumentError`.
3324
+
3325
+ ```rust
3326
+ // Rust
3327
+ match qn.admin.show_endpoint("missing").await {
3328
+ Ok(resp) => println!("{:?}", resp.data),
3329
+ Err(SdkError::Api { status, body }) if status.as_u16() == 404 => {
3330
+ eprintln!("not found: {body}")
3331
+ }
3332
+ Err(e) if matches!(e.http_kind(), Some(HttpKind::Timeout)) => eprintln!("timed out"),
3333
+ Err(e) => eprintln!("other: {e}"),
3334
+ }
3335
+ ```
3336
+
3337
+ ```python
3338
+ # Python
3339
+ from sdk import ApiError, TimeoutError
3340
+ try:
3341
+ await qn.admin.show_endpoint("missing")
3342
+ except ApiError as e:
3343
+ if e.status == 404:
3344
+ print(f"not found: {e.body}")
3345
+ else:
3346
+ raise
3347
+ except TimeoutError:
3348
+ print("timed out")
3349
+ ```
3350
+
3351
+ ```typescript
3352
+ // Node.js
3353
+ import { ApiError, TimeoutError } from "@quicknode/sdk";
3354
+ try {
3355
+ await qn.admin.showEndpoint("missing");
3356
+ } catch (e) {
3357
+ if (e instanceof ApiError && e.status === 404) console.error("not found:", e.body);
3358
+ else if (e instanceof TimeoutError) console.error("timed out");
3359
+ else throw e;
3360
+ }
3361
+ ```
3362
+
3363
+ ```ruby
3364
+ # Ruby
3365
+ begin
3366
+ qn.admin.show_endpoint(id: "missing")
3367
+ rescue QuickNodeSdk::ApiError => e
3368
+ warn "api #{e.status}: #{e.body}" if e.status == 404
3369
+ rescue QuickNodeSdk::TimeoutError
3370
+ warn "timed out"
3371
+ end
3372
+ ```
3373
+
3374
+ ## Development
3375
+
3376
+ ### Prerequisites
3377
+
3378
+ - Rust (stable)
3379
+ - Python 3.8+ with [uv](https://docs.astral.sh/uv/)
3380
+ - Node.js 18+
3381
+ - Ruby 3.0+
3382
+ - [just](https://github.com/casey/just)
3383
+
3384
+ ### Build Commands
3385
+
3386
+ Use the commands in the `Justfile` for the setup and build commands.
3387
+
3388
+ ```bash
3389
+ # Core library
3390
+ cargo check
3391
+ cargo test -p quicknode-sdk
3392
+
3393
+ # Python (from project root)
3394
+ just python-setup-env
3395
+ just python-build
3396
+
3397
+ # Node.js (from npm/)
3398
+ just node-build
3399
+
3400
+ # Ruby
3401
+ just ruby-build
3402
+
3403
+ # Rust
3404
+ cargo build -p quicknode-sdk
3405
+ ```
3406
+
3407
+ ### Testing
3408
+
3409
+ ```bash
3410
+ just test
3411
+ ```
3412
+
3413
+ Runs the Rust unit tests for `quicknode-sdk` using [wiremock](https://github.com/LukeMathWalker/wiremock-rs) to mock HTTP responses — no API key required.
3414
+
3415
+ ### Examples
3416
+
3417
+ ```bash
3418
+ # Rust
3419
+ QN_SDK__API_KEY=replaceme cargo run --example admin -p quicknode-sdk --features rust
3420
+
3421
+ # Python
3422
+ QN_SDK__API_KEY=replaceme uv run python/examples/admin.py
3423
+ QN_SDK__API_KEY=replaceme uv run python/examples/streams.py
3424
+
3425
+ # Node.js
3426
+ cd npm && QN_SDK__API_KEY=replaceme npx tsx examples/admin.ts
3427
+ cd npm && QN_SDK__API_KEY=replaceme npx tsx examples/streams.ts
3428
+
3429
+ # Ruby (build first, then run)
3430
+ just ruby-build
3431
+ QN_SDK__API_KEY=replaceme ruby ruby/examples/admin.rb
3432
+ QN_SDK__API_KEY=replaceme ruby ruby/examples/admin_e2e.rb
3433
+ QN_SDK__API_KEY=replaceme ruby ruby/examples/streams.rb
3434
+ ```
3435
+
3436
+ ### Releasing
3437
+
3438
+ The Rust crate (`quicknode-sdk` on crates.io) versions independently from the Python, Node, and Ruby bindings. Its version lives in `crates/core/Cargo.toml`; the bindings share the workspace version in the root `Cargo.toml`.
3439
+
3440
+ #### Rust crate only (crates.io)
3441
+
3442
+ ```bash
3443
+ # 1. Bump the version in crates/core/Cargo.toml (e.g. 0.1.0 → 0.1.0-alpha.5)
3444
+ # Pre-release identifiers use SemVer 2.0 syntax: MAJOR.MINOR.PATCH-<id>.<N>
3445
+ # Examples: 0.1.0-alpha.4, 0.2.0-beta.1, 0.2.0-rc.1
3446
+
3447
+ # 2. Commit and push
3448
+ git commit -am "chore: release quicknode-sdk 0.1.0-alpha.5"
3449
+ git push
3450
+
3451
+ # 3. Validate the tarball (no upload)
3452
+ cargo publish -p quicknode-sdk --dry-run
3453
+
3454
+ # 4. Publish (requires `cargo login` with a crates.io token)
3455
+ cargo publish -p quicknode-sdk
3456
+ ```
3457
+
3458
+ The first publish claims the `quicknode-sdk` name permanently. Published versions are immutable — you cannot overwrite or delete them (only `cargo yank`, which hides but doesn't remove).
3459
+
3460
+ #### All bindings together (Python / Node / Ruby)
3461
+
3462
+ macOS (Apple Silicon) artifacts are built locally rather than on GitHub Actions to avoid the ~10× runner cost. Linux artifacts are built by CI when a GitHub release is published.
3463
+
3464
+ 1. **Bump versions and commit:**
3465
+ ```bash
3466
+ just release 0.2.0
3467
+ git push
3468
+ ```
3469
+
3470
+ 2. **Create the GitHub release** via the GitHub UI:
3471
+ - **Releases → Draft a new release**.
3472
+ - **Choose a tag** → type `v0.2.0` → **Create new tag on publish**.
3473
+ - Target branch: `main`.
3474
+ - Fill in title and notes (or click **Generate release notes**).
3475
+ - Click **Publish release**.
3476
+
3477
+ This creates + pushes the tag and triggers `.github/workflows/release.yml`, which builds Linux artifacts and attaches them to the release.
3478
+
3479
+ 3. **Build macOS arm64 artifacts locally and append them to the release:**
3480
+ ```bash
3481
+ just macos-build-and-publish 0.2.0
3482
+ ```
3483
+
3484
+ Step 3 requires the [`gh` CLI](https://cli.github.com/) authenticated to the repo. Intel macOS (`x86_64-apple-darwin`) is not shipped — users on Intel Macs install from source.
3485
+
3486
+ `just release` does **not** bump the Rust crate version (that's managed separately in `crates/core/Cargo.toml`). If you want the Rust crate to move in lockstep with a binding release, bump it manually in the same commit.
3487
+
3488
+ #### npm publish (`@quicknode/sdk`)
3489
+
3490
+ The Node package is published to npm as `@quicknode/sdk`. During the 3.x pre-release period, publishes use the `next` dist-tag so `npm install @quicknode/sdk` continues to resolve to the legacy 2.x release while `npm install @quicknode/sdk@next` pulls the rewrite.
3491
+
3492
+ The npm publish uses [napi-rs's multi-package layout](https://napi.rs/docs/deep-dive/release): one main package plus per-platform sub-packages (`@quicknode/sdk-linux-x64-gnu`, `@quicknode/sdk-darwin-arm64`, etc.) declared as `optionalDependencies`. Publishing is triggered manually via a GitHub Actions workflow so the macOS binary (built locally) can be uploaded to the GitHub release before publish.
3493
+
3494
+ Anyone with permission to run the `Publish npm` workflow in this repo can cut a release.
3495
+
3496
+ **Note on versions:** the git tag tracks the overall project version (e.g. `v0.1.0-alpha.5`) and is set in `crates/core/Cargo.toml` / the root `Cargo.toml`. The npm package version is set independently in `npm/package.json` (e.g. `3.0.0-alpha.5`) to stay compatible with the pre-existing `@quicknode/sdk` 2.x series on npm. The two versions do not need to match.
3497
+
3498
+ **Per-release flow:**
3499
+
3500
+ 1. **Bump the npm version** in `npm/package.json` (e.g. `3.0.0-alpha.4` → `3.0.0-alpha.5`), commit, and push to `main`. (Bump the overall project version in `just release <version>` as part of the normal release flow above — this sets the git tag.)
3501
+
3502
+ 2. **Create the GitHub release** via the GitHub UI:
3503
+ - Go to **Releases → Draft a new release**.
3504
+ - Click **Choose a tag**, type the new tag (e.g. `v0.1.0-alpha.5`), and select **Create new tag on publish**.
3505
+ - Target branch: `main`.
3506
+ - Fill in the title and release notes (or click **Generate release notes**).
3507
+ - Click **Publish release**.
3508
+
3509
+ Publishing the release creates + pushes the tag, which triggers `.github/workflows/release.yml`. CI builds the Linux `.node` artifacts and attaches them to the release you just created.
3510
+
3511
+ 3. **Wait for `release.yml` to finish.** Confirm the four Linux `index.*.node` artifacts are attached to the release.
3512
+
3513
+ 4. **Build and upload the macOS arm64 binary** locally (Apple Silicon Mac required):
3514
+ ```bash
3515
+ just node-build
3516
+ gh release upload v0.1.0-alpha.5 npm/index.darwin-arm64.node
3517
+ ```
3518
+
3519
+ 5. **Trigger the publish workflow.** From the GitHub UI: **Actions → Publish npm → Run workflow**, then enter the git tag (`v0.1.0-alpha.5`) and npm dist-tag (`next`). Or via CLI:
3520
+ ```bash
3521
+ gh workflow run publish-npm.yml -f tag=v0.1.0-alpha.5 -f npm_tag=next
3522
+ ```
3523
+
3524
+ 6. **Verify.**
3525
+ ```bash
3526
+ npm view @quicknode/sdk dist-tags
3527
+ # Expected: next: 3.0.0-alpha.5, latest: 2.6.0 (unchanged)
3528
+ ```
3529
+
3530
+ Users can install the pre-release with `npm install @quicknode/sdk@next`.
3531
+
3532
+ ## License
3533
+
3534
+ MIT