{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://dreamify-partner-api.netlify.app/schema/listing.v1.schema.json",
  "title": "Dreamify partner listing v1",
  "type": "object",
  "required": [
    "external_id",
    "type",
    "title"
  ],
  "properties": {
    "external_id": {
      "type": "string",
      "maxLength": 200,
      "minLength": 1,
      "pattern": "^[\\w.:\\-/@#]+$",
      "description": "Your id for this listing. Unique per partner + environment; PUT with the same id updates it."
    },
    "type": {
      "type": "string",
      "enum": [
        "publish.image",
        "publish.article",
        "publish.news",
        "publish.video",
        "publish.podcast",
        "publish.music",
        "publish.profile",
        "local.profile",
        "local.deal",
        "local.item",
        "local.job",
        "local.menu",
        "local.service",
        "venues.profile",
        "local.ride",
        "local.activity",
        "travel.stay",
        "travel.package",
        "travel.retreat",
        "travel.adventure",
        "travel.car",
        "travel.cruise",
        "travel.deal",
        "host.event",
        "host.activity",
        "host.tour",
        "host.class",
        "host.retreat",
        "teach.course",
        "teach.workshop",
        "teach.cohort",
        "teach.coaching",
        "teach.webinar",
        "teach.instructor",
        "teach.membership",
        "sell.product",
        "sell.service",
        "sell.digital",
        "sell.subscription",
        "sell.gift-card",
        "sell.bundle",
        "talent.performer",
        "talent.creator",
        "talent.professional",
        "inspire.idea",
        "inspire.list",
        "inspire.challenge",
        "inspire.guide",
        "community.group",
        "community.club",
        "community.team"
      ],
      "description": "Listing type id from the shared DFB tree (category.type). See GET /v1/taxonomy."
    },
    "destinations": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "shop",
          "explore.images",
          "explore.articles",
          "explore.news",
          "explore.videos",
          "explore.podcasts",
          "explore.music",
          "explore.people",
          "local.places",
          "deals",
          "marketplace",
          "local.jobs",
          "local.meals",
          "local.services",
          "your-store",
          "local-venues",
          "local.rides",
          "local.activities",
          "travel.stays",
          "travel.packages",
          "travel.retreats",
          "travel.adventures",
          "travel.cars",
          "travel.cruises",
          "travel.deals",
          "travel.flights",
          "experiences",
          "learn",
          "learn.instructors",
          "local-talent",
          "ideas",
          "dream.lists",
          "dream.challenges",
          "community.groups",
          "feed",
          "cities"
        ]
      },
      "maxItems": 8,
      "uniqueItems": true,
      "description": "Where to publish. Must be allowed for the type; defaults to the type's first destination."
    },
    "title": {
      "type": "string",
      "maxLength": 200,
      "minLength": 3
    },
    "summary": {
      "type": "string",
      "maxLength": 500
    },
    "description": {
      "type": "string",
      "maxLength": 20000,
      "description": "Plain text or Markdown."
    },
    "language": {
      "type": "string",
      "maxLength": 20,
      "description": "BCP 47, e.g. en, fr-CA."
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "maxItems": 30
    },
    "sample": {
      "type": "boolean",
      "description": "Mark test/sample content. Sample listings can never go live."
    },
    "market": {
      "type": "string",
      "pattern": "^[a-z0-9][a-z0-9.-]{0,119}$",
      "description": "DreamSuite Market taxonomy id (GET /v1/taxonomy → market), e.g. \"places.restaurants\". Decides where the listing shows in the Dreamify Marketplace."
    },
    "category": {
      "type": "object",
      "properties": {
        "cat": {
          "type": "string",
          "maxLength": 80
        },
        "sub": {
          "type": "string",
          "maxLength": 80
        },
        "kind": {
          "type": "string",
          "maxLength": 40,
          "description": "Experiences only: events | activities (derived from cat when omitted)."
        }
      },
      "description": "Slugs from the main category's tree (GET /v1/taxonomy → categories)."
    },
    "place": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string",
          "maxLength": 120
        },
        "region": {
          "type": "string",
          "maxLength": 120
        },
        "country": {
          "type": "string",
          "maxLength": 2,
          "pattern": "^[A-Za-z]{2}$"
        },
        "place_id": {
          "type": "string",
          "maxLength": 200,
          "description": "Dreamify place id, e.g. na.ca.bc.vancouver"
        },
        "online": {
          "type": "boolean"
        }
      }
    },
    "location": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 200
        },
        "address": {
          "type": "string",
          "maxLength": 500
        },
        "postal_code": {
          "type": "string",
          "maxLength": 20
        },
        "lat": {
          "type": "number",
          "minimum": -90,
          "maximum": 90
        },
        "lng": {
          "type": "number",
          "minimum": -180,
          "maximum": 180
        }
      }
    },
    "media": {
      "type": "array",
      "maxItems": 30,
      "items": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2000,
            "format": "uri",
            "pattern": "^https://"
          },
          "type": {
            "enum": [
              "image",
              "video"
            ]
          },
          "alt": {
            "type": "string",
            "maxLength": 300
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          }
        }
      }
    },
    "price": {
      "type": "object",
      "properties": {
        "amount_cents": {
          "type": "integer",
          "minimum": 0,
          "description": "Money in minor units (cents). 2500 = 25.00."
        },
        "amount": {
          "type": "number",
          "minimum": 0,
          "description": "Alternative to amount_cents in major units (25.00)."
        },
        "currency": {
          "type": "string",
          "maxLength": 3,
          "pattern": "^[A-Za-z]{3}$",
          "description": "ISO 4217, e.g. usd, cad."
        },
        "unit": {
          "enum": [
            "total",
            "per_person",
            "per_item",
            "per_night",
            "per_hour",
            "per_day",
            "per_week",
            "per_month",
            "per_session"
          ]
        },
        "from": {
          "type": "boolean",
          "description": "\"From $X\" pricing."
        },
        "free": {
          "type": "boolean"
        }
      }
    },
    "offers": {
      "type": "array",
      "maxItems": 50,
      "items": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 100
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "price_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Money in minor units (cents). 2500 = 25.00."
          },
          "original_price_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Money in minor units (cents). 2500 = 25.00."
          },
          "currency": {
            "type": "string",
            "maxLength": 3
          },
          "quantity": {
            "type": "integer",
            "minimum": 0
          },
          "valid_from": {
            "type": "string",
            "format": "date-time"
          },
          "valid_to": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "starts_at": {
      "type": "string",
      "format": "date-time"
    },
    "ends_at": {
      "type": "string",
      "format": "date-time"
    },
    "timezone": {
      "type": "string",
      "maxLength": 60,
      "description": "IANA, e.g. America/Vancouver."
    },
    "sessions": {
      "type": "array",
      "maxItems": 500,
      "items": {
        "type": "object",
        "required": [
          "starts_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 100
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "ends_at": {
            "type": "string",
            "format": "date-time"
          },
          "capacity": {
            "type": "integer",
            "minimum": 0
          },
          "remaining": {
            "type": "integer",
            "minimum": 0
          },
          "price_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Money in minor units (cents). 2500 = 25.00."
          }
        }
      }
    },
    "booking": {
      "type": "object",
      "properties": {
        "mode": {
          "enum": [
            "handoff",
            "native"
          ],
          "default": "handoff"
        },
        "url": {
          "type": "string",
          "maxLength": 2000,
          "format": "uri",
          "pattern": "^https://"
        },
        "phone": {
          "type": "string",
          "maxLength": 40
        }
      },
      "description": "handoff = Dreamify sends guests to booking.url (required for sell/teach/host/rent). native = book on Dreamify (needs partner enablement)."
    },
    "booking_url": {
      "type": "string",
      "maxLength": 2000,
      "description": "Shorthand for booking.url."
    },
    "expires_at": {
      "type": "string",
      "format": "date-time",
      "description": "Listing is hidden after this time."
    },
    "rating": {
      "type": "object",
      "properties": {
        "value": {
          "type": "number",
          "minimum": 0,
          "maximum": 5
        },
        "count": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "experience": {
      "type": "object",
      "description": "host.* types",
      "properties": {
        "schedule_mode": {
          "enum": [
            "single",
            "multi",
            "weekly",
            "flexible"
          ]
        },
        "duration_minutes": {
          "type": "integer"
        },
        "highlights": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "included": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "excluded": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "meeting_point": {
          "type": "string",
          "maxLength": 2000
        },
        "min_age": {
          "type": "integer"
        },
        "languages": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "accessibility": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "cancellation_policy": {
          "type": "string",
          "maxLength": 2000
        }
      }
    },
    "deal": {
      "type": "object",
      "description": "Required when destinations include \"deals\".",
      "properties": {
        "deal_category": {
          "type": "string",
          "maxLength": 80,
          "description": "Slug from categories.deals"
        },
        "original_price_cents": {
          "type": "integer",
          "minimum": 0,
          "description": "Money in minor units (cents). 2500 = 25.00."
        },
        "deal_price_cents": {
          "type": "integer",
          "minimum": 0,
          "description": "Money in minor units (cents). 2500 = 25.00."
        },
        "discount_percent": {
          "type": "number"
        },
        "fine_print": {
          "type": "string",
          "maxLength": 2000
        },
        "redeem_by": {
          "type": "string",
          "format": "date-time"
        },
        "voucher_type": {
          "enum": [
            "voucher",
            "promo_code",
            "instant",
            "booking"
          ]
        },
        "promo_code": {
          "type": "string",
          "maxLength": 100
        },
        "max_per_customer": {
          "type": "integer"
        },
        "quantity_available": {
          "type": "integer"
        }
      }
    },
    "product": {
      "type": "object",
      "description": "sell.* types",
      "properties": {
        "sku": {
          "type": "string",
          "maxLength": 100
        },
        "brand": {
          "type": "string",
          "maxLength": 120
        },
        "gtin": {
          "type": "string",
          "maxLength": 20
        },
        "condition": {
          "enum": [
            "new",
            "used",
            "refurbished"
          ]
        },
        "availability": {
          "enum": [
            "in_stock",
            "out_of_stock",
            "preorder",
            "limited"
          ]
        },
        "digital": {
          "type": "boolean"
        },
        "shipping": {
          "type": "object",
          "properties": {
            "regions": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "maxItems": 50
            },
            "price_cents": {
              "type": "integer",
              "minimum": 0,
              "description": "Money in minor units (cents). 2500 = 25.00."
            },
            "days": {
              "type": "integer"
            }
          }
        },
        "variants": {
          "type": "array",
          "maxItems": 100,
          "items": {
            "type": "object",
            "required": [
              "name"
            ],
            "properties": {
              "sku": {
                "type": "string",
                "maxLength": 100
              },
              "name": {
                "type": "string",
                "maxLength": 200
              },
              "price_cents": {
                "type": "integer",
                "minimum": 0,
                "description": "Money in minor units (cents). 2500 = 25.00."
              }
            }
          }
        }
      }
    },
    "course": {
      "type": "object",
      "description": "teach.* types",
      "properties": {
        "format": {
          "enum": [
            "online",
            "in_person",
            "hybrid",
            "self_paced"
          ]
        },
        "level": {
          "enum": [
            "beginner",
            "intermediate",
            "advanced",
            "all"
          ]
        },
        "duration_hours": {
          "type": "number"
        },
        "lessons_count": {
          "type": "integer"
        },
        "certificate": {
          "type": "boolean"
        },
        "instructor": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "maxLength": 200
            },
            "bio": {
              "type": "string",
              "maxLength": 2000
            }
          }
        },
        "prerequisites": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        }
      }
    },
    "stay": {
      "type": "object",
      "description": "rent.* types",
      "properties": {
        "property_type": {
          "type": "string",
          "maxLength": 80
        },
        "bedrooms": {
          "type": "integer"
        },
        "bathrooms": {
          "type": "number"
        },
        "max_guests": {
          "type": "integer"
        },
        "amenities": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "check_in": {
          "type": "string",
          "maxLength": 40
        },
        "check_out": {
          "type": "string",
          "maxLength": 40
        },
        "min_nights": {
          "type": "integer"
        },
        "rate_unit": {
          "enum": [
            "hour",
            "day",
            "night",
            "week",
            "month"
          ]
        },
        "deposit_cents": {
          "type": "integer",
          "minimum": 0,
          "description": "Money in minor units (cents). 2500 = 25.00."
        }
      }
    },
    "profile": {
      "type": "object",
      "description": "talent.* and venues.* types",
      "properties": {
        "kind": {
          "enum": [
            "talent",
            "venue"
          ]
        },
        "headline": {
          "type": "string",
          "maxLength": 200
        },
        "capacity": {
          "type": "integer"
        },
        "rate_cents": {
          "type": "integer",
          "minimum": 0,
          "description": "Money in minor units (cents). 2500 = 25.00."
        },
        "rate_unit": {
          "enum": [
            "hour",
            "day",
            "event",
            "set",
            "project"
          ]
        },
        "genres": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "skills": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "amenities": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 50
        },
        "portfolio_urls": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "maxItems": 20
        }
      }
    },
    "studio": {
      "type": "object",
      "description": "studio.* types (video, show, podcast, music, article)",
      "properties": {
        "duration_seconds": {
          "type": "integer"
        },
        "embed_url": {
          "type": "string",
          "maxLength": 2000,
          "format": "uri",
          "pattern": "^https://"
        },
        "season": {
          "type": "integer"
        },
        "episode": {
          "type": "integer"
        },
        "published_at": {
          "type": "string",
          "format": "date-time"
        }
      }
    }
  },
  "additionalProperties": true
}