UCP Checkout Sessions: A Deep Dive

UCP Checkout Sessions: A Deep Dive

Checkout sessions are the core transactional mechanism in Universal Commerce Protocol. They represent the stateful journey from “add to cart” to “order confirmed”—but designed for AI agents rather than human shoppers.

This deep dive covers the complete lifecycle, implementation patterns, and edge cases you’ll encounter in production.

Session Lifecycle Overview

Session Lifecycle

Sessions move through discrete states:

  1. created — Session initialized with cart items
  2. address_set — Shipping address provided, rates calculated
  3. shipping_selected — Shipping method chosen
  4. payment_pending — Ready for payment initiation
  5. payment_processing — Payment in progress
  6. completed — Order placed successfully
  7. expired — Session timed out without completion
  8. abandoned — Explicitly cancelled

Creating a Session

Sessions begin when an agent adds items to cart:

POST /api/ucp/checkout/sessions
Content-Type: application/json

{
  "items": [
    {
      "product_id": "SKU-RUNNER-PRO-001",
      "variant_id": "size-10-color-black",
      "quantity": 1
    },
    {
      "product_id": "SKU-SOCKS-CUSHION-003",
      "quantity": 2
    }
  ],
  "locale": "en-US",
  "currency": "USD"
}

Response Structure

{
  "session_id": "cs_live_a1b2c3d4e5f6",
  "status": "created",
  "created_at": "2026-01-05T14:30:00Z",
  "expires_at": "2026-01-05T15:30:00Z",
  "items": [
    {
      "line_id": "li_001",
      "product_id": "SKU-RUNNER-PRO-001",
      "variant_id": "size-10-color-black",
      "name": "CloudRunner Pro Marathon Shoe",
      "quantity": 1,
      "unit_price": 17999,
      "line_total": 17999,
      "available": true,
      "image_url": "https://store.com/images/runner-pro.jpg"
    },
    {
      "line_id": "li_002",
      "product_id": "SKU-SOCKS-CUSHION-003",
      "name": "Performance Cushion Socks (3-pack)",
      "quantity": 2,
      "unit_price": 1599,
      "line_total": 3198,
      "available": true,
      "image_url": "https://store.com/images/socks.jpg"
    }
  ],
  "totals": {
    "subtotal": 21197,
    "discount": 0,
    "shipping": null,
    "tax": null,
    "total": 21197
  },
  "currency": "USD",
  "requires": ["shipping_address"]
}

Key Implementation Notes

Pricing should be authoritative. Don’t trust prices sent by the agent. Always look up current catalog prices at session creation.

Inventory validation is critical. Check availability before confirming items. Return clear errors if products are out of stock or discontinued.

Session expiration prevents stale carts. 60 minutes is typical, but adjust based on your checkout complexity.

Updating Sessions

Sessions are modified through PATCH requests. Each update recalculates totals.

Adding Shipping Address

PATCH /api/ucp/checkout/sessions/cs_live_a1b2c3d4e5f6
Content-Type: application/json

{
  "shipping_address": {
    "recipient": "John Smith",
    "line1": "123 Market Street",
    "line2": "Apt 4B",
    "city": "San Francisco",
    "region": "CA",
    "postal_code": "94105",
    "country": "US",
    "phone": "+14155551234"
  }
}

Response now includes shipping options:

{
  "session_id": "cs_live_a1b2c3d4e5f6",
  "status": "address_set",
  "shipping_address": { /* address object */ },
  "shipping_options": [
    {
      "id": "standard",
      "name": "Standard Shipping",
      "description": "5-7 business days",
      "price": 799,
      "estimated_delivery": {
        "min_days": 5,
        "max_days": 7
      }
    },
    {
      "id": "express",
      "name": "Express Shipping",
      "description": "2-3 business days",
      "price": 1499,
      "estimated_delivery": {
        "min_days": 2,
        "max_days": 3
      }
    },
    {
      "id": "overnight",
      "name": "Overnight Shipping",
      "description": "Next business day",
      "price": 2999,
      "estimated_delivery": {
        "min_days": 1,
        "max_days": 1
      }
    }
  ],
  "totals": {
    "subtotal": 21197,
    "discount": 0,
    "shipping": null,
    "tax": null,
    "total": 21197
  },
  "requires": ["shipping_selection"]
}

Selecting Shipping Method

PATCH /api/ucp/checkout/sessions/cs_live_a1b2c3d4e5f6
Content-Type: application/json

{
  "shipping_selection": "express"
}

Response now includes calculated taxes:

{
  "session_id": "cs_live_a1b2c3d4e5f6",
  "status": "shipping_selected",
  "shipping_selection": {
    "id": "express",
    "name": "Express Shipping",
    "price": 1499
  },
  "totals": {
    "subtotal": 21197,
    "discount": 0,
    "shipping": 1499,
    "tax": 1872,
    "total": 24568
  },
  "requires": ["payment"]
}

Applying Discount Codes

PATCH /api/ucp/checkout/sessions/cs_live_a1b2c3d4e5f6
Content-Type: application/json

{
  "discount_code": "WELCOME20"
}

Validation response if invalid:

{
  "error": {
    "code": "invalid_discount",
    "message": "Code WELCOME20 has expired",
    "field": "discount_code"
  }
}

Payment Initiation

When all requirements are satisfied, agents can initiate payment:

