Skip to content
cresvaDevelopers
v3.0Current

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:

AI Agent (Client)
↕ HTTPS + JSON
Cresva Storefront API (OSP Server)
↕ Internal
Brand Backend (Catalog, Pricing, Inventory)

Request/Response Lifecycle

  1. Agent sends an authenticated request to the storefront API endpoint
  2. The storefront validates the API key, rate limits, and request schema
  3. The request is processed against the brand's catalog and policies
  4. A structured JSON response is returned with product data, negotiation state, or transaction status
  5. The agent parses the response and presents results to the user

Base URL

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:

HTTP
GET https://{domain}/.well-known/osp.json

The discovery file format:

JSON
{
  "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_versionstringrequired

The 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_urlstringrequired

The base URL for all API requests to this storefront.

brand_idstringrequired

Unique identifier for the brand.

brand_namestringrequired

Human-readable brand name.

capabilitiesstring[]required

List 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_limitsobject

Rate limit information by key type.

documentationstring

URL 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

HTTP
POST /query

AgentQueryRequest

The request body for a query:

protocol_versionstringrequired

The protocol version this request is written against.

request_idstringrequired

Caller-generated id for this request.

timestampstringrequired

When the request was made.

agentobjectrequired

Identifies the caller: platform, optional version and session_id. Session belongs here, not in a context block.

queryobjectrequired

The query itself. It carries text, intent, and both filters and context nested inside it, not beside it.

paginationobject

Carries page, limit and cursor.

responseobject

Shapes 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.

JSON
// 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[]required

The matching product cards. Note the key: /products and /search answer with products, this endpoint answers with results.

metaobject

Carries 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

idstringrequired

Unique product identifier. A cuid, not a prefixed id.

titlestringrequired

Product title.

priceobjectrequired

An 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.

availabilitystring

Stock status, e.g. in_stock.

ratingnumber | null

null when no rating could be established. Never 0 to mean unknown.

summarystring

A short description. May be empty.

attributesobject

A free map of the storefront's own keys. There is no standardised taxonomy.

purchase_urlstring

Where a shopper completes the purchase, on the merchant's own store.

imagesstring[]

Image URLs. Strings, not objects.

Example

JSON
// 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

initiateaction

Agent sends an initial offer for a product.

counteraction

Brand responds with a counter-offer.

acceptaction

Either party accepts the current offer.

rejectaction

Either party rejects and ends negotiation.

withdrawaction

Agent withdraws their offer before a response.

inquireaction

Agent asks what deals are possible without committing.

Endpoint

HTTP
POST /negotiate

Negotiation Request

actionstringrequired

One of: "initiate", "counter", "accept", "reject", "withdraw", "inquire".

negotiation_idstring

Required for all actions except "initiate" and "inquire".

product_idstringrequired

The product being negotiated.

offered_pricenumber

The price being offered (for initiate and counter).

currencystring

Currency of the offer. Default: storefront's primary currency.

quantitynumber

Number of units. Default: 1.

messagestring

Optional 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

JSON
// 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_txn

Endpoints

POST/checkout/sessionsOpen a checkout session, priced from the catalogue
POST/checkout/sessions/{id}/completeCreate the merchant's order and return a link to pay it
GET/checkout/sessions/{id}/statusHas the buyer paid yet
GONE/transactionsWithdrawn 2026-09-10. Returns 410; use a checkout session.
GET/transactions/{id}Get transaction status
POST/transactions/{id}/confirmConfirm a transaction and proceed to payment
POST/transactions/{id}/cancelCancel a transaction

Confirmation 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.

POST/transactions/{id}/confirmation-window/confirmClose the confirmation window early (buyer confirms receipt)
POST/transactions/{id}/confirmation-window/disputeRecord a dispute, moving the window to disputed

Payment

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

JSON
// 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}/complete

POST /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

scorenumberrequired

Composite score from 0-100.

tierstringrequired

Tier: platinum (90-100), gold (75-89), silver (60-74), bronze (40-59), unrated (under 40). Lowercase, as returned.

componentsobject

Individual score components (0-100 each).

Score Components

data_accuracynumber | null

How accurately product listings match what is delivered.

fulfillment_speednumber | null

How quickly orders reach the buyer.

return_ratenumber | null

How often orders come back.

negotiation_fairnessnumber | null

Whether agreed prices are honoured.

response_reliabilitynumber | null

Whether the storefront answers, and how consistently.

dispute_recordnumber | null

Disputes raised and how they ended.

customer_satisfactionnumber | null

Aggregated 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

💎
Platinum
90-100
🥇
Gold
75-89
🥈
Silver
60-74
🥉
Bronze
40-59
-
Unrated
<40

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

JSON
{
  "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)Anonymous

Storefront reads work without a key, limited by source address. Rate limit: 10 requests/minute.

Authorization Header

HTTP
Authorization: Bearer pk_live_your_key_here

Include 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

JSON
{
  "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 parameters
401UnauthorizedMissing or invalid API key
403ForbiddenAPI key lacks required permissions
404Not FoundResource does not exist
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server error
503Service UnavailableStorefront is temporarily unavailable

12. 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.

HTTP
// Request
X-OSP-Version: 2026-03-01

// Response
X-OSP-Version: 2026-03-01
X-ACP-Version: 2026-03-01

Backward 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:

  1. A deprecation notice is published in the changelog. No Sunset header is sent today, and this document does not promise one until it is.
  2. The feature continues to work for 6 months after the deprecation notice
  3. After the sunset date, the endpoint returns 410 Gone