@conto_finance/sdk 0.6.0-beta.2 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @conto_finance/sdk
2
2
 
3
- Conto is a buyer-side control layer. Integrate it in the agent owner's runtime or payment tools;
4
- the owner or budget holder sets policy before the agent pays.
3
+ Conto is a buyer-side control layer. Integrate it in the agent owner's runtime or payment tools.
4
+ The owner or budget holder sets policy before the agent pays.
5
5
  See [buyer-side control](https://conto.finance/docs/introduction/concepts) for the authorization
6
6
  and execution boundary.
7
7
 
@@ -66,16 +66,20 @@ Use `conto.checkout` when your agent runs its own checkout. Call Conto right bef
66
66
  method opens and again right before the payment is submitted. Conto checks the agent's rules at
67
67
  both points and holds the spend in between. Your checkout must stop whenever a call fails.
68
68
 
69
- Checkout checkpoints need SDK 0.6.0-beta.2 or later: `npm install @conto_finance/sdk@beta`.
69
+ Checkout checkpoints need SDK 0.6.0 or later.
70
70
 
71
71
  ```typescript
72
- const checkpoint = await conto.checkout.begin({
72
+ let checkpoint = await conto.checkout.begin({
73
73
  idempotencyKey: order.id,
74
74
  merchant: { id: 'acme-retail', name: 'ACME Retail', checkoutUrl: page.url() },
75
75
  amountMinor: 7900,
76
76
  purpose: 'Drip coffee maker the shopper chose',
77
77
  lineItems: [{ name: 'Drip coffee maker', quantity: 1, unitAmountMinor: 7900 }],
78
78
  });
79
+ if (checkpoint.stage === 'AWAITING_APPROVAL') {
80
+ // The agent's rules need the owner. Throws if the owner rejects or the approval expires.
81
+ checkpoint = await conto.checkout.waitForApproval(checkpoint, { timeoutMs: 15 * 60_000 });
82
+ }
79
83
  // Open Link. The buyer signs in and the final total appears.
80
84
 
81
85
  const confirmed = await conto.checkout.confirm(checkpoint, {
@@ -87,8 +91,17 @@ const confirmed = await conto.checkout.confirm(checkpoint, {
87
91
  await conto.checkout.reportOutcome(confirmed.id, { outcome: 'PAID', reference: orderNumber });
88
92
  ```
89
93
 
94
+ - Open the payment method only when the stage is `APPROVED`. When the rules require approval,
95
+ `begin()` returns `AWAITING_APPROVAL`, holds the spend, and asks the owner.
96
+ `waitForApproval()` resolves to the approved checkpoint, whose 30 minutes to confirm start at
97
+ the approval. It throws `CheckoutApprovalDeniedError`, `CheckoutApprovalExpiredError`, or
98
+ `CheckoutApprovalTimeoutError`. After a timeout the spend stays held until you cancel.
90
99
  - If the final total differs from the approved one, `confirm()` starts a new checkpoint at the
91
- final total, so the rules check the amount actually paid.
100
+ final total, so the rules check the amount actually paid. If that total needs approval,
101
+ `confirm()` throws `CheckoutApprovalPendingError`: wait with
102
+ `waitForApproval(error.checkpoint)`, then confirm the checkpoint it returns.
103
+ - `begin()` throws `CheckoutCheckpointClosedError` when its idempotency key belongs to a
104
+ checkpoint that already closed. Start a new purchase with a new key.
92
105
  - `reportOutcome()` accepts `PAID`, `NOT_PAID`, or `UNKNOWN`. Until an outcome arrives, the spend
93
106
  stays counted. Cancel an unconfirmed checkpoint with `conto.checkout.cancel(id)`.
94
107
  - Conto records what your checkout reports and does not see the charge. For control at the
@@ -154,6 +167,16 @@ const result = await conto.payments.pay({
154
167
  });
155
168
  ```
156
169
 
170
+ ## Connected-card delivery
171
+
172
+ For an organization with connected cards enabled, `conto.connectedCards.draw()` returns a disposable
173
+ credential exactly once. Keep it in memory and use it at the mandate's merchant before
174
+ `credentialExpiresAt`. Never log or persist it. After receipt, call
175
+ `conto.connectedCards.acknowledgeDelivery(draw.requestId)`. Acknowledgement is idempotent and records
176
+ delivery only, not payment settlement. Replaying the original draw key cannot recover a lost
177
+ credential. Check the original outcome before requesting a replacement. Issued capacity stays
178
+ consumed even when checkout fails.
179
+
157
180
  ## Protocol Clients
158
181
 
159
182
  `Conto` also exposes typed helpers for x402 and MPP service payments:
@@ -194,7 +217,7 @@ automatically. The record response then includes `policyBypass: true`, `bypassRe
194
217
  `agentFrozen`.
195
218
 
196
219
  For a Privy-managed EVM wallet, `conto.x402.pay` replaces `preAuthorize`. Pass the same fields
197
- plus the accepted 402 requirements; when approved, Conto signs the payment and returns the header
220
+ plus the accepted 402 requirements. When approved, Conto signs the payment and returns the header
198
221
  to retry with:
199
222
 
200
223
  ```typescript
@@ -220,82 +243,21 @@ The MPP client mirrors the same shape with `conto.mpp.preAuthorize`, `conto.mpp.
220
243
  `conto.mpp.budget`, and `conto.mpp.services`. An MPP session grant is a ceiling: several
221
244
  `record` calls with the same `sessionId` may draw it down until it is used up or expires.
222
245
 
223
- ## Stripe Link Checkout Commerce (test only)
224
-
225
- `LINK_CHECKOUT` is test-only and works only with a merchant enrolled with Conto. It is not a
226
- general merchant integration. It evaluates policy before creating an ordinary Stripe-hosted
227
- Checkout Session with card and Link enabled. It needs no private Link SDK permission,
228
- Link connection, saved payment-method ID, SPT, or LPT.
229
-
230
- Commerce execution responses separate the provider-neutral `executionChannel`, optional
231
- `checkoutMethod`, and optional `settlementRail` from the legacy provider and adapter identifiers.
232
- For dynamic hosted Checkout, `checkoutMethod` remains absent until settlement observes Link or the
233
- card fallback; callers must not treat the adapter name as proof that Link was selected.
234
-
235
- ```typescript
236
- let { execution } = await conto.beginCommerceExecution({
237
- executionMethod: 'LINK_CHECKOUT',
238
- idempotencyKey: crypto.randomUUID(),
239
- amountMinor: 6400,
240
- currency: 'USD',
241
- category: 'OFFICE_SUPPLIES',
242
- purpose: 'Office supplies',
243
- counterparty: {
244
- canonicalId: 'conto-demo-office-supply',
245
- },
246
- cart: {
247
- lineItems: [{ name: 'Office supplies', quantity: 1, unitAmountMinor: 6400 }],
248
- totals: [{ type: 'total', label: 'Total', amountMinor: 6400 }],
249
- },
250
- providerPresentation: {
251
- approvalSummary: 'Buy office supplies from the configured merchant',
252
- lineItemLabels: ['Office supplies'],
253
- },
254
- });
255
-
256
- if (execution.status !== 'ACTION_REQUIRED' || !execution.approvalUrl) {
257
- throw new Error('Stripe Checkout was not created safely');
258
- }
259
-
260
- if (execution.executionAdapter !== 'LINK_CHECKOUT') {
261
- throw new Error('Unexpected commerce execution adapter');
262
- }
263
-
264
- // Hand the temporary capability to a trusted interactive browser/UI. Do not log it.
265
- await presentCheckoutActionSecurely(execution.approvalUrl);
266
-
267
- while (execution.status === 'ACTION_REQUIRED' || execution.status === 'PROCESSING') {
268
- ({ execution } = await conto.refreshCommerceExecution(execution.id));
269
- }
270
-
271
- if (execution.status !== 'SETTLED' || execution.evidence.actualPaymentMethod !== 'LINK') {
272
- throw new Error('This execution did not settle with Link');
273
- }
274
- ```
275
-
276
- Stripe decides whether Link appears and whether the customer chooses it. Card is an explicit
277
- fallback, surfaced as `actualPaymentMethod: 'CARD'`; it is a valid payment but not Link evidence.
278
- The read-only `getCommerceExecution` projection redacts action URLs; use the initial begin result or
279
- the `payments:confirm` refresh path while action remains required.
280
- The server resolves the exact merchant name, Stripe account, and return URLs from configuration, so
281
- the caller cannot redirect the approved purchase. This path covers only that enrolled merchant's
282
- Conto channel, not unrelated Checkout pages or the consumer's Link wallet. See
283
- `docs/STRIPE_LINK_CHECKOUT_ADAPTER.md` for configuration, webhook, drill, and reconciliation
284
- requirements.
285
-
286
246
  ## Agent checkout guard
287
247
 
288
248
  For browser checkouts on supported rails (Link, card, Shop Pay, or PayPal), use
289
249
  `runGuardedCheckout` with `executionMethod: 'BROWSER_CHECKOUT'`. Other rails and checkout flows are
290
250
  not covered. It binds and atomically authorizes one exact Link, card,
291
251
  Shop Pay, or PayPal browser action without requiring private provider integration. The
292
- privileged browser executor must be available only through this wrapper; an unrestricted click or
293
- payment tool would bypass the control. Persist its stable dispatch attempt before calling the helper; it owns authorization,
294
- one-use claim, the privileged callback, and finalization. See `docs/AGENT_CHECKOUT_GUARD.md` for the
295
- runtime boundary and example.
252
+ privileged browser executor must be available only through this wrapper. An unrestricted click or
253
+ payment tool would bypass the control. Persist its stable dispatch attempt before calling the
254
+ helper. The helper owns authorization, one-use claim, the privileged callback, and finalization.
255
+ The
256
+ [Stripe Link guide](https://conto.finance/docs/guides/stripe-link#make-the-check-enforceable)
257
+ describes the runtime boundary and includes an example.
296
258
 
297
259
  Isolated browser processes can implement `PrivilegedBrowserCheckoutDriver` and use
298
- `runGuardedCheckoutWithDriver`; the agent model must receive only that guarded wrapper, never the
260
+ `runGuardedCheckoutWithDriver`. The agent model must receive only that guarded wrapper, never the
299
261
  driver's `dispatch` primitive. Generate `action.targetFingerprint` with
300
262
  `fingerprintBrowserCheckoutTarget` during inspection, then wrap the privileged target resolver with
301
263
  `createVerifiedBrowserCheckoutDriver`. The wrapper re-hashes the current page, locator, accessible
@@ -305,7 +267,7 @@ the action's `targetUrl`, allowing Conto's origin trust policy to evaluate them
305
267
 
306
268
  For Browserbase, use the first-class provider host. Its binding store must reject duplicate session
307
269
  IDs and atomically revoke only an active record matching the supplied organization, agent, and
308
- project. Store the credential-free binding durably; never add the provider `connectUrl` to it.
270
+ project. Store the credential-free binding durably, and never add the provider `connectUrl` to it.
309
271
 
310
272
  ```typescript
311
273
  import {
@@ -357,15 +319,15 @@ export const POST = host.handler;
357
319
  The Browserbase host exposes `handler`, `createSession`, `attachSession`, `observeSession`,
358
320
  `liveViewForSession`, `releaseSession`, `detachSession`, and `close`. `observeSession` returns only a
359
321
  bounded page origin/title and visible
360
- link/button outline; it excludes full page text, DOM, form values, URL paths and queries,
322
+ link/button outline. It excludes full page text, DOM, form values, URL paths and queries,
361
323
  screenshots, cookies, storage, and provider capabilities. It rechecks ownership and page identity
362
324
  after observing.
363
325
  `createSession` returns a session ID, active status, and expiry without a connection URL.
364
326
  `liveViewForSession` is a privileged operator capability: expose it only behind separate human
365
327
  authentication, never as an agent tool. A configured Context persists login state but must remain
366
- server-side; its durable binding store should prevent concurrent sessions from sharing one Context.
328
+ server-side. Its durable binding store should prevent concurrent sessions from sharing one Context.
367
329
  `releaseSession` atomically revokes Conto ownership before closing the local connection and asking
368
- Browserbase to release the keep-alive session. Call it when the agent session ends; `close` only
330
+ Browserbase to release the keep-alive session. Call it when the agent session ends. `close` only
369
331
  closes local Playwright connections. `attachSession` accepts only a five-minute HMAC assertion
370
332
  binding the exact provider project, Conto organization, agent, session, and expiry. Its matching
371
333
  `detachSession` revokes Conto access and closes the local connection without releasing the
@@ -377,12 +339,12 @@ For another Playwright or secure CDP provider, use
377
339
  exposes only `handler`, `observeSession`, `closeSession`, and `close`, keeping discovery, mutation
378
340
  primitives, the target store, the Playwright page, and raw dispatch private.
379
341
  Lower-level integrations can still compose `createGuardedPlaywrightCheckoutRuntime`, whose façade
380
- exposes only `discover` and `run`; the raw driver is not returned. Target
342
+ exposes only `discover` and `run`. The raw driver is not returned. Target
381
343
  registrations expire after ten minutes by default. The encrypted store persists only ciphertext
382
344
  through a small durable blob port, so a runtime restart does not lose an approved
383
345
  target or disclose its locator. `dispatchAttemptFor` must atomically return the same persisted ID for
384
346
  the same checkout intent. The dispatch result classifies the browser outcome as `NEW_PAGE`,
385
- `PAGE_NAVIGATION`, or `SAME_PAGE`, but always reports settlement as `UNVERIFIED`; only merchant or
347
+ `PAGE_NAVIGATION`, or `SAME_PAGE`, but always reports settlement as `UNVERIFIED`. Only merchant or
386
348
  provider evidence can prove payment. Never expose the page, target store, persisted registrations,
387
349
  or provider-derived locator state to the model. The inert positional reference returned by
388
350
  `observeSession` is the only locator intended for the model.
@@ -406,15 +368,15 @@ ambiguous-dispatch probes pass.
406
368
 
407
369
  The probe runner belongs to the trusted integration host and must execute against an isolated
408
370
  fixture session. A report does not remotely attest an untrusted host or discover tools omitted from
409
- its inventory. See `docs/CHECKOUT_INTEGRATION_VERIFIER.md` for the manifest, probe, and status
410
- contract.
371
+ its inventory. The types exported from `@conto_finance/sdk/checkout-verifier` define the
372
+ manifest, probe, and status contract.
411
373
 
412
374
  The HTTP handler implements the authenticated endpoint consumed by the optional
413
375
  `guarded_browser_checkout` MCP tool. It accepts Web `Request` objects, so it can be mounted directly
414
376
  in a compatible route or adapted to a Node server. It authenticates before reading the body,
415
377
  rejects unknown fields and mismatched cart totals, performs discovery and guarded execution in one
416
378
  request, and returns only minimized browser evidence. Use a dedicated random token of at least 32
417
- characters; the handler rejects Conto API keys.
379
+ characters. The handler rejects Conto API keys.
418
380
 
419
381
  Organization-specific proposal facts can be sent through the typed normalization call after an
420
382
  owner or administrator registers the namespace schema:
@@ -435,60 +397,6 @@ const proposal = await conto.normalizeCommerceProposal({
435
397
  Conto rejects unregistered namespaces and schema-invalid values before policy evaluation. Accepted
436
398
  facts are bound to the schema revision and fingerprint returned on the normalized proposal.
437
399
 
438
- ## Stripe Link Sandbox Commerce
439
-
440
- The gated Stripe Link sandbox surface uses the agent key for policy evaluation and execution. A
441
- Link connection must first be created by its user and explicitly bound to the agent through the
442
- authenticated session API. The agent SDK never receives the Link access token or payment artifact.
443
-
444
- ```typescript
445
- const linkContext = await conto.listStripeLinkPaymentMethods(connectionId);
446
- if (linkContext.organizationId !== expectedOrganizationId) {
447
- throw new Error('Agent key belongs to the wrong Conto organization');
448
- }
449
- const { paymentMethods } = linkContext;
450
-
451
- let { execution } = await conto.beginCommerceExecution({
452
- connectionId,
453
- paymentMethodId: paymentMethods[0].id,
454
- executionMethod: 'STRIPE_PAYMENT_INTENT',
455
- idempotencyKey: crypto.randomUUID(),
456
- amountMinor: 6400,
457
- currency: 'USD',
458
- purpose: 'Office supplies',
459
- counterparty: {
460
- canonicalId: 'conto-demo-office-supply',
461
- displayName: 'Conto Demo Office Supply',
462
- },
463
- cart: {
464
- lineItems: [{ name: 'Office supplies', quantity: 1, unitAmountMinor: 6400 }],
465
- totals: [{ type: 'total', label: 'Total', amountMinor: 6400 }],
466
- },
467
- providerPresentation: {
468
- approvalSummary: 'Buy office supplies from Conto Demo Office Supply',
469
- lineItemLabels: ['Office supplies'],
470
- },
471
- });
472
-
473
- if (execution.status === 'AWAITING_APPROVAL') {
474
- // Present execution.approvalUrl to the Link user, then poll with refresh.
475
- ({ execution } = await conto.refreshCommerceExecution(execution.id));
476
- }
477
-
478
- if (execution.status === 'READY') {
479
- const result = await conto.executeCommerceExecution(execution.id);
480
- console.log(result.execution.evidence, result.execution.evidenceFingerprint);
481
- }
482
- ```
483
-
484
- Use `getCommerceExecution(id)` for read-only reconciliation and
485
- `cancelCommerceExecution(id)` to cancel a nonterminal execution. `beginCommerceExecution` safely
486
- retries with its stable idempotency key, and `refreshCommerceExecution` safely retries observation.
487
- Checkout execution and cancellation are not transport-retried.
488
- The Stripe account and Link business-network profile are resolved from server-owned configuration;
489
- an agent cannot supply or replace them. Use `executionMethod: 'MPP'` only for the optional,
490
- configured MPP compatibility transport.
491
-
492
400
  ## Admin SDK Quick Start
493
401
 
494
402
  `ContoAdmin` uses an organization API key (`conto_xxx...`), not an agent SDK key.
@@ -523,27 +431,6 @@ if (frozen) {
523
431
  }
524
432
  ```
525
433
 
526
- ### Stripe Link Checkout readiness
527
-
528
- Organization administrators can inspect the configured merchant Checkout adapter without calling
529
- Stripe, then explicitly verify that Conto's configured credential resolves to its configured
530
- merchant account:
531
-
532
- ```typescript
533
- const { readiness } = await admin.paymentRails.getStripeLinkCheckoutReadiness();
534
-
535
- if (readiness.canVerify && !readiness.checks.accountVerified) {
536
- const verified = await admin.paymentRails.verifyStripeLinkCheckoutAccount();
537
- console.log(verified.readiness.status, verified.readiness.verification);
538
- }
539
- ```
540
-
541
- The result contains only mode, booleans, safe status codes, timestamps, and scope statements. It
542
- never contains merchant/account identifiers, URLs, credentials, hashes, Stripe objects, or raw
543
- provider errors. Verification proves the configured account match only; Stripe still decides
544
- whether Link is eligible, displayed, and selected at Checkout. The static GET is retry-safe; the
545
- active POST verification is not automatically retried.
546
-
547
434
  ## What This Package Covers
548
435
 
549
436
  - `Conto` for agent-scoped payment flows
@@ -556,7 +443,7 @@ active POST verification is not automatically retried.
556
443
  The SDK automatically adds an idempotency key to wallet payment authorization
557
444
  requests. Provide your own stable `idempotencyKey` when a retry may happen in
558
445
  another process or job attempt. External-card approvals always require a stable
559
- caller-supplied `idempotencyKey`; card approval and confirmation are safe to
446
+ caller-supplied `idempotencyKey`. Card approval and confirmation are safe to
560
447
  retry. x402 and MPP pre-authorization calls issue a grant and reserve spend. A retry
561
448
  with the same `paymentId` (x402) or `challengeId`/`credentialId` (MPP) returns the
562
449
  same live grant instead of reserving again.
@@ -566,7 +453,7 @@ ambiguous failure can be reconciled safely.
566
453
  Read-only calls retry transient network failures, `429`, and `5xx` responses. Financial execution
567
454
  and other non-idempotent mutations are never retried automatically. If execution times out, call
568
455
  `conto.payments.status(requestId)` before attempting it again. A
569
- `reconciliation_required` status means the execution outcome is ambiguous; do not resend the
456
+ `reconciliation_required` status means the execution outcome is ambiguous. Do not resend the
570
457
  payment until an operator has resolved it.
571
458
 
572
459
  `ContoError` and `ContoAdminError` expose a stable error contract: `code`, `status`, `requestId`,
@@ -602,14 +489,4 @@ try {
602
489
 
603
490
  MIT
604
491
 
605
- Developer support: [support@conto.finance](mailto:support@conto.finance). Include the package version and request ID; never include API keys.
606
-
607
- ### Connected-card delivery
608
-
609
- For an organization with connected cards enabled, `conto.connectedCards.draw()` returns a disposable
610
- credential exactly once. Keep it in memory and use it at the mandate's merchant before
611
- `credentialExpiresAt`. Never log or persist it. After receipt, call
612
- `conto.connectedCards.acknowledgeDelivery(draw.requestId)`. Acknowledgement is idempotent and records
613
- delivery only, not payment settlement. Replaying the original draw key cannot recover a lost
614
- credential; check the original outcome before requesting a replacement. Issued capacity stays
615
- consumed even when checkout fails.
492
+ Developer support: [support@conto.finance](mailto:support@conto.finance). Include the package version and request ID. Never include API keys.