Stripe triggers
Handle payment, invoice and subscription events with private signing and local testing.
#Start an agent from Stripe events
Connect your own Stripe account's payment, invoice, or subscription events to a running agent. This connection receives selected snapshot events. It does not create charges, manage your mAIvn subscription, or need your Stripe API key.
#Create your connection
For local account testing, install the open-source Stripe CLI and sign in with stripe login. Obtain its webhook signing secret privately with stripe listen --print-secret. For a Live connection, use stripe listen --live --print-secret instead. Stripe documents that this secret stays the same across listener restarts. Keep it out of source files, shell history, and shared logs. Stripe listener reference.
In Studio, open Config → Triggers → Connections → Add a connection. In the Developer Portal, open your project's Integrations → Connections. Choose Stripe events, enter a recognizable name and that signing secret, and select Test. Test is the default; choose Live only for a live endpoint. Save the connection ID and callback address. The secret is stored privately and is not returned by the connection list or reveal.
Without Studio or the Portal, use the existing signed-in connection API with this body at POST /v1/projects/{project_id}/connections:
{
"display_name": "Stripe sandbox",
"setup_kind": "stripe_webhook",
"webhook_secret": "<supplied privately>",
"stripe_livemode": false
}The mode is required. A signed live event sent to a Test connection is rejected, and vice versa. Connection management retains its existing human-session and CSRF requirements; your agent's project API key is a different credential.
#Declare what your agent handles
Add a declaration beside your configured Agent:
paid = (
agent.on_payment("conn_your_stripe", status="succeeded")
.key("payment-received")
.named("Payment received")
.target("your-agent-id", version=1)
.asking(
"Summarize this payment event without taking external actions: "
"{{event.payload.id}} for {{event.payload.data.object.id}}, "
"amount {{event.payload.data.object.amount_received}} "
"{{event.payload.data.object.currency}}."
)
.display_in_app()
)The defaults are payment succeeded, invoice paid, and subscription created. Each helper selects exactly one event type. These are the eight supported snapshot types; thin events and other Stripe types are not admitted by this connection. A subscription update and a paid invoice mean different things. Do not use a checkout completion event as a substitute for payment confirmation.
In Studio, select the project, start the agent, and turn on the declared trigger. In a terminal, configure the project key and plane origins, then run:
maivn serve ./your_agent.py --project-id PROJECT --agent-id your-agent-id --version 1After Announced, register with paid.invoke() once from a separate script. Keep registration out of import-time code and leave the serving process open. The Portal manages the connection and shows runs; the local agent still runs on your machine.
#Test with your Stripe account locally
Forward only your selected snapshot events to the saved callback:
stripe listen --events payment_intent.succeeded --forward-to http://127.0.0.1:8000/v1/connections/conn_your_stripe/eventsFor an explicitly configured Live connection, use stripe listen --live --events payment_intent.succeeded --forward-to <saved-callback> with its Live signing secret. The listener mode and saved connection mode must match.
Keep the listener running. In another terminal, stripe trigger payment_intent.succeeded creates a Stripe test fixture. Inspect the receiver response and then the trigger's run history and actual answer. The local listener needs no public tunnel or paid forwarding subscription. Stripe's local testing instructions.
For a Workbench endpoint, use that endpoint's own signing secret, select snapshot events, and update its destination to the saved connection's public callback. The CLI listener secret and Workbench endpoint secret are different. When the platform is local, the free receiver-only HTTPS tunnel can supply its public address. Do not mix test/live endpoints or secrets.
#Test the receiver without a Stripe account
Create a separate Test connection using a private synthetic signing secret. From tooling/mock-gateway in the source checkout:
uv run --frozen maivn-mock-gateway replay-stripe --connection-id conn_your_stripe --event-type invoice.paid --event-id evt_fixture_001 --repeat 2The command prompts privately for the secret. Automation can supply one stdin line or name a private environment variable using --secret-env NAME; there is no secret argument. This command signs local synthetic snapshots and makes no payment or Stripe API call. Agent inference can still incur normal model usage.
Two sends with the same event ID produce one logical delivery. Use --new-event-id for a new event, --tamper to prove byte verification, or --mode live only with a separate synthetic Live connection. 202 proves durable reception, not completion of the agent's work.
#Payloads and recovery
Your prompt receives event.payload.id, type, created, api_version, livemode, optional account ID, and data.object. The object contains selected IDs, status, amounts, currency, relevant timestamps and bounded metadata. Expanded customer/subscription references become IDs. Client secrets, payment method details, addresses, authentication context and raw headers are excluded. Only put metadata you intend your agent to receive in Stripe metadata fields.
The receiver checks exact signed bytes and accepts any valid current v1 signature. Its replay window is five minutes. Retries with fresh signatures retain the same signed event ID; unsigned headers cannot choose another identity. Durably acknowledged events remain queued through a local service restart.
Events can arrive out of order. Distinct IDs remain distinct even when their object or creation time matches. Use your own business idempotency checks before performing side effects; this connection does not reconcile a customer's current subscription state for you.
Stop the listener with Ctrl-C. Pause the trigger to stop new runs, or revoke the connection to reject new deliveries while preserving its record and history.