{"openapi":"3.1.0","info":{"title":"Charter Captain API","version":"1.0.0","description":"REST API for the Charter Captain iOS apps"},"servers":[{"url":"http://localhost:3002","description":"Development"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Clerk JWT token"}},"schemas":{"Customer":{"type":"object","required":["id","firstName","lastName","email"],"properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"CustomerSummary":{"allOf":[{"$ref":"#/components/schemas/Customer"},{"type":"object","properties":{"trips":{"type":"integer"},"totalSpent":{"type":"number"},"lastTrip":{"type":"string","format":"date-time","nullable":true}}}]},"Payment":{"type":"object","required":["id","amount","type","status"],"properties":{"id":{"type":"string"},"bookingId":{"type":"string"},"amount":{"type":"number"},"type":{"type":"string","enum":["DEPOSIT","BALANCE","TIP","REFUND"]},"status":{"type":"string","enum":["PENDING","SUCCEEDED","FAILED","REFUNDED"]},"createdAt":{"type":"string","format":"date-time"}}},"WeatherCheck":{"type":"object","properties":{"id":{"type":"string"},"bookingId":{"type":"string"},"checkedAt":{"type":"string","format":"date-time"},"hasAdvisory":{"type":"boolean"},"advisoryType":{"type":"string","nullable":true},"windSpeed":{"type":"number","nullable":true},"waveHeight":{"type":"number","nullable":true},"forecast":{"type":"string","nullable":true}}},"ContractTemplate":{"type":"object","required":["id","boatOwnerId","name","content","version","isActive","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"boatOwnerId":{"type":"string"},"name":{"type":"string"},"content":{"type":"string"},"version":{"type":"integer"},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"WaiverTemplate":{"type":"object","required":["id","boatOwnerId","name","content","version","isActive","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"boatOwnerId":{"type":"string"},"name":{"type":"string"},"content":{"type":"string"},"version":{"type":"integer"},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"SignedContract":{"type":"object","required":["id","rentalId","contractTemplateId","contractTemplateVersion","signerName","signerEmail","signedAt"],"properties":{"id":{"type":"string"},"rentalId":{"type":"string"},"contractTemplateId":{"type":"string"},"contractTemplateVersion":{"type":"integer"},"signerName":{"type":"string"},"signerEmail":{"type":"string","format":"email"},"signedAt":{"type":"string","format":"date-time"},"signatureData":{"type":"string","nullable":true}}},"Guest":{"type":"object","required":["id","rentalId","firstName","lastName","email","isCharterer","waiverStatus"],"properties":{"id":{"type":"string"},"rentalId":{"type":"string"},"waiverTemplateId":{"type":"string","nullable":true},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string","nullable":true},"isCharterer":{"type":"boolean"},"waiverStatus":{"type":"string","enum":["PENDING","SENT","SIGNED"]},"waiverSentAt":{"type":"string","format":"date-time","nullable":true},"waiverSignedAt":{"type":"string","format":"date-time","nullable":true},"signatureData":{"type":"string","nullable":true}}},"DocumentStatus":{"type":"object","required":["rentalId","charterAgreement","waivers"],"properties":{"rentalId":{"type":"string"},"charterAgreement":{"type":"object","required":["required","signed"],"properties":{"required":{"type":"boolean"},"signed":{"type":"boolean"},"signerName":{"type":"string","nullable":true},"signerEmail":{"type":"string","format":"email","nullable":true},"signedAt":{"type":"string","format":"date-time","nullable":true}}},"waivers":{"type":"object","required":["total","signed","pending","guests"],"properties":{"total":{"type":"integer"},"signed":{"type":"integer"},"pending":{"type":"integer"},"guests":{"type":"array","items":{"$ref":"#/components/schemas/Guest"}}}}}},"CoastGuardView":{"type":"object","required":["vessel","charter","charterAgreement","passengers"],"properties":{"vessel":{"type":"object"},"charter":{"type":"object"},"charterAgreement":{"type":"object"},"passengers":{"type":"array","items":{"type":"object"}}}},"Review":{"type":"object","required":["id","rentalId","rating","authorName","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"rentalId":{"type":"string"},"captainId":{"type":"string","nullable":true},"boatId":{"type":"string","nullable":true},"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":"string","nullable":true},"authorName":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"PublicReview":{"type":"object","required":["id","rating","authorName","createdAt"],"properties":{"id":{"type":"string"},"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":"string","nullable":true},"authorName":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"Favorite":{"type":"object","required":["id","customerId","createdAt"],"properties":{"id":{"type":"string"},"customerId":{"type":"string"},"captainId":{"type":"string","nullable":true},"boatId":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"MarketplaceCaptain":{"type":"object","required":["slug","name","defaultHarbor","rating","reviewCount","startingPrice"],"properties":{"slug":{"type":"string"},"name":{"type":"string"},"bio":{"type":"string","nullable":true},"profileImageUrl":{"type":"string","nullable":true},"defaultHarbor":{"type":"string","nullable":true,"deprecated":true,"description":"Deprecated: use harbors[]. This field returns the home harbor name. Removed in a future release."},"harbors":{"type":"array","items":{"type":"object","properties":{"harborId":{"type":"string"},"isHome":{"type":"boolean"},"name":{"type":"string"}},"required":["harborId","isHome","name"]}},"rating":{"type":"number"},"reviewCount":{"type":"integer"},"startingPrice":{"type":"number"}}},"MarketplaceBoat":{"type":"object","required":["id","name","type","photos","capacity","harbor","ownerName","rating","reviewCount"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"photos":{"type":"array","items":{"type":"string"}},"capacity":{"type":"integer"},"minRatePerHour":{"type":"number","nullable":true},"maxRatePerHour":{"type":"number","nullable":true},"harbor":{"type":"string"},"ownerName":{"type":"string"},"rating":{"type":"number"},"reviewCount":{"type":"integer"}}},"CaptainProfile":{"type":"object","required":["slug","name","defaultHarbor","rating","reviewCount","startingPrice"],"properties":{"slug":{"type":"string"},"name":{"type":"string"},"bio":{"type":"string","nullable":true},"profileImageUrl":{"type":"string","nullable":true},"defaultHarbor":{"type":"string","nullable":true,"deprecated":true,"description":"Deprecated: use harbors[]. This field returns the home harbor name. Removed in a future release."},"harbors":{"type":"array","items":{"type":"object","properties":{"harborId":{"type":"string"},"isHome":{"type":"boolean"},"name":{"type":"string"}},"required":["harborId","isHome","name"]}},"rating":{"type":"number"},"reviewCount":{"type":"integer"},"startingPrice":{"type":"number"},"depositType":{"type":"string","enum":["FIXED","PERCENTAGE"],"nullable":true},"depositValue":{"type":"integer","nullable":true},"windowDays":{"type":"integer","nullable":true},"depositRefundable":{"type":"boolean","nullable":true},"weatherCancellationPolicy":{"type":"string","nullable":true},"availableDurations":{"type":"array","items":{"$ref":"#/components/schemas/Duration"}},"pricingTiers":{"type":"array","items":{"$ref":"#/components/schemas/PricingTier"}}}},"BoatPricingTier":{"type":"object","required":["id","name","ratePerHour","daysOfWeek","isSpecialRate"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePerHour":{"type":"number"},"daysOfWeek":{"type":"array","items":{"type":"integer","minimum":0,"maximum":6}},"isSpecialRate":{"type":"boolean"}}},"BoatProfile":{"type":"object","required":["id","name","type","photos","capacity","harbor","ownerName","rating","reviewCount"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"photos":{"type":"array","items":{"type":"string"}},"capacity":{"type":"integer"},"pricingTiers":{"type":"array","items":{"$ref":"#/components/schemas/BoatPricingTier"}},"durations":{"type":"array","description":"Bookable trip lengths for the boat's owner tenant. `id` is the durationId required when creating a rental.","items":{"type":"object","required":["id","hours","label","sortOrder"],"properties":{"id":{"type":"string"},"hours":{"type":"integer"},"label":{"type":"string"},"sortOrder":{"type":"integer"}}}},"harbor":{"type":"string"},"ownerName":{"type":"string"},"rating":{"type":"number"},"reviewCount":{"type":"integer"},"features":{"type":"array","items":{"type":"string"},"nullable":true},"rules":{"type":"string","nullable":true},"depositType":{"type":"string","enum":["FIXED","PERCENTAGE"],"nullable":true},"depositValue":{"type":"integer","nullable":true},"cancellationWindowDays":{"type":"integer","nullable":true}}},"Booking":{"type":"object","required":["id","captainId","date","status"],"properties":{"id":{"type":"string"},"captainId":{"type":"string"},"date":{"type":"string","format":"date-time"},"startTime":{"type":"string","nullable":true},"endTime":{"type":"string","nullable":true},"status":{"type":"string","enum":["PENDING_DEPOSIT","DEPOSIT_PAID","CONFIRMED","COMPLETED","CANCELLED","WEATHER_CANCELLED"]},"totalPrice":{"type":"number","nullable":true},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"customer":{"$ref":"#/components/schemas/Customer"},"harbor":{"$ref":"#/components/schemas/Harbor"},"duration":{"$ref":"#/components/schemas/Duration"},"payments":{"type":"array","items":{"$ref":"#/components/schemas/Payment"}},"weatherChecks":{"type":"array","items":{"$ref":"#/components/schemas/WeatherCheck"}},"notifications":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}}}},"BookingSummary":{"type":"object","required":["id","captainId","date","status"],"properties":{"id":{"type":"string"},"captainId":{"type":"string"},"date":{"type":"string","format":"date-time"},"status":{"type":"string"},"totalPrice":{"type":"number","nullable":true},"customer":{"$ref":"#/components/schemas/Customer"},"harbor":{"$ref":"#/components/schemas/Harbor"}}},"IdempotencyConflictError":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["IDEMPOTENCY_CONFLICT"]}},"required":["message","code"]}},"required":["error"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"}},"required":["message","code"]}},"required":["error"]},"PaginationMeta":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"}},"required":["page","limit","total"]},"Captain":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"phone":{"type":["string","null"]},"bio":{"type":["string","null"]},"profileImageUrl":{"type":["string","null"]},"homeHarborId":{"type":["string","null"],"description":"Convenience pointer to the harbor in `harbors[]` with `isHome = true`. Returned for clients that only need the captain's primary harbor without iterating the list."},"harbors":{"type":"array","items":{"type":"object","properties":{"harborId":{"type":"string"},"isHome":{"type":"boolean"},"name":{"type":"string"}},"required":["harborId","isHome","name"]}},"depositType":{"type":["string","null"],"enum":["FIXED","PERCENTAGE",null]},"depositValue":{"type":["integer","null"]},"cancellationWindowDays":{"type":["integer","null"]},"depositRefundable":{"type":"boolean"},"weatherCancellationPolicy":{"type":["string","null"]},"notificationPreferences":{},"googleCalendarConnected":{"type":"boolean"}},"required":["id"]},"DeviceToken":{"type":"object","properties":{"id":{"type":"string"},"captainId":{"type":"string"},"token":{"type":"string"},"platform":{"type":"string","enum":["ios"]},"isActive":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","captainId","token","platform"]},"CreateBoat":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"harborId":{"type":"string","minLength":1},"capacity":{"type":"integer","minimum":1,"maximum":50},"slipNumber":{"type":["string","null"],"maxLength":200},"gateCode":{"type":["string","null"],"maxLength":200},"slipInstructions":{"type":["string","null"],"maxLength":200},"fuelSurchargeEnabled":{"type":"boolean"},"fuelSurchargeAmount":{"type":["integer","null"],"minimum":0},"depositType":{"type":"string","enum":["FIXED","PERCENTAGE"]},"depositValue":{"type":"integer","minimum":0},"photos":{"type":"array","items":{"type":"string","format":"uri"},"default":[]}},"required":["name","type","harborId","capacity"]},"UpdateBoat":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"harborId":{"type":"string","minLength":1},"capacity":{"type":"integer","minimum":1,"maximum":50},"slipNumber":{"type":["string","null"],"maxLength":200},"gateCode":{"type":["string","null"],"maxLength":200},"slipInstructions":{"type":["string","null"],"maxLength":200},"fuelSurchargeEnabled":{"type":"boolean"},"fuelSurchargeAmount":{"type":["integer","null"],"minimum":0},"depositType":{"type":"string","enum":["FIXED","PERCENTAGE"]},"depositValue":{"type":"integer","minimum":0},"photos":{"type":"array","items":{"type":"string","format":"uri"},"default":[]},"isActive":{"type":"boolean"},"isListed":{"type":"boolean"}}},"Boat":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"description":{"type":["string","null"]},"capacity":{"type":"integer"},"lengthFeet":{"type":["integer","null"]},"year":{"type":["integer","null"]},"make":{"type":["string","null"]},"model":{"type":["string","null"]},"photos":{"type":"array","items":{"type":"string"}},"features":{"type":"array","items":{"type":"string"}},"rules":{"type":["string","null"]},"depositType":{"type":["string","null"],"enum":["FIXED","PERCENTAGE",null]},"depositValue":{"type":["integer","null"]},"fuelSurchargeEnabled":{"type":["boolean","null"]},"fuelSurchargeAmount":{"type":["integer","null"]},"minimumNoticeHours":{"type":"integer"},"harborId":{"type":"string"},"isActive":{"type":"boolean"},"isListed":{"type":"boolean"},"slipNumber":{"type":["string","null"]},"gateCode":{"type":["string","null"]},"slipInstructions":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","ownerId","name","type","description","capacity","lengthFeet","year","make","model","photos","features","rules","depositType","depositValue","fuelSurchargeEnabled","fuelSurchargeAmount","minimumNoticeHours","harborId","isActive","isListed","slipNumber","gateCode","slipInstructions","createdAt","updatedAt"]},"ManualPayment":{"type":"object","properties":{"type":{"type":"string","enum":["DEPOSIT","BALANCE","TIP"]},"amount":{"type":"integer","minimum":0},"note":{"type":"string","default":""}},"required":["type","amount"]},"ManualBooking":{"type":"object","properties":{"date":{"type":"string","minLength":1},"startTime":{"type":"string","minLength":1},"endTime":{"type":"string","minLength":1},"durationId":{"type":"string"},"harborId":{"type":"string","minLength":1},"pricingTierId":{"type":"string","minLength":1},"firstName":{"type":"string","default":""},"lastName":{"type":"string","default":""},"email":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","format":"email"}],"default":""},"phone":{"type":"string","default":""},"partySize":{"type":"integer","minimum":1,"maximum":13},"specialRequests":{"type":"string","default":""},"slipNumber":{"type":["string","null"],"maxLength":200},"gateCode":{"type":["string","null"],"maxLength":200},"slipInstructions":{"type":["string","null"],"maxLength":200},"status":{"type":"string","enum":["CONFIRMED","DEPOSIT_PAID","BALANCE_PAID","COMPLETED"]},"totalPriceOverride":{"type":"integer","minimum":0},"depositOverride":{"type":"integer","minimum":0},"balanceOverride":{"type":"integer","minimum":0},"tipAmount":{"type":"integer","minimum":0},"payments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["DEPOSIT","BALANCE","TIP"]},"amount":{"type":"integer","minimum":0},"note":{"type":"string","default":""}},"required":["type","amount"]},"default":[]}},"required":["date","startTime","endTime","harborId","pricingTierId","partySize","status"]},"ManualBookingResponse":{"type":"object","properties":{"bookingId":{"type":"string"}},"required":["bookingId"]},"CustomerGigDetail":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"harbor":{"type":"string"},"boatTypes":{"type":"array","items":{"type":"string","enum":["POWERBOAT","SAILBOAT","YACHT","PONTOON","FISHING"]},"default":[]},"date":{"type":"string","format":"date"},"startTime":{"type":["string","null"]},"duration":{"type":"string"},"compensationCents":{"type":["integer","null"]},"needsCaptain":{"type":"boolean"},"needsBoat":{"type":"boolean"},"status":{"type":"string"},"bids":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slot":{"type":"string","enum":["CAPTAIN","BOAT"]},"status":{"type":"string","enum":["SUBMITTED","SELECTED","APPROVED","VETOED","WITHDRAWN"]},"proposedRateCents":{"type":"integer"},"message":{"type":["string","null"]},"bidderTenant":{"type":"object","properties":{"id":{"type":"string"},"displayName":{"type":"string"},"slug":{"type":["string","null"]}},"required":["id","displayName"]},"boat":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"required":["id","slot","status","proposedRateCents","bidderTenant"]}},"convertedBooking":{"type":["object","null"],"properties":{"id":{"type":"string"},"status":{"type":"string"},"date":{"type":"string","format":"date"}},"required":["id","status","date"]}},"required":["id","title","harbor","date","duration","needsCaptain","needsBoat","status","bids"]},"Harbor":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"sortOrder":{"type":"integer"},"isActive":{"type":"boolean"}},"required":["id","name","isActive"]},"DepositPaymentIntent":{"type":"object","properties":{"clientSecret":{"type":"string"},"stripeAccountId":{"type":"string"}},"required":["clientSecret","stripeAccountId"]},"Notification":{"type":"object","properties":{"id":{"type":"string"},"bookingId":{"type":"string"},"type":{"type":"string"},"sentAt":{"type":"string","format":"date-time"},"status":{"type":"string"}}},"NotificationPreferences":{"type":"object","properties":{"push":{"type":"object","properties":{"newBooking":{"type":"boolean"},"paymentReceived":{"type":"boolean"},"tipReceived":{"type":"boolean"},"weatherAlert":{"type":"boolean"},"bookingCancelled":{"type":"boolean"},"waiverSigned":{"type":"boolean"},"allDocumentsSigned":{"type":"boolean"},"checkoutReminder":{"type":"boolean"}}}}},"PushTestResult":{"type":"object","properties":{"sent":{"type":"integer","description":"Number of devices reached"},"failed":{"type":"integer","description":"Number of delivery failures"}},"required":["sent","failed"]},"RecordPayment":{"type":"object","properties":{"type":{"type":"string","enum":["DEPOSIT","BALANCE","TIP"]},"amount":{"type":"integer","minimum":0},"method":{"type":"string","enum":["CASH","CHECK","CONNECTED_CARD"]},"note":{"type":"string","default":""},"connectedPaymentMethodId":{"type":["string","null"]},"source":{"type":["string","null"],"enum":["CASH","CHECK","VENMO","ZELLE","PAYPAL","OTHER",null]}},"required":["type","amount","method"]},"Refund":{"type":"object","properties":{"amount":{"type":"integer","minimum":1},"reason":{"type":"string","enum":["REQUESTED_BY_CUSTOMER","DUPLICATE","FRAUDULENT","OTHER"]},"note":{"type":"string","default":""}}},"RecordRentalPayment":{"type":"object","properties":{"type":{"type":"string","enum":["RENTAL_DEPOSIT","RENTAL_BALANCE","SECURITY_AUTHORIZATION","SECURITY_CAPTURE"]},"amount":{"type":"integer","minimum":0},"method":{"type":"string","enum":["CASH","CHECK","CONNECTED_CARD"]},"note":{"type":"string","default":""},"connectedPaymentMethodId":{"type":["string","null"]},"source":{"type":["string","null"],"enum":["CASH","CHECK","VENMO","ZELLE","PAYPAL","OTHER",null]}},"required":["type","amount","method"]},"RentalRefund":{"type":"object","properties":{"amount":{"type":"integer","minimum":1},"reason":{"type":"string","enum":["REQUESTED_BY_CUSTOMER","DUPLICATE","FRAUDULENT","OTHER"]},"note":{"type":"string","default":""}}},"ManualRentalPayment":{"type":"object","properties":{"amount":{"type":"integer","minimum":0},"type":{"type":"string","enum":["RENTAL_DEPOSIT","RENTAL_BALANCE","SECURITY_AUTHORIZATION","SECURITY_CAPTURE","SECURITY_RELEASE","RENTAL_REFUND"]},"note":{"type":"string","default":""}},"required":["amount","type"]},"ManualRental":{"type":"object","properties":{"boatId":{"type":"string","minLength":1},"date":{"type":"string","minLength":1},"startTime":{"type":"string","minLength":1},"endTime":{"type":"string","default":""},"durationHours":{"type":"number","exclusiveMinimum":0,"maximum":24},"pricingTierId":{"type":"string","minLength":1},"firstName":{"type":"string","default":""},"lastName":{"type":"string","default":""},"email":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","format":"email"}],"default":""},"phone":{"type":"string","default":""},"partySize":{"type":"integer","minimum":1,"maximum":150},"specialRequests":{"type":"string","default":""},"status":{"type":"string","enum":["CONFIRMED","DEPOSIT_PAID","BALANCE_PAID","COMPLETED"]},"totalPriceOverride":{"type":"integer","minimum":0},"depositOverride":{"type":"integer","minimum":0},"balanceOverride":{"type":"integer","minimum":0},"payments":{"type":"array","items":{"type":"object","properties":{"amount":{"type":"integer","minimum":0},"type":{"type":"string","enum":["RENTAL_DEPOSIT","RENTAL_BALANCE","SECURITY_AUTHORIZATION","SECURITY_CAPTURE","SECURITY_RELEASE","RENTAL_REFUND"]},"note":{"type":"string","default":""}},"required":["amount","type"]},"default":[]}},"required":["boatId","date","startTime","pricingTierId","partySize","status"]},"ManualRentalResponse":{"type":"object","properties":{"rentalId":{"type":"string"}},"required":["rentalId"]},"AvailabilityException":{"type":"object","properties":{"id":{"type":"string"},"captainId":{"type":"string"},"date":{"type":"string","format":"date-time"},"isBlocked":{"type":"boolean"},"reason":{"type":["string","null"]},"overrideStart":{"type":["string","null"]},"overrideEnd":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","captainId","date","isBlocked"]},"AvailabilityRule":{"type":"object","properties":{"id":{"type":"string"},"captainId":{"type":"string"},"dayOfWeek":{"type":"integer","minimum":0,"maximum":6},"startTime":{"type":["string","null"]},"endTime":{"type":["string","null"]},"isActive":{"type":"boolean"}},"required":["id","captainId","dayOfWeek"]},"Duration":{"type":"object","properties":{"id":{"type":"string"},"hours":{"type":"integer"},"label":{"type":"string"},"isActive":{"type":"boolean"},"sortOrder":{"type":"integer"}},"required":["id","hours","label"]},"PricingTier":{"type":"object","properties":{"id":{"type":"string"},"captainId":{"type":"string"},"name":{"type":"string"},"ratePerHour":{"type":["number","null"]},"isActive":{"type":"boolean"}},"required":["id","captainId","name"]},"SubOutRequestInput":{"type":"object","properties":{"bookingId":{"type":"string"},"reason":{"type":"string"}},"required":["bookingId"]},"SubOutRequestBody":{"type":"object","properties":{"reason":{"type":"string"}}},"GetShortlistCandidatesInput":{"type":"object","properties":{"referralId":{"type":"string"}},"required":["referralId"]},"OfferToCandidateInput":{"type":"object","properties":{"referralId":{"type":"string"},"candidateTenantId":{"type":"string"}},"required":["referralId","candidateTenantId"]},"OfferToCandidateBody":{"type":"object","properties":{"candidateTenantId":{"type":"string"}},"required":["candidateTenantId"]},"RequestExpansionInput":{"type":"object","properties":{"referralId":{"type":"string"}},"required":["referralId"]},"ApproveExpansionInput":{"type":"object","properties":{"referralId":{"type":"string"},"postToMarketplace":{"type":"boolean"}},"required":["referralId","postToMarketplace"]},"ApproveExpansionBody":{"type":"object","properties":{"postToMarketplace":{"type":"boolean"}},"required":["postToMarketplace"]},"DeclineExpansionInput":{"type":"object","properties":{"referralId":{"type":"string"}},"required":["referralId"]},"AcceptOfferInput":{"type":"object","properties":{"referralId":{"type":"string"}},"required":["referralId"]},"DeclineOfferInput":{"type":"object","properties":{"referralId":{"type":"string"}},"required":["referralId"]},"ClaimBookingInput":{"type":"object","properties":{"referralId":{"type":"string"}},"required":["referralId"]},"SubOutReferral":{"type":"object","properties":{"id":{"type":"string"},"sourceType":{"type":"string"},"sourceId":{"type":"string"},"bookingId":{"type":["string","null"]},"subOutState":{"type":["string","null"]},"subOutReason":{"type":["string","null"]},"status":{"type":"string"},"targetType":{"type":"string"},"targetId":{"type":"string"},"currentOfferId":{"type":["string","null"]},"offerExpiresAt":{"type":["string","null"],"format":"date-time"},"ownerApproval":{"type":["string","null"]}},"required":["id","sourceType","sourceId","bookingId","subOutState","subOutReason","status","targetType","targetId"]},"SubOutShortlistCandidate":{"type":"object","properties":{"tenantId":{"type":"string"},"displayName":{"type":"string"},"slug":{"type":"string"},"captainId":{"type":["string","null"]},"relationshipId":{"type":"string"},"priority":{"type":"number"},"breakdown":{"type":"object","properties":{"grossEarnings":{"type":"number"},"referralFee":{"type":"number"},"referralFeePercent":{"type":"number"},"platformFee":{"type":"number"},"platformFeeRate":{"type":"number"},"netEarnings":{"type":"number"}},"required":["grossEarnings","referralFee","referralFeePercent","platformFee","platformFeeRate","netEarnings"]}},"required":["tenantId","displayName","slug","captainId","relationshipId","priority","breakdown"]},"EmptyBody":{"type":"object","properties":{},"additionalProperties":{}},"SubOutOpportunity":{"type":"object","properties":{"referralId":{"type":"string"},"booking":{"type":"object","properties":{"id":{"type":"string"},"date":{"type":"string","format":"date-time"},"startTime":{"type":["string","null"]},"endTime":{"type":["string","null"]},"harbor":{}},"required":["id","date","startTime","endTime"]},"breakdown":{"type":"object","properties":{"grossEarnings":{"type":"number"},"platformFee":{"type":"number"},"platformFeeRate":{"type":"number"},"netEarnings":{"type":"number"}},"required":["grossEarnings","platformFee","platformFeeRate","netEarnings"]}},"required":["referralId","booking","breakdown"]},"WeatherForecast":{"type":"object","properties":{"forecast":{"type":"string"},"windSpeed":{"type":["number","null"]},"waveHeight":{"type":["number","null"]},"hasAdvisory":{"type":"boolean"},"advisoryType":{"type":["string","null"]}},"required":["forecast","hasAdvisory"]},"IdempotencyKeyHeader":{"type":"string","format":"uuid","description":"Client-supplied UUID v4. Repeat requests with the same key return the cached response."},"PaymentMutationResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"paymentId":{"type":"string"}},"required":["paymentId"]}},"required":["data"]},"SignUploadResponse":{"type":"object","properties":{"token":{"type":"string"},"blobUrl":{"type":"string","format":"uri"},"pathname":{"type":"string"}},"required":["token","blobUrl","pathname"]},"SignUpload":{"type":"object","properties":{"contentType":{"type":"string","pattern":"^image\\/(jpeg|png|heic|webp)$"},"sizeBytes":{"type":"integer","minimum":1,"maximum":52428800},"kind":{"type":"string","enum":["boat-photo","profile","gallery","booking-attachment"]},"refId":{"type":["string","null"]}},"required":["contentType","sizeBytes","kind"]},"BlobWebhookBody":{"type":"object","properties":{"type":{"type":"string","enum":["blob.generate-client-token","blob.upload-completed"],"description":"Vercel Blob event type. Set by the Vercel Blob service; not authored by clients of the Charter Captain API."}},"required":["type"],"additionalProperties":{}}},"responses":{"IdempotencyConflict":{"description":"Idempotency-Key was reused with a different request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdempotencyConflictError"}}}},"Unauthorized":{"description":"Authentication required, or the bearer token is missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Authenticated but not permitted to access this resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"The requested resource does not exist (existence is not leaked).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationError":{"description":"Request body or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":{"IdempotencyKeyHeader":{"schema":{"$ref":"#/components/schemas/IdempotencyKeyHeader"},"required":false,"description":"Client-supplied UUID v4. Repeat requests with the same key return the cached response.","name":"Idempotency-Key","in":"header"}}},"paths":{"/api/v1/customers":{"get":{"summary":"List customers with search","description":"Returns a paginated list of customers for the authenticated captain, with trip and spend summary fields.","operationId":"getCustomers","tags":["Customers"],"parameters":[{"name":"q","in":"query","description":"Search by name, email, or phone","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Page number (default 1)","required":false,"schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","description":"Page size (default 20, max 100)","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Paginated customer list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerSummary"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/customers/{id}":{"get":{"summary":"Customer detail","description":"Returns a single customer with full booking history, total spend, and trip count.","operationId":"getCustomerById","tags":["Customers"],"parameters":[{"name":"id","in":"path","description":"Customer ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Customer detail","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/CustomerSummary"},{"type":"object","properties":{"bookings":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}}}}]}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Customer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/customers/{id}/notes":{"patch":{"summary":"Update customer notes","description":"Updates the private notes field for a customer.","operationId":"patchCustomerNotes","tags":["Customers"],"parameters":[{"name":"id","in":"path","description":"Customer ID","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["notes"],"properties":{"notes":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated customer","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Customer"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Customer not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/rentals/{rentalId}/documents":{"get":{"summary":"Document status dashboard","description":"Returns the status of all documents (contract and waivers) for a rental.","operationId":"getRentalDocuments","tags":["Contracts"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"rentalId","in":"path","description":"Rental ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Document status","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DocumentStatus"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Rental not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/rentals/{rentalId}/coast-guard":{"get":{"summary":"Coast guard presentation view","description":"Returns a structured view of vessel, charter, and passenger info for coast guard inspection. Accessible to either the assigned captain or the boat owner of the rental.","operationId":"getRentalCoastGuard","tags":["Contracts"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"rentalId","in":"path","description":"Rental ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Coast guard view","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CoastGuardView"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Rental not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contracts/templates":{"get":{"summary":"List contract templates","description":"Returns all contract templates for the authenticated boat owner.","operationId":"getContractTemplates","tags":["Contracts"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Contract templates","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ContractTemplate"}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create contract template","description":"Creates a new contract template for the authenticated boat owner.","operationId":"postContractTemplate","tags":["Contracts"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","content"],"properties":{"name":{"type":"string"},"content":{"type":"string"}}}}}},"responses":{"201":{"description":"Contract template created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ContractTemplate"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/contracts/templates/{id}":{"patch":{"summary":"Update contract template","description":"Updates an existing contract template by ID.","operationId":"patchContractTemplate","tags":["Contracts"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","description":"Contract template ID","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"content":{"type":"string"},"isActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated contract template","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ContractTemplate"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Contract template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/waivers/templates":{"get":{"summary":"List waiver templates","description":"Returns all waiver templates for the authenticated boat owner.","operationId":"getWaiverTemplates","tags":["Waivers"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Waiver templates","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WaiverTemplate"}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create waiver template","description":"Creates a new waiver template for the authenticated boat owner.","operationId":"postWaiverTemplate","tags":["Waivers"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","content"],"properties":{"name":{"type":"string"},"content":{"type":"string"}}}}}},"responses":{"201":{"description":"Waiver template created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WaiverTemplate"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/waivers/templates/{id}":{"patch":{"summary":"Update waiver template","description":"Updates an existing waiver template by ID.","operationId":"patchWaiverTemplate","tags":["Waivers"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","description":"Waiver template ID","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"content":{"type":"string"},"isActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated waiver template","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WaiverTemplate"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Waiver template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/rentals/{rentalId}/guests":{"post":{"summary":"Add guests to rental","description":"Adds one or more guests to a rental for waiver tracking.","operationId":"postRentalGuests","tags":["Waivers"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"rentalId","in":"path","description":"Rental ID","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["guests"],"properties":{"guests":{"type":"array","items":{"type":"object","required":["firstName","lastName","email"],"properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"isCharterer":{"type":"boolean","default":false}}}}}}}}},"responses":{"201":{"description":"Guests added","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Guest"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Rental not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/rentals/{rentalId}/waivers/send":{"post":{"summary":"Send waivers to pending guests","description":"Sends waiver emails to all guests in PENDING status for the rental.","operationId":"postRentalWaiversSend","tags":["Waivers"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"rentalId","in":"path","description":"Rental ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Waivers sent","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"sent":{"type":"integer"},"guestIds":{"type":"array","items":{"type":"string"}}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Rental not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/rentals/{rentalId}/contract":{"get":{"summary":"View contract template and status","description":"Returns the contract template content and current signing status for the rental. Requires authentication; accessible only to the rental's operator (its owning captain or boat owner).","operationId":"getRentalContract","tags":["Contracts"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"rentalId","in":"path","description":"Rental ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Contract template and status","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"template":{"$ref":"#/components/schemas/ContractTemplate"},"signed":{"type":"boolean"},"signedAt":{"type":"string","format":"date-time","nullable":true}}}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Rental or contract not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/rentals/{rentalId}/contract/sign":{"post":{"summary":"Sign contract","description":"Records a customer's signature for the rental contract. On success, the server stamps the active `ContractTemplate` id plus the request's `userAgent` and `ipAddress` into `signatureData`. Public endpoint — no authentication required.","operationId":"postRentalContractSign","tags":["Contracts"],"security":[],"parameters":[{"name":"rentalId","in":"path","description":"Rental ID","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["signerName","signerEmail","signatureData"],"properties":{"signerName":{"type":"string","minLength":1},"signerEmail":{"type":"string","format":"email"},"signatureData":{"type":"object","required":["typedName","agreedAt"],"properties":{"typedName":{"type":"string","minLength":1,"description":"Customer's typed name as their signature."},"agreedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp of when the customer agreed."}},"additionalProperties":true}}}}}},"responses":{"201":{"description":"Contract signed.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SignedContract"}}}}}},"400":{"description":"Bad request — `VALIDATION_ERROR` if the request body is invalid, `ALREADY_SIGNED` if the rental contract has already been signed, or `NO_TEMPLATE` if the boat owner has no active contract template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Rental not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/rentals/{rentalId}/waiver/{guestId}":{"get":{"summary":"View waiver and guest info","description":"Returns the waiver template content and guest information for signing. Public endpoint — no authentication required.","operationId":"getRentalWaiver","tags":["Waivers"],"security":[],"parameters":[{"name":"rentalId","in":"path","description":"Rental ID","required":true,"schema":{"type":"string"}},{"name":"guestId","in":"path","description":"Guest ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Waiver template and guest info","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"template":{"$ref":"#/components/schemas/WaiverTemplate"},"guest":{"$ref":"#/components/schemas/Guest"}}}}}}}},"404":{"description":"Rental, guest, or waiver not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/rentals/{rentalId}/waiver/{guestId}/sign":{"post":{"summary":"Sign waiver","description":"Records a guest's signature for their waiver. On success, the server stamps `userAgent` and `ipAddress` into `signatureData`. Public endpoint — no authentication required.","operationId":"postRentalWaiverSign","tags":["Waivers"],"security":[],"parameters":[{"name":"rentalId","in":"path","description":"Rental ID","required":true,"schema":{"type":"string"}},{"name":"guestId","in":"path","description":"Guest ID","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["signatureData"],"properties":{"signatureData":{"type":"object","required":["typedName","agreedAt"],"properties":{"typedName":{"type":"string","minLength":1,"description":"Guest's typed name as their signature."},"agreedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp of when the guest agreed."}},"additionalProperties":true}}}}}},"responses":{"200":{"description":"Waiver signed.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Guest"}}}}}},"400":{"description":"Bad request — `VALIDATION_ERROR` if the request body is invalid, or `ALREADY_SIGNED` if the waiver has already been signed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Rental, guest, or waiver not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/harbors":{"get":{"summary":"List marketplace harbors","description":"Returns all active harbors available in the marketplace.","operationId":"getMarketplaceHarbors","tags":["Marketplace"],"security":[],"responses":{"200":{"description":"List of harbors","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Harbor"}}}}}}}}}},"/api/v1/marketplace/featured":{"get":{"summary":"Get featured marketplace listings","description":"Returns featured captains and boats for the marketplace homepage.","operationId":"getMarketplaceFeatured","tags":["Marketplace"],"security":[],"responses":{"200":{"description":"Featured captains and boats","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"captains":{"type":"array","items":{"$ref":"#/components/schemas/MarketplaceCaptain"}},"boats":{"type":"array","items":{"$ref":"#/components/schemas/MarketplaceBoat"}}}}}}}}}}}},"/api/v1/marketplace/captains":{"get":{"summary":"Search marketplace captains","description":"Returns a paginated list of captains matching search criteria.","operationId":"getMarketplaceCaptains","tags":["Marketplace"],"security":[],"parameters":[{"name":"harbor","in":"query","description":"Filter by harbor ID or name","required":false,"schema":{"type":"string"}},{"name":"date","in":"query","description":"Filter by available date (YYYY-MM-DD)","required":false,"schema":{"type":"string","format":"date"}},{"name":"duration","in":"query","description":"Filter by charter duration in hours","required":false,"schema":{"type":"integer"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"Paginated list of captains","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MarketplaceCaptain"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/captains/{slug}":{"get":{"summary":"Get captain profile","description":"Returns the full public profile for a captain by slug.","operationId":"getMarketplaceCaptainBySlug","tags":["Marketplace"],"security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Captain profile","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CaptainProfile"}}}}}},"404":{"description":"Captain not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/captains/{slug}/availability":{"get":{"summary":"Get captain availability","description":"Returns available dates and time slots for a marketplace captain.","operationId":"getMarketplaceCaptainAvailability","tags":["Marketplace"],"security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"daysAhead","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":365,"default":90}}],"responses":{"200":{"description":"Available dates","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","required":["date","pricingCategory","minRateDollars"],"properties":{"date":{"type":"string","format":"date"},"pricingCategory":{"type":"string","enum":["weekday","weekend","peak","holiday"]},"minRateDollars":{"type":"integer","nullable":true,"description":"Minimum hourly rate in whole dollars for the date, or null if no active pricing tier applies."}}}}}}}}},"404":{"description":"Captain not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/captains/{slug}/pricing":{"get":{"summary":"Get captain pricing","description":"Returns pricing tiers and rates for a marketplace captain.","operationId":"getMarketplaceCaptainPricing","tags":["Marketplace"],"security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Captain pricing","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PricingTier"}}}}}}},"404":{"description":"Captain not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/captains/{slug}/reviews":{"get":{"summary":"Get captain reviews","description":"Returns paginated reviews for a marketplace captain.","operationId":"getMarketplaceCaptainReviews","tags":["Marketplace"],"security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"Paginated reviews","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicReview"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"404":{"description":"Captain not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/boats":{"get":{"summary":"Search marketplace boats","description":"Returns a paginated list of rental boats matching search criteria.","operationId":"getMarketplaceBoats","tags":["Marketplace"],"security":[],"parameters":[{"name":"harbor","in":"query","description":"Filter by harbor ID or name","required":false,"schema":{"type":"string"}},{"name":"date","in":"query","description":"Filter by available date (YYYY-MM-DD)","required":false,"schema":{"type":"string","format":"date"}},{"name":"capacity","in":"query","description":"Minimum passenger capacity","required":false,"schema":{"type":"integer"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"Paginated list of boats","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MarketplaceBoat"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/boats/{boatId}":{"get":{"summary":"Get boat profile","description":"Returns the full public profile for a rental boat.","operationId":"getMarketplaceBoatById","tags":["Marketplace"],"security":[],"parameters":[{"name":"boatId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Boat profile","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BoatProfile"}}}}}},"404":{"description":"Boat not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/boats/{boatId}/availability":{"get":{"summary":"Get boat availability","description":"Returns available dates for a marketplace boat.","operationId":"getMarketplaceBoatAvailability","tags":["Marketplace"],"security":[],"parameters":[{"name":"boatId","in":"path","required":true,"schema":{"type":"string"}},{"name":"daysAhead","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":365,"default":90}}],"responses":{"200":{"description":"Available dates","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"string","format":"date"}}}}}}},"404":{"description":"Boat not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/boats/{boatId}/reviews":{"get":{"summary":"Get boat reviews","description":"Returns paginated reviews for a marketplace boat.","operationId":"getMarketplaceBoatReviews","tags":["Marketplace"],"security":[],"parameters":[{"name":"boatId","in":"path","required":true,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"Paginated reviews","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicReview"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"404":{"description":"Boat not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/bookings":{"post":{"summary":"Create charter booking","description":"Creates a new charter booking through the marketplace. Public endpoint — no authentication required.","operationId":"postMarketplaceBooking","tags":["Marketplace"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["captainSlug","date","durationHours","harborId","customer"],"properties":{"captainSlug":{"type":"string"},"date":{"type":"string","format":"date"},"startTime":{"type":"string","example":"08:00","nullable":true},"durationHours":{"type":"integer","minimum":1},"harborId":{"type":"string"},"customer":{"type":"object","required":["firstName","lastName","email"],"properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string","nullable":true}}},"notes":{"type":"string","nullable":true}}}}}},"responses":{"201":{"description":"Booking created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Booking"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Captain or harbor not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/marketplace/rentals/{rentalId}/review":{"post":{"summary":"Submit rental review","description":"Submits a review for a completed rental. Public endpoint — no authentication required.","operationId":"postMarketplaceRentalReview","tags":["Marketplace"],"security":[],"parameters":[{"name":"rentalId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rating","authorName"],"properties":{"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":"string","nullable":true},"authorName":{"type":"string"}}}}}},"responses":{"201":{"description":"Review submitted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Review"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Rental not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/favorites":{"get":{"summary":"List favorites","description":"Returns all favorites for the authenticated customer.","operationId":"getFavorites","tags":["Favorites"],"responses":{"200":{"description":"List of favorites","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Favorite"}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add favorite","description":"Adds a captain or boat to the authenticated customer's favorites.","operationId":"postFavorite","tags":["Favorites"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"captainId":{"type":"string","nullable":true},"boatId":{"type":"string","nullable":true}}}}}},"responses":{"201":{"description":"Favorite added","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Favorite"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/favorites/{id}":{"delete":{"summary":"Remove favorite","description":"Removes a favorite by ID for the authenticated customer.","operationId":"deleteFavoriteById","tags":["Favorites"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Favorite removed"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Favorite not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/favorites/check":{"get":{"summary":"Check if favorited","description":"Checks whether a given captain or boat is in the authenticated customer's favorites.","operationId":"getFavoritesCheck","tags":["Favorites"],"parameters":[{"name":"captainId","in":"query","required":false,"schema":{"type":"string"}},{"name":"boatId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Favorite check result","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["isFavorited"],"properties":{"isFavorited":{"type":"boolean"},"favoriteId":{"type":"string","nullable":true}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/openapi.json":{"get":{"summary":"OpenAPI 3.1 spec","description":"Returns this OpenAPI specification. Use with swift-openapi-generator to produce a typed Swift client.","operationId":"getOpenApiSpec","tags":["Meta"],"security":[],"responses":{"200":{"description":"OpenAPI spec","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/auth/me":{"get":{"operationId":"getAuthMe","summary":"Get current captain profile","description":"Returns the authenticated captain's profile and settings.","tags":["Auth"],"responses":{"200":{"description":"Captain profile","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Captain"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/auth/register-device":{"post":{"operationId":"postAuthRegisterDevice","summary":"Register push notification device token","description":"Registers or updates an iOS push notification device token for the authenticated captain.","tags":["Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1},"platform":{"type":"string","enum":["ios"],"default":"ios"}},"required":["token"]}}}},"responses":{"201":{"description":"Device token registered","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DeviceToken"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/auth/register-consumer-device":{"post":{"operationId":"postAuthRegisterConsumerDevice","summary":"Register consumer push notification device token","description":"Registers or updates an iOS push notification device token for the authenticated consumer (Clerk userId).","tags":["Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1},"platform":{"type":"string","enum":["ios"],"default":"ios"}},"required":["token"]}}}},"responses":{"201":{"description":"Device token registered","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DeviceToken"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/availability/dates":{"get":{"operationId":"getAvailabilityDates","summary":"Get available dates","description":"Returns available booking dates for the captain within the specified look-ahead window, with pricing category per date.","tags":["Availability"],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":365,"default":90,"description":"Number of days to look ahead (1–365, default 90)"},"required":false,"description":"Number of days to look ahead (1–365, default 90)","name":"daysAhead","in":"query"},{"schema":{"type":"string","description":"Comma-separated list of day-of-week numbers to filter (0=Sun … 6=Sat)","example":"5,6"},"required":false,"description":"Comma-separated list of day-of-week numbers to filter (0=Sun … 6=Sat)","name":"filterDays","in":"query"}],"responses":{"200":{"description":"Available dates with pricing categories","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date","example":"2026-07-04"},"pricingCategory":{"type":"string","enum":["weekday","weekend","peak","holiday"]}},"required":["date","pricingCategory"]}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/availability/slots":{"get":{"operationId":"getAvailabilitySlots","summary":"Get time slots for a date","description":"Returns the resolved bookable slots for a given date. A date is bookable when at least one slot is not taken and offers ≥1 duration.","tags":["Availability"],"parameters":[{"schema":{"type":"string","format":"date","description":"Date in YYYY-MM-DD format","example":"2026-07-04"},"required":true,"description":"Date in YYYY-MM-DD format","name":"date","in":"query"}],"responses":{"200":{"description":"Resolved slots for the date","content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string","format":"date","example":"2026-07-04"},"available":{"type":"boolean","description":"True when some slot is not taken and offers ≥1 duration"},"slots":{"type":"array","items":{"type":"object","properties":{"slotKey":{"type":"string","example":"morning"},"label":{"type":"string","example":"Morning"},"anchorTime":{"type":["string","null"],"description":"HH:mm anchor for the slot, or null","example":"08:00"},"anchorDirection":{"type":"string","enum":["START","END"],"description":"Whether anchorTime marks the slot's start or end"},"offeredDurations":{"type":"array","items":{"type":"object","properties":{"hours":{"type":"integer","example":4},"startTime":{"type":"string","example":"08:00"},"endTime":{"type":"string","example":"12:00"},"priceCents":{"type":"integer","example":60000}},"required":["hours","startTime","endTime","priceCents"]}},"defaultDurationHours":{"type":["integer","null"],"description":"Default duration to preselect, or null","example":4},"taken":{"type":"boolean","description":"True when the slot's window is occupied by a reservation"}},"required":["slotKey","label","anchorTime","anchorDirection","offeredDurations","defaultDurationHours","taken"]}}},"required":["date","available","slots"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/boats":{"get":{"operationId":"getBoats","summary":"List boats owned by the caller","description":"Returns boats where ownerId = caller. Default filter: isActive=true. Pass includeInactive=true to include retired boats.","tags":["Boats"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"includeInactive","in":"query"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"boats":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"description":{"type":["string","null"]},"capacity":{"type":"integer"},"lengthFeet":{"type":["integer","null"]},"year":{"type":["integer","null"]},"make":{"type":["string","null"]},"model":{"type":["string","null"]},"photos":{"type":"array","items":{"type":"string"}},"features":{"type":"array","items":{"type":"string"}},"rules":{"type":["string","null"]},"depositType":{"type":["string","null"],"enum":["FIXED","PERCENTAGE",null]},"depositValue":{"type":["integer","null"]},"fuelSurchargeEnabled":{"type":["boolean","null"]},"fuelSurchargeAmount":{"type":["integer","null"]},"minimumNoticeHours":{"type":"integer"},"harborId":{"type":"string"},"isActive":{"type":"boolean"},"isListed":{"type":"boolean"},"slipNumber":{"type":["string","null"]},"gateCode":{"type":["string","null"]},"slipInstructions":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","ownerId","name","type","description","capacity","lengthFeet","year","make","model","photos","features","rules","depositType","depositValue","fuelSurchargeEnabled","fuelSurchargeAmount","minimumNoticeHours","harborId","isActive","isListed","slipNumber","gateCode","slipInstructions","createdAt","updatedAt"]}}},"required":["boats"]}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}},"post":{"operationId":"postBoat","summary":"Create a new boat","description":"Creates a boat owned by the caller using the full Boat surface (name, type, harbor, capacity, slip info, fuel surcharge, deposit, photos). Pricing tiers are managed separately and are not created by this endpoint.","tags":["Boats"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"harborId":{"type":"string","minLength":1},"capacity":{"type":"integer","minimum":1,"maximum":50},"slipNumber":{"type":["string","null"],"maxLength":200},"gateCode":{"type":["string","null"],"maxLength":200},"slipInstructions":{"type":["string","null"],"maxLength":200},"fuelSurchargeEnabled":{"type":"boolean"},"fuelSurchargeAmount":{"type":["integer","null"],"minimum":0},"depositType":{"type":"string","enum":["FIXED","PERCENTAGE"]},"depositValue":{"type":"integer","minimum":0},"photos":{"type":"array","items":{"type":"string","format":"uri"},"default":[]}},"required":["name","type","harborId","capacity"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"boat":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"description":{"type":["string","null"]},"capacity":{"type":"integer"},"lengthFeet":{"type":["integer","null"]},"year":{"type":["integer","null"]},"make":{"type":["string","null"]},"model":{"type":["string","null"]},"photos":{"type":"array","items":{"type":"string"}},"features":{"type":"array","items":{"type":"string"}},"rules":{"type":["string","null"]},"depositType":{"type":["string","null"],"enum":["FIXED","PERCENTAGE",null]},"depositValue":{"type":["integer","null"]},"fuelSurchargeEnabled":{"type":["boolean","null"]},"fuelSurchargeAmount":{"type":["integer","null"]},"minimumNoticeHours":{"type":"integer"},"harborId":{"type":"string"},"isActive":{"type":"boolean"},"isListed":{"type":"boolean"},"slipNumber":{"type":["string","null"]},"gateCode":{"type":["string","null"]},"slipInstructions":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","ownerId","name","type","description","capacity","lengthFeet","year","make","model","photos","features","rules","depositType","depositValue","fuelSurchargeEnabled","fuelSurchargeAmount","minimumNoticeHours","harborId","isActive","isListed","slipNumber","gateCode","slipInstructions","createdAt","updatedAt"]}},"required":["boat"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}}},"/api/v1/boats/{id}":{"get":{"operationId":"getBoatById","summary":"Get a single boat","description":"Returns the boat when it is owned by the caller. Foreign / unknown ids return 404 (existence is not leaked).","tags":["Boats"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"boat":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"description":{"type":["string","null"]},"capacity":{"type":"integer"},"lengthFeet":{"type":["integer","null"]},"year":{"type":["integer","null"]},"make":{"type":["string","null"]},"model":{"type":["string","null"]},"photos":{"type":"array","items":{"type":"string"}},"features":{"type":"array","items":{"type":"string"}},"rules":{"type":["string","null"]},"depositType":{"type":["string","null"],"enum":["FIXED","PERCENTAGE",null]},"depositValue":{"type":["integer","null"]},"fuelSurchargeEnabled":{"type":["boolean","null"]},"fuelSurchargeAmount":{"type":["integer","null"]},"minimumNoticeHours":{"type":"integer"},"harborId":{"type":"string"},"isActive":{"type":"boolean"},"isListed":{"type":"boolean"},"slipNumber":{"type":["string","null"]},"gateCode":{"type":["string","null"]},"slipInstructions":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","ownerId","name","type","description","capacity","lengthFeet","year","make","model","photos","features","rules","depositType","depositValue","fuelSurchargeEnabled","fuelSurchargeAmount","minimumNoticeHours","harborId","isActive","isListed","slipNumber","gateCode","slipInstructions","createdAt","updatedAt"]}},"required":["boat"]}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"operationId":"patchBoat","summary":"Partially update a boat","description":"Updates only the fields present in the body. To retire a boat: PATCH { isActive: false }. To unlist from the marketplace: PATCH { isListed: false }. Per spec §4.4 this endpoint is not idempotent (PATCH semantics).","tags":["Boats"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"harborId":{"type":"string","minLength":1},"capacity":{"type":"integer","minimum":1,"maximum":50},"slipNumber":{"type":["string","null"],"maxLength":200},"gateCode":{"type":["string","null"],"maxLength":200},"slipInstructions":{"type":["string","null"],"maxLength":200},"fuelSurchargeEnabled":{"type":"boolean"},"fuelSurchargeAmount":{"type":["integer","null"],"minimum":0},"depositType":{"type":"string","enum":["FIXED","PERCENTAGE"]},"depositValue":{"type":"integer","minimum":0},"photos":{"type":"array","items":{"type":"string","format":"uri"},"default":[]},"isActive":{"type":"boolean"},"isListed":{"type":"boolean"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"boat":{"type":"object","properties":{"id":{"type":"string"},"ownerId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["SAILBOAT","POWERBOAT","PONTOON","JETSKI","KAYAK","CANOE","YACHT","OTHER"]},"description":{"type":["string","null"]},"capacity":{"type":"integer"},"lengthFeet":{"type":["integer","null"]},"year":{"type":["integer","null"]},"make":{"type":["string","null"]},"model":{"type":["string","null"]},"photos":{"type":"array","items":{"type":"string"}},"features":{"type":"array","items":{"type":"string"}},"rules":{"type":["string","null"]},"depositType":{"type":["string","null"],"enum":["FIXED","PERCENTAGE",null]},"depositValue":{"type":["integer","null"]},"fuelSurchargeEnabled":{"type":["boolean","null"]},"fuelSurchargeAmount":{"type":["integer","null"]},"minimumNoticeHours":{"type":"integer"},"harborId":{"type":"string"},"isActive":{"type":"boolean"},"isListed":{"type":"boolean"},"slipNumber":{"type":["string","null"]},"gateCode":{"type":["string","null"]},"slipInstructions":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","ownerId","name","type","description","capacity","lengthFeet","year","make","model","photos","features","rules","depositType","depositValue","fuelSurchargeEnabled","fuelSurchargeAmount","minimumNoticeHours","harborId","isActive","isListed","slipNumber","gateCode","slipInstructions","createdAt","updatedAt"]}},"required":["boat"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/bookings":{"post":{"operationId":"postBooking","summary":"Create a manual booking (captain)","tags":["Bookings"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string","minLength":1},"startTime":{"type":"string","minLength":1},"endTime":{"type":"string","minLength":1},"durationId":{"type":"string"},"harborId":{"type":"string","minLength":1},"pricingTierId":{"type":"string","minLength":1},"firstName":{"type":"string","default":""},"lastName":{"type":"string","default":""},"email":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","format":"email"}],"default":""},"phone":{"type":"string","default":""},"partySize":{"type":"integer","minimum":1,"maximum":13},"specialRequests":{"type":"string","default":""},"slipNumber":{"type":["string","null"],"maxLength":200},"gateCode":{"type":["string","null"],"maxLength":200},"slipInstructions":{"type":["string","null"],"maxLength":200},"status":{"type":"string","enum":["CONFIRMED","DEPOSIT_PAID","BALANCE_PAID","COMPLETED"]},"totalPriceOverride":{"type":"integer","minimum":0},"depositOverride":{"type":"integer","minimum":0},"balanceOverride":{"type":"integer","minimum":0},"tipAmount":{"type":"integer","minimum":0},"payments":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["DEPOSIT","BALANCE","TIP"]},"amount":{"type":"integer","minimum":0},"note":{"type":"string","default":""}},"required":["type","amount"]},"default":[]}},"required":["date","startTime","endTime","harborId","pricingTierId","partySize","status"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"bookingId":{"type":"string"}},"required":["bookingId"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}},"get":{"operationId":"getBookings","summary":"List bookings with filters","description":"Returns a paginated list of bookings, optionally filtered by status, search query, and date range.","tags":["Bookings"],"parameters":[{"name":"status","in":"query","description":"Filter by booking status","required":false,"schema":{"type":"string","enum":["PENDING_DEPOSIT","DEPOSIT_PAID","CONFIRMED","COMPLETED","CANCELLED","WEATHER_CANCELLED"]}},{"name":"search","in":"query","description":"Search by customer name or email","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Filter bookings on or after this date (YYYY-MM-DD)","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"Filter bookings on or before this date (YYYY-MM-DD)","required":false,"schema":{"type":"string","format":"date"}},{"name":"page","in":"query","description":"Page number (default 1)","required":false,"schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","description":"Page size (default 20, max 100)","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Paginated booking list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/bookings/today":{"get":{"operationId":"getBookingsToday","summary":"Today's bookings","description":"Returns all active bookings for today (excludes CANCELLED, WEATHER_CANCELLED, PENDING_DEPOSIT).","tags":["Bookings"],"responses":{"200":{"description":"Today's bookings","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/bookings/upcoming":{"get":{"operationId":"getBookingsUpcoming","summary":"Upcoming bookings (7 days)","description":"Returns active bookings for the next 7 days starting from tomorrow, ordered by date ascending.","tags":["Bookings"],"responses":{"200":{"description":"Upcoming bookings","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BookingSummary"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/bookings/{id}":{"get":{"operationId":"getBookingById","summary":"Get booking detail","description":"Returns a single booking with full related data.","tags":["Bookings"],"parameters":[{"name":"id","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Booking detail","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Booking"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/bookings/{id}/status":{"patch":{"operationId":"patchBookingStatus","summary":"Update booking status","description":"Updates the status of a booking.","tags":["Bookings"],"parameters":[{"name":"id","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["PENDING_DEPOSIT","DEPOSIT_PAID","CONFIRMED","COMPLETED","CANCELLED","WEATHER_CANCELLED"]}}}}}},"responses":{"200":{"description":"Updated booking","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Booking"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/bookings/{id}/send-checkout":{"post":{"operationId":"postBookingSendCheckout","summary":"Send checkout link","description":"Sends a checkout link to the customer for a booking in DEPOSIT_PAID or CONFIRMED status.","tags":["Bookings"],"parameters":[{"name":"id","in":"path","description":"Booking ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Checkout link sent","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"sent":{"type":"boolean"},"bookingId":{"type":"string"}}}}}}}},"400":{"description":"Booking not eligible for checkout","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/calendar/{year}/{month}":{"get":{"operationId":"getCalendarMonth","summary":"Calendar month view","description":"Returns bookings and availability exceptions for a given calendar month.","tags":["Calendar"],"parameters":[{"name":"year","in":"path","description":"4-digit year","required":true,"schema":{"type":"integer","example":2026}},{"name":"month","in":"path","description":"Month number (1–12)","required":true,"schema":{"type":"integer","minimum":1,"maximum":12,"example":7}}],"responses":{"200":{"description":"Calendar month data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"bookings":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}},"exceptions":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityException"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/calendar/blocks":{"post":{"operationId":"postCalendarBlock","summary":"Create availability block","description":"Blocks or overrides availability for a specific date.","tags":["Calendar"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["date"],"properties":{"date":{"type":"string","format":"date","example":"2026-07-04"},"isBlocked":{"type":"boolean","default":true},"reason":{"type":"string"},"overrideStart":{"type":"string","example":"10:00"},"overrideEnd":{"type":"string","example":"16:00"}}}}}},"responses":{"201":{"description":"Availability block created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AvailabilityException"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/calendar/blocks/{id}":{"delete":{"operationId":"deleteCalendarBlock","summary":"Delete availability block","description":"Removes an availability exception (block) by ID.","tags":["Calendar"],"parameters":[{"name":"id","in":"path","description":"Availability exception ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Block deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/customers/me/gigs/{id}":{"get":{"operationId":"getCustomerGig","summary":"Get customer gig detail","description":"Returns full gig detail with bids for the authenticated customer who owns the gig.","tags":["Customer Gigs"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Gig detail","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CustomerGigDetail"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"operationId":"patchCustomerGig","summary":"Edit customer gig","description":"Edits gig content while the gig is pending verification or open. Slot flags lock once non-withdrawn bids exist.","tags":["Customer Gigs"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1},"description":{"type":"string"},"harbor":{"type":"string","minLength":1},"boatTypes":{"type":"array","items":{"type":"string","enum":["POWERBOAT","SAILBOAT","YACHT","PONTOON","FISHING"]},"default":[]},"dateMode":{"type":"string","enum":["SPECIFIC","FLEXIBLE"],"default":"SPECIFIC"},"dates":{"type":"array","items":{"type":["string","null"],"format":"date-time"},"default":[]},"flexibleWindowStart":{"type":["string","null"],"format":"date-time"},"flexibleWindowEnd":{"type":["string","null"],"format":"date-time"},"duration":{"type":"string","minLength":1},"startTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"timeWindow":{"type":"string","enum":["MORNING","AFTERNOON","SUNSET","EVENING","FLEXIBLE"]},"compensationCents":{"type":"integer","exclusiveMinimum":0},"occasion":{"type":"string","enum":["corporate-outings","bachelorette-party","proposal","milestone-birthday","general"]},"partySize":{"type":"integer","minimum":1,"maximum":150},"needsCaptain":{"type":"boolean"},"needsBoat":{"type":"boolean"}},"required":["title","harbor","duration","needsCaptain","needsBoat"]}}}},"responses":{"200":{"description":"Gig updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/customers/me/gigs/{id}/bids/{bidId}/accept":{"post":{"operationId":"postCustomerGigBidAccept","summary":"Accept a bid on customer gig","description":"Accepts a single submitted bid on an open gig owned by the authenticated customer.","tags":["Customer Gigs"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"bidId","in":"path"}],"responses":{"200":{"description":"Bid accepted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/customers/me/gigs/{id}/bids/accept":{"post":{"operationId":"postCustomerGigBidsAccept","summary":"Accept captain and boat bids","description":"Atomically accepts captain and/or boat bids on a two-slot open gig owned by the authenticated customer.","tags":["Customer Gigs"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"captainBidId":{"type":["string","null"]},"boatBidId":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"Bids accepted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/dashboard":{"get":{"operationId":"getDashboard","summary":"Composite dashboard data","description":"Returns today's charters, revenue stats, upcoming bookings, and optional weather in a single request.","tags":["Dashboard"],"responses":{"200":{"description":"Dashboard data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"todaysCharters":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}},"weather":{"allOf":[{"$ref":"#/components/schemas/WeatherForecast"}],"nullable":true},"stats":{"type":"object","properties":{"weeklyRevenue":{"type":"number"},"monthlyRevenue":{"type":"number"},"bookingsThisMonth":{"type":"integer"},"avgCharterValue":{"type":"number"}}},"upcoming":{"type":"array","items":{"$ref":"#/components/schemas/BookingSummary"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/harbors":{"get":{"operationId":"getHarbors","summary":"List active harbors","description":"Returns all active harbors sorted by sortOrder.","tags":["Harbors"],"responses":{"200":{"description":"List of harbors","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Harbor"}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/marketplace/bookings/{bookingId}/deposit-payment-intent":{"post":{"operationId":"postMarketplaceBookingDepositPaymentIntent","summary":"Create Stripe PaymentIntent for booking deposit","description":"Creates a Stripe PaymentIntent on the captain's Connect account for an existing marketplace booking in PENDING_DEPOSIT status. Public endpoint — no authentication required.","tags":["Marketplace"],"security":[],"parameters":[{"schema":{"type":"string"},"required":true,"name":"bookingId","in":"path"}],"responses":{"201":{"description":"PaymentIntent created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"clientSecret":{"type":"string"},"stripeAccountId":{"type":"string"}},"required":["clientSecret","stripeAccountId"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/marketplace/rentals/{rentalId}/deposit-payment-intent":{"post":{"operationId":"postMarketplaceRentalDepositPaymentIntent","summary":"Create Stripe PaymentIntent for rental deposit","description":"Creates a Stripe PaymentIntent on the boat owner's Connect account for an existing marketplace rental in PENDING_DEPOSIT status. Public endpoint — no authentication required.","tags":["Marketplace"],"security":[],"parameters":[{"schema":{"type":"string"},"required":true,"name":"rentalId","in":"path"}],"responses":{"201":{"description":"PaymentIntent created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"clientSecret":{"type":"string"},"stripeAccountId":{"type":"string"}},"required":["clientSecret","stripeAccountId"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/notifications":{"get":{"operationId":"getNotifications","summary":"List notifications (paginated)","description":"Returns a paginated list of notifications for the authenticated captain, ordered by most recent first.","tags":["Notifications"],"parameters":[{"schema":{"type":"integer","minimum":1,"default":1,"description":"Page number (default 1)"},"required":false,"description":"Page number (default 1)","name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":20,"description":"Items per page (1–50, default 20)"},"required":false,"description":"Items per page (1–50, default 20)","name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated notification list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/notifications/preferences":{"patch":{"operationId":"patchNotificationPreferences","summary":"Update notification preferences","description":"Merges the provided push notification preferences with the captain's existing settings.","tags":["Notifications"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferences"}}}},"responses":{"200":{"description":"Updated preferences","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/NotificationPreferences"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/notifications/test":{"post":{"operationId":"postNotificationsTest","summary":"Send a test push notification","description":"Sends a test push notification to all active devices for the authenticated captain. Returns delivery counts.","tags":["Notifications"],"responses":{"200":{"description":"Push delivery result","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PushTestResult"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/bookings/{id}/payments":{"post":{"operationId":"postBookingPayment","summary":"Record a payment on a booking (captain)","tags":["Payments"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["DEPOSIT","BALANCE","TIP"]},"amount":{"type":"integer","minimum":0},"method":{"type":"string","enum":["CASH","CHECK","CONNECTED_CARD"]},"note":{"type":"string","default":""},"connectedPaymentMethodId":{"type":["string","null"]},"source":{"type":["string","null"],"enum":["CASH","CHECK","VENMO","ZELLE","PAYPAL","OTHER",null]}},"required":["type","amount","method"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMutationResponse"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}}},"/api/v1/payments/{id}/refund":{"post":{"operationId":"postPaymentRefund","summary":"Refund a payment (captain)","tags":["Payments"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"integer","minimum":1},"reason":{"type":"string","enum":["REQUESTED_BY_CUSTOMER","DUPLICATE","FRAUDULENT","OTHER"]},"note":{"type":"string","default":""}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMutationResponse"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/rentals/{rentalId}/payments":{"post":{"operationId":"postRentalPayment","summary":"Record a payment on a rental (boat owner)","tags":["Rental Payments"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"rentalId","in":"path"},{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["RENTAL_DEPOSIT","RENTAL_BALANCE","SECURITY_AUTHORIZATION","SECURITY_CAPTURE"]},"amount":{"type":"integer","minimum":0},"method":{"type":"string","enum":["CASH","CHECK","CONNECTED_CARD"]},"note":{"type":"string","default":""},"connectedPaymentMethodId":{"type":["string","null"]},"source":{"type":["string","null"],"enum":["CASH","CHECK","VENMO","ZELLE","PAYPAL","OTHER",null]}},"required":["type","amount","method"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"paymentId":{"type":"string"}},"required":["paymentId"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}}},"/api/v1/rental-payments/{id}/refund":{"post":{"operationId":"postRentalPaymentRefund","summary":"Refund a rental payment (boat owner)","tags":["Rental Payments"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"integer","minimum":1},"reason":{"type":"string","enum":["REQUESTED_BY_CUSTOMER","DUPLICATE","FRAUDULENT","OTHER"]},"note":{"type":"string","default":""}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"paymentId":{"type":"string"}},"required":["paymentId"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/rentals":{"post":{"operationId":"postRental","summary":"Create a manual rental (boat owner)","tags":["Rentals"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"boatId":{"type":"string","minLength":1},"date":{"type":"string","minLength":1},"startTime":{"type":"string","minLength":1},"endTime":{"type":"string","default":""},"durationHours":{"type":"number","exclusiveMinimum":0,"maximum":24},"pricingTierId":{"type":"string","minLength":1},"firstName":{"type":"string","default":""},"lastName":{"type":"string","default":""},"email":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","format":"email"}],"default":""},"phone":{"type":"string","default":""},"partySize":{"type":"integer","minimum":1,"maximum":150},"specialRequests":{"type":"string","default":""},"status":{"type":"string","enum":["CONFIRMED","DEPOSIT_PAID","BALANCE_PAID","COMPLETED"]},"totalPriceOverride":{"type":"integer","minimum":0},"depositOverride":{"type":"integer","minimum":0},"balanceOverride":{"type":"integer","minimum":0},"payments":{"type":"array","items":{"type":"object","properties":{"amount":{"type":"integer","minimum":0},"type":{"type":"string","enum":["RENTAL_DEPOSIT","RENTAL_BALANCE","SECURITY_AUTHORIZATION","SECURITY_CAPTURE","SECURITY_RELEASE","RENTAL_REFUND"]},"note":{"type":"string","default":""}},"required":["amount","type"]},"default":[]}},"required":["boatId","date","startTime","pricingTierId","partySize","status"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"rentalId":{"type":"string"}},"required":["rentalId"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}}},"/api/v1/revenue/stats":{"get":{"operationId":"getRevenueStats","summary":"Revenue analytics","description":"Returns revenue totals, booking counts, weekly breakdown, harbor distribution, and day-of-week distribution for the specified period.","tags":["Revenue"],"parameters":[{"schema":{"type":"string","enum":["this-month","last-30","this-season","this-year","all-time"],"default":"this-month","description":"Time period for analytics"},"required":false,"description":"Time period for analytics","name":"period","in":"query"}],"responses":{"200":{"description":"Revenue stats","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"totalRevenue":{"type":"number"},"prevRevenue":{"type":"number"},"bookingCount":{"type":"integer"},"prevBookingCount":{"type":"integer"},"avgCharterValue":{"type":"number"},"avgTipAmount":{"type":"number"},"weeklyRevenue":{"type":"array","items":{"type":"object","properties":{"week":{"type":"string","format":"date"},"revenue":{"type":"number"},"bookings":{"type":"integer"}}}},"harborData":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"count":{"type":"integer"}}}},"dayData":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","example":"Mon"},"count":{"type":"integer"}}}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/settings":{"get":{"operationId":"getSettings","summary":"All captain settings","description":"Returns all settings for the captain: profile, availability rules, exceptions, pricing tiers, harbors, and durations.","tags":["Settings"],"responses":{"200":{"description":"Captain settings bundle","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"captain":{"$ref":"#/components/schemas/Captain"},"availabilityRules":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityRule"}},"availabilityExceptions":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityException"}},"pricingTiers":{"type":"array","items":{"$ref":"#/components/schemas/PricingTier"}},"harbors":{"type":"array","items":{"$ref":"#/components/schemas/Harbor"}},"durations":{"type":"array","items":{"$ref":"#/components/schemas/Duration"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/settings/availability":{"put":{"operationId":"putSettingsAvailability","summary":"Update availability rules","description":"Updates startTime, endTime, and/or isActive for one or more availability rules.","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"startTime":{"type":"string","example":"08:00"},"endTime":{"type":"string","example":"18:00"},"isActive":{"type":"boolean"}},"required":["id"]}}},"required":["rules"]}}}},"responses":{"200":{"description":"Updated availability rules","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityRule"}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/settings/durations":{"put":{"operationId":"putSettingsDurations","summary":"Update durations","description":"Updates hours, label, and/or isActive for one or more duration options.","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"durations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"hours":{"type":"integer"},"label":{"type":"string"},"isActive":{"type":"boolean"}},"required":["id"]}}},"required":["durations"]}}}},"responses":{"200":{"description":"Updated durations","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Duration"}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/settings/pricing":{"put":{"operationId":"putSettingsPricing","summary":"Update pricing tiers","description":"Updates name, ratePerHour, and/or isActive for one or more pricing tiers.","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tiers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ratePerHour":{"type":"number"},"isActive":{"type":"boolean"}},"required":["id"]}}},"required":["tiers"]}}}},"responses":{"200":{"description":"Updated pricing tiers","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PricingTier"}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/bookings/{id}/sub-out":{"post":{"operationId":"postBookingSubOut","summary":"Request sub-out for a booking","description":"Originating captain requests to sub out a booking they own. Creates a Referral in REQUESTED state.","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubOutRequestBody"}}}},"responses":{"201":{"description":"Referral created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SubOutReferral"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}}},"/api/v1/sub-out/{referralId}/shortlist":{"get":{"operationId":"getSubOutShortlist","summary":"Get shortlist candidates","description":"Returns the originating captain's preferred shortlist candidates for a referral, with a financial breakdown per candidate.","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"referralId","in":"path"}],"responses":{"200":{"description":"List of shortlist candidates","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SubOutShortlistCandidate"}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/sub-out/{referralId}/offer":{"post":{"operationId":"postSubOutOffer","summary":"Offer referral to a candidate","description":"Originating captain offers the sub-out referral to a specific candidate tenant from the shortlist. Per spec §10.9, candidateTenantId is the candidate captain's tenant identity, not their captain id.","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"referralId","in":"path"},{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferToCandidateBody"}}}},"responses":{"200":{"description":"Offer recorded","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SubOutReferral"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"412":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sub-out/{referralId}/expansion/request":{"post":{"operationId":"postSubOutExpansionRequest","summary":"Request shortlist expansion","description":"Originating captain asks the boat owner to approve expanding the shortlist beyond the captain's preferred candidates.","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"referralId","in":"path"}],"responses":{"200":{"description":"Expansion requested","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SubOutReferral"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/sub-out/{referralId}/expansion/approve":{"post":{"operationId":"postSubOutExpansionApprove","summary":"Approve shortlist expansion","description":"Boat owner approves the captain's request to expand the shortlist. Authorization is on tenantId (per spec §10.9).","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"referralId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveExpansionBody"}}}},"responses":{"200":{"description":"Expansion approved","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SubOutReferral"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sub-out/{referralId}/expansion/decline":{"post":{"operationId":"postSubOutExpansionDecline","summary":"Decline shortlist expansion","description":"Boat owner declines the captain's request to expand the shortlist. Transitions referral to FAILED.","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"referralId","in":"path"}],"responses":{"200":{"description":"Expansion declined","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SubOutReferral"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sub-out/opportunities":{"get":{"operationId":"getSubOutOpportunities","summary":"List sub-out claim opportunities","description":"Candidate captain lists OWNER_APPROVED sub-out referrals available to claim. Filtered by marketplace opt-in, Stripe Connect onboarding, and source-tenant blacklist. Includes per-row financial breakdown.","tags":["sub-out"],"responses":{"200":{"description":"Available opportunities","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SubOutOpportunity"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/api/v1/sub-out/{referralId}/accept":{"post":{"operationId":"postSubOutAccept","summary":"Accept a sub-out offer","description":"Candidate captain accepts a sub-out referral offered to their tenant. Atomic transition to SHORTLIST_ACCEPTED with booking ownership reassignment. Body is empty `{}`. Per spec §10.9, the actor identity is Tenant, resolved from the authenticated captain id.","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"referralId","in":"path"},{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmptyBody"}}}},"responses":{"200":{"description":"Offer accepted","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SubOutReferral"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}}},"/api/v1/sub-out/{referralId}/decline":{"post":{"operationId":"postSubOutDecline","summary":"Decline a sub-out offer","description":"Candidate captain declines a sub-out referral offered to their tenant. Resets the referral to REQUESTED so the originating captain can offer again. Optional `reason` field is accepted but not currently persisted.","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"referralId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Offer declined","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SubOutReferral"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"412":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sub-out/bookings/{bookingId}/claim":{"post":{"operationId":"postSubOutClaim","summary":"Claim a marketplace-expanded sub-out booking","description":"Candidate captain claims an OWNER_APPROVED sub-out referral that was posted to the marketplace expansion. URL is keyed on bookingId for friendlier iOS UX; the route resolves the corresponding referral. Atomic guard rejects races with 409.","tags":["sub-out"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"bookingId","in":"path"},{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmptyBody"}}}},"responses":{"200":{"description":"Booking claimed","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SubOutReferral"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}}},"/api/v1/uploads/sign":{"post":{"operationId":"postUploadSign","summary":"Mint a Vercel Blob client token for a direct image upload","description":"Returns a short-lived token (1h) the iOS client uses with @vercel/blob to upload a single image. Bypasses Vercel's 4.5MB function body limit. The completion webhook fires at /api/v1/uploads/complete and persists the URL to the appropriate Prisma model based on `kind`.","tags":["Uploads"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"idempotency-key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignUpload"}}}},"responses":{"201":{"description":"Client token minted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SignUploadResponse"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyConflict"}}}},"/api/v1/uploads/complete":{"post":{"operationId":"postUploadComplete","summary":"Vercel Blob upload-completed webhook","description":"Called by Vercel Blob after a client upload finishes. Signature verification is delegated to @vercel/blob/client.handleUpload. iOS clients do NOT call this endpoint — it's documented here for completeness only.","tags":["Uploads"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlobWebhookBody"}}}},"responses":{"200":{"description":"Webhook handled"},"400":{"description":"Invalid body, malformed clientPayload, or persistence failure"}}}},"/api/v1/weather/forecast":{"get":{"operationId":"getWeatherForecast","summary":"Weather forecast for harbor","description":"Returns the current NOAA weather forecast for a harbor's coordinates.","tags":["Weather"],"parameters":[{"schema":{"type":"string","description":"Harbor ID to fetch forecast for"},"required":true,"description":"Harbor ID to fetch forecast for","name":"harborId","in":"query"}],"responses":{"200":{"description":"Weather forecast","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WeatherForecast"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}