@neus/sdk 1.1.4 → 1.1.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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # @neus/sdk
2
2
 
3
- Create, check, and reuse NEUS trust receipts from apps and backends. The same package ships the **`neus`** CLI for hosted MCP setup.
3
+ Create, check, and reuse NEUS trust receipts from apps and backends. The same package ships the **`neus`** CLI for assistant setup.
4
4
 
5
- NEUS makes trust portable across apps, agents, and ecosystems before access, payment, or action.
5
+ NEUS is trust infrastructure for apps, agents, and ecosystems.
6
6
 
7
7
  Roadmap: [docs.neus.network/platform/status](https://docs.neus.network/platform/status)
8
8
 
@@ -18,25 +18,26 @@ One command detects your environment and configures hosted MCP for Claude Code,
18
18
 
19
19
  ```bash
20
20
  npx -y -p @neus/sdk neus setup
21
- npx -y -p @neus/sdk neus doctor --live
21
+ npx -y -p @neus/sdk neus check
22
+ npx -y -p @neus/sdk neus examples
22
23
  ```
23
24
 
24
- Open your MCP client and ask the assistant to use NEUS Trust.
25
+ Ask your assistant: **"Use NEUS Verify before taking sensitive actions."**
25
26
 
26
27
  ## MCP docs
27
28
 
28
29
  | Topic | Link |
29
30
  | --------------------------------- | ----------------------------------------------------------------------------- |
30
31
  | Setup, JSON snippets, and headers | [MCP setup](https://docs.neus.network/mcp/setup) |
31
- | Tools and session order | [MCP overview](https://docs.neus.network/mcp/overview) |
32
+ | Reuse-first MCP flow | [MCP overview](https://docs.neus.network/mcp/overview) |
32
33
  | Discovery URLs | [Discovery and endpoints](https://docs.neus.network/mcp/endpoints) |
33
- | Install NEUS Trust | [Install NEUS Trust](https://docs.neus.network/install) |
34
+ | Install NEUS | [Install NEUS](https://docs.neus.network/install) |
34
35
 
35
36
  Prefer `neus setup` over hand-editing config files so every host stays on **`https://mcp.neus.network/mcp`**.
36
37
 
37
38
  ## What you can ship
38
39
 
39
- - Hosted verification flows that return a reusable receipt
40
+ - Hosted verification flows that return reusable receipts
40
41
  - Server checks before access, rewards, payments, or actions
41
42
  - React gates with `VerifyGate`
42
43
  - Agent identity and scoped delegation
@@ -163,14 +164,14 @@ const client = new NeusClient({
163
164
 
164
165
  ```bash
165
166
  npx -y -p @neus/sdk neus setup
166
- npx -y -p @neus/sdk neus doctor --live
167
+ npx -y -p @neus/sdk neus check
167
168
  ```
168
169
 
169
170
  `neus setup` configures MCP and signs you in: `NEUS_ACCESS_KEY` from the environment when set, otherwise the selected host starts OAuth. Cursor, VS Code, and Claude Code use browser sign-in on NEUS. Pass `--access-key <npk_...>` only to override.
170
171
 
171
172
  Codex owns its local MCP OAuth session. Use `npx -y -p @neus/sdk neus setup --client codex`, then `npx -y -p @neus/sdk neus auth --client codex`.
172
173
 
173
- Integrators embedding install UX in apps should import **`@neus/sdk/mcp-hosts`** (setup commands, deeplinks, host labels) instead of duplicating strings.
174
+ Embed install UX with **`@neus/sdk/mcp-hosts`** (setup commands, deeplinks, host labels).
174
175
 
175
176
  Claude Code users can install **`neus-trust@neus`** for the bundled session workflow:
176
177
 
@@ -179,7 +180,7 @@ Claude Code users can install **`neus-trust@neus`** for the bundled session work
179
180
  /plugin install neus-trust@neus
180
181
  ```
181
182
 
182
- Other hosts: [Install NEUS Trust](https://docs.neus.network/install).
183
+ Other hosts: [Install NEUS](https://docs.neus.network/install).
183
184
 
184
185
  ## Docs
185
186
 
package/cjs/client.cjs CHANGED
@@ -468,19 +468,19 @@ var NEUS_CONSTANTS = {
468
468
  // client.js
469
469
  var FALLBACK_PUBLIC_VERIFIER_CATALOG = {
470
470
  "ownership-basic": { supportsDirectApi: true },
471
+ "ownership-social": { supportsDirectApi: false },
471
472
  "ownership-pseudonym": { supportsDirectApi: true },
472
473
  "ownership-dns-txt": { supportsDirectApi: true },
473
- "ownership-social": { supportsDirectApi: false },
474
474
  "ownership-org-oauth": { supportsDirectApi: false },
475
475
  "contract-ownership": { supportsDirectApi: true },
476
+ "proof-of-human": { supportsDirectApi: false },
476
477
  "nft-ownership": { supportsDirectApi: true },
477
478
  "token-holding": { supportsDirectApi: true },
478
- "wallet-link": { supportsDirectApi: true },
479
479
  "wallet-risk": { supportsDirectApi: true },
480
- "proof-of-human": { supportsDirectApi: false },
480
+ "wallet-link": { supportsDirectApi: true },
481
+ "ai-content-moderation": { supportsDirectApi: true },
481
482
  "agent-identity": { supportsDirectApi: true },
482
- "agent-delegation": { supportsDirectApi: true },
483
- "ai-content-moderation": { supportsDirectApi: true }
483
+ "agent-delegation": { supportsDirectApi: true }
484
484
  };
485
485
  var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
486
486
  var WALLET_LINK_RELATIONSHIP_TYPES = /* @__PURE__ */ new Set(["primary", "personal", "org", "affiliate", "agent", "linked"]);
@@ -1530,6 +1530,11 @@ ${bytes.length}`;
1530
1530
  storeOriginalContent: typeof options?.storeOriginalContent === "boolean" ? options.storeOriginalContent : true
1531
1531
  };
1532
1532
  if (typeof options?.enableIpfs === "boolean") optionsPayload.enableIpfs = options.enableIpfs;
1533
+ if (options?.publishToHub === true) {
1534
+ optionsPayload.publishToHub = true;
1535
+ } else {
1536
+ delete optionsPayload.publishToHub;
1537
+ }
1533
1538
  const requestData = {
1534
1539
  verifierIds: normalizedVerifierIds,
1535
1540
  data,
@@ -1750,18 +1755,39 @@ ${bytes.length}`;
1750
1755
  }
1751
1756
  return true;
1752
1757
  }
1758
+ _buildProofsByWalletQuery(options = {}) {
1759
+ const qs = [];
1760
+ if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
1761
+ const cursorRaw = options.cursor !== null && options.cursor !== void 0 ? String(options.cursor).trim() : "";
1762
+ if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
1763
+ else if (options.offset !== void 0 && options.offset !== null) {
1764
+ qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
1765
+ }
1766
+ if (options.q) qs.push(`q=${encodeURIComponent(String(options.q))}`);
1767
+ if (options.qHash) qs.push(`qHash=${encodeURIComponent(String(options.qHash).toLowerCase())}`);
1768
+ if (options.verifierId) qs.push(`verifierId=${encodeURIComponent(String(options.verifierId))}`);
1769
+ if (options.verifierIds) qs.push(`verifierIds=${encodeURIComponent(String(options.verifierIds))}`);
1770
+ if (options.tags) qs.push(`tags=${encodeURIComponent(String(options.tags))}`);
1771
+ if (options.tagPrefix) qs.push(`tagPrefix=${encodeURIComponent(String(options.tagPrefix))}`);
1772
+ if (options.tagContains) qs.push(`tagContains=${encodeURIComponent(String(options.tagContains))}`);
1773
+ if (options.tagPrefixesAll) qs.push(`tagPrefixesAll=${encodeURIComponent(String(options.tagPrefixesAll))}`);
1774
+ if (options.status) qs.push(`status=${encodeURIComponent(String(options.status))}`);
1775
+ if (options.appId) qs.push(`appId=${encodeURIComponent(String(options.appId))}`);
1776
+ if (options.chainCoverage) qs.push(`chainCoverage=${encodeURIComponent(String(options.chainCoverage))}`);
1777
+ if (options.privacyLevel) qs.push(`privacyLevel=${encodeURIComponent(String(options.privacyLevel))}`);
1778
+ if (options.includeHistory) qs.push("includeHistory=1");
1779
+ if (options.includeFacets) qs.push(`includeFacets=${encodeURIComponent(String(options.includeFacets))}`);
1780
+ if (options.visibility) qs.push(`visibility=${encodeURIComponent(String(options.visibility))}`);
1781
+ if (options.isPublicRead) qs.push("isPublicRead=1");
1782
+ return qs;
1783
+ }
1753
1784
  async getProofsByWallet(walletAddress, options = {}) {
1754
1785
  if (!walletAddress || typeof walletAddress !== "string") {
1755
1786
  throw new ValidationError("walletAddress is required");
1756
1787
  }
1757
1788
  const id = walletAddress.trim();
1758
1789
  const pathId = /^0x[a-fA-F0-9]{40}$/i.test(id) ? id.toLowerCase() : id;
1759
- const qs = [];
1760
- if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
1761
- const cursorRaw = options.cursor !== null && options.cursor !== void 0 ? String(options.cursor).trim() : "";
1762
- if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
1763
- else if (options.offset) qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
1764
- if (options.qHash) qs.push(`qHash=${encodeURIComponent(options.qHash.toLowerCase())}`);
1790
+ const qs = this._buildProofsByWalletQuery(options);
1765
1791
  const query = qs.length ? `?${qs.join("&")}` : "";
1766
1792
  const response = await this._makeRequest(
1767
1793
  "GET",
@@ -1774,10 +1800,11 @@ ${bytes.length}`;
1774
1800
  return {
1775
1801
  success: true,
1776
1802
  proofs: Array.isArray(proofs) ? proofs : [],
1777
- totalCount: response.data?.totalCount ?? proofs.length,
1803
+ totalCount: typeof response.data?.totalCount === "number" ? response.data.totalCount : null,
1778
1804
  hasMore: Boolean(response.data?.hasMore),
1779
1805
  nextOffset: response.data?.nextOffset ?? null,
1780
- nextCursor: typeof response.data?.nextCursor === "string" && response.data.nextCursor.trim() ? response.data.nextCursor.trim() : null
1806
+ nextCursor: typeof response.data?.nextCursor === "string" && response.data.nextCursor.trim() ? response.data.nextCursor.trim() : null,
1807
+ facets: response.data?.facets || null
1781
1808
  };
1782
1809
  }
1783
1810
  async getPrivateProofsByWallet(walletAddress, options = {}, wallet = null) {
@@ -1827,12 +1854,7 @@ ${bytes.length}`;
1827
1854
  }
1828
1855
  throw new ValidationError(`Failed to sign message: ${error.message}`);
1829
1856
  }
1830
- const qs = [];
1831
- if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
1832
- const cursorRaw = options.cursor !== null && options.cursor !== void 0 ? String(options.cursor).trim() : "";
1833
- if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
1834
- else if (options.offset) qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
1835
- if (options.qHash) qs.push(`qHash=${encodeURIComponent(options.qHash.toLowerCase())}`);
1857
+ const qs = this._buildProofsByWalletQuery(options);
1836
1858
  const query = qs.length ? `?${qs.join("&")}` : "";
1837
1859
  const response = await this._makeRequest("GET", `/api/v1/proofs/by-wallet/${encodeURIComponent(pathId)}${query}`, null, {
1838
1860
  "x-wallet-address": signerWalletAddress,
@@ -1847,7 +1869,7 @@ ${bytes.length}`;
1847
1869
  return {
1848
1870
  success: true,
1849
1871
  proofs: Array.isArray(proofs) ? proofs : [],
1850
- totalCount: response.data?.totalCount ?? proofs.length,
1872
+ totalCount: typeof response.data?.totalCount === "number" ? response.data.totalCount : null,
1851
1873
  hasMore: Boolean(response.data?.hasMore),
1852
1874
  nextOffset: response.data?.nextOffset ?? null,
1853
1875
  nextCursor: typeof response.data?.nextCursor === "string" && response.data.nextCursor.trim() ? response.data.nextCursor.trim() : null
@@ -1964,6 +1986,64 @@ ${bytes.length}`;
1964
1986
  }
1965
1987
  return response;
1966
1988
  }
1989
+ /**
1990
+ * Get the public snapshot of a published gate: requirements, charge, schedule,
1991
+ * checkout plan, and reward presence. Never returns the secret reward value —
1992
+ * that is delivered post-verify via fulfillGate().
1993
+ *
1994
+ * @param {string} gateId Published gate handle
1995
+ * @returns {Promise<object>} Public gate snapshot
1996
+ */
1997
+ async getGate(gateId) {
1998
+ const id = String(gateId || "").trim();
1999
+ if (!id || id.length > 80 || !/^[a-zA-Z0-9:_-]+$/.test(id)) {
2000
+ throw new ValidationError("Valid gateId is required");
2001
+ }
2002
+ const response = await this._makeRequest("GET", `/api/v1/gates/${encodeURIComponent(id)}`);
2003
+ if (!response.success || !response.data?.gate) {
2004
+ throw new ApiError(`Gate lookup failed: ${response.error?.message || "Gate not found"}`, response.error);
2005
+ }
2006
+ return response.data.gate;
2007
+ }
2008
+ /**
2009
+ * Post-verify reward delivery for hosted gate checkout. Requires a verified
2010
+ * proof (qHash) for the gate; paid gates also require payment evidence
2011
+ * (paymentCheckoutSessionId for card, or paymentTxHash for USDC).
2012
+ *
2013
+ * @param {object} params
2014
+ * @param {string} params.gateId Published gate handle
2015
+ * @param {string} params.qHash Verified proof receipt id
2016
+ * @param {string} [params.walletAddress] Wallet bound to the proof (required without a session cookie)
2017
+ * @param {string} [params.paymentCheckoutSessionId] Stripe checkout session id (card rail)
2018
+ * @param {string} [params.paymentTxHash] USDC payment transaction hash (wallet rail)
2019
+ * @returns {Promise<object>} `{ success, data: { gateId, qHash, fulfillment, successReturnUrl? } }`
2020
+ */
2021
+ async fulfillGate(params = {}) {
2022
+ const gateId = String(params.gateId || "").trim();
2023
+ if (!gateId || gateId.length > 80 || !/^[a-zA-Z0-9:_-]+$/.test(gateId)) {
2024
+ throw new ValidationError("Valid gateId is required");
2025
+ }
2026
+ const qHash = String(params.qHash || "").trim();
2027
+ if (!/^0x[a-fA-F0-9]{64}$/.test(qHash)) {
2028
+ throw new ValidationError("Valid qHash is required");
2029
+ }
2030
+ const body = { qHash };
2031
+ const walletAddress = String(params.walletAddress || "").trim();
2032
+ if (walletAddress) body.walletAddress = walletAddress;
2033
+ const paymentCheckoutSessionId = String(params.paymentCheckoutSessionId || "").trim();
2034
+ if (paymentCheckoutSessionId) body.paymentCheckoutSessionId = paymentCheckoutSessionId;
2035
+ const paymentTxHash = String(params.paymentTxHash || "").trim();
2036
+ if (paymentTxHash) body.paymentTxHash = paymentTxHash;
2037
+ const response = await this._makeRequest(
2038
+ "POST",
2039
+ `/api/v1/gates/${encodeURIComponent(gateId)}/fulfill`,
2040
+ body
2041
+ );
2042
+ if (!response.success) {
2043
+ throw new ApiError(`Gate fulfillment failed: ${response.error?.message || "Unknown error"}`, response.error);
2044
+ }
2045
+ return response;
2046
+ }
1967
2047
  async checkGate(params) {
1968
2048
  const { walletAddress, requirements, proofs: preloadedProofs } = params;
1969
2049
  if (!validateUniversalAddress(walletAddress)) {
package/cjs/index.cjs CHANGED
@@ -1180,19 +1180,19 @@ var init_client = __esm({
1180
1180
  init_utils();
1181
1181
  FALLBACK_PUBLIC_VERIFIER_CATALOG = {
1182
1182
  "ownership-basic": { supportsDirectApi: true },
1183
+ "ownership-social": { supportsDirectApi: false },
1183
1184
  "ownership-pseudonym": { supportsDirectApi: true },
1184
1185
  "ownership-dns-txt": { supportsDirectApi: true },
1185
- "ownership-social": { supportsDirectApi: false },
1186
1186
  "ownership-org-oauth": { supportsDirectApi: false },
1187
1187
  "contract-ownership": { supportsDirectApi: true },
1188
+ "proof-of-human": { supportsDirectApi: false },
1188
1189
  "nft-ownership": { supportsDirectApi: true },
1189
1190
  "token-holding": { supportsDirectApi: true },
1190
- "wallet-link": { supportsDirectApi: true },
1191
1191
  "wallet-risk": { supportsDirectApi: true },
1192
- "proof-of-human": { supportsDirectApi: false },
1192
+ "wallet-link": { supportsDirectApi: true },
1193
+ "ai-content-moderation": { supportsDirectApi: true },
1193
1194
  "agent-identity": { supportsDirectApi: true },
1194
- "agent-delegation": { supportsDirectApi: true },
1195
- "ai-content-moderation": { supportsDirectApi: true }
1195
+ "agent-delegation": { supportsDirectApi: true }
1196
1196
  };
1197
1197
  EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
1198
1198
  WALLET_LINK_RELATIONSHIP_TYPES = /* @__PURE__ */ new Set(["primary", "personal", "org", "affiliate", "agent", "linked"]);
@@ -2223,6 +2223,11 @@ ${bytes.length}`;
2223
2223
  storeOriginalContent: typeof options?.storeOriginalContent === "boolean" ? options.storeOriginalContent : true
2224
2224
  };
2225
2225
  if (typeof options?.enableIpfs === "boolean") optionsPayload.enableIpfs = options.enableIpfs;
2226
+ if (options?.publishToHub === true) {
2227
+ optionsPayload.publishToHub = true;
2228
+ } else {
2229
+ delete optionsPayload.publishToHub;
2230
+ }
2226
2231
  const requestData = {
2227
2232
  verifierIds: normalizedVerifierIds,
2228
2233
  data,
@@ -2443,18 +2448,39 @@ ${bytes.length}`;
2443
2448
  }
2444
2449
  return true;
2445
2450
  }
2451
+ _buildProofsByWalletQuery(options = {}) {
2452
+ const qs = [];
2453
+ if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
2454
+ const cursorRaw = options.cursor !== null && options.cursor !== void 0 ? String(options.cursor).trim() : "";
2455
+ if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
2456
+ else if (options.offset !== void 0 && options.offset !== null) {
2457
+ qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
2458
+ }
2459
+ if (options.q) qs.push(`q=${encodeURIComponent(String(options.q))}`);
2460
+ if (options.qHash) qs.push(`qHash=${encodeURIComponent(String(options.qHash).toLowerCase())}`);
2461
+ if (options.verifierId) qs.push(`verifierId=${encodeURIComponent(String(options.verifierId))}`);
2462
+ if (options.verifierIds) qs.push(`verifierIds=${encodeURIComponent(String(options.verifierIds))}`);
2463
+ if (options.tags) qs.push(`tags=${encodeURIComponent(String(options.tags))}`);
2464
+ if (options.tagPrefix) qs.push(`tagPrefix=${encodeURIComponent(String(options.tagPrefix))}`);
2465
+ if (options.tagContains) qs.push(`tagContains=${encodeURIComponent(String(options.tagContains))}`);
2466
+ if (options.tagPrefixesAll) qs.push(`tagPrefixesAll=${encodeURIComponent(String(options.tagPrefixesAll))}`);
2467
+ if (options.status) qs.push(`status=${encodeURIComponent(String(options.status))}`);
2468
+ if (options.appId) qs.push(`appId=${encodeURIComponent(String(options.appId))}`);
2469
+ if (options.chainCoverage) qs.push(`chainCoverage=${encodeURIComponent(String(options.chainCoverage))}`);
2470
+ if (options.privacyLevel) qs.push(`privacyLevel=${encodeURIComponent(String(options.privacyLevel))}`);
2471
+ if (options.includeHistory) qs.push("includeHistory=1");
2472
+ if (options.includeFacets) qs.push(`includeFacets=${encodeURIComponent(String(options.includeFacets))}`);
2473
+ if (options.visibility) qs.push(`visibility=${encodeURIComponent(String(options.visibility))}`);
2474
+ if (options.isPublicRead) qs.push("isPublicRead=1");
2475
+ return qs;
2476
+ }
2446
2477
  async getProofsByWallet(walletAddress, options = {}) {
2447
2478
  if (!walletAddress || typeof walletAddress !== "string") {
2448
2479
  throw new ValidationError("walletAddress is required");
2449
2480
  }
2450
2481
  const id = walletAddress.trim();
2451
2482
  const pathId = /^0x[a-fA-F0-9]{40}$/i.test(id) ? id.toLowerCase() : id;
2452
- const qs = [];
2453
- if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
2454
- const cursorRaw = options.cursor !== null && options.cursor !== void 0 ? String(options.cursor).trim() : "";
2455
- if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
2456
- else if (options.offset) qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
2457
- if (options.qHash) qs.push(`qHash=${encodeURIComponent(options.qHash.toLowerCase())}`);
2483
+ const qs = this._buildProofsByWalletQuery(options);
2458
2484
  const query = qs.length ? `?${qs.join("&")}` : "";
2459
2485
  const response = await this._makeRequest(
2460
2486
  "GET",
@@ -2467,10 +2493,11 @@ ${bytes.length}`;
2467
2493
  return {
2468
2494
  success: true,
2469
2495
  proofs: Array.isArray(proofs) ? proofs : [],
2470
- totalCount: response.data?.totalCount ?? proofs.length,
2496
+ totalCount: typeof response.data?.totalCount === "number" ? response.data.totalCount : null,
2471
2497
  hasMore: Boolean(response.data?.hasMore),
2472
2498
  nextOffset: response.data?.nextOffset ?? null,
2473
- nextCursor: typeof response.data?.nextCursor === "string" && response.data.nextCursor.trim() ? response.data.nextCursor.trim() : null
2499
+ nextCursor: typeof response.data?.nextCursor === "string" && response.data.nextCursor.trim() ? response.data.nextCursor.trim() : null,
2500
+ facets: response.data?.facets || null
2474
2501
  };
2475
2502
  }
2476
2503
  async getPrivateProofsByWallet(walletAddress, options = {}, wallet = null) {
@@ -2520,12 +2547,7 @@ ${bytes.length}`;
2520
2547
  }
2521
2548
  throw new ValidationError(`Failed to sign message: ${error.message}`);
2522
2549
  }
2523
- const qs = [];
2524
- if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
2525
- const cursorRaw = options.cursor !== null && options.cursor !== void 0 ? String(options.cursor).trim() : "";
2526
- if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
2527
- else if (options.offset) qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
2528
- if (options.qHash) qs.push(`qHash=${encodeURIComponent(options.qHash.toLowerCase())}`);
2550
+ const qs = this._buildProofsByWalletQuery(options);
2529
2551
  const query = qs.length ? `?${qs.join("&")}` : "";
2530
2552
  const response = await this._makeRequest("GET", `/api/v1/proofs/by-wallet/${encodeURIComponent(pathId)}${query}`, null, {
2531
2553
  "x-wallet-address": signerWalletAddress,
@@ -2540,7 +2562,7 @@ ${bytes.length}`;
2540
2562
  return {
2541
2563
  success: true,
2542
2564
  proofs: Array.isArray(proofs) ? proofs : [],
2543
- totalCount: response.data?.totalCount ?? proofs.length,
2565
+ totalCount: typeof response.data?.totalCount === "number" ? response.data.totalCount : null,
2544
2566
  hasMore: Boolean(response.data?.hasMore),
2545
2567
  nextOffset: response.data?.nextOffset ?? null,
2546
2568
  nextCursor: typeof response.data?.nextCursor === "string" && response.data.nextCursor.trim() ? response.data.nextCursor.trim() : null
@@ -2657,6 +2679,64 @@ ${bytes.length}`;
2657
2679
  }
2658
2680
  return response;
2659
2681
  }
2682
+ /**
2683
+ * Get the public snapshot of a published gate: requirements, charge, schedule,
2684
+ * checkout plan, and reward presence. Never returns the secret reward value —
2685
+ * that is delivered post-verify via fulfillGate().
2686
+ *
2687
+ * @param {string} gateId Published gate handle
2688
+ * @returns {Promise<object>} Public gate snapshot
2689
+ */
2690
+ async getGate(gateId) {
2691
+ const id = String(gateId || "").trim();
2692
+ if (!id || id.length > 80 || !/^[a-zA-Z0-9:_-]+$/.test(id)) {
2693
+ throw new ValidationError("Valid gateId is required");
2694
+ }
2695
+ const response = await this._makeRequest("GET", `/api/v1/gates/${encodeURIComponent(id)}`);
2696
+ if (!response.success || !response.data?.gate) {
2697
+ throw new ApiError(`Gate lookup failed: ${response.error?.message || "Gate not found"}`, response.error);
2698
+ }
2699
+ return response.data.gate;
2700
+ }
2701
+ /**
2702
+ * Post-verify reward delivery for hosted gate checkout. Requires a verified
2703
+ * proof (qHash) for the gate; paid gates also require payment evidence
2704
+ * (paymentCheckoutSessionId for card, or paymentTxHash for USDC).
2705
+ *
2706
+ * @param {object} params
2707
+ * @param {string} params.gateId Published gate handle
2708
+ * @param {string} params.qHash Verified proof receipt id
2709
+ * @param {string} [params.walletAddress] Wallet bound to the proof (required without a session cookie)
2710
+ * @param {string} [params.paymentCheckoutSessionId] Stripe checkout session id (card rail)
2711
+ * @param {string} [params.paymentTxHash] USDC payment transaction hash (wallet rail)
2712
+ * @returns {Promise<object>} `{ success, data: { gateId, qHash, fulfillment, successReturnUrl? } }`
2713
+ */
2714
+ async fulfillGate(params = {}) {
2715
+ const gateId = String(params.gateId || "").trim();
2716
+ if (!gateId || gateId.length > 80 || !/^[a-zA-Z0-9:_-]+$/.test(gateId)) {
2717
+ throw new ValidationError("Valid gateId is required");
2718
+ }
2719
+ const qHash = String(params.qHash || "").trim();
2720
+ if (!/^0x[a-fA-F0-9]{64}$/.test(qHash)) {
2721
+ throw new ValidationError("Valid qHash is required");
2722
+ }
2723
+ const body = { qHash };
2724
+ const walletAddress = String(params.walletAddress || "").trim();
2725
+ if (walletAddress) body.walletAddress = walletAddress;
2726
+ const paymentCheckoutSessionId = String(params.paymentCheckoutSessionId || "").trim();
2727
+ if (paymentCheckoutSessionId) body.paymentCheckoutSessionId = paymentCheckoutSessionId;
2728
+ const paymentTxHash = String(params.paymentTxHash || "").trim();
2729
+ if (paymentTxHash) body.paymentTxHash = paymentTxHash;
2730
+ const response = await this._makeRequest(
2731
+ "POST",
2732
+ `/api/v1/gates/${encodeURIComponent(gateId)}/fulfill`,
2733
+ body
2734
+ );
2735
+ if (!response.success) {
2736
+ throw new ApiError(`Gate fulfillment failed: ${response.error?.message || "Unknown error"}`, response.error);
2737
+ }
2738
+ return response;
2739
+ }
2660
2740
  async checkGate(params) {
2661
2741
  const { walletAddress, requirements, proofs: preloadedProofs } = params;
2662
2742
  if (!validateUniversalAddress(walletAddress)) {