sipi.bot integration

sipi.bot + Stripe

sipi.bot integrates directly with Stripe to enforce per-charge merchant allowlists, transaction velocity caps, and daily spend ceilings. The agent's intent is policy-checked before the Stripe charge is created.

The one integration where real money moves

Every other integration on this site guards inference spend — tokens that turn into an invoice at the end of the month. Stripe is different in kind. When an agent creates a charge, funds actually move, to a real counterparty, and reversing it is a business process rather than a code change.

That asymmetry should change your policy. Token spend that is slightly too high is a cost problem you fix next sprint. A payment sent to the wrong destination is a different category of event. It is reasonable to run tighter rules here than anywhere else in the same system.

Where the allowlist finally earns its place

On a Claude or Gemini integration there is one counterparty, so a merchant allowlist has almost nothing to do. On Stripe it is the central control: the agent can pay anyone, and constraining who matters at least as much as constraining how much. A destination that is not on the list should not be a large payment that gets flagged — it should not be a payment at all.

Rule typeWhat it does on this integration
merchant_allowThe primary rule here. Constrains the set of destinations the agent may pay, rather than only the amount.
approval_thresholdHuman sign-off above a figure. On real money this should be set lower than instinct suggests.
per_transactionA hard ceiling no single charge may exceed under any circumstances.
velocityCatches the duplicate-charge loop, where each payment is individually valid and the sequence is not.

Check before you create the intent

The decision belongs before the payment object exists — not after creation and not after confirmation. Once a charge is created you are managing a payment rather than preventing one.

import requests, stripe

def guarded_charge(amount_cents, destination, reason):
    d = requests.post(
        "https://sipi.bot/v1/transactions/evaluate",
        headers={"Authorization": f"Bearer {SIPI_KEY}"},
        json={"amount": amount_cents / 100, "merchant": destination,
              "category": "payment", "description": reason},
        timeout=5,
    ).json()
    if d["decision"] == "BLOCKED":
        raise PaymentBlocked(d["reason"])
    if d["decision"] == "FLAGGED":
        return queue_for_human(d["transaction_id"], amount_cents, destination)
    return stripe.PaymentIntent.create(
        amount=amount_cents, currency="usd",
        metadata={"sipi_transaction_id": d["transaction_id"]},
    )

Carrying transaction_id into the payment metadata is worth the one line. It links the policy decision to the payment record permanently, which is what turns an audit trail into something you can actually reconcile against Stripe.

Retries and double counting

Stripe's idempotency keys stop a retried request creating two charges. They do not stop a retried request consuming your daily ceiling twice — that is a separate system with separate state. If you retry around the firewall call, reuse the original decision rather than requesting a new one, or you will exhaust a budget on payments that never happened.

What this is not

Questions

Should an agent be able to move money at all?

A fair question and worth answering deliberately rather than by default. Where the answer is yes, the useful posture is a narrow allowlist, a low approval threshold, and human sign-off on anything unusual — autonomy inside a small, explicitly drawn box.

Does the check slow down checkout?

It adds a round trip before payment creation. On agent-initiated payments there is no user waiting, so the latency is not the constraint people assume it is.

Get started with sipi.bot →