diff --git a/docs/specification/product-safety.md b/docs/specification/product-safety.md new file mode 100644 index 00000000..63ba5d23 --- /dev/null +++ b/docs/specification/product-safety.md @@ -0,0 +1,203 @@ + + +# Product Safety Extension + +**Version:** `2026-01-27` +**Namespace:** `io.deeprecall.shopping.product_safety` + +## Overview + +This extension defines a message contract for querying product recall databases. +It enables platforms to retrieve recall evidence for a given product before checkout. + +The extension returns similarity matches only. It does not make safety judgments, +block transactions, or enforce compliance decisions. + +## Scope + +This extension covers: + +- Pre-checkout recall evidence lookup +- Synchronous request/response pattern +- Evidence-only output (no decisions) + +### Non-Goals + +This extension explicitly does NOT: + +- Block or prevent checkout completion +- Make legal or compliance judgments +- Enforce any regulatory requirements +- Replace platform or business safety policies +- Provide definitive product identification + +Interpretation of results and any subsequent actions are the responsibility of the caller. + +## Discovery + +Platforms advertise product safety support in their profile: + +```json +{ + "capabilities": { + "io.deeprecall.shopping.product_safety": [ + { + "version": "2026-01-27", + "spec": "https://deeprecall.io/ucp/product-safety", + "schema": "https://deeprecall.io/ucp/schemas/shopping/product_safety.json" + } + ] + } +} +``` + +This is a standalone vendor capability and does not extend core UCP capabilities. + +## Operations + +### search + +Query for recalled products similar to the provided product identifiers. + +**Method:** `POST` +**Path:** `/product-safety/search` + +## Request Schema + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `product` | object | Yes | Product identifiers for lookup | +| `product.description` | string | No* | Text description of the product | +| `product.name` | string | No* | Product name or title | +| `product.brand` | string | No | Product brand or manufacturer | +| `product.gtin` | string | No* | Global Trade Item Number | +| `product.image_urls` | array | No* | Product image URLs (max 5) | +| `filters` | object | No | Optional search filters | +| `filters.country` | string | No | ISO 3166-1 alpha-2 country code | +| `filters.data_sources` | array | No | Regulatory agencies to query | +| `filters.top_k` | integer | No | Max results (1-20, default 5) | + +*At least one of `description`, `name`, `gtin`, or `image_urls` is required. + +### Supported Data Sources + +| ID | Agency | Jurisdiction | +|----|--------|--------------| +| `us_cpsc` | Consumer Product Safety Commission | United States | +| `us_fda` | Food and Drug Administration | United States | +| `safety_gate` | EU Safety Gate | European Union | +| `uk_opss` | Office for Product Safety & Standards | United Kingdom | +| `canada_recalls` | Health Canada | Canada | +| `oecd` | OECD GlobalRecalls | International | +| `rappel_conso` | RappelConso | France | +| `accc_recalls` | ACCC | Australia | + +## Response Schema + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `matches` | array | Yes | Recalls similar to the queried product | +| `matches[].recall_id` | string | Yes | Unique identifier for this recall | +| `matches[].similarity_score` | number | Yes | Similarity confidence (0.0 to 1.0) | +| `matches[].regulator` | string | Yes | Regulatory agency that issued recall | +| `matches[].regulator_case_id` | string | No | Agency's case ID | +| `matches[].title` | string | Yes | Title of the recall notice | +| `matches[].product_name` | string | No | Name of the recalled product | +| `matches[].hazard` | string | No | Description of the hazard | +| `matches[].remedy` | string | No | Recommended remedy | +| `matches[].recall_date` | string | No | Date recall was issued (YYYY-MM-DD) | +| `matches[].evidence_url` | string | Yes | URL to official recall notice | +| `query_timestamp` | string | Yes | Query execution time (RFC 3339) | + +## Examples + +### Request + +```json +{ + "product": { + "description": "Infant sleep rocker with vibrating seat", + "brand": "Example Brand" + }, + "filters": { + "country": "US", + "top_k": 3 + } +} +``` + +### Response + +```json +{ + "matches": [ + { + "recall_id": "rec_abc123", + "similarity_score": 0.87, + "regulator": "us_cpsc", + "regulator_case_id": "21-123", + "title": "Infant Sleeper Recalled Due to Safety Concern", + "product_name": "Example Infant Rocker", + "hazard": "Suffocation hazard when used on inclined surface", + "remedy": "Refund", + "recall_date": "2024-03-15", + "evidence_url": "https://www.cpsc.gov/Recalls/2024/example" + } + ], + "query_timestamp": "2026-01-27T14:30:00Z" +} +``` + +## Error Handling + +Implementations MUST return standard HTTP error codes: + +| Code | Condition | +|------|-----------| +| `400` | Invalid request (missing required fields, malformed input) | +| `401` | Authentication required or invalid credentials | +| `429` | Rate limit exceeded | +| `500` | Internal server error | +| `503` | Service temporarily unavailable | + +Error responses SHOULD include a `message` field with a human-readable description: + +```json +{ + "error": { + "code": "invalid_request", + "message": "At least one of description, name, gtin, or image_urls is required" + } +} +``` + +## Backward Compatibility + +This extension is optional and does not modify any existing UCP schemas or flows. +Platforms and businesses MAY implement this extension without affecting core +checkout operations. + +## Security Considerations + +- Requests MUST be made over HTTPS +- Implementations SHOULD rate-limit queries to prevent abuse +- Response data is public regulatory information; no PII is transmitted + +## References + +- [JSON Schema: Request](https://deeprecall.io/ucp/schemas/shopping/product_safety.search_req.json) +- [JSON Schema: Response](https://deeprecall.io/ucp/schemas/shopping/product_safety_resp.json) diff --git a/mkdocs.yml b/mkdocs.yml index d6ceae26..69ea58bc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,6 +40,7 @@ nav: - Buyer Consent Extension: specification/buyer-consent.md - Discounts Extension: specification/discount.md - Fulfillment Extension: specification/fulfillment.md + - Product Safety Extension: specification/product-safety.md - Cart Capability: - Overview: specification/cart.md - HTTP/REST Binding: specification/cart-rest.md @@ -207,6 +208,7 @@ plugins: - specification/discount.md - specification/embedded-checkout.md - specification/fulfillment.md + - specification/product-safety.md Cart Capability: - specification/cart.md - specification/cart-rest.md diff --git a/spec/schemas/shopping/product_safety.search_req.json b/spec/schemas/shopping/product_safety.search_req.json new file mode 100644 index 00000000..f1e1758f --- /dev/null +++ b/spec/schemas/shopping/product_safety.search_req.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://deeprecall.io/ucp/schemas/shopping/product_safety.search_req.json", + "name": "io.deeprecall.shopping.product_safety", + "version": "2026-01-27", + "title": "Product Safety Search Request", + "description": "Request schema for querying product recall databases. Returns evidence of similar recalled products; does not make safety judgments or enforcement decisions.", + "type": "object", + "properties": { + "product": { + "$ref": "#/$defs/product_query" + }, + "filters": { + "$ref": "#/$defs/search_filters" + } + }, + "required": ["product"], + "$defs": { + "product_query": { + "type": "object", + "description": "Product identifiers for recall lookup", + "properties": { + "description": { + "type": "string", + "description": "Text description of the product" + }, + "name": { + "type": "string", + "description": "Product name or title" + }, + "brand": { + "type": "string", + "description": "Product brand or manufacturer" + }, + "gtin": { + "type": "string", + "description": "Global Trade Item Number (UPC, EAN, ISBN)" + }, + "image_urls": { + "type": "array", + "items": { "type": "string", "format": "uri" }, + "maxItems": 5, + "description": "Product image URLs for visual matching" + } + }, + "anyOf": [ + { "required": ["description"] }, + { "required": ["name"] }, + { "required": ["gtin"] }, + { "required": ["image_urls"] } + ] + }, + "search_filters": { + "type": "object", + "description": "Optional filters to scope the search", + "properties": { + "country": { + "type": "string", + "pattern": "^[A-Z]{2}$", + "description": "ISO 3166-1 alpha-2 country code to filter recalls by jurisdiction" + }, + "data_sources": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "us_cpsc", + "us_fda", + "safety_gate", + "uk_opss", + "canada_recalls", + "oecd", + "rappel_conso", + "accc_recalls" + ] + }, + "description": "Filter by specific regulatory agencies" + }, + "top_k": { + "type": "integer", + "minimum": 1, + "maximum": 20, + "default": 5, + "description": "Maximum number of results to return" + } + } + } + } +} diff --git a/spec/schemas/shopping/product_safety_resp.json b/spec/schemas/shopping/product_safety_resp.json new file mode 100644 index 00000000..65397c04 --- /dev/null +++ b/spec/schemas/shopping/product_safety_resp.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://deeprecall.io/ucp/schemas/shopping/product_safety_resp.json", + "name": "io.deeprecall.shopping.product_safety", + "version": "2026-01-27", + "title": "Product Safety Search Response", + "description": "Response schema containing recall evidence. Provides similarity matches only; interpretation and action decisions are left to the caller.", + "type": "object", + "properties": { + "matches": { + "type": "array", + "items": { "$ref": "#/$defs/recall_match" }, + "description": "Recalls similar to the queried product, ordered by similarity" + }, + "query_timestamp": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the query was executed (RFC 3339)" + } + }, + "required": ["matches", "query_timestamp"], + "$defs": { + "recall_match": { + "type": "object", + "description": "A recalled product that matches the query", + "properties": { + "recall_id": { + "type": "string", + "description": "Unique identifier for this recall record" + }, + "similarity_score": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Similarity confidence (0.0 to 1.0)" + }, + "regulator": { + "type": "string", + "description": "Regulatory agency that issued the recall", + "enum": [ + "us_cpsc", + "us_fda", + "safety_gate", + "uk_opss", + "canada_recalls", + "oecd", + "rappel_conso", + "accc_recalls" + ] + }, + "regulator_case_id": { + "type": "string", + "description": "Case ID assigned by the regulatory agency" + }, + "title": { + "type": "string", + "description": "Title of the recall notice" + }, + "product_name": { + "type": "string", + "description": "Name of the recalled product" + }, + "hazard": { + "type": "string", + "description": "Description of the identified hazard" + }, + "remedy": { + "type": "string", + "description": "Recommended remedy (refund, repair, etc.)" + }, + "recall_date": { + "type": "string", + "format": "date", + "description": "Date the recall was issued" + }, + "evidence_url": { + "type": "string", + "format": "uri", + "description": "URL to the official recall notice" + } + }, + "required": [ + "recall_id", + "similarity_score", + "regulator", + "title", + "evidence_url" + ] + } + } +}