# Enable in-context shopping on AI agents Learn how to let your businesses sell their products on AI chat agents. > If your platform wants to use agentic commerce to sell your businesses’ products, [join the waitlist](https://go.stripe.global/agentic-commerce-contact-sales). Agentic commerce for platforms is currently available to businesses in the US and requires waitlist approval. Use agentic commerce to let your connected accounts sell products through AI chat agents. Your platform uploads [product feed data](https://docs.stripe.com/agentic-commerce/product-feed.md), configures checkout hooks for tax and fees, and connected accounts opt in to agent channels. When a buyer makes a purchase through an agent, Stripe runs checkout and calls your platform’s hooks before it completes the transaction. A simplified overview of the agentic commerce flow for platforms (See full diagram at https://docs.stripe.com/connect/saas/tasks/enable-in-context-selling-on-ai-agents) ## Choosing your charge type In the in-context shopping flow, Stripe creates an agentic checkout session for your platform. To create the session, Stripe needs to know which charge type you want to use. Stripe supports direct charges and destination charges with the `on_behalf_of` parameter. In both charge types, the connected account acts as the seller. Contact Stripe with the charge type you want, and Stripe updates that setting for your account. ## Upload your product catalog data to Stripe ### Prepare your product catalog For each connected account, create a CSV file that conforms to the [Stripe product catalog specification](https://docs.stripe.com/agentic-commerce/product-feed.md). ### Upload the product catalog data to Stripe Upload product catalog data for each connected account separately through the Stripe APIs. You can send updates every 15 minutes. Use a sandbox to validate parsing, field mappings, and data quality before enabling live updates. First, upload a connected account’s product catalog CSV using the [Files API](https://docs.stripe.com/api/files.md). A successful request returns a [File object](https://docs.stripe.com/api/files/object.md), which includes the `id`. - Set the `purpose` to `data_management_manual_upload`. - Make sure the MIME type matches the file format. Acceptable formats include CSV and TSV, where each row represents one product or variant. - The maximum file size is 200 MB. ```curl curl https://files.stripe.com/v1/files \ -u <>: \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" \ -F purpose=data_management_manual_upload \ -F file="@/path/to/your/file.csv;type=text/csv" ``` Then, use the Data Management API to create an `ImportSet`. This call starts catalog processing and makes the data available in the Dashboard. Include the following: - The file `id` returned. - The preview header (for example, `Stripe-Version: 2025-09-30.clover;udap_beta=v1`). ```curl curl https://api.stripe.com/v1/data_management/import_sets \ -H "Stripe-Version: 2025-09-30.clover;udap_beta=v1" \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" \ -u <>: \ -d file={{FILE_ID}} \ --data-urlencode standard_data_format="product_catalog_feed" ``` ### Monitor feed status Stripe validates and cleans the product catalog data, indexes it, and converts it to a format for AI agents. Monitor indexing progress using the `status` field on the import set. The status can be `pending`, `failed`, `succeeded`, `succeeded_with_errors`, `pending_archive`, or `archived`. ```curl curl https://api.stripe.com/v1/data_management/import_sets/{{IMPORT_SET_ID}} \ -u <>: \ -H "Stripe-Version: 2025-09-30.clover;udap_beta=v1" \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" ``` The response includes the status and any errors: ```json { "id": "impset_7MabcdZ8b617780e5145413", "object": "data_management.import_set", "created": 1643992696, "livemode": true, "result": { "errors": { "file": "file_234923sIENc", "row_count": 30 }, "rows_processed": 120, "successes": { "row_count": 90 } }, "status": "succeeded_with_errors" } ``` If your import status is `succeeded_with_errors`, you can download the error report: 1. Find the `result.errors.file` field in the response. 1. Use the [Files API](https://docs.stripe.com/api/files.md) to retrieve the error file by its ID. 1. The downloaded CSV contains your original data with a leading column named `stripe_error_message` that describes why each row failed. ```curl curl https://files.stripe.com/v1/files/{{ERROR_FILE_ID}}/contents \ -u <>: ``` The API returns a CSV file containing only the rows that failed, with a `stripe_error_message` column describing each error. ## Configure taxes for your connected accounts Use Stripe Tax for fully managed tax calculations, or implement a tax hook in your integration if you need custom tax logic or must integrate a third‑party tax provider. #### Automated tax calculation with Stripe Tax Use Stripe Tax to calculate and collect tax for each of your connected accounts. See the [Tax for platforms setup](https://docs.stripe.com/tax/tax-for-platforms.md) to configure Stripe Tax. In your product catalog CSV upload, set the `stripe_product_tax_code` column to associate each product with a tax treatment. See [tax codes](https://docs.stripe.com/tax/tax-codes.md) for the full list of supported tax codes. You can also calculate taxes through [third-party tax providers](https://docs.stripe.com/tax/third-party-apps.md), such as Anrok, Avalara, and Sphere. #### Custom tax calculation with hooks Use a [checkout customization hook](https://docs.stripe.com/connect/saas/tasks/enable-in-context-selling-on-ai-agents.md#checkout-customization-hook) to handle the `v1.delegated_checkout.customize_checkout` event and return `tax_rates` for each line item. To set up the tax hook: 1. Create an endpoint on your server to receive checkout customization events. See [webhooks](https://docs.stripe.com/webhooks.md) for more information about setting up webhooks in your application. #### Node.js ```javascript const stripe = require('stripe'); const express = require('express'); const app = express(); app.use("/checkout-customization-hook", express.raw({ type: "application/json" })); app.post("/checkout-customization-hook", (req, res) => { const sig = req.headers["stripe-signature"]; const endpointSecret = process.env.WEBHOOK_SECRET; // Store this securely try { stripe.webhooks.signature.verifyHeader(req.body, sig, endpointSecret, stripe.webhooks.DEFAULT_TOLERANCE); } catch (err) { console.log(`Webhook signature verification failed: ${err.message}`); return res.status(400).send(`Webhook Error: ${err.message}`); } const event = JSON.parse(req.body); if (event.type === "v1.delegated_checkout.customize_checkout") { const data = event.data; const connectedAccount = event.context; const response = { line_items: data.line_item_details?.map((item) => ({ id: item.id, tax_rates: [ { rate_data: { display_name: "Sales tax", inclusive: false, percentage: 8.5, // Replace with your actual tax rate }, }, { rate_data: { display_name: "Environmental fee", inclusive: false, percentage: 1.0, // Replace with your actual fee rate }, }, ], })) || [], }; return res.status(200).json(response); } return res.status(400).json({ error: "Unsupported webhook type" }); }); app.listen(4567, () => console.log("Server is running on port 4567")); ``` 1. [Register your webhook URL with Stripe](https://docs.stripe.com/connect/saas/tasks/enable-in-context-selling-on-ai-agents.md#checkout-customization-hook) by contacting Stripe. 1. Handle the `v1.delegated_checkout.customize_checkout` event and return tax rates for each line item, as shown below. ## Monetize transactions By default, when you create an agentic checkout session, no application fee is applied. > You must add an application fee when your platform pays Stripe fees for a connected account. Without an application fee, you can lose money on each transaction. #### Application fee hook Use a [checkout customization hook](https://docs.stripe.com/connect/saas/tasks/enable-in-context-selling-on-ai-agents.md#checkout-customization-hook) to handle the `v1.delegated_checkout.finalize_checkout` event and return an applicaiton fee. See [webhooks](https://docs.stripe.com/webhooks.md) for information about setting up webhooks in your application. #### Node.js ```javascript const stripe = require('stripe'); const express = require('express'); const app = express(); app.use("/agentic-commerce-hook", express.raw({ type: "application/json" })); app.post("/agentic-commerce-hook", async (req, res) => { const sig = req.headers["stripe-signature"]; const endpointSecret = process.env.WEBHOOK_SECRET; // Store this securely try { stripe.webhooks.signature.verifyHeader(req.body, sig, endpointSecret, stripe.webhooks.DEFAULT_TOLERANCE); } catch (err) { console.log(`Webhook signature verification failed: ${err.message}`); return res.status(400).send(`Webhook Error: ${err.message}`); } const event = JSON.parse(req.body); console.log("Received event:", event.type); if (event.type === "v1.delegated_checkout.finalize_checkout") { const data = event.data; // Perform validation checks for order approval const orderAmount = data.amount_total || 0; const lineItems = data.line_items_details || []; const connectedAccount = event.context; const stripeCheckoutSessionId = data.checkout_session; const applicatonFee = await calculateApplicationFee(lineItems); const {reason, isApproved} = await calculateOrderApproval(); if (isApproved) { return res.status(200).json({ manual_approval_details: { type: "approved" }, application_fee_details: { application_fee_amount: applicatonFee } }); } else { return res.status(200).json({ manual_approval_details: { type: "declined", declined: { reason, } } }); } } return res.status(400).json({ error: "Unsupported webhook type" }); }); app.listen(4567, () => console.log("Server is running on port 4567")); ``` #### Charge an application fee with the platform pricing tool If a connected account uses a fee payer configuration where your platform pays Stripe fees, you can use the [platform pricing tool](https://docs.stripe.com/connect/platform-pricing-tools.md) to charge a application fee. ## Enable agents for your connected accounts Stripe offers an embedded component to help onboard connected accounts to agentic commerce. When you’re ready for your connected accounts to sell on an AI chat agent, they must review the agent terms and enable the agent using this embedded component. Stripe sends the agent an approval request that the agent must accept. The component lets connected accounts manage which AI agents can sell their products, customize how their business appears across agent platforms, and pause or stop selling on an AI chat agent. > We’re actively developing this embedded component. The code snippets are for illustrative purposes only and are subject to change. The following code example shows how your platform can integrate with this embedded component. For details on how to embed dashboard functionality into your website, see [Get started with Connect embedded components](https://docs.stripe.com/connect/get-started-connect-embedded-components.md). #### Ruby ```ruby require 'sinatra' require 'stripe' # This is a placeholder - it should be replaced with your secret API key. # Sign in to see your own test API key embedded in code samples. # Don't submit any personally identifiable information in requests made with this key. Stripe.api_key = '<>' post '/account_session' do content_type 'application/json' begin account_session = Stripe::AccountSession.create({ account: '{{CONNECTED_ACCOUNT_ID}}', components: { agentic_commerce_settings: { enabled: true, } } }) { client_secret: account_session[:client_secret] }.to_json rescue => error puts "An error occurred when calling the Stripe API to create an account session: #{error.message}"; return [500, { error: error.message }.to_json] end end ``` #### HTML + JS ```html

