@tallyforagents/sdk 0.1.0 → 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.ts CHANGED
@@ -14,6 +14,18 @@ declare class TallyClient {
14
14
  request<T>(method: "GET" | "POST" | "DELETE", path: string, body?: unknown): Promise<T>;
15
15
  }
16
16
 
17
+ type AgentWallet = {
18
+ /** Wallet address (lowercased EVM hex) the agent can spend from. */
19
+ address: string;
20
+ /** Friendly name the wallet was given in the dashboard. */
21
+ display_name: string;
22
+ /** Optional role label (e.g., "main", "treasury"); null if unset. */
23
+ role_label: string | null;
24
+ /** Max per-tx spend in USDC as a decimal string. */
25
+ max_per_tx_usdc: string;
26
+ /** Daily cap in USDC as a decimal string, null if uncapped. */
27
+ daily_cap_usdc: string | null;
28
+ };
17
29
  type Agent = {
18
30
  /** Stable user-provided identifier (e.g., "research-bot"). */
19
31
  id: string;
@@ -24,10 +36,18 @@ type Agent = {
24
36
  mode: "test" | "live";
25
37
  /** ISO 8601 timestamp. */
26
38
  created_at: string;
39
+ /** The account slug this agent belongs to. Surfaced so callers can
40
+ * build dashboard deep-links (e.g., when telling the user to grant
41
+ * a permission). */
42
+ account_slug: string;
27
43
  /** Count of non-revoked, activated signers. */
28
44
  active_signers: number;
29
45
  /** Count of non-revoked, not-yet-activated signers. */
30
46
  pending_signers: number;
47
+ /** Wallets this agent has active permission to spend from, with the
48
+ * per-wallet caps from the permission. Empty array if no active
49
+ * grants. Pick one to pass as `wallet` to tally.payments.create(). */
50
+ wallets: AgentWallet[];
31
51
  };
32
52
  type AgentUpsertInput = {
33
53
  /** User-provided stable string identifier (e.g., "research-bot"). */
@@ -45,22 +65,123 @@ declare class AgentsResource {
45
65
  upsert(input: AgentUpsertInput): Promise<Agent>;
46
66
  list(): Promise<Agent[]>;
47
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
+ }>;
48
135
  }
49
136
 
50
137
  type Payment = {
51
138
  id: string;
52
139
  /** "pending" until the on-chain tx confirms; updated by a follow-up
53
- * GET (planned) or webhook (Phase 6). */
140
+ * GET or webhook. */
54
141
  status: "pending" | "confirmed" | "failed";
55
142
  /** Transaction hash on the underlying chain. Set as soon as Privy
56
143
  * accepts the signed RPC; the SDK does not wait for receipt. */
57
144
  tx_hash: string | null;
58
145
  amount_usdc: string;
146
+ /** Destination address. For outbound, this is the external recipient.
147
+ * For inbound, this is one of your wallets. */
59
148
  to: string;
149
+ /** Source address. For outbound, this is your wallet. For inbound,
150
+ * this is the external sender. */
60
151
  from: string;
61
152
  memo: string | null;
62
153
  idempotency_key: string | null;
63
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;
64
185
  };
65
186
  type PaymentCreateInput = {
66
187
  /** Agent's user-provided id (the externalId), e.g. "research-bot". */
@@ -104,6 +225,120 @@ declare class PaymentsResource {
104
225
  * this is the canonical way to wait for `confirmed` / `failed`.
105
226
  */
106
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>;
107
342
  }
108
343
 
109
344
  type WebhookVerifyResult = {
@@ -128,7 +363,44 @@ type WebhookVerifyInput = {
128
363
  * the longer a stolen signature stays replayable. */
129
364
  toleranceSeconds?: number;
130
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
+ };
131
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);
132
404
  /**
133
405
  * Verify a `tally-signature` header against the request body. Use in
134
406
  * your webhook handler before processing the payload.
@@ -147,6 +419,42 @@ declare class WebhooksResource {
147
419
  * ```
148
420
  */
149
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>;
150
458
  }
151
459
  declare function verifySignature(input: WebhookVerifyInput): WebhookVerifyResult;
152
460
 
@@ -188,9 +496,11 @@ declare class ConflictError extends TallyError {
188
496
  declare class Tally {
189
497
  readonly agents: AgentsResource;
190
498
  readonly payments: PaymentsResource;
499
+ readonly permissions: PermissionsResource;
500
+ readonly wallets: WalletsResource;
191
501
  readonly webhooks: WebhooksResource;
192
502
  private readonly client;
193
503
  constructor(opts: ClientOptions);
194
504
  }
195
505
 
196
- export { type Agent, type AgentUpsertInput, 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 };