Skip to main content

UCP Endpoint Reference

Discovery

GET /.well-known/ucp

Returns the UCP discovery manifest describing the store's services, capabilities, and supported payment handlers.

Headers: None required.

Response:

{
"ucp": {
"version": "2026-01-11",
"status": "success",
"services": {
"dev.ucp.shopping": [
{
"transport": "rest",
"endpoint": "https://api.jyt.com/ucp"
}
]
},
"capabilities": {
"dev.ucp.shopping.checkout": [{ "version": "2026-01-11" }],
"dev.ucp.shopping.cart": [{ "version": "2026-01-11" }],
"dev.ucp.shopping.order": [{ "version": "2026-01-11" }],
"dev.ucp.shopping.fulfillment": [{ "version": "2026-01-11" }]
},
"payment_handlers": [
{ "id": "payu", "name": "PayU", "currencies": ["inr"] },
{ "id": "stripe", "name": "Stripe", "currencies": ["usd", "eur", "gbp"] }
]
}
}

Checkout Sessions

POST /ucp/checkout-sessions

Creates a new checkout session (Medusa cart) with line items, buyer info, and optional shipping address.

Request body:

{
line_items: Array<{
item: { id: string } // variant ID
quantity: number
}>
buyer?: {
email?: string
first_name?: string
last_name?: string
full_name?: string
phone_number?: string
}
shipping_address?: UcpAddress
context?: {
region_id?: string
currency?: string // ISO 4217 code, e.g. "usd", "inr"
}
discounts?: {
codes?: string[]
}
}

Response (201):

{
ucp: { version: "2026-01-11", status: "success" }
id: string // cart ID
status: "incomplete" | "ready_for_complete"
currency: string // e.g. "USD"
line_items: UcpLineItem[]
totals: UcpTotal[]
buyer?: { email?: string, first_name?: string, ... }
shipping_address?: UcpAddress
fulfillment?: UcpFulfillment
messages: UcpMessage[]
links: UcpLink[]
}

Example:

curl -X POST https://api.jyt.com/ucp/checkout-sessions \
-H "Content-Type: application/json" \
-H "UCP-Agent: profile=\"https://agent.example/profile\"" \
-H "Request-Id: req-001" \
-H "x-publishable-api-key: pk_..." \
-d '{
"line_items": [{ "item": { "id": "variant_01..." }, "quantity": 2 }],
"buyer": { "email": "[email protected]" },
"context": { "region_id": "reg_01..." }
}'

GET /ucp/checkout-sessions/:id

Retrieves a checkout session by ID.

Response (200): Same shape as POST response.

Errors:

StatusCodeDescription
404not_foundSession does not exist

PUT /ucp/checkout-sessions/:id

Updates an existing checkout session. Supports partial updates — only provided fields are modified.

Request body (all optional):

{
buyer?: { email?: string, first_name?: string, last_name?: string, ... }
shipping_address?: UcpAddress
line_items?: Array<{
line_item_id?: string // existing item to update
item?: { id: string } // new item to add
quantity: number
}>
fulfillment_option_id?: string
discounts?: { codes?: string[] }
}

Response (200): Updated session.

Notes:

  • If shipping_address changes the country, the cart's region is automatically switched to a matching region.
  • Providing line_item_id updates an existing line item's quantity; providing item.id adds a new line item.

POST /ucp/checkout-sessions/:id/complete

Initiates payment for a checkout session. Returns a next_action the agent/shopper must take to complete payment.

Prerequisites:

  • At least one line item
  • Buyer email set
  • Shipping address with address_1 (not just an auto-created empty record)

Request body:

{
payment: {
instruments: Array<{
handler_id: string // "payu" or "stripe"
instrument?: {
token?: string // optional payment instrument token
}
}>
}
}

Response (200):

{
ucp: { version: "2026-01-11", status: "success" }
id: string
status: "complete_in_progress"
payment: {
handler_id: "payu" | "stripe"
provider_id: string // e.g. "pp_payu_payu"
next_action: {
type: "redirect" | "client_secret"
url?: string // for redirect type
client_secret?: string // for client_secret type
description: string
}
description: string
}
...
}