POST /api/ucp/checkout/sessions/cs_live_a1b2c3d4e5f6/pay
Content-Type: application/json

{
  "payment_method": "card",
  "payment_token": "tok_visa_4242424242424242",
  "billing_address": {
    "line1": "123 Market Street",
    "city": "San Francisco",
    "region": "CA",
    "postal_code": "94105",
    "country": "US"
  }
}

Successful Payment Response

{
  "session_id": "cs_live_a1b2c3d4e5f6",
  "status": "completed",
  "order": {
    "order_id": "ORD-2026-01-05-789456",
    "order_number": "FP-789456",
    "status": "confirmed",
    "created_at": "2026-01-05T14:45:00Z",
    "totals": {
      "subtotal": 21197,
      "discount": 0,
      "shipping": 1499,
      "tax": 1872,
      "total": 24568
    },
    "items": [ /* line items */ ],
    "shipping_address": { /* address */ },
    "tracking_url": "https://store.com/orders/FP-789456/track",
    "estimated_delivery": "2026-01-08"
  },
  "payment": {
    "id": "pay_a1b2c3d4",
    "status": "captured",
    "amount": 24568,
    "method": "card",
    "last_four": "4242"
  }
}

Handling 3D Secure

If the payment requires additional authentication:

{
  "session_id": "cs_live_a1b2c3d4e5f6",
  "status": "payment_action_required",
  "payment": {
    "id": "pay_a1b2c3d4",
    "status": "requires_action",
    "action": {
      "type": "three_d_secure",
      "redirect_url": "https://acs.bank.com/3ds/challenge/...",
      "return_url": "https://store.com/api/ucp/checkout/sessions/cs_live_a1b2c3d4e5f6/3ds-return"
    }
  }
}

Agents handle 3DS by directing the user to complete authentication, then polling the session for status updates.

Error Handling

Inventory Changes During Checkout

{
  "error": {
    "code": "item_unavailable",
    "message": "One or more items are no longer available",
    "details": [
      {
        "line_id": "li_001",
        "product_id": "SKU-RUNNER-PRO-001",
        "issue": "out_of_stock",
        "available_quantity": 0
      }
    ]
  }
}

Price Changes During Checkout

{
  "error": {
    "code": "price_changed",
    "message": "Prices have changed since session creation",
    "details": [
      {
        "line_id": "li_002",
        "previous_price": 1599,
        "current_price": 1799
      }
    ],
    "action_required": "confirm_price_change"
  }
}

Payment Failures

{
  "error": {
    "code": "payment_declined",
    "message": "Your card was declined",
    "decline_code": "insufficient_funds",
    "retry_allowed": true
  }
}

Session Management Best Practices

1. Idempotency

Use idempotency keys to prevent duplicate operations:

POST /api/ucp/checkout/sessions/cs_live_a1b2c3d4e5f6/pay
Idempotency-Key: pay_attempt_abc123

If the same key is sent twice, return the original response instead of processing again.

2. Optimistic Locking

Include version numbers to detect concurrent modifications:

{
  "session_id": "cs_live_a1b2c3d4e5f6",
  "version": 4,
  // ... rest of session
}

Reject updates where the provided version doesn’t match:

PATCH /api/ucp/checkout/sessions/cs_live_a1b2c3d4e5f6
If-Match: "4"

3. Session Recovery

Store enough state to recover from failures:

{
  "recovery": {
    "can_retry_payment": true,
    "items_reservable": true,
    "original_session_id": "cs_live_a1b2c3d4e5f6",
    "recovery_expires_at": "2026-01-05T16:30:00Z"
  }
}

4. Rate Limiting

Implement per-session rate limits to prevent abuse:

  • Session creation: 100/hour per IP
  • Session updates: 30/minute per session
  • Payment attempts: 5/hour per session

Testing Checkout Sessions

Test Card Numbers

NumberBehavior
4242424242424242Succeeds
4000000000000002Declines
4000000000003220Requires 3DS
4000000000009995Insufficient funds

Test Scenarios Checklist

  • Successful checkout end-to-end
  • Inventory changes mid-checkout
  • Price changes mid-checkout
  • Session expiration handling
  • Payment decline and retry
  • 3D Secure flow completion
  • Discount code validation
  • International shipping address
  • Multi-item cart modifications

Implementing checkout sessions? Our open-source SFCC cartridge includes a complete reference implementation. Questions? Get in touch.

KEEP READING

Related Articles

Why We Open Sourced Our UCP Cartridge

Why We Open Sourced Our UCP Cartridge

We open-sourced our UCP cartridge for Salesforce Commerce Cloud because agentic commerce adoption requires the whole ecosystem to move — not just well-resourced enterprises. An MIT-licensed, productio ...

Read Article
What is Universal Commerce Protocol? A Technical Primer

What is Universal Commerce Protocol? A Technical Primer

Universal Commerce Protocol (UCP) is an open standard developed by Google, Shopify, and 20+ partners that enables AI agents to discover products, compare options, and complete purchases on behalf of s ...

Read Article
Introducing the First Open-Source UCP Cartridge for SFCC

Introducing the First Open-Source UCP Cartridge for SFCC

The ucp-sfcc cartridge is the first open-source, MIT-licensed implementation of Universal Commerce Protocol (UCP) for Salesforce Commerce Cloud. It enables SFCC merchants to expose a UCP discovery man ...

Read Article