{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/cervejabarata-com-br/json-schema/cervejabarata-com-br-submission-schema.json",
  "title": "Submission",
  "x-generated": "2026-11-03",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/cervejabarata-com-br-openapi.json#/components/schemas/Submission",
  "type": "object",
  "properties": {
    "city": {
      "type": "string",
      "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
    },
    "venue": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "id": {
              "type": "integer",
              "exclusiveMinimum": 0
            }
          },
          "required": [
            "id"
          ],
          "additionalProperties": false,
          "description": "A bar already on the map"
        },
        {
          "type": "object",
          "properties": {
            "new": {
              "$ref": "#/$defs/NewVenue"
            }
          },
          "required": [
            "new"
          ],
          "additionalProperties": false,
          "description": "A bar that isn't on the map yet"
        }
      ]
    },
    "beer": {
      "type": [
        "string",
        "null"
      ],
      "maxLength": 80,
      "description": "As written; null for \"outra / não sei\""
    },
    "format_code": {
      "type": "string",
      "maxLength": 40,
      "description": "A format from the catalog, e.g. garrafa_600"
    },
    "price": {
      "type": "number",
      "exclusiveMinimum": 0,
      "maximum": 10000,
      "description": "What was paid, in currency units (9.99)"
    },
    "units": {
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    },
    "packaging": {
      "type": "string",
      "enum": [
        "single",
        "balde",
        "torre",
        "combo",
        "dobro"
      ]
    },
    "kind": {
      "type": "string",
      "enum": [
        "regular",
        "happy_hour",
        "promo",
        "combo",
        "open_bar"
      ]
    },
    "open_bar_minutes": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 1440
    },
    "schedules": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ScheduleDraft"
      },
      "maxItems": 14
    },
    "valid_from": {
      "type": "string",
      "format": "date"
    },
    "valid_until": {
      "type": "string",
      "format": "date"
    },
    "conditions": {
      "type": "string",
      "maxLength": 280
    },
    "service_charge_pct": {
      "type": "integer",
      "minimum": 0,
      "maximum": 30
    },
    "couvert": {
      "type": "number",
      "minimum": 0,
      "maximum": 1000
    },
    "attributes": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "musica_ao_vivo",
          "transmite_jogos",
          "area_externa",
          "pe_na_areia"
        ]
      },
      "maxItems": 4
    },
    "seen_in": {
      "type": "string",
      "enum": [
        "menu",
        "poster",
        "instagram",
        "staff",
        "receipt",
        "other"
      ]
    },
    "link": {
      "type": "string",
      "maxLength": 500,
      "format": "uri"
    },
    "observed_on": {
      "type": "string",
      "format": "date",
      "description": "City-local date the price was seen"
    },
    "notes": {
      "type": "string",
      "maxLength": 500
    },
    "is_owner": {
      "type": "boolean",
      "description": "Sent by the bar's owner or manager"
    },
    "nickname": {
      "type": "string",
      "minLength": 2,
      "maxLength": 40,
      "description": "Credited on the site when the price goes live"
    },
    "email": {
      "type": "string",
      "maxLength": 200,
      "format": "email",
      "description": "Only to say \"seu preço está no ar\"; deleted after"
    },
    "consent": {
      "type": "boolean",
      "description": "LGPD consent: required with an email"
    },
    "turnstile": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Cloudflare Turnstile token"
    }
  },
  "required": [
    "city",
    "venue",
    "beer",
    "format_code",
    "price",
    "turnstile"
  ],
  "additionalProperties": false,
  "$defs": {
    "NewVenue": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "minLength": 2,
          "maxLength": 120
        },
        "lat": {
          "type": "number",
          "minimum": -90,
          "maximum": 90
        },
        "lng": {
          "type": "number",
          "minimum": -180,
          "maximum": 180
        },
        "kind": {
          "type": "string",
          "enum": [
            "bar",
            "boteco",
            "pub",
            "barraca",
            "restaurante",
            "deposito",
            "cervejaria",
            "outro"
          ]
        },
        "on_site": {
          "type": "boolean",
          "description": "People can drink there (a depósito only counts when they can)"
        },
        "address": {
          "type": "string",
          "maxLength": 200
        },
        "instagram": {
          "type": "string",
          "pattern": "^[a-z0-9._]{1,30}$"
        },
        "opening_hours": {
          "type": "string",
          "maxLength": 255,
          "description": "As written (\"seg a sex 17h–1h\")"
        }
      },
      "required": [
        "name",
        "lat",
        "lng",
        "kind",
        "on_site"
      ],
      "additionalProperties": false
    },
    "ScheduleDraft": {
      "type": "object",
      "properties": {
        "days": {
          "type": "array",
          "items": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6
          },
          "minItems": 1,
          "maxItems": 7,
          "description": "0 = Sunday … 6 = Saturday, city time"
        },
        "start": {
          "type": "string",
          "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
          "description": "HH:MM"
        },
        "end": {
          "type": "string",
          "pattern": "^([01]\\d|2[0-4]):[0-5]\\d$",
          "description": "HH:MM; earlier than start when it ends after midnight"
        }
      },
      "required": [
        "days",
        "start",
        "end"
      ]
    }
  }
}