Payment provider selection:

Cart currencyProviderHandler IDNext action
INRPayUpayuRedirect to PayU hosted page
Non-INRStripestripeRedirect to Stripe hosted card page (falls back to client_secret)

After completion: Poll GET /ucp/checkout-sessions/:id until status changes to "completed". The response will include an order link.

Errors:

StatusCodeDescription
400missing_itemsNo line items in cart
400missing_emailBuyer email not set
400missing_shipping_addressNo shipping address with street
404not_foundSession does not exist
500payment_setup_failedCould not create payment collection
500payment_session_failedPayment provider rejected session init

Catalog

POST /ucp/catalog/search

Full-text search of the storefront catalog.

Request body:

{
query?: string // full-text search
filters?: {
category?: string // category ID
collection?: string // collection ID
}
pagination?: {
limit?: number // default 20
offset?: number // default 0
}
}

Response (200):

{
ucp: { version: "2026-01-11", status: "success" }
products: UcpProduct[]
count: number
offset: number
limit: number
}

POST /ucp/catalog/lookup

Retrieve specific products by ID.

Request body:

{
ids: string[] // product IDs
}

Response (200): Same as search, but filtered to requested IDs.


Orders

GET /ucp/orders/:id

Retrieve an order by ID. Includes fulfillment events and tracking info.

Response (200):

{
ucp: { version: "2026-01-11", status: "success" }
id: string
display_id: string | null
checkout_id: string | null // original cart ID
status: string // e.g. "completed", "pending"
currency: string
line_items: UcpLineItem[]
totals: UcpTotal[]
fulfillment_status: string
fulfillment_events: Array<{
type: "shipped" | "created"
timestamp: string
tracking_number: string | null
carrier: string | null
items: Array<{ product_id: string, quantity: number }>
}>
buyer?: { email: string }
shipping_address?: UcpAddress
links: UcpLink[]
}

Errors:

StatusCodeDescription
404not_foundOrder does not exist

Types

UcpAddress

{
first_name?: string
last_name?: string
street_address: string // maps to Medusa address_1
address_locality: string // maps to Medusa city
address_region?: string // maps to Medusa province
address_country: string // ISO 3166 alpha-2/alpha-3/full name
postal_code?: string
phone_number?: string
}

Country code normalization handles:

  • Alpha-2: us, in, gb
  • Alpha-3: usa, ind, gbr
  • Full names: United States, India, United Kingdom

UcpLineItem

{
id: string
item: {
id: string // variant ID
title: string
price: number
}
quantity: number
totals: Array<{ type: "line_total", display_text: string, amount: number }>
}

UcpTotal

{
type: "subtotal" | "fulfillment" | "tax" | "discount" | "total"
display_text: string
amount: number
}

UcpMessage

// Error message
{
type: "error"
code: string // e.g. "missing_email", "missing_shipping_address"
content: string
severity: "recoverable" | "fatal"
path?: string // JSON pointer, e.g. "$.buyer.email"
}

// Info message
{
type: "info"
code?: string
content: string
}

UcpProduct

{
id: string
title: string
description: string
handle: string
categories: string[]
price_range: {
min: { amount: number, currency: string }
max: { amount: number, currency: string }
} | null
variants: Array<{
id: string
title: string
sku: string | null
price: { amount: number, currency: string } | null
availability: {
available: boolean
status: "in_stock" | "out_of_stock"
}
}>
media: Array<{ url: string, type: "image" }>
}

Error Envelope

All errors follow a consistent shape:

{
"ucp": {
"version": "2026-01-11",
"status": "error"
},
"messages": [
{
"type": "error",
"code": "not_found",
"content": "Checkout session not found",
"severity": "fatal"
}
]
}
CodeHTTP StatusSeverityDescription
not_found404fatalResource does not exist
missing_items400recoverableCart has no line items
missing_email400recoverableBuyer email not set
missing_shipping_address400recoverableNo shipping address
country_not_supported400recoverableCountry not in any region
payment_setup_failed500fatalPayment collection creation failed
payment_session_failed500fatalPayment session init failed
checkout_failed500fatalGeneral checkout error
internal_error500fatalUnhandled error