@tallyforagents/sdk 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -65,22 +65,123 @@ declare class AgentsResource {
65
65
  upsert(input: AgentUpsertInput): Promise<Agent>;
66
66
  list(): Promise<Agent[]>;
67
67
  get(id: string): Promise<Agent>;
68
+ /**
69
+ * Soft-deletes an agent. The row stays in the DB so historical
70
+ * transactions keep their attribution; future list/get responses
71
+ * hide it.
72
+ *
73
+ * Throws `ConflictError` (HTTP 409, `code: "has_active_grants"`) if
74
+ * the agent has any active permissions — revoke them first.
75
+ *
76
+ * Re-creating an agent with the same `id` via `upsert()` is the
77
+ * supported way to bring a deleted agent back.
78
+ */
79
+ delete(id: string): Promise<void>;
80
+ }
81
+
82
+ interface PageFetcher<T> {
83
+ (cursor: string | null): Promise<{
84
+ data: T[];
85
+ next_cursor: string | null;
86
+ }>;
87
+ }
88
+ declare class AsyncResourcePage<T> implements AsyncIterable<T> {
89
+ private readonly fetchPage;
90
+ constructor(fetchPage: PageFetcher<T>);
91
+ /**
92
+ * Async-iterates every item across all pages. Stops when the server
93
+ * returns `next_cursor: null`.
94
+ *
95
+ * ```ts
96
+ * for await (const p of tally.payments.list({ status: "confirmed" })) {
97
+ * console.log(p.id);
98
+ * }
99
+ * ```
100
+ */
101
+ [Symbol.asyncIterator](): AsyncGenerator<T>;
102
+ /**
103
+ * Collects items into an array, optionally bounded. `toArray()` with
104
+ * no argument pulls every page; `toArray(20)` stops after the first
105
+ * 20 items (across whatever page boundaries that crosses).
106
+ *
107
+ * ```ts
108
+ * const recent = await tally.payments.list().toArray(20);
109
+ * ```
110
+ */
111
+ toArray(maxItems?: number): Promise<T[]>;
112
+ /**
113
+ * Returns just the first page (no auto-pagination). Use when the
114
+ * cursor itself matters — e.g., persisting it across runs to resume
115
+ * iteration later.
116
+ *
117
+ * ```ts
118
+ * const { data, next_cursor } = await tally.payments.list().firstPage();
119
+ * // … store next_cursor; next run: tally.payments.list().pageAfter(saved_cursor)
120
+ * ```
121
+ */
122
+ firstPage(): Promise<{
123
+ data: T[];
124
+ next_cursor: string | null;
125
+ }>;
126
+ /**
127
+ * Returns the page that follows the given cursor. Useful for resuming
128
+ * iteration from a previously-persisted cursor, or for manual paging
129
+ * when the auto-iterator's eager fetching isn't a fit.
130
+ */
131
+ pageAfter(cursor: string): Promise<{
132
+ data: T[];
133
+ next_cursor: string | null;
134
+ }>;
68
135
  }
69
136
 
