{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://splitclubapp.com/schemas/v1/boat.json",
  "title": "Boat",
  "description": "One boat in a rental fleet, with everything SplitClub needs to show it: specs, captain terms, home marina, prices by duration, photos and amenities. Sent one at a time or up to 100 per request (POST /v1/boats), upserted on external_id. Every boat is reviewed before it can appear, and only ever inside a boat-day or tie-up event SplitClub runs; boats are never shown on a map.",
  "x-doc": "schema-boat",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "external_id",
    "name",
    "type",
    "length_ft",
    "capacity",
    "captain",
    "location",
    "pricing",
    "photo_urls"
  ],
  "properties": {
    "external_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128,
      "pattern": "^[^\\s]+$",
      "description": "Your system's id for this boat. Sending the same external_id again updates it; it never makes a second one.",
      "x-expected": "your own id for it, 1–128 characters, no spaces",
      "examples": [
        "listing-88213"
      ]
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120,
      "description": "The boat's name or listing title.",
      "examples": [
        "55' Azimut \"Sea Breeze\""
      ]
    },
    "type": {
      "type": "string",
      "enum": [
        "motor_yacht",
        "sport_yacht",
        "catamaran",
        "sailboat",
        "center_console",
        "pontoon",
        "jet_ski",
        "other"
      ]
    },
    "make": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80,
      "examples": [
        "Azimut"
      ]
    },
    "model": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80,
      "examples": [
        "55 Flybridge"
      ]
    },
    "year": {
      "type": "integer",
      "minimum": 1900,
      "maximum": 2100,
      "examples": [
        2019
      ]
    },
    "length_ft": {
      "type": "number",
      "minimum": 5,
      "maximum": 600,
      "description": "Length overall, in feet.",
      "examples": [
        55
      ]
    },
    "capacity": {
      "type": "integer",
      "minimum": 1,
      "maximum": 500,
      "description": "The most guests allowed aboard for a day trip, crew not counted.",
      "examples": [
        12
      ]
    },
    "cabins": {
      "type": "integer",
      "minimum": 0,
      "maximum": 50,
      "examples": [
        3
      ]
    },
    "captain": {
      "type": "string",
      "enum": [
        "included",
        "optional",
        "required_extra",
        "bareboat"
      ],
      "description": "included: a captain comes with every booking; optional: the renter chooses; required_extra: a captain is required and charged on top (see fees_note); bareboat: no captain, the renter skippers."
    },
    "location": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "city"
      ],
      "description": "Where the boat is kept and boards. Used to place it in the right city's events and to tell guests where to go; never drawn on a map.",
      "properties": {
        "city": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "examples": [
            "Miami"
          ]
        },
        "region": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "State, province or region.",
          "examples": [
            "FL"
          ]
        },
        "country": {
          "type": "string",
          "pattern": "^[A-Z]{2}$",
          "x-expected": "a two-letter ISO 3166-1 country code in upper case (e.g. \"US\")",
          "examples": [
            "US"
          ]
        },
        "marina": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120,
          "examples": [
            "Miami Beach Marina"
          ]
        },
        "address": {
          "type": "string",
          "minLength": 1,
          "maxLength": 300,
          "examples": [
            "300 Alton Rd, Miami Beach, FL 33139"
          ]
        },
        "lat": {
          "type": "number",
          "minimum": -90,
          "maximum": 90,
          "examples": [
            25.7712
          ]
        },
        "lng": {
          "type": "number",
          "minimum": -180,
          "maximum": 180,
          "examples": [
            -80.1397
          ]
        }
      },
      "dependentRequired": {
        "lat": [
          "lng"
        ],
        "lng": [
          "lat"
        ]
      }
    },
    "pricing": {
      "type": "array",
      "minItems": 1,
      "maxItems": 20,
      "description": "What the whole boat costs for each duration you rent it for.",
      "items": {
        "$ref": "#/$defs/rate"
      }
    },
    "deposit_amount": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100000000,
      "description": "Security deposit in integer minor units, in the pricing currency.",
      "x-expected": "an integer in minor units (e.g. 100000 for $1,000.00)",
      "examples": [
        100000
      ]
    },
    "deposit_note": {
      "type": "string",
      "maxLength": 1000,
      "examples": [
        "Refundable $1,000 hold, released within 48 hours of return."
      ]
    },
    "fees_note": {
      "type": "string",
      "maxLength": 1000,
      "description": "Anything charged on top of the rate: fuel, captain's gratuity, cleaning, dockage, taxes.",
      "examples": [
        "Captain's gratuity (18–20%) and Florida sales tax not included."
      ]
    },
    "amenities": {
      "type": "array",
      "maxItems": 40,
      "uniqueItems": true,
      "description": "What's aboard. Free text; these words are recognized: bathroom, air_conditioning, bluetooth_audio, cooler, ice, water_toys, paddleboard, snorkel_gear, floating_mat, seabob, jet_ski, tender, swim_platform, grill, shade, wifi.",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 60
      },
      "examples": [
        [
          "bathroom",
          "air_conditioning",
          "bluetooth_audio",
          "floating_mat"
        ]
      ]
    },
    "photo_urls": {
      "type": "array",
      "minItems": 1,
      "maxItems": 20,
      "uniqueItems": true,
      "description": "In the order to show them; the first is the cover.",
      "items": {
        "type": "string",
        "maxLength": 2000,
        "pattern": "^https://[^\\s]{4,}$",
        "format": "uri",
        "x-expected": "an https:// URL",
        "examples": [
          "https://example.com/boats/88213/cover.jpg"
        ]
      }
    },
    "description": {
      "type": "string",
      "maxLength": 4000
    },
    "booking_url": {
      "type": "string",
      "maxLength": 2000,
      "pattern": "^https://[^\\s]{4,}$",
      "format": "uri",
      "x-expected": "an https:// URL",
      "description": "Where this boat is booked on your own site.",
      "examples": [
        "https://example.com/boats/88213"
      ]
    },
    "availability": {
      "type": "object",
      "additionalProperties": false,
      "description": "Optional. A calendar feed, a page, or explicit blocks: whichever your system has.",
      "properties": {
        "ical_url": {
          "type": "string",
          "maxLength": 2000,
          "pattern": "^https://[^\\s]{4,}$",
          "format": "uri",
          "x-expected": "an https:// URL to an iCalendar (.ics) feed",
          "examples": [
            "https://example.com/boats/88213/calendar.ics"
          ]
        },
        "url": {
          "type": "string",
          "maxLength": 2000,
          "pattern": "^https://[^\\s]{4,}$",
          "format": "uri",
          "x-expected": "an https:// URL",
          "examples": [
            "https://example.com/boats/88213/availability"
          ]
        },
        "timezone": {
          "type": "string",
          "maxLength": 64,
          "pattern": "^[A-Za-z_]+(/[A-Za-z0-9_+-]+){0,2}$",
          "x-expected": "an IANA time zone (e.g. \"America/New_York\")",
          "examples": [
            "America/New_York"
          ]
        },
        "blocks": {
          "type": "array",
          "maxItems": 500,
          "items": {
            "$ref": "#/$defs/block"
          }
        }
      }
    },
    "source_platform": {
      "type": "string",
      "minLength": 1,
      "maxLength": 40,
      "pattern": "^[a-z0-9_-]+$",
      "description": "Where the listing lives, as a lower-case slug: your platform's name, or \"own\" for a fleet's own system.",
      "x-expected": "a lower-case slug (e.g. \"own\")",
      "examples": [
        "own"
      ]
    },
    "source_url": {
      "type": "string",
      "maxLength": 2000,
      "pattern": "^https://[^\\s]{4,}$",
      "format": "uri",
      "x-expected": "an https:// URL",
      "description": "The listing's public page on that platform.",
      "examples": [
        "https://example.com/boats/88213"
      ]
    },
    "active": {
      "type": "boolean",
      "default": true,
      "description": "false takes the boat out of everything at once (sold, laid up, delisted). Send true again to bring it back; an unchanged boat that was approved returns approved."
    },
    "metadata": {
      "type": "object",
      "maxProperties": 20,
      "propertyNames": {
        "type": "string",
        "minLength": 1,
        "maxLength": 40,
        "pattern": "^[A-Za-z0-9_.-]+$"
      },
      "additionalProperties": {
        "type": "string",
        "maxLength": 500
      },
      "description": "Up to 20 string keys of your own, echoed back on every read.",
      "examples": [
        {
          "owner_ref": "OWN-2231"
        }
      ]
    },
    "extra": {
      "type": "object",
      "maxProperties": 50,
      "description": "Anything else your listing has (engines, speed, fuel burn, rules…), in your own shape. Stored with the boat and shown to SplitClub's reviewers; not shown to guests.",
      "examples": [
        {
          "engines": "2 x Volvo IPS 800",
          "cruise_knots": 26
        }
      ]
    }
  },
  "$defs": {
    "rate": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "duration_hours",
        "amount",
        "currency"
      ],
      "properties": {
        "label": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "examples": [
            "Half day"
          ]
        },
        "duration_hours": {
          "type": "number",
          "minimum": 0.5,
          "maximum": 336,
          "examples": [
            4
          ]
        },
        "amount": {
          "type": "integer",
          "minimum": 0,
          "maximum": 100000000,
          "description": "The price of the whole boat for this duration, in integer minor units (cents for usd): 220000 is $2,200.00.",
          "x-expected": "an integer in minor units (e.g. 220000 for $2,200.00)",
          "examples": [
            220000
          ]
        },
        "currency": {
          "type": "string",
          "pattern": "^[a-z]{3}$",
          "description": "ISO 4217 currency code, lower case.",
          "x-expected": "a three-letter ISO 4217 code in lower case (e.g. \"usd\")",
          "examples": [
            "usd"
          ]
        },
        "captain_included": {
          "type": "boolean",
          "examples": [
            true
          ]
        },
        "fuel_included": {
          "type": "boolean",
          "examples": [
            true
          ]
        },
        "days": {
          "type": "array",
          "minItems": 1,
          "maxItems": 7,
          "uniqueItems": true,
          "description": "Only on these days (weekend rates, say). Omit for every day.",
          "items": {
            "type": "string",
            "enum": [
              "mon",
              "tue",
              "wed",
              "thu",
              "fri",
              "sat",
              "sun"
            ]
          }
        }
      }
    },
    "block": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "starts_at",
        "ends_at"
      ],
      "properties": {
        "starts_at": {
          "type": "string",
          "format": "date-time",
          "examples": [
            "2026-11-07T10:00:00-05:00"
          ]
        },
        "ends_at": {
          "type": "string",
          "format": "date-time",
          "examples": [
            "2026-11-07T14:00:00-05:00"
          ]
        },
        "status": {
          "type": "string",
          "enum": [
            "available",
            "unavailable"
          ],
          "default": "unavailable"
        }
      }
    }
  },
  "examples": [
    {
      "external_id": "listing-88213",
      "name": "55' Azimut \"Sea Breeze\"",
      "type": "motor_yacht",
      "make": "Azimut",
      "model": "55 Flybridge",
      "year": 2019,
      "length_ft": 55,
      "capacity": 12,
      "cabins": 3,
      "captain": "included",
      "location": {
        "city": "Miami",
        "region": "FL",
        "country": "US",
        "marina": "Miami Beach Marina",
        "address": "300 Alton Rd, Miami Beach, FL 33139",
        "lat": 25.7712,
        "lng": -80.1397
      },
      "pricing": [
        {
          "label": "Half day",
          "duration_hours": 4,
          "amount": 220000,
          "currency": "usd",
          "captain_included": true,
          "fuel_included": true
        },
        {
          "label": "Full day",
          "duration_hours": 8,
          "amount": 380000,
          "currency": "usd",
          "captain_included": true,
          "fuel_included": true
        }
      ],
      "deposit_amount": 100000,
      "deposit_note": "Refundable $1,000 hold, released within 48 hours of return.",
      "fees_note": "Captain's gratuity (18–20%) and Florida sales tax not included.",
      "amenities": [
        "bathroom",
        "air_conditioning",
        "bluetooth_audio",
        "floating_mat",
        "paddleboard",
        "cooler"
      ],
      "photo_urls": [
        "https://example.com/boats/88213/cover.jpg",
        "https://example.com/boats/88213/flybridge.jpg",
        "https://example.com/boats/88213/salon.jpg"
      ],
      "description": "Three-cabin Azimut flybridge with a sunpad forward and a shaded aft deck. Cruise Biscayne Bay, anchor off Nixon Sandbar, and swim from the platform.",
      "booking_url": "https://example.com/boats/88213",
      "availability": {
        "ical_url": "https://example.com/boats/88213/calendar.ics",
        "timezone": "America/New_York"
      },
      "source_platform": "own",
      "source_url": "https://example.com/boats/88213",
      "active": true,
      "metadata": {
        "owner_ref": "OWN-2231"
      }
    }
  ]
}
