{
  "openapi": "3.1.0",
  "info": {
    "title": "Doorprice API",
    "version": "0.1.0",
    "description": "Agent-first price comparison, review intelligence, checkout and appointment booking. Every call requires an intent. Demo catalog data is synthetic; source=shopify_live returns real Shopify products."
  },
  "servers": [
    {
      "url": "https://doorprice.co"
    }
  ],
  "paths": {
    "/api/v1/search": {
      "get": {
        "operationId": "searchProducts",
        "summary": "Search products by lowest delivered price and Doorprice Score",
        "parameters": [
          {
            "name": "intent",
            "in": "query",
            "required": true,
            "description": "Required. One sentence on why you are calling, in the person’s words. No personal details. Can also be sent as the X-Doorprice-Intent header.",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 500
            }
          },
          {
            "name": "intent_stage",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "discover",
                "compare",
                "decide",
                "purchase",
                "post_purchase"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "headphones",
                "robot-vacuums",
                "espresso",
                "air-fryers",
                "keyboards"
              ]
            }
          },
          {
            "name": "max_price",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "min_score",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "condition",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "new",
                "any"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "best_value",
                "lowest_total",
                "highest_score",
                "fastest_delivery"
              ]
            }
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "demo",
                "shopify_live"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 20
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "zip",
            "in": "query",
            "description": "Ship-to / near ZIP code (US).",
            "schema": {
              "type": "string",
              "example": "94103"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/products/{id}": {
      "get": {
        "operationId": "getProduct",
        "summary": "Product detail: every offer with delivered totals, score evidence, specs",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Demo slug (e.g. ostra-q7) or shopify:<id>"
          },
          {
            "name": "intent",
            "in": "query",
            "required": true,
            "description": "Required. One sentence on why you are calling, in the person’s words. No personal details. Can also be sent as the X-Doorprice-Intent header.",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 500
            }
          },
          {
            "name": "intent_stage",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "discover",
                "compare",
                "decide",
                "purchase",
                "post_purchase"
              ]
            }
          },
          {
            "name": "zip",
            "in": "query",
            "description": "Ship-to / near ZIP code (US).",
            "schema": {
              "type": "string",
              "example": "94103"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/compare": {
      "get": {
        "operationId": "compareProducts",
        "summary": "Aligned comparison table",
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated product ids (2-4)"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "markdown",
                "csv"
              ]
            }
          },
          {
            "name": "intent",
            "in": "query",
            "required": true,
            "description": "Required. One sentence on why you are calling, in the person’s words. No personal details. Can also be sent as the X-Doorprice-Intent header.",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 500
            }
          },
          {
            "name": "intent_stage",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "discover",
                "compare",
                "decide",
                "purchase",
                "post_purchase"
              ]
            }
          },
          {
            "name": "zip",
            "in": "query",
            "description": "Ship-to / near ZIP code (US).",
            "schema": {
              "type": "string",
              "example": "94103"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Comparison",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/checkout_sessions": {
      "post": {
        "operationId": "createCheckout",
        "summary": "Create a checkout session for an offer; returns payment_url for human approval",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "offer_id",
                  "intent"
                ],
                "properties": {
                  "offer_id": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10
                  },
                  "ship_to_zip": {
                    "type": "string"
                  },
                  "payment_method": {
                    "type": "string",
                    "enum": [
                      "card",
                      "ach"
                    ]
                  },
                  "intent": {
                    "type": "string"
                  },
                  "intent_stage": {
                    "type": "string",
                    "enum": [
                      "discover",
                      "compare",
                      "decide",
                      "purchase",
                      "post_purchase"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout session"
          },
          "400": {
            "description": "Bad input"
          }
        }
      }
    },
    "/api/v1/checkout_sessions/{id}": {
      "get": {
        "operationId": "getCheckoutStatus",
        "summary": "Payment and order status",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "intent",
            "in": "query",
            "required": true,
            "description": "Required. One sentence on why you are calling, in the person’s words. No personal details. Can also be sent as the X-Doorprice-Intent header.",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/services/search": {
      "get": {
        "operationId": "searchServices",
        "summary": "Search appointments by all-in price, availability, distance and provider score",
        "parameters": [
          {
            "name": "intent",
            "in": "query",
            "required": true,
            "description": "Required. One sentence on why you are calling, in the person’s words. No personal details. Can also be sent as the X-Doorprice-Intent header.",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 500
            }
          },
          {
            "name": "intent_stage",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "discover",
                "compare",
                "decide",
                "purchase",
                "post_purchase"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "haircut",
                "massage",
                "dental-cleaning",
                "primary-care"
              ]
            }
          },
          {
            "name": "max_price",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "insurance",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "best_match",
                "lowest_price",
                "soonest",
                "nearest",
                "top_rated"
              ]
            }
          },
          {
            "name": "zip",
            "in": "query",
            "description": "Ship-to / near ZIP code (US).",
            "schema": {
              "type": "string",
              "example": "94103"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Providers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/services/providers/{id}": {
      "get": {
        "operationId": "getProvider",
        "summary": "Provider services, open slots and score",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "intent",
            "in": "query",
            "required": true,
            "description": "Required. One sentence on why you are calling, in the person’s words. No personal details. Can also be sent as the X-Doorprice-Intent header.",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 500
            }
          },
          {
            "name": "zip",
            "in": "query",
            "description": "Ship-to / near ZIP code (US).",
            "schema": {
              "type": "string",
              "example": "94103"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Provider",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/services/bookings": {
      "post": {
        "operationId": "bookAppointment",
        "summary": "Hold a slot and return confirm_url for the person",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "provider_id",
                  "slot",
                  "intent"
                ],
                "properties": {
                  "provider_id": {
                    "type": "string"
                  },
                  "service": {
                    "type": "string"
                  },
                  "slot": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "patient_consent": {
                    "type": "boolean",
                    "description": "Required true for health appointments"
                  },
                  "intent": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Booking hold"
          },
          "422": {
            "description": "Consent required"
          }
        }
      }
    },
    "/api/v1/fees": {
      "get": {
        "operationId": "quoteFees",
        "summary": "Doorprice fee and unit economics under each pricing model",
        "parameters": [
          {
            "name": "intent",
            "in": "query",
            "required": true,
            "description": "Required. One sentence on why you are calling, in the person’s words. No personal details. Can also be sent as the X-Doorprice-Intent header.",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 500
            }
          },
          {
            "name": "subtotal",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "offer_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fee quotes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/research": {
      "post": {
        "operationId": "researchProducts",
        "summary": "Research a product category Doorprice does not cover yet (live: candidates, evidence, Doorprice Scores). Returns 202 while running.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query",
                  "intent"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "example": "standing desk"
                  },
                  "intent": {
                    "type": "string"
                  },
                  "intent_stage": {
                    "type": "string",
                    "enum": [
                      "discover",
                      "compare",
                      "decide",
                      "purchase",
                      "post_purchase"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Research status and scored items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/research/{slug}": {
      "get": {
        "operationId": "getResearch",
        "summary": "Status and results of a research job",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "intent",
            "in": "query",
            "required": true,
            "description": "Required. One sentence on why you are calling, in the person’s words. No personal details. Can also be sent as the X-Doorprice-Intent header.",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Research status and scored items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/feedback": {
      "post": {
        "operationId": "sendFeedback",
        "summary": "Ask a question, report an issue or request a feature. A person reviews every report.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind",
                  "message",
                  "intent"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "question",
                      "issue",
                      "bug",
                      "feature_request",
                      "praise",
                      "other"
                    ]
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 4000
                  },
                  "context": {
                    "type": "object",
                    "description": "Ids, tool names, error messages. No personal details."
                  },
                  "intent": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Feedback id",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/v1/status": {
      "get": {
        "operationId": "getStatus",
        "summary": "Which features and evidence sources are switched on",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing intent or bad input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    }
  }
}