@tallyforagents/sdk 0.1.1 → 0.3.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
@@ -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,9 +419,149 @@ 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
 
461
+ /** Shape of a single offer inside an x402 402 response's `accepts[]`. */
462
+ type X402PaymentTerms = {
463
+ scheme: string;
464
+ network: string;
465
+ /** Atomic USDC amount (micro-units, 6 decimals). String to preserve
466
+ * precision over JSON. */
467
+ maxAmountRequired: string;
468
+ resource: string;
469
+ description?: string;
470
+ payTo: string;
471
+ asset: string;
472
+ /** Optional metadata the server may attach. */
473
+ extra?: Record<string, unknown>;
474
+ };
475
+ /** Shape of an x402 402 response body. */
476
+ type X402Response = {
477
+ x402Version: number;
478
+ error?: string;
479
+ accepts?: X402PaymentTerms[];
480
+ };
481
+ type X402FetchInput = {
482
+ /** Agent's externalId. The agent must have an active permission
483
+ * grant on `wallet` with enough headroom to cover the x402 charge. */
484
+ agent_id: string;
485
+ /** Sender wallet address. Typically `agent.wallets[0].address` from
486
+ * the response to `tally.agents.upsert()`. */
487
+ wallet: string;
488
+ /** HTTP method for the request. Defaults to "GET". */
489
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
490
+ /** Extra request headers. The SDK overrides `x-payment` on the retry
491
+ * but everything else passes through. */
492
+ headers?: Record<string, string>;
493
+ /** Request body. Passed through to fetch on both the initial call
494
+ * and the retry; the SDK does not modify it. */
495
+ body?: BodyInit | null;
496
+ /** Client-side cap on what the SDK will spend on this call. If the
497
+ * 402 asks for more than this, the SDK throws before paying. The
498
+ * agent's policy caps remain authoritative server-side; this is a
499
+ * per-call belt to the policy's suspenders. Decimal USDC, e.g. "1.00". */
500
+ max_amount_usdc?: string;
501
+ /** Memo stored on the Tally Payment row. Defaults to
502
+ * `x402:${host}${pathname}`. */
503
+ memo?: string;
504
+ /** Idempotency key for the underlying `payments.create` call. If you
505
+ * may retry the same logical x402 call, set this to a stable string
506
+ * so a retry returns the original payment instead of double-spending.
507
+ * Defaults to a fresh per-call key. */
508
+ idempotency_key?: string;
509
+ /** Per-call timeout in ms for the underlying fetches. Default: no
510
+ * timeout (the runtime's default applies). */
511
+ timeout_ms?: number;
512
+ };
513
+ type X402PaymentReceipt = {
514
+ /** Tally Payment id (e.g. `pay_…`). */
515
+ id: string;
516
+ /** On-chain transaction hash. */
517
+ tx_hash: string;
518
+ /** Decimal USDC amount paid, e.g. "0.05". */
519
+ amount_usdc: string;
520
+ /** Recipient address from the 402 terms. */
521
+ to: string;
522
+ /** Network the payment landed on. */
523
+ network: string;
524
+ /** Memo stored on the Tally Payment row. */
525
+ memo: string | null;
526
+ };
527
+ type X402FetchResult = {
528
+ /** The final HTTP response. If the first call returned 200, this is
529
+ * that response. Otherwise it's the post-payment retry response.
530
+ * Check `response.ok` / `response.status` as you would with any
531
+ * fetch — the SDK does not throw for non-200s after a successful
532
+ * payment, because some x402 services return 4xx/5xx legitimately. */
533
+ response: Response;
534
+ /** Receipt for the payment the SDK made on your behalf. Null when
535
+ * the first call returned 200 (no payment was needed). */
536
+ payment: X402PaymentReceipt | null;
537
+ };
538
+ declare class X402Resource {
539
+ #private;
540
+ constructor(client: TallyClient);
541
+ /**
542
+ * Fetch a URL that may be paywalled by the x402 protocol. If the
543
+ * service returns 402, the SDK pays via Tally and retries
544
+ * automatically. The agent code sees one call.
545
+ *
546
+ * ```ts
547
+ * const { response, payment } = await tally.x402.fetch(
548
+ * "https://api.example.com/weather?city=Tokyo",
549
+ * { agent_id: "hermes", wallet: agent.wallets[0].address }
550
+ * );
551
+ *
552
+ * if (response.ok) {
553
+ * const data = await response.json();
554
+ * if (payment) console.log(`paid ${payment.amount_usdc} USDC — ${payment.tx_hash}`);
555
+ * }
556
+ * ```
557
+ *
558
+ * Throws `TallyError` for SDK-side problems (network unreachable,
559
+ * malformed 402 body, unsupported network, payment refused by
560
+ * Tally, etc.). Lets non-200 retry responses pass through as-is.
561
+ */
562
+ fetch(url: string, input: X402FetchInput): Promise<X402FetchResult>;
563
+ }
564
+
173
565
  type ApiErrorPayload = {
174
566
  type: string;
175
567
  message: string;
@@ -208,9 +600,12 @@ declare class ConflictError extends TallyError {
208
600
  declare class Tally {
209
601
  readonly agents: AgentsResource;
210
602
  readonly payments: PaymentsResource;
603
+ readonly permissions: PermissionsResource;
604
+ readonly wallets: WalletsResource;
211
605
  readonly webhooks: WebhooksResource;
606
+ readonly x402: X402Resource;
212
607
  private readonly client;
213
608
  constructor(opts: ClientOptions);
214
609
  }
215
610
 
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 };
611
+ 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, type X402FetchInput, type X402FetchResult, type X402PaymentReceipt, type X402PaymentTerms, type X402Response, verifySignature };