# DeAlgo agent commerce pilot

Version 0.4 includes financial proposals for purchases, sales, refunds, payouts, and subscriptions. Proposals reserve an allowance and require human review. **APPROVED does not execute a payment.** The separate refund tool retains its existing test-only execution path.

Start with the [agent quickstart](https://dealgo-portal.vercel.app/downloads/dealgo-agent-quickstart.md). It covers connection checks, MCP setup, policy drafts, evidence and errors.

## Connect

1. Sign in as a workspace administrator at [Financial controls](https://dealgo-portal.vercel.app/commerce/control).
2. Create a restricted agent connection and save its one-time key in your application's secret store.
3. Create a mandate for that agent: action, counterparty IDs, currency, per-action limit, total allowance, and expiration. Open proposal intake.
4. Install the release archive:

```sh
npm install ./dealgo-middleware-0.5.0.tgz
```

Use the existing DEALGO_URL and DEALGO_API_KEY environment variables. A commerce key is different from a refund-only key; the server refuses requests outside the credential's scope.

```js
import { DeAlgo } from "@dealgo/middleware";
const dealgo = new DeAlgo({
  url: process.env.DEALGO_URL,
  apiKey: process.env.DEALGO_API_KEY,
});
const { mandates } = await dealgo.listMandates();
// Select the intended mandate by its stored ID, not blindly by array order.
const mandate = mandates.find((m) => m.id === process.env.DEALGO_MANDATE_ID);
if (!mandate) throw new Error("Required mandate is unavailable");
const result = await dealgo.propose({
  mandateId: mandate.id,
  action: "purchase",
  counterpartyId: "vendor_acme",
  amountMinor: 2500,
  currency: "usd",
  description: "Office supplies for order PO-1042",
  requestKey: "purchase-order-1042",
});
console.log(result.approvalUrl);
// A later read never resubmits the proposal.
const status = await dealgo.getProposal(result.proposal.id);
console.log(status.proposal.status, status.executionEnabled);
```

Persist the requestKey alongside your own order before sending. Retry an uncertain submission using that same key and exact terms. A changed amount or counterparty needs a new business request. Do not generate a new key merely because a connection timed out.

## MCP

For a global installation, run `dealgo-middleware doctor-commerce` for a read-only connection check, or `dealgo-middleware mcp-commerce` for the stdio server. Configure the same environment variables in your MCP host. This server exposes:

- dealgo_connection_status
- dealgo_prepare_policy
- dealgo_request_policy
- dealgo_policy_request_status
- dealgo_report_tool_evidence
- dealgo_list_mandates
- dealgo_propose_deal
- dealgo_proposal_status

It contains no approval, payment execution, billing, merchant connection, or raw Stripe tool. Agent-to-agent negotiation can produce a proposed deal, but messages and negotiations never grant spending authority. This is not a certified A2A/AP2 implementation.

## Direct HTTP

POST /api/v1/commerce/proposals with a Bearer connection key, Content-Type application/json, and an Idempotency-Key header. The JSON fields match the JavaScript example except requestKey, which is carried in the header. GET /api/v1/commerce/mandates and GET /api/v1/commerce/proposals/:id are read-only.

Amounts are positive integers in the currency's minor units. USD 2500 means $25; currencies do not all use two decimal places. Never submit payment-card data, passwords, bank credentials, tax IDs, or identity documents in descriptions.

## Current boundaries

Collection reads return the latest 100 records. Reservations include pending and approved proposals; rejecting or cancelling an unexecuted proposal releases its reservation. Expired/revoked mandates cannot approve pending requests, but reservations remain until cancellation. The workspace pause affects this commerce control path only. Keep direct payment credentials away from agents and remove any bypass tool from the agent environment.

Commerce records are append-only application records, not independently signed settlement receipts. General live financial execution, production acceptance, operational support commitments, and provider onboarding must be completed before a live-money rollout.

## Searchable workspace history

Administrators can search Activity across stored events and load older results, 50 at a time. Search covers event/request IDs, actors, action names, tool names, and proposal counterparties/descriptions. Selecting a request loads its own history independently of the search. Pagination preserves a time boundary while you browse; refresh to include newly recorded events. The overview remains a recent activity summary. These records are not a settlement ledger.

The administrator-only endpoint is `GET /api/v1/commerce/events?q=...&category=...&resource=...&cursor=...`. Preserve the returned `nextCursor` with the same filters. `category` supports `proposal.`, `mandate.`, `connection.`, `commerce.`, `tool.`, and `refund.`. Agent keys cannot read the entire workspace history.

## Opt-in external tool evidence

An administrator first enables evidence reporting in Integrations, specifying exact tool names and HTTPS evidence origins. This authorizes recording metadata, not invoking tools or making payments. Permission changes and reports are append-only workspace events. Revocation prevents subsequent reports, including replays.

A connected agent may report only against its own commerce proposal. The current SDK 0.2 does not have an evidence method; use this direct HTTP endpoint from your existing tool adapter:

```js
// Run only after your separately authorized tool call has completed.
// Persist this body before sending so retries use the same eventKey and metadata.
const evidence = {
  eventKey: "inventory-check-1042",
  proposalId: storedProposalId,
  tool: "inventory.lookup",
  outcome: "succeeded", // or "failed"
  startedAt: storedStartTime,
  finishedAt: storedFinishTime,
  inputSha256: storedInputDigest, // 64 lowercase hexadecimal characters
  outputSha256: storedOutputDigest,
  evidenceUrl: "https://evidence.example.com/records/1042", // optional
  // parentEventId: priorReport.id, // optional; same agent and proposal only
};
const response = await fetch(
  `${process.env.DEALGO_URL}/api/v1/commerce/tool-evidence`,
  {
    method: "POST",
    redirect: "error",
    headers: {
      Authorization: `Bearer ${process.env.DEALGO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(evidence),
  },
);
if (!response.ok)
  throw new Error(
    "Evidence delivery unconfirmed; do not repeat the tool action.",
  );
const receipt = await response.json();
```

Reports are labelled **AGENT_REPORTED**, never independently verified execution. Digests identify the exact bytes chosen by the adapter; a digest alone does not establish truthful content. Do not send raw arguments, outputs, secrets, personal information, or signed URLs. URLs must use an approved HTTPS origin with no user information, query, or fragment; the Portal never fetches them. Store evidence at an access-controlled URL without secrets in its path. Operators may follow links and independently compare content to the recorded digest.

Retry evidence delivery with the same eventKey and exact metadata. A changed payload returns 409. Evidence delivery failure must never cause the underlying financial or external tool action to run again. Reporting is not a durable delivery queue: an adapter must persist undelivered reports and retry them separately.

## Guided agent onboarding

Setup & assistant provides grounded, deterministic help and a policy draft form. No language model is connected and customer data is not sent to a model. The assistant cannot execute or approve money movement.

An already connected agent may call `POST /api/v1/commerce/onboarding/draft` with the mandate fields below to validate a proposed policy for itself:

```json
{
  "agentId": "YOUR_BOUND_AGENT_ID",
  "name": "Proposed purchasing policy",
  "currency": "usd",
  "actions": ["purchase"],
  "counterparties": ["vendor_acme"],
  "maxSingleMinor": 2500,
  "budgetMinor": 10000,
  "expiresAt": "REPLACE_WITH_A_FUTURE_ISO_TIMESTAMP_WITHIN_90_DAYS"
}
```

This endpoint writes no mandate and returns `activated: false`. An administrator must review and activate the exact policy through Setup & assistant or Spending policies. Agents cannot mint their own credentials, approve proposals, grant evidence permissions, or raise limits. Merchant verification and live-money activation remain separate requirements.

## Refund execution and reconciliation

Refund operations brings the existing governed Stripe **test-mode** refund path into the workspace. It searches and paginates recorded requests, shows exact amounts and provider observations, and supports administrator approval, rejection, and reconciliation. These requests come from the refund API and its recorded-payment/authorization prerequisites; a general commerce refund proposal does not become an executable refund automatically.

`Refresh stored records` reads DeAlgo. `Check provider outcome` queries the provider for the existing approved operation and records its observation, without creating a second refund. Pending and unknown outcomes are not success. The receipt is a Portal-recorded provider response, not an independently signed settlement proof. Reconciliation is operator-triggered; there is no automatic scheduled sweep in this release.

The commerce proposal pause and the governed refund engine's freeze/quarantine controls are distinct. Refund execution remains test-only. Live operation requires verified provider ownership, production configuration and validation, monitoring, and an agreed operational process.

## Agent-to-owner policy handoff (0.4.0)

Use `requestPolicy({ ...policyTerms, requestKey })` or MCP `dealgo_request_policy` to save immutable terms for owner review. The response includes `reviewUrl`, `workspaceId`, `digest`, `expiresAt`, and `status: PENDING`. Give the owner that URL and workspace identity; do not perform their approval. This creates no mandate and moves no money.

The owner reviews the exact stored terms in Setup & assistant. Activation rechecks the submitting connection and terms digest and creates one policy atomically. A request expires within 24 hours. The agent reads `getPolicyRequest(id)` / `dealgo_policy_request_status` for the result; ACTIVATED supplies a mandateId for proposals only. Intake pause is unchanged. Read current mandates before proposing; historical activation does not override revocation.

Preserve the requestKey, connection, and exact payload, including the original expiresAt, on retry. Changed terms return idempotency_conflict. No automatic mutation retries are performed. At most 25 unexpired requests can be pending per agent. The collection endpoint shows the oldest 50 open requests; retain IDs to read later states. Expired requests require a newly reviewed request rather than renewed authority by retry.

`preparePolicy` is still validate-only and writes nothing. Full contract and errors: https://dealgo-portal.vercel.app/downloads/dealgo-agent-quickstart.md.

## Connected test refunds (0.5.0)

This connects an approved commerce proposal to one canonical Stripe test refund. It does not enable general execution or live money. An eligible original payment decision must already be recorded for the same workspace **and agent**. This is not a way to import arbitrary Stripe payments. The separate legacy refund integration remains available with its own restricted credential.

1. Read the original decision ID, PaymentIntent ID and currency from your existing recorded payment integration. Obtain an owner-activated refund policy whose counterparty allowlist contains `stripe:test:<paymentIntentId>`.
2. Submit exact terms, including the original payment and reason:

```js
const result = await dealgo.propose({
  mandateId, action: "refund", counterpartyId: `stripe:test:${paymentIntentId}`,
  amountMinor: 1000, currency: "usd", description: "Duplicate order refund",
  requestKey: "order-123-refund-v1",
  refundTarget: { decisionId, paymentIntentId, reason: "duplicate" }
});
// Give result.approvalUrl to the owner. Wait for APPROVED.
const approved = await dealgo.getProposal(result.proposal.id);
const handoff = await dealgo.stageTestRefund(approved.proposal.id, approved.proposal.digest);
// Give handoff.reviewUrl to the owner for separate final submission.
const outcome = await dealgo.getTestRefund(approved.proposal.id);
```

The equivalent MCP tools are `dealgo_propose_deal` (with `refundTarget`), `dealgo_stage_test_refund`, and `dealgo_test_refund_status`. Staging never submits a refund. The owner sees the original payment, exact amount, currency and reason in Refund operations. No term retyping is required.

`refundTarget` is optional on general proposals, immutable when present, and included in the terms digest. A refund target requires action `refund` and the exact counterparty format above. Existing proposals without a target remain approval-only and cannot be retrofitted for execution. Refunds return to the original payment method, not an arbitrary agent-selected recipient.

Handoff checks approval, active policy, original connection, agent ownership and payment binding. At the canonical release boundary, DeAlgo checks those controls again, plus the final approval's freshness and refund expiry. Once release starts, cancellation cannot free the reserved allowance, even if the provider result is unknown. Pause/revocation blocks a release that has not started; it cannot recall an operation already in flight. Reservations remain conservative even after a refund failure; there is no automatic budget recycling.

Retry staging with the same proposal ID and digest to retrieve its one existing operation. Never generate a replacement request to resolve a timeout. `getTestRefund` reads stored records only. The owner uses **Check provider outcome** to reconcile; this makes provider reads, not another refund-create call. `APPROVED`, `SUBMITTED`, `OUTCOME_UNKNOWN` and `PROVIDER_PENDING` are not evidence of completed refund settlement. Recorded provider responses are not independently signed evidence.

Common refusals: `proposal_approval_required`, `exact_refund_target_required`, `refund_target_counterparty_mismatch`, `refund_decision_agent_mismatch`, `refund_payment_binding_mismatch`, `connection_revoked`, `mandate_inactive`, `commerce_paused`, and `execution_started_reconcile_existing_refund`. Return the refusal and existing record to the owner rather than broadening authority yourself.