70
137
  type Payment = {
71
138
  id: string;
72
139
  /** "pending" until the on-chain tx confirms; updated by a follow-up
73
- * GET (planned) or webhook (Phase 6). */
140
+ * GET or webhook. */
74
141
  status: "pending" | "confirmed" | "failed";
75
142
  /** Transaction hash on the underlying chain. Set as soon as Privy
76
143
  * accepts the signed RPC; the SDK does not wait for receipt. */
77
144
  tx_hash: string | null;
78
145
  amount_usdc: string;
146
+ /** Destination address. For outbound, this is the external recipient.
147
+ * For inbound, this is one of your wallets. */
79
148
  to: string;
149
+ /** Source address. For outbound, this is your wallet. For inbound,
150
+ * this is the external sender. */
80
151
  from: string;
81
152
  memo: string | null;
82
153
  idempotency_key: string | null;
83
154
  created_at: string;
155
+ /** "inbound" or "outbound". Populated on list responses; may be
156
+ * undefined on responses from `create()` or `get()` (which always
157
+ * describe outbound payments). */
158
+ direction?: "inbound" | "outbound";
159
+ /** Your wallet's address — always one of YOUR wallets regardless of
160
+ * direction. Same as `from` for outbound, `to` for inbound. */
161
+ wallet_address?: string;
162
+ /** Friendly name of `wallet_address`. */
163
+ wallet_display_name?: string;
164
+ /** The agent's externalId for outbound payments; null for inbound. */
165
+ agent_id?: string | null;
166
+ /** Block timestamp from the chain, set after the tx confirms. */
167
+ block_timestamp?: string | null;
168
+ /** Human-readable reason for `status: "failed"` rows. */
169
+ error_reason?: string | null;
170
+ };
171
+ type PaymentListFilters = {
172
+ /** Filter by payment status. */
173
+ status?: "pending" | "confirmed" | "failed";
174
+ /** Filter by direction. */
175
+ direction?: "inbound" | "outbound";
176
+ /** Filter by agent (external id, the user-provided string). */
177
+ agent_id?: string;
178
+ /** Filter by wallet address (case-insensitive). */
179
+ wallet?: string;
180
+ /** Free-text search over tx hash, addresses, memo. */
181
+ q?: string;
182
+ /** Max items per page. Default 50, max 100. The auto-iterator uses
183
+ * this for each page fetch. */
184
+ limit?: number;
84
185
  };
