# Payment API access for autonomous agents: approve by action

Giving an autonomous agent one payment credential that can refund customers, capture funds, and rewrite subscriptions is a bad shortcut. Those actions affect different facts, fail in different ways, and deserve different human decisions.

Payment API access for autonomous agents should start in a sandbox, but the sandbox is only where you learn whether the action path works. It does not answer whether an agent should make a given financial change. Build the approval boundary around the operation, the target record, and the consequence, then carry that boundary into live use.

I have seen teams treat payment access as a checkbox because the API call itself looks small. A refund endpoint may take an identifier and amount. A capture may take no body at all. A subscription update can look like a harmless field change. The short request is exactly why people underestimate the decision behind it.

## Sandbox success proves routing, not judgment

A sandbox proves that the agent can identify objects, submit requests, handle errors, and read responses without moving real money. It does not prove that its input is trustworthy, that its reasoning matches your support policy, or that a reviewer will recognize a bad request before it executes.

Keep the first sandbox scope deliberately narrow. Use synthetic customers and orders whose names make the scenario obvious. Give each fixture one purpose: a partially captured authorization, a fully captured payment, a payment with a prior partial refund, an active subscription that will prorate, and one that must cancel at period end. Random test data creates false confidence because nobody can tell what a result means.

Write the operational contract before you let the agent call anything. This is not a policy file for an API gateway. It is a compact artifact that product, finance, and engineering can review together.

```yaml
environment: sandbox
operations:
  capture:
    approval: per_call
    reviewer_must_see:
      - authorization_amount
      - requested_amount
      - order_status
      - authorization_expiry
  refund:
    approval: per_call
    reviewer_must_see:
      - original_payment
      - total_refunded
      - requested_amount
      - customer_request_reference
  subscription_change:
    approval: per_call
    reviewer_must_see:
      - current_plan
      - proposed_plan
      - proration_effect
      - billing_anchor
      - cancellation_state
```

This worksheet prevents a familiar failure: somebody approves an agent process at the beginning of a support shift, then discovers later that a plan downgrade with an immediate credit and a manual capture both ran under that vague approval. The worksheet makes the missing facts visible before production does it for you.

Run each fixture twice. First, let the agent propose the action and reject it. Confirm that rejection causes no downstream retry with a fresh request identifier. Second, approve it and compare the provider response with the expected record state. Test an API error after the request leaves your gateway as well. The retry path causes more payment accidents than the happy path.

Sandbox data can still hurt you. An agent that deletes fixtures, changes shared test subscriptions, or generates thousands of noisy events will slow down everyone trying to validate a release. The financial impact is absent, but the control problem is real. Keep sandbox approval less burdensome than production, not absent.

## Refund authority needs provenance and a hard amount check

A refund should require a per-call decision in production because it sends money back and because the original payment alone does not justify the refund. A support conversation, fulfillment failure, fraud review, or contract term supplies the justification. The agent needs enough evidence to propose the refund, but it should not turn a vague sentence such as "make this right" into an unreviewed debit.

Require the proposed action to bind to the original payment object, not a customer name or an order number that might match more than one charge. The reviewer should see the original amount and currency, prior refunds, requested amount, and a reference to the customer request or internal case. If the provider supports partial refunds, calculate the remaining refundable amount from the provider's latest state, not a local cached balance.

The dangerous recommendation is to auto-approve small refunds. People like it because it lowers ticket time and many small amounts feel harmless. It fails because the count is unbounded, an agent can select the wrong payment, and a small amount can still be a policy violation. A per-call approval takes a moment. Reversing a bad refund often requires an awkward customer conversation and may not be possible through the payment rail.

Set an amount expectation even when a person approves each call. If the agent requests more than the original capture or more than the remaining refundable amount, your action layer should reject it before asking a human. Do not turn that check into an agent instruction. Enforce it where the credential and request execution live.

A good approval record reads like a payment clerk's note, not a model transcript:

```text
Refund request
Payment: pay_123
Original captured: 84.00 USD
Already refunded: 20.00 USD
Requested: 64.00 USD
Reason reference: case_481
Expected result: payment fully refunded
```

The reason reference matters. It lets someone reviewing a later dispute trace why the action happened without placing customer correspondence or payment credentials in an agent prompt. Keep the agent's summary short, but preserve the source record outside the agent conversation.

Do not let refund approval stand in for customer identity verification. The payment API generally knows about a payment object, not whether a chat participant is the account owner. Put identity checks in the support workflow before the agent proposes the financial action.

## A capture is collection, even when authorization exists

A payment capture should receive separate per-call approval because authorization and collection are different customer states. The customer may have authorized a maximum amount, while your business still needs to decide whether an order shipped, whether the final amount changed, and whether the authorization remains valid.

