@conto_finance/sdk 0.1.0 → 0.4.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/README.md CHANGED
@@ -19,6 +19,11 @@ npm install @conto_finance/sdk
19
19
  An admin SDK key is still an agent SDK key. It keeps the `conto_agent_...` prefix, so it is not a
20
20
  drop-in replacement for `ContoAdmin`.
21
21
 
22
+ ## Verify the connection
23
+
24
+ Requires Node.js 20.19+. Call `await conto.setup()` to inspect your agent, linked wallets,
25
+ effective policies, and scopes without creating a payment.
26
+
22
27
  ## Agent SDK Quick Start
23
28
 
24
29
  ```typescript
@@ -34,14 +39,94 @@ const request = await conto.payments.request({
34
39
  recipientName: 'OpenAI',
35
40
  purpose: 'API credits',
36
41
  category: 'API_PROVIDER',
42
+ // Optional for a single attempt. Supply and reuse your own key when retrying
43
+ // across process restarts or independent job attempts.
44
+ idempotencyKey: crypto.randomUUID(),
37
45
  });
38
46
 
39
47
  if (request.status === 'APPROVED') {
40
48
  const result = await conto.payments.execute(request.requestId);
41
- console.log(result.txHash);
49
+ console.log(result.status);
50
+ console.log(result.receipt.transactionId, result.receipt.settlementMode);
51
+ console.log(result.receipt.txHash);
42
52
  }
43
53
  ```
44
54
 
