{
  "openapi": "3.1.1",
  "info": {
    "title": "VapeDirectoryFeed.Web | v1",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "http://www.ismyvapelegal.com/"
    }
  ],
  "paths": {
    "/billing/portal": {
      "post": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "A link to Stripe's page for changing or cancelling your subscription.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/lookup": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Look up one UPC in one state. Any plan.",
        "parameters": [
          {
            "name": "upc",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LookupResult"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/lookup/bulk": {
      "post": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Look up many UPCs in one state. Store: 100 per request. Wholesaler and API: 1000.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/states/{state}/listings": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "List a state's directory. Paid plans.",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "skip",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "take",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListPage"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/changes": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Rows added, removed or changed since a date. Paid plans.",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChangesPage"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/status": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Last successful run and stale flag per state. No key needed.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StateStatusDto"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/plans": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Plans, prices and limits. No key needed.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AnonymousTypeOfPlanAndstringAnddecimalAnddecimalAndbooleanAndintAndintAndbooleanAndbooleanAndstring"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "The plan and limits behind the key you sent.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnonymousTypeOfstringAndstringAndintAndintAndbooleanAndboolean"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/webhooks": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Your webhook endpoints.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Add a webhook endpoint. The signing secret is in the response and is shown once.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/webhooks/{id}": {
      "delete": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Remove a webhook endpoint.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "boolean"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/webhooks/{id}/test": {
      "post": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Queue a test event.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/webhooks/{id}/deliveries": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "The last 100 deliveries and their retries.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DeliveryDto"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/columns": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Feed fields a column mapping may use. No key needed.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnonymousTypeOfIReadOnlyListOfstringAndIReadOnlyListOfstring"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/mappings": {
      "get": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Your column mappings.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MappingInfo"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/mappings/{name}": {
      "put": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Save a column mapping, then download files with ?mapping=name.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MappingRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MappingInfo"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "VapeDirectoryFeed.Web"
        ],
        "summary": "Delete a column mapping.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "boolean"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AnonymousTypeOfIReadOnlyListOfstringAndIReadOnlyListOfstring": {
        "required": [
          "sources",
          "required"
        ],
        "type": "object",
        "properties": {
          "sources": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "required": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      },
      "AnonymousTypeOfPlanAndstringAnddecimalAnddecimalAndbooleanAndintAndintAndbooleanAndbooleanAndstring": {
        "required": [
          "plan",
          "name",
          "monthly_usd",
          "annual_usd",
          "per_location",
          "requests_per_minute",
          "bulk_limit",
          "lists",
          "files",
          "summary"
        ],
        "type": "object",
        "properties": {
          "plan": {
            "$ref": "#/components/schemas/Plan"
          },
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "monthly_usd": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "format": "double"
          },
          "annual_usd": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "format": "double"
          },
          "per_location": {
            "type": "boolean"
          },
          "requests_per_minute": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "bulk_limit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "lists": {
            "type": "boolean"
          },
          "files": {
            "type": "boolean"
          },
          "summary": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "AnonymousTypeOfstringAndstringAndintAndintAndbooleanAndboolean": {
        "required": [
          "email",
          "plan",
          "requests_per_minute",
          "bulk_limit",
          "lists",
          "files"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": [
              "null",
              "string"
            ]
          },
          "plan": {
            "type": [
              "null",
              "string"
            ]
          },
          "requests_per_minute": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "bulk_limit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "lists": {
            "type": "boolean"
          },
          "files": {
            "type": "boolean"
          }
        }
      },
      "BulkRequest": {
        "required": [
          "state",
          "upcs"
        ],
        "type": "object",
        "properties": {
          "state": {
            "type": [
              "null",
              "string"
            ]
          },
          "upcs": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ChangeDto": {
        "required": [
          "state",
          "at",
          "change_type",
          "listing_key",
          "changed_fields",
          "before",
          "after"
        ],
        "type": "object",
        "properties": {
          "state": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "change_type": {
            "type": "string"
          },
          "listing_key": {
            "type": "string"
          },
          "changed_fields": {
            "type": [
              "null",
              "string"
            ]
          },
          "before": {
            "type": [
              "null",
              "string"
            ]
          },
          "after": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "ChangesPage": {
        "required": [
          "since",
          "items",
          "disclaimer"
        ],
        "type": "object",
        "properties": {
          "since": {
            "type": "string",
            "format": "date-time"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChangeDto"
            }
          },
          "disclaimer": {
            "type": "string"
          }
        }
      },
      "ColumnSpec": {
        "required": [
          "header",
          "source",
          "value"
        ],
        "type": "object",
        "properties": {
          "header": {
            "type": "string"
          },
          "source": {
            "type": [
              "null",
              "string"
            ]
          },
          "value": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "DeliveryDto": {
        "required": [
          "id",
          "event_id",
          "event_type",
          "attempts",
          "created_at",
          "delivered_at",
          "next_attempt_at",
          "gave_up_at",
          "last_status",
          "last_error"
        ],
        "type": "object",
        "properties": {
          "id": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "event_id": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "attempts": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "next_attempt_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "gave_up_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "last_status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "last_error": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "Freshness": {
        "required": [
          "disclaimer",
          "last_verified",
          "stale_since",
          "official_source"
        ],
        "type": "object",
        "properties": {
          "disclaimer": {
            "type": "string"
          },
          "last_verified": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "stale_since": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "official_source": {
            "type": "string"
          }
        }
      },
      "ListingDto": {
        "required": [
          "state",
          "manufacturer",
          "brand",
          "product",
          "flavor",
          "nicotine_strength",
          "pack_size",
          "volume",
          "category",
          "upc_a",
          "gtin14",
          "upc_provenance",
          "list_type",
          "status",
          "state_date_added",
          "first_seen",
          "last_seen",
          "source_url",
          "state_code",
          "state_note"
        ],
        "type": "object",
        "properties": {
          "state": {
            "type": "string"
          },
          "manufacturer": {
            "type": "string"
          },
          "brand": {
            "type": [
              "null",
              "string"
            ]
          },
          "product": {
            "type": "string"
          },
          "flavor": {
            "type": [
              "null",
              "string"
            ]
          },
          "nicotine_strength": {
            "type": [
              "null",
              "string"
            ]
          },
          "pack_size": {
            "type": [
              "null",
              "string"
            ]
          },
          "volume": {
            "type": [
              "null",
              "string"
            ]
          },
          "category": {
            "type": [
              "null",
              "string"
            ]
          },
          "upc_a": {
            "type": [
              "null",
              "string"
            ]
          },
          "gtin14": {
            "type": [
              "null",
              "string"
            ]
          },
          "upc_provenance": {
            "type": "string"
          },
          "list_type": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "state_date_added": {
            "type": [
              "null",
              "string"
            ],
            "format": "date"
          },
          "first_seen": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen": {
            "type": "string",
            "format": "date-time"
          },
          "source_url": {
            "type": "string"
          },
          "state_code": {
            "type": [
              "null",
              "string"
            ]
          },
          "state_note": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "ListPage": {
        "required": [
          "state",
          "total",
          "items",
          "freshness"
        ],
        "type": "object",
        "properties": {
          "state": {
            "type": "string"
          },
          "total": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListingDto"
            }
          },
          "freshness": {
            "$ref": "#/components/schemas/Freshness"
          }
        }
      },
      "LookupResult": {
        "required": [
          "query",
          "state",
          "status",
          "explanation",
          "upc_a",
          "gtin14",
          "matches",
          "freshness"
        ],
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SaleStatus"
          },
          "explanation": {
            "type": "string"
          },
          "upc_a": {
            "type": [
              "null",
              "string"
            ]
          },
          "gtin14": {
            "type": [
              "null",
              "string"
            ]
          },
          "matches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListingDto"
            }
          },
          "freshness": {
            "$ref": "#/components/schemas/Freshness"
          },
          "known": {
            "type": "boolean"
          }
        }
      },
      "MappingInfo": {
        "required": [
          "name",
          "columns",
          "updated_at"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "columns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ColumnSpec"
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MappingRequest": {
        "required": [
          "columns"
        ],
        "type": "object",
        "properties": {
          "columns": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ColumnSpec"
            }
          }
        }
      },
      "Plan": {
        "enum": [
          "free",
          "store",
          "wholesaler",
          "api"
        ]
      },
      "SaleStatus": {
        "enum": [
          "listed",
          "not_listed",
          "removed",
          "not_approved",
          "banned",
          "not_on_banned_list",
          "unknown",
          "invalid_code",
          "state_not_covered"
        ]
      },
      "StateStatusDto": {
        "required": [
          "state",
          "agency",
          "list_type",
          "state_publishes_codes",
          "official_url",
          "last_attempt",
          "last_successful_run",
          "stale_since",
          "stale_reason",
          "active_rows"
        ],
        "type": "object",
        "properties": {
          "state": {
            "type": "string"
          },
          "agency": {
            "type": "string"
          },
          "list_type": {
            "type": "string"
          },
          "state_publishes_codes": {
            "type": "boolean"
          },
          "official_url": {
            "type": "string"
          },
          "last_attempt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "last_successful_run": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "stale_since": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "stale_reason": {
            "type": [
              "null",
              "string"
            ]
          },
          "active_rows": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          }
        }
      },
      "WebhookDto": {
        "required": [
          "id",
          "url",
          "states",
          "created_at",
          "disabled_at",
          "disabled_reason",
          "secret"
        ],
        "type": "object",
        "properties": {
          "id": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "url": {
            "type": "string"
          },
          "states": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "disabled_at": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "disabled_reason": {
            "type": [
              "null",
              "string"
            ]
          },
          "secret": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "WebhookRequest": {
        "required": [
          "url",
          "states"
        ],
        "type": "object",
        "properties": {
          "url": {
            "type": [
              "null",
              "string"
            ]
          },
          "states": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "VapeDirectoryFeed.Web"
    }
  ]
}