Payments

``` ```javascript import {loadConnectAndInitialize} from '@stripe/connect-js'; const fetchClientSecret = async () => { // Fetch the AccountSession client secret const response = await fetch('/account_session', { method: "POST" }); if (!response.ok) { // Handle errors on the client side here const {error} = await response.json(); console.error('An error occurred: ', error); document.querySelector('#error').removeAttribute('hidden'); return undefined; } else { const {client_secret: clientSecret} = await response.json(); document.querySelector('#error').setAttribute('hidden', ''); return clientSecret; } } const stripeConnectInstance = loadConnectAndInitialize({ // This is a placeholder - it should be replaced with your publishable API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. publishableKey: "<>", fetchClientSecret: fetchClientSecret, }); const paymentComponent = stripeConnectInstance.create("agentic-commerce-settings"); const container = document.getElementById("container"); container.appendChild(paymentComponent); ``` ## Respond to purchases and fulfill orders Listen to Stripe webhooks to monitor orders made on AI chat agents. When an order is confirmed, Stripe emits webhook events that your server can handle to run fulfillment logic. Set up an endpoint on your server to accept, process, and acknowledge these events. See the [webhooks guide](https://docs.stripe.com/webhooks.md) for step-by-step instructions on integrating with and testing Stripe webhooks. Stripe emits `checkout.session.completed` and `payment_intent.succeeded`. If your fulfillment logic already handles these events, you don’t need additional integration changes. You can customize your fulfillment logic for in-context agentic selling (for example, by noting in your order confirmation email that the checkout occurred through an agent). For details on setting up webhooks for connected accounts, see [Connect webhooks](https://docs.stripe.com/connect/webhooks.md). #### Node.js ```javascript const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); const express = require('express'); const app = express(); // Use the secret provided by Stripe CLI for local testing // or your webhook endpoint's secret const endpointSecret = 'whsec_...'; app.post('/webhook', async (request, response) => { const sig = request.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret); } catch (err) { response.status(400).send(`Webhook Error: ${err.message}`); return; } if (event.type === 'checkout.session.completed') { let session = event.data.object; // For V1 webhooks event.account is the connected account session = await stripe.checkout.sessions.retrieve( session.id, { expand: ["line_items.data.price.product", "line_items.data.price"], }, // If using direct charges, the checkout session is created on the connected account and the `stripeAccount` parameter must be passed. If using destination charges the checkout session is created on the platform account and `stripeAccount` parameter can be ignored. { stripeAccount: "{{CONNECTED_ACCOUNT_ID}}", }, ); // SKU id is available at session.line_items.data[number].price.external_reference fulfillCheckout(event.account, session); } response.status(200).send(); }); ``` After you receive the webhook, retrieve all required fields with a single API call. To avoid multiple requests, expand sub-resources using the [expand](https://docs.stripe.com/api/expanding_objects.md) request parameter with the preview header `Stripe-Version: 2025-12-15.preview`. ```curl curl https://api.stripe.com/v1/checkout/sessions/{{SESSION_ID}}?expand[]=line_items.data.price.product&expand[]=line_items.data.taxes&expand[]=payment_intent.latest_charge \ -u <>: \ -H "Stripe-Version: 2025-12-15.preview" \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" ``` See fields in the expanded [Checkout Session](https://docs.stripe.com/api/checkout/sessions/object.md), such as `amount_total`, quantity, and SKU ID. ### Checkout session field reference | Order field | Available resource | API path | | ----------------------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------- | | **Order date** | `CheckoutSession.PaymentIntent.LatestCharge` | `CheckoutSessions.PaymentIntent.LatestCharge.created` | | **Order quantity** | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].quantity` | | **SKU** | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].price.external_reference` | | **Product description** | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].price.product.description` | | **Unit price** | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].price.unit_amount` | | **Tax amount** | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].Taxes[].amount` | | **Tax type** | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].Taxes[].Rate.tax_type` | | **Tax rate** | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].Taxes[].Rate.percentage` | | **ShippingAddress** | `CheckoutSessions` | `CheckoutSessions.CollectedInformation.shipping_details` | | **BillingAddress** | `CheckoutSessions.PaymentIntent.LatestCharge` | `CheckoutSessions.PaymentIntent.LatestCharge.billing_details` | | **Last4** | `CheckoutSessions.PaymentIntent.LatestCharge` | `CheckoutSessions.PaymentIntent.LatestCharge.payment_method_details.card.last4` | | **ExpMonth** | `CheckoutSessions.PaymentIntent.LatestCharge` | `CheckoutSessions.PaymentIntent.LatestCharge.payment_method_details.card.exp_month` | | **ExpYear** | `CheckoutSessions.PaymentIntent.LatestCharge` | `CheckoutSessions.PaymentIntent.LatestCharge.payment_method_details.card.exp_year` | | **CreditCardType** | `CheckoutSessions.PaymentIntent.LatestCharge` | `CheckoutSessions.PaymentIntent.LatestCharge.payment_method_details.card.brand` | | **Final amount** | `CheckoutSessions` | `CheckoutSessions.amount_total` | | **GTIN** (Private preview) | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].price.product.identifiers.gtin` | | **MPN** (Private preview) | `CheckoutSessions` | `CheckoutSession.LineItems.Data[].price.product.identifiers.mpn` | | **Agent details** (Private preview) | `CheckoutSession.PaymentIntent` | `CheckoutSessions.PaymentIntent.agent_details` | | **OrderNo** | `CheckoutSession.PaymentIntent.LatestCharge` | `CheckoutSessions.PaymentIntent.LatestCharge.receipt_number` | ## Test your integration You can test your integration directly from the Dashboard in a [sandbox](https://docs.stripe.com/sandboxes.md): 1. Open the [Trigger Agentic Purchase](https://dashboard.stripe.com/test/workbench/blueprints/agentic-commerce-turnkey-agentic-purchase) blueprint in Workbench 1. Enter the connected account’s ID and a SKU ID 1. Click **Run** to simulate a charge from an agent ## Optional: Manual capture By default, Stripe captures payments immediately when a purchase completes. Contact Stripe to enable manual capture. After it’s enabled, call the capture method on the `PaymentIntent` returned in the webhook described in the previous section. ```curl curl -X POST https://api.stripe.com/v1/payment_intents/pi_3MrPBM2eZvKYlo2C1TEMacFD/capture \ -u "<>:" ``` ## Optional: Set up an order approval hook Before we confirm a payment, we check inventory based on your product catalog data and run fraud checks with [Radar](https://docs.stripe.com/radar.md) by default. To control whether we complete a purchase, configure an order approval hook. Before we complete checkout, we send an approval request to your service. You must approve or decline the request. Stripe enforces a four-second timeout for your hook. If your hook doesn’t respond within this time, Stripe declines the payment. To set up an order approval hook: 1. Specify the endpoint on the [Agentic Commerce settings](https://dashboard.stripe.com/settings/connect/agentic-commerce) page in the Dashboard. 1. Enable **Order approvals**. 1. Implement your logic at the endpoint and use the request and response formats below. ### Request format Stripe sends the following request to your endpoint: ```typescript { type: "v1.delegated_checkout.finalize_checkout", id: string, livemode: boolean, // account ID context?: string, // request specific data data: { amount_subtotal?: number, amount_total?: number, billing_details?: { name?: string, address?: { line1?: string, line2?: string, city?: string, state?: string, postal_code?: string, country?: string } }, currency: string, email?: string, line_items_details: Array<{ id: string, unit_amount: number, quantity: number, name: string }>, payment_method_details?: { type: "card" | ..., card?: { brand: "amex" | "visa" | "master_card" | ..., country?: string, exp_month: number, exp_year: number, fingerprint?: string, funding: "credit" | "debit" | "prepaid" | "unknown", iin?: string, last4: string, wallet?: { type: "apple_pay" | "google_pay" | ... } } }, phone?: string, shipping_details?: { name?: string, address?: { line1?: string, line2?: string, city?: string, state?: string, postal_code?: string, country?: string }, }, total_details?: { amount_discount?: number, amount_shipping?: number, amount_tax?: number } } } ``` ### Response format Your endpoint must return `200` HTTP responses with the following format: ```typescript { manual_approval_details: { type: "approved" | "declined", declined?: { reason: string } }, // Connect only: set an application fee for the transaction application_fee_details?: { application_fee_amount: number, transfer_data?: { amount?: number, } } } ``` ## Optional: Set up a checkout customization hook By default, Stripe calculates taxes and shipping options for your products based on the options defined in your [product catalog](https://docs.stripe.com/agentic-commerce/product-feed.md). To calculate taxes or shipping options and costs dynamically with your logic: 1. Specify the endpoint on the [Agentic Commerce settings](https://dashboard.stripe.com/settings/connect/agentic-commerce) page in the Dashboard. 1. Enable **Custom tax rates** or **Custom shipping options**. 1. Implement your logic at the endpoint and use the request and response formats below. ### Request format Stripe sends the following request to your endpoint: ```typescript { type: "v1.delegated_checkout.customize_checkout", id: string, livemode: boolean, // Connected account ID context?: string, // Request specific data data: { // Used by the seller to determine whether they can set manual tax rates on line items automatic_tax: { enabled: boolean, }, currency: string, line_item_details?: Array<{ id: string, sku_id: string, unit_amount: number, amount_discount: number, amount_subtotal: number, amount_tax: number, amount_total: number, quantity: number, name: string, tax_rates: Array<{ rate: { id: string, display_name: string, percentage: number, inclusive: boolean, } // Amount of tax applied for this rate. amount: number }> }>, shipping_details?: { // Same as the shipping rate object described at https://docs.stripe.com/api/shipping_rates/object#shipping_rate_object shipping_rate?: { id: string, display_name?: string, metadata?: Map, tax_code?: string , tax_behavior: 'unspecified' | 'included' | 'excluded', fixed_amount: { amount: number, currency: 'usd' | 'cad' | etc., currency_options.: { amount: number, tax_behavior: 'unspecified' | 'included' | 'excluded', } }, delivery_estimate?: { maximum: { unit: 'business_day' | 'day' | 'hour' | 'month' | 'year', value: number }, minimum: { unit: 'business_day' | 'day' | 'hour' | 'month' | 'year', value: number } } }, // Same as the shipping rate object described at https://docs.stripe.com/api/shipping_rates/object#shipping_rate_object shipping_rates?: Array<{ id: string, display_name?: string, metadata?: Map, tax_code?: string, tax_behavior: 'unspecified' | 'included' | 'excluded', fixed_amount: { amount: number, currency: 'usd' | 'cad' | etc., currency_options.: { amount: number, tax_behavior: 'unspecified' | 'included' | 'excluded', } }, delivery_estimate?: { maximum: { unit: 'business_day' | 'day' | 'hour' | 'month' | 'year', value: number }, minimum: { unit: 'business_day' | 'day' | 'hour' | 'month' | 'year', value: number } } }, address?: { line1?: string, line2?: string, city?: string, state?: string, postal_code?: string, country?: string } }, amount_total?: number, amount_subtotal?: number, total_details?: { amount_discount?: number, amount_shipping?: number, amount_tax?: number } } } ``` ### Response format Your endpoint must return a `200` HTTP response with the following format: ```typescript { shipping_options?: Array<{ // ID of the shipping rate, or data provided to create the shipping rate. Only provide one; not both shipping_rate?: string, shipping_rate_data: { display_name?: string, fixed_amount: { amount: number, currency: 'usd' | 'cad' | etc., }, metadata?: Map, tax_code?: string , tax_behavior?: 'unspecified' | 'included' | 'excluded', // Same as the shipping rate object described at https://docs.stripe.com/api/shipping_rates/create#create_shipping_rate-delivery_estimate delivery_estimate?: { maximum: { unit: 'business_day' | 'day' | 'hour' | 'month' | 'year', value: number }, minimum: { unit: 'business_day' | 'day' | 'hour' | 'month' | 'year', value: number } } }, }>, line_items?: Array<{ // Corresponding ID of the line item to update id: string, // List of tax rates to apply to this line item // Provide either `rate` or `rate_data` tax_rates: Array<{ // ID of a v1 tax rate rate?: string, // Or `rate_data`. // This will use an existing tax rate that matches the params or will create one if a matching rate does not exist rate_data?: { display_name: string, inclusive: boolean, // percentage out of 100 percentage: number, } }, }> } ``` ## Optional: Test your hooks You can test your order approval or checkout customization hook by providing a publicly accessible endpoint that can accept hook requests with a `POST` method. Set up your endpoint function so that it: 1. Handles `POST` requests with a JSON payload 1. Returns a successful status code (`200`) For local development, use a tunneling tool such as [ngrok](https://ngrok.com/) to expose your local endpoint. ### Example endpoint This code snippet is an endpoint function configured to receive `v1.delegated_checkout.finalize_checkout` requests and return a `200` responses. #### Ruby ```ruby require 'json' require 'sinatra' require 'stripe' set :port, 4242 # Replace with your endpoint's secret from your webhook settings endpoint_secret = 'whsec_...' # Using Sinatra post '/hooks' do payload = request.body.read sig_header = request.env['HTTP_STRIPE_SIGNATURE'] # Verify the webhook signature begin Stripe::Webhook::Signature.verify_header( payload, sig_header, endpoint_secret ) rescue Stripe::SignatureVerificationError => e status 400 return end # Handle the payload payload = JSON.parse(payload) case payload['type'] when 'v1.delegated_checkout.finalize_checkout' # Check inventory and accept payment data = { manual_approval_details: { type: 'approved' } } end status 200 body data.to_json end ``` ## Optional: Handle the agreement with agents > This API is in private preview. Examples below are subject to change. When a connected account onboards to an agent, Stripe creates an Agreement object between the seller and the agent. Stripe emits webhook events to the platform and the agent whenever the Agreement object’s status changes: - `v2.orchestrated_commerce.agreement.created`: An Agreement was created between the seller and the agent. - `v2.orchestrated_commerce.agreement.partially_confirmed`: The seller has confirmed the Agreement. - `v2.orchestrated_commerce.agreement.confirmed`: The agent has confirmed the Agreement. - `v2.orchestrated_commerce.agreement.terminated`: Either the seller or agent terminated the Agreement. Listen for these events on your webhook endpoint to keep your platform’s state in sync (for example, updating UI, enabling or disabling agent access, or notifying sellers). ### Fetch an Agreement Retrieve an Agreement object using the Agreements API: ```curl curl https://api.stripe.com/v2/orchestrated_commerce/agreements/{{AGREEMENT_ID}} \ -H "Authorization: Bearer {{API_KEY}}" \ -H "Stripe-Version: 2025-12-19-17.internal" \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" ``` ## Optional: Send incremental inventory updates In addition to uploading your product catalog, you can send individual product inventory updates using the Inventory Feed API. Use the same upload process as catalog uploads, but set `standard_data_format` to `inventory_feed`: ```curl # Step 1: Upload your CSV using the Files API curl https://files.stripe.com/v1/files \ -u <>: \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" \ -F purpose=data_management_manual_upload \ -F file="@/path/to/your/file.csv;type=text/csv" # Step 2: Create an ImportSet object curl https://api.stripe.com/v1/data_management/import_sets \ -H "Stripe-Version: ${STRIPE_API_VERSION};udap_beta=v1" \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" \ -u <>: \ -d file={{FILE_ID}} \ --data-urlencode standard_data_format="inventory_feed" ``` ## Optional: Handle refunds and disputes If you already use the [PaymentIntents](https://docs.stripe.com/api/payment_intents.md) or [Checkout Sessions API](https://docs.stripe.com/api/checkout/sessions.md), your existing refunds and disputes integration doesn’t require changes for in-context agentic selling. PaymentIntents are still created for in-context agentic selling flows. As long as you associate the `PaymentIntent` ID with your order, your refunds and disputes integration continues to work. After checkout succeeds, you can initiate a refund if a customer cancels the order from your website or through customer service. If you already use the Checkout Sessions or PaymentIntents API, your existing refund flow works without changes for in-context agentic selling. Manage refunds and disputes without code in the Dashboard from the [Transactions page](https://dashboard.stripe.com/payments). To handle refunds programmatically, integrate with the [Refunds API](https://docs.stripe.com/api/refunds.md) for post-checkout cancellation or refund requests.