Capture requests often look deceptively safe. An agent may see an authorized payment and conclude that it should capture it when it sees a shipping label. That label might be voided, the order might be split, inventory might be backordered, or a human might have arranged a different settlement. The agent cannot infer a collection decision from a single status field.

Show the reviewer four facts: the authorized amount, the proposed amount, the fulfillment state, and the authorization's expiry. If partial capture is permitted, show whether a later capture remains possible under the provider's rules. Those rules differ by payment method and provider, so use the provider response as the source of truth rather than copying assumptions into prompts.

Treat a requested amount mismatch as a separate approval moment. A capture for the entire authorization and a capture for a reduced final amount have different explanations. The approval card should state the difference plainly, for example, "Authorized 100.00 USD, capture requested 86.50 USD after item removal." A reviewer cannot catch a mismatch if the interface hides the original authorization.

Do not grant a capture capability merely because the agent can create an order. Order creation is an internal intent. Capture is an external financial act. Keeping those capabilities apart also improves incident response: if the fulfillment integration starts behaving strangely, you can disable collection without disabling ordinary order research.

For sandbox work, create one authorization that should capture successfully, one that should remain uncaptured, and one where your fixture simulates an expired authorization. The agent should propose no action for the second and should surface an exception for the third. If it simply retries either state, you have tested request formatting rather than judgment.

## Subscription edits carry delayed financial consequences

A subscription change deserves per-call approval because its impact may appear on the next invoice rather than in the immediate API response. The risky fields are not limited to plan identifiers. Quantity, billing anchor, trial dates, cancellation settings, discounts, tax settings, and proration behavior can all change what a customer pays or receives.

Do not approve a subscription request that displays only the proposed plan. The reviewer needs a before and after view: current plan and quantity, proposed plan and quantity, current renewal date, intended renewal date, cancellation state, and the provider's projected proration or invoice result where available. Without that comparison, a reviewer sees a product label and misses the bill.

Subscription operations need their own vocabulary. A downgrade at renewal, an immediate downgrade with a credit, a cancellation at period end, and an immediate cancellation are not interchangeable. Make the agent select one explicit intent from your support workflow. If the customer request is ambiguous, send it back for clarification rather than asking the agent to choose a billing consequence.

The worst subscription failures tend to look administratively correct. The API returns success, the account has the expected plan name, and the customer later finds an unexpected invoice or lost access. That is why the approval should include the expected financial and access result, not just the changed fields.

Sandbox scenarios should include a mid-cycle quantity reduction, an upgrade that produces proration, and a cancellation scheduled for period end. Confirm the provider's sandbox response and any generated invoice objects. Then have a reviewer reject the same proposed change and confirm that the agent does not try a nearby action, such as setting quantity to zero instead of setting a cancellation date.

## Idempotency prevents retries, not bad authority

Idempotency protects a request from duplicate execution after a network failure or uncertain response, but it cannot make an unauthorized or mistaken action safe. Teams regularly blur these controls because both seem related to duplicate refunds. They are not the same control and they fail in different directions.

Stripe's documentation for idempotent requests says that it stores the first status code and response body for an idempotency key, including a server error response, and returns that result for later requests using the same key. It also says that later requests must use matching parameters. That is useful behavior, but it does not stop an agent from generating a different idempotency key for the same intended refund, nor does it tell you whether the refund should exist.

Use one stable action identifier for one human-approved intent. Generate it before execution, record it with the approval, and reuse it for a retry of that exact request. Do not derive it from the current time, and do not allow the agent to replace it after a timeout.

A sandbox test should deliberately simulate uncertainty:

1. Approve a partial refund of a fixture payment and assign an action identifier.
2. Submit the request, then make the caller behave as though it lost the response.
3. Retry with the same identifier and identical amount.
4. Inspect the payment state and confirm that the provider reports one refund.
5. Attempt the same request with a changed amount and confirm that your action layer rejects it rather than silently treating it as a retry.

That last check catches a damaging bug. A developer may reuse an identifier by accident while an agent has changed its requested amount after reading a fresh support note. The provider's mismatch response is a warning that two different intents have been confused. Preserve both requests in the audit record and require a new human decision for the new amount.

Idempotency also does not handle concurrent reasoning. Two agent runs can each propose the same refund with different identifiers. Before execution, fetch the latest payment state and check prior refunds. Better yet, serialize actions against the same payment object in your gateway so that a second proposal waits for the first result. The reviewer should never need to race two approval cards to prevent duplicate money movement.

## Approval screens must show the decision, not an API blob

An approval screen works only when a person can decide from the facts on screen. A raw endpoint name, a large JSON payload, and an Allow button shift the burden to a reviewer who has neither time nor context to decode them.