85
186
  type PaymentCreateInput = {
86
187
  /** Agent's user-provided id (the externalId), e.g. "research-bot". */
@@ -124,6 +225,120 @@ declare class PaymentsResource {
124
225
  * this is the canonical way to wait for `confirmed` / `failed`.
125
226
  */
126
227
  get(id: string): Promise<Payment>;
228
+ /**
229
+ * Lists payments in the API key's account + mode, auto-paginated.
230
+ * Returns an `AsyncResourcePage` you can `for await` over to iterate
231
+ * every payment, or call `.toArray(n)` for a bounded read.
232
+ *
233
+ * Pending outbound rows are lazily refreshed from the chain on read,
234
+ * matching the dashboard's behavior — polling `list({ status: "pending" })`
235
+ * is a valid way to wait for confirmation.
236
+ *
237
+ * ```ts
238
+ * for await (const p of tally.payments.list({ status: "confirmed" })) {
239
+ * console.log(p.id, p.amount_usdc, p.to);
240
+ * }
241
+ *
242
+ * // Or bounded:
243
+ * const last20 = await tally.payments.list({ direction: "outbound" }).toArray(20);
244
+ * ```
245
+ */
246
+ list(filters?: PaymentListFilters): AsyncResourcePage<Payment>;
247
+ }
248
+
249
+ type Permission = {
250
+ /** Stable cuid for this grant. Use as the reference id if/when revoke
251
+ * or update endpoints land. */
252
+ id: string;
253
+ /** Agent's user-provided externalId (e.g., "research-bot"). */
254
+ agent_id: string;
255
+ /** Wallet address this grant is on. */
256
+ wallet_address: string;
257
+ /** Friendly wallet name set at provisioning. */
258
+ wallet_display_name: string;
259
+ /** "active" once the wallet owner has authorized the signer on-chain;
260
+ * "pending" before that. */
261
+ status: "active" | "pending";
262
+ max_per_tx_usdc: string;
263
+ daily_cap_usdc: string | null;
264
+ /** USDC already moved by this grant today (UTC). */
265
+ used_today_usdc: string;
266
+ /** daily_cap_usdc minus used_today_usdc, or null if uncapped. Clamped
267
+ * to 0 when over-spent (shouldn't happen — daily-cap enforcement is
268
+ * pre-broadcast — but defensively non-negative). */
269
+ remaining_today_usdc: string | null;
270
+ /** Whitelisted recipient addresses. Empty = unrestricted. */
271
+ recipient_allowlist: string[];
272
+ /** Whitelisted contract addresses. Defaults to the mode's USDC contract. */
273
+ contract_allowlist: string[];
274
+ /** ISO 8601 timestamp; null if no expiry. */
275
+ expires_at: string | null;
276
+ created_at: string;
277
+ activated_at: string | null;
278
+ };
279
+ type PermissionListFilters = {
280
+ /** Filter by agent (external id). */
281
+ agent_id?: string;
282
+ /** Max items per page. Default 50, max 100. */
283
+ limit?: number;
284
+ };
285
+ declare class PermissionsResource {
286
+ private readonly client;
287
+ constructor(client: TallyClient);
288
+ /**
289
+ * Lists active grants in the API key's account + mode, auto-paginated.
290
+ * Returns an `AsyncResourcePage` you can `for await` over to iterate
291
+ * every permission, or call `.toArray(n)` for a bounded read.
292
+ *
293
+ * Each row includes the granted caps, today's usage, and the rolling
294
+ * remaining-daily-allowance, so agent code can preflight a payment
295
+ * without round-tripping the policy bounds.
296
+ *
297
+ * ```ts
298
+ * for await (const p of tally.permissions.list({ agent_id: "research-bot" })) {
299
+ * console.log(`${p.wallet_display_name}: $${p.remaining_today_usdc} left`);
300
+ * }
301
+ * ```
302
+ */
303
+ list(filters?: PermissionListFilters): AsyncResourcePage<Permission>;
304
+ }
305
+
306
+ type Wallet = {
307
+ /** Stable Tally cuid for this wallet. Pass as `wallet_id` to permission
308
+ * routes (grant / edit / revoke). */
309
+ id: string;
310
+ /** EVM address. Use as `wallet` in `tally.payments.create({ wallet })`. */
311
+ address: string;
312
+ /** Friendly name set when the wallet was created. */
313
+ display_name: string;
314
+ /** Optional role label (e.g., "treasury", "ops"); null if unset. */
315
+ role_label: string | null;
316
+ mode: "test" | "live";
317
+ /** ISO 8601 timestamp. */
318
+ created_at: string;
319
+ };
320
+ type WalletCreateInput = {
321
+ /** Human-friendly name. 1–60 chars after trimming. */
322
+ display_name: string;
323
+ /** Optional role label (1–40 chars after trimming). */
324
+ role_label?: string;
325
+ };
326
+ declare class WalletsResource {
327
+ private readonly client;
328
+ constructor(client: TallyClient);
329
+ /**
330
+ * Lists every wallet in the API key's account + mode. Wallets are
331
+ * returned oldest-first (so the "Main Wallet" auto-provisioned at
332
+ * sign-in shows up first).
333
+ */
334
+ list(): Promise<Wallet[]>;
335
+ /**
336
+ * Provisions a new Privy server wallet in the API key's account + mode.
337
+ * The wallet is owned (in Privy) by the oldest `owner`-role member of
338
+ * the account, so SDK-created wallets behave identically to dashboard-
339
+ * created ones: the same passkey approves any future signer changes.
340
+ */
341
+ create(input: WalletCreateInput): Promise<Wallet>;
127
342
  }
128
343
 
129
344
  type WebhookVerifyResult = {
@@ -148,7 +363,44 @@ type WebhookVerifyInput = {
148
363
  * the longer a stolen signature stays replayable. */
149
364
  toleranceSeconds?: number;
150
365
  };
366
+ type WebhookEventType = "payment.created" | "payment.confirmed" | "payment.failed" | "inbound.received" | "permission.granted" | "permission.updated" | "permission.revoked";
367
+ type Webhook = {
368
+ /** Stable cuid for this endpoint. Pass to `revoke()`. */
369
+ id: string;
370
+ mode: "test" | "live";
371
+ /** Receiver URL Tally POSTs to. */
372
+ url: string;
373
+ /** Event types this endpoint subscribes to. */
374
+ events: string[];
375
+ /** First few characters of the secret (e.g., `whsec_test_abc…`).
376
+ * Useful for identifying the secret in dashboards without
377
+ * exposing the plaintext. */
378
+ secret_prefix: string;
379
+ created_at: string;
380
+ revoked_at: string | null;
381
+ /** ISO timestamp of the last delivery attempt. null until first send. */
382
+ last_delivery_at: string | null;
383
+ };
384
+ type CreatedWebhook = Webhook & {
385
+ /** Plaintext signing secret. Returned exactly once at creation.
386
+ * Store it now — it's NOT recoverable later. */
387
+ secret: string;
388
+ };
389
+ type WebhookCreateInput = {
390
+ /** Receiver URL. Must be https:// (or http://localhost for dev). */
391
+ url: string;
392
+ /** Event types to subscribe to. */
393
+ events: WebhookEventType[];
394
+ };
151
395
  declare class WebhooksResource {
396
+ private readonly client?;
397
+ /**
398
+ * The TallyClient is optional so legacy zero-arg construction
399
+ * (`new WebhooksResource()`) still works for non-class consumers who
400
+ * only use `verifySignature`. List / create / revoke require a
401
+ * client; calling them on a clientless instance throws.
402
+ */
403
+ constructor(client?: TallyClient | undefined);
152
404
  /**
153
405
  * Verify a `tally-signature` header against the request body. Use in
154
406
  * your webhook handler before processing the payload.
@@ -167,6 +419,42 @@ declare class WebhooksResource {
167
419
  * ```
168
420
  */
169
421
  verifySignature(input: WebhookVerifyInput): WebhookVerifyResult;
422
+ private requireClient;
423
+ /**
424
+ * Lists every webhook endpoint in the API key's account + mode.
425
+ * Revoked endpoints are included (with `revoked_at` set) for audit.
426
+ *
427
+ * ```ts
428
+ * for (const w of await tally.webhooks.list()) {
429
+ * console.log(`${w.url} → ${w.events.join(",")}`);
430
+ * }
431
+ * ```
432
+ */
433
+ list(): Promise<Webhook[]>;
434
+ /**
435
+ * Creates a new webhook endpoint. The signing secret is returned in
436
+ * `webhook.secret` **exactly once** — store it now; it's not
437
+ * recoverable later. If you lose it, revoke and recreate.
438
+ *
439
+ * ```ts
440
+ * const webhook = await tally.webhooks.create({
441
+ * url: "https://example.com/tally/events",
442
+ * events: ["payment.confirmed", "payment.failed"],
443
+ * });
444
+ * console.log(webhook.secret); // shown once
445
+ * ```
446
+ */
447
+ create(input: WebhookCreateInput): Promise<CreatedWebhook>;
448
+ /**
449
+ * Revokes a webhook endpoint by id. No further deliveries will be
450
+ * enqueued; deliveries already in-flight are not cancelled.
451
+ * Idempotent: revoking an already-revoked endpoint is a no-op.
452
+ *
453
+ * ```ts
454
+ * await tally.webhooks.revoke("whk_01HXYZ...");
455
+ * ```
456
+ */
457
+ revoke(id: string): Promise<Webhook>;
170
458
  }
171
459
  declare function verifySignature(input: WebhookVerifyInput): WebhookVerifyResult;
172
460
 
@@ -208,9 +496,11 @@ declare class ConflictError extends TallyError {
208
496
  declare class Tally {
209
497
  readonly agents: AgentsResource;
210
498
  readonly payments: PaymentsResource;
499
+ readonly permissions: PermissionsResource;
500
+ readonly wallets: WalletsResource;
211
501
  readonly webhooks: WebhooksResource;
212
502
  private readonly client;
213
503
  constructor(opts: ClientOptions);
214
504
  }
215
505
 
216
- export { type Agent, type AgentUpsertInput, type AgentWallet, AuthenticationError, type ClientOptions, ConflictError, NotFoundError, type Payment, type PaymentCreateInput, RateLimitError, Tally, TallyError, ValidationError, type WebhookVerifyInput, type WebhookVerifyResult, verifySignature };
506
+ export { type Agent, type AgentUpsertInput, type AgentWallet, AsyncResourcePage, AuthenticationError, type ClientOptions, ConflictError, type CreatedWebhook, NotFoundError, type Payment, type PaymentCreateInput, type PaymentListFilters, type Permission, type PermissionListFilters, RateLimitError, Tally, TallyError, ValidationError, type Wallet, type WalletCreateInput, type Webhook, type WebhookCreateInput, type WebhookEventType, type WebhookVerifyInput, type WebhookVerifyResult, verifySignature };
package/dist/index.d.ts CHANGED
@@ -65,22 +65,123 @@ declare class AgentsResource {
65
65
  upsert(input: AgentUpsertInput): Promise<Agent>;
66
66
  list(): Promise<Agent[]>;
67
67
  get(id: string): Promise<Agent>;
68
+ /**
69
+ * Soft-deletes an agent. The row stays in the DB so historical
70
+ * transactions keep their attribution; future list/get responses
71
+ * hide it.
72
+ *
73
+ * Throws `ConflictError` (HTTP 409, `code: "has_active_grants"`) if
74
+ * the agent has any active permissions — revoke them first.
75
+ *
76
+ * Re-creating an agent with the same `id` via `upsert()` is the
77
+ * supported way to bring a deleted agent back.
78
+ */
79
+ delete(id: string): Promise<void>;
80
+ }
81
+
82
+ interface PageFetcher<T> {
83
+ (cursor: string | null): Promise<{
84
+ data: T[];
85
+ next_cursor: string | null;
86
+ }>;
87
+ }
88
+ declare class AsyncResourcePage<T> implements AsyncIterable<T> {
89
+ private readonly fetchPage;
90
+ constructor(fetchPage: PageFetcher<T>);
91
+ /**
92
+ * Async-iterates every item across all pages. Stops when the server
93
+ * returns `next_cursor: null`.
94
+ *
95
+ * ```ts
96
+ * for await (const p of tally.payments.list({ status: "confirmed" })) {
97
+ * console.log(p.id);
98
+ * }
99
+ * ```
100
+ */
101
+ [Symbol.asyncIterator](): AsyncGenerator<T>;
102
+ /**
103
+ * Collects items into an array, optionally bounded. `toArray()` with
104
+ * no argument pulls every page; `toArray(20)` stops after the first
105
+ * 20 items (across whatever page boundaries that crosses).
106
+ *
107
+ * ```ts
108
+ * const recent = await tally.payments.list().toArray(20);
109
+ * ```
110
+ */
111
+ toArray(maxItems?: number): Promise<T[]>;
112
+ /**
113
+ * Returns just the first page (no auto-pagination). Use when the
114
+ * cursor itself matters — e.g., persisting it across runs to resume
115
+ * iteration later.
116
+ *
117
+ * ```ts
118
+ * const { data, next_cursor } = await tally.payments.list().firstPage();
119
+ * // … store next_cursor; next run: tally.payments.list().pageAfter(saved_cursor)
120
+ * ```
121
+ */
122
+ firstPage(): Promise<{
123
+ data: T[];
124
+ next_cursor: string | null;
125
+ }>;
126
+ /**
127
+ * Returns the page that follows the given cursor. Useful for resuming
128
+ * iteration from a previously-persisted cursor, or for manual paging
129
+ * when the auto-iterator's eager fetching isn't a fit.
130
+ */
131
+ pageAfter(cursor: string): Promise<{
132
+ data: T[];
133
+ next_cursor: string | null;
134
+ }>;
68
135
  }
69
136
 
70
137
  type Payment = {
71
138
  id: string;
72
139
  /** "pending" until the on-chain tx confirms; updated by a follow-up
73
- * GET (planned) or webhook (Phase 6). */
140
+ * GET or webhook. */
74
141
  status: "pending" | "confirmed" | "failed";
75
142
  /** Transaction hash on the underlying chain. Set as soon as Privy
76
143
  * accepts the signed RPC; the SDK does not wait for receipt. */
77
144
  tx_hash: string | null;
78
145
  amount_usdc: string;
146
+ /** Destination address. For outbound, this is the external recipient.
147
+ * For inbound, this is one of your wallets. */
79
148
  to: string;
149
+ /** Source address. For outbound, this is your wallet. For inbound,
150
+ * this is the external sender. */
80
151
  from: string;
81
152
  memo: string | null;
82
153
  idempotency_key: string | null;
83
154
  created_at: string;
155
+ /** "inbound" or "outbound". Populated on list responses; may be
156
+ * undefined on responses from `create()` or `get()` (which always
157
+ * describe outbound payments). */
158
+ direction?: "inbound" | "outbound";
159
+ /** Your wallet's address — always one of YOUR wallets regardless of
160
+ * direction. Same as `from` for outbound, `to` for inbound. */
161
+ wallet_address?: string;
162
+ /** Friendly name of `wallet_address`. */
163
+ wallet_display_name?: string;
164
+ /** The agent's externalId for outbound payments; null for inbound. */
165
+ agent_id?: string | null;
166
+ /** Block timestamp from the chain, set after the tx confirms. */
167
+ block_timestamp?: string | null;
168
+ /** Human-readable reason for `status: "failed"` rows. */
169
+ error_reason?: string | null;
170
+ };
171
+ type PaymentListFilters = {
172
+ /** Filter by payment status. */
173
+ status?: "pending" | "confirmed" | "failed";
174
+ /** Filter by direction. */
175
+ direction?: "inbound" | "outbound";
176
+ /** Filter by agent (external id, the user-provided string). */
177
+ agent_id?: string;
178
+ /** Filter by wallet address (case-insensitive). */
179
+ wallet?: string;
180
+ /** Free-text search over tx hash, addresses, memo. */
181
+ q?: string;
182
+ /** Max items per page. Default 50, max 100. The auto-iterator uses
183
+ * this for each page fetch. */
184
+ limit?: number;
84
185
  };
85
186
  type PaymentCreateInput = {
86
187
  /** Agent's user-provided id (the externalId), e.g. "research-bot". */
@@ -124,6 +225,120 @@ declare class PaymentsResource {
124
225
  * this is the canonical way to wait for `confirmed` / `failed`.
125
226
  */
126
227
  get(id: string): Promise<Payment>;
228
+ /**
229
+ * Lists payments in the API key's account + mode, auto-paginated.
230
+ * Returns an `AsyncResourcePage` you can `for await` over to iterate
231
+ * every payment, or call `.toArray(n)` for a bounded read.
232
+ *
233
+ * Pending outbound rows are lazily refreshed from the chain on read,
234
+ * matching the dashboard's behavior — polling `list({ status: "pending" })`
235
+ * is a valid way to wait for confirmation.
236
+ *
237
+ * ```ts
238
+ * for await (const p of tally.payments.list({ status: "confirmed" })) {
239
+ * console.log(p.id, p.amount_usdc, p.to);
240
+ * }
241
+ *
242
+ * // Or bounded:
243
+ * const last20 = await tally.payments.list({ direction: "outbound" }).toArray(20);
244
+ * ```
245
+ */
246
+ list(filters?: PaymentListFilters): AsyncResourcePage<Payment>;
247
+ }
248
+
249
+ type Permission = {
250
+ /** Stable cuid for this grant. Use as the reference id if/when revoke
251
+ * or update endpoints land. */
252
+ id: string;
253
+ /** Agent's user-provided externalId (e.g., "research-bot"). */
254
+ agent_id: string;
255
+ /** Wallet address this grant is on. */
256
+ wallet_address: string;
257
+ /** Friendly wallet name set at provisioning. */
258
+ wallet_display_name: string;
259
+ /** "active" once the wallet owner has authorized the signer on-chain;
260
+ * "pending" before that. */
261
+ status: "active" | "pending";
262
+ max_per_tx_usdc: string;
263
+ daily_cap_usdc: string | null;
264
+ /** USDC already moved by this grant today (UTC). */
265
+ used_today_usdc: string;
266
+ /** daily_cap_usdc minus used_today_usdc, or null if uncapped. Clamped
267
+ * to 0 when over-spent (shouldn't happen — daily-cap enforcement is
268
+ * pre-broadcast — but defensively non-negative). */
269
+ remaining_today_usdc: string | null;
270
+ /** Whitelisted recipient addresses. Empty = unrestricted. */
271
+ recipient_allowlist: string[];
272
+ /** Whitelisted contract addresses. Defaults to the mode's USDC contract. */
273
+ contract_allowlist: string[];
274
+ /** ISO 8601 timestamp; null if no expiry. */
275
+ expires_at: string | null;
276
+ created_at: string;
277
+ activated_at: string | null;
278
+ };
279
+ type PermissionListFilters = {
280
+ /** Filter by agent (external id). */
281
+ agent_id?: string;
282
+ /** Max items per page. Default 50, max 100. */
283
+ limit?: number;
284
+ };
285
+ declare class PermissionsResource {
286
+ private readonly client;
287
+ constructor(client: TallyClient);
288
+ /**
289
+ * Lists active grants in the API key's account + mode, auto-paginated.
290
+ * Returns an `AsyncResourcePage` you can `for await` over to iterate
291
+ * every permission, or call `.toArray(n)` for a bounded read.
292
+ *
293
+ * Each row includes the granted caps, today's usage, and the rolling
294
+ * remaining-daily-allowance, so agent code can preflight a payment
295
+ * without round-tripping the policy bounds.
296
+ *
297
+ * ```ts
298
+ * for await (const p of tally.permissions.list({ agent_id: "research-bot" })) {
299
+ * console.log(`${p.wallet_display_name}: $${p.remaining_today_usdc} left`);
300
+ * }
301
+ * ```
302
+ */
303
+ list(filters?: PermissionListFilters): AsyncResourcePage<Permission>;
304
+ }
305
+
306
+ type Wallet = {
307
+ /** Stable Tally cuid for this wallet. Pass as `wallet_id` to permission
308
+ * routes (grant / edit / revoke). */
309
+ id: string;
310
+ /** EVM address. Use as `wallet` in `tally.payments.create({ wallet })`. */
311
+ address: string;
312
+ /** Friendly name set when the wallet was created. */
313
+ display_name: string;
314
+ /** Optional role label (e.g., "treasury", "ops"); null if unset. */
315
+ role_label: string | null;
316
+ mode: "test" | "live";
317
+ /** ISO 8601 timestamp. */
318
+ created_at: string;
319
+ };
320
+ type WalletCreateInput = {
321
+ /** Human-friendly name. 1–60 chars after trimming. */
322
+ display_name: string;
323
+ /** Optional role label (1–40 chars after trimming). */
324
+ role_label?: string;
325
+ };
326
+ declare class WalletsResource {
327
+ private readonly client;
328
+ constructor(client: TallyClient);
329
+ /**
330
+ * Lists every wallet in the API key's account + mode. Wallets are
331
+ * returned oldest-first (so the "Main Wallet" auto-provisioned at
332
+ * sign-in shows up first).
333
+ */
334
+ list(): Promise<Wallet[]>;
335
+ /**
336
+ * Provisions a new Privy server wallet in the API key's account + mode.
337
+ * The wallet is owned (in Privy) by the oldest `owner`-role member of
338
+ * the account, so SDK-created wallets behave identically to dashboard-
339
+ * created ones: the same passkey approves any future signer changes.
340
+ */
341
+ create(input: WalletCreateInput): Promise<Wallet>;
127
342
  }
128
343
 
129
344
  type WebhookVerifyResult = {
@@ -148,7 +363,44 @@ type WebhookVerifyInput = {
148
363
  * the longer a stolen signature stays replayable. */
149
364
  toleranceSeconds?: number;
150
365
  };
366
+ type WebhookEventType = "payment.created" | "payment.confirmed" | "payment.failed" | "inbound.received" | "permission.granted" | "permission.updated" | "permission.revoked";
367
+ type Webhook = {
368
+ /** Stable cuid for this endpoint. Pass to `revoke()`. */
369
+ id: string;
370
+ mode: "test" | "live";
371
+ /** Receiver URL Tally POSTs to. */
372
+ url: string;
373
+ /** Event types this endpoint subscribes to. */
374
+ events: string[];
375
+ /** First few characters of the secret (e.g., `whsec_test_abc…`).
376
+ * Useful for identifying the secret in dashboards without
377
+ * exposing the plaintext. */
378
+ secret_prefix: string;
379
+ created_at: string;
380
+ revoked_at: string | null;
381
+ /** ISO timestamp of the last delivery attempt. null until first send. */
382
+ last_delivery_at: string | null;
383
+ };
384
+ type CreatedWebhook = Webhook & {
385
+ /** Plaintext signing secret. Returned exactly once at creation.
386
+ * Store it now — it's NOT recoverable later. */
387
+ secret: string;
388
+ };
389
+ type WebhookCreateInput = {
390
+ /** Receiver URL. Must be https:// (or http://localhost for dev). */
391
+ url: string;
392
+ /** Event types to subscribe to. */
393
+ events: WebhookEventType[];
394
+ };
151
395
  declare class WebhooksResource {
396
+ private readonly client?;
397
+ /**
398
+ * The TallyClient is optional so legacy zero-arg construction
399
+ * (`new WebhooksResource()`) still works for non-class consumers who
400
+ * only use `verifySignature`. List / create / revoke require a
401
+ * client; calling them on a clientless instance throws.
402
+ */
403
+ constructor(client?: TallyClient | undefined);
152
404
  /**
153
405
  * Verify a `tally-signature` header against the request body. Use in
154
406
  * your webhook handler before processing the payload.
@@ -167,6 +419,42 @@ declare class WebhooksResource {
167
419
  * ```
168
420
  */
169
421
  verifySignature(input: WebhookVerifyInput): WebhookVerifyResult;
422
+ private requireClient;
423
+ /**
424
+ * Lists every webhook endpoint in the API key's account + mode.
425
+ * Revoked endpoints are included (with `revoked_at` set) for audit.
426
+ *
427
+ * ```ts
428
+ * for (const w of await tally.webhooks.list()) {
429
+ * console.log(`${w.url} → ${w.events.join(",")}`);
430
+ * }
431
+ * ```
432
+ */
433
+ list(): Promise<Webhook[]>;
434
+ /**
435
+ * Creates a new webhook endpoint. The signing secret is returned in
436
+ * `webhook.secret` **exactly once** — store it now; it's not
437
+ * recoverable later. If you lose it, revoke and recreate.
438
+ *
439
+ * ```ts
440
+ * const webhook = await tally.webhooks.create({
441
+ * url: "https://example.com/tally/events",
442
+ * events: ["payment.confirmed", "payment.failed"],
443
+ * });
444
+ * console.log(webhook.secret); // shown once
445
+ * ```
446
+ */
447
+ create(input: WebhookCreateInput): Promise<CreatedWebhook>;
448
+ /**
449
+ * Revokes a webhook endpoint by id. No further deliveries will be
450
+ * enqueued; deliveries already in-flight are not cancelled.
451
+ * Idempotent: revoking an already-revoked endpoint is a no-op.
452
+ *
453
+ * ```ts
454
+ * await tally.webhooks.revoke("whk_01HXYZ...");
455
+ * ```
456
+ */
457
+ revoke(id: string): Promise<Webhook>;
170
458
  }
171
459
  declare function verifySignature(input: WebhookVerifyInput): WebhookVerifyResult;
172
460
 
@@ -208,9 +496,11 @@ declare class ConflictError extends TallyError {
208
496
  declare class Tally {
209
497
  readonly agents: AgentsResource;
210
498
  readonly payments: PaymentsResource;
499
+ readonly permissions: PermissionsResource;
500
+ readonly wallets: WalletsResource;
211
501
  readonly webhooks: WebhooksResource;
212
502
  private readonly client;
213
503
  constructor(opts: ClientOptions);
214
504
  }
215
505
 
216
- export { type Agent, type AgentUpsertInput, type AgentWallet, AuthenticationError, type ClientOptions, ConflictError, NotFoundError, type Payment, type PaymentCreateInput, RateLimitError, Tally, TallyError, ValidationError, type WebhookVerifyInput, type WebhookVerifyResult, verifySignature };
506
+ export { type Agent, type AgentUpsertInput, type AgentWallet, AsyncResourcePage, AuthenticationError, type ClientOptions, ConflictError, type CreatedWebhook, NotFoundError, type Payment, type PaymentCreateInput, type PaymentListFilters, type Permission, type PermissionListFilters, RateLimitError, Tally, TallyError, ValidationError, type Wallet, type WalletCreateInput, type Webhook, type WebhookCreateInput, type WebhookEventType, type WebhookVerifyInput, type WebhookVerifyResult, verifySignature };