Open Storefront Protocol Specification
The formal specification for OSP v3.0. This document defines the complete protocol for AI agent-to-brand commerce.
1. Introduction
The Open Storefront Protocol (OSP) is an open standard that defines how AI shopping agents interact with brand storefronts. It provides a structured, machine-readable interface for product discovery, search, negotiation, and transactions.
Purpose
OSP enables any AI agent - whether it's ChatGPT, Claude, Gemini, or a custom shopping assistant - to query products, negotiate prices, and complete purchases from any OSP-compatible storefront using a single, consistent protocol.
Design Goals
- Simplicity: RESTful JSON APIs that any developer can implement in hours
- Machine-readability: Every response is structured for AI consumption, not human browsing
- Extensibility: Custom attributes and metadata without breaking the protocol
- Backward compatibility: New features never break existing integrations within a major version
Versioning
OSP uses semantic versioning. The current version is 3.0. Minor versions (3.1, 3.2) add features without breaking changes. Major versions may introduce breaking changes with a 6-month deprecation window. A withdrawal for a correctness or safety defect is immediate and does not use that window, as POST /transactions was on 2026-09-10.
2. Protocol Overview
Architecture
OSP follows a three-tier architecture:
Request/Response Lifecycle
- Agent sends an authenticated request to the storefront API endpoint
- The storefront validates the API key, rate limits, and request schema
- The request is processed against the brand's catalog and policies
- A structured JSON response is returned with product data, negotiation state, or transaction status
- The agent parses the response and presents results to the user
Base URL
https://cresva.ai/api/storefront/{brandId}All endpoints are relative to this base URL. Replace {brandId}with the brand's unique identifier.
The two hosts are not interchangeable. api.cresva.ai serves the platform API at /v1/ and the .well-known documents, and answers every other path with a 404 before routing. A storefront path sent there returns not_found whatever else is right about it.
Content Type
All requests and responses use application/json. Requests must include the Content-Type: application/json header for POST/PUT/PATCH methods.
Custody
Cresva never holds, receives, routes, finances, insures, or pays out funds. OSP facilitates discovery, negotiation, and order handoff between an agent and a storefront; payment happens directly between the buyer and the merchant, on the merchant's own rails. See Section 6, Transactions, for how this applies to the transaction lifecycle specifically.
3. Discovery
Discovery allows AI agents to find OSP-compatible storefronts. Brands publish a discovery file at a well-known URL, similar to robots.txt but designed for AI agents.
Discovery File
A storefront is described by a JSON document at /.well-known/osp.json. On a multi-tenant host it is addressed by brand, and the bare path describes the platform rather than a shop:
GET https://{domain}/.well-known/osp.jsonThe discovery file format:
{
"osp_version": "3.0",
"storefront_url": "https://cresva.ai/api/storefront/brand_abc123",
"brand_id": "brand_abc123",
"brand_name": "Acme Co",
"capabilities": [
"search",
"recommend",
"compare",
"negotiate",
"transact"
],
"supported_currencies": ["USD", "EUR"],
"supported_languages": ["en", "es", "fr"],
"rate_limits": {
"public": "60/minute",
"authenticated": "300/minute",
"anonymous": "10/minute"
},
"documentation": "https://developers.cresva.ai/protocol/spec"
}Discovery File Fields
osp_versionstringrequiredThe OSP specification version this storefront IMPLEMENTS, a semver. Currently "3.0". Not the same fact as the X-OSP-Version header, which carries a dated wire revision; see Protocol Version Header below.
storefront_urlstringrequiredThe base URL for all API requests to this storefront.
brand_idstringrequiredUnique identifier for the brand.
brand_namestringrequiredHuman-readable brand name.
capabilitiesstring[]requiredList of supported OSP features: search, recommend, compare, negotiate, transact.
supported_currenciesstring[]ISO 4217 currency codes accepted by this storefront.
supported_languagesstring[]ISO 639-1 language codes supported.
rate_limitsobjectRate limit information by key type.
documentationstringURL to the storefront's API documentation.
4. Query Protocol
The query protocol is the primary interface for agents to search and retrieve products. All queries go through a unified endpoint that supports multiple intent types.
Endpoint
POST /queryAgentQueryRequest
The request body for a query:
protocol_versionstringrequiredThe protocol version this request is written against.
request_idstringrequiredCaller-generated id for this request.
timestampstringrequiredWhen the request was made.
agentobjectrequiredIdentifies the caller: platform, optional version and session_id. Session belongs here, not in a context block.
queryobjectrequiredThe query itself. It carries text, intent, and both filters and context nested inside it, not beside it.
paginationobjectCarries page, limit and cursor.
responseobjectShapes the answer: format, include, language.
Inside query: filters takes category, price_min, price_max, currency, attributes, in_stock and rating_min, as separate fields rather than a nested range object. context takes user_budget, user_preferences, previous_products_viewed and comparison_against.
// No credential. x-cresva-auth for this operation is "none".
POST /api/storefront/{brandId}/query
Content-Type: application/json
{
"query": {
"text": "snowboard",
"intent": "search",
"filters": { "in_stock": true, "price_max": 900, "currency": "USD" },
"context": { "user_preferences": ["all-mountain"] }
},
"agent": { "platform": "audit" },
"pagination": { "limit": 2 }
}Query response
resultsAgentProductCard[]requiredThe matching product cards. Note the key: /products and /search answer with products, this endpoint answers with results.
metaobjectCarries total and timing.
5. Product Card Format
The AgentProductCard is the standard format for representing a product to an AI agent. It is optimized for machine consumption - structured, typed, and rich with metadata.
AgentProductCard Fields
idstringrequiredUnique product identifier. A cuid, not a prefixed id.
titlestringrequiredProduct title.
priceobjectrequiredAn object, not a number: amount, currency and formatted. amount is a decimal, not an integer of cents, and is null when the shop's currency could not be established.
availabilitystringStock status, e.g. in_stock.
ratingnumber | nullnull when no rating could be established. Never 0 to mean unknown.
summarystringA short description. May be empty.
attributesobjectA free map of the storefront's own keys. There is no standardised taxonomy.
purchase_urlstringWhere a shopper completes the purchase, on the merchant's own store.
imagesstring[]Image URLs. Strings, not objects.
Example
// GET /api/storefront/{brandId}/products?limit=1
// Captured from production, 2026-09-16.
{
"id": "cmtpqwhit001ajv04e1vvv5j6",
"title": "The Videographer Snowboard",
"summary": "",
"price": { "amount": 885.95, "currency": "USD", "formatted": "$885.95" },
"availability": "in_stock",
"rating": null,
"attributes": {},
"comparison_context": null,
"purchase_url": "https://cresva-pilot-two.myshopify.com/products/the-videographer-snowboard",
"images": ["https://cdn.shopify.com/s/files/1/0803/1135/3541/files/Main.jpg?v=1787511528"]
}6. Negotiation Protocol
The negotiation protocol enables AI agents to negotiate pricing with brand storefronts. This supports direct offers, counter-offers, and alternative deal structures.
Actions
initiateactionAgent sends an initial offer for a product.
counteractionBrand responds with a counter-offer.
acceptactionEither party accepts the current offer.
rejectactionEither party rejects and ends negotiation.
withdrawactionAgent withdraws their offer before a response.
inquireactionAgent asks what deals are possible without committing.
Endpoint
POST /negotiateNegotiation Request
actionstringrequiredOne of: "initiate", "counter", "accept", "reject", "withdraw", "inquire".
negotiation_idstringRequired for all actions except "initiate" and "inquire".
product_idstringrequiredThe product being negotiated.
offered_pricenumberThe price being offered (for initiate and counter).
currencystringCurrency of the offer. Default: storefront's primary currency.
quantitynumberNumber of units. Default: 1.
messagestringOptional natural language message with the offer.
State Machine
INITIATED ├── COUNTERING (brand sends counter-offer) │ ├── COUNTERING (agent counters back) │ ├── ACCEPTED (either party accepts) │ └── REJECTED (either party rejects) ├── ACCEPTED (brand accepts initial offer) ├── REJECTED (brand rejects) ├── EXPIRED (no response within timeout) └── WITHDRAWN (agent withdraws offer)
Alternative Deal Types
- Volume discount: Better price for buying multiple units
- Bundle: Discount when purchasing with related products
- Subscription: Lower per-unit price for recurring purchases
- Time-limited: Special price valid for a limited window
Timeout Rules
Negotiations expire after 24 hours of inactivity. Brands can configure shorter timeouts. That figure is the intended behaviour and is not sourced to a published constant, so do not build a timer against it. The expires_at field in the response indicates when the current offer expires.
Example: Initiating a Negotiation
// Request
POST /negotiate
{
"action": "initiate",
"product_id": "prod_h7k2m",
"offered_price": 149.99,
"currency": "USD",
"quantity": 1,
"message": "User is comparing with a competitor at $145"
}
// Response
{
"negotiation_id": "neg_a1b2c3",
"status": "COUNTERING",
"original_price": 179.99,
"offered_price": 149.99,
"counter_price": 164.99,
"currency": "USD",
"message": "We can offer 8% off. Bundle with our carrying case for an additional 5% off.",
"alternatives": [
{
"type": "bundle",
"products": ["prod_h7k2m", "prod_case01"],
"bundle_price": 189.99,
"savings": "15%"
}
],
"expires_at": "2026-03-28T12:00:00Z"
}7. Transaction Protocol
The transaction protocol manages the full lifecycle of a purchase, from cart creation through payment, fulfillment, and completion.
Transaction Lifecycle
created_txn → confirmed_txn → paid_txn → fulfilling_txn → completed_txn
↘ cancelled_txn
↘ refunded_txn
↘ disputed_txnEndpoints
/checkout/sessionsOpen a checkout session, priced from the catalogue/checkout/sessions/{id}/completeCreate the merchant's order and return a link to pay it/checkout/sessions/{id}/statusHas the buyer paid yet/transactionsWithdrawn 2026-09-10. Returns 410; use a checkout session./transactions/{id}Get transaction status/transactions/{id}/confirmConfirm a transaction and proceed to payment/transactions/{id}/cancelCancel a transactionConfirmation Window
A transaction MAY carry an optional confirmation window: a status-only period after payment during which the buyer can confirm receipt early, or raise a dispute, before the window auto-closes on its own due date. STATUS ONLY: no funds move through Cresva at any point, at any status value. This is a release-timing state machine on a record, not fund custody. The confirmation window's status is one of pending_confirmation, confirmed, or disputed, and starts at pending_confirmation only when the storefront explicitly enables it on transaction creation.
/transactions/{id}/confirmation-window/confirmClose the confirmation window early (buyer confirms receipt)/transactions/{id}/confirmation-window/disputeRecord a dispute, moving the window to disputedPayment
Cresva never holds funds. Payment happens directly between the buyer and the merchant, on the merchant's own payment rails, exactly as it would on the merchant's own storefront. OSP is not a party to the transaction, does not process the payment, and holds no funds at any point in this flow. Where a payment reference is useful, the storefront may include an external id from its own payment provider on the transaction record; OSP stores it as-is without acting on it.
Example: Opening a Checkout Session
// Request. Needs a secret key.
POST /api/storefront/{brandId}/checkout/sessions
{
"items": [
{ "product_id": "cmtpqwhge000wjv049l5iij9q", "quantity": 1 }
]
}
// There is no price field, and that is the point. A line is priced
// from the catalogue at the moment it is added, never from this body.
// Completing the session returns a link the buyer opens to pay
// the merchant. Cresva takes nothing and holds nothing.
POST /api/storefront/{brandId}/checkout/sessions/{sessionId}/completePOST /transactions is withdrawn and answers 410 Gone. See the transactions reference for the refusal body and what replaced it.
8. Trust Score
Trust scores help AI agents make informed recommendations. Every OSP storefront has a composite trust score based on multiple quality and reliability signals.
Trust Score Format
scorenumberrequiredComposite score from 0-100.
tierstringrequiredTier: platinum (90-100), gold (75-89), silver (60-74), bronze (40-59), unrated (under 40). Lowercase, as returned.
componentsobjectIndividual score components (0-100 each).
Score Components
data_accuracynumber | nullHow accurately product listings match what is delivered.
fulfillment_speednumber | nullHow quickly orders reach the buyer.
return_ratenumber | nullHow often orders come back.
negotiation_fairnessnumber | nullWhether agreed prices are honoured.
response_reliabilitynumber | nullWhether the storefront answers, and how consistently.
dispute_recordnumber | nullDisputes raised and how they ended.
customer_satisfactionnumber | nullAggregated feedback.
Each is null when it could not be measured, rather than a plausible middle number. Read measured_components and measured_weight before trusting any of them.
Tier System
Agent Usage Guidelines
Agents SHOULD use trust scores to weight recommendations. Higher-trust storefronts should be preferred when product quality and price are comparable. Agents MUST NOT hide products solely based on trust score but MAY include trust information in recommendations to users.
9. Events + Webhooks
OSP supports real-time event notifications via webhooks. Storefronts emit events for state changes across queries, negotiations, transactions, and trust scores.
Event Types
transaction.createdA transaction record was opened.transaction.confirmedThe transaction was confirmed.transaction.paidnot yet emittedPayment was recorded against it. Cresva did not take it: this reflects what the merchant's own rails reported.transaction.shippednot yet emittedThe merchant marked it shipped.transaction.completedThe transaction reached its end state.transaction.cancelledIt was cancelled before completion.transaction.disputedA dispute was recorded. Cresva records it and does not adjudicate it.transaction.refundednot yet emittedA refund was recorded.confirmation_window.openednot yet emittedThe window opened on a transaction.confirmation_window.confirmedThe buyer confirmed and the window closed early.confirmation_window.disputedThe buyer disputed within the window.storefront.queryAn agent ran a structured query.storefront.search_emptyA search returned nothing. This is the one worth subscribing to first: it is demand you could not answer.storefront.product_viewednot yet emittedAn agent read one product.storefront.product_recommendedA product was returned by the recommend endpoint.offer.claimedAn agent claimed an offer.offer.redeemedA claimed offer was redeemed.offer.depletedAn offer ran out of remaining claims.offer.expiredAn offer passed its end date.certification.startedA certification run began.certification.passedThe run reached bronze or above.certification.failedThe run scored below 50.certification.expiringA certificate is within 7 days of expiry.certification.revokedA certificate was revoked.alert.visibility_dropAgent visibility fell.alert.competitor_gainedA competitor gained ground.alert.zero_results_trendingnot yet emittedSearches returning nothing are trending up.alert.rate_limit_hitCallers are hitting a rate limit against this brand.funnel.agent_clickAn agent followed a purchase URL.funnel.purchaseA purchase was attributed to an agent path.funnel.review_requestednot yet emittedA review was requested.product.updatedA product changed.product.new_reviewnot yet emittedA review landed on a product.product.score_changedA product's score moved.feedback.submittedFeedback was submitted.feedback.negativeThe feedback was negative.Webhook Delivery Format
{
"id": "d1ae5039-4640-4c9f-9e49-e2ee545cde2f",
"type": "transaction.completed",
"created_at": "2026-09-16T10:04:11.201Z",
"data": {
"transaction_id": "txn_x9y8z7",
"status": "COMPLETED",
"total": 178.60,
"currency": "USD"
}
}Signature Verification
Deliveries are signed with HMAC-SHA256 over the raw request body and carry X-Cresva-Signature with a sha256= prefix. See the webhooks guide for verification code in three languages.
Retry Behavior
Failed webhook deliveries (non-2xx response, or timeout after 10 seconds) are retried with exponential backoff: 1 min, 5 min, 30 min, 2 hours, 12 hours. After 5 failed attempts the endpoint is marked failed and stops receiving deliveries. No email is sent.
10. Authentication
OSP uses API keys for authentication. Keys are scoped by type and environment.
API Key Types
pk_live_*Public Key (Live)Read-only access to product data. Safe to use in client-side code. Rate limit: 60 requests/minute.
sk_live_*Secret Key (Live)Full access including transactions and negotiations. Must be kept server-side. Rate limit: 300 requests/minute.
pk_test_*Public Key (Test)Read-only access to sandbox data. Rate limit: 60 requests/minute.
sk_test_*Secret Key (Test)Full sandbox access. Rate limit: 300 requests/minute.
(no key)AnonymousStorefront reads work without a key, limited by source address. Rate limit: 10 requests/minute.
Authorization Header
Authorization: Bearer pk_live_your_key_hereInclude the API key in the Authorization header of every request using the Bearer scheme.
Rate Limits
Rate limits are applied per API key, per IP for anonymous callers, and per brand across all callers. When exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating when the limit resets.
11. Errors
OSP uses standard HTTP status codes and returns structured error responses.
Error Response Format
{
"error": {
"code": "invalid_query",
"message": "The 'intent' field is required for all query requests.",
"details": {
"field": "intent",
"expected": "one of: search, recommend, compare, detail, review, availability"
}
}
}HTTP Status Codes
400Bad RequestInvalid request body or parameters401UnauthorizedMissing or invalid API key403ForbiddenAPI key lacks required permissions404Not FoundResource does not exist429Too Many RequestsRate limit exceeded500Internal Server ErrorUnexpected server error503Service UnavailableStorefront is temporarily unavailable12. Versioning + Compatibility
Protocol Version Header
Every request and response includes an X-OSP-Version header, with X-ACP-Version sent alongside it carrying the identical value for clients written before the rename. Clients SHOULD send their supported version; servers MUST include the version used to process the request.
This header is a dated wire revision, not the spec version. The body's osp_version is a semver and states which version of this specification a storefront implements. The header states which wire contract is in use and is a date. They are different facts and are not expected to match.
// Request
X-OSP-Version: 2026-03-01
// Response
X-OSP-Version: 2026-03-01
X-ACP-Version: 2026-03-01Backward Compatibility Rules
- New optional fields MAY be added to responses without a version bump
- New optional request fields MAY be added without a version bump
- Existing fields MUST NOT be removed or have their type changed within a major version
- New required fields MUST trigger a major version increment
- New endpoints MAY be added in minor versions
Deprecation Policy
When a feature or endpoint is deprecated:
- A deprecation notice is published in the changelog. No
Sunsetheader is sent today, and this document does not promise one until it is. - The feature continues to work for 6 months after the deprecation notice
- After the sunset date, the endpoint returns
410 Gone
Changelog
See the full protocol changelog for a detailed history of all versions and changes.