For refunds, lead with money leaving the business and identify the original payment. For captures, lead with money being collected and compare authorization with requested capture. For subscription changes, lead with the before and after billing state. Then provide the provider object identifiers and raw payload behind the summary for investigation, not as the main interface.

The approval must bind to the exact request. If a reviewer approves a subscription downgrade, the action layer cannot later add an immediate invoice payment, alter the quantity, or switch the target subscription. Hash or otherwise bind the reviewed fields in the execution record, and invalidate approval when a material field changes.

Per-session approval still has a place. It establishes that a particular agent process may ask for actions during a bounded run. It should show the process identity, who launched it, and the scope of work. It does not authorize every payment action that process may invent after it begins. Session approval answers "may this process request work?" Per-call approval answers "should this exact financial act happen?"

Avoid approval prompts for routine read operations. Agents need to inspect a payment's status, retrieve a subscription, and read prior refund state to prepare a useful proposal. If every read produces a modal, people will approve blindly or disable approval altogether. Reserve the interrupt for state changes and ensure the read path does not expose credentials to the agent.

## Credentials belong behind an action boundary

The agent should never receive a payment secret, even temporarily, because an agent process can place it in logs, shell history, source files, chat context, or a request to another service. Redacting a secret after the fact does not repair the copies you failed to notice.

Put the credential in an action boundary that accepts a constrained request, injects the credential itself, and returns the provider result. The agent can ask to retrieve a payment or propose a refund, but it cannot print the secret or repurpose it for an endpoint you did not expose. Separate sandbox and live credentials at that boundary so an environment switch cannot happen through an agent-written environment variable.

Sallyport keeps API and SSH credentials in an encrypted vault on the Mac and executes supported HTTP calls without passing the credential to the connected agent. Its vault gate, session authorization, and optional approval on every use map well to payment work when you reserve the per-use setting for financial writes.

The action boundary should validate more than authentication. It should reject a live request sent through a sandbox route, block an unsupported HTTP method, verify that an identifier has the expected type, and require a recorded approval for the financial operations you designated. These checks are constraints on execution, not suggestions that a model may follow when convenient.

Do not treat a broad payment provider secret as a development convenience. If your agent needs only payment retrieval, refund creation, capture, and a narrow subscription update, expose only those calls. A secret with account administration, payout, dispute, or customer data powers expands the blast radius for no good reason.

## An audit trail must explain both action and authorization

A payment provider's event history can tell you that a refund or subscription update occurred. It often cannot tell you which agent process requested it, what evidence it considered, whether a person approved it, or what request it made before a retry. Keep an execution record that answers those questions without storing the full agent transcript as your only evidence.

Record the environment, operation, target object, submitted fields, action identifier, provider response, agent session identity, approval outcome, reviewer identity, and time order. For actions that use amounts, retain amount and currency as separate fields. For subscription changes, retain the before state and intended after state. Store references to support cases or fulfillment records rather than duplicating sensitive customer text where you do not need it.

Make the audit log useful during disagreement. If a customer says a refund was wrong, an operator should reconstruct the chain: the agent read payment state, proposed a partial refund tied to a case, a person approved the exact amount, the gateway submitted one request, and the provider returned a refund object. If the chain has a gap, fix the system rather than asking staff to remember what happened weeks later.

Tamper evidence matters because payment incidents often become access incidents. A person with administrative access should not be able to erase an inconvenient action record without detection. Sallyport projects agent sessions and individual calls from an encrypted hash-chained audit log, and its `sp audit verify` command can verify the chain offline without a vault secret.

Keep audit review practical. Look for repeated rejected proposals against the same payment, changed amounts after a timeout, many attempts from a new process identity, and subscription changes that create unexpected invoice behavior. Those patterns point to a bad integration or a confused agent before they turn into a larger finance cleanup.

## Live access should follow evidence, not a calendar

Move to live payment actions only after the sandbox runs show that the agent proposes the right operation, reviewers see enough context, retries stay idempotent, and rejection actually stops execution. A fixed number of successful test calls is less useful than evidence across the failure cases that production will eventually hand you.

Start live use with read access and proposals if your workflow permits it. Then choose one operation and one narrow business case, such as refunds that already have a closed support case and a verified original payment. Keep captures and subscription changes disabled until their own sandbox fixtures, approval views, and reversal procedures have been exercised.

Before the first live call, rehearse revocation. Lock the credential boundary, terminate the agent session, confirm pending approvals cannot execute, and verify that the audit record remains available. Do this while everyone is calm. A payment incident is a terrible time to discover that revocation depends on somebody finding the right terminal command.

Do not gradually relax per-call review because the agent behaved well for a week. Relax it only when you can name a bounded action, a reliable source of authorization, a measurable error path, and an owner who will review exceptions. Refunds, captures, and subscription edits rarely meet those conditions at the same time. Keep their approval expectations separate, because their consequences are separate.