55
+ ## External-card Policy Overlay
56
+
57
+ The admin client registers only a masked card alias and assigns cooperative
58
+ controls. Never pass PAN, CVC, expiry, or another usable credential to these
59
+ methods.
60
+
61
+ ```typescript
62
+ import { ContoAdmin } from '@conto_finance/sdk';
63
+
64
+ const admin = new ContoAdmin({
65
+ orgApiKey: process.env.CONTO_ORG_API_KEY!,
66
+ });
67
+
68
+ const { card } = await admin.cards.register({
69
+ name: 'Agent purchasing card',
70
+ last4: '4242',
71
+ brand: 'VISA',
72
+ fundingType: 'CREDIT',
73
+ spendLimitDaily: 500,
74
+ spendLimitPerTx: 200,
75
+ });
76
+
77
+ await admin.cards.assignToAgent('agent_id', {
78
+ cardId: card.id,
79
+ timezone: 'America/Los_Angeles',
80
+ spendLimitDaily: 250,
81
+ spendLimitPerTx: 100,
82
+ allowedMerchants: ['Example Software'],
83
+ });
84
+
85
+ await admin.cards.updateAgentAssignment('agent_id', card.id, {
86
+ timezone: 'America/Los_Angeles',
87
+ spendLimitDaily: 250,
88
+ spendLimitPerTx: 100,
89
+ activeUntil: '2026-09-01T00:00:00.000Z',
90
+ });
91
+
92
+ await admin.cards.pauseForAgent('agent_id', card.id);
93
+ await admin.cards.resumeForAgent('agent_id', card.id);
94
+ await admin.cards.revokeFromAgent('agent_id', card.id);
95
+ ```
96
+
97
+ The policy overlay stops at approval, claiming, and reconciliation. For a
98
+ customer-hosted payment tool that gates access to customer payment code, use the
99
+ separate
100
+ [`@conto_finance/card-access`](https://conto.finance/docs/card-access/overview) product
101
+ package.
102
+
103
+ ```typescript
104
+ import { ContoCardAccess } from '@conto_finance/card-access';
105
+
106
+ const cardAccess = new ContoCardAccess({
107
+ policy: conto.cards,
108
+ integrationId: 'procurement-agent-production',
109
+ deployment: {
110
+ credentialCustody: 'CUSTOMER',
111
+ agentHasRawCredentialAccess: false,
112
+ uncontrolledAgentPaymentPath: false,
113
+ },
114
+ executor: customerHostedExecutor,
115
+ });
116
+ ```
117
+
118
+ Card Access claims each approved request exactly once before the executor can run. A
119
+ duplicate or uncertain claim cannot invoke the executor again. It remains
120
+ bypassable if another person or tool can access the card, and results are
121
+ self-reported rather than issuer-verified. The executor receives the normalized
122
+ purchase `idempotencyKey`; pass it to the payment processor so ambiguous
123
+ processor responses cannot create duplicate charges. The legacy `conto.cards.controlled()`
124
+ helper remains available for compatibility, but new customer-hosted enforcement
125
+ should use the standalone Card Access package. Use
126
+ `admin.cards.listRequests({ status: 'EXECUTING' })` for reconciliation and
127
+ `admin.cards.pause(card.id)` to revoke unclaimed grants and stop new approvals
128
+ in the Conto-controlled path.
129
+
45
130
  For a single-call managed-wallet flow, use:
46
131
 
47
132
  ```typescript
@@ -52,6 +137,303 @@ const result = await conto.payments.pay({
52
137
  });
53
138
  ```
54
139
 
140
+ ## Protocol Clients
141
+
142
+ `Conto` also exposes typed helpers for x402 and MPP service payments:
143
+
144
+ ```typescript
145
+ const paymentId = crypto.randomUUID();
146
+
147
+ await conto.x402.preAuthorize({
148
+ amount: 0.01,
149
+ recipientAddress: '0x...',
150
+ resourceUrl: 'https://api.example.com/v1/search',
151
+ paymentId,
152
+ sessionId: 'session_123',
153
+ });
154
+
155
+ await conto.x402.record({
156
+ amount: 0.01,
157
+ recipientAddress: '0x...',
158
+ resourceUrl: 'https://api.example.com/v1/search',
159
+ paymentId,
160
+ txHash: '0x...',
161
+ sessionId: 'session_123',
162
+ });
163
+
164
+ const budget = await conto.x402.budget();
165
+ ```
166
+
167
+ The MPP client mirrors the same shape with `conto.mpp.preAuthorize`, `conto.mpp.record`,
168
+ `conto.mpp.budget`, and `conto.mpp.services`.
169
+
170
+ ## Stripe Link Checkout Commerce
171
+
172
+ For a merchant Conto controls, `LINK_CHECKOUT` evaluates policy before creating an ordinary
173
+ Stripe-hosted Checkout Session with card and Link enabled. It needs no private Link SDK permission,
174
+ Link connection, saved payment-method ID, SPT, or LPT.
175
+
176
+ Commerce execution responses separate the provider-neutral `executionChannel`, optional
177
+ `checkoutMethod`, and optional `settlementRail` from the legacy provider and adapter identifiers.
178
+ For dynamic hosted Checkout, `checkoutMethod` remains absent until settlement observes Link or the
179
+ card fallback; callers must not treat the adapter name as proof that Link was selected.
180
+
181
+ ```typescript
182
+ let { execution } = await conto.beginCommerceExecution({
183
+ executionMethod: 'LINK_CHECKOUT',
184
+ idempotencyKey: crypto.randomUUID(),
185
+ amountMinor: 6400,
186
+ currency: 'USD',
187
+ category: 'OFFICE_SUPPLIES',
188
+ purpose: 'Office supplies',
189
+ counterparty: {
190
+ canonicalId: 'conto-demo-office-supply',
191
+ },
192
+ cart: {
193
+ lineItems: [{ name: 'Office supplies', quantity: 1, unitAmountMinor: 6400 }],
194
+ totals: [{ type: 'total', label: 'Total', amountMinor: 6400 }],
195
+ },
196
+ providerPresentation: {
197
+ approvalSummary: 'Buy office supplies from the configured merchant',
198
+ lineItemLabels: ['Office supplies'],
199
+ },
200
+ });
201
+
202
+ if (execution.status !== 'ACTION_REQUIRED' || !execution.approvalUrl) {
203
+ throw new Error('Stripe Checkout was not created safely');
204
+ }
205
+
206
+ if (execution.executionAdapter !== 'LINK_CHECKOUT') {
207
+ throw new Error('Unexpected commerce execution adapter');
208
+ }
209
+
210
+ // Hand the temporary capability to a trusted interactive browser/UI. Do not log it.
211
+ await presentCheckoutActionSecurely(execution.approvalUrl);
212
+
213
+ while (execution.status === 'ACTION_REQUIRED' || execution.status === 'PROCESSING') {
214
+ ({ execution } = await conto.refreshCommerceExecution(execution.id));
215
+ }
216
+
217
+ if (execution.status !== 'SETTLED' || execution.evidence.actualPaymentMethod !== 'LINK') {
218
+ throw new Error('This execution did not settle with Link');
219
+ }
220
+ ```
221
+
222
+ Stripe decides whether Link appears and whether the customer chooses it. Card is an explicit
223
+ fallback, surfaced as `actualPaymentMethod: 'CARD'`; it is a valid payment but not Link evidence.
224
+ The read-only `getCommerceExecution` projection redacts action URLs; use the initial begin result or
225
+ the `payments:confirm` refresh path while action remains required.
226
+ The server resolves the exact merchant name, Stripe account, and return URLs from configuration, so
227
+ the caller cannot redirect the approved purchase. This path controls only that merchant's Conto
228
+ channel, not unrelated Checkout pages or the consumer's Link wallet. See
229
+ `docs/STRIPE_LINK_CHECKOUT_ADAPTER.md` for configuration, webhook, drill, and reconciliation
230
+ requirements.
231
+
232
+ ## Agent checkout guard
233
+
234
+ For arbitrary third-party merchants, use `runGuardedCheckout` with
235
+ `executionMethod: 'BROWSER_CHECKOUT'`. It binds and atomically authorizes one exact Link, card,
236
+ Shop Pay, or PayPal browser action without requiring merchant or private provider integration. The
237
+ privileged browser executor must be available only through this wrapper; an unrestricted click or
238
+ payment tool would bypass the control. Persist its stable dispatch attempt before calling the helper; it owns authorization,
239
+ one-use claim, the privileged callback, and finalization. See `docs/AGENT_CHECKOUT_GUARD.md` for the
240
+ runtime boundary and example.
241
+
242
+ Isolated browser processes can implement `PrivilegedBrowserCheckoutDriver` and use
243
+ `runGuardedCheckoutWithDriver`; the agent model must receive only that guarded wrapper, never the
244
+ driver's `dispatch` primitive. Generate `action.targetFingerprint` with
245
+ `fingerprintBrowserCheckoutTarget` during inspection, then wrap the privileged target resolver with
246
+ `createVerifiedBrowserCheckoutDriver`. The wrapper re-hashes the current page, locator, accessible
247
+ name, destination, form action, and disabled state immediately before dispatch and refuses any
248
+ changed target without clicking it. Observed link and form destinations must be explicitly bound as
249
+ the action's `targetUrl`, allowing Conto's origin trust policy to evaluate them before the claim.
250
+
251
+ For Browserbase, use the first-class provider host. Its binding store must reject duplicate session
252
+ IDs and atomically revoke only an active record matching the supplied organization, agent, and
253
+ project. Store the credential-free binding durably; never add the provider `connectUrl` to it.
254
+
255
+ ```typescript
256
+ import {
257
+ createBrowserbaseGuardedPlaywrightCheckoutRuntimeHost,
258
+ createBrowserbaseSessionHandoffAssertion,
259
+ } from '@conto_finance/sdk/browserbase';
260
+ import { chromium } from 'playwright-core';
261
+
262
+ const host = createBrowserbaseGuardedPlaywrightCheckoutRuntimeHost({
263
+ conto,
264
+ browserbase: {
265
+ apiKey: process.env.BROWSERBASE_API_KEY!,
266
+ projectId: process.env.BROWSERBASE_PROJECT_ID!,
267
+ organizationId,
268
+ agentId,
269
+ bindingStore: browserbaseSessionBindings,
270
+ contextId: process.env.BROWSERBASE_CONTEXT_ID,
271
+ handoffKey: Buffer.from(process.env.CONTO_BROWSER_HANDOFF_KEY!, 'base64'),
272
+ },
273
+ connectOverCDP: (connectUrl) => chromium.connectOverCDP(connectUrl),
274
+ targetStore: {
275
+ encryptionKey: Buffer.from(process.env.CONTO_BROWSER_TARGET_KEY!, 'base64'),
276
+ blobStore: durableTargetBlobs,
277
+ },
278
+ dispatchAttemptFor: ({ idempotencyKey }) => attempts.loadOrCreate(idempotencyKey),
279
+ token: process.env.CONTO_BROWSER_RUNTIME_TOKEN!,
280
+ });
281
+
282
+ const browser = await host.createSession();
283
+ // Give browser.browserSessionId, never a provider connection URL, to the guarded agent runtime.
284
+
285
+ // Or attach a session created by the customer's trusted Browserbase backend.
286
+ const browserSessionId = customerBrowserbaseSession.id;
287
+ const expiresAt = new Date(Date.now() + 60_000).toISOString();
288
+ const handoffAssertion = createBrowserbaseSessionHandoffAssertion(
289
+ {
290
+ browserSessionId,
291
+ projectId: process.env.BROWSERBASE_PROJECT_ID!,
292
+ organizationId,
293
+ agentId,
294
+ expiresAt,
295
+ },
296
+ Buffer.from(process.env.CONTO_BROWSER_HANDOFF_KEY!, 'base64')
297
+ );
298
+ await host.attachSession({ browserSessionId, handoffAssertion });
299
+ export const POST = host.handler;
300
+ ```
301
+
302
+ The Browserbase host exposes `handler`, `createSession`, `attachSession`, `observeSession`,
303
+ `liveViewForSession`, `releaseSession`, `detachSession`, and `close`. `observeSession` returns only a
304
+ bounded page origin/title and visible
305
+ link/button outline; it excludes full page text, DOM, form values, URL paths and queries,
306
+ screenshots, cookies, storage, and provider capabilities. It rechecks ownership and page identity
307
+ after observing.
308
+ `createSession` returns a session ID, active status, and expiry without a connection URL.
309
+ `liveViewForSession` is a privileged operator capability: expose it only behind separate human
310
+ authentication, never as an agent tool. A configured Context persists login state but must remain
311
+ server-side; its durable binding store should prevent concurrent sessions from sharing one Context.
312
+ `releaseSession` atomically revokes Conto ownership before closing the local connection and asking
313
+ Browserbase to release the keep-alive session. Call it when the agent session ends; `close` only
314
+ closes local Playwright connections. `attachSession` accepts only a five-minute HMAC assertion
315
+ binding the exact provider project, Conto organization, agent, session, and expiry. Its matching
316
+ `detachSession` revokes Conto access and closes the local connection without releasing the
317
+ agent-host-owned provider session. Keep the independent 32-byte handoff key and assertions outside
318
+ the model and browser page.
319
+
320
+ For another Playwright or secure CDP provider, use
321
+ `createGuardedPlaywrightCheckoutRuntimeHost` from `@conto_finance/sdk/playwright`. The generic host
322
+ exposes only `handler`, `observeSession`, `closeSession`, and `close`, keeping discovery, mutation
323
+ primitives, the target store, the Playwright page, and raw dispatch private.
324
+ Lower-level integrations can still compose `createGuardedPlaywrightCheckoutRuntime`, whose façade
325
+ exposes only `discover` and `run`; the raw driver is not returned. Target
326
+ registrations expire after ten minutes by default. The encrypted store persists only ciphertext
327
+ through a small durable blob port, so a runtime restart does not lose an approved
328
+ target or disclose its locator. `dispatchAttemptFor` must atomically return the same persisted ID for
329
+ the same checkout intent. The dispatch result classifies the browser outcome as `NEW_PAGE`,
330
+ `PAGE_NAVIGATION`, or `SAME_PAGE`, but always reports settlement as `UNVERIFIED`; only merchant or
331
+ provider evidence can prove payment. Never expose the page, target store, persisted registrations,
332
+ or provider-derived locator state to the model. The inert positional reference returned by
333
+ `observeSession` is the only locator intended for the model.
334
+
335
+ The generic CDP registry accepts connection URLs only from exact secure origins and rechecks
336
+ `connectUrlForSession` ownership on every lookup. Revocation closes a cached connection. It caches
337
+ one connection per owned session and requires exactly one open page unless the runtime supplies
338
+ an exact `selectPage` callback. Keep Browserbase API keys and `connectUrl` values in the privileged process.
339
+ Call `closeSession` only when the agent session is finished because closing the Playwright browser
340
+ may end the provider session.
341
+
342
+ ### Verify the exclusive checkout path
343
+
344
+ Use `@conto_finance/sdk/checkout-verifier` during agent-host onboarding and whenever its tool
345
+ registry changes. The manifest inventories every capability exposed to the model, identifies the
346
+ session ownership boundary, and names the supported checkout methods. The verifier reports
347
+ `ADVISORY` when an unrestricted payment-capable browser tool remains available and `UNVERIFIED`
348
+ when active probes have not run. It reports `ENFORCEABLE` and `canBypass: false` only after the
349
+ complete inventory is isolated and all denial, replay, tamper, alternate-path, and
350
+ ambiguous-dispatch probes pass.
351
+
352
+ The probe runner belongs to the trusted integration host and must execute against an isolated
353
+ fixture session. A report does not remotely attest an untrusted host or discover tools omitted from
354
+ its inventory. See `docs/CHECKOUT_INTEGRATION_VERIFIER.md` for the manifest, probe, and status
355
+ contract.
356
+
357
+ The HTTP handler implements the authenticated endpoint consumed by the optional
358
+ `guarded_browser_checkout` MCP tool. It accepts Web `Request` objects, so it can be mounted directly
359
+ in a compatible route or adapted to a Node server. It authenticates before reading the body,
360
+ rejects unknown fields and mismatched cart totals, performs discovery and guarded execution in one
361
+ request, and returns only minimized browser evidence. Use a dedicated random token of at least 32
362
+ characters; the handler rejects Conto API keys.
363
+
364
+ Organization-specific proposal facts can be sent through the typed normalization call after an
365
+ owner or administrator registers the namespace schema:
366
+
367
+ ```typescript
368
+ const proposal = await conto.normalizeCommerceProposal({
369
+ proposalKey: 'office-toner-2026-09-02',
370
+ purpose: 'Purchase toner for operations',
371
+ extensions: [
372
+ {
373
+ namespace: 'acme.procurement.v1',
374
+ value: { costCenter: 'OPS', project: 'AP_AUTOMATION' },
375
+ },
376
+ ],
377
+ });
378
+ ```
379
+
380
+ Conto rejects unregistered namespaces and schema-invalid values before policy evaluation. Accepted
381
+ facts are bound to the schema revision and fingerprint returned on the normalized proposal.
382
+
383
+ ## Stripe Link Sandbox Commerce
384
+
385
+ The gated Stripe Link sandbox surface uses the agent key for policy evaluation and execution. A
386
+ Link connection must first be created by its user and explicitly bound to the agent through the
387
+ authenticated session API. The agent SDK never receives the Link access token or payment artifact.
388
+
389
+ ```typescript
390
+ const linkContext = await conto.listStripeLinkPaymentMethods(connectionId);
391
+ if (linkContext.organizationId !== expectedOrganizationId) {
392
+ throw new Error('Agent key belongs to the wrong Conto organization');
393
+ }
394
+ const { paymentMethods } = linkContext;
395
+
396
+ let { execution } = await conto.beginCommerceExecution({
397
+ connectionId,
398
+ paymentMethodId: paymentMethods[0].id,
399
+ executionMethod: 'STRIPE_PAYMENT_INTENT',
400
+ idempotencyKey: crypto.randomUUID(),
401
+ amountMinor: 6400,
402
+ currency: 'USD',
403
+ purpose: 'Office supplies',
404
+ counterparty: {
405
+ canonicalId: 'conto-demo-office-supply',
406
+ displayName: 'Conto Demo Office Supply',
407
+ },
408
+ cart: {
409
+ lineItems: [{ name: 'Office supplies', quantity: 1, unitAmountMinor: 6400 }],
410
+ totals: [{ type: 'total', label: 'Total', amountMinor: 6400 }],
411
+ },
412
+ providerPresentation: {
413
+ approvalSummary: 'Buy office supplies from Conto Demo Office Supply',
414
+ lineItemLabels: ['Office supplies'],
415
+ },
416
+ });
417
+
418
+ if (execution.status === 'AWAITING_APPROVAL') {
419
+ // Present execution.approvalUrl to the Link user, then poll with refresh.
420
+ ({ execution } = await conto.refreshCommerceExecution(execution.id));
421
+ }
422
+
423
+ if (execution.status === 'READY') {
424
+ const result = await conto.executeCommerceExecution(execution.id);
425
+ console.log(result.execution.evidence, result.execution.evidenceFingerprint);
426
+ }
427
+ ```
428
+
429
+ Use `getCommerceExecution(id)` for read-only reconciliation and
430
+ `cancelCommerceExecution(id)` to cancel a nonterminal execution. `beginCommerceExecution` safely
431
+ retries with its stable idempotency key, and `refreshCommerceExecution` safely retries observation.
432
+ Checkout execution and cancellation are not transport-retried.
433
+ The Stripe account and Link business-network profile are resolved from server-owned configuration;
434
+ an agent cannot supply or replace them. Use `executionMethod: 'MPP'` only for the optional,
435
+ configured MPP compatibility transport.
436
+
55
437
  ## Admin SDK Quick Start
56
438
 
57
439
  `ContoAdmin` uses an organization API key (`conto_xxx...`), not an agent SDK key.
@@ -79,19 +461,76 @@ const agent = await admin.agents.create({
79
461
  const wallet = await admin.wallets.create({
80
462
  name: 'Operations Wallet',
81
463
  });
464
+
465
+ const { frozen } = await admin.agents.freeze(agent.id, { freezeWallets: true });
466
+ if (frozen) {
467
+ await admin.agents.unfreeze(agent.id, { unfreezeWallets: true });
468
+ }
469
+ ```
470
+
471
+ ### Stripe Link Checkout readiness
472
+
473
+ Organization administrators can inspect the configured merchant Checkout adapter without calling
474
+ Stripe, then explicitly verify that Conto's configured credential resolves to its configured
475
+ merchant account:
476
+
477
+ ```typescript
478
+ const { readiness } = await admin.paymentRails.getStripeLinkCheckoutReadiness();
479
+
480
+ if (readiness.canVerify && !readiness.checks.accountVerified) {
481
+ const verified = await admin.paymentRails.verifyStripeLinkCheckoutAccount();
482
+ console.log(verified.readiness.status, verified.readiness.verification);
483
+ }
82
484
  ```
83
485
 
486
+ The result contains only mode, booleans, safe status codes, timestamps, and scope statements. It
487
+ never contains merchant/account identifiers, URLs, credentials, hashes, Stripe objects, or raw
488
+ provider errors. Verification proves the configured account match only; Stripe still decides
489
+ whether Link is eligible, displayed, and selected at Checkout. The static GET is retry-safe; the
490
+ active POST verification is not automatically retried.
491
+
84
492
  ## What This Package Covers
85
493
 
86
494
  - `Conto` for agent-scoped payment flows
87
495
  - `ContoAdmin` for organization-scoped provisioning and management
88
- - Built-in retries for `429` and `5xx` responses
496
+ - Built-in retries for reads and idempotency-protected payment authorization requests
89
497
  - Typed request and response shapes for the public SDK surface
90
498
 
499
+ ## Retry and Error Safety
500
+
501
+ The SDK automatically adds an idempotency key to wallet payment authorization
502
+ requests. Provide your own stable `idempotencyKey` when a retry may happen in
503
+ another process or job attempt. External-card approvals always require a stable
504
+ caller-supplied `idempotencyKey`; card approval and confirmation are safe to
505
+ retry. x402 and MPP pre-authorization calls are non-mutating policy checks.
506
+ Record calls require an explicit stable `paymentId` or `credentialId` so an
507
+ ambiguous failure can be reconciled safely.
508
+
509
+ Read-only calls retry transient network failures, `429`, and `5xx` responses. Financial execution
510
+ and other non-idempotent mutations are never retried automatically. If execution times out, call
511
+ `conto.payments.status(requestId)` before attempting it again. A
512
+ `reconciliation_required` status means the execution outcome is ambiguous; do not resend the
513
+ payment until an operator has resolved it.
514
+
515
+ `ContoError` and `ContoAdminError` expose a stable error contract: `code`, `status`, `requestId`,
516
+ `retryAfter`, and locally generated `retryGuidance` where applicable. Arbitrary server details,
517
+ context, hints, and workflow steps are not forwarded through SDK errors:
518
+
519
+ ```typescript
520
+ import { ContoError } from '@conto_finance/sdk';
521
+
522
+ try {
523
+ await conto.payments.status('req_123');
524
+ } catch (error) {
525
+ if (error instanceof ContoError) {
526
+ console.error(error.code, error.requestId, error.retryGuidance);
527
+ }
528
+ }
529
+ ```
530
+
91
531
  ## What To Use Instead
92
532
 
93
- - Use the REST API docs if you need endpoints that are not wrapped by this package yet, especially
94
- external-wallet `approve -> transfer -> confirm` flows.
533
+ - Use `conto.payments.approve()` and `conto.payments.confirm()` for external wallets. Your own signer sends the transfer between those calls. Neither write retries automatically.
95
534
  - Use the MCP server if a tool-calling assistant should operate Conto directly.
96
535
 
97
536
  ## Docs
@@ -105,3 +544,15 @@ const wallet = await admin.wallets.create({
105
544
  ## License
106
545
 
107
546
  MIT
547
+
548
+ Developer support: [support@conto.finance](mailto:support@conto.finance). Include the package version and request ID; never include API keys.
549
+
550
+ ### Connected-card delivery
551
+
552
+ For an organization with connected cards enabled, `conto.connectedCards.draw()` returns a disposable
553
+ credential exactly once. Keep it in memory and use it at the mandate's merchant before
554
+ `credentialExpiresAt`. Never log or persist it. After receipt, call
555
+ `conto.connectedCards.acknowledgeDelivery(draw.requestId)`. Acknowledgement is idempotent and records
556
+ delivery only, not payment settlement. Replaying the original draw key cannot recover a lost
557
+ credential; check the original outcome before requesting a replacement. Issued capacity stays
558
+ consumed even when checkout fails.
@@ -0,0 +1,139 @@
1
+ import { Browser } from 'playwright-core';
2
+ import { GuardedPlaywrightCheckoutHttpHandler, PlaywrightSessionObservation, GuardedPlaywrightCheckoutClient, PlaywrightCdpSessionRegistryConfig, EncryptedPlaywrightCheckoutTargetStoreConfig, GuardedPlaywrightCheckoutRuntimeConfig } from './playwright.mjs';
3
+ import './index.mjs';
4
+
5
+ type BrowserbaseSessionStatus = 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';
6
+ type BrowserbaseSessionBindingState = 'ACTIVE' | 'REVOKED';
7
+ type BrowserbaseSessionLifecycleOwner = 'CONTO_RUNTIME' | 'AGENT_HOST';
8
+ /** Credential-free durable record. Never persist Browserbase connect URLs or API keys here. */
9
+ interface BrowserbaseSessionBinding {
10
+ browserSessionId: string;
11
+ organizationId: string;
12
+ agentId: string;
13
+ projectId: string;
14
+ /** Missing legacy values are treated as CONTO_RUNTIME. */
15
+ lifecycleOwner?: BrowserbaseSessionLifecycleOwner;
16
+ state: BrowserbaseSessionBindingState;
17
+ createdAt: string;
18
+ expiresAt: string;
19
+ }
20
+ /**
21
+ * Durable ownership port for remote sessions. `create` must reject duplicate IDs and `revoke`
22
+ * must atomically transition only an ACTIVE record matching every supplied ownership field.
23
+ */
24
+ interface BrowserbaseSessionBindingStore {
25
+ create(binding: BrowserbaseSessionBinding): Promise<boolean>;
26
+ get(browserSessionId: string): Promise<BrowserbaseSessionBinding | null>;
27
+ revoke(input: {
28
+ browserSessionId: string;
29
+ organizationId: string;
30
+ agentId: string;
31
+ projectId: string;
32
+ revokedAt: string;
33
+ }): Promise<boolean>;
34
+ /** Required with contextId. Release only after Browserbase confirms the provider session closed. */
35
+ releaseContextLease?(input: {
36
+ browserSessionId: string;
37
+ organizationId: string;
38
+ agentId: string;
39
+ projectId: string;
40
+ }): Promise<boolean>;
41
+ }
42
+ interface BrowserbaseSessionConfig {
43
+ /** Browserbase credential retained by the privileged runtime. Conto credentials are rejected. */
44
+ apiKey: string;
45
+ projectId: string;
46
+ organizationId: string;
47
+ agentId: string;
48
+ bindingStore: BrowserbaseSessionBindingStore;
49
+ /** Optional 32-byte server-only key required to attach sessions created by an agent host. */
50
+ handoffKey?: Uint8Array;
51
+ /** Optional server-only Browserbase Context used to persist Link or merchant authentication. */
52
+ contextId?: string;
53
+ /** Browserbase session timeout. Defaults to 900 seconds. */
54
+ timeoutSeconds?: number;
55
+ /** Browserbase API request timeout. Defaults to 10 seconds and may not exceed 60 seconds. */
56
+ requestTimeoutMs?: number;
57
+ /** Injected for tests or runtimes with an instrumented fetch implementation. */
58
+ fetch?: typeof fetch;
59
+ now?: () => Date;
60
+ }
61
+ interface BrowserbaseGuardedPlaywrightCheckoutRuntimeHostConfig {
62
+ conto: GuardedPlaywrightCheckoutClient;
63
+ browserbase: BrowserbaseSessionConfig;
64
+ /** Usually `(url) => chromium.connectOverCDP(url)`. */
65
+ connectOverCDP(connectUrl: string): Promise<Browser>;
66
+ selectPage?: PlaywrightCdpSessionRegistryConfig['selectPage'];
67
+ maxSessions?: number;
68
+ targetStore: EncryptedPlaywrightCheckoutTargetStoreConfig;
69
+ dispatchAttemptFor: GuardedPlaywrightCheckoutRuntimeConfig['dispatchAttemptFor'];
70
+ /** Dedicated service credential. Conto and Browserbase API keys are rejected. */
71
+ token: string;
72
+ maxBodyBytes?: number;
73
+ clickTimeoutMs?: number;
74
+ popupTimeoutMs?: number;
75
+ targetTtlMs?: number;
76
+ now?: () => Date;
77
+ }
78
+ /** Safe session projection for the agent-facing browser launcher. */
79
+ interface BrowserbaseOwnedSession {
80
+ browserSessionId: string;
81
+ status: Extract<BrowserbaseSessionStatus, 'PENDING' | 'RUNNING'>;
82
+ expiresAt: string;
83
+ }
84
+ /** Proof-carrying request from a trusted agent host for an existing Browserbase session. */
85
+ interface BrowserbaseSessionHandoff {
86
+ browserSessionId: string;
87
+ handoffAssertion: string;
88
+ }
89
+ /** Safe projection for a session whose provider lifecycle remains owned by the agent host. */
90
+ interface BrowserbaseAttachedSession extends BrowserbaseOwnedSession {
91
+ lifecycleOwner: 'AGENT_HOST';
92
+ }
93
+ interface BrowserbaseSessionHandoffAssertionInput {
94
+ browserSessionId: string;
95
+ projectId: string;
96
+ organizationId: string;
97
+ agentId: string;
98
+ expiresAt: string;
99
+ }
100
+ /** Operator-only capability for a human authentication or challenge handoff. */
101
+ interface BrowserbaseOwnedLiveView {
102
+ browserSessionId: string;
103
+ liveViewUrl: string;
104
+ expiresAt: string;
105
+ }
106
+ /**
107
+ * Closed Browserbase lifecycle plus guarded checkout façade. It never exposes the provider API
108
+ * key, CDP connection URL, Playwright page, target store, discovery primitive, or raw dispatch.
109
+ */
110
+ interface BrowserbaseGuardedPlaywrightCheckoutRuntimeHost {
111
+ handler: GuardedPlaywrightCheckoutHttpHandler;
112
+ createSession(): Promise<BrowserbaseOwnedSession>;
113
+ /** Attaches an existing agent-host session after verifying a short-lived server assertion. */
114
+ attachSession(input: BrowserbaseSessionHandoff): Promise<BrowserbaseAttachedSession>;
115
+ /** Read-only, bounded projection of the exact runtime-owned page. */
116
+ observeSession(browserSessionId: string): Promise<PlaywrightSessionObservation>;
117
+ /** Returns a short-lived provider capability only to a separately authenticated operator path. */
118
+ liveViewForSession(browserSessionId: string): Promise<BrowserbaseOwnedLiveView>;
119
+ /** Revokes Conto ownership first, closes the local connection, then releases Browserbase. */
120
+ releaseSession(browserSessionId: string): Promise<void>;
121
+ /** Revokes the Conto binding without ending the externally owned Browserbase session. */
122
+ detachSession(browserSessionId: string): Promise<void>;
123
+ /** Closes local Playwright connections. Call releaseSession for each keep-alive provider session. */
124
+ close(): Promise<void>;
125
+ }
126
+ type BrowserbaseSessionErrorCode = 'BROWSERBASE_CONFIG_INVALID' | 'BROWSERBASE_PROVIDER_UNAVAILABLE' | 'BROWSERBASE_PROVIDER_RESPONSE_INVALID' | 'BROWSERBASE_SESSION_BINDING_FAILED' | 'BROWSERBASE_SESSION_HANDOFF_DISABLED' | 'BROWSERBASE_SESSION_HANDOFF_INVALID' | 'BROWSERBASE_LIVE_VIEW_UNAVAILABLE' | 'BROWSERBASE_SESSION_NOT_OWNED' | 'BROWSERBASE_SESSION_RELEASE_FAILED';
127
+ declare class BrowserbaseSessionError extends Error {
128
+ readonly code: BrowserbaseSessionErrorCode;
129
+ constructor(code: BrowserbaseSessionErrorCode, message: string);
130
+ }
131
+ /** Creates a short-lived assertion after the trusted agent host creates its Browserbase session. */
132
+ declare function createBrowserbaseSessionHandoffAssertion(input: BrowserbaseSessionHandoffAssertionInput, keyValue: Uint8Array): string;
133
+ /**
134
+ * Creates the recommended Browserbase deployment boundary. Provider lifecycle, ownership checks,
135
+ * remote connection resolution, guarded execution, and cleanup remain in one privileged object.
136
+ */
137
+ declare function createBrowserbaseGuardedPlaywrightCheckoutRuntimeHost(config: BrowserbaseGuardedPlaywrightCheckoutRuntimeHostConfig): BrowserbaseGuardedPlaywrightCheckoutRuntimeHost;
138
+
139
+ export { type BrowserbaseAttachedSession, type BrowserbaseGuardedPlaywrightCheckoutRuntimeHost, type BrowserbaseGuardedPlaywrightCheckoutRuntimeHostConfig, type BrowserbaseOwnedLiveView, type BrowserbaseOwnedSession, type BrowserbaseSessionBinding, type BrowserbaseSessionBindingState, type BrowserbaseSessionBindingStore, type BrowserbaseSessionConfig, BrowserbaseSessionError, type BrowserbaseSessionErrorCode, type BrowserbaseSessionHandoff, type BrowserbaseSessionHandoffAssertionInput, type BrowserbaseSessionLifecycleOwner, type BrowserbaseSessionStatus, createBrowserbaseGuardedPlaywrightCheckoutRuntimeHost, createBrowserbaseSessionHandoffAssertion };