# RentalTide Documentation — Full Content > Auto-generated single-file mirror of every page on docs.rentaltide.com. > Intended for LLM ingestion. For a structured site index see /llms.txt. > Generated: 2026-05-18T20:18:22.833Z --- # RentalTide Documentation Source: https://docs.rentaltide.com/ > Everything you need to manage your watercraft rental and marina business # RentalTide Documentation Welcome to the RentalTide documentation. Here you will find everything you need to set up, run, and grow your watercraft rental or marina business. Whether you are configuring your first location or integrating with the API, these guides walk you through every feature step by step. {% callout type="tip" title="New to RentalTide?" %} Start with the [quick start guide](/getting-started/) to get your first location accepting bookings in under 15 minutes. {% /callout %} --- ## Explore the platform {% cardGroup cols=3 %} {% card title="Core" href="/core/" /%} {% card title="Operations" href="/operations/" /%} {% card title="Point of Sale" href="/pos/" /%} {% card title="Fleet & Business" href="/fleet/" /%} {% card title="Staffing" href="/staffing/" /%} {% card title="Marina" href="/marina/" /%} {% card title="Marketing" href="/marketing/" /%} {% card title="Reporting" href="/reporting/" /%} {% card title="Admin" href="/admin/" /%} {% card title="Customer-Facing" href="/public/" /%} {% card title="API Reference" href="/api-reference/" /%} {% /cardGroup %} --- ## Platform highlights **All-in-one operations.** Manage bookings, walk-ups, manifests, and real-time asset tracking from a single dashboard. **Built-in point of sale.** Ring up retail, snack bar, and rental transactions with integrated Stripe terminals. **Marina management.** Assign slips, track contracts, handle haul services, and bill for meter readings. **Embeddable booking widget.** Drop a booking experience directly onto your website with one script tag. **Reporting and ledger.** Every dollar flows through a double-entry general ledger with real-time financial reports. **API-first.** Everything the dashboard can do is available through the REST API for custom integrations. --- # Admin Source: https://docs.rentaltide.com/admin/ > Staff management, integrations, subscription, storage, data import, and partner access Account-level settings and tools for managing your team, integrations, billing, and data. {% cardGroup cols=2 %} {% card title="Staff management" href="/admin/staff/" /%} {% card title="App store" href="/admin/app-store/" /%} {% card title="Subscription" href="/admin/subscription/" /%} {% card title="Storage" href="/admin/storage/" /%} {% card title="Import" href="/admin/import/" /%} {% card title="Partner access" href="/admin/partner-access/" /%} {% /cardGroup %} --- # App store Source: https://docs.rentaltide.com/admin/app-store/ > Browse, install, and manage integrations across analytics, accounting, marketing, security, and developer tools The app store is your central hub for connecting RentalTide to third-party services and enabling add-on products. Browse available integrations, install them in a few clicks, and manage everything from one place. Navigate to **Admin > App Store** in the sidebar. ## Page layout The app store has two main views: - **My Apps** -- shows all currently installed integrations with their connection status, setup date, and quick-access configuration buttons. - **Discover** -- browse all available integrations organized by category, with search and filtering. ## Add-on products RentalTide offers modular product add-ons that unlock entire feature sets when enabled: | Product | Features unlocked | | ---------------- | ----------------------------------------------------------------------------------------------------------- | | Staffing | Staff scheduling, timesheets, clock in/out, shift swaps, time-off management, GPS tracking, staff portal | | Point of Sale | Product catalog, transaction processing, till closing, invoicing, inventory audit, stock transfers | | Slips and Marina | Slip inventory, reservations, waitlist, guest access, haul services, contracts, meter readings, marina maps | Each product controls which sidebar items are visible. Disabling a product hides the corresponding navigation items for all users. ## Integration categories | Category | Examples | | ------------------------- | ------------------------------------------------------------------- | | Website | Hosted Website builder, WordPress Plugin | | Analytics | Google Tags (GTM/GA4), AI Business Audit | | Accounting | Xero, QuickBooks, Wave Accounting | | Marketing | Meta Conversion API, TikTok Ads Pixel, Mailchimp | | Reviews and Reputation | Google Reviews, TripAdvisor Reviews | | Communication | WhatsApp Business | | Calendar | Google Calendar | | Security and Verification | Stripe Identity, Solink video surveillance | | Inspection and AI | AI Receipt Validation | | Automation | Webhooks, Zapier | | Developer Tools | API Key, Custom Code Block | | OTAs and Marketplaces | Viator, Google Things to Do, LetsBatch, WetRentals | | Navigation and Safety | Nautras navigation | | Insurance | Buoy Insurance | | E-commerce | Gift Cards, Memberships, Loyalty, Referral Program | | Operations | Self-Service Check-In, VQuip fleet inspection, HighKey social media | | CRM | SignlOS customer engagement | ## Installing an integration 1. Navigate to the **Discover** tab or use the search bar to find an integration. 2. Click the app card to view its details, features, pricing, and required permissions. 3. Click **Install** or **Connect**. 4. Complete the authorization flow -- this varies by integration: - **OAuth integrations** (Google Calendar, Xero, QuickBooks, Wave) redirect you to the provider to authorize access. - **API key integrations** (Meta Conversion API, TikTok Pixel, WhatsApp) require you to enter credentials from the provider's dashboard. - **One-click integrations** (Google Tags, Webhooks, Custom Code) are configured directly in RentalTide. 5. The app appears in **My Apps** once connected. ## Pricing Integrations have different pricing models: | Pricing type | Examples | | ------------ | ------------------------------------------------------------------------------------ | | Free | Google Calendar, Google Tags, WordPress, Webhooks, Meta Conversion API, TikTok Pixel | | Per-use | AI Receipt Validation ($0.10/receipt), Stripe Identity ($2/verification) | | One-time | AI Business Audit ($4.99) | | Monthly | Solink (from $150/month) | | Per-booking | Buoy Insurance (from $2.99/booking) | ## API keys The API Key integration lets you access RentalTide data programmatically. Generate keys from the API Key app card. Keys are scoped to your account and rate-limited. Full API documentation is available at `https://public.api.rentaltide.com/`. ## Custom code The Custom Code Block integration lets you inject custom HTML, CSS, and JavaScript into your booking pages: - **Header code** -- injected into the page `
` (useful for analytics scripts, chat widgets) - **Footer code** -- injected before the closing `` tag - **Custom CSS** -- additional stylesheets applied to your booking pages ## Webhooks Configure HTTP webhook endpoints to receive real-time notifications for events including: - Booking created, updated, or cancelled - Calendar sync updates Webhooks send POST requests with JSON payloads to your specified URL. ## Accounting integrations Xero, QuickBooks, and Wave integrations support: - Automatic transaction sync and daily sales invoice creation - GL code mapping between RentalTide ledger codes and your chart of accounts - Bank account integration - OAuth token management (tokens refresh automatically; Xero tokens expire after 60 days of inactivity) {% callout type="tip" %} All OAuth integrations use secure authorization -- RentalTide never stores your third-party passwords. Review the permissions each app requests before installing. Uninstall apps you no longer use to keep your account clean. Some integrations (like Google Calendar) are per-operator, meaning each staff member can connect their own calendar. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **App not connecting** -- Try uninstalling and reinstalling the integration. Make sure you complete the full authorization flow in the popup window. **OAuth token expired** -- For Xero and QuickBooks, reconnect from the My Apps view. Tokens expire if not refreshed within the provider's window (60 days for Xero). **App installed but not working** -- Some integrations require additional configuration after installation (API keys, webhook URLs, GL code mapping). Check the app's settings panel in My Apps. **Webhook not firing** -- Verify the endpoint URL is correct and publicly accessible. Check that the events you want are enabled in the webhook configuration. {% /callout %} --- # Import Source: https://docs.rentaltide.com/admin/import/ > Import data from FareHarbor, Rezdy, or CSV files using a guided six-step wizard The import tool walks you through migrating data from another platform into RentalTide with a six-step wizard. Navigate to **Admin > Import** in the sidebar. ## Supported sources | Source | Connection method | Data types | | ------------------ | ------------------ | ------------------------------------------ | | FareHarbor | CSV file upload | Bookings, customers, gift cards, inventory | | Rezdy | API key connection | Bookings, customers, products, vouchers | | PeekPro | Coming soon | -- | | Checkfront | Coming soon | -- | | Generic CSV | CSV file upload | Customers, bookings | | Historical Renters | CSV file upload | Customer records (simplified flow) | ## Import steps ### Step 1: Select Source Choose the platform or file type you are importing from. Click a source card to select it. If you select **Historical Renters**, the wizard switches to a simplified upload flow that bypasses the standard six-step process. ### Step 2: Connect or Upload The connection method depends on your selected source: - **FareHarbor** -- Upload one or more CSV files exported from FareHarbor. Files are auto-detected as bookings, contacts, items, or gift cards based on their column headers. - **Rezdy** -- Enter your Rezdy API key and click **Connect**. The system fetches your products, booking count, customer count, and voucher count directly from the Rezdy API. - **Generic CSV** -- Upload CSV files and tag each as bookings or contacts. ### Step 3: Configure 1. **Select the target location** -- choose which RentalTide location the imported data should be associated with. 2. **Choose data types** to import: - Renters (customer records) - Inventory - Pricing - Bookings - Gift cards 3. **Map inventory items** -- for FareHarbor and Rezdy imports, each unique product/item name from the source must be mapped to an existing RentalTide inventory item. The wizard lists all unique items detected and provides a dropdown to select the corresponding RentalTide asset. All items must be mapped before you can proceed (when booking import is selected). ### Step 4: Preview Click **Generate Preview** to analyze the uploaded data without importing it. The preview shows: - Total counts of renters, inventory items, bookings, and gift cards that will be imported - Sample records from each category so you can verify the data looks correct For Rezdy, the preview is generated from the connected API rather than uploaded files. ### Step 5: Import Click **Start Import** to begin the import process. The import runs in the background and shows real-time progress: - Current phase (e.g., importing renters, importing bookings) - Progress bar with completed/total counts and percentage - Any errors encountered during import You can navigate away from the page -- the import continues in the background and you can check back later. ### Step 6: Complete The completion step shows the final results: - Number of renters, inventory items, bookings, and gift cards imported - Number of renters matched to existing records (de-duplicated by email) - Any errors that occurred, with details and timestamps ## Import options | Option | Default | Description | | ----------------- | ------- | ------------------------------------- | | Import Renters | On | Import customer records | | Import Inventory | On | Import fleet/product items | | Import Pricing | On | Import pricing data | | Import Bookings | On | Import booking/reservation records | | Import Gift Cards | On | Import gift card balances and codes | | Skip Duplicates | On | De-duplicate renters by email address | ## Inventory mapping During FareHarbor and Rezdy imports, you must map each source product name to an existing RentalTide inventory item. The mapper shows: - All unique product/item names detected in the source data - A dropdown for each item to select the corresponding RentalTide inventory asset The import will not proceed until all items are mapped when booking import is enabled. {% callout type="tip" %} Always review the preview step before starting the import. Large imports run in the background -- you can navigate away and check back later. Keep a backup of your original CSV files. The import handles duplicate detection by email address. Create your inventory items in RentalTide before starting the import so they are available in the mapping step. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Import failed** -- Check the error details in the completion step. Common causes are incorrect file format, missing required columns, or network issues during API-based imports. **Duplicate records** -- The importer de-duplicates by email address. If you see duplicates after import, use the merge tool in the Customers page to combine them. **FareHarbor CSV format issues** -- FareHarbor CSVs sometimes have a title row before the header row. The importer automatically detects and skips this row. Ensure the "Item" column is present in booking CSVs. **Rezdy connection failed** -- Verify your API key is correct and has not expired. Check that your Rezdy account has API access enabled. **Items not mapping** -- Make sure you have created the corresponding inventory items in RentalTide before starting the import. The mapper only shows existing RentalTide assets. {% /callout %} --- # Partner access Source: https://docs.rentaltide.com/admin/partner-access/ > Manage third-party partner access requests, approve or deny impersonation, and audit session history The partner access page lets you control when third-party partners (such as support agents, consultants, or resellers) can access your account. Review requests, grant time-limited access, and audit everything they do. Navigate to **Admin > Partner Access** in the sidebar. ## Customer ID Your Customer ID is displayed at the top of the page. Share this ID with partners who need to request access to your account. Partners use this ID to initiate an access request through the RentalTide partner portal. ## Tabs | Tab | Description | | --------------------- | ---------------------------------------------------------------------------------------------------------- | | Pending Requests | Partner access requests awaiting your approval. Shows the count of pending requests in the tab label. | | Active Access | Partners currently authorized to access your account. Shows the count of active partners in the tab label. | | Impersonation History | Audit log of all past and current partner sessions. | ## Access levels | Level | Badge | Description | | -------- | --------------- | ------------------------------------------------- | | Partner | Primary badge | Full partner access with configurable permissions | | Referral | Secondary badge | Referral-level access with limited scope | Each access request also specifies whether impersonation is allowed. Impersonation lets the partner act as your account for support and troubleshooting purposes. ## Approving a request 1. Navigate to the **Pending Requests** tab. 2. Review the partner's name, email, company, access level, and whether impersonation is requested. 3. Click **Approve** to grant access. 4. Confirm the approval in the dialog that describes the scope of access being granted. Approved partners receive time-limited, scoped access to the areas you authorized. ## Denying a request 1. Click **Deny** on the pending request. 2. Optionally enter a reason for the denial in the text field. 3. Click **Deny** to reject the request. The partner is notified of the denial. The denial reason is recorded for your reference. ## Revoking active access From the **Active Access** tab, click **Revoke** on any partner. Confirm the revocation in the dialog. Access is terminated immediately. ## Impersonation history The **Impersonation History** tab shows every partner session with the following details: | Column | Description | | --------- | ------------------------------------------------------------------------------------------------- | | Partner | Name and email of the person who accessed your account | | Started | Date and time the session began | | Duration | Length of the session in minutes (capped at the maximum allowed duration) | | API Calls | Number of API calls made during the session | | Status | Active (session in progress), Expired (exceeded max duration), or Ended (session formally closed) | Sessions have a maximum duration (default: 60 minutes). If a session is not formally ended, it is marked as expired once the maximum duration has elapsed. ## Required permission The partner access page requires the `partner_access` permission. Typically only Admin-role users have this permission. {% callout type="tip" %} Only approve access for partners you trust. Revoke access as soon as the partner's work is complete. Review the impersonation history periodically to ensure no unexpected activity. Partner access is always scoped -- partners can only see and do what you authorized. Share your Customer ID only with trusted partners. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **No pending requests showing** -- Partners must initiate the access request from their side using your Customer ID. Confirm they have the correct ID. **Partner cannot access after approval** -- Access may have expired. Check the Active Access tab to verify the partner's session is still active. **Suspicious activity in audit log** -- If you see unexpected API calls or page visits, revoke the partner's access immediately and contact RentalTide support. **Cannot approve or deny requests** -- Ensure your user has the `partner_access` permission and Admin role. {% /callout %} --- # Staff management Source: https://docs.rentaltide.com/admin/staff/ > Add staff members, assign roles and permissions, manage certifications, track skills, and audit activity The staff management page lets you invite team members, assign roles with granular permissions, manage certifications, and define skills -- all from a single view. Navigate to **Admin > Staff** in the sidebar. ## Page tabs | Tab | Purpose | | ------------------- | --------------------------------------------------------- | | Staff | View, add, edit, and delete staff accounts | | Certifications | Assign and track certifications with expiration dates | | Certification Types | Define the types of certifications your organization uses | | Skills | Manage the global skill tag library assigned to staff | ## Adding staff 1. Click **+ Add Staff** in the top-right corner. 2. Enter the staff member's full name and email address. 3. Select a role -- **Admin** or **Standard**. 4. Assign one or more locations using the location dropdown. Each selected location appears as a chip. 5. Select applicable skills from the skill chip list. 6. Configure permissions by toggling individual permission chips (Standard role only -- Admin automatically receives all permissions). 7. Optionally configure hidden sidebar items to limit the navigation a staff member sees. 8. Click **Add User**. An invitation email is sent automatically. The staff member must complete the invitation to activate their account. ### Copy settings from another user When adding a new staff member, you can copy the role, permissions, locations, skills, and view settings from an existing user. Select a user from the **Copy settings from user** dropdown at the top of the Add Staff dialog. ### CSV import Click **Import CSV** to bulk-create staff accounts from a CSV file. The importer recognizes the following column headers: | Column | Required | Notes | | ---------------- | -------- | ------------------------------------------------------------------------- | | Email | Yes | Must be a valid email address | | First Name | No | Combined with Last Name for the display name | | Last Name | No | Combined with First Name | | Permission Level | No | "manager" or "admin" maps to Admin role; everything else maps to Standard | | Location | No | Matched against your location names (falls back to first location) | | Role Name | No | Passed as a skill tag | Duplicate emails are automatically skipped. The system reports how many were created, skipped, or failed. ## Roles | Role | Access level | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Admin | Full access to all features, settings, and data across all assigned locations. All permissions are automatically granted and cannot be individually toggled. | | Standard | Limited access based on individually assigned permissions. Each permission can be enabled or disabled independently. | ## Permissions Permissions are organized into groups. You can toggle an entire group at once using the **Select All / Deselect All** button next to each group header. The groups are: | Group | Permissions | | --------------- | ----------------------------------------------------------------------------------------------------------- | | Inventory | Access, add, delete, edit pricing | | Point of Sale | Add/delete/edit inventory, add transactions, access POS | | POS Accounts | View summary, add/edit/delete transactions, manage debt, manage terminals, access cashout, access reporting | | Text Templates | Access, add, edit, delete | | Email Templates | Access, add, edit, delete | | Bookings | Access, manual payment, edit, delete, force, check in/out, download, deposit, revoke, collect extra | | Calendar | Access, edit | | Customers | Access, add, edit, delete | | Reports | Access, export | | Marketing | Access promotions, gift cards, bundles | | Staff | Access, add, edit, delete | | Settings | Access location and account settings | ### Hidden sidebar items You can hide specific sidebar navigation items for a staff member. This is separate from permissions -- it controls what the user sees in the navigation, not what they can access via direct URL. Items are grouped by product area and toggled individually or in bulk. ## Editing staff Click the edit icon on any staff row to open the Update Staff dialog. You can change the name, email, role, locations, skills, permissions, and view settings. Click **Save** to apply changes. ## Activity audit Click the history icon on any staff row to open the **Activity Log** modal. The audit log shows: - **Total requests** -- lifetime count of API calls made by the user - **First activity** and **Last activity** dates - **Request log** -- each entry shows the HTTP method (color-coded), endpoint path, timestamp, response status code, response time in milliseconds, IP address, and kiosk staff name (if applicable) Pagination controls allow you to browse through large activity logs at 10, 25, 50, or 100 entries per page. ## Certifications The **Certifications** tab displays all certifications assigned to staff members. You can filter by staff member, certification type, and status (active, expiring soon, expired, revoked). ### Certification statuses | Status | Criteria | | ------------- | ------------------------------------------------ | | Active | No expiry date, or expiry more than 30 days away | | Expiring Soon | Expiry date within 30 days | | Expired | Expiry date has passed | | Revoked | Manually revoked by an administrator | The staff table shows certification counts inline -- an active count and a warning count for expiring or expired certifications. Clicking the certification count navigates to the Certifications tab filtered to that staff member. ### Adding a certification 1. Click **+ Add Certification**. 2. Select the staff member and certification type. 3. Enter the issued date and expiry date. 4. Click **Save**. ## Skills The **Skills** tab manages the global list of skill tags available when adding or editing staff. Skills are freeform text labels (stored lowercase). Type a skill name and press Enter or click **Add Skill** to create a new tag. Click the delete icon on any skill chip to remove it from the library. ## Resending invitations If a staff member did not receive or complete their invitation, click the email icon on their row to resend the invitation. ## Deleting staff Click the delete icon on a staff row and confirm the deletion. You cannot delete your own account. ## User limits Your plan has a maximum number of staff accounts (shown as a count next to the staff total, e.g., "5/10"). When you reach the limit, the **+ Add Staff** button is disabled. Upgrade your plan or remove inactive users to make room. {% callout type="tip" %} Follow the principle of least privilege -- give staff members only the permissions they need. Use the copy-from-user feature when onboarding multiple staff with the same role. The activity audit helps you investigate issues and verify that staff are performing the correct actions. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Staff member cannot log in** -- Make sure they completed the invitation process. Try resending the invitation from their row. **Missing features for a staff member** -- Check their permission settings and hidden sidebar items. Standard role users only see features they have been explicitly granted access to. **Reached user limit** -- Your plan has a maximum number of staff accounts. Upgrade your plan or remove inactive users to make room. **Invitation email not received** -- Check the spam folder. Verify the email address is correct in the staff profile. **CSV import skipped users** -- Duplicate emails are silently skipped. Rows without a valid email address are also skipped. {% /callout %} --- # Storage Source: https://docs.rentaltide.com/admin/storage/ > Monitor S3 file storage usage, review breakdowns by category, export reports, and clean up unused files The storage page shows how much file storage your account is using and lets you clean up files you no longer need. Navigate to **Admin > Storage** in the sidebar. ## Storage usage overview The top of the page displays a **Storage Usage Card** showing: - Total storage used (in bytes, KB, MB, or GB) - Storage quota for your plan - Percentage of quota consumed - Visual progress bar indicating usage level If your usage approaches the quota limit, a warning is displayed to help you avoid overage charges. ## Usage breakdown Storage is broken down by category with a progress bar for each: | Category | Description | | ----------------- | ----------------------------------------------------------------- | | Images and Videos | Photos and videos attached to bookings, fleet items, and profiles | | Documents | Signed waivers, receipts, contracts, and uploaded documents | | Other | Miscellaneous files that do not fit the above categories | Each category shows: - Total bytes used - Percentage of total storage - Number of files in the category ## Cleanup The cleanup tool identifies temporary and outdated files that can be safely removed. 1. Click **Clean Storage** in the header. 2. A dialog opens and automatically scans for removable files. The scan identifies: - **Temporary files** -- files created during processing that were never finalized - **Old files** -- files past their retention period 3. Review the estimate showing file counts and estimated space that will be freed. 4. Click **Delete Files Permanently** to execute the cleanup. 5. The storage breakdown refreshes automatically after cleanup completes. The cleanup results report how many files were deleted, broken down by type (temp vs. old), and how much space was freed in GB. {% callout type="warning" %} Deleted files cannot be recovered. Always review the cleanup estimate before proceeding. {% /callout %} ## Export report Click **Export Report** to download a CSV file listing all files in your storage bucket. The CSV includes file names, sizes, categories, and timestamps. Use this for manual review or auditing purposes. The file downloads with a name in the format `storage-report-YYYY-MM-DD.csv`. ## Required permission The storage page requires the `business_access` permission. Only Admin-role users can perform cleanup and export operations. ## Storage management tips - **Optimize images** -- Compress and resize images before uploading to reduce storage usage - **Regular cleanup** -- Run the cleanup tool periodically to remove expired temporary files and free up space - **Plan ahead** -- Monitor your usage trend and upgrade your plan before hitting the quota to avoid overage charges {% callout type="tip" %} Large or high-resolution photos are the most common source of high storage usage. Consider optimizing images before uploading them to fleet inventory or bookings. Run cleanup at the start of each season to clear out old files from the previous year. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Storage breakdown not loading** -- Check your internet connection and refresh the page. The breakdown is fetched from the server and may take a moment for accounts with many files. **Cleanup shows zero files** -- Your account has no temporary or expired files to clean up. All files are currently active. **Export taking too long** -- Large storage buckets may take a few seconds to generate the CSV. The download starts automatically once the file is ready. **Overage charges** -- If you exceed your storage quota, contact support to add capacity or upgrade your plan. {% /callout %} --- # Store settings Source: https://docs.rentaltide.com/admin/store-settings/ > Configure every aspect of your location — business info, taxes, pricing, booking rules, POS, and more Store settings control how each location operates. Navigate to **Settings > Stores** in the sidebar, then click a location to open its settings. Each tab covers a different area of configuration. --- ## Business info Basic information about your location and Stripe payment processing. ### Location details | Setting | Description | | ----------------- | -------------------------------------------------------------------- | | **Location Name** | Display name for this location | | **Address** | Physical address (Mapbox autocomplete) | | **Phone Number** | Contact phone number | | **Website** | Your company website URL | | **Company Email** | Main email contact | | **Company Logo** | Logo for booking widget and receipts (recommended 80x80px) | | **Favicon** | Browser tab icon (16x16 or 32x32px) | | **Splash Image** | Landscape image for multi-location booking page selector (280x158px) | ### Industry type Choose the industry that best matches your business. This affects default settings, terminology, and AI suggestions. Options: General, Watercraft Rental, Winter Spa/Hot Springs, Snow Sports, Fishing Charter, Diving/Snorkeling, General Outdoor Activity, Indoor Attraction. ### Stripe connection | Setting | Description | | -------------------------- | --------------------------------------------------------------------------------------------------- | | **Connect Stripe Express** | Link your Stripe account for payment processing | | **Country** | Country for your Stripe account (set during creation) | | **Test Mode** | Enable test mode for development. Use card `4242 4242 4242 4242` with any future expiry and any CVC | {% callout type="tip" %} You can manage your Stripe account (payouts, bank details, verification) by clicking **Manage Account** after connecting. {% /callout %} --- ## General Timezone, currency, formatting, and business hours. ### Regional settings | Setting | Description | | ----------------- | ------------------------------------------------------------------------------ | | **Timezone** | Location timezone (affects all booking times) | | **Currency** | USD, CAD, AUD, GBP, EUR, CHF, SEK, NOK, DKK, PLN, CZK, HUF, RON, BGN, HRK, ISK | | **Date Format** | dd/mm/yyyy or mm/dd/yyyy | | **Time Format** | 24-hour or 12-hour | | **Start of Week** | Sunday or Monday | | **Units** | Imperial or Metric | ### Business hours | Setting | Description | | -------------------- | ----------------------------------------------------------------------- | | **Core Hours Start** | Opening time for this location | | **Core Hours End** | Closing time for this location | | **Slot Steps** | Time intervals for booking slots (5–60 minutes, in 5-minute increments) | ### Visibility | Setting | Description | | -------------------------- | ---------------------------------------------------------------------------------------------------------------- | | **Hide from Booking Page** | Hides this location from the public booking page selector. Use for internal-only or not-yet-open locations | | **Location Slug** | Human-readable URL for your booking page (e.g., `app.rentaltide.com/booking/your-slug`). Auto-generate available | --- ## Taxes Configure up to 5 tax rates per location. Each tax rate has: | Setting | Description | | --------------------- | ----------------------------------------------- | | **Tax Name** | Display name (e.g., "Sales Tax", "GST") | | **Tax Rate (%)** | Percentage, 0–20% in 0.1% increments | | **GL Code** | Accounting code (maps to GL 3050–3064) | | **Apply to Bookings** | Whether this tax applies to rental bookings | | **Apply to Slips** | Whether this tax applies to marina slip charges | {% callout type="tip" %} Tax appears as a separate line item on customer invoices. Multiple taxes stack (e.g., state + county). {% /callout %} --- ## Pricing & payments Controls how you charge customers, handle fees, and process payments. ### Pricing display | Setting | Description | | ------------------------------ | ---------------------------------------------------- | | **Show Starting From Pricing** | Display "from $X" pricing on the booking widget | | **Show Fee Breakdown** | Show itemized fee breakdown to customers at checkout | | **Show Inclusive Pricing** | Show all-in pricing instead of base + fees | ### Fees & overages | Setting | Description | | -------------------------------------------- | ----------------------------------------------------------------- | | **Charge Application Fee (Platform Fee)** | Pass the 4.9% platform fee to customers as a line item | | **Charge Interchange Fee (Credit Card Fee)** | Pass the 2.7% + $0.30 credit card processing fee to customers | | **Charge Booking Overages** | Bill customers for late returns | | **Reprice Late Returns at Regular Rate** | Use standard hourly rate instead of overage rate for late returns | | **Overage Amount** | Dollar amount charged per overage increment | | **Overage Threshold** | Time increment for overage billing (e.g., every 15 minutes) | {% callout type="warning" title="Australia" %} Credit card surcharging is prohibited in Australia effective October 1, 2026 under RBA regulation. The interchange fee toggle displays a warning for Australian locations. {% /callout %} {% callout type="tip" %} To make customers pay the credit card fee, enable **Charge Interchange Fee**. This adds approximately 2.7% + $0.30 as a separate line item on their checkout total. {% /callout %} ### Payment methods | Setting | Description | | ------------------------------ | --------------------------------------------- | | **Allow Transfer To AR** | Allow marking payments as Accounts Receivable | | **Allow Cheque/Check Payment** | Accept cheque payments | ### Pre-auth hold settings Pre-authorization holds are used for damage deposits. | Setting | Description | | ------------------------- | ------------------------------------------------------------------------------------- | | **Collect Pre-Auth Hold** | When to place the hold: 24h, 12h, 6h, or 3h before rental. Or manual only | | **Release Pre-Auth Hold** | When to release: 3h, 6h, 8h, 12h, 24h, 48h, 96h, or 120h after rental. Or manual only | ### Tipping | Setting | Description | | ----------------------------- | ---------------------------------------------------- | | **Enable Tipping** | Allow customer tipping | | **Section Title** | Title shown on tipping UI (default: "Tip Your Crew") | | **Description** | Optional text below the title | | **Suggested Tip Percentages** | Comma-separated values (e.g., 15,18,20,25) | | **Allow Custom Tip Amount** | Let customers enter a custom amount | | **Minimum Custom Amount** | Floor for custom tips | | **Maximum Custom Amount** | Cap for custom tips | --- ## Booking & checkout Rules for how bookings are created, what customers see at checkout, and payment collection. ### Booking rules | Setting | Description | | ---------------------------------- | ----------------------------------------------------- | | **Enable Booking Groups (Orders)** | Allow multi-item orders | | **Only Allow One Booking** | Restrict customers to 1 active booking at a time | | **Require Booking Approval** | Staff must approve each booking before it's confirmed | | **Require Call To Book** | Disable online booking — customers must call | | **Show Save My Spot** | Enable hold/reservation feature | | **Enable Bundles & Cart** | Allow multi-item carts with bundle discounts | | **Include Waiver On Checkout** | Show waiver during checkout flow | | **Show Address Field on Waivers** | Display address input on waiver form | | **Require Address on Waivers** | Make address mandatory on waivers | ### Cutoffs | Setting | Description | | ------------------------------- | ------------------------------------------------------------------- | | **Online Checkout Last Call** | How far in advance online bookings must be made (0–72 hours slider) | | **Max Simultaneous Departures** | Limit concurrent departures (0–50, 0 = unlimited) | ### Protection plans Toggle each plan on/off and customize the customer-facing description. | Plan | Coverage | | ------------------- | ---------------------- | | **AquaShield** | Damage coverage | | **AquaCover** | Liability coverage | | **BlueBalance** | Carbon offset | | **AquaTow** | Tow coverage | | **Trip Protection** | Cancellation insurance | Additional badges: **Best Selling**, **Best Value**, **X Remaining** — shown on time slots in the booking widget. ### Checkout notes Up to 4 custom fields on the checkout page. | Setting | Description | | --------------- | --------------------------------------------- | | **Type** | Note Field (text input) or Agreement Checkbox | | **Label** | Field label (supports markdown) | | **Placeholder** | Help text or example | | **Required** | Whether the field is mandatory | ### Launch locations Define pickup/departure points with pricing. | Setting | Description | | ------------------- | -------------------------------------------- | | **Location Name** | Name of the launch point | | **Price** | Price for this launch location | | **Layover Minutes** | Required layover time (15-minute increments) | ### Payment links (Quick Book) | Setting | Description | | ------------------------------- | ------------------------------------------------- | | **Auto-cancel Unpaid Bookings** | Automatically cancel if not paid in time | | **Auto-cancel After (Hours)** | Hours before auto-cancel triggers | | **Reminder Schedule** | Comma-separated hours for payment reminder emails | | **Default Send Method** | Email, SMS, or Both | ### Payment options | Setting | Description | | --------------------------- | ------------------------------ | | **Allow Pay Half** | Enable 50/50 payment split | | **Allow Installment Plans** | Enable multi-payment schedules | ### Deposit payment plans | Setting | Description | | -------------------------------- | ------------------------------------------------------- | | **Enable Deposit Payment Plans** | Customers pay deposit now, remainder auto-charged later | | **Failure Action** | Flag for Staff Review or Auto-Cancel Booking | | **Max Retries** | Number of retry attempts for failed auto-charges | | **Reminder Days Before Charge** | Days in advance to send reminder email | #### How fees are collected on deposit-plan bookings When a booking is split across a deposit and a later balance charge, **the platform fee for the full booking is collected upfront on the deposit charge**, not split across both payments. The later balance charge carries only the credit-card processing fee — no additional platform fee. **Why this matters:** without upfront collection, the platform fee on the remaining balance would be lost if the customer later pays the balance in cash at the counter (cash payments don't route through Stripe, so there's no opportunity to withhold a platform fee). Collecting the full-booking platform fee on the card-backed deposit guarantees the fee is captured regardless of how the balance is ultimately paid. **Example:** $400 booking, 50% deposit plan, 4.9% platform fee rate. | Charge | Customer pays | Platform fee withheld | CC fee withheld | Merchant receives | | ------------------ | ------------- | --------------------- | --------------- | ----------------- | | Deposit (upfront) | $200 | $19.60 (4.9% × $400) | $6.10 | $174.30 | | Balance (auto) | $200 | $0 | $6.10 | $193.90 | | **Totals** | $400 | $19.60 | $12.20 | $368.20 | The deposit payout looks smaller than a simple 4.9% of $200 would suggest — that's expected. The upfront deposit carries the full-booking platform fee; the balance charge only carries the CC fee on its own amount. {% callout type="tip" %} If you're reconciling payouts against expected margins, remember that a deposit-plan booking's **deposit** is charged the full platform fee for the entire booking, and the **balance** is charged no platform fee at all. Your total platform-fee cost across both payments still equals `platformRate × bookingTotal`. {% /callout %} --- ## Inventory ### Categories Define custom categories for organizing your fleet (e.g., "Jet Skis", "Pontoons", "Kayaks"). Each category has an auto-generated ID and a display name. ### Booking pipeline Configure the stages bookings flow through (Kanban columns). Each stage has a name, color, icon, and can be enabled/disabled. Drag to reorder. ### Inventory settings | Setting | Description | | ----------------------------------- | ------------------------------------------------------- | | **Show "Require Insurance Upload"** | Display insurance upload requirement on inventory items | | **Show "Require Ownership Upload"** | Display ownership proof requirement on inventory items | --- ## Nav board ### Tags Up to 20 custom tags for the navigation board. Each tag has a name and color (20 preset colors). Use tags to categorize or flag active rentals. ### Pipeline column order Drag-and-drop reorder of pipeline stage columns on the journey board. --- ## Operations (check-in/out) ### Staff assignments Configure how staff are assigned to bookings during checkout. ### Equipment checklist Predefined checklist items for the checkout/check-in process. Toggle each item on or off. ### Zello integration | Setting | Description | | ------------------- | ----------------------------------------------- | | **Network Name** | Your Zello network | | **Username** | Zello account username | | **Password** | Zello account password | | **API Key** | Zello API key | | **Device Mappings** | Map Zello devices to inventory items and assets | ### Waiver template Select which waiver template is active for this location's checkout flow. --- ## Kiosk ### Self-service check-in Toggle each step in the kiosk check-in flow: ID verification, payment collection, damage deposit, waiver signing, safety briefing. ### Safety briefing quiz Create quiz questions customers must answer before their rental. | Setting | Description | | ------------------ | --------------------------------------------------------------- | | **Question** | The question text | | **Type** | Multiple Choice or True/False | | **Options** | Answer choices (2–6 for multiple choice) | | **Correct Answer** | Mark the correct answer | | **AI Generate** | Auto-generate location-specific safety questions (max 10 total) | ### Terminal reader Select and configure the Stripe card reader for this kiosk location. --- ## Customer pages ### Next Steps styling Choose a style template for the post-booking Next Steps page. ### Customer cancellation | Setting | Description | | ----------------------------------- | ---------------------------------------------------------------------------- | | **Allow Self-Service Cancellation** | Let customers cancel from their Next Steps page | | **Cancellation Cutoff (Days)** | Minimum days before rental to allow cancellation | | **Cancellation Fee** | Charge a fee for cancellations | | **Fee Type** | Percentage or Fixed Amount | | **Fee Amount** | The fee value | | **Fee Basis** | Calculate fee on the refund amount or the full booking total | | **Store Credit Option** | Offer store credit as an alternative to cash refund | | **Refund Method** | Customer chooses, Card only, or Store credit only | | **Bonus Markup (%)** | Extra percentage added to store credit (e.g., 10% bonus for choosing credit) | ### Self-service reschedule | Setting | Description | | ---------------------------- | ------------------------------------------------ | | **Allow Reschedule** | Let customers reschedule from Next Steps | | **Reschedule Cutoff (Days)** | Minimum days before rental to allow rescheduling | | **Charge Reschedule Fee** | Apply a fee for rescheduling | | **Fee Type** | Percentage or Fixed Amount | | **Fee Amount** | The fee value | ### Other settings | Setting | Description | | --------------------------------- | --------------------------------------------------- | | **Safety Briefing on Next Steps** | Show safety briefing content on the Next Steps page | | **Hide waiver section** | Remove the waiver section from Next Steps | | **Hide participants section** | Remove the participant manager from Next Steps | | **Terms of Service URL** | Link to your terms of service | | **Cancellation Policy URL** | Link to your cancellation policy | --- ## POS & receipts ### POS settings | Setting | Description | | ---------------------------- | ----------------------------------------- | | **Allow POS Receipt Prompt** | Ask customers if they want a receipt | | **Show SKU on Receipt** | Display product SKU on printed receipts | | **Enable Store Credit** | Enable store credit for customer accounts | | **Default Credit Amount** | Default amount when issuing store credit | ### Cash denominations Configure which bill and coin denominations appear during cash transactions. Each denomination has a value, label, and enabled toggle. Sorted by value. ### Receipt customization | Setting | Description | | ------------------------ | --------------------------------------------- | | **Show Logo on Receipt** | Display your company logo on printed receipts | | **Receipt Header** | Custom header text | | **Receipt Footer** | Custom footer text | --- ## Translations Override default UI text with custom translations per language. | Feature | Description | | ----------------------- | ------------------------------------------------------------ | | **Languages** | English, French, Spanish | | **Custom Translations** | JSON format overrides for any UI string | | **Templates** | Quick-apply: Boat Rentals, Spa Reservations, ATV/UTV Rentals | | **Show Defaults** | Display system default translations alongside custom | {% callout type="tip" %} Use translation templates to quickly adapt the UI terminology for your industry — for example, changing "boat" to "unit" or "reservation" to "appointment". {% /callout %} --- ## Dynamic pricing AI-powered pricing that adjusts rates based on demand signals. ### Enable Master toggle to turn dynamic pricing on or off for this location. ### Strategy | Strategy | Behavior | | -------------------------- | ------------------------------------------ | | **Balanced** (recommended) | Blends urgency and demand signals | | **Urgency Increase** | Prices rise as the booking date approaches | | **Last Minute Deal** | Prices drop for low-occupancy time slots | | **Demand Responsive** | Pure demand-based adjustments | ### Limits | Setting | Description | | ------------------------- | ----------------------------------------------------------- | | **Max Increase (%)** | Maximum percentage prices can rise above base rate (0–100%) | | **High Demand Threshold** | Occupancy percentage that triggers price increases | | **Low Demand Threshold** | Occupancy percentage that triggers price decreases | | **Show Original Price** | Display crossed-out base price next to adjusted price | ### Per-listing overrides Enable or disable dynamic pricing for individual inventory items independent of the global setting. ### Advanced signal weights Fine-tune how different signals influence pricing. Weights auto-normalize to 100%. | Signal | Description | | ------------------- | -------------------------------------- | | **Forecast Demand** | Weight for demand forecast data | | **Occupancy Rate** | Weight for current occupancy | | **Engagement** | Weight for customer engagement metrics | | **Weather** | Weight for weather conditions | --- ## GL codes Map RentalTide's default general ledger codes to your accounting system's chart of accounts. ### Overrides Create mappings from system GL codes to your custom codes. For example, map system code `6520` to your accounting system's rental revenue code. ### System GL code reference | Range | Category | | --------- | ----------------------------------------------------- | | 1020–1140 | Assets (Cash, AR, Revenue Clearing, Deferred Revenue) | | 2100–2150 | Liabilities (Gift Cards, Platform Fees Payable) | | 3015–3115 | Damages, Tips | | 3050–3064 | Tax 1 through Tax 15 | | 4160 | Moorage Seasonal Revenue | | 6509–6551 | Rental Revenue (by type) | | 6560 | Credit Card Surcharge Revenue | | 6561 | Platform Fee Revenue | | 6580–6587 | Protection Plan Revenue | | 6600–6610 | Cancellation Fees, Late Return, Overtime | | 5110–5130 | Cost of Goods Sold | | 8190–8214 | Expenses (Processing, Discounts) | --- ## Slip booking Configure marina slip booking settings. | Setting | Description | | ----------------------- | ---------------------------------------------------- | | **Enable Slip Booking** | Turn on the slips module for this location | | **Deposit Settings** | Configure deposit requirements for slip reservations | | **Rate Configuration** | Set pricing for slip rentals | | **Contract Templates** | Manage lease contract templates | | **Additional Services** | Configure add-on services for slip tenants | {% callout type="note" %} The Slips & Marina module is a paid add-on at $249.99/month for up to 50 units. Additional 50-unit blocks are available for larger marinas. {% /callout %} --- ## Haul services Configure boat haul-out, launch, and maintenance services. ### Service types | Service | Settings | | ------------------- | ------------------------ | | **Haul Out** | Base fee + per-foot rate | | **Put In (Launch)** | Base fee + per-foot rate | | **Pressure Wash** | Base fee + per-foot rate | ### Additional services Up to 15 custom services, each with a name, price, and enabled toggle. ### Scheduling | Setting | Description | | ---------------------- | ----------------------------------------------------------------- | | **Time Slots** | Up to 8 time slots with label, start/end time, and enabled toggle | | **Operating Schedule** | Enable/disable each day of the week with max services per day | ### Equipment & limits | Setting | Description | | ------------------------ | ------------------------------------------------------------ | | **Available Equipment** | Travel Lift, Crane, Hydraulic Trailer, Forklift (checkboxes) | | **Deposit Required** | Toggle + type (percentage or fixed) + amount | | **Rush Surcharge (%)** | Extra charge for rush service | | **Min Boat Length (ft)** | Minimum vessel length (0 = no minimum) | | **Max Boat Length (ft)** | Maximum vessel length (0 = no limit) | | **Max Weight (lbs)** | Maximum vessel weight (0 = no limit) | --- # Stripe account & payments Source: https://docs.rentaltide.com/admin/stripe/ > How RentalTide connects to Stripe, what changed with Stripe Connect Express, and where to access your dashboard RentalTide uses **Stripe Connect** to process payments on your behalf. This page explains how the connection works, what changed when we moved to Stripe Connect **Express**, and where to find the things you used to access in the full Stripe dashboard. --- ## Quick answer: where's my Stripe dashboard? If you used to log into Stripe directly and now your dashboard looks different (or you can only see payouts), this is expected. RentalTide now uses **Stripe Connect Express** instead of Standard accounts, and almost everything you used to do in Stripe is now built directly into RentalTide. **Open Settings → Stripe Dashboard** inside RentalTide to access: - Payments, refunds, and disputes - Payouts and payout schedules - Account balance - Bank account and business details - Onboarding (if not yet complete) You no longer need to leave RentalTide to manage your Stripe account. --- ## Why we changed from Standard to Express In November 2025 RentalTide moved to **Stripe Connect Express with Embedded Components**. The change was deliberate — it gives you a better, more integrated experience without losing any functionality. ### What you gain - **Everything in one place** — payments, payouts, refunds, disputes, and bank details are all inside RentalTide. No more bouncing between two products. - **Faster onboarding** — Express has a streamlined signup flow with fewer fields. New locations get up and running in minutes, not days. - **Branded experience** — your team and customers see RentalTide, not a Stripe-branded screen. - **Built-in role-based access** — RentalTide controls who on your team can issue refunds, manage payouts, or view balances. ### What changed - The connected account no longer has access to the **full Stripe dashboard at `dashboard.stripe.com`**. Instead, you have the **Express Dashboard** (limited — payouts and basic reporting), or the much more capable **embedded dashboard inside RentalTide** (recommended). - Express accounts can't be converted back to Standard. This is a Stripe limitation, not a RentalTide one. {% callout type="note" %} If you have an older **Standard** account from before November 2025, it still works exactly as it did. You can keep using `dashboard.stripe.com` for that account. The change only affects new locations created after the switch. {% /callout %} --- ## Where to find common tasks | What you want to do | Where to do it | | -------------------------------------- | ------------------------------------------------------------- | | Issue a refund | RentalTide → **Stripe Dashboard → Payments**, find the charge | | View payouts and schedule | RentalTide → **Stripe Dashboard → Payouts** | | Trigger an instant payout | RentalTide → **Stripe Dashboard → Payouts → Pay out now** | | Update your bank account | RentalTide → **Stripe Dashboard → Account** | | Update business details (EIN, address) | RentalTide → **Stripe Dashboard → Account** | | View account balance | RentalTide → **Stripe Dashboard → Balances** | | Handle a dispute or chargeback | RentalTide → **Stripe Dashboard → Payments → Disputes** | | Complete onboarding | RentalTide → **Stripe Dashboard → Onboarding** | | Download tax forms (1099-K) | Express Dashboard at `connect.stripe.com/app/express` | | View transactions and exports | RentalTide → **Reporting → Payments** | --- ## Connecting Stripe to a new location When you create a new store in RentalTide: 1. Go to **Settings → Stores → Add store** 2. Fill in the location details (name, address, email, country) 3. Click **Add and Setup** 4. RentalTide creates an Express account behind the scenes and saves the `connectId` to the location 5. Complete onboarding inline by going to **Stripe Dashboard → Onboarding** The whole process happens inside RentalTide — there's no separate Stripe signup. --- ## Test mode Use test mode while you're setting things up or trying new flows: - Toggle **Test Mode** in **Settings → Stores → Business info** - Use card `4242 4242 4242 4242` with any future expiry and any CVC - Test charges show up in the embedded dashboard tagged as test transactions Switch to live mode when you're ready to take real payments. --- ## Frequently asked questions ### Why can't I log into Stripe like I used to? Express connected accounts have a limited Stripe-hosted dashboard at `connect.stripe.com/app/express`. Everything you used to do in the full Stripe dashboard is now built into RentalTide — open **Stripe Dashboard** in the sidebar. ### Can I get my Standard account back? Stripe doesn't allow converting an Express account back to Standard. If you genuinely need full `dashboard.stripe.com` access for something RentalTide doesn't expose, contact us and we'll figure out the right path — but in nearly every case, the embedded dashboard inside RentalTide does the job better. ### What about my old Standard account from before November 2025? It still works. We don't migrate existing Standard accounts. You can keep using `dashboard.stripe.com` for those locations indefinitely. ### Who can access the Stripe Dashboard inside RentalTide? By default, **admins** can access everything. You can restrict refund management, payout management, and account changes to specific roles in **Settings → Staff**. ### Is anything missing from the embedded dashboard? Almost nothing. The embedded dashboard supports payments, refunds, disputes, payouts (instant + scheduled), balances, account management, and onboarding. The only thing currently outside the embedded view is **tax forms (1099-K)** — those still come from `connect.stripe.com/app/express`. --- ## Need help? If you're stuck on a Stripe-related task and can't find what you need, reach out to our support channel (**Settings → Support**) and we'll help you locate it. --- # Subscription Source: https://docs.rentaltide.com/admin/subscription/ > Manage your plan, view pricing, update payment methods, and manage slip add-ons The subscription page shows your current plan details, billing status, and payment methods. Manage everything related to how you pay for RentalTide. Navigate to **Admin > Subscription** in the sidebar. ## Current plan Your subscription page displays the current plan status including: - **Plan name** and tier (Standard or Custom) - **Billing status** (active, past due, cancelled) - **Next billing date** - **Current usage** against plan limits ### Standard plan | Feature | Limit | | --------------- | -------------------- | | Transaction fee | 4.9% | | Interchange fee | 2.7% per transaction | | Locations | Up to 10 | | Fleet items | Up to 100 | | Bookings | Up to 10,000 | | Staff accounts | Up to 100 | | Discount codes | Up to 500 | | API keys | Up to 2 | ### Custom plan Enterprise customers may be on a custom plan with different rates and limits. Custom plans can include: - Reduced transaction and interchange fees - Custom monthly pricing - Higher limits for locations, fleet items, staff, and bookings - Guaranteed revenue thresholds Custom plan details are displayed on the subscription page when applicable. Contact support for custom pricing arrangements. ## Slip add-on The slip add-on enables marina slip management features. It is billed as a separate subscription: | Slip add-on | Rate | | ------------------- | ------------------------------- | | Base | $249.99/month per 50 slip units | | Additional capacity | Added in blocks of 10 slips | ### Managing slips - **Start subscription** -- begins a new slip subscription if you do not have one - **Add slips** -- adds incremental slip capacity (10 units per block) - **Manage billing** -- opens the Stripe billing portal for your slip subscription - **Cancel** -- cancels the slip subscription at the end of the current billing period The subscription page shows your current slip quota, how many slips are in use, and remaining capacity. ## Payment methods View and manage the payment methods on file for your account. You can: - **Add a payment method** -- attach a new card via Stripe's secure setup flow - **View existing cards** -- see the card brand, last four digits, and expiration date - **Set default** -- the default card is charged on each billing cycle ## Actions | Action | Description | | -------------------- | -------------------------------------------------------------------------------------------------- | | View Billing History | Opens your complete billing history in Stripe's customer portal | | Cancel Subscription | Cancels your subscription at the end of the current billing period -- you retain access until then | | Reactivate | If cancelled, reactivate before the period ends to continue uninterrupted | | Add Payment Method | Opens the Stripe setup form to attach a new card | ## Required permission The subscription page requires the `subscription_access` permission. {% callout type="tip" %} Cancellation takes effect at the end of your current billing period -- you do not lose access immediately. Payment method changes take effect on the next billing cycle. Contact support for enterprise pricing or custom arrangements. Slip capacity can be increased at any time without downtime. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Payment failed** -- Update your payment method. Stripe retries the charge automatically on a schedule. **Need more capacity** -- Review your plan limits on the subscription page. Contact support to upgrade or adjust limits. **Slip units exceeded** -- Add another capacity block from the subscription page to increase your slip quota. **Cannot access subscription page** -- Ensure your user has the `subscription_access` permission. Only Admin-role users typically have this permission. **Stripe portal not loading** -- Check for popup blockers in your browser. The Stripe billing portal opens in a new tab. {% /callout %} --- # API Reference Source: https://docs.rentaltide.com/api-reference/ > Integrate with RentalTide using the REST API # API reference The RentalTide API lets you programmatically manage bookings, inventory, customers, transactions, and more. Every action available in the dashboard is available through the API. --- ## Base URL All API requests are made to: ``` https://v3.api.rentaltide.com ``` --- ## Authentication The API supports two authentication methods. ### Bearer token (JWT) Include your JWT in the `Authorization` header. Tokens are issued through Auth0 when a user logs in to the dashboard. ``` Authorization: Bearer eyJhbGciOiJSUzI1NiIs... ``` ### API key For server-to-server integrations, use a static API key. Generate one from **Admin > API Keys** in the dashboard. ``` x-api-key: rtk_live_abc123def456 ``` {% callout type="warning" title="Keep keys secret" %} API keys carry the same permissions as the user who created them. Never expose them in client-side code or public repositories. {% /callout %} --- ## Request format All request bodies must be JSON with the `Content-Type: application/json` header. ```bash curl -X POST https://v3.api.rentaltide.com/bookings \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"inventoryId": "inv_123", "startDate": "2026-06-15T09:00:00Z"}' ``` --- ## Response format Every response returns JSON. Successful responses use this structure: ```json { "success": true, "data": { ... } } ``` Error responses include a message and an error code: ```json { "success": false, "error": "BOOKING_CONFLICT", "message": "The requested time slot is no longer available." } ``` ### HTTP status codes | Code | Meaning | | ----- | ---------------------------------------------------------- | | `200` | Success | | `201` | Created | | `400` | Bad request — check the request body | | `401` | Unauthorized — missing or invalid credentials | | `403` | Forbidden — valid credentials but insufficient permissions | | `404` | Not found | | `409` | Conflict — resource state prevents the action | | `429` | Rate limited — slow down | | `500` | Internal server error | --- ## Rate limits API requests are rate-limited per authentication credential: | Plan | Limit | | ---------- | ----------------------- | | Standard | 100 requests per minute | | Enterprise | 500 requests per minute | When you exceed the limit, the API returns a `429` status with a `Retry-After` header indicating how many seconds to wait. {% callout type="note" %} Webhook deliveries and dashboard usage do not count against your API rate limit. {% /callout %} --- ## Pagination List endpoints return paginated results. Use the `limit` and `offset` query parameters to navigate pages. ``` GET /bookings?limit=25&offset=50 ``` The response includes a `total` count: ```json { "success": true, "data": [ ... ], "total": 142, "limit": 25, "offset": 50 } ``` --- ## API sections {% cardGroup cols=3 %} {% card title="Bookings" href="/api/bookings/" /%} {% card title="Inventory" href="/api/inventory/" /%} {% card title="Customers" href="/api/customers/" /%} {% card title="Point of Sale" href="/api/pos/" /%} {% card title="Availability" href="/api/availability/" /%} {% card title="Payments" href="/api/payments/" /%} {% card title="Locations" href="/api/locations/" /%} {% card title="Staff" href="/api/staff/" /%} {% card title="Waivers" href="/api/waivers/" /%} {% card title="Slips" href="/api/slips/" /%} {% card title="Memberships" href="/api/memberships/" /%} {% card title="Marina" href="/api/marina/" /%} {% card title="Marketing" href="/api/marketing/" /%} {% card title="Public" href="/api/public/" /%} {% card title="Analytics" href="/api/analytics/" /%} {% card title="Integrations" href="/api/integrations/" /%} {% /cardGroup %} --- ## Webhooks RentalTide can send real-time event notifications to your server. Configure webhook endpoints from **Admin > Webhooks** in the dashboard. ### Supported events | Event | Fired when | | --------------------- | -------------------------------- | | `booking.created` | A new booking is placed | | `booking.updated` | A booking is modified | | `booking.cancelled` | A booking is cancelled | | `booking.checked_in` | A customer checks in | | `booking.checked_out` | A rental is returned | | `payment.completed` | A payment is captured | | `payment.refunded` | A refund is issued | | `customer.created` | A new customer record is created | ### Webhook payload ```json { "event": "booking.created", "timestamp": "2026-06-15T14:30:00Z", "data": { "id": "bk_abc123", "inventoryId": "inv_456", "customerId": "cust_789", "startDate": "2026-06-15T09:00:00Z", "endDate": "2026-06-15T11:00:00Z", "status": "confirmed" } } ``` {% callout type="tip" title="Verifying webhooks" %} Each webhook request includes an `X-RentalTide-Signature` header. Verify this HMAC-SHA256 signature against your webhook secret to confirm the request came from RentalTide. {% /callout %} --- # Analytics API Source: https://docs.rentaltide.com/api-reference/analytics/ > Reporting, forecasting, AI insights, and event tracking Endpoints for analytics dashboards, revenue forecasting, AI-powered insights, and event tracking. ## Analytics ### Get analytics metrics for dashboard KPIs `GET /analytics/metrics` Returns aggregated metrics including revenue, tax, tips, discounts, and booking counts with daily breakdown **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ------------------------------------ | | `locationIds` | query | string | Yes | Comma-separated list of location IDs | | `startDate` | query | string | Yes | Start date (YYYY-MM-DD) | | `endDate` | query | string | Yes | End date (YYYY-MM-DD) | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | Analytics metrics returned successfully | | `400` | Missing required parameters | | `500` | Server error | --- ### Get inventory performance metrics `GET /analytics/inventory-performance` Returns top performing inventory items and assets by revenue **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------- | -------- | ------------------------------------ | | `locationIds` | query | string | Yes | Comma-separated list of location IDs | | `startDate` | query | string | Yes | Start date (YYYY-MM-DD) | | `endDate` | query | string | Yes | End date (YYYY-MM-DD) | | `limit` | query | integer | No | Number of items to return | **Responses** | Code | Description | | ----- | ------------------------------------------------ | | `200` | Inventory performance data returned successfully | | `400` | Missing required parameters | | `500` | Server error | --- ### Get year-over-year revenue data `GET /analytics/year-over-year` Returns monthly revenue data for specified locations comparing current and previous year. Uses ledger/journal entries to include all revenue sources (bookings + POS). **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------- | -------- | ------------------------------------------------------ | | `locationIds` | query | string | Yes | Comma-separated list of location IDs | | `currentYear` | query | integer | No | The current year to compare (defaults to current year) | **Responses** | Code | Description | | ----- | ------------------------------------------------- | | `200` | Year-over-year revenue data returned successfully | | `400` | Missing or invalid locationIds parameter | | `500` | Server error | --- ### Update revenue target `PATCH /analytics/revenue-target` Updates the revenue target and/or previous year revenue override for a location **Request body** | Field | Type | Required | Description | | ----------------------------- | ------ | -------- | ----------------------------------------------------------- | | `locationId` | string | Yes | The location ID to update | | `revenueTarget` | number | No | Revenue target amount (must be positive) | | `previousYearRevenueOverride` | number | No | Override value for previous year revenue (must be positive) | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Revenue settings updated successfully | | `400` | Missing locationId or invalid values | | `404` | Location not found | | `500` | Server error | --- ### Realize revenue for all completed bookings `POST /analytics/realize-completed-bookings` Creates journal entries for all completed bookings that haven't had revenue recognized yet. This syncs booking revenue to the ledger. **Request body** | Field | Type | Required | Description | | ------------- | ------- | -------- | ---------------------------------------------------- | | `locationIds` | array | No | Optional - limit to specific location IDs | | `dryRun` | boolean | No | If true, only returns count without creating entries | **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Revenue realization completed successfully | | `500` | Server error | --- ### Get widget/booking page engagement metrics `GET /analytics/widget-metrics` Returns funnel, temporal heatmap, daily engagement, source breakdown, and top products **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ------------------------------------ | | `locationIds` | query | string | Yes | Comma-separated list of location IDs | | `startDate` | query | string | Yes | | | `endDate` | query | string | Yes | | **Responses** | Code | Description | | ----- | ----------------------------------------------- | | `200` | Widget engagement metrics returned successfully | | `400` | Missing required parameters | | `500` | Server error | --- ## Forecast ### Get demand forecast for a location `GET /forecast` Returns predicted daily renter counts using historical data, weather, and holiday detection **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------- | -------- | ----------------------------------------------------- | | `locationId` | query | string | Yes | Location ID to forecast for | | `days` | query | integer | No | Number of days to forecast (7 or 14) | | `timezone` | query | string | No | Timezone for date calculations (e.g. America/Toronto) | | `units` | query | string | No | Temperature units | **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | Forecast data returned successfully | | `400` | Missing required parameters | | `500` | Server error | --- ## AI ### Process AI request `POST /ai` Routes AI requests to different services based on the 'what' parameter **Request body** | Field | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------------------ | | `what` | string | Yes | Type of AI request | | `message` | string | No | Message or prompt for the AI | | `prompt` | string | No | Detailed prompt for AI analysis (for emailEnhancement) | | `businessName` | string | No | Business name (for socialMediaPost) | | `businessType` | string | No | Type of business (for socialMediaPost) | | `businessLocation` | string | No | Business location (for socialMediaPost) | | `bookings` | array | No | Booking data (for bookingSidekick) | | `websiteUrl` | string | No | Website URL (for businessAudit) | | `businessData` | object | No | Additional business data (for businessAudit) | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | AI response returned successfully | | `400` | Invalid or missing parameters | | `401` | Unauthorized | | `500` | Server error | --- ### Perform business audit `POST /ai/business-audit` Performs a comprehensive business audit using AI analysis of website and business data. Charges $4.99 per audit. **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------- | | `websiteUrl` | string | Yes | Website URL to audit | | `businessData` | object | No | Additional business data for analysis | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Business audit completed successfully | | `400` | Invalid or missing websiteUrl | | `401` | Unauthorized | | `403` | Business audit integration not enabled | | `404` | Customer not found | | `500` | Server error | --- ### Get business audit billing history `GET /ai/business-audit/billing` Retrieves business audit billing history and analytics for the authenticated customer **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Billing history returned successfully | | `401` | Unauthorized | | `404` | Customer not found | | `500` | Server error | --- ### Validate receipt data `POST /ai/validate-receipt` Validates inputted transaction data against an uploaded receipt image using AI Vision **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | --------------------------------- | | `receiptImage` | string | Yes | Base64 encoded image or image URL | | `inputData` | object | No | | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Validation completed successfully | | `400` | Missing receipt image or validation data | | `500` | Server error | --- ### Extract receipt data `POST /ai/extract-receipt-data` Extracts structured data from a receipt image without validation (useful for auto-filling forms) **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | --------------------------------- | | `receiptImage` | string | Yes | Base64 encoded image or image URL | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Data extracted successfully | | `400` | Missing receipt image | | `500` | Server error | --- ### Proxy external webpage `GET /ai/proxy-page` Proxies an external webpage and injects selection script for click-to-tag inventory import functionality **Parameters** | Name | In | Type | Required | Description | | ----- | ----- | ------ | -------- | ------------------------ | | `url` | query | string | Yes | URL of the page to proxy | **Responses** | Code | Description | | ----- | ----------------------------------------------- | | `200` | Proxied HTML page with injected selection tools | | `400` | Missing or invalid URL | | `500` | Failed to load page | --- ### Scrape inventory from URL `POST /ai/scrape-inventory` Scrapes inventory/rental information from a website URL using Puppeteer screenshots and OpenAI Vision to extract structured data **Request body** | Field | Type | Required | Description | | ----- | ------ | -------- | ----------------------------------- | | `url` | string | Yes | URL of the inventory page to scrape | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Inventory data extracted successfully | | `400` | Missing or invalid URL | | `401` | Customer ID required | | `500` | Server error | --- ### Extract driver's license data `POST /ai/extract-license` Extracts driver's license information from an image using GPT-4 Vision **Request body** | Field | Type | Required | Description | | ------- | ------ | -------- | --------------------------------------------------------- | | `image` | string | Yes | Base64 encoded image or image URL of the driver's license | **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | License data extracted successfully | | `400` | Missing image or extraction failed | | `500` | Server error | --- ## Events ### Create a new booking event `POST /events` **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ----------- | | `transactionid` | string | No | | | `rentalId` | string | No | | | `event_type` | string | No | | | `eventType` | string | No | | | `event_change` | string | No | | | `eventLabel` | string | No | | | `content` | string | No | | | `details` | object | No | | | `actorName` | string | No | | | `actorId` | string | No | | | `customerId` | string | No | | **Responses** | Code | Description | | ----- | -------------------------- | | `200` | Event created successfully | | `500` | Error creating event | --- ### Get events for booking(s) `GET /events` **Parameters** | Name | In | Type | Required | Description | | --------------- | ----- | ------ | -------- | ------------------------------------------ | | `transactionid` | query | string | No | Single rental ID (backward compat) | | `rentalIds` | query | string | No | Comma-separated rental IDs for batch fetch | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Events retrieved successfully | | `400` | transactionid or rentalIds is required | | `500` | Error retrieving events | --- --- # Availability API Source: https://docs.rentaltide.com/api-reference/availability/ > Check real-time availability, holds, and maintenance blocks Endpoints for checking inventory availability, placing temporary holds on assets, and managing dry dock maintenance periods. ## Availability ### Preload availability data `GET /preload-availability` Preloads booking data for a given inventory and month to enable faster availability checking on the client **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | -------------------------------------------- | | `inventoryId` | query | string | Yes | The inventory ID to preload availability for | | `month` | query | string | Yes | The month to preload in YYYY-MM format | **Responses** | Code | Description | | ----- | ------------------------------------------------- | | `200` | Availability data retrieved successfully | | `400` | Missing or invalid inventoryId or month parameter | | `500` | Internal server error | --- ## Hold Asset ### Create a waitlist entry to hold an asset `POST /holdAsset` Creates a waitlist entry to temporarily hold/reserve an asset for a customer. Entries expire after 2 days (TTL). **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------ | | `customerId` | string | Yes | The customer ID | | `email` | string | Yes | Customer email address | | `inventoryId` | string | Yes | The ID of the inventory item to hold | | `locationId` | string | Yes | The location ID | **Responses** | Code | Description | | ----- | ----------------------------------- | | `201` | Waitlist entry created successfully | | `400` | Missing required fields | | `500` | Could not create waitlist entry | --- ## Dry Dock ### Create a new drydock booking `POST /drydock` Creates a new drydock booking to block a boat from rentals during a specified time period **Request body** | Field | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------- | | `boatId` | string | Yes | The ID of the boat to block | | `startDate` | string | Yes | Start date of the drydock period | | `endDate` | string | Yes | End date of the drydock period | | `assetIds` | array | No | Optional array of asset IDs to assign | **Responses** | Code | Description | | ----- | ---------------------------------------------- | | `200` | Drydock booking created successfully | | `400` | Missing required fields or invalid date format | | `500` | Server error | --- ### Get drydock bookings `GET /drydock` Retrieves drydock bookings for a given boat and optionally filtered by asset **Parameters** | Name | In | Type | Required | Description | | --------- | ----- | ------ | -------- | ---------------------------------------------- | | `boatId` | query | string | Yes | The ID of the boat to get drydock bookings for | | `assetId` | query | string | No | Optional asset ID to filter by | **Responses** | Code | Description | | ----- | ------------------------ | | `200` | List of drydock bookings | | `400` | Missing boatId parameter | | `500` | Server error | --- ### Update a drydock booking `PATCH /drydock` Updates an existing drydock booking with new dates or asset assignment **Request body** | Field | Type | Required | Description | | ----------- | ------ | -------- | --------------------------------------- | | `rentalId` | string | Yes | The ID of the drydock booking to update | | `startDate` | string | Yes | New start date of the drydock period | | `endDate` | string | Yes | New end date of the drydock period | | `assetId` | string | No | Optional new asset ID to assign | **Responses** | Code | Description | | ----- | ---------------------------------------------- | | `200` | Drydock booking updated successfully | | `400` | Missing required fields or invalid date format | | `500` | Server error | --- ### Delete a drydock booking `DELETE /drydock` Deletes an existing drydock booking **Request body** | Field | Type | Required | Description | | ---------- | ------ | -------- | --------------------------------------- | | `rentalId` | string | Yes | The ID of the drydock booking to delete | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Drydock booking deleted successfully | | `400` | Missing rentalId | | `500` | Server error | --- --- # Bookings API Source: https://docs.rentaltide.com/api-reference/bookings/ > Create, retrieve, update, and cancel rental bookings Endpoints for managing the booking lifecycle -- from creation through checkout and return. ## Core Booking ### Create a new booking `POST /coreBooking/book` Main booking endpoint that handles the complete booking workflow including: - Customer creation/updates - Payment processing (full or installment plans) - Inventory management and asset assignment - Booking record creation - Notifications and accounting entries - Loyalty points awarding **Request body** | Field | Type | Required | Description | | -------------------- | ------ | -------- | ------------------------------------------------------------ | | `rentalDetails` | object | Yes | Core rental information | | `rentalPricing` | object | Yes | Pricing breakdown | | `customerInfo` | object | Yes | Customer details | | `depositInformation` | object | No | Deposit configuration | | `discountDetails` | object | No | Applied discounts | | `paymentMethodId` | string | No | Stripe payment method ID | | `paymentPlan` | object | No | Payment plan type (full or installments) | | `selectedBoatAddons` | array | No | | | `globalAddons` | array | No | | | `extendedData` | object | Yes | Additional booking data including location and customer info | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `201` | Booking created successfully | | `400` | Invalid request data or validation error | | `402` | Payment authorization failed | | `409` | Time slot unavailable or fully booked | | `500` | Internal server error | --- ## Booking Schedule ### Get booking schedule for an inventory `GET /schedule/schedule` Retrieves booking schedule with date range optimization, filtering, sorting, and pagination support **Parameters** | Name | In | Type | Required | Description | | ------------------ | ----- | ------- | -------- | ------------------------------------------ | | `inventory_id` | query | string | Yes | The inventory ID to get schedule for | | `start_date` | query | string | No | Filter bookings from this date | | `end_date` | query | string | No | Filter bookings until this date | | `extend` | query | string | No | Include extended record data | | `dates_only` | query | string | No | Return only date information | | `page` | query | integer | No | Page number for pagination | | `limit` | query | integer | No | Number of items per page | | `include_drydock` | query | string | No | Include drydock bookings | | `use_parallel` | query | string | No | Enable parallel loading | | `use_scan` | query | string | No | Force table scan if GSI issues | | `search_rental_id` | query | string | No | Search for specific rental ID | | `sort_by` | query | string | No | Sort field | | `sort_order` | query | string | No | Sort direction | | `status_filter` | query | string | No | Filter by booking status (comma-separated) | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | Successfully retrieved booking schedule | | `400` | Missing required inventory_id parameter | | `500` | Internal server error | --- ### Search bookings `GET /schedule/schedule/search` Quick search endpoint to find specific bookings by rental ID, customer name, or email **Parameters** | Name | In | Type | Required | Description | | --------------- | ----- | ------ | -------- | --------------------------------- | | `inventory_id` | query | string | Yes | The inventory ID to search within | | `rental_id` | query | string | No | Partial rental ID to search for | | `customer_name` | query | string | No | Customer name to search for | | `email` | query | string | No | Customer email to search for | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | Search results returned successfully | | `400` | Missing required inventory_id parameter | | `500` | Search failed | --- ### Get inventory items by location `GET /schedule/location` Retrieves inventory items for specified location IDs **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------ | -------- | --------------------------------- | | `location_ids` | query | string | Yes | JSON array string of location IDs | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Successfully retrieved inventory items | | `400` | Missing or invalid location_ids parameter | | `500` | Internal server error | --- ### Get bookings by customer and status `GET /schedule/by-customer-status` Query bookings by customerId and optionally filter by Status using the customerId-Status-index GSI **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------- | -------- | -------------------------------------------- | | `customer_id` | query | string | No | Customer ID (defaults to authenticated user) | | `status` | query | string | No | Filter by specific booking status | | `start_date` | query | string | No | Filter bookings from this date | | `end_date` | query | string | No | Filter bookings until this date | | `page` | query | integer | No | Page number for pagination | | `limit` | query | integer | No | Number of items per page | | `sort_order` | query | string | No | Sort direction | | `lightweight` | query | string | No | Return minimal data for boards | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Successfully retrieved bookings | | `400` | Missing required customer_id | | `500` | Internal server error | --- ### Get bookings by location and status `GET /schedule/by-location-status` Query bookings by locationId and optionally filter by Status using the locationId-Status-index GSI **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------- | -------- | --------------------------------- | | `location_id` | query | string | Yes | Location ID to query | | `status` | query | string | No | Filter by specific booking status | | `start_date` | query | string | No | Filter bookings from this date | | `end_date` | query | string | No | Filter bookings until this date | | `page` | query | integer | No | Page number for pagination | | `limit` | query | integer | No | Number of items per page | | `sort_order` | query | string | No | Sort direction | | `lightweight` | query | string | No | Return minimal data for boards | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Successfully retrieved bookings | | `400` | Missing required location_id | | `500` | Internal server error | --- ### Get paginated schedules with cursor-based pagination `GET /schedule/schedules/paginated` Retrieves schedules with smart pagination and advanced filtering support **Parameters** | Name | In | Type | Required | Description | | --------- | ----- | ------- | -------- | ------------------------------------ | | `limit` | query | integer | No | Number of items per page | | `cursor` | query | string | No | Base64 encoded cursor for pagination | | `filters` | query | string | No | JSON encoded filter criteria | **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Successfully retrieved paginated schedules | | `400` | Invalid cursor or filters format | | `401` | Customer ID not found | | `500` | Failed to fetch schedules | --- ### Get total count of schedules `GET /schedule/schedules/count` Returns the total count of schedules for the authenticated customer with optional filtering **Parameters** | Name | In | Type | Required | Description | | --------- | ----- | ------ | -------- | ---------------------------- | | `filters` | query | string | No | JSON encoded filter criteria | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Successfully retrieved count | | `400` | Invalid filters format | | `401` | Customer ID not found | | `500` | Failed to count schedules | --- ## Booking Payments ### Authorize a deposit `POST /preAuth/authorize-deposit` Creates a pre-authorization for a security/damage deposit using Stripe or terminal payment **Request body** | Field | Type | Required | Description | | -------------------- | ------ | -------- | --------------------------------------------------- | | `rentalId` | string | Yes | The ID of the rental/booking | | `authorizeAmount` | number | Yes | Amount to authorize in dollars | | `currency` | string | No | Currency code | | `description` | string | No | Description for the payment | | `customer` | string | No | Stripe customer ID | | `paymentMethodType` | string | Yes | Payment method type | | `paymentMethodId` | string | No | Stripe payment method ID (required for stripe type) | | `notes` | string | No | Notes for the deposit | | `readerId` | string | No | Terminal reader ID (for terminal payments) | | `connectedAccountId` | string | No | Stripe connected account ID | | `platformFeePercent` | number | No | Platform fee percentage | | `customerId` | string | No | Customer ID | | `locationId` | string | No | Location ID | **Responses** | Code | Description | | ----- | ----------------------------------------------------- | | `200` | Deposit pre-authorization successful | | `400` | Missing required fields or unsupported payment method | | `500` | Server error | --- ### Finalize a deposit `POST /preAuth/post-deposit` Records the deposit information in the booking record after terminal payment completes **Request body** | Field | Type | Required | Description | | ----------------- | ------ | -------- | ---------------------------- | | `rentalId` | string | Yes | The ID of the rental/booking | | `authorizeAmount` | number | No | Amount that was authorized | | `customerId` | string | No | Customer ID | | `locationId` | string | No | Location ID | | `notes` | string | No | Notes for the deposit | | `intentId` | string | Yes | Stripe PaymentIntent ID | | `connectId` | string | No | Stripe connected account ID | **Responses** | Code | Description | | ----- | ------------------------------------------------- | | `200` | Deposit finalized successfully | | `400` | Missing required fields or payment not successful | | `500` | Server error | --- ### Capture a deposit `POST /preAuth/capture-deposit` Captures a pre-authorized deposit payment for checkout or damage claims **Request body** | Field | Type | Required | Description | | -------------------- | ------ | -------- | ---------------------------------- | | `rentalId` | string | Yes | The ID of the rental/booking | | `paymentIntentId` | string | Yes | Stripe PaymentIntent ID to capture | | `captureAmount` | number | Yes | Amount to capture in dollars | | `connectedAccountId` | string | No | Stripe connected account ID | | `description` | string | No | Description for the capture | | `claimType` | string | No | Type of claim | | `feeInCents` | number | No | Platform fee in cents | | `locationId` | string | No | Location ID | **Responses** | Code | Description | | ----- | ----------------------------------------------------- | | `200` | Deposit captured successfully | | `400` | Missing required fields or payment cannot be captured | | `404` | Payment intent not found | | `500` | Server error | --- ### Release a deposit `POST /preAuth/release-deposit` Releases (cancels) a pre-authorized deposit that won't be charged **Request body** | Field | Type | Required | Description | | -------------------- | ------ | -------- | ---------------------------------- | | `rentalId` | string | Yes | The ID of the rental/booking | | `paymentIntentId` | string | Yes | Stripe PaymentIntent ID to release | | `amount` | number | No | Amount being released | | `customerId` | string | No | Customer ID for notification | | `locationId` | string | No | Location ID for notification | | `connectedAccountId` | string | No | Stripe connected account ID | **Responses** | Code | Description | | ----- | ----------------------------- | | `200` | Deposit released successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Sync missing pre-authorization `POST /preAuth/sync-missing-preauth/{paymentIntentId}` Recovery endpoint to manually sync a missing pre-authorization from Stripe to database **Parameters** | Name | In | Type | Required | Description | | ----------------- | ---- | ------ | -------- | ------------------------------- | | `paymentIntentId` | path | string | Yes | Stripe PaymentIntent ID to sync | **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------- | | `rentalId` | string | No | Override rental ID if not in metadata | | `locationId` | string | No | Location ID for Stripe region | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Pre-authorization synced successfully | | `400` | Missing paymentIntentId or rentalId | | `404` | PaymentIntent not found | | `500` | Server error | --- ### Audit pre-authorizations `GET /preAuth/audit-preauths/{rentalId}` Retrieves all pre-authorizations for a rental from Stripe with status and expiration details **Parameters** | Name | In | Type | Required | Description | | ---------------------- | ----- | ------ | -------- | ---------------------------------------------- | | `rentalId` | path | string | Yes | The rental ID to audit | | `locationId` | query | string | No | Location ID for Stripe region | | `connected_account_id` | query | string | No | Stripe connected account ID for direct charges | **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | Audit results returned successfully | | `500` | Server error | --- ### Create terminal connection token `POST /preAuth/connection-token` Creates a Stripe Terminal connection token for terminal payments **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------- | | `locationId` | string | No | Location ID for Stripe region | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Connection token created successfully | | `500` | Server error | --- --- # Customers API Source: https://docs.rentaltide.com/api-reference/customers/ > Look up and manage customer records and renters Endpoints for managing customer records, renter profiles, and pending renter approvals. ## Customers ### Get customer for authenticated user `GET /customer` Retrieves the customer record associated with the authenticated user **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Successfully retrieved customer | | `401` | Unauthorized | | `404` | User or customer not found | | `500` | Server error | --- ### Update customer `PATCH /customer` Updates the customer record associated with the authenticated user. Handles subscription changes via Stripe. **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------- | | `name` | string | No | | | `email` | string | No | | | `permissions` | object | No | | | `integrations` | object | No | Integration settings (merged with existing) | **Responses** | Code | Description | | ----- | ---------------------------------------------- | | `200` | Customer updated successfully | | `400` | No data provided or subscription change failed | | `401` | Unauthorized | | `404` | Customer not found | | `500` | Server error | --- ### Create a new customer `POST /customer` Creates a new customer record and associates it with the authenticated user. Also creates a Stripe customer. **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ----------------------------------------------- | | `name` | string | Yes | | | `email` | string | No | | | `business_name` | string | No | | | `affiliateCode` | string | No | Affiliate/partner code for tracking conversions | | `permissions` | object | No | | **Responses** | Code | Description | | ----- | ----------------------------- | | `201` | Customer created successfully | | `400` | Customer name is required | | `401` | Unauthorized | | `500` | Server error | --- ### Create Stripe customer `POST /customer/create-stripe-customer` Creates a Stripe customer for an existing customer if one does not already exist **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Stripe customer created or already exists | | `401` | Unauthorized | | `404` | Customer not found | | `500` | Server error | --- ### Attach payment method `POST /customer/payment-method` Attaches a payment method to the customer's Stripe account and sets it as default **Request body** | Field | Type | Required | Description | | ----------------- | ------ | -------- | ------------------------ | | `paymentMethodId` | string | Yes | Stripe payment method ID | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Payment method attached successfully | | `400` | Payment method ID is required | | `401` | Unauthorized | | `404` | Customer or Stripe customer not found | | `500` | Server error | --- ### Create Stripe SetupIntent `POST /customer/create-setup-intent` Creates a SetupIntent for setting up a payment method via Stripe Elements **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | SetupIntent created successfully | | `401` | Unauthorized | | `404` | Customer or Stripe customer not found | | `500` | Server error | --- ### Get saved payment methods `GET /customer/payment-methods` Retrieves all saved payment methods for the customer from Stripe **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Successfully retrieved payment methods | | `401` | Unauthorized | | `404` | Customer or Stripe customer not found | | `500` | Server error | --- ### Get subscription details `GET /customer/subscription` Retrieves the customer's current subscription details from Stripe **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | Successfully retrieved subscription | | `401` | Unauthorized | | `404` | Customer not found | | `500` | Server error | --- ### Cancel subscription `POST /customer/cancel-subscription` Cancels the customer's subscription at the end of the current billing period **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | Subscription scheduled for cancellation | | `401` | Unauthorized | | `404` | No active subscription found | | `500` | Server error | --- ### Reactivate subscription `POST /customer/reactivate-subscription` Reactivates a subscription that was scheduled for cancellation **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Subscription reactivated successfully | | `401` | Unauthorized | | `404` | No active subscription found | | `500` | Server error | --- ## Renters ### Get paginated list of renters `GET /renters` Fetches renters with server-side search, filtering, sorting, and optional accounting enrichment **Parameters** | Name | In | Type | Required | Description | | ------------------- | ----- | ------- | -------- | --------------------------------------------------------------- | | `customerId` | query | string | Yes | The customer ID to filter renters by | | `page` | query | integer | No | Page number for pagination | | `limit` | query | integer | No | Number of renters per page (max 100) | | `includeAccounting` | query | boolean | No | Include accounting data (balance, debits, credits) | | `search` | query | string | No | Search term to filter renters by name, email, phone, or license | | `status` | query | string | No | Filter by renter status | | `sortField` | query | string | No | Field to sort by | | `sortDirection` | query | string | No | Sort direction | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Successfully retrieved renters | | `400` | Missing required customerId parameter | | `401` | Unauthorized access | | `500` | Server error | --- ### Create or update a renter `POST /renters` Creates a new renter or updates an existing one if email and customerId match. Also creates a Stripe customer if needed. **Request body** | Field | Type | Required | Description | | --------------------- | ------ | -------- | ----------- | | `firstName` | string | Yes | | | `lastName` | string | Yes | | | `email` | string | Yes | | | `address` | string | Yes | | | `customerId` | string | Yes | | | `dob` | string | No | | | `waivers_signed_urls` | array | No | | | `id_verifications` | object | No | | | `transactions` | array | No | | | `additional_info` | object | No | | | `stripe_customer_id` | string | No | | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Existing renter updated successfully | | `201` | New renter created successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Export renters data `GET /renters/export` Export renters data as CSV or JSON format with optional filtering **Parameters** | Name | In | Type | Required | Description | | ------------------- | ----- | ------- | -------- | ------------------------------------ | | `customerId` | query | string | Yes | The customer ID to filter renters by | | `format` | query | string | No | Export format | | `includeAccounting` | query | boolean | No | Include accounting data in export | | `search` | query | string | No | Search term to filter renters | | `status` | query | string | No | Filter by renter status | | `sortField` | query | string | No | Field to sort by | | `sortDirection` | query | string | No | Sort direction | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | Successfully exported renters data | | `400` | Invalid request parameters | | `401` | Unauthorized access | | `500` | Server error | --- ### Get search suggestions for autocomplete `GET /renters/search-suggestions` Returns suggestions based on renter names, emails, phone numbers, and license numbers **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------------------------------- | | `customerId` | query | string | Yes | The customer ID to search within | | `query` | query | string | Yes | Search query (minimum 2 characters) | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Successfully retrieved suggestions | | `400` | Missing required customerId parameter | | `401` | Unauthorized access | | `500` | Server error | --- ### Get a single renter by ID `GET /renters/{id}` Fetches a single renter with their accounting information **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ------------- | | `id` | path | string | Yes | The renter ID | **Responses** | Code | Description | | ----- | ----------------------------- | | `200` | Successfully retrieved renter | | `400` | Renter ID is required | | `401` | Unauthorized access | | `404` | Renter not found | | `500` | Server error | --- ### Update a renter `PATCH /renters/{id}` Updates renter fields. Can also append notes using add_note flag. **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ------------- | | `id` | path | string | Yes | The renter ID | **Request body** | Field | Type | Required | Description | | ----------------- | ------- | -------- | -------------------------------------------- | | `add_note` | boolean | No | If true, appends the note to the notes array | | `note` | object | No | Note to append (used with add_note) | | `firstName` | string | No | | | `lastName` | string | No | | | `email` | string | No | | | `address` | string | No | | | `status` | string | No | | | `riskScore` | number | No | | | `additional_info` | object | No | | | `transactions` | array | No | Transactions are appended to existing array | **Responses** | Code | Description | | ----- | ------------------------------------------- | | `200` | Renter updated successfully | | `400` | Invalid request or no valid fields provided | | `401` | Unauthorized access | | `404` | Renter not found | | `500` | Server error | --- ### Delete a renter `DELETE /renters/{id}` Deletes a renter from DynamoDB and also deletes the associated Stripe customer **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ------------- | | `id` | path | string | Yes | The renter ID | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Renter deleted successfully | | `400` | Renter ID is required | | `401` | Unauthorized access | | `404` | Renter not found | | `500` | Server error | --- ### Attach a payment method to a renter `POST /renters/{id}/payment-method` Attaches a payment method to the renter's Stripe customer and sets it as the default **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ------------- | | `id` | path | string | Yes | The renter ID | **Request body** | Field | Type | Required | Description | | ------------------- | ------ | -------- | ------------------------ | | `payment_method_id` | string | Yes | Stripe payment method ID | **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Payment method added successfully | | `400` | Renter ID or payment method ID is required | | `404` | Renter or Stripe customer not found | | `500` | Server error | --- ### Get renters with outstanding balances `GET /owing/renters` Fetches all renters who owe money (balance >= $1) with optional historical date filtering **Parameters** | Name | In | Type | Required | Description | | ---------- | ----- | ------ | -------- | ------------------------------------------------------------- | | `asOfDate` | query | string | No | Calculate balances as of this date (for historical reporting) | **Responses** | Code | Description | | ----- | -------------------------------------------------------- | | `200` | Successfully retrieved renters with outstanding balances | | `500` | Server error | --- ### Get journal entries for a renter `GET /renters/journal-entries/{id}` Fetches all non-deleted journal entries for a specific renter **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ------------- | | `id` | path | string | Yes | The renter ID | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Successfully retrieved journal entries | | `400` | Renter ID is required | | `401` | Unauthorized access | | `404` | Renter not found | | `500` | Server error | --- ### Get renter's saved payment methods `GET /renters/{id}/payment-methods` Retrieves all saved payment methods (cards) for a renter from Stripe **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ----------- | | `id` | path | string | Yes | Renter ID | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Payment methods retrieved successfully | | `404` | Renter not found or no Stripe customer ID | | `500` | Server error | --- ## Pending Renters ### Create a new waiver number `POST /pending-renters` Creates a new waiver number with a unique PIN for waiver signing **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | --------------- | | `customerId` | string | Yes | The customer ID | | `locationId` | string | No | The location ID | | `firstName` | string | No | | | `lastName` | string | No | | | `email` | string | No | | | `phone` | string | No | | | `dob` | string | No | | | `address` | object | No | | | `assignTo` | string | No | | | `assetId` | string | No | | | `transactionId` | string | No | | | `renterId` | string | No | | | `templateId` | string | No | | | `rawData` | object | No | | **Responses** | Code | Description | | ----- | ---------------------------------- | | `201` | Waiver number created successfully | | `400` | Missing required customerId | | `409` | Waiver number already exists | | `500` | Could not create waiver number | --- ### Get waiver numbers `GET /pending-renters` Retrieves waiver numbers by transactionId, customerId, or locationId **Parameters** | Name | In | Type | Required | Description | | --------------- | ----- | ------ | -------- | ---------------------------------------------- | | `transactionId` | query | string | No | Transaction ID to filter by | | `customerId` | query | string | No | Customer ID to filter by | | `locationId` | query | string | No | Location ID to filter by | | `status` | query | string | No | Status to filter by (e.g., PENDING, COMPLETED) | **Responses** | Code | Description | | ----- | ---------------------------------------------------- | | `200` | Waiver numbers retrieved successfully | | `400` | transactionId, customerId, or locationId is required | | `500` | Could not fetch waiver numbers | --- ### Validate a waiver PIN `GET /pending-renters/validate` Validates a PIN and returns waiver details if valid and not expired **Parameters** | Name | In | Type | Required | Description | | ----- | ----- | ------ | -------- | ------------------- | | `pin` | query | string | Yes | The PIN to validate | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | PIN validated successfully | | `400` | PIN is required | | `404` | Invalid PIN or waiver already completed | | `410` | Waiver has expired | | `500` | Could not validate PIN | --- ### Update a waiver number `PATCH /pending-renters/{waiverNumber}` Updates waiver details with optimistic locking support **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | --------------------------- | | `waiverNumber` | path | string | Yes | The waiver number to update | **Request body** | Field | Type | Required | Description | | ----------------- | ------ | -------- | ---------------------- | | `firstName` | string | No | | | `lastName` | string | No | | | `email` | string | No | | | `phone` | string | No | | | `dob` | string | No | | | `address` | object | No | | | `assignTo` | string | No | | | `assetId` | string | No | | | `status` | string | No | | | `renterId` | string | No | | | `waiverUrl` | string | No | | | `templateId` | string | No | | | `expectedVersion` | string | No | For optimistic locking | **Responses** | Code | Description | | ----- | ----------------------------------------------------- | | `200` | Waiver updated successfully | | `400` | waiverNumber is required or no valid fields to update | | `404` | Waiver number not found | | `409` | Record has been modified by another user | | `500` | Could not update waiver number | --- ### Delete a waiver number `DELETE /pending-renters/{waiverNumber}` Deletes a waiver number (only if it is in pending status) **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | --------------------------- | | `waiverNumber` | path | string | Yes | The waiver number to delete | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Waiver number deleted successfully | | `400` | waiverNumber is required | | `409` | Can only delete pending waiver numbers | | `500` | Could not delete waiver number | --- ### Complete a waiver `PATCH /pending-renters/{waiverNumber}/complete` Marks a waiver as completed, removes the PIN, and optionally updates the renter record **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------------------- | | `waiverNumber` | path | string | Yes | The waiver number to complete | **Request body** | Field | Type | Required | Description | | ----------------- | ------ | -------- | ---------------------------------------- | | `renterId` | string | Yes | The renter ID | | `waiverUrl` | string | Yes | URL to the completed waiver | | `signatureId` | string | No | | | `completionData` | object | No | Additional data to store with the waiver | | `expectedVersion` | string | No | For optimistic locking | **Responses** | Code | Description | | ----- | ---------------------------------------------------- | | `200` | Waiver completed successfully | | `400` | waiverNumber, renterId, and waiverUrl are required | | `404` | Waiver number not found | | `409` | Waiver is not in pending status or has been modified | | `500` | Could not complete waiver | --- --- # Integrations API Source: https://docs.rentaltide.com/api-reference/integrations/ > Connect with Google Calendar, Viator, Wave, and more Endpoints for managing third-party integrations including Google Calendar sync, Google Things to Do, Viator, vQuip fleet management, Wave accounting, voice services, and shipment tracking. ## Integrations ### Get GPS alerts for a location `GET /integrations/alerts` Retrieves OneStepGPS alerts from a specific location within a time range **Parameters** | Name | In | Type | Required | Description | | --------- | ----- | ------- | -------- | ----------------------------------------- | | `minutes` | query | integer | No | Number of minutes to look back for alerts | | `limit` | query | integer | No | Maximum number of alerts to return | **Responses** | Code | Description | | ----- | --------------------- | | `200` | List of alerts | | `500` | Internal server error | --- ### Get GPS alerts for a specific device `GET /integrations/device-alerts/{deviceName}` Retrieves OneStepGPS alerts for a specific device within a time range **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------- | -------- | ----------------------------------------- | | `deviceName` | path | string | Yes | The name of the GPS device | | `minutes` | query | integer | No | Number of minutes to look back for alerts | | `limit` | query | integer | No | Maximum number of alerts to return | **Responses** | Code | Description | | ----- | --------------------- | | `200` | List of device alerts | | `500` | Internal server error | --- ### Get alert summary by device `GET /integrations/alert-summary` Retrieves a summary of alert counts grouped by device name for a location **Parameters** | Name | In | Type | Required | Description | | ------- | ----- | ------- | -------- | ---------------------------------------------- | | `hours` | query | integer | No | Number of hours to look back for alert summary | **Responses** | Code | Description | | ----- | ----------------------- | | `200` | Alert summary by device | | `500` | Internal server error | --- ## Google Calendar ### Initiate Google Calendar OAuth flow `GET /integrations/google-calendar/connect` Starts the Google Calendar OAuth2 authorization flow. Returns an authorization URL to redirect the user to. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------------------------- | | `userId` | query | string | Yes | The user ID initiating the connection | | `customerId` | query | string | Yes | The customer/business ID | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Authorization URL generated successfully | | `400` | Missing userId or customerId | | `500` | Failed to initiate Google Calendar connection | --- ### Google Calendar OAuth callback `GET /integrations/google-calendar/callback` Handles the OAuth2 callback from Google, exchanges the code for tokens, and stores the encrypted credentials **Parameters** | Name | In | Type | Required | Description | | ------- | ----- | ------ | -------- | -------------------------------------- | | `code` | query | string | Yes | Authorization code from Google | | `state` | query | string | Yes | State token to validate the request | | `error` | query | string | No | Error code if authorization was denied | **Responses** | Code | Description | | ----- | -------------------------------------------------------- | | `200` | Returns HTML page with postMessage to close popup | | `302` | Redirects to frontend with error if authorization denied | | `400` | Missing code/state or invalid state token | --- ### Disconnect Google Calendar `POST /integrations/google-calendar/disconnect` Revokes Google Calendar access and removes stored credentials for a user **Request body** | Field | Type | Required | Description | | -------- | ------ | -------- | ------------------------- | | `userId` | string | Yes | The user ID to disconnect | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Google Calendar disconnected successfully | | `400` | Missing userId | | `500` | Failed to disconnect Google Calendar | --- ### Get calendar connection status `GET /integrations/google-calendar/status` Returns the Google Calendar connection status for a user **Parameters** | Name | In | Type | Required | Description | | -------- | ----- | ------ | -------- | -------------------- | | `userId` | query | string | Yes | The user ID to check | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Connection status retrieved successfully | | `400` | Missing userId | | `500` | Failed to get calendar status | --- ### Get available calendars `GET /integrations/google-calendar/calendars` Returns the list of Google Calendars available to the user **Parameters** | Name | In | Type | Required | Description | | -------- | ----- | ------ | -------- | ----------- | | `userId` | query | string | Yes | The user ID | **Responses** | Code | Description | | ----- | ----------------------------------------------- | | `200` | Calendars retrieved successfully | | `400` | Missing userId or Google Calendar not connected | | `500` | Failed to fetch calendars | --- ### Update selected calendar `POST /integrations/google-calendar/update-calendar` Changes the calendar used for syncing bookings **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------- | | `userId` | string | Yes | The user ID | | `calendarId` | string | Yes | The Google Calendar ID to use | **Responses** | Code | Description | | ----- | ---------------------------------------------------------- | | `200` | Calendar updated successfully | | `400` | Missing userId/calendarId or Google Calendar not connected | | `500` | Failed to update calendar | --- ### Trigger manual calendar sync `POST /integrations/google-calendar/sync` Manually triggers a full calendar sync job. Primarily for testing and debugging. **Responses** | Code | Description | | ----- | ---------------------- | | `200` | Sync job started | | `500` | Failed to trigger sync | --- ## Google Things To Do ### Get schema for inventory item `GET /schema/inventory/{inventoryId}` Returns JSON-LD structured data for a single inventory item, formatted for Google Search rich results (Things to Do) **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ----------------------------- | | `inventoryId` | path | string | Yes | The inventory item ID | | `baseUrl` | query | string | No | Base URL for generating links | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | JSON-LD schema for the inventory item | | `403` | Inventory is not publicly visible | | `404` | Inventory or location not found | | `500` | Internal server error | --- ### Get schema for location `GET /schema/location/{locationId}` Returns JSON-LD LocalBusiness schema for a location including all its public inventory items **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------------------------- | | `locationId` | path | string | Yes | The location ID | | `baseUrl` | query | string | No | Base URL for generating links | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | JSON-LD LocalBusiness schema for the location | | `404` | Location not found | | `500` | Internal server error | --- ### Get all schemas for customer `GET /schema/all/{customerId}` Returns an array of JSON-LD schemas for all locations and their public inventory belonging to a customer **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------------------------- | | `customerId` | path | string | Yes | The customer/business ID | | `baseUrl` | query | string | No | Base URL for generating links | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Array of JSON-LD schemas | | `404` | No locations found for customer | | `500` | Internal server error | --- ## Viator Integration ### Configure Viator integration `POST /viator/admin/configure` Create or update the Viator marketplace configuration for a customer. Uses JWT auth (admin only). **Request body** | Field | Type | Required | Description | | -------------- | ------- | -------- | ---------------------------------------------------------- | | `customerId` | string | Yes | | | `supplierId` | string | Yes | | | `apiKey` | string | Yes | Inbound API key for Viator to authenticate with RentalTide | | `viatorApiKey` | string | No | Outbound API key for sending notifications to Viator | | `currency` | string | No | | | `isActive` | boolean | No | | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Configuration saved successfully | | `400` | Missing required fields | | `500` | Internal server error | --- ### Tour List (V1) `POST /viator/tourlist` Returns the full product catalog for this supplier. Authenticated via Viator API key. **Responses** | Code | Description | | ----- | ---------------------- | | `200` | List of tours/products | | `401` | Unauthorized | --- ### Calendar availability (V2) `POST /viator/v2/availability/calendar` Returns availability and pricing for a date range across products. **Request body** | Field | Type | Required | Description | | ------------------ | ------ | -------- | ----------- | | `productOptionIds` | array | Yes | | | `startDate` | string | Yes | | | `endDate` | string | Yes | | **Responses** | Code | Description | | ----- | -------------------------- | | `200` | Calendar availability data | | `400` | Missing required fields | --- ### Availability check (V2) `POST /viator/v2/availability/check` Real-time capacity check for specific products, dates, and traveler counts. **Request body** | Field | Type | Required | Description | | ---------------- | ------ | -------- | ----------- | | `productOptions` | array | Yes | | | `travelDate` | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Availability status per product | | `400` | Missing required fields | --- ### Reserve inventory (V2) `POST /viator/v2/reserve` Hold inventory for 15+ minutes while Viator processes payment. **Request body** | Field | Type | Required | Description | | ----------------- | ------- | -------- | ----------- | | `productOptionId` | string | Yes | | | `travelDate` | string | Yes | | | `startTime` | string | No | | | `tickets` | array | No | | | `totalTravelers` | integer | No | | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Reservation created with expiration time | | `400` | Missing required fields | | `404` | Product not found | --- ### Confirm booking (V1) `POST /viator/booking` Confirm a previously reserved booking after Viator has processed payment. **Responses** | Code | Description | | ----- | ----------------------------- | | `200` | Booking confirmed or rejected | --- ### Cancel booking (V1) `POST /viator/booking-cancellation` Cancel a confirmed Viator booking. **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | Cancellation confirmed or rejected | --- ### Special offers (V2) `POST /viator/v2/product/special-offers` Returns active promotions and special offers. **Responses** | Code | Description | | ----- | ------------------------------------------------ | | `200` | List of special offers (currently returns empty) | --- ### Amend booking (V1) `POST /viator/booking-amendment` Modify an existing booking (date change, traveler details). **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Amendment confirmed or rejected | --- ### Unified dispatcher (all request types) `POST /viator/api` Single endpoint that accepts all Viator request types via a `requestType` discriminator. Auth is read from `data.ApiKey` in the request body instead of headers. **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `requestType` | string | Yes | | | `data` | object | Yes | | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Response from the dispatched handler | | `400` | Missing or unknown requestType | | `401` | Invalid or missing ApiKey in data | --- ## vQuip ### Check vQuip integration status `GET /vquip/status` Check if the current customer has an active vQuip integration. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Integration status retrieved successfully | | `400` | customerId is required | | `500` | Failed to check vQuip status | --- ### Connect vQuip integration `POST /vquip/connect` Save vQuip credentials, test the connection, and activate the integration. **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `clientId` | string | Yes | | | `clientSecret` | string | Yes | | | `tokenUrl` | string | Yes | | **Responses** | Code | Description | | ----- | ----------------------------------------------------- | | `200` | vQuip connected successfully | | `400` | Missing required parameters or connection test failed | | `500` | Failed to connect vQuip | --- ### Disconnect vQuip integration `DELETE /vquip/disconnect` Deactivate the vQuip integration for the customer. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | vQuip disconnected successfully | | `400` | customerId is required | | `500` | Failed to disconnect vQuip | --- ### Get vQuip inventory types `GET /vquip/inventory-types` Fetch all inventory types from the connected vQuip account. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Inventory types retrieved successfully | | `400` | vQuip not configured for this customer | | `500` | Failed to get inventory types | --- ### Get vQuip products `GET /vquip/products` Fetch all products from the connected vQuip account. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Products retrieved successfully | | `400` | vQuip not configured for this customer | | `500` | Failed to get products | --- ### Get vQuip reservations `GET /vquip/reservations` Fetch reservations from vQuip by external booking ID. **Parameters** | Name | In | Type | Required | Description | | ------------------- | ----- | ------ | -------- | --------------------------------------------- | | `customerId` | query | string | Yes | The customer ID | | `externalBookingId` | query | string | Yes | The RentalTide booking ID to look up in vQuip | **Responses** | Code | Description | | ----- | --------------------------------------------------- | | `200` | Reservations retrieved successfully | | `400` | Missing required parameters or vQuip not configured | | `500` | Failed to get reservations | --- ### Create a vQuip reservation `POST /vquip/reservations` Push a RentalTide booking to vQuip as a new reservation. **Request body** | Field | Type | Required | Description | | ------------------- | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `externalBookingId` | string | Yes | | | `startDate` | string | Yes | | | `endDate` | string | Yes | | | `productId` | string | Yes | | | `customerName` | string | Yes | | | `customerEmail` | string | No | | | `customerPhone` | string | No | | | `totalAmount` | number | No | | | `notes` | string | No | | **Responses** | Code | Description | | ----- | --------------------------------------------------- | | `201` | Reservation created successfully | | `400` | Missing required parameters or vQuip not configured | | `500` | Failed to create reservation | --- ### Update a vQuip reservation `PUT /vquip/reservations/{reservationId}` Update an existing reservation in vQuip. **Parameters** | Name | In | Type | Required | Description | | --------------- | ---- | ------ | -------- | ------------------------ | | `reservationId` | path | string | Yes | The vQuip reservation ID | **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `startDate` | string | No | | | `endDate` | string | No | | | `productId` | string | No | | | `customerName` | string | No | | | `customerEmail` | string | No | | | `customerPhone` | string | No | | | `totalAmount` | number | No | | | `notes` | string | No | | | `status` | string | No | | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Reservation updated successfully | | `400` | vQuip not configured for this customer | | `500` | Failed to update reservation | --- ### Cancel a vQuip reservation `PUT /vquip/reservations/{reservationId}/cancel` Cancel an existing reservation in vQuip. **Parameters** | Name | In | Type | Required | Description | | --------------- | ---- | ------ | -------- | ------------------------ | | `reservationId` | path | string | Yes | The vQuip reservation ID | **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `customerId` | string | Yes | | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Reservation cancelled successfully | | `400` | vQuip not configured for this customer | | `500` | Failed to cancel reservation | --- ### Create an external invoice on a vQuip reservation `POST /vquip/reservations/{reservationId}/invoice` Create an external invoice linked to a vQuip reservation. **Parameters** | Name | In | Type | Required | Description | | --------------- | ---- | ------ | -------- | ------------------------ | | `reservationId` | path | string | Yes | The vQuip reservation ID | **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `amount` | number | Yes | | | `description` | string | No | | **Responses** | Code | Description | | ----- | --------------------------------------------------- | | `201` | Invoice created successfully | | `400` | Missing required parameters or vQuip not configured | | `500` | Failed to create invoice | --- ### Sync a RentalTide booking to vQuip `POST /vquip/sync-booking` Load a booking and create vQuip reservations for each assigned asset with a product mapping. Creates an invoice on the first reservation if totalPrice > 0. **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `rentalId` | string | Yes | | | `customerId` | string | Yes | | | `locationId` | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Sync completed | | `400` | Missing parameters or vQuip not configured | | `500` | Sync failed | --- ## Wave Integration ### Initiate Wave OAuth2 flow `GET /auth/wave/connect` Generates an authorization URL to start the Wave OAuth2 connection process. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ---------------------------------- | | `businessId` | query | string | Yes | The business ID to connect Wave to | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Authorization URL generated successfully | | `400` | Business ID required | | `500` | Failed to initiate Wave connection | --- ### Handle Wave OAuth2 callback `GET /auth/wave/callback` Handles the OAuth2 callback from Wave, exchanges authorization code for tokens. **Parameters** | Name | In | Type | Required | Description | | ------- | ----- | ------ | -------- | ----------- | | `code` | query | string | Yes | | | `state` | query | string | Yes | | **Responses** | Code | Description | | ----- | -------------------------------------------- | | `302` | Redirects to frontend with connection status | --- ### Get Wave integration status `GET /wave/integration-status` **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------- | | `businessId` | query | string | Yes | | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Integration status retrieved successfully | --- ### Disconnect Wave integration `DELETE /wave/disconnect` Disconnects the Wave integration while preserving GL mappings. **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `businessId` | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Wave disconnected successfully | --- ### Fetch chart of accounts from Wave `GET /wave/chart-of-accounts` **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------- | | `businessId` | query | string | Yes | | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Chart of accounts fetched successfully | --- ### Get Wave businesses for the connected account `GET /wave/businesses` **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------- | | `businessId` | query | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Businesses fetched successfully | --- ### Select which Wave business to use `PUT /wave/select-business` **Request body** | Field | Type | Required | Description | | ------------------ | ------ | -------- | ----------- | | `businessId` | string | Yes | | | `waveBusinessId` | string | Yes | | | `waveBusinessName` | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Business selected successfully | --- ### Set bank account for invoice payments `PUT /wave/bank-account` **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ----------- | | `businessId` | string | Yes | | | `bankAccountId` | string | Yes | | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Bank account updated successfully | --- ### Get current GL code mappings `GET /wave/gl-mappings` **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------- | | `businessId` | query | string | Yes | | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | GL mappings retrieved successfully | --- ### Update GL code mappings `PUT /wave/gl-mappings` **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `businessId` | string | Yes | | | `glMappings` | object | Yes | | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | GL mappings updated successfully | --- ## Voice ### Handle incoming phone call `POST /communication/voice/incoming-call` Twilio webhook endpoint that handles incoming phone calls. Looks up the location by Twilio phone number, identifies the caller, and generates IVR menu response. **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | TwiML response with IVR menu | --- ### Handle IVR key press `POST /communication/voice/handle-key` Processes DTMF key presses from the IVR menu and executes the corresponding action (dial, voicemail, say message, etc.). **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------- | | `locationId` | query | string | Yes | The location ID | | `callSid` | query | string | Yes | The Twilio call SID | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | TwiML response with action result | --- ### Handle sequential dial progression `POST /communication/voice/sequential-dial-next` Handles the progression of sequential dialing when a number doesn't answer. Moves to the next number in the ring list. **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------- | -------- | ----------------------------------- | | `locationId` | query | string | Yes | The location ID | | `callSid` | query | string | Yes | The Twilio call SID | | `currentIndex` | query | integer | Yes | The current index in the ring list | | `ringList` | query | string | Yes | JSON-encoded array of phone numbers | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | TwiML response for next dial or message | --- ### Handle recording webhook from Twilio `POST /communication/voice/recording-webhook` Webhook endpoint that receives call recording notifications from Twilio. Processes and uploads recordings to S3. **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Recording processed successfully | | `400` | Missing required recording data | | `500` | Error processing recording | --- ### Handle voicemail recording completion `POST /communication/voice/handle-recording` Processes a completed voicemail recording and saves it to the database. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `locationId` | query | string | Yes | The location ID | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | TwiML response with thank you message | --- ### Get recorded calls with pagination `GET /communication/voice/recordings/{locationId}` Retrieves call recordings for a location with pagination and optional type filtering. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------- | -------- | --------------------------------- | | `locationId` | path | string | Yes | The location ID | | `page` | query | integer | No | Page number | | `limit` | query | integer | No | Number of items per page (max 50) | | `type` | query | string | No | Filter by recording type | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Recordings retrieved successfully | | `500` | Failed to fetch recordings | --- ### Get voicemails for a location `GET /communication/voice/voicemails/{locationId}` Legacy endpoint that retrieves voicemails for a specific location, sorted by timestamp descending. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `locationId` | path | string | Yes | The location ID | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Voicemails retrieved successfully | | `500` | Failed to fetch voicemails | --- ### Delete a voicemail `DELETE /communication/voice/voicemails/{voicemailId}` Permanently deletes a voicemail recording by its ID. **Parameters** | Name | In | Type | Required | Description | | ------------- | ---- | ------ | -------- | ---------------- | | `voicemailId` | path | string | Yes | The voicemail ID | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Voicemail deleted successfully | | `500` | Failed to delete voicemail | --- ### Test OpenAI voice generation `GET /communication/voice/test-openai-voice` Test endpoint to verify OpenAI voice generation is working correctly. **Parameters** | Name | In | Type | Required | Description | | ------- | ----- | ------ | -------- | ----------------------------- | | `text` | query | string | No | The text to convert to speech | | `voice` | query | string | No | The OpenAI voice to use | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Voice generated successfully | | `500` | OpenAI voice test failed | --- ### Generate voice preview for IVR configuration `GET /communication/voice/voice-preview/{voice}` Generates a preview of a specific OpenAI voice for IVR configuration purposes. **Parameters** | Name | In | Type | Required | Description | | ------- | ---- | ------ | -------- | ------------------------------------------------------ | | `voice` | path | string | Yes | The OpenAI voice identifier (e.g., alloy, echo, fable) | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Voice preview generated successfully | | `500` | Voice preview generation failed | --- ### Configure location to use OpenAI voice `POST /communication/voice/configure-openai/{locationId}` Updates a location's IVR configuration to use OpenAI voice engine with the specified voice. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `locationId` | path | string | Yes | The location ID | **Request body** | Field | Type | Required | Description | | ------- | ------ | -------- | ----------------------- | | `voice` | string | No | The OpenAI voice to use | **Responses** | Code | Description | | ----- | ------------------------------------------------- | | `200` | Location configured for OpenAI voice successfully | | `404` | Location not found | | `500` | Failed to configure OpenAI voice | --- ### Get call logs for a location with pagination `GET /communication/voice/logs/{locationId}` Retrieves call logs for a specific location with pagination support. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------- | -------- | --------------------------------- | | `locationId` | path | string | Yes | The location ID | | `page` | query | integer | No | Page number | | `limit` | query | integer | No | Number of items per page (max 50) | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Call logs retrieved successfully | | `500` | Failed to fetch call logs | --- ### Get all call logs (admin view) `GET /communication/voice/call-logs` Retrieves all call logs across all locations for admin purposes. **Parameters** | Name | In | Type | Required | Description | | ------- | ----- | ------- | -------- | -------------------------------- | | `limit` | query | integer | No | Maximum number of logs to return | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Call logs retrieved successfully | | `500` | Failed to fetch call logs | --- ### Handle call status webhook from Twilio `POST /communication/voice/call-status` Webhook endpoint that receives call status updates from Twilio and updates the call log. **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Call status update processed | | `500` | Error updating call status | --- ### Initiate callback call `POST /communication/voice/initiate-callback` Initiates a callback call where the user is called first, then connected to the destination number. Requires authentication. **Request body** | Field | Type | Required | Description | | ------------------ | ------ | -------- | ----------------------------------------------- | | `userPhone` | string | Yes | The user's phone number to call first | | `destinationPhone` | string | Yes | The destination phone number to connect to | | `locationId` | string | Yes | The location ID for caller ID and authorization | **Responses** | Code | Description | | ----- | -------------------------------------------------------- | | `200` | Callback call initiated successfully | | `400` | Missing required parameters | | `403` | Unauthorized - location doesn't belong to user's account | | `404` | Location not found | | `500` | Failed to initiate callback call | --- ### Handle callback call answer `POST /communication/voice/callback-answer` Twilio webhook called when the user answers the callback call. Dials the destination number. **Parameters** | Name | In | Type | Required | Description | | ------------------ | ----- | ------ | -------- | ------------------------------------------ | | `sessionId` | query | string | Yes | The callback session ID | | `destinationPhone` | query | string | Yes | The destination phone number to connect to | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | TwiML response to dial destination | --- ### Handle callback call completion `POST /communication/voice/callback-complete` Twilio webhook called when the callback call is completed. Updates call log with final status. **Parameters** | Name | In | Type | Required | Description | | ----------- | ----- | ------ | -------- | ----------------------- | | `sessionId` | query | string | Yes | The callback session ID | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | TwiML response with completion message | --- ### Search for customer by phone number `GET /communication/voice/search-renter/{phoneNumber}` Searches for a customer by phone number within the authenticated user's customer records. Requires authentication. **Parameters** | Name | In | Type | Required | Description | | ------------- | ---- | ------ | -------- | ------------------------------ | | `phoneNumber` | path | string | Yes | The phone number to search for | **Responses** | Code | Description | | ----- | ----------------------------- | | `200` | Search result | | `500` | Failed to search for customer | --- ## Shipments ### Create a new incoming shipment `POST /shipments` Creates a new incoming shipment record to track inventory deliveries. **Request body** | Field | Type | Required | Description | | --------------------- | ------ | -------- | ---------------------------------- | | `locationId` | string | Yes | The location ID for the shipment | | `expectedArrivalDate` | string | Yes | Expected arrival date (ISO string) | | `trackingNumber` | string | No | Shipping tracking number | | `notes` | string | No | Additional notes | | `items` | array | Yes | | | `shippingCost` | number | No | | | `importCost` | number | No | | | `taxes` | number | No | | **Responses** | Code | Description | | ----- | ----------------------------- | | `201` | Shipment created successfully | | `400` | Missing required fields | | `500` | Internal server error | --- ### List all shipments for a location `GET /shipments` Retrieves all shipments for a given location. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------------------------ | | `locationId` | query | string | Yes | The location ID to get shipments for | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Shipments retrieved successfully | | `400` | Missing locationId parameter | | `500` | Internal server error | --- ### Get a single shipment `GET /shipments/{shipmentId}` Retrieves details for a specific shipment. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `shipmentId` | path | string | Yes | The shipment ID | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Shipment retrieved successfully | | `400` | Missing shipmentId parameter | | `404` | Shipment not found | | `500` | Internal server error | --- ### Delete a shipment `DELETE /shipments/{shipmentId}` Deletes a shipment if it has not been marked as arrived/received. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `shipmentId` | path | string | Yes | The shipment ID | **Responses** | Code | Description | | ----- | ---------------------------------------------------- | | `200` | Shipment deleted successfully | | `400` | Missing shipmentId or cannot delete arrived shipment | | `404` | Shipment not found | | `500` | Internal server error | --- ### Mark shipment as arrived `PUT /shipments/{shipmentId}/arrived` Marks a shipment as arrived and updates inventory quantities accordingly. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `shipmentId` | path | string | Yes | The shipment ID | **Responses** | Code | Description | | ----- | ------------------------------------------------ | | `200` | Shipment marked as arrived and inventory updated | | `400` | Missing shipmentId or shipment already arrived | | `404` | Shipment not found | | `500` | Internal server error | --- --- # Inventory API Source: https://docs.rentaltide.com/api-reference/inventory/ > Manage your rental fleet, pricing, and product catalog Endpoints for managing inventory items, pricing rules, and inventory records. ## Inventory ### Upload inventory photo `POST /inventory/photo/{inventory_id}` Uploads and optimizes a photo for an inventory item, storing it in S3 **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | -------------------------------- | | `inventory_id` | path | string | Yes | The inventory ID to add photo to | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | Photo uploaded successfully | | `400` | Missing inventory_id or photo file | | `500` | Internal server error | --- ### Upload inventory video `POST /inventory/video/{inventory_id}` Uploads a video for an inventory item, storing it in S3 **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | -------------------------------- | | `inventory_id` | path | string | Yes | The inventory ID to add video to | **Responses** | Code | Description | | ----- | ------------------------------------------------------- | | `200` | Video uploaded successfully | | `400` | Missing inventory_id, video file, or unsupported format | | `500` | Internal server error | --- ### Save asset map configuration `POST /inventory/asset-map/{inventory_id}` Saves asset map pin configuration for an inventory item, optionally uploading a map image **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ------------------------------------------- | | `inventory_id` | path | string | Yes | The inventory ID to configure asset map for | **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Asset map configuration saved successfully | | `400` | Missing inventory_id or invalid pins data | | `403` | Unauthorized to update this inventory | | `404` | Inventory not found | | `500` | Internal server error | --- ### Create or update inventory settings `POST /inventory/setup` Creates or updates an inventory record with all settings including pricing, capacity, and asset pool configuration **Request body** | Field | Type | Required | Description | | ------ | ------ | -------- | ----------- | | `data` | object | Yes | | **Responses** | Code | Description | | ----- | ----------------------------------------------------- | | `200` | Inventory settings updated successfully | | `400` | Missing required data | | `403` | Unauthorized to update inventory for another customer | | `500` | Internal server error | --- ### Get inventory by customer `GET /inventory/customer` Retrieves all inventory items for a given customer ID **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ------------------------------------ | | `customer_id` | query | string | Yes | The customer ID to get inventory for | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Inventory items retrieved successfully | | `400` | Missing customer_id query parameter | | `500` | Internal server error | --- ### Delete an inventory item `DELETE /inventory/delete/{inventory_id}` Deletes an inventory item by ID **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | -------------------------- | | `inventory_id` | path | string | Yes | The inventory ID to delete | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Inventory deleted successfully | | `400` | Missing inventory_id param | | `403` | Unauthorized to delete this inventory | | `404` | Inventory not found | | `500` | Internal server error | --- ### Reorder inventory items `POST /inventory/reorder` Updates the display order of inventory items **Request body** | Field | Type | Required | Description | | -------------- | ----- | -------- | ----------- | | `orderedBoats` | array | Yes | | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Order updated successfully | | `400` | Invalid data - orderedBoats array is required | | `500` | Internal server error | --- ## Inventory Pricing ### Get inventory pricing `GET /inventory-pricing` Retrieves pricing configuration for an inventory item including seasons, hourly rates, and addons **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------ | -------- | -------------------------------------------------------------- | | `inventory_id` | query | string | Yes | The inventory ID to get pricing for (also accepts inventoryId) | **Responses** | Code | Description | | ----- | -------------------------------------------------------- | | `200` | Pricing data retrieved successfully | | `400` | Missing inventory_id query parameter or validation error | | `404` | No pricing found for the inventory | | `500` | Internal server error | --- ### Create or update inventory pricing `POST /inventory-pricing` Creates or updates pricing configuration for an inventory item including seasons, schedule exceptions, and addons **Request body** | Field | Type | Required | Description | | ------ | ------ | -------- | ----------- | | `data` | object | Yes | | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Inventory pricing updated successfully | | `400` | Missing required data or validation error | | `500` | Internal server error | --- ## Inventory Records ### Get inventory records `GET /boatRecords` Retrieves records for an inventory item, optionally filtered by record type **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ----------------------------------------------- | | `inventoryId` | query | string | Yes | The inventory ID to get records for | | `RecordType` | query | string | No | Filter by record type (e.g., checkout, checkin) | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Records retrieved successfully | | `400` | inventoryId is required | | `500` | Internal server error | --- ### Create an inventory record `POST /boatRecords` Creates a new record for an inventory item. May trigger Ship Shape AI analysis for check-in records with photos. **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------- | | `inventoryId` | string | Yes | The inventory ID | | `RecordType` | string | Yes | Type of record (checkout, checkin, etc.) | | `Details` | object | Yes | Record details | | `RentalId` | string | No | Associated rental ID | | `AssetId` | string | No | Associated asset ID | | `timestamp` | string | No | Record timestamp (defaults to current time) | **Responses** | Code | Description | | ----- | --------------------------- | | `201` | Record created successfully | | `400` | Missing required fields | | `500` | Internal server error | --- ### Update an inventory record `PATCH /boatRecords` Updates an existing record's Details attribute **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | --------------------------------------------- | | `inventoryId` | string | Yes | The inventory ID | | `timestamp` | string | Yes | Record timestamp (used as sort key) | | `Updates` | object | Yes | Fields to update within the Details attribute | **Responses** | Code | Description | | ----- | ------------------------------------------------ | | `200` | Record updated successfully | | `400` | inventoryId, timestamp, and Updates are required | | `500` | Internal server error | --- ### Delete an inventory record `DELETE /boatRecords` Deletes a record by inventoryId and timestamp **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------------------------------- | | `inventoryId` | string | Yes | The inventory ID | | `timestamp` | string | Yes | Record timestamp (used as sort key) | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Record deleted successfully | | `400` | inventoryId and timestamp are required | | `500` | Internal server error | --- ### Get paginated rundown records `GET /boatRecords/rundownRecords` Retrieves records for an inventory item with pagination support **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------- | -------- | ------------------------------------------ | | `inventoryId` | query | string | Yes | The inventory ID to get records for | | `limit` | query | integer | No | Number of items to return | | `nextToken` | query | string | No | URL-encoded pagination token for next page | **Responses** | Code | Description | | ----- | --------------------------------------------------- | | `200` | Records retrieved successfully with pagination info | | `400` | inventoryId is required | | `500` | Internal server error | --- --- # Locations API Source: https://docs.rentaltide.com/api-reference/locations/ > Manage business locations and their settings Endpoints for managing business locations, operating hours, and location-specific settings. ## Locations ### Get locations `GET /location` If inventoryId is provided, returns the location for that boat. Otherwise, returns all locations for the authenticated user's customer. **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | -------------------------------------------- | | `inventoryId` | query | string | No | Inventory ID to find the associated location | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | Location(s) retrieved successfully | | `401` | Unauthorized | | `404` | User, boat, or location not found | | `500` | Internal server error | --- ### Create a new location `POST /location` Creates a new location and sends a Slack notification. Automatically geocodes the address. **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | --------------------------- | | `address` | string | Yes | | | `phoneNumber` | string | Yes | | | `name` | string | Yes | | | `connectId` | string | No | Stripe Connect account ID | | `details` | object | No | Additional location details | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | Location created successfully | | `400` | Missing required fields | | `401` | Unauthorized | | `404` | User not found or customer ID not found | | `500` | Internal server error | --- ### Update a location `PATCH /location` Updates an existing location. If address is updated, automatically geocodes for new coordinates. **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ---------------------------- | | `locationId` | string | Yes | ID of the location to update | | `address` | string | No | | | `name` | string | No | | | `phoneNumber` | string | No | | | `details` | object | No | | **Responses** | Code | Description | | ----- | ------------------------------------------------------- | | `200` | Location updated successfully | | `400` | locationId is required or no valid attributes to update | | `500` | Internal server error | --- ### Schedule a location for deletion `DELETE /location` Soft-deletes a location by scheduling it for permanent deletion after 60 days. The location can be restored before the grace period expires. **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------- | | `locationId` | string | Yes | ID of the location to delete | | `customerId` | string | No | Customer ID (optional, for notification purposes) | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Location scheduled for deletion | | `400` | locationId is required | | `500` | Internal server error | --- ### Update location IVR configuration `PATCH /location/ivr` Updates the IVR (Interactive Voice Response) configuration for a location **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ---------------------------- | | `locationId` | string | Yes | ID of the location to update | | `ivrConfig` | object | Yes | IVR configuration object | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | IVR configuration updated successfully | | `400` | locationId and ivrConfig are required | | `500` | Internal server error | --- ### Restore a location scheduled for deletion `PATCH /location/restore` Cancels a pending scheduled deletion, restoring the location to active status. **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------- | | `locationId` | string | Yes | ID of the location to restore | **Responses** | Code | Description | | ----- | ---------------------------------------------------------------- | | `200` | Location restored successfully | | `400` | locationId is required or location is not scheduled for deletion | | `404` | Location not found | | `500` | Internal server error | --- --- # Marina API Source: https://docs.rentaltide.com/api-reference/marina/ > Haul services, meter readings, and marina maps Endpoints for managing marina-specific operations including haul-out services, utility meter readings, and interactive marina maps. ## Haul Services ### Get haul services for a location `GET /haul-services` **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------- | | `locationId` | query | string | Yes | | | `status` | query | string | No | | | `startDate` | query | string | No | | | `endDate` | query | string | No | | --- ### Request a new haul service `POST /haul-services` --- ### Get haul service statistics `GET /haul-services/stats` --- ### Get services scheduled for a specific date `GET /haul-services/scheduled/{date}` --- ### Get services for a specific renter `GET /haul-services/renter/{renterId}` --- ### Get services for a specific reservation `GET /haul-services/reservation/{reservationId}` --- ### Get a single haul service by ID `GET /haul-services/{id}` --- ### Update a haul service `PUT /haul-services/{id}` --- ### Delete a haul service (only if not started) `DELETE /haul-services/{id}` --- ### Schedule a haul service with pricing `POST /haul-services/{id}/schedule` --- ### Confirm a scheduled haul service `POST /haul-services/{id}/confirm` --- ### Start a haul service `POST /haul-services/{id}/start` --- ### Complete a haul service `POST /haul-services/{id}/complete` --- ### Cancel a haul service `POST /haul-services/{id}/cancel` --- ### Add photos to a haul service `POST /haul-services/{id}/photos` --- ## Meter Readings ### Get meter readings with filters `GET /meter-readings` --- ### Record a new meter reading `POST /meter-readings` --- ### Get meter reading statistics `GET /meter-readings/stats` --- ### Get unbilled readings for a location `GET /meter-readings/unbilled` --- ### Get latest reading for a unit `GET /meter-readings/unit/{unitId}/latest` --- ### Get reading history for a unit `GET /meter-readings/unit/{unitId}/history` --- ### Get a single meter reading `GET /meter-readings/{id}` --- ### Update a meter reading `PUT /meter-readings/{id}` --- ### Delete a meter reading `DELETE /meter-readings/{id}` --- ### Mark multiple readings as billed `POST /meter-readings/mark-billed` --- ### Add utility charges to a reservation `POST /meter-readings/add-to-reservation` --- ## Marina Maps ### Get all maps for a location `GET /slips/maps/{locationId}` --- ### Create a new marina map `POST /slips/maps` --- ### Update display order for multiple maps `PUT /slips/maps/reorder` --- ### Batch update pin positions `PUT /slips/maps/batch-pins` --- ### Get a single map by ID `GET /slips/maps/{mapId}` --- ### Update a marina map `PUT /slips/maps/{mapId}` --- ### Delete a marina map (unlinks all slips) `DELETE /slips/maps/{mapId}` --- ### Toggle map active status `PATCH /slips/maps/{mapId}/toggle-active` --- ### Get all pins (slips) for a specific map `GET /slips/maps/{mapId}/pins` --- ### Get slips not assigned to any map for a location `GET /slips/maps/{locationId}/unassigned` --- ### Assign or update a slip's map position `PUT /slips/{slipId}/map-pin` --- ### Update only the position of a slip pin `PATCH /slips/{slipId}/map-pin` --- ### Remove a slip from the map `DELETE /slips/{slipId}/map-pin` --- --- # Marketing API Source: https://docs.rentaltide.com/api-reference/marketing/ > Affiliates, cart recovery, and communications Endpoints for managing affiliate programs, cart recovery campaigns, and customer communications. ## Affiliates ### Check affiliate ID availability `GET /affiliate/check-availability` Check if a given affiliate ID is available for use **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ------------------------- | | `affiliateId` | query | string | Yes | The affiliate ID to check | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Availability check result | | `400` | Missing or invalid affiliate ID | | `500` | Server error | --- ### Create an affiliate `POST /affiliate` Create a new affiliate with optional custom affiliate ID **Request body** | Field | Type | Required | Description | | ------------------- | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `name` | string | Yes | | | `email` | string | Yes | | | `percentageEarned` | number | No | | | `customAffiliateId` | string | No | | **Responses** | Code | Description | | ----- | ----------------------------------------------- | | `201` | Affiliate created successfully | | `400` | Missing required fields or invalid affiliate ID | | `409` | Affiliate ID already exists | | `500` | Server error | --- ### Get affiliates for a customer `GET /affiliate` Retrieve all affiliates associated with a customer ID **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------------------------- | | `customerId` | query | string | Yes | The customer ID to get affiliates for | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | List of affiliates | | `400` | Missing customerId parameter | | `500` | Server error | --- ### Delete an affiliate `DELETE /affiliate` Delete an affiliate by their affiliate ID **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | -------------------------- | | `affiliateId` | query | string | Yes | The affiliate ID to delete | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Affiliate deleted successfully | | `400` | Missing affiliateId parameter | | `404` | Affiliate not found | | `500` | Server error | --- ### Update affiliate data `PATCH /affiliate` Perform various affiliate update operations based on action parameter **Parameters** | Name | In | Type | Required | Description | | -------- | ----- | ------ | -------- | --------------------- | | `action` | query | string | Yes | The action to perform | **Request body** | Field | Type | Required | Description | | ------------------ | ------ | -------- | -------------------------------------- | | `affiliateId` | string | No | | | `paidOut` | number | No | Amount for addPayout action | | `percentageEarned` | number | No | Percentage for editPercentage action | | `totalAmount` | number | No | Total amount for addACommission action | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Operation completed successfully | | `400` | Missing required fields or invalid action | | `404` | Affiliate not found | | `500` | Server error | --- ### Recalculate affiliate earnings `POST /affiliate/recalculate` Recalculates all affiliate earnings from bookings (excludes cancelled bookings) **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | -------------------------------------------------- | | `customerId` | string | Yes | Customer ID to recalculate affiliates for | | `affiliateId` | string | No | Optional - recalculate for specific affiliate only | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Recalculation completed successfully | | `400` | Missing required fields | | `500` | Server error | --- ## Cart Recovery ### Get cart recovery items for a location `GET /cartRecovery/{locationId}` Retrieves all abandoned cart items for the authenticated customer at a specific location **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------------------------------------- | | `locationId` | path | string | Yes | The location ID to filter cart recovery items | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | List of cart recovery items | | `400` | Missing required parameters | | `500` | Internal server error | --- ### Delete a cart recovery item `DELETE /cartRecovery/{id}` Deletes an abandoned cart item. Only the owner of the cart can delete it. **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ------------------------- | | `id` | path | string | Yes | The cart recovery item ID | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Item deleted successfully | | `400` | Missing required parameters | | `403` | Access denied - not the owner of the cart | | `404` | Item not found | --- ## Communications ### Send a templated communication `POST /communication/send-templated` Sends a templated email or SMS communication to recipients. If rentalId is provided, the recipient is determined from the rental's CustomerInfo. If both rentalId and recipientList are provided, all parties receive the communication. **Request body** | Field | Type | Required | Description | | ---------------- | ------- | -------- | ------------------------------------------------------------------------------------- | | `customerId` | string | Yes | The customer ID | | `locationId` | string | Yes | The location ID | | `templateId` | string | Yes | The template identifier (e.g., booking_confirmation) | | `rentalId` | string | No | The rental ID (optional, but either rentalId or recipientList is required) | | `recipientList` | array | No | List of email recipients (optional, but either rentalId or recipientList is required) | | `forceSendEmail` | boolean | No | Force sending via email | | `forceSendText` | boolean | No | Force sending via SMS | **Responses** | Code | Description | | ----- | ------------------------------------------- | | `200` | Communication sent successfully | | `400` | Missing required fields or validation error | | `500` | Server error | --- ### Send cart recovery email `POST /communication/send-cart-recovery-email` Sends a cart recovery email to a customer with optional custom message and booking information. Generates a short URL for tracking. **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ---------------------------------------------- | | `to` | string | Yes | Recipient email address | | `firstName` | string | No | Customer's first name | | `lastName` | string | No | Customer's last name | | `customMessage` | string | No | Optional custom message content | | `recoveryUrl` | string | Yes | The URL for the customer to recover their cart | | `bookingInfo` | object | No | | **Responses** | Code | Description | | ----- | ----------------------------------------------- | | `200` | Cart recovery email sent successfully | | `400` | Missing required fields or invalid email format | | `500` | Failed to send cart recovery email | --- ### Generate a short code for a URL `POST /communication/generate-short-code` Creates a shortened URL with optional metadata for tracking purposes. Returns the short URL and code with a 30-day expiration. **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | -------------------------------------------------- | | `originalUrl` | string | Yes | The original URL to shorten | | `metadata` | object | No | Optional metadata to associate with the short code | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Short code generated successfully | | `400` | Missing or invalid URL | | `500` | Failed to generate short code | --- ### Send an SMS message `POST /communication/send-sms` Sends an SMS message with enhanced error handling and international support. Validates phone number format and SMS service availability. **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------- | | `phoneNumber` | string | Yes | The recipient phone number | | `message` | string | Yes | The SMS message content | | `customerId` | string | No | The customer ID for tracking | | `locationId` | string | No | The location ID for tracking | | `country` | string | No | The country code for phone validation | **Responses** | Code | Description | | ----- | ------------------------------------------------- | | `200` | SMS sent successfully | | `400` | Invalid phone number or SMS service not available | | `500` | Failed to send SMS | --- ### Send waiver link via SMS `POST /communication/send-waiver-link` Sends a waiver link to a customer via SMS. Creates a short link for tracking and sends the SMS with a personalized message. **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | --------------------------------------------------------------------------- | | `phoneNumber` | string | Yes | The recipient phone number | | `message` | string | No | Optional custom message (default message will be generated if not provided) | | `waiverUrl` | string | Yes | The waiver URL (can be relative or absolute) | | `customerId` | string | No | The customer ID | | `transactionId` | string | No | The transaction ID | | `firstName` | string | No | Customer's first name for personalization | | `lastName` | string | No | Customer's last name | | `locationId` | string | No | The location ID | | `country` | string | No | The country code for phone validation | **Responses** | Code | Description | | ----- | ----------------------------------------------- | | `200` | Waiver link sent successfully | | `400` | Invalid phone number or missing required fields | | `500` | Failed to send waiver link | --- ### Mark cart recovery item as contacted `POST /communication/cartRecovery/{id}/contact` Updates a cart recovery item to mark it as contacted with the current timestamp. **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ------------------------- | | `id` | path | string | Yes | The cart recovery item ID | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Cart recovery item marked as contacted | | `400` | Missing cart recovery ID | | `404` | Cart recovery item not found | | `500` | Failed to mark as contacted | --- ### Get cart recovery items needing contact `GET /communication/cartRecovery/items-needing-contact` Returns active, uncontacted cart recovery items whose ContactOn time has passed. Used by the cart recovery Lambda. **Responses** | Code | Description | | ----- | -------------------------------------------- | | `200` | Items needing contact retrieved successfully | | `500` | Failed to retrieve items | --- ### Get cart recovery items for a location `GET /communication/cartRecovery/{locationId}` Retrieves cart recovery items for a specific location with optional filtering and statistics. **Parameters** | Name | In | Type | Required | Description | | ------------------ | ----- | ------ | -------- | ------------------------------------------------- | | `locationId` | path | string | Yes | The location ID | | `includeContacted` | query | string | No | Whether to include contacted items in the results | **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Cart recovery items retrieved successfully | | `400` | Missing location ID | | `500` | Failed to retrieve cart recovery items | --- ### Delete cart recovery item `DELETE /communication/cartRecovery/{id}` Permanently deletes a cart recovery item by ID. **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ------------------------- | | `id` | path | string | Yes | The cart recovery item ID | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | Cart recovery item deleted successfully | | `400` | Missing cart recovery ID | | `404` | Cart recovery item not found | | `500` | Failed to delete cart recovery item | --- ### Validate a phone number `POST /communication/validate-phone` Validates a phone number for SMS capability with enhanced international support. **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------- | | `phoneNumber` | string | Yes | The phone number to validate | | `country` | string | No | The country code for validation | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Phone validation result | | `400` | Missing phone number | | `500` | Failed to validate phone number | --- ### Process scheduled communications `POST /communication/process-scheduled` Cron job endpoint that processes pending scheduled communications. Should be called every 5 minutes to fetch pending communications, send them, and update their status. **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Scheduled communications processed | | `500` | Failed to process scheduled communications | --- ### Schedule a communication `POST /communication/schedule` Schedules a communication to be sent at a later time based on delay or trigger conditions. **Request body** | Field | Type | Required | Description | | ---------------- | ------- | -------- | --------------------------------------- | | `customerId` | string | Yes | The customer ID | | `locationId` | string | Yes | The location ID | | `templateId` | string | Yes | The template identifier | | `recipientEmail` | string | Yes | The recipient email address | | `rentalId` | string | No | Optional rental ID | | `delayMinutes` | integer | No | Delay in minutes before sending | | `trigger` | string | No | Trigger condition for the communication | | `conditions` | object | No | Additional conditions for sending | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Communication scheduled successfully | | `400` | Missing required fields | | `500` | Failed to schedule communication | --- ### Cancel a scheduled communication `DELETE /communication/schedule/{scheduleId}` Cancels a previously scheduled communication by its schedule ID. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | ---------------------------------------------- | | `scheduleId` | path | string | Yes | The schedule ID of the communication to cancel | **Responses** | Code | Description | | ----- | ---------------------------------------------- | | `200` | Scheduled communication cancelled successfully | | `400` | Missing schedule ID | | `500` | Failed to cancel scheduled communication | --- ### Get scheduled communications for a rental `GET /communication/schedule/rental/{rentalId}` Retrieves all scheduled communications associated with a specific rental. **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | ------------- | | `rentalId` | path | string | Yes | The rental ID | **Responses** | Code | Description | | ----- | ----------------------------------------------- | | `200` | Scheduled communications retrieved successfully | | `400` | Missing rental ID | | `500` | Failed to retrieve scheduled communications | --- ### Get default email template content `GET /communication/templates/defaults/{templateId}` Retrieves the default content for a specific email template by ID. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | ----------------------- | | `templateId` | path | string | Yes | The template identifier | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | Default template retrieved successfully | | `400` | Missing template ID | | `404` | Default template not found | | `500` | Failed to retrieve default template | --- ### Get all available default templates `GET /communication/templates/defaults` Retrieves a list of all available default email templates with their content. **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Default templates retrieved successfully | | `500` | Failed to retrieve default templates | --- --- # Memberships API Source: https://docs.rentaltide.com/api-reference/memberships/ > Manage membership programs, tiers, billing, and member access Endpoints for managing membership programs including tiers, billing, credits, family plans, organizations, public enrollment, and the member portal. ## Memberships ### Get all memberships `GET /memberships` Retrieves all memberships for a customer with pagination and optional status filtering **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------- | -------- | --------------------------- | | `customerId` | query | string | Yes | The customer ID | | `status` | query | string | No | Filter by membership status | | `tierId` | query | string | No | Filter by tier ID | | `page` | query | integer | No | Page number | | `limit` | query | integer | No | Items per page | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | Successfully retrieved memberships | | `400` | customerId is required | | `500` | Server error | --- ### Create a new membership `POST /memberships` Creates a new membership (staff-created). Can optionally create a new renter. **Request body** | Field | Type | Required | Description | | -------------- | ------- | -------- | --------------------------- | | `customerId` | string | Yes | | | `tierId` | string | Yes | | | `memberType` | string | No | | | `memberId` | string | No | | | `renterId` | string | No | Alias for memberId | | `memberEmail` | string | Yes | | | `memberName` | string | No | | | `memberPhone` | string | No | | | `billingCycle` | string | No | | | `staffId` | string | No | | | `notes` | string | No | | | `createRenter` | boolean | No | Create a new renter if true | | `firstName` | string | No | | | `lastName` | string | No | | **Responses** | Code | Description | | ----- | --------------------------------------------------------------- | | `201` | Membership created successfully | | `400` | Missing required fields or member already has active membership | | `404` | Tier not found | | `500` | Server error | --- ### Get a specific membership `GET /memberships/{membershipId}` Retrieves a specific membership with full details including tier and renter info **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Successfully retrieved membership | | `400` | customerId is required | | `404` | Membership not found | | `500` | Server error | --- ### Cancel a membership `POST /memberships/{membershipId}/cancel` Cancels a membership. Can cancel immediately or at period end. **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ------------------- | ------- | -------- | -------------------------------------------------------------- | | `customerId` | string | Yes | | | `cancelImmediately` | boolean | No | If true, cancels immediately. Otherwise cancels at period end. | **Responses** | Code | Description | | ----- | ------------------------------------------------- | | `200` | Membership canceled or scheduled for cancellation | | `400` | customerId is required | | `404` | Membership not found | | `500` | Server error | --- ### Reactivate a canceled membership `POST /memberships/{membershipId}/reactivate` Reactivates a membership that was canceled **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `customerId` | string | Yes | | **Responses** | Code | Description | | ----- | ---------------------- | | `200` | Membership reactivated | | `400` | customerId is required | | `500` | Server error | --- ### Pause a membership `POST /memberships/{membershipId}/pause` Pauses a membership. Staff can use force=true to bypass tier policy restrictions. **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ------------ | ------- | -------- | ------------------------------------- | | `customerId` | string | Yes | | | `resumeAt` | string | No | Optional date to auto-resume | | `force` | boolean | No | Bypass tier pause policy restrictions | **Responses** | Code | Description | | ----- | ----------------------------------------------------------------- | | `200` | Membership paused | | `400` | customerId is required or pause not enabled or max pauses reached | | `404` | Membership not found | | `500` | Server error | --- ### Resume a paused membership `POST /memberships/{membershipId}/resume` Resumes a membership that was paused **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `customerId` | string | Yes | | **Responses** | Code | Description | | ----- | -------------------------------------------------- | | `200` | Membership resumed | | `400` | customerId is required or membership is not paused | | `404` | Membership not found | | `500` | Server error | --- ### Get Stripe invoices and billing history for a membership `GET /memberships/{membershipId}/invoices` **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------- | -------- | ----------- | | `membershipId` | path | string | Yes | | | `customerId` | query | string | Yes | | | `limit` | query | integer | No | | **Responses** | Code | Description | | ----- | --------------------------------------------------- | | `200` | Successfully retrieved invoices and billing history | | `400` | customerId is required | | `404` | Membership not found | --- ## Memberships - Billing ### Add a free month to membership `POST /memberships/{membershipId}/billing/add-free-month` Extends the current period end date by one billing cycle without charging **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------- | | `customerId` | string | Yes | | | `reason` | string | No | Reason for adding free month (for audit purposes) | **Responses** | Code | Description | | ----- | ----------------------------- | | `200` | Free month added successfully | | `400` | customerId is required | | `404` | Membership not found | | `500` | Server error | --- ### Change billing date `POST /memberships/{membershipId}/billing/change-date` Changes the billing date by updating the current period end **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ---------------- | ------- | -------- | ---------------------------------------- | | `customerId` | string | Yes | | | `newBillingDate` | string | Yes | New billing date (must be in the future) | | `proRate` | boolean | No | Whether to pro-rate the price difference | | `reason` | string | No | | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Billing date changed successfully | | `400` | Invalid request | | `404` | Membership not found | | `500` | Server error | --- ### Switch billing cycle `POST /memberships/{membershipId}/billing/switch-cycle` Switches between monthly and yearly billing cycles with pro-rating **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ----------------- | ------- | -------- | ----------- | | `customerId` | string | Yes | | | `newBillingCycle` | string | Yes | | | `proRate` | boolean | No | | **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | Billing cycle switched successfully | | `400` | Invalid request | | `404` | Membership not found | | `500` | Server error | --- ### Change membership price `POST /memberships/{membershipId}/billing/change-price` Changes the recurring price for an individual membership **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ------------ | ------- | -------- | ------------------ | | `customerId` | string | Yes | | | `newPrice` | number | Yes | New price in cents | | `proRate` | boolean | No | | | `reason` | string | No | | **Responses** | Code | Description | | ----- | -------------------------- | | `200` | Price changed successfully | | `400` | Invalid request | | `404` | Membership not found | | `500` | Server error | --- ### Refund a membership invoice payment `POST /memberships/{membershipId}/refund` --- ### Resend a membership invoice receipt email `POST /memberships/{membershipId}/invoices/{invoiceId}/resend` --- ## Memberships - Credits ### Get credit balance and history `GET /memberships/{membershipId}/credits` Retrieves the credit balance and transaction history for a membership **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------- | -------- | ----------------------------------- | | `membershipId` | path | string | Yes | The membership ID | | `customerId` | query | string | Yes | The customer ID | | `limit` | query | integer | No | Number of history entries to return | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Successfully retrieved credits | | `400` | customerId is required | | `404` | Membership not found | | `500` | Server error | --- ### Adjust membership credits `POST /memberships/{membershipId}/credits/adjust` Manual credit adjustment for a membership (staff only) **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------------------------------- | | `customerId` | string | Yes | | | `amount` | number | Yes | Positive to add, negative to deduct | | `description` | string | Yes | Reason for the adjustment | | `staffId` | string | No | | **Responses** | Code | Description | | ----- | ----------------------------------------------------------- | | `200` | Credit adjustment successful | | `400` | Missing required fields or would result in negative balance | | `404` | Membership not found | | `500` | Server error | --- ## Memberships - Family ### Add a family member `POST /memberships/{membershipId}/family` Adds a family member to a membership **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `renterId` | string | Yes | | | `name` | string | No | | | `email` | string | Yes | | **Responses** | Code | Description | | ----- | ---------------------------------------------------------------------------------------- | | `200` | Family member added | | `400` | Missing required fields or family members not enabled or max reached or already a member | | `404` | Membership not found | | `500` | Server error | --- ### Remove a family member `DELETE /memberships/{membershipId}/family/{renterId}` Removes a family member from a membership **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------ | -------- | ----------------------- | | `membershipId` | path | string | Yes | The membership ID | | `renterId` | path | string | Yes | The renter ID to remove | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | -------------------------------------------------- | | `200` | Family member removed | | `400` | customerId is required | | `404` | Membership not found or renter not a family member | | `500` | Server error | --- ## Memberships - Organizations ### Get all organizations `GET /memberships/organizations` Retrieves all organizations for a customer **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Successfully retrieved organizations | | `400` | customerId is required | | `500` | Server error | --- ### Create an organization `POST /memberships/organizations` Creates a new organization for membership purposes **Request body** | Field | Type | Required | Description | | ---------------------- | ------- | -------- | ----------- | | `customerId` | string | Yes | | | `name` | string | Yes | | | `contactEmail` | string | Yes | | | `contactName` | string | Yes | | | `contactPhone` | string | No | | | `billingAddress` | object | No | | | `maxAuthorizedRenters` | integer | No | | **Responses** | Code | Description | | ----- | --------------------------------- | | `201` | Organization created successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Get organization by ID `GET /memberships/organizations/{orgId}` Retrieves a specific organization with its membership details **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------- | | `orgId` | path | string | Yes | The organization ID | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | Successfully retrieved organization | | `400` | customerId is required | | `404` | Organization not found | | `500` | Server error | --- ### Update an organization `PATCH /memberships/organizations/{orgId}` Updates an existing organization's details **Parameters** | Name | In | Type | Required | Description | | ------- | ---- | ------ | -------- | ------------------- | | `orgId` | path | string | Yes | The organization ID | **Request body** | Field | Type | Required | Description | | ---------------------- | ------- | -------- | ----------- | | `customerId` | string | Yes | | | `name` | string | No | | | `contactEmail` | string | No | | | `contactName` | string | No | | | `contactPhone` | string | No | | | `billingAddress` | object | No | | | `maxAuthorizedRenters` | integer | No | | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Organization updated successfully | | `400` | customerId is required | | `500` | Server error | --- ### Delete an organization `DELETE /memberships/organizations/{orgId}` Deletes an organization. Cannot delete if organization has an active membership. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------- | | `orgId` | path | string | Yes | The organization ID | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ------------------------------------------------------------ | | `200` | Organization deleted successfully | | `400` | customerId is required or organization has active membership | | `500` | Server error | --- ### Add authorized renter to organization `POST /memberships/organizations/{orgId}/members` Adds a renter to the organization's authorized renters list **Parameters** | Name | In | Type | Required | Description | | ------- | ---- | ------ | -------- | ------------------- | | `orgId` | path | string | Yes | The organization ID | **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `renterId` | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------------------------------------------------- | | `200` | Renter added to organization | | `400` | Missing required fields or renter already authorized or max limit reached | | `404` | Organization not found | | `500` | Server error | --- ### Get authorized renters for organization `GET /memberships/organizations/{orgId}/members` Retrieves all authorized renters for an organization with their details **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------- | | `orgId` | path | string | Yes | The organization ID | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Successfully retrieved members | | `400` | customerId is required | | `404` | Organization not found | | `500` | Server error | --- ### Remove authorized renter from organization `DELETE /memberships/organizations/{orgId}/members/{renterId}` Removes a renter from the organization's authorized renters list **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------- | | `orgId` | path | string | Yes | The organization ID | | `renterId` | path | string | Yes | The renter ID | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Renter removed from organization | | `400` | customerId is required | | `404` | Organization or renter not found | | `500` | Server error | --- ### Create membership for organization `POST /memberships/organizations/{orgId}/membership` Creates a membership for an organization **Parameters** | Name | In | Type | Required | Description | | ------- | ---- | ------ | -------- | ------------------- | | `orgId` | path | string | Yes | The organization ID | **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `tierId` | string | Yes | | | `billingCycle` | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------------------------------------------------------------------- | | `201` | Membership created successfully | | `400` | Missing required fields or tier doesn't support organizations or org already has membership | | `404` | Organization or tier not found | | `500` | Server error | --- ## Memberships - Tiers ### Get all membership tiers `GET /memberships/tiers` Retrieves all membership tiers for a customer, optionally filtered by active status **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------- | -------- | ------------------------ | | `customerId` | query | string | Yes | The customer ID | | `activeOnly` | query | boolean | No | Only return active tiers | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Successfully retrieved tiers | | `400` | customerId is required | | `500` | Server error | --- ### Create a new membership tier `POST /memberships/tiers` Creates a new membership tier with pricing and benefits **Request body** | Field | Type | Required | Description | | -------------------- | ------- | -------- | ----------- | | `customerId` | string | Yes | | | `name` | string | Yes | | | `description` | string | No | | | `scope` | string | No | | | `locationIds` | array | No | | | `pricing` | object | No | | | `benefits` | object | No | | | `pausePolicy` | object | No | | | `familyOptions` | object | No | | | `allowedMemberTypes` | array | No | | | `displayOrder` | integer | No | | **Responses** | Code | Description | | ----- | -------------------------------- | | `201` | Tier created successfully | | `400` | customerId and name are required | | `500` | Server error | --- ### Get a specific membership tier `GET /memberships/tiers/{tierId}` Retrieves a specific membership tier by ID **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `tierId` | path | string | Yes | The tier ID | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Successfully retrieved tier | | `400` | customerId is required | | `404` | Tier not found | | `500` | Server error | --- ### Update a membership tier `PATCH /memberships/tiers/{tierId}` Updates an existing membership tier **Parameters** | Name | In | Type | Required | Description | | -------- | ---- | ------ | -------- | ----------- | | `tierId` | path | string | Yes | The tier ID | **Request body** | Field | Type | Required | Description | | --------------- | ------- | -------- | ----------- | | `customerId` | string | Yes | | | `name` | string | No | | | `description` | string | No | | | `isActive` | boolean | No | | | `displayOrder` | integer | No | | | `pricing` | object | No | | | `benefits` | object | No | | | `pausePolicy` | object | No | | | `familyOptions` | object | No | | **Responses** | Code | Description | | ----- | --------------------------------------------------- | | `200` | Tier updated successfully | | `400` | customerId is required or no valid fields to update | | `500` | Server error | --- ### Deactivate a membership tier `DELETE /memberships/tiers/{tierId}` Soft-deletes (deactivates) a membership tier. Cannot delete if tier has active memberships. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `tierId` | path | string | Yes | The tier ID | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ----------------------------------------------------- | | `200` | Tier deactivated successfully | | `400` | customerId is required or tier has active memberships | | `500` | Server error | --- ## Memberships - Public ### Get public membership tiers for a location `GET /memberships/public/tiers/{locationId}` Retrieves active membership tiers for a location (no authentication required) **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `locationId` | path | string | Yes | The location ID | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Successfully retrieved tiers | | `404` | Location not found | | `500` | Server error | --- ### Create checkout session for membership signup `POST /memberships/public/checkout` Creates a Stripe checkout session for membership signup. If renter has existing payment method, creates subscription directly. **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | --------------------------- | | `customerId` | string | Yes | | | `tierId` | string | Yes | | | `billingCycle` | string | Yes | | | `renterEmail` | string | Yes | | | `renterName` | string | No | | | `renterPhone` | string | No | | | `renterId` | string | No | Optional existing renter ID | | `successUrl` | string | No | | | `cancelUrl` | string | No | | **Responses** | Code | Description | | ----- | ------------------------------------------------ | | `200` | Checkout session or subscription created | | `400` | Missing required fields or Stripe not configured | | `404` | Tier or location not found | | `500` | Server error | --- ### Check membership signup status `GET /memberships/public/status/{sessionId}` Check the status of a membership signup by checkout session ID **Parameters** | Name | In | Type | Required | Description | | ----------- | ---- | ------ | -------- | ------------------------------ | | `sessionId` | path | string | Yes | The Stripe checkout session ID | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Membership status retrieved | | `404` | Membership not found | | `500` | Server error | --- ## Member Portal ### Get membership details `GET /portal/membership` Returns the current member's membership details, credits balance, tier information, and business info. Requires OTP session authentication. **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------ | -------- | ----------------- | | `x-otp-session` | header | string | Yes | OTP session token | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Membership details retrieved successfully | | `401` | Unauthorized - invalid or missing OTP session | | `404` | Membership not found | | `500` | Internal server error | --- ### Get credit transaction history `GET /portal/membership/credits` Returns the member's credit transaction history, sorted by most recent first **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------- | -------- | ---------------------------------------- | | `x-otp-session` | header | string | Yes | OTP session token | | `limit` | query | integer | No | Maximum number of transactions to return | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Credit transactions retrieved successfully | | `401` | Unauthorized - invalid or missing OTP session | | `500` | Internal server error | --- ### Pause membership `POST /portal/membership/pause` Pauses the member's subscription if the tier's pause policy allows it. Pauses the Stripe subscription collection. **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------ | -------- | ----------------- | | `x-otp-session` | header | string | Yes | OTP session token | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Membership paused successfully | | `400` | Pausing not allowed or membership not active | | `401` | Unauthorized - invalid or missing OTP session | | `404` | Membership not found | | `500` | Internal server error | --- ### Resume paused membership `POST /portal/membership/resume` Resumes a paused membership and reactivates Stripe subscription collection **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------ | -------- | ----------------- | | `x-otp-session` | header | string | Yes | OTP session token | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Membership resumed successfully | | `400` | Membership is not paused | | `401` | Unauthorized - invalid or missing OTP session | | `404` | Membership not found | | `500` | Internal server error | --- ### Cancel membership `POST /portal/membership/cancel` Schedules the membership for cancellation at the end of the current billing period. Respects minimum commitment periods. **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------ | -------- | ----------------- | | `x-otp-session` | header | string | Yes | OTP session token | **Request body** | Field | Type | Required | Description | | -------- | ------ | -------- | ---------------------------- | | `reason` | string | No | Optional cancellation reason | **Responses** | Code | Description | | ----- | ------------------------------------------------------------------------- | | `200` | Membership scheduled for cancellation | | `400` | Cancellation not allowed, already canceled, or minimum commitment not met | | `401` | Unauthorized - invalid or missing OTP session | | `404` | Membership not found | | `500` | Internal server error | --- ### Get payment management link `GET /portal/membership/payment` Returns a Stripe Customer Portal URL where the member can update their payment method **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------ | -------- | ----------------- | | `x-otp-session` | header | string | Yes | OTP session token | **Responses** | Code | Description | | ----- | -------------------------------------------------------------- | | `200` | Portal URL generated successfully | | `400` | No payment information found or payment management unavailable | | `401` | Unauthorized - invalid or missing OTP session | | `500` | Internal server error | --- ### Get member's bookings `GET /portal/bookings` Returns the member's booking history with optional filtering by status **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------- | -------- | ------------------------------------ | | `x-otp-session` | header | string | Yes | OTP session token | | `limit` | query | integer | No | Maximum number of bookings to return | | `status` | query | string | No | Filter bookings by status | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Bookings retrieved successfully | | `401` | Unauthorized - invalid or missing OTP session | | `500` | Internal server error | --- ### Get booking details `GET /portal/bookings/{rentalId}` Returns detailed information about a specific booking **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------ | -------- | --------------------- | | `x-otp-session` | header | string | Yes | OTP session token | | `rentalId` | path | string | Yes | The booking rental ID | **Responses** | Code | Description | | ----- | --------------------------------------------------- | | `200` | Booking details retrieved successfully | | `401` | Unauthorized - invalid or missing OTP session | | `403` | Access denied - booking belongs to different member | | `404` | Booking not found | | `500` | Internal server error | --- ### Get saved payment methods `GET /portal/payment-methods` Returns the member's saved payment methods (credit cards) from Stripe **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------ | -------- | ----------------- | | `x-otp-session` | header | string | Yes | OTP session token | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Payment methods retrieved successfully | | `401` | Unauthorized - invalid or missing OTP session | | `500` | Internal server error | --- --- # Payments API Source: https://docs.rentaltide.com/api-reference/payments/ > Manage promo codes, gift cards, and protection plans Endpoints for promotional codes, gift card management, and protection plan configuration. ## Promo & Gift Cards ### Get promo/gift cards for a customer `GET /promoGiftCards` Returns all promo codes and gift cards for a customer, or a specific one if promoCode is provided **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------------------------------------ | | `customerId` | query | string | Yes | The customer ID to retrieve promo/gift cards for | | `promoCode` | query | string | No | Optional specific promo code to retrieve | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | Promo/gift cards retrieved successfully | | `400` | customerId is required | | `500` | Internal server error | --- ### Create a new promo/gift card `POST /promoGiftCards` Creates a new promo code or gift card. Requires either $Discount or %Discount in Details. **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------ | | `promoCode` | string | Yes | | | `customerId` | string | Yes | | | `ExpiryDate` | string | Yes | | | `Details` | object | Yes | | | `inventoryIds` | array | No | | | `locationId` | string | No | | | `affiliateId` | string | No | Optional affiliate association | **Responses** | Code | Description | | ----- | ------------------------------------------- | | `201` | Promo/gift card created successfully | | `400` | Missing required fields or invalid discount | | `409` | Promo code already exists for this customer | | `500` | Internal server error | --- ### Update a promo/gift card `PATCH /promoGiftCards` Updates an existing promo/gift card. Supports updating affiliateId and discount fields. **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ---------------- | | `promoCode` | string | Yes | | | `customerId` | string | Yes | | | `Updates` | object | Yes | Fields to update | **Responses** | Code | Description | | ----- | -------------------------------------------- | | `200` | Promo/gift card updated successfully | | `400` | Missing required fields or invalid affiliate | | `404` | Promo code not found for this customer | | `500` | Internal server error | --- ### Delete a promo/gift card `DELETE /promoGiftCards` Deletes a promo/gift card by promo code and customer ID **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------------ | | `promoCode` | query | string | Yes | The promo code to delete | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Promo/gift card deleted successfully | | `400` | promoCode and customerId are required | | `404` | Promo code not found for this customer | | `500` | Internal server error | --- ### Search promo codes across all customers `GET /promoGiftCards/search` Uses GSI to find all instances of a promo code across customers (for admin purposes) **Parameters** | Name | In | Type | Required | Description | | ----------- | ----- | ------ | -------- | ---------------------------- | | `promoCode` | query | string | Yes | The promo code to search for | **Responses** | Code | Description | | ----- | --------------------- | | `200` | Search results | | `400` | promoCode is required | | `500` | Internal server error | --- ### Check gift card balance `GET /promoGiftCards/balance` Check the remaining balance of a gift card or usage info for a promo code **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | -------------------------------------- | | `promoCode` | query | string | Yes | The promo/gift card code to check | | `customerId` | query | string | No | Optional customer ID for faster lookup | **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Balance information retrieved successfully | | `400` | promoCode is required | | `404` | Gift card or promo code not found | | `500` | Internal server error | --- ### Apply a promo code `POST /promoGiftCards/use` Tracks promo code usage and affiliate commissions when a promo code is applied **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------- | | `promoCode` | string | Yes | | | `customerId` | string | Yes | The customer who owns the promo code | | `orderAmount` | number | Yes | The order amount to apply the discount to | | `userId` | string | No | Optional - the user applying the promo code | **Responses** | Code | Description | | ----- | ------------------------------------------------------------------- | | `200` | Promo code applied successfully | | `400` | Missing required fields, expired promo code, or usage limit reached | | `404` | Promo code not found for this customer | | `500` | Internal server error | --- ### Get promo codes for an affiliate `GET /promoGiftCards/affiliate/{affiliateId}` Retrieves all promo codes associated with a specific affiliate and analytics **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ---------------- | | `affiliateId` | path | string | Yes | The affiliate ID | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | -------------------------------------------- | | `200` | Affiliate promo codes retrieved successfully | | `400` | customerId is required or invalid affiliate | | `500` | Internal server error | --- ## Public Gift Cards ### Create payment intent for gift card `POST /public/gift-card/create-payment-intent` Creates a Stripe payment intent for purchasing a gift card **Request body** | Field | Type | Required | Description | | ---------------------- | ------ | -------- | -------------------------------------- | | `amount` | number | Yes | Total amount including fees | | `giftCardAmount` | number | No | Gift card value amount | | `customerId` | string | Yes | | | `giftCardCode` | string | Yes | | | `recipientName` | string | No | | | `recipientEmail` | string | No | | | `message` | string | No | | | `expiryDate` | string | No | | | `currency` | string | No | | | `connectAccountId` | string | No | Stripe Connect account ID for transfer | | `applicationFeeAmount` | number | No | Platform fee amount | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Payment intent created successfully | | `400` | Missing required fields or invalid amount | | `500` | Internal server error | --- ### Confirm gift card payment `POST /public/gift-card/confirm-payment` Confirms payment and creates the gift card after successful Stripe payment **Request body** | Field | Type | Required | Description | | ----------------- | ------ | -------- | --------------------------------------- | | `paymentIntentId` | string | Yes | The Stripe payment intent ID to confirm | **Responses** | Code | Description | | ----- | ---------------------------------------------------- | | `201` | Gift card created successfully | | `400` | Payment intent ID required or payment not successful | | `409` | Gift card code already exists | | `500` | Internal server error | --- ### Stripe webhook handler `POST /public/gift-card/webhook` Stripe webhook endpoint to handle payment events for gift cards (backup verification) **Responses** | Code | Description | | ----- | ------------------------------------------- | | `200` | Webhook received and processed | | `400` | Webhook not configured or invalid signature | --- ### Send gift card email `POST /public/gift-card/send-email` Sends a gift card via email to the recipient with redemption instructions **Request body** | Field | Type | Required | Description | | ---------------- | ------ | -------- | ----------- | | `giftCardCode` | string | Yes | | | `giftCardAmount` | number | Yes | | | `recipientEmail` | string | Yes | | | `recipientName` | string | No | | | `message` | string | No | | | `expiryDate` | string | No | | | `customerId` | string | Yes | | **Responses** | Code | Description | | ----- | ----------------------------------------------- | | `200` | Email sent successfully | | `400` | Missing required fields or invalid email format | | `500` | Email service error or failed to send | --- ### Charge saved card for gift card `POST /public/gift-card/charge-saved-card` Charges a saved payment method for gift card purchase **Request body** | Field | Type | Required | Description | | ---------------------- | ------ | -------- | -------------------------------------- | | `paymentMethodId` | string | Yes | Stripe payment method ID | | `amount` | number | Yes | | | `currency` | string | No | | | `customerId` | string | Yes | | | `stripeCustomerId` | string | No | | | `description` | string | No | | | `metadata` | object | No | | | `connectAccountId` | string | No | Stripe Connect account ID for transfer | | `applicationFeeAmount` | number | No | Platform fee amount | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Payment processed successfully | | `400` | Missing required fields or invalid amount | | `500` | Internal server error | --- ## Protection Plans ### Get available protection plans `GET /api/v2/protection-plans` Returns protection plans for a specific industry **Parameters** | Name | In | Type | Required | Description | | ----------------- | ----- | ------- | -------- | -------------------------------------- | | `industry` | query | string | No | Industry type (defaults to marine) | | `locationId` | query | string | No | Location ID to filter by enabled plans | | `includeInactive` | query | boolean | No | Include inactive plans (admin only) | **Responses** | Code | Description | | ----- | ------------------------ | | `200` | List of protection plans | | `500` | Server error | --- ### Get available protection bundles `GET /api/v2/protection-bundles` Returns protection bundles for a specific industry **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ---------------------------------------- | | `industry` | query | string | No | Industry type (defaults to marine) | | `locationId` | query | string | No | Location ID to filter by enabled bundles | **Responses** | Code | Description | | ----- | -------------------------- | | `200` | List of protection bundles | | `500` | Server error | --- ### Get a specific protection plan by code `GET /api/v2/protection-plans/{planCode}` **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | ----------- | | `planCode` | path | string | Yes | | **Responses** | Code | Description | | ----- | ----------------------- | | `200` | Protection plan details | | `404` | Plan not found | | `500` | Server error | --- ### Get a specific protection bundle by code with included plans `GET /api/v2/protection-bundles/{bundleCode}` **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | ----------- | | `bundleCode` | path | string | Yes | | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | Protection bundle details with included plans | | `404` | Bundle not found | | `500` | Server error | --- ### Get protection settings for a location `GET /api/v2/locations/{locationId}/protection-settings` **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | ----------- | | `locationId` | path | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Location protection settings | | `404` | Settings not found (returns defaults) | | `500` | Server error | --- ### Update protection settings for a location `PUT /api/v2/locations/{locationId}/protection-settings` **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | ----------- | | `locationId` | path | string | Yes | | **Request body** | Field | Type | Required | Description | | -------------------- | ------ | -------- | ----------- | | `industry` | string | No | | | `enabledPlanCodes` | array | No | | | `enabledBundleCodes` | array | No | | | `planOverrides` | object | No | | **Responses** | Code | Description | | ----- | ----------------------------- | | `200` | Settings updated successfully | | `400` | Invalid request | | `500` | Server error | --- ### Enable a plan for a location `POST /api/v2/locations/{locationId}/protection-settings/plan/{planCode}` **Responses** | Code | Description | | ----- | ------------ | | `200` | Plan enabled | --- ### Disable a plan for a location `DELETE /api/v2/locations/{locationId}/protection-settings/plan/{planCode}` **Responses** | Code | Description | | ----- | ------------- | | `200` | Plan disabled | --- ### Enable a bundle for a location `POST /api/v2/locations/{locationId}/protection-settings/bundle/{bundleCode}` --- ### Disable a bundle for a location `DELETE /api/v2/locations/{locationId}/protection-settings/bundle/{bundleCode}` --- ### Set pricing override for a plan at a location `PUT /api/v2/locations/{locationId}/protection-settings/override/{planCode}` **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `percentage` | number | No | | | `maxCap` | number | No | | --- ### Remove pricing override for a plan at a location `DELETE /api/v2/locations/{locationId}/protection-settings/override/{planCode}` --- --- # Point of Sale API Source: https://docs.rentaltide.com/api-reference/pos/ > Process retail and rental transactions at the register Endpoints for managing the point-of-sale system including products, categories, transactions, and register operations. ## Point of Sale ### Get POS inventory items `GET /pos/inventory` Retrieves all POS inventory items for a location **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | -------------------------------- | | `locationId` | query | string | Yes | Location ID to get inventory for | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | List of inventory items | | `400` | Missing locationId parameter | | `500` | Server error | --- ### Create inventory item `POST /pos/inventory` Creates a new POS inventory item **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `name` | string | Yes | | | `category` | string | No | | | `taxRate` | number | No | | | `image` | string | No | | | `variants` | array | No | | | `tags` | array | No | | **Responses** | Code | Description | | ----- | ------------------------- | | `201` | Item created successfully | | `400` | Invalid request body | | `500` | Server error | --- ### Update inventory item `PUT /pos/inventory` Updates an existing POS inventory item **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `itemId` | string | Yes | | | `name` | string | No | | | `category` | string | No | | | `taxRate` | number | No | | | `variants` | array | No | | **Responses** | Code | Description | | ----- | ------------------------- | | `200` | Item updated successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Update variant quantity `PATCH /pos/inventory` Updates the quantity of a specific variant in an inventory item **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | --------------------------------- | | `locationId` | string | Yes | | | `itemId` | string | Yes | | | `variantId` | string | Yes | | | `quantity` | number | Yes | Quantity change (can be negative) | **Responses** | Code | Description | | ----- | ------------------------- | | `200` | Variant quantity updated | | `400` | Missing required fields | | `404` | Item or variant not found | | `500` | Server error | --- ### Delete inventory item `DELETE /pos/inventory` Deletes a POS inventory item **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `itemId` | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------- | | `200` | Item deleted successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Get current gas price `GET /pos/gas-price` Retrieves the current gas price for a location **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | -------------------------------- | | `locationId` | query | string | Yes | Location ID to get gas price for | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Gas price returned successfully | | `400` | Missing locationId parameter | | `404` | Gas item not found | | `500` | Server error | --- ### Update till-specific inventory `PATCH /pos/inventory/till` Updates inventory quantity for a specific till/register **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `itemId` | string | Yes | | | `tillId` | string | Yes | | | `variantId` | string | Yes | | | `quantity` | number | Yes | | **Responses** | Code | Description | | ----- | ----------------------- | | `200` | Till inventory updated | | `400` | Missing required fields | | `404` | Item not found | | `500` | Server error | --- ### Get folders `GET /pos/folders` Retrieves all product folders for a location **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------- | | `locationId` | query | string | Yes | Location ID | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | List of folders | | `400` | Missing locationId parameter | | `500` | Server error | --- ### Create folder `POST /pos/folders` Creates a new product folder **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `name` | string | Yes | | | `color` | string | No | | | `tillId` | string | No | | **Responses** | Code | Description | | ----- | --------------------------- | | `201` | Folder created successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Update folder `PUT /pos/folders` Updates an existing product folder **Request body** | Field | Type | Required | Description | | ------------ | ------- | -------- | ----------- | | `locationId` | string | Yes | | | `folderId` | string | Yes | | | `name` | string | No | | | `color` | string | No | | | `favorite` | boolean | No | | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Folder updated successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Delete folder `DELETE /pos/folders` Deletes a product folder and removes folder association from all products in it **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `folderId` | string | Yes | | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Folder deleted successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Reorder folders `PUT /pos/folders/reorder` Updates the sort order of folders **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `folderOrder` | array | Yes | | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Folders reordered successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Duplicate folders `POST /pos/folders/duplicate` Duplicates folders from a source till to a target till **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `sourceTillId` | string | Yes | | | `targetTillId` | string | Yes | | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Folders duplicated successfully | | `400` | Missing required fields | | `404` | No folders found for source till | | `500` | Server error | --- ### List transactions `GET /pos/transaction` Retrieves all POS transactions for a location, optionally filtered by till **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | -------------------------------- | | `locationId` | query | string | Yes | Location ID | | `tillId` | query | string | No | Filter by specific till/register | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | List of transactions | | `400` | Missing locationId parameter | | `500` | Server error | --- ### Create transaction `POST /pos/transaction` Creates a new POS transaction with items, discounts, taxes, and payment information **Request body** | Field | Type | Required | Description | | -------------------- | ------- | -------- | --------------------------------- | | `items` | array | Yes | Array of items in the transaction | | `locationId` | string | Yes | | | `userId` | string | Yes | | | `paymentType` | string | No | | | `accountId` | string | No | | | `discountPercentage` | number | No | | | `discountAmount` | number | No | | | `settleBooking` | boolean | No | | **Responses** | Code | Description | | ----- | -------------------------------- | | `201` | Transaction created successfully | | `400` | Invalid request body | | `500` | Server error | --- ### Get transaction by ID `GET /pos/transaction/{transactionId}` Retrieves a specific POS transaction by its ID **Parameters** | Name | In | Type | Required | Description | | --------------- | ---- | ------ | -------- | -------------- | | `transactionId` | path | string | Yes | Transaction ID | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Transaction details | | `400` | Missing transactionId parameter | | `404` | Transaction not found | | `500` | Server error | --- ### Get all POS transactions for a rental `GET /pos/transactions/rental/{rentalId}` Retrieves all POS transactions associated with a specific rental/booking ID **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | List of transactions for the rental | | `400` | Missing rental ID | | `500` | Server error | --- ### Refund transaction `POST /pos/transaction/refund` Processes a full or partial refund for a POS transaction **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ---------------------------- | | `transactionId` | string | Yes | | | `refundType` | string | Yes | | | `amount` | number | No | Required for partial refunds | **Responses** | Code | Description | | ----- | ---------------------------------------------- | | `200` | Refund processed successfully | | `400` | Invalid request or transaction already settled | | `404` | Transaction not found | | `500` | Server error | --- ### Settle single transaction `PUT /pos/transaction/settle` Marks a single transaction as settled **Parameters** | Name | In | Type | Required | Description | | --------------- | ----- | ------ | -------- | ------------------------ | | `transactionId` | query | string | Yes | Transaction ID to settle | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Transaction settled successfully | | `400` | Missing transactionId or already settled | | `404` | Transaction not found | | `500` | Server error | --- ### Settle transactions in date range `PUT /pos/transaction/settle-range` Settles all transactions within a date range, typically used for cash drawer closing **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ----------- | | `locationId` | string | Yes | | | `startDate` | string | No | | | `endDate` | string | No | | | `drawerId` | string | No | | | `countedCash` | number | No | | | `expectedCash` | number | No | | | `difference` | number | No | | | `cashBreakdown` | object | No | | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Transactions settled successfully | | `400` | Missing locationId | | `500` | Server error | --- ### Create Stripe payment intent `POST /pos/create_payment` Creates a Stripe payment intent for card-present payments **Request body** | Field | Type | Required | Description | | ---------- | ------- | -------- | --------------- | | `amount` | integer | Yes | Amount in cents | | `currency` | string | No | | **Responses** | Code | Description | | ----- | ---------------------- | | `200` | Payment intent created | | `400` | Error creating payment | --- ### Create Stripe account link `POST /pos/account_link` Creates a Stripe Connect account onboarding link **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------ | | `account` | string | Yes | Connected account ID | | `stripeRegion` | string | No | Stripe region (US or CA) | **Responses** | Code | Description | | ----- | ------------------------- | | `200` | Account link URL returned | | `500` | Server error | --- ### Charge saved customer `POST /pos/charge_customer` Charges a saved customer using their stored payment method **Request body** | Field | Type | Required | Description | | -------------------- | ------- | -------- | --------------- | | `amount` | integer | Yes | Amount in cents | | `currency` | string | No | | | `stripe_customer_id` | string | Yes | | | `payment_method` | string | Yes | | | `fee_amount` | number | No | | | `connect_id` | string | No | | **Responses** | Code | Description | | ----- | ------------------ | | `200` | Payment successful | | `500` | Server error | --- ### Create Stripe Connect account `POST /pos/account` Creates a new Stripe Express connected account **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | ----------- | | `country` | string | No | | | `stripeRegion` | string | No | | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Account created successfully | | `500` | Server error | --- ### Create Stripe account session `POST /pos/account_session` Creates a Stripe account session for embedded Connect components **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | -------------------- | | `account` | string | Yes | Connected account ID | | `stripeRegion` | string | No | | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Account session client secret returned | | `400` | Missing account parameter | | `500` | Server error | --- ### Create Stripe Express Dashboard login link `POST /pos/dashboard_link` Creates a login link for the connected account to access their full Stripe Dashboard with reports and analytics **Request body** | Field | Type | Required | Description | | -------------- | ------ | -------- | -------------------- | | `account` | string | Yes | Connected account ID | | `stripeRegion` | string | No | | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Dashboard login URL returned | | `400` | Missing account parameter | | `500` | Server error | --- ### Create terminal connection token `POST /pos/connection_token` Creates a Stripe Terminal connection token for reader initialization **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Connection token secret returned | | `500` | Server error | --- ### Create payment intent for reader `POST /pos/create_payment_intent` Creates a Stripe payment intent for terminal reader payments with support for connected accounts **Request body** | Field | Type | Required | Description | | ------------------------- | ------- | -------- | --------------- | | `amount` | integer | Yes | Amount in cents | | `currency` | string | No | | | `connected_account_id` | string | No | | | `fee_in_cents` | integer | No | | | `use_destination_charges` | boolean | No | | **Responses** | Code | Description | | ----- | ---------------------- | | `200` | Payment intent created | | `500` | Server error | --- --- # Public API Source: https://docs.rentaltide.com/api-reference/public/ > Public-facing endpoints for booking widgets and customer self-service Public endpoints that power the customer-facing booking widget, cancellation flows, participant management, and membership enrollment. These endpoints do not require admin authentication. ## Public API ### Get customer inventory data `GET /public/customer` Retrieves all inventory items for a customer including locations, pricing, and bookings. Response is AES encrypted. **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | --------------- | | `customer_id` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ----------------------------- | | `200` | Encrypted inventory data | | `400` | Missing customer_id parameter | | `500` | Server error | --- ### Get minimal inventory info for embed widget `GET /public/widget-info` Returns unencrypted minimal boat data (name, image, price, capacity, category) for use by the embed-booking.js widget on third-party sites. **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------ | -------- | ----------- | | `customer_id` | query | string | Yes | | | `inventory_id` | query | string | Yes | | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Minimal inventory info | | `400` | Missing required parameters | | `404` | Inventory not found | --- ### Get booking details `GET /public/booking` Retrieves details for a specific booking by transaction ID **Parameters** | Name | In | Type | Required | Description | | --------------- | ----- | ------ | -------- | ------------------------- | | `TransactionId` | query | string | Yes | The rental/transaction ID | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Booking details | | `400` | Missing TransactionId parameter | | `500` | Server error | --- ### Update booking `PATCH /public/booking` Updates booking fields using dynamic field paths **Request body** | Field | Type | Required | Description | | ---------- | ------ | -------- | ----------------------------------------------------------- | | `rentalId` | string | Yes | | | `data` | object | No | Key-value pairs of fields to update (supports dot notation) | **Responses** | Code | Description | | ----- | ------------------------ | | `200` | Updated booking data | | `400` | Missing data or rentalId | | `500` | Server error | --- ### Get order with all line items `GET /public/order/{orderId}` Resolves orderId as bookingId, bundleGroupId, or single rentalId and returns grouped line items **Parameters** | Name | In | Type | Required | Description | | --------- | ---- | ------ | -------- | ---------------------------------------------------- | | `orderId` | path | string | Yes | The order ID (bookingId, bundleGroupId, or rentalId) | **Responses** | Code | Description | | ----- | --------------------- | | `200` | Order with line items | | `404` | Order not found | | `500` | Server error | --- ### Check asset availability `GET /public/check-availability` Checks if a specific asset is available during a time range **Parameters** | Name | In | Type | Required | Description | | ----------------- | ----- | ------- | -------- | -------------------------------------------- | | `inventoryId` | query | string | Yes | The inventory ID | | `assetId` | query | string | Yes | The asset ID | | `startUtc` | query | integer | Yes | Start time as Unix timestamp | | `endUtc` | query | integer | Yes | End time as Unix timestamp | | `excludeRentalId` | query | string | No | Rental ID to exclude from availability check | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Availability status | | `400` | Missing required parameters | | `500` | Server error | --- ### Get available staff `GET /public/available-staff` Retrieves staff members available for a location with a specific skill during a time range **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | -------------------------------------------------------- | | `locationId` | query | string | Yes | The location ID | | `skill` | query | string | Yes | The required skill (coach, captain, trainer, operations) | | `startTime` | query | string | Yes | Start time in ISO format | | `endTime` | query | string | Yes | End time in ISO format | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | List of available staff | | `400` | Missing required parameters | | `500` | Server error | --- ### Get available staff for date range `GET /public/available-staff-range` Retrieves staff availability by skill for each day in a date range **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------------ | | `locationId` | query | string | Yes | The location ID | | `start` | query | string | Yes | Start date in ISO format | | `end` | query | string | Yes | End date in ISO format | **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | Staff availability by day and skill | | `400` | Missing required parameters | | `500` | Server error | --- ### Add to waitlist `POST /public/waitlist` Adds a customer to the waitlist for an inventory item **Request body** | Field | Type | Required | Description | | -------------- | ------- | -------- | ----------- | | `firstName` | string | Yes | | | `lastName` | string | Yes | | | `email` | string | Yes | | | `date` | string | Yes | | | `time` | string | No | | | `duration` | integer | No | | | `inventoryId` | string | Yes | | | `locationId` | string | Yes | | | `customerId` | string | No | | | `addons` | array | No | | | `boatAddons` | array | No | | | `giftCardCode` | string | No | | | `refAffiliate` | string | No | | **Responses** | Code | Description | | ----- | ----------------------------------- | | `201` | Waitlist entry created successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Save cart recovery data `POST /public/cartRecovery` Saves or updates cart recovery data for abandoned cart emails **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `id` | string | Yes | | | `firstName` | string | No | | | `lastName` | string | No | | | `email` | string | Yes | | | `bookingInfo` | object | No | | | `locationId` | string | Yes | | | `cartData` | object | No | | | `customerId` | string | No | | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Cart recovery data saved | | `400` | Missing required fields or invalid email | | `500` | Server error | --- ### Get cart recovery data `GET /public/cartRecovery/{id}` Retrieves cart recovery data by stable ID **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | -------------------- | | `id` | path | string | Yes | The cart recovery ID | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Cart recovery data | | `400` | Recovery ID required | | `404` | Cart recovery data not found | | `500` | Server error | --- ### Mark cart recovery as completed `DELETE /public/cartRecovery/{id}` Marks a cart recovery record as completed (booking was finished) **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | -------------------- | | `id` | path | string | Yes | The cart recovery ID | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Cart recovery marked as completed | | `400` | Recovery ID required | | `500` | Server error | --- ### Get cart recovery analytics `GET /public/cartRecovery/analytics/{locationId}` Retrieves cart recovery analytics for a location **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------------- | | `locationId` | path | string | Yes | The location ID | | `startDate` | query | string | No | Start date filter | | `endDate` | query | string | No | End date filter | **Responses** | Code | Description | | ----- | -------------- | | `200` | Analytics data | | `500` | Server error | --- ### Get monthly availability `GET /public/availability` Retrieves availability for each day in a month for an inventory item **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ------------------------------ | | `inventoryId` | query | string | Yes | The inventory ID | | `locationId` | query | string | Yes | The location ID | | `month` | query | string | Yes | Month in YYYY-MM format | | `timezone` | query | string | No | Timezone for date calculations | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Daily availability for the month | | `400` | Missing required parameters | | `404` | Inventory or location not found | | `500` | Server error | --- ### Get custom code integration `GET /public/getCustomCode` Returns custom code integration settings for a customer. Returns 204 if not configured. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Custom code configuration | | `204` | Custom code not configured | | `400` | Missing customerId parameter | | `500` | Server error | --- ### Get Google Tag Manager configuration `GET /public/getGTM` Returns GTM configuration for a customer. Returns 204 if not configured. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | GTM configuration | | `204` | GTM not configured | | `400` | Missing customerId parameter | | `500` | Server error | --- ### Get Meta Conversion API configuration `GET /public/getMetaConversion` Returns Meta Conversion API (Facebook Pixel) configuration. Returns 204 if not configured. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Meta Conversion configuration | | `204` | Meta Conversion not configured | | `400` | Missing customerId parameter | | `500` | Server error | --- ### Get TikTok Pixel configuration `GET /public/getTikTokPixel` Returns TikTok Pixel configuration for a customer. Returns 204 if not configured. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | TikTok Pixel configuration | | `204` | TikTok Pixel not configured | | `400` | Missing customerId parameter | | `500` | Server error | --- ### Get WhatsApp Business configuration `GET /public/getWhatsApp` Returns WhatsApp Business configuration for a customer **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | --------------- | | `customerId` | query | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | WhatsApp Business configuration | | `400` | Missing customerId parameter | | `404` | Customer not found or WhatsApp not configured | | `500` | Server error | --- ### Add protection plan to booking `POST /public/bookings/{rentalId}/protection-plans` Adds a protection plan to an existing booking and charges the customer **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Request body** | Field | Type | Required | Description | | ---------------- | ------ | -------- | ----------- | | `planId` | string | Yes | | | `planName` | string | Yes | | | `percentage` | number | No | | | `minimum` | number | No | | | `price` | number | No | | | `bookingTotal` | number | No | | | `calculatedCost` | number | Yes | | **Responses** | Code | Description | | ----- | -------------------------------------------- | | `200` | Protection plan added successfully | | `400` | Missing required fields or no payment method | | `404` | Booking not found | | `500` | Server error | --- ### Add a tip to a booking `POST /public/bookings/{rentalId}/tip` Processes a tip for a completed booking using the customer's stored payment method **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Request body** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------------------------------- | | `amount` | number | Yes | The tip amount in the booking's currency | | `percentage` | number | No | The tip percentage (if calculated from booking total) | **Responses** | Code | Description | | ----- | --------------------------------- | | `200` | Tip processed successfully | | `400` | Invalid request or payment failed | | `404` | Booking not found | | `500` | Server error | --- ### Get Apple Wallet pass data `GET /public/bookings/{rentalId}/apple-wallet` Generates Apple Wallet pass data including QR code for a booking **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Responses** | Code | Description | | ----- | ---------------------- | | `200` | Apple Wallet pass data | | `404` | Booking not found | | `500` | Server error | --- ### Download Apple Wallet pass file `GET /public/bookings/{rentalId}/apple-wallet/download` Downloads the Apple Wallet pass file (.pkpass) for a booking **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | PKPass file download | | `404` | Booking not found | | `500` | Server error | | `501` | Apple Wallet not configured | --- ### Get Google Wallet pass data `GET /public/bookings/{rentalId}/google-wallet` Generates Google Wallet pass save URL for a booking **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Responses** | Code | Description | | ----- | ---------------------- | | `200` | Google Wallet save URL | | `404` | Booking not found | | `500` | Server error | --- ### Get detailed day availability `GET /public/day-availability` Retrieves detailed availability for a specific day including time slots and bookings **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------ | -------- | ------------------------------ | | `inventoryId` | query | string | Yes | The inventory ID | | `locationId` | query | string | Yes | The location ID | | `date` | query | string | Yes | Date in YYYY-MM-DD format | | `timezone` | query | string | No | Timezone for date calculations | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Day availability details | | `400` | Missing required parameters | | `404` | Inventory or location not found | | `500` | Server error | --- ### Get time slot popularity data `GET /public/time-slot-popularity` Returns historical booking popularity data for time slots. Used for "Best Selling" badges. **Parameters** | Name | In | Type | Required | Description | | ------------- | ----- | ------- | -------- | --------------------------------------- | | `inventoryId` | query | string | Yes | The inventory ID | | `locationId` | query | string | Yes | The location ID | | `dayOfWeek` | query | string | No | Day of week for day-specific popularity | | `duration` | query | integer | No | Rental duration in hours | **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Time slot popularity data | | `400` | Missing required parameters | | `404` | Location not found | | `500` | Server error | --- ### Fetch reviews for a location `POST /public/locations/{locationId}/reviews` Public endpoint to fetch reviews from connected platforms (Google, TripAdvisor) for a location **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `locationId` | path | string | Yes | The location ID | **Request body** | Field | Type | Required | Description | | ------------------- | ----- | -------- | ----------- | | `reviewConnections` | array | Yes | | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Reviews from connected platforms | | `400` | Missing reviewConnections array | | `500` | Server error | --- ### Upload document for booking `POST /public/bookings/{rentalId}/documents` Upload insurance or ownership documents for a booking. No authentication required. **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Document uploaded successfully | | `400` | Missing or invalid parameters | | `404` | Booking not found | | `500` | Server error | --- ### AI customer assistant `POST /public/customer-sidekick` Customer-facing AI assistant for booking-specific questions **Request body** | Field | Type | Required | Description | | ---------- | ------ | -------- | ----------- | | `rentalId` | string | Yes | | | `message` | string | Yes | | **Responses** | Code | Description | | ----- | ----------------------- | | `200` | AI response | | `400` | Missing required fields | | `500` | Server error | --- ### AI boat recommendation engine `POST /public/boat-concierge` AI-powered boat recommendation engine to help customers find the perfect boat **Request body** | Field | Type | Required | Description | | --------------------- | ------ | -------- | ----------- | | `customerId` | string | Yes | | | `message` | string | Yes | | | `conversationHistory` | array | No | | | `selectedDate` | string | No | | | `locationId` | string | No | | **Responses** | Code | Description | | ----- | -------------------------- | | `200` | AI recommendation response | | `400` | Missing required fields | | `500` | Server error | --- ### Get public customer settings `GET /public/customer/{customerId}/public` Get public customer settings (loyalty, etc.) for checkout **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `customerId` | path | string | Yes | The customer ID | **Responses** | Code | Description | | ----- | ------------------------ | | `200` | Public customer settings | | `400` | Missing customerId | | `404` | Customer not found | | `500` | Server error | --- ### Get saved payment methods `GET /public/payment-methods` Get saved payment methods for a Stripe customer **Parameters** | Name | In | Type | Required | Description | | ------------------ | ----- | ------ | -------- | ---------------------- | | `stripeCustomerId` | query | string | Yes | The Stripe customer ID | **Responses** | Code | Description | | ----- | ------------------------ | | `200` | List of payment methods | | `400` | Missing stripeCustomerId | | `500` | Server error | --- ## Public Cancellation ### Request cancellation OTP `POST /public/bookings/{rentalId}/cancellation-otp` Request an OTP code for booking cancellation. Sends OTP to the booking's email address. **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Responses** | Code | Description | | ----- | --------------------------------------------- | | `200` | OTP sent successfully | | `400` | Missing rentalId or no valid email on booking | | `404` | Booking not found | | `500` | Server error | --- ### Verify cancellation OTP `POST /public/bookings/{rentalId}/verify-cancellation-otp` Verify OTP and return a session token for cancellation **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Request body** | Field | Type | Required | Description | | ----------- | ------ | -------- | ----------- | | `sessionId` | string | Yes | | | `code` | string | Yes | | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | OTP verified successfully | | `400` | Invalid or expired session/code | | `500` | Server error | --- ### Check cancellation eligibility `GET /public/bookings/{rentalId}/cancellation-eligibility` Check if a booking is eligible for self-service cancellation. No authentication required. **Parameters** | Name | In | Type | Required | Description | | ---------- | ---- | ------ | -------- | --------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Cancellation eligibility status | | `400` | Missing rentalId | | `404` | Booking not found | | `500` | Server error | --- ### Cancel booking `POST /public/bookings/{rentalId}/cancel` Customer self-service cancellation endpoint. Requires OTP verification via X-OTP-Session header. **Parameters** | Name | In | Type | Required | Description | | --------------- | ------ | ------ | -------- | ----------------------------------- | | `rentalId` | path | string | Yes | The rental/booking ID | | `X-OTP-Session` | header | string | Yes | OTP session token from verification | **Request body** | Field | Type | Required | Description | | -------- | ------ | -------- | ----------------------- | | `reason` | string | No | Reason for cancellation | **Responses** | Code | Description | | ----- | ----------------------------------------------------- | | `200` | Booking cancelled successfully | | `400` | Missing rentalId or booking already cancelled | | `401` | Invalid or expired session | | `403` | Not permitted to cancel or cancellation window closed | | `404` | Booking not found | | `500` | Server error | --- ## Public Participants ### Get booking participants `GET /public/booking/participants` Get all participants for a booking with their waiver status **Parameters** | Name | In | Type | Required | Description | | ---------- | ----- | ------ | -------- | --------------------- | | `rentalId` | query | string | Yes | The rental/booking ID | **Responses** | Code | Description | | ----- | --------------------------------------- | | `200` | List of participants with waiver status | | `400` | Missing rentalId parameter | | `404` | Booking not found | | `500` | Server error | --- ### Add or update participant `POST /public/booking/participants` Add a new participant or update an existing one for a booking **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `rentalId` | string | Yes | | | `participant` | object | Yes | | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Participant added/updated successfully | | `400` | Missing required fields | | `404` | Booking not found | | `500` | Server error | --- ### Update participant `PUT /public/booking/participants` Update an existing participant for a booking **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `rentalId` | string | Yes | | | `participant` | object | Yes | | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Participant updated successfully | | `400` | Missing required fields or participant ID | | `404` | Booking or participant not found | | `500` | Server error | --- ### Delete participant `DELETE /public/booking/participants/{participantId}` Remove a participant from a booking **Parameters** | Name | In | Type | Required | Description | | --------------- | ---- | ------ | -------- | ---------------------------- | | `participantId` | path | string | Yes | The participant ID to delete | **Request body** | Field | Type | Required | Description | | ---------- | ------ | -------- | ----------- | | `rentalId` | string | Yes | | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Participant deleted successfully | | `400` | Missing required fields | | `404` | Booking not found | | `500` | Server error | --- ### Send waiver email `POST /public/booking/send-waiver-email` Send a waiver completion email to a participant **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ----------- | | `rentalId` | string | No | | | `participantId` | string | No | | | `email` | string | Yes | | | `firstName` | string | No | | | `lastName` | string | No | | | `waiverUrl` | string | Yes | | | `locationId` | string | No | | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Waiver email sent successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Send waiver SMS `POST /public/booking/send-waiver-sms` Send a waiver completion SMS to a participant **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ----------- | | `rentalId` | string | No | | | `participantId` | string | No | | | `phoneNumber` | string | Yes | | | `firstName` | string | No | | | `lastName` | string | No | | | `waiverUrl` | string | Yes | | | `locationId` | string | No | | **Responses** | Code | Description | | ----- | ---------------------------- | | `200` | Waiver SMS sent successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Process participant payment `POST /public/booking/process-payment` Process a payment for a participant's share of the booking **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ----------- | | `rentalId` | string | Yes | | | `participantId` | string | Yes | | | `paymentSplit` | object | Yes | | **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | Payment processed successfully | | `400` | Missing required fields | | `404` | Booking or participant not found | | `500` | Server error | --- ### Create payment intent `POST /public/booking/create-payment-intent` Create a Stripe payment intent for a participant payment **Request body** | Field | Type | Required | Description | | ----------------- | ------ | -------- | --------------- | | `rentalId` | string | Yes | | | `participantId` | string | Yes | | | `paymentMethodId` | string | Yes | | | `amount` | number | Yes | Amount in cents | | `paymentSplit` | object | Yes | | **Responses** | Code | Description | | ----- | ----------------------------------------- | | `200` | Payment intent created successfully | | `400` | Missing required fields or payment failed | | `404` | Booking or participant not found | | `500` | Server error | --- ## Public Memberships ### Get public membership tiers `GET /public/memberships/tiers/{locationId}` Get public membership tiers for a location (no auth required). Only returns tiers marked as showToPublic=true. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------- | | `locationId` | path | string | Yes | The location ID | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | List of public membership tiers | | `404` | Location not found | | `500` | Server error | --- ### Create membership checkout session `POST /public/memberships/checkout` Create a Stripe checkout session for membership signup (no auth required) **Request body** | Field | Type | Required | Description | | -------------- | ------- | -------- | ----------- | | `customerId` | string | Yes | | | `tierId` | string | Yes | | | `billingCycle` | string | Yes | | | `renterEmail` | string | Yes | | | `renterName` | string | No | | | `renterPhone` | string | No | | | `renterId` | string | No | | | `successUrl` | string | No | | | `cancelUrl` | string | No | | | `locationId` | string | No | | | `isInternal` | boolean | No | | **Responses** | Code | Description | | ----- | --------------------------------------------------------- | | `200` | Checkout session created or membership activated directly | | `400` | Missing required fields or tier not available | | `404` | Tier or location not found | | `500` | Server error | --- ### Get membership details `GET /public/memberships/details/{membershipId}` Get membership details by membership ID (for success page after direct subscription) **Parameters** | Name | In | Type | Required | Description | | -------------- | ---- | ------ | -------- | ----------------- | | `membershipId` | path | string | Yes | The membership ID | **Responses** | Code | Description | | ----- | -------------------- | | `200` | Membership details | | `404` | Membership not found | | `500` | Server error | --- ### Check membership status `GET /public/memberships/status/{sessionId}` Check status of a membership signup by checkout session ID **Parameters** | Name | In | Type | Required | Description | | ----------- | ---- | ------ | -------- | ------------------------------ | | `sessionId` | path | string | Yes | The Stripe checkout session ID | **Responses** | Code | Description | | ----- | -------------------- | | `200` | Membership status | | `404` | Membership not found | | `500` | Server error | --- ### Activate membership `POST /public/memberships/activate` Activate a membership after successful Stripe checkout (no auth required). Called from the success page after Stripe redirects back. **Request body** | Field | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------ | | `sessionId` | string | Yes | The Stripe checkout session ID | **Responses** | Code | Description | | ----- | ------------------------------------------ | | `200` | Membership activated successfully | | `400` | Missing sessionId or payment not completed | | `404` | Membership not found for this session | | `500` | Server error | --- ## Scanner ### Scan a license `GET /scanner/scan` Triggers the license scanner and parses the scanned license data. **Responses** | Code | Description | | ----- | ------------------------------------------- | | `200` | License scanned successfully | | `404` | No license scanned or scanner not triggered | | `500` | Scanner error | --- --- # Slips API Source: https://docs.rentaltide.com/api-reference/slips/ > Manage marina slips, reservations, contracts, and tenant access Endpoints for managing marina slip inventory, reservations, contracts, waitlists, guest access, subscriptions, and tenant portal operations. ## Slips ### Get all slips for a location `GET /slips` **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------- | | `locationId` | query | string | Yes | | | `status` | query | string | No | | | `unitType` | query | string | No | | | `section` | query | string | No | | --- ### Create a new slip `POST /slips` Creates a new slip. Requires an active slip subscription. If adding this slip exceeds the current quota, additional slips ($10/slip/month) will be automatically billed. --- ### Get available slips matching criteria `GET /slips/availability` --- ### Get distinct sections for a location `GET /slips/sections` --- ### Get slip status counts for a location `GET /slips/stats` --- ### Search slips by unit number or name `GET /slips/search` --- ### Bulk create slips with a prefix and number range `POST /slips/bulk-create` --- ### Bulk update multiple slips `PUT /slips/bulk` --- ### Rename a section across all slips `PUT /slips/sections/rename` --- ### Get sections with slip counts for a location `GET /slips/sections/counts` --- ### Delete a section (clear section from all slips) `DELETE /slips/sections/{name}` --- ### Get a single slip by ID `GET /slips/{id}` --- ### Update a slip `PUT /slips/{id}` --- ### Delete a slip (soft delete by setting status to out_of_service) `DELETE /slips/{id}` --- ### Update slip status `PATCH /slips/{id}/status` --- ## Slip Reservations ### Get reservations with filters `GET /slip-reservations` --- ### Create a new reservation `POST /slip-reservations` --- ### Get reservation statistics `GET /slip-reservations/stats` --- ### Get reservations due for billing `GET /slip-reservations/due-billing` --- ### Get reservations expiring soon `GET /slip-reservations/expiring` --- ### Check if a unit is available for dates `GET /slip-reservations/check-availability` --- ### Get reservation by reservation number `GET /slip-reservations/by-number/{reservationNumber}` --- ### Get a single reservation by ID `GET /slip-reservations/{id}` --- ### Update a reservation `PUT /slip-reservations/{id}` --- ### Permanently delete a cancelled reservation and all associated data `DELETE /slip-reservations/{id}` --- ### Recalculate totalAmount based on current pricing fields `POST /slip-reservations/{id}/recalculate-total` --- ### Confirm a pending reservation `POST /slip-reservations/{id}/confirm` --- ### Send bill to customer with hold timer `POST /slip-reservations/{id}/send-bill` --- ### Check in a reservation `POST /slip-reservations/{id}/check-in` --- ### Check out a reservation `POST /slip-reservations/{id}/check-out` --- ### Cancel a reservation `POST /slip-reservations/{id}/cancel` --- ### Transfer reservation to a different unit `POST /slip-reservations/{id}/transfer` --- ### Mark contract as signed `POST /slip-reservations/{id}/contract-signed` --- ### Send a message to slip reservation customer (staff) `POST /slip-reservations/{id}/messages` --- ### Get messages for a slip reservation (staff) `GET /slip-reservations/{id}/messages` --- ### Record a payment for a slip reservation `POST /slip-reservations/{id}/record-payment` --- ### Get payment history for a slip reservation `GET /slip-reservations/{id}/payments` --- ### Charge a saved credit card for a slip reservation `POST /slip-reservations/{id}/charge-card` --- ### Record a terminal or tap-to-pay payment for a slip reservation `POST /slip-reservations/{id}/terminal-charge` --- ### Recognize deferred revenue for a slip reservation `POST /slip-reservations/{id}/recognize-revenue` --- ### Refund a slip reservation payment `POST /slip-reservations/{id}/refund` --- ## Slip Contracts ### Get contract templates for a location `GET /slip-contracts/templates` --- ### Create a new contract template `POST /slip-contracts/templates` --- ### Get available template variables `GET /slip-contracts/templates/variables` --- ### Get a specific contract template `GET /slip-contracts/templates/{id}` --- ### Update a contract template `PUT /slip-contracts/templates/{id}` --- ### Delete a contract template `DELETE /slip-contracts/templates/{id}` --- ### Duplicate a contract template (including defaults) `POST /slip-contracts/templates/{id}/duplicate` --- ### Get all contracts for a reservation `GET /slip-contracts/reservation/{reservationId}` --- ### Get reservations with pending contracts `GET /slip-contracts/pending` --- ### Get reservations with signed contracts `GET /slip-contracts/signed` --- ### Get contract statistics `GET /slip-contracts/stats` --- ### Generate a contract for a reservation `POST /slip-contracts/generate` --- ### Send contract for signature `POST /slip-contracts/send` --- ### Mark contract as signed `POST /slip-contracts/sign` --- ### Resend contract for signature `POST /slip-contracts/resend` --- ### Void a contract `POST /slip-contracts/void` --- ### Regenerate PDF for a signed contract `POST /slip-contracts/regenerate-pdf` --- ## Slip Waitlist ### Get waitlist entries for a location `GET /slips/waitlist` **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------- | | `locationId` | query | string | Yes | | | `status` | query | string | No | | --- ### Add to waitlist `POST /slips/waitlist` --- ### Get waitlist statistics for a location `GET /slips/waitlist/stats` --- ### Get waitlist entries for a specific renter `GET /slips/waitlist/renter/{renterId}` --- ### Get a single waitlist entry `GET /slips/waitlist/{id}` --- ### Update a waitlist entry `PUT /slips/waitlist/{id}` --- ### Delete a waitlist entry `DELETE /slips/waitlist/{id}` --- ### Make an offer to a waitlist entry `POST /slips/waitlist/{id}/offer` --- ### Accept an offer `POST /slips/waitlist/{id}/accept` --- ### Decline an offer `POST /slips/waitlist/{id}/decline` --- ### Cancel a waitlist entry `POST /slips/waitlist/{id}/cancel` --- ### Find matching waitlist entries for an available unit `GET /slips/waitlist/match/{unitId}` --- ## Slip Guest Access ### Get guest access entries for a location `GET /slips/guest-access` **Parameters** | Name | In | Type | Required | Description | | --------------- | ----- | ------ | -------- | ----------- | | `locationId` | query | string | Yes | | | `reservationId` | query | string | No | | | `status` | query | string | No | | --- ### Create a new guest access pass `POST /slips/guest-access` --- ### Get guest access statistics `GET /slips/guest-access/stats` --- ### Verify a guest access code `GET /slips/guest-access/verify/{accessCode}` --- ### Get a single guest access entry `GET /slips/guest-access/{id}` --- ### Update a guest access entry `PUT /slips/guest-access/{id}` --- ### Delete a guest access entry `DELETE /slips/guest-access/{id}` --- ### Record a guest check-in `POST /slips/guest-access/{id}/check-in` --- ### Mark guest ID as verified `POST /slips/guest-access/{id}/verify-id` --- ### Revoke guest access `POST /slips/guest-access/{id}/revoke` --- ### Expire old guest passes for a location `POST /slips/guest-access/expire` --- ## Slip Settings ### Get slip booking settings for a location `GET /slip-settings/{locationId}` Returns the slip booking configuration with defaults merged --- ### Update slip booking settings for a location `PUT /slip-settings/{locationId}` --- ### Get available contract templates for a location `GET /slip-settings/{locationId}/templates` --- ### Reset slip booking settings to defaults `POST /slip-settings/{locationId}/reset` --- ## Slip Subscription ### Get current slip subscription status `GET /slip-subscription/status` --- ### Create Stripe checkout session for slip subscription `POST /slip-subscription/create-checkout` --- ### Add more slips to existing subscription (auto-bill per slip) `POST /slip-subscription/add-slips` --- ### Create Stripe customer portal session for subscription management `POST /slip-subscription/portal` --- ### Cancel slip subscription at end of billing period `POST /slip-subscription/cancel` --- ## Slip Customer Portal ### Get current session info `GET /slip-portal/session` --- ### Get customer's reservations `GET /slip-portal/reservations` --- ### Get reservation details `GET /slip-portal/reservations/:id` --- ### Update vessel information for a reservation `PUT /slip-portal/reservations/:id/vessel` --- ### Get contract for signing `GET /slip-portal/reservations/:id/contract` --- ### Sign contract `POST /slip-portal/reservations/:id/contract/sign` --- ### Get document requirements and submitted documents `GET /slip-portal/reservations/:id/documents` --- ### Upload a document `POST /slip-portal/reservations/:id/documents` --- ### Get message thread `GET /slip-portal/reservations/:id/messages` --- ### Send a message `POST /slip-portal/reservations/:id/messages` --- ### Upload a document file to S3 `POST /slip-portal/upload` --- ### Get saved payment methods `GET /slip-portal/payment-methods` --- ### Create a SetupIntent to add a new payment method `POST /slip-portal/setup-intent` --- ### Pay for a reservation `POST /slip-portal/reservations/:id/pay` --- ### Create a PaymentIntent for a reservation (for Stripe Elements) `POST /slip-portal/reservations/:id/create-payment-intent` --- ### Get guest access list for a reservation `GET /slip-portal/reservations/:id/guest-access` --- ### Add a guest access pass `POST /slip-portal/reservations/:id/guest-access` --- ### Revoke guest access `DELETE /slip-portal/reservations/:id/guest-access/:guestId` --- ### Get haul services for a reservation `GET /slip-portal/reservations/:id/haul-services` --- ### Request a haul service `POST /slip-portal/reservations/:id/haul-services` --- ### Cancel a haul service request `POST /slip-portal/reservations/:id/haul-services/:serviceId/cancel` --- ### Confirm a scheduled haul service `POST /slip-portal/reservations/:id/haul-services/:serviceId/confirm` --- ## Public Slips ### Get all active maps with pins for a location `GET /public/slips/maps/{locationId}` --- ### Get slips as a list (for list view) `GET /public/slips/list/{locationId}` --- ### Get single slip details `GET /public/slips/{slipId}` --- ### Check slip availability for date range `GET /public/slips/{slipId}/availability` --- ### Join the waitlist for a slip `POST /public/slips/{slipId}/waitlist` --- ### Calculate price for a date range `POST /public/slips/calculate-price` --- ### Create a slip reservation (initiates checkout flow) `POST /public/slips/book` Creates a reservation using location-level booking settings for payment, approval, and documents --- ### Get document requirements and submission status `GET /public/slips/reservations/{reservationId}/documents` --- ### Submit a document for a reservation `POST /public/slips/reservations/{reservationId}/documents` Accepts document metadata (actual file upload handled separately via S3 presigned URL) --- ### Get location info for slip booking widget `GET /public/slips/location/{locationId}` Returns location details with slip booking configuration --- --- # Staff API Source: https://docs.rentaltide.com/api-reference/staff/ > Manage staff accounts, permissions, and schedules Endpoints for managing user accounts, staff roles and permissions, and staff scheduling. ## Users ### Get current user data `GET /user` Returns user data including locations, permissions, and settings **Responses** | Code | Description | | ----- | -------------------------------- | | `200` | User data retrieved successfully | | `401` | Unauthorized | | `500` | Internal server error | --- ### Create a new user `POST /user` Creates a new user via Auth0 and stores the record in DynamoDB **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `email` | string | Yes | | | `customerId` | string | Yes | | | `name` | string | Yes | | | `role` | string | No | | | `permissions` | array | No | | | `locations` | array | No | | | `userSkills` | array | No | | **Responses** | Code | Description | | ----- | ------------------------- | | `201` | User created successfully | | `400` | Missing required fields | | `500` | Internal server error | --- ### Update current user `PATCH /user` Updates the authenticated user's record in DynamoDB **Request body** | Field | Type | Required | Description | | ----------------- | ------ | -------- | ----------- | | `profileName` | string | No | | | `locations` | array | No | | | `userPermissions` | array | No | | | `userSkills` | array | No | | **Responses** | Code | Description | | ----- | ------------------------- | | `200` | User updated successfully | | `401` | Unauthorized | | `500` | Internal server error | --- ### Delete a user `DELETE /user` Deletes the user from Auth0 and DynamoDB **Request body** | Field | Type | Required | Description | | ----- | ------ | -------- | --------------------- | | `id` | string | Yes | The user ID to delete | **Responses** | Code | Description | | ----- | ------------------------- | | `200` | User deleted successfully | | `400` | User ID is required | | `500` | Internal server error | --- ### Accept terms and conditions `POST /user/accept-terms` Records the user's acceptance of terms of service, provider terms, and privacy policy **Responses** | Code | Description | | ----- | --------------------------- | | `200` | Terms accepted successfully | | `401` | Unauthorized | | `500` | Internal server error | --- ### Check if user needs to accept terms `GET /user/terms-status` Returns whether the user has accepted the current version of terms **Responses** | Code | Description | | ----- | ---------------------- | | `200` | Terms status retrieved | | `401` | Unauthorized | --- ### Download user's personal data as PDF `GET /user/export/my-data` Exports all personal data associated with the authenticated user as a formatted PDF (GDPR/privacy compliance) **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | User data exported successfully as PDF | | `401` | Unauthorized | | `500` | Internal server error | --- ### Get audit logs for a specific user `GET /user/{userId}/audit-logs` Returns activity logs for a user (requires admin permissions) **Parameters** | Name | In | Type | Required | Description | | -------- | ----- | ------- | -------- | ----------- | | `userId` | path | string | Yes | | | `limit` | query | integer | No | | | `offset` | query | integer | No | | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Audit logs retrieved successfully | | `401` | Unauthorized | | `403` | Forbidden - insufficient permissions | | `500` | Internal server error | --- ### Register an Expo push token `POST /user/push-token` Stores an Expo push token for the authenticated user to enable push notifications **Request body** | Field | Type | Required | Description | | ------- | ------ | -------- | ------------------------------------------------- | | `token` | string | Yes | The Expo push token (e.g. ExponentPushToken[xxx]) | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | Push token registered successfully | | `400` | Token is required | | `401` | Unauthorized | | `500` | Internal server error | --- ### Remove an Expo push token `DELETE /user/push-token` Removes an Expo push token for the authenticated user **Request body** | Field | Type | Required | Description | | ------- | ------ | -------- | ----------------------------- | | `token` | string | Yes | The Expo push token to remove | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Push token removed successfully | | `400` | Token is required | | `401` | Unauthorized | | `500` | Internal server error | --- ## User Management ### Get users by customer ID `GET /userManagement` Retrieves all users associated with a customer ID. If no customerId is provided, uses the authenticated user's customerId. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------------------ | | `customerId` | query | string | No | Customer ID to query users for | **Responses** | Code | Description | | ----- | ------------------------------------------------ | | `200` | Users retrieved successfully | | `400` | Customer ID not found for the authenticated user | | `401` | Unauthorized | | `404` | User not found | | `500` | Internal server error | --- ### Create a new user `POST /userManagement` Creates a new user via Auth0 and stores the record in DynamoDB **Request body** | Field | Type | Required | Description | | ------------- | ------- | -------- | ----------- | | `email` | string | Yes | | | `customerId` | string | Yes | | | `name` | string | Yes | | | `userRole` | string | No | | | `permissions` | array | No | | | `locations` | array | No | | | `userSkills` | array | No | | | `isSetup` | boolean | No | | **Responses** | Code | Description | | ----- | ------------------------- | | `201` | User created successfully | | `400` | Missing required fields | | `409` | User already exists | | `500` | Internal server error | --- ### Update a user `PATCH /userManagement` Updates the user record in DynamoDB **Request body** | Field | Type | Required | Description | | ----- | ------ | -------- | ----------------- | | `id` | string | Yes | User ID to update | **Responses** | Code | Description | | ----- | ------------------------- | | `200` | User updated successfully | | `400` | User ID is required | | `500` | Internal server error | --- ### Delete a user `DELETE /userManagement` Deletes the user from Auth0 and DynamoDB **Request body** | Field | Type | Required | Description | | ----- | ------ | -------- | ----------------- | | `id` | string | Yes | User ID to delete | **Responses** | Code | Description | | ----- | ------------------------- | | `200` | User deleted successfully | | `400` | User ID is required | | `500` | Internal server error | --- ### Find available phone numbers `POST /userManagement/find-phone-numbers` Searches for available phone numbers by country and area code **Request body** | Field | Type | Required | Description | | ---------- | ------ | -------- | ------------------- | | `country` | string | Yes | Country code | | `areaCode` | string | Yes | Area code to search | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Available numbers retrieved successfully | | `400` | Country and area code are required | | `500` | Internal server error | --- ### Purchase a phone number `POST /userManagement/purchase-phone-number` Purchases a phone number and assigns it to a location with default IVR configuration **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------------------------------- | | `phoneNumber` | string | Yes | Phone number to purchase | | `locationId` | string | Yes | Location ID to assign the number to | **Responses** | Code | Description | | ----- | ---------------------------------------- | | `200` | Phone number purchased successfully | | `400` | Phone number and locationId are required | | `500` | Internal server error | --- ### Resend staff invitation email `POST /userManagement/{id}/resend-invitation` Sends a password change email to the staff member so they can set their password **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | ----------- | | `id` | path | string | Yes | User ID | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | Invitation email sent successfully | | `404` | User not found | | `500` | Internal server error | --- ## Staff Schedule ### Create a new staff schedule `POST /staffSchedule` Creates a new schedule entry for a staff member at a location **Request body** | Field | Type | Required | Description | | ------------------- | ------- | -------- | ------------------------------- | | `customerId` | string | Yes | | | `locationId` | string | Yes | | | `staffId` | string | Yes | | | `startTime` | string | Yes | | | `endTime` | string | Yes | | | `recurring` | boolean | No | | | `recurrencePattern` | object | No | Pattern for recurring schedules | | `templateId` | string | No | | **Responses** | Code | Description | | ----- | ----------------------------- | | `201` | Schedule created successfully | | `400` | Missing required fields | | `500` | Server error | --- ### Get staff schedules `GET /staffSchedule` Retrieves schedules by locationId or customerId **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ---------------------------------------------------------- | | `locationId` | query | string | No | The location ID (either locationId or customerId required) | | `customerId` | query | string | No | The customer ID (either locationId or customerId required) | **Responses** | Code | Description | | ----- | ------------------------------------- | | `200` | Successfully retrieved schedules | | `400` | At least one query parameter required | | `500` | Server error | --- ### Update a staff schedule `PUT /staffSchedule/{id}` Updates details of an existing staff schedule **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | --------------- | | `id` | path | string | Yes | The schedule ID | **Request body** | Field | Type | Required | Description | | ------------------- | ------- | -------- | ----------- | | `startTime` | string | No | | | `endTime` | string | No | | | `recurring` | boolean | No | | | `recurrencePattern` | object | No | | **Responses** | Code | Description | | ----- | ----------------------------- | | `200` | Schedule updated successfully | | `400` | No fields to update | | `500` | Server error | --- ### Delete a staff schedule `DELETE /staffSchedule/{id}` Removes a staff schedule. Only the owner (matching customerId) can delete. **Parameters** | Name | In | Type | Required | Description | | ---- | ---- | ------ | -------- | --------------- | | `id` | path | string | Yes | The schedule ID | **Responses** | Code | Description | | ----- | ----------------------------------- | | `200` | Schedule deleted successfully | | `403` | Forbidden - no permission to delete | | `404` | Schedule not found | | `500` | Server error | --- --- # Waivers API Source: https://docs.rentaltide.com/api-reference/waivers/ > Manage waivers and in-house waiver templates Endpoints for managing waiver templates, retrieving signed waivers, and configuring in-house waiver flows. ## Waivers ### Get waiver URL by location `GET /waiverUrl` Retrieves the waiver URL and logo URL for a given location ID **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ------------------------------------------ | | `locationId` | query | string | Yes | The location ID to retrieve waiver URL for | **Responses** | Code | Description | | ----- | ------------------------------------------------ | | `200` | Waiver URL and logo URL retrieved successfully | | `400` | Invalid or missing locationId | | `404` | Waiver URL not found for the provided locationId | | `500` | Internal server error | --- ### Validate a waiver number `GET /waiverNumberValidation` Validates a waiver number (PIN) and returns the waiver details if found and pending **Parameters** | Name | In | Type | Required | Description | | -------------- | ----- | ------ | -------- | ---------------------------------------------- | | `waiverNumber` | query | string | Yes | The waiver number (PIN) to validate | | `locationId` | query | string | Yes | The location ID to validate the waiver against | **Responses** | Code | Description | | ----- | --------------------------------------------------------- | | `200` | Waiver number validated successfully | | `400` | Invalid or missing waiverNumber or locationId | | `404` | Waiver number not found or duplicate waiver numbers found | | `500` | Internal server error | --- ## In-House Waivers ### Get location logo `GET /inHouseWaiver/logo/{locationId}` Retrieves the logo URL for a specific location **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | ---------------------------------------- | | `locationId` | path | string | Yes | The location ID to retrieve the logo for | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | Logo URL retrieved successfully | | `404` | Location not found | | `500` | Failed to fetch location logo | --- ### List waiver templates by location `GET /inHouseWaiver/waiver/templates/location/{locationId}` Retrieves all waiver templates for a specific location ID **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | ----------------------------------------- | | `locationId` | path | string | Yes | The location ID to retrieve templates for | | `fn` | query | string | No | First name (for tracking) | | `ln` | query | string | No | Last name (for tracking) | | `em` | query | string | No | Email (for tracking) | **Responses** | Code | Description | | ----- | -------------------------------------------- | | `200` | List of waiver templates | | `500` | Failed to fetch waiver templates by location | --- ### Create a waiver template `POST /inHouseWaiver/waiver/template` Creates a new waiver template. Requires authentication. **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `title` | string | Yes | | | `description` | string | No | | | `sections` | array | Yes | | | `customerId` | string | Yes | | | `locationId` | string | Yes | | | `assignTo` | array | No | | **Responses** | Code | Description | | ----- | ------------------------------------------- | | `201` | Template created successfully | | `403` | Unauthorized to create this waiver template | | `500` | Failed to create waiver template | --- ### Get a waiver template by ID `GET /inHouseWaiver/waiver/template/{templateId}` Retrieves a specific waiver template by its template ID **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | --------------------------- | | `templateId` | path | string | Yes | The template ID to retrieve | **Responses** | Code | Description | | ----- | -------------------------------------- | | `200` | Waiver template retrieved successfully | | `404` | Waiver template not found | | `500` | Failed to fetch waiver template | --- ### Update a waiver template `PUT /inHouseWaiver/waiver/template/{templateId}` Updates an existing waiver template. Requires authentication. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | ------------------------- | | `templateId` | path | string | Yes | The template ID to update | **Request body** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------- | | `title` | string | No | | | `description` | string | No | | | `sections` | array | No | | | `customerId` | string | Yes | | | `locationId` | string | No | | | `assignTo` | array | No | | **Responses** | Code | Description | | ----- | ------------------------------------------- | | `200` | Template updated successfully | | `403` | Unauthorized to update this waiver template | | `404` | Waiver template not found | | `500` | Failed to update waiver template | --- ### Delete a waiver template `DELETE /inHouseWaiver/waiver/template/{templateId}` Deletes a waiver template. Requires authentication. **Parameters** | Name | In | Type | Required | Description | | ------------ | ---- | ------ | -------- | ------------------------- | | `templateId` | path | string | Yes | The template ID to delete | **Responses** | Code | Description | | ----- | ------------------------------------------- | | `200` | Template deleted successfully | | `403` | Unauthorized to delete this waiver template | | `404` | Waiver template not found | | `500` | Failed to delete waiver template | --- ### List all waiver templates `GET /inHouseWaiver/waiver/templates` Lists all waiver templates for the authenticated user's customer. Requires authentication. **Parameters** | Name | In | Type | Required | Description | | ------------ | ----- | ------ | -------- | -------------------------------------- | | `locationId` | query | string | Yes | The location ID to filter templates by | **Responses** | Code | Description | | ----- | ------------------------------- | | `200` | List of waiver templates | | `400` | Location ID is required | | `500` | Failed to list waiver templates | --- ### Sign a waiver `POST /inHouseWaiver/waiver/sign` Signs a waiver internally with form data, generates a PDF, and stores the signature record **Request body** | Field | Type | Required | Description | | --------------- | ------ | -------- | ------------------------------------------------------- | | `templateId` | string | Yes | The waiver template ID to sign | | `firstName` | string | Yes | | | `lastName` | string | Yes | | | `dob` | string | No | Date of birth | | `userEmail` | string | No | | | `phoneNumber` | string | No | | | `addressLine` | string | No | | | `formData` | object | Yes | Form responses including signatures as base64 data URLs | | `ipAddress` | string | No | | | `userAgent` | string | No | | | `waiverNumber` | string | No | | | `transactionId` | string | No | | | `tid` | string | No | | | `bookingData` | object | No | | | `assignTo` | string | No | | **Responses** | Code | Description | | ----- | -------------------------------------------------- | | `201` | Waiver signed successfully | | `404` | Waiver template not found | | `409` | Signature ID already exists (duplicate submission) | | `500` | Failed to sign waiver | --- ### Get a signed waiver by ID `GET /inHouseWaiver/waiver/sign/{signatureId}` Retrieves a signed waiver record by its signature ID **Parameters** | Name | In | Type | Required | Description | | ------------- | ---- | ------ | -------- | ---------------------------- | | `signatureId` | path | string | Yes | The signature ID to retrieve | **Responses** | Code | Description | | ----- | ------------------------------------ | | `200` | Signed waiver retrieved successfully | | `404` | Signed waiver not found | | `500` | Failed to fetch signed waiver | --- ### Get waiver signature info `GET /inHouseWaiver/waiver/sign/info/{signatureId}` Returns collected information about a signed waiver **Parameters** | Name | In | Type | Required | Description | | ------------- | ---- | ------ | -------- | -------------------------------- | | `signatureId` | path | string | Yes | The signature ID to get info for | **Responses** | Code | Description | | ----- | ---------------------------------- | | `200` | Waiver info retrieved successfully | | `404` | Signed waiver not found | | `500` | Failed to fetch waiver info | --- ### Verify waiver status `GET /inHouseWaiver/waiver/verify/{signatureId}` Verifies the status of a signed waiver including PDF existence and transaction update status **Parameters** | Name | In | Type | Required | Description | | ------------- | ---- | ------ | -------- | -------------------------- | | `signatureId` | path | string | Yes | The signature ID to verify | **Responses** | Code | Description | | ----- | ------------------------------ | | `200` | Waiver status verified | | `404` | Waiver signature not found | | `500` | Failed to verify waiver status | --- --- # Core features Source: https://docs.rentaltide.com/core/ > Dashboard, bookings, calendar, customers, messages, and support The core features you use every day to run your rental business. {% cardGroup cols=2 %} {% card title="Dashboard" href="/core/dashboard/" /%} {% card title="Bookings" href="/core/bookings/" /%} {% card title="Calendar" href="/core/calendar/" /%} {% card title="Customers" href="/core/customers/" /%} {% card title="Messages" href="/core/messages/" /%} {% card title="Support channel" href="/core/support-channel/" /%} {% /cardGroup %} --- # Bookings Source: https://docs.rentaltide.com/core/bookings/ > Central hub for viewing, filtering, and managing all reservations in a searchable data grid with inline status updates The Bookings page is your central command center for managing every reservation across your business. It displays all bookings in a sortable, filterable data grid with server-side pagination, inline status editing, order grouping, and quick actions for rescheduling. --- ## Bookings data grid The grid displays one row per booking. When a customer places a multi-item order, those items are grouped under a collapsible parent row showing the order ID and total item count. Click the expand arrow to reveal individual line items within the order. ### Columns | Column | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Booking ID** | Unique identifier. Clicking it navigates to the booking detail page. Multi-item orders display a cart icon and an item count chip. | | **Inventory Name** | The asset type name, or the assigned asset nickname if an asset has been assigned | | **Quantity** | Number of units reserved | | **Assignment** | Staff member assigned to this booking | | **Price** | Total price for the booking | | **Outstanding** | Remaining balance owed. Shows `$0.00` in green when fully paid, or the amount in red. A green checkmark appears if an auto-charge is scheduled; a warning icon appears if auto-charge has failed. | | **First Name** | Customer first name (sortable) | | **Last Name** | Customer last name (sortable) | | **Phone Number** | Customer phone number | | **Start Date** | Reservation start date and time, formatted for the location's timezone and date format | | **Booking Date** | When the reservation was originally placed | | **Status** | Current pipeline stage, displayed as a clickable colored chip | Additional columns are available through the column visibility menu: | Column | Description | | -------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | **Email** | Customer email address | | **End Date** | Reservation end date and time | | **Subtotal** | Pre-tax subtotal | | **Tax** | Tax amount | | **Deposit** | Deposit amount collected | | **Fees** | Total fee amount | | **Discount** | Total discount applied | | **Promo Code** | Gift card or promo code used | | **Affiliate** | Affiliate source for the booking | | **Type** | Booking type (hourly, daily, multi-day, etc.) | | **Documents** | Upload status chip for required documents (insurance, ownership proof). Shows a fraction like `1/2` with color indicating completeness. | --- ## Searching and filtering ### Quick search Use the search bar at the top of the grid to filter across all visible fields. Results update as you type. ### Advanced filters Click the filter bar to add structured filters. Each filter consists of a field, operator, and value. Available filters: | Filter | What you can search by | | ------------------- | ------------------------------------- | | **First Name** | Customer first name | | **Last Name** | Customer last name | | **Booking ID** | Exact or partial booking ID | | **Inventory Name** | Asset type or name | | **Price** | Total price with comparison operators | | **Quantity** | Number of units | | **Phone Number** | Customer phone | | **Email** | Customer email | | **Start Date** | Reservation date range | | **Booking Date** | Date the booking was placed | | **Promo Code** | Gift card or promo code | | **Affiliate** | Affiliate source | | **Discount Amount** | Discount value | | **Booking Type** | Type (hourly, daily, etc.) | Multiple filters can be combined and are applied server-side for accurate results across all data. ### Status quick filter A row of colored status chips appears above the grid. Click any chip to instantly filter the grid to that pipeline stage -- for example, **Booked**, **Check In**, **On Lake**, **Returned**, **Complete**, **Cancelled**, or **No Show**. Click the chip again to clear the filter. --- ## Saved views Save your current combination of filters and column visibility as a named view for quick access. 1. Set up the filters and column arrangement you want. 2. Open the **Saved Views** dropdown. 3. Click **Save Current View** and enter a name. 4. To restore a saved view later, select it from the dropdown. 5. To remove a saved view, click the delete icon next to its name. Saved views are stored on your user profile and persist across sessions and devices. --- ## Changing booking status Click any status chip in the grid to open a dropdown of all available pipeline stages. Select a new status to update the booking immediately. If the booking belongs to a multi-item order and you update the parent row, all items in the order are updated together via a batch status change. The pipeline stages are configurable per location. If your location uses a custom pipeline, the dropdown reflects your custom stage names and colors. The default pipeline stages are: | Stage | Description | | ----------------------- | -------------------------------------- | | **Booked** | Reservation confirmed | | **Check In** | Customer has arrived | | **Rundown Ready** | Boat rundown can begin | | **Rundown In Progress** | Staff is conducting the safety rundown | | **On Lake** | Customer is actively on the water | | **Returned** | Equipment has been returned | | **Check Out** | Final checkout processing | Terminal statuses (**Complete**, **No Show**, **Cancelled**, **Preauth Held**) appear at the bottom of the dropdown. {% callout type="warning" %} If a stage has unmet requirements (such as waiver not signed or deposit not collected), a confirmation dialog appears listing the missing items. You can override and proceed or cancel the status change. {% /callout %} --- ## Order grouping Bookings that belong to the same order are automatically grouped. The parent row shows: - A cart icon and item count chip (e.g., "3 items") - The order ID (click to navigate to the order detail page) - Combined pricing from the order - The order-level status Click the expand arrow on the parent row to see each individual booking line item indented beneath it. Child rows display in a subdued style with their own inventory name, status, and pricing. --- ## Date navigation Use the controls in the header to navigate through dates: - **Left / Right arrows** to move one day forward or back - **Today** button to jump to the current date - **Date picker** icon to jump to any specific date --- ## Quick reschedule Use the context menu on a booking row to open the **Quick Reschedule** dialog. This lets you change the start date and time without opening the full booking detail page. The customer can be notified automatically of the change. --- ## Pagination The grid uses server-side pagination. Navigate between pages using the controls at the bottom. As you move to later pages, additional data is fetched automatically. Adjust the page size to show more or fewer rows per page. --- ## Creating a new booking Click the **New Booking** button (plus icon) in the page header. This navigates to the booking page where you can create a reservation. {% callout type="tip" %} The bookings grid respects the location selector in the sidebar. Switch to **All Locations** to see bookings across every location, or pick a specific location to narrow results. Column visibility settings are also saved per user. {% /callout %} --- # Calendar Source: https://docs.rentaltide.com/core/calendar/ > Visual resource calendar for bookings and tours with drag-and-drop rescheduling and quick booking The Calendar page provides a visual timeline of every booking across your fleet. Each inventory type is displayed as a swim lane with its assets as sub-lanes, so you can see exactly which asset is booked and when. Create new bookings by clicking empty slots, drag existing bookings to reschedule, and switch between rental and tour views. --- ## View modes Toggle between three view modes using the buttons in the calendar toolbar: | View | Description | | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Day** | Multi-day horizontal timeline starting from the selected date. Shows a configurable number of days (starting at 3) with horizontal scrolling. Each hour is a column. | | **Week** | Single-day view showing the selected date's full hourly breakdown. Each inventory type is a row, with sub-rows for individual assets. | | **Month** | High-level monthly grid showing booking counts per day per inventory item. | ### Day view zoom In day view, use the **zoom in** and **zoom out** buttons to adjust the column width. This is helpful when you have many bookings packed into a short window, or when you want to see more days at once. --- ## Rentals vs. tours The calendar supports two modes, selectable via the toggle in the toolbar: - **Rentals** -- Shows standard rental bookings on the resource timeline. - **Tours** -- Switches to the tour calendar view, showing tour sessions and their capacity. Tour sessions display departure times, capacity fill, and booking counts. --- ## Navigating dates | Control | Action | | ---------------- | ----------------------------------------- | | **Left arrow** | Move back one day | | **Right arrow** | Move forward one day | | **Today** button | Jump to the current date | | **Date picker** | Open a calendar popup to jump to any date | The calendar automatically centers on the location's configured core hours (e.g., 8:00 AM to 8:00 PM). When **All Locations** is selected, it uses the earliest start time and latest end time across all your locations. --- ## Reading the calendar Each swim lane represents one inventory type (e.g., "Pontoon Boat"). If that inventory type has multiple physical assets, each asset appears as a sub-lane within the swim lane. Booking blocks show: - Customer name - Booking time span - Status color coding Hover over any booking block to see a quick summary tooltip with customer details, pricing, and status. --- ## Creating a booking from the calendar 1. Click an empty time slot on any asset lane. 2. A booking form dialog opens with the inventory, date, and time pre-populated. 3. Search for an existing customer or enter new customer details. 4. Review the price breakdown (calculated server-side). 5. Choose a payment method and confirm. ### Quick Book The calendar also offers a **Quick Book** option for fast reservations. Quick Book creates a booking and generates a payment link that can be sent to the customer via email, SMS, or both. The customer completes payment through the link. {% callout type="tip" %} Quick Book is ideal for phone reservations where you want to confirm the slot immediately and let the customer pay on their own time. {% /callout %} --- ## Rescheduling with drag and drop Drag any booking block to a different time slot or asset lane. When you release, a confirmation dialog appears showing the old and new times. Confirm to save the change. The calendar checks for overbooking conflicts when you drag. Each booking has a layover buffer (configurable per inventory type, defaulting to 30 minutes) that prevents bookings from being placed too close together on the same asset. {% callout type="warning" %} If a drag would create a conflict with an existing booking on the same asset (accounting for layover time), the calendar warns you before saving. You can force the overbooking if needed. {% /callout %} --- ## Filtering Use the filter controls in the toolbar to narrow what the calendar displays: | Filter | Description | | -------------------- | -------------------------------------------------------------------- | | **Inventory** | Show only specific inventory types. Select "ALL" to show everything. | | **Search** | Text search to find bookings by customer name or booking ID | | **Seating capacity** | Slider to filter by minimum seating capacity | | **Staff** | Filter bookings by assigned staff member | --- ## Time format The calendar respects the time format setting from your location configuration. It supports both 12-hour (AM/PM) and 24-hour formats. The format automatically updates when you switch locations. --- ## Ghost bookings When navigating to the calendar from a booking detail page (via URL parameter), a "ghost" outline of that booking appears on the calendar to help you locate it visually. The calendar auto-scrolls to the correct date. --- ## Timezone handling All times on the calendar are displayed in the selected location's timezone. When switching between locations in different timezones, the calendar adjusts automatically. The timezone is derived from the location's settings and applied globally during your calendar session. {% callout type="tip" %} The calendar works well on tablets for dock-side operations. On mobile devices, the view automatically switches to day view for the best experience on smaller screens. {% /callout %} --- # Customers Source: https://docs.rentaltide.com/core/customers/ > Searchable customer database with booking history, financial accounts, license scanning, and customer management The Customers page is a searchable, paginated database of every renter who has interacted with your business. View a customer's full rental history, manage their contact information, track their financial standing, and access documents like signed waivers -- all from a single profile. --- ## Customer table The main view displays all customers in a sortable table with server-side pagination and search. ### Columns | Column | Description | | ---------------- | ----------------------------------------------------------------------------- | | **Avatar** | Initials-based avatar with a color based on status | | **Name** | Full name (first and last), clickable to open the customer profile | | **Email** | Email address | | **Phone** | Phone number | | **Status** | Current customer status displayed as a colored indicator | | **Total Spend** | Lifetime revenue from this customer | | **Bookings** | Total number of bookings | | **Last Booking** | Date of the most recent reservation | | **Risk Score** | Computed risk score (color-coded: green under 30, yellow 30-70, red above 70) | | **Balance** | Outstanding account balance from the ledger (debit and credit) | | **Actions** | Menu with View, Delete, and other options | ### Search Type in the search bar to find customers by name, email, phone number, or any text field. Search is debounced (waits for you to stop typing before querying) and runs server-side, so it works across all customers regardless of pagination. ### Status filter Use the status dropdown to filter the table by customer standing: | Status | Description | | ----------- | -------------------------------------------- | | **All** | Show all customers | | **New** | Recently added, no completed bookings yet | | **Good** | Customer in good standing | | **VIP** | High-value or frequent customer | | **Warning** | Flagged for attention (e.g., past incidents) | | **Owing** | Has an outstanding balance | | **Banned** | Blocked from making new bookings | ### Sorting Click any column header to sort ascending or descending. The sort is applied server-side for accurate ordering across all pages. ### Pagination Navigate between pages using the pagination controls at the bottom. The table displays 20 customers per page. The total customer count and filtered count are displayed above the table. --- ## Adding a customer 1. Click the **Add Customer** button (person-plus icon) in the page header. 2. Choose an account creation mode: - **Basic** -- Enter first name, last name, email, and phone number only - **Full** -- Enter all details including address, date of birth, ID type, ID number, and expiry date 3. Fill in the required fields and click **Add Renter**. ### License scanning In Full mode, you can use the built-in license scanner to capture a driver's license via your device camera. The scan auto-populates the customer's name, address, date of birth, ID number, and expiry date, saving significant time during check-in. --- ## Customer profile Click any customer row to open their full profile page. The profile is organized into sections: ### Contact information Displays and allows editing of: - First name, last name, email, phone number, address, and date of birth - Profile photo (can be uploaded) - Customer status (changeable via dropdown) - Customer group assignment (Gas, VIP, Staff, Military, Seniors, or No Group) ### Financial overview | Field | Description | | ----------------------- | ----------------------------------------------------------------------- | | **Total Spend** | Lifetime revenue from this customer | | **Outstanding Balance** | Current amount owed, clickable to view the full ledger breakdown | | **Store Credit** | Toggle to enable/disable store credit, with an adjustable credit amount | | **First Booked** | Date of the customer's first reservation | ### Booking history Paginated list of all reservations associated with this customer, including rental date, inventory name, status, and total amount. Each booking links to its detail page. ### POS purchases Separate paginated list of point-of-sale transactions (retail, food, etc.) that are not rental bookings. ### Notes Add internal notes to a customer's profile. Notes are visible only to staff and include timestamps. Notes are paginated with 5 per page. You can also upload files (photos, documents) that are attached as notes with clickable links. ### Waivers View all signed waivers for this customer, with pagination. Each waiver entry shows the signing date and can be opened for review. ### ID verification View the customer's ID verification status, including license scans and face comparison results if configured. ### Memberships If the customer has an active membership, their membership details and loyalty badge are displayed. ### Slip reservations For marina operations, shows any active slip reservations associated with this customer. ### Dependants View and manage dependants linked to this customer account. ### Saved payment methods View the customer's saved payment methods on file (stored in Stripe). --- ## Customer status management From the customer profile, you can change their status using the status dropdown: | Status | Color | Effect | | ----------- | -------- | ------------------------------------------------------------------------ | | **Good** | Green | Standard customer in good standing | | **VIP** | Purple | High-value customer; may receive special pricing via customer groups | | **Warning** | Orange | Flagged for staff attention | | **Owing** | Red | Has outstanding balance | | **Banned** | Dark red | Blocked from booking. A ban confirmation dialog appears before applying. | --- ## Exporting customers Click the **Export** button in the table header to download customer data. Choose between: - **CSV** -- Spreadsheet-compatible format - **JSON** -- Structured data format The export respects your current search and filter settings, so you can export a targeted subset of customers. The downloaded file is timestamped and includes any active filter in the filename. {% callout type="tip" %} The customer table includes financial accounting data by default, showing each customer's debit, credit, and net balance. Use the status filter to quickly find customers who owe money by selecting **Owing**. {% /callout %} --- ## Duplicate management A separate **Duplicate Manager** page is available for identifying and merging duplicate customer records. Duplicates are typically caused by customers booking with different email addresses. Merging combines all booking history, notes, and financial data into a single profile. {% callout type="warning" %} Deleting a customer is permanent. Customers with existing booking history should be set to **Banned** status rather than deleted, to preserve their rental records and financial history. {% /callout %} --- # Dashboard Source: https://docs.rentaltide.com/core/dashboard/ > Your personalized home screen with key business metrics, charts, and operational alerts at a glance The dashboard is the first screen you see after logging in. It displays real-time business metrics, revenue trends, booking activity, and operational alerts -- all organized into a customizable, drag-and-drop widget grid. Each staff member maintains their own layout, so changes you make will not affect other users. --- ## Dashboard header The header greets you by name and provides quick-action buttons: | Button | What it does | | --------------------------- | ------------------------------------------------------------------------------------------------------------- | | **New Booking** | Opens the booking page to create a new reservation | | **Manage Fleet** | Navigates to the inventory page | | **Theme** (paintbrush icon) | Opens the theme customization popover where you can change colors, fonts, border radius, and toggle dark mode | --- ## Available widgets The dashboard is built from modular widgets. Each widget can be toggled on or off and repositioned by dragging. ### Booking and operations widgets | Widget | Description | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | | **Revenue Stats** | 7-day revenue total with a gradient area chart showing daily revenue trends | | **Tickets Stats** | 7-day booking count with an animated bar chart showing daily ticket volume | | **Inventory Stats** | Count of active inventory types with a horizontal bar chart of usage | | **Recent Bookings** | Table of the six most recently booked reservations, showing customer name, asset, date booked, rental date, total price, and status | | **Certification Alerts** | Lists staff certifications that are expiring or have already expired | | **Pricing Season Gaps** | Flags inventory items that have no pricing season configured for the next three months | | **Terminal Orders** | Shows recent Stripe Terminal (in-person) transactions | ### Analytics widgets | Widget | Description | | -------------------------- | ------------------------------------------------------------------------------------------------------ | | **Year-over-Year Revenue** | Compares current year revenue against the previous year on a chart | | **Revenue Correlation** | Dual-axis chart plotting daily revenue against booking count, with the calculated average ticket value | | **Booking Activity** | GitHub-style heatmap showing booking volume over time | | **Demand Forecast** | AI-predicted daily renter volume for the next five days | | **Top Inventory** | Ranked list of your most-booked asset types over the past seven days | | **Daily Briefing** | AI-generated operations summary for today covering bookings, weather, and staffing | | **Maintenance Alerts** | AI-predicted maintenance needs for your fleet | ### Financial / Ledger widgets | Widget | Description | | ----------------------- | ---------------------------------------------------------- | | **Cash Flow Trends** | Chart of cash inflows and outflows over recent periods | | **Account Breakdown** | Pie chart breaking down revenue by GL account category | | **Recent Transactions** | List of the latest ledger transactions across all accounts | ### Messaging widget | Widget | Description | | ------------------- | -------------------------------------------------------------------------------------------------------- | | **Unread Messages** | Shows up to five conversations with unread customer messages, with quick links to open each conversation | --- ## Customizing your layout 1. Click the **gear icon** in the dashboard header to open the widget management panel. 2. Toggle individual widgets on or off using their switches. 3. Drag any widget card by its header to reposition it within the grid. 4. Resize widgets by dragging the bottom-right corner handle. 5. Click **Reset to Defaults** to restore the original layout. Your layout preferences are saved to your user profile automatically. If the RentalTide team releases a layout update, your grid will reset once to include new default widgets. {% callout type="tip" %} The dashboard auto-refreshes booking data to keep metrics current. Revenue and ticket charts cover a rolling 7-day window ending today. {% /callout %} --- ## Location filtering The dashboard respects your location selector in the sidebar. When a specific location is selected, all widgets show data scoped to that location. When **All Locations** is selected, widgets aggregate data across every location. --- ## Banners and onboarding The dashboard may show contextual banners depending on your account state: | Banner | When it appears | | -------------------- | --------------------------------------------------------------------------------- | | **Connect Account** | One or more locations are missing a Stripe Connect account for payment processing | | **Settings Wizard** | A new location has not completed initial setup | | **Inventory Wizard** | The selected location has zero inventory configured | | **Welcome Tour** | First-time users are guided through the main features of RentalTide | Banners can be dismissed individually and will not reappear once closed. --- ## Kiosk mode When a non-manager staff member is logged in through kiosk mode, the dashboard is restricted to only the **Recent Bookings** table and **Unread Messages** widgets. This keeps the view focused on day-to-day operations without exposing sensitive financial data. --- ## Permissions Widget visibility is governed by your user role and the **dashboardWidgetPermissions** setting configured by your admin. Admin and SuperAdmin users can see all widgets regardless of permission settings. Standard staff may only see widgets their admin has enabled for them. --- # Messages Source: https://docs.rentaltide.com/core/messages/ > Unified two-pane inbox for all customer conversations across bookings and slip reservations The Messages page is a unified inbox for all customer communication. Every conversation tied to a booking or slip reservation lives in one place, organized as a two-pane layout with conversation threads on the left and the active conversation on the right. --- ## Layout The page uses a split-pane design: - **Left pane** -- Scrollable list of all conversations, sorted by most recent message. Each entry shows the customer name, inventory or order name, last message preview, timestamp, and an unread badge. - **Right pane** -- Full message thread for the selected conversation. Messages appear in chronological order with clear visual distinction between staff messages and customer messages. On mobile devices, the layout stacks vertically. Tap a conversation to view it, and use the back arrow to return to the conversation list. --- ## Conversation list Each conversation entry displays: | Field | Description | | -------------------------- | ------------------------------------------------------------------------------ | | **Avatar** | Color-coded customer initial | | **Customer name** | Full name of the renter | | **Inventory / Order name** | The asset name for single bookings, or "Order (N items)" for multi-item orders | | **Last message** | Preview text of the most recent message | | **Timestamp** | Relative time (e.g., "2 hours ago") for the last message | | **Unread badge** | Count of unread customer messages | | **Status chip** | Current booking status | ### Searching conversations Use the search bar at the top of the conversation list to filter by customer name, inventory name, or message content. Results update as you type. --- ## Order-level conversations When a customer places a multi-item order, all messages from every line item in that order are merged into a single conversation. This prevents the same customer from appearing as 15 separate threads when they booked 15 items in one order. The conversation shows "Order (N items)" as the inventory name and aggregates all messages with deduplication. --- ## Slip reservation messages Messages from slip reservations (marina slip tenants) appear alongside booking conversations. They are visually distinguished by a boat icon next to the slip name. Sending messages to slip reservations uses a separate endpoint, but the interface is identical. --- ## Reading messages Click any conversation to view the full message thread. Messages display: | Element | Description | | ------------------ | ----------------------------------------------------------- | | **Sender** | "You" for staff messages, customer name for renter messages | | **Timestamp** | Date and time the message was sent | | **Message body** | Full text of the message | | **Read indicator** | Checkmark showing the message has been read | Messages from customers appear on the left side. Messages from staff appear on the right side with a different background color. --- ## Sending a message 1. Select a conversation from the left pane. 2. Type your message in the compose area at the bottom of the right pane. 3. Press **Enter** or click the **Send** button. Messages are sent immediately. The conversation list re-sorts to move the active conversation to the top. The customer receives the message via the same channel they originally used (email notification tied to their booking). {% callout type="tip" %} Messages you send from the booking detail page also appear in this inbox, so you always have a complete conversation history in one place. {% /callout %} --- ## Unread tracking When you open a conversation, all unread messages from the customer are automatically marked as read. The unread badge on the conversation entry clears, and the backend is updated so the read state persists across devices. The unread count also feeds into the **Unread Messages** widget on the dashboard, so you can see at a glance how many conversations need attention. --- ## Auto-refresh The messages page polls for new messages every 10 seconds and also refreshes when you switch back to the browser tab. This ensures you see new customer messages promptly without needing to manually refresh. --- ## Navigating to booking details From any conversation, you can navigate directly to the associated booking detail page by clicking the booking reference. This is useful when a customer message requires you to check pricing, reschedule, or process a payment. {% callout type="warning" %} Messages cannot be deleted from the customer-facing thread. All sent messages are permanent and visible to both staff and the customer. Internal notes should be added to the booking detail page rather than sent as messages. {% /callout %} --- # Support channel Source: https://docs.rentaltide.com/core/support-channel/ > Real-time internal team messaging with threads, reactions, file sharing, search, and video huddles The Support Channel is a real-time, Slack-style messaging space for your internal team. Each location gets its own dedicated channel where staff can coordinate operations, share updates, and jump on video calls -- all without leaving RentalTide. --- ## Getting started Each location has its own support channel. When you navigate to the Support Channel page, it automatically loads the channel for your currently selected location. - If no channel exists yet for a location, one is created automatically the first time you visit. - If automatic creation does not succeed, click the **Create Channel** button to set it up manually. - When **All Locations** is selected in the sidebar, the page prompts you to select a specific location, since each channel is location-scoped. --- ## Channel header The header bar at the top of the channel displays: | Element | Description | | -------------------- | -------------------------------------------------------------------------------- | | **Channel name** | The name of the channel (prefixed with #), typically matching your location name | | **Online indicator** | Shows how many team members are currently online in this channel | | **Search** | Opens a search overlay to find messages by keyword | | **Saved messages** | Opens the saved messages panel to view bookmarked messages | | **Huddle** | Start or join a live video huddle | | **Slack link** | Connect or disconnect a Slack channel for cross-platform messaging (admin only) | --- ## Sending messages Type your message in the composer at the bottom of the channel and press **Enter** to send. Messages appear instantly for all staff members connected to the channel via WebSocket. The message composer supports: | Feature | Description | | -------------------- | ------------------------------------------------------------------------ | | **Text messages** | Standard text messages sent in real time | | **File attachments** | Click the attachment icon to upload images, PDFs, and other documents | | **Typing indicator** | Other users see a "typing..." indicator when you are composing a message | --- ## Message list Messages appear in chronological order with automatic date separators between different days. Each message displays: | Element | Description | | -------------------- | --------------------------------------------- | | **Author name** | Name of the staff member who sent the message | | **Author avatar** | Profile picture or initials | | **Timestamp** | Time the message was sent | | **Message body** | Full text content | | **Reactions** | Emoji reactions added by team members | | **Thread indicator** | Shows reply count if the message has a thread | Older messages are loaded automatically as you scroll up (infinite scroll with pagination). --- ## Threads Threads keep focused conversations organized without cluttering the main channel timeline. 1. Hover over any message to reveal the action bar. 2. Click the **thread** icon to open the thread panel on the right side. 3. Reply within the thread. Thread replies do not appear in the main channel view. 4. The original message shows a reply count indicator so others can see there is an active thread. On mobile, the thread panel replaces the main message view. Use the back button to return to the channel. --- ## Emoji reactions Add quick reactions to any message without adding to the message stream. 1. Hover over a message to reveal the action bar. 2. Click the **emoji** icon to open the emoji picker. 3. Select an emoji to add your reaction. 4. Click an existing reaction to toggle it on or off. Multiple users can react with the same emoji, and the count is displayed next to the reaction. --- ## Message actions Hovering over a message reveals additional actions: | Action | Description | | ------------------- | --------------------------------------------------------------------------- | | **Reply in thread** | Open a thread for focused discussion | | **React** | Add an emoji reaction | | **Save** | Bookmark the message for later reference | | **Delete** | Remove the message (available for your own messages or if you are an admin) | --- ## Search Click the **search icon** in the channel header to open the search overlay. Type keywords to search through all messages in the current channel. Click a search result to jump to that message in the timeline. If the result is within a thread, the thread panel opens automatically. --- ## Saved messages Click the **bookmark icon** in the channel header to open the Saved Messages panel. This shows all messages you have bookmarked for quick reference. Saved messages persist across sessions. --- ## Video huddles Huddles are lightweight video calls that any channel member can start or join. 1. Click the **headphones icon** in the channel header to start a huddle. 2. Other staff members see a notification and can click **Join** to enter the call. 3. During a huddle, you have controls for: | Control | Description | | ------------------- | ----------------------------------------- | | **Mute / Unmute** | Toggle your microphone | | **Camera On / Off** | Toggle your video feed | | **Screen Share** | Share your screen with other participants | | **Leave** | Exit the huddle | The huddle bar appears at the bottom of the message area when a huddle is active, showing participant tiles and control buttons. {% callout type="tip" %} Huddles are ideal for quick coordination -- like discussing a customer situation or walking through a process with a remote team member. They require no scheduling and start instantly. {% /callout %} --- ## Slack integration Admins can link a RentalTide support channel to an external Slack channel for cross-platform messaging. 1. Click the **Slack icon** in the channel header. 2. Enter the Slack channel ID you want to link. 3. Click **Link Channel**. 4. Messages posted in either RentalTide or Slack will appear in both places. To unlink, click the Slack icon again and select **Unlink Channel**. {% callout type="warning" %} Slack integration requires your Slack workspace to have the RentalTide app installed. Contact your administrator if the link fails. {% /callout %} --- ## Real-time updates The support channel uses WebSocket connections for real-time message delivery. Messages, reactions, typing indicators, and huddle status all update instantly without polling. If the connection drops (e.g., due to a network issue), the channel reconnects automatically. --- ## Permissions All staff members at a location can view and send messages in that location's channel. Message deletion is restricted to the message author or admin/owner users. Slack linking and unlinking is available only to admin and owner roles. {% callout type="tip" %} Support channel conversations are entirely internal. Customers never see these messages. Use the **Messages** page for customer-facing communication instead. {% /callout %} --- # Fleet & business Source: https://docs.rentaltide.com/fleet/ > Manage your inventory, availability, waivers, memberships, and bundles The fleet and business section is where you build and maintain everything customers can book. Create inventory items, control when they are available, collect digital waivers, offer membership tiers, and package items into discounted bundles. {% cardGroup cols=3 %} {% card title="Inventory" href="/fleet/inventory/" /%} {% card title="Availability" href="/fleet/availability/" /%} {% card title="Waivers" href="/fleet/waivers/" /%} {% card title="Memberships" href="/fleet/memberships/" /%} {% card title="Bundles" href="/fleet/bundles/" /%} {% /cardGroup %} --- # Availability Source: https://docs.rentaltide.com/fleet/availability/ > Override default schedules with date-specific blocks, capacity limits, and bulk changes The availability overrides page gives you fine-grained control over when and how many units of each inventory item can be booked. A two-dimensional grid shows inventory items on rows and dates on columns, with color-coded cells indicating override status at a glance. --- ## Reading the grid The grid displays a scrollable date range (14 days on desktop, 5 on mobile) with each inventory item as a row. Navigate forward and backward using the arrow buttons, or jump to today. | Cell indicator | Meaning | | -------------- | ------------------------------------------------------------------------------------- | | Red chip | Fully blocked -- zero units available for that date | | Orange chip | Partially restricted -- fewer units available than total capacity | | Blue chip | Custom availability -- modified but not fully restricted | | No indicator | Default schedule applies -- all units available per the item's standard configuration | Each cell chip shows the number of available units (e.g., "0" for a full block, "2" for a partial restriction). Clicking a cell opens the override dialog pre-filled with that item and date. --- ## Creating an override 1. Click any cell on the grid, or click the **+ New Override** button. 2. Select the **inventory item** from the dropdown (pre-filled if you clicked a cell). 3. Optionally select an **asset type** to restrict only units of a specific type (e.g., "Premium" jet skis). 4. Enter a **name** for the override (e.g., "Maintenance Window", "Private Event"). 5. Set **max available** -- the maximum number of units that can be booked during this period. Set to 0 for a full block. 6. Choose the **start date** and **end date** for the override. 7. Toggle **active** on or off. 8. Toggle **call to book** if customers should be able to request a booking by phone even though online booking is blocked. 9. Add optional **notes** visible to staff. 10. Click **Save**. ### Override fields | Field | Description | | --------------------- | -------------------------------------------------------------------------------- | | Inventory item | Which fleet item this override applies to | | Asset type | Optionally restrict only a specific asset type within the item | | Name | Internal label for the override (e.g., "Holiday Block", "Fleet Maintenance") | | Max available | Maximum bookable units during the override period. Set to 0 for a complete block | | Start date / End date | Date range the override applies to (inclusive) | | Active | Whether the override is currently enforced | | Call to book | Allow phone-based booking even when online booking is blocked | | Notes | Internal notes visible to staff only | --- ## Editing and deleting overrides Click an existing override cell to open it for editing. All fields can be modified. To delete an override, click the delete button and confirm the action. --- ## Priority rules When multiple settings overlap, the system resolves conflicts as follows: 1. **Active overrides take priority** over inactive overrides on the same date. 2. Among active overrides on the same date and item, the **most restrictive** (lowest max available) wins. 3. **Date-specific overrides** always take priority over the item's default schedule. 4. If an item has both a block (max available = 0) and a partial restriction on the same date, the block wins. --- ## Bulk overrides To apply the same override across multiple dates: 1. Click **+ New Override**. 2. Set a broad **start date** and **end date** range. 3. The override applies to every date within that range for the selected inventory item. This is useful for blocking an entire week for maintenance, reducing capacity across your fleet for a slow season, or opening extended availability for a holiday weekend. --- ## Call to book When an override has **call to book** enabled, the booking widget shows the item as "Call to Book" rather than fully blocked. Customers see a phone number and are directed to call your team to arrange the booking manually. This is useful for high-value bookings, group events, or dates that need manual coordination. {% callout type="tip" %} Plan ahead for holidays, maintenance windows, and special events by setting overrides months in advance. Overrides are reflected on the booking widget in real time, so customers always see accurate availability. Use the date range feature to apply blocks across your entire fleet for seasonal closures. Asset type filtering lets you take specific unit types out of rotation while keeping others available. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Customers booking when they should not be** -- Verify that the override is set on the correct inventory item and date range. Check that the override is marked as **active**. Also confirm that no other override with a higher max available exists for the same date. **Override not applying** -- Confirm the date range is correct and that the override is active. Check whether the location-level operating hours are also restricting availability. **Cannot see all items** -- The grid loads all published inventory for the selected location. Switch locations using the location selector in the header. If an item is missing, verify it is published and assigned to the current location. **Partial restriction showing wrong count** -- The max available count on the override must be less than the item's total capacity to have an effect. If max available is equal to or greater than total capacity, the override has no practical impact. {% /callout %} --- # Bundles Source: https://docs.rentaltide.com/fleet/bundles/ > Package multiple inventory items, gift cards, or memberships into discounted bundles Bundles let you group multiple items into a single bookable package with a built-in discount. Create themed offerings like a "Family Fun Package" or "Sunset Adventure" that include inventory items, gift cards, or membership tiers at a better price than purchasing each one individually. --- ## Creating a bundle 1. Navigate to **Marketing > Bundles** and click **+ New Bundle**. 2. Enter a **name** and **description** for the package. 3. Choose a **discount type** (see table below). 4. Set the **discount value** (percentage or fixed price amount). 5. **Add items** -- select inventory items, gift cards, or membership tiers to include. 6. Configure each item's quantity and optional per-item settings. 7. Optionally set availability constraints (start/end dates, max redemptions, location). 8. Toggle the bundle **active** or inactive. 9. Save the bundle. --- ## Item types Bundles can contain three types of items: | Item type | Description | | ---------- | ------------------------------------------ | | Inventory | A bookable rental item from your fleet | | Gift card | A prepaid gift card with a specified value | | Membership | A membership tier enrollment | Each item in the bundle has a **quantity** and can optionally be marked as a **free item** (included at no charge) or given a **per-item discount percentage** when using the per-item discount model. --- ## Discount types | Discount type | How it works | | -------------- | ------------------------------------------------------------------------------------------------------------- | | Percentage off | Reduces the combined price of all items by a percentage (e.g., 15% off the total) | | Fixed price | Sets a flat price for the entire bundle regardless of individual item rates | | Free item | One or more items in the bundle are marked as free -- the customer pays for the remaining items at full price | --- ## Bundle settings | Field | Description | | --------------- | ---------------------------------------------------------------------- | | Name | Display name shown to customers on the booking widget | | Description | Short description explaining what the bundle includes | | Discount type | How the discount is calculated (percentage, fixed price, or free item) | | Discount value | The percentage or dollar amount of the discount | | Location | Optionally restrict the bundle to a specific location | | Active | Whether the bundle is currently available for purchase | | Start date | Optional date when the bundle becomes available | | End date | Optional date when the bundle expires | | Max redemptions | Optional cap on how many times the bundle can be purchased | --- ## Customer experience 1. The customer visits the booking widget and sees the bundle as a bookable option alongside individual items. 2. They select the bundle and choose their date and time. 3. All items in the bundle are added to the cart with the discount applied automatically. 4. The customer completes checkout as a single transaction. Behind the scenes, each item in the bundle creates its own booking record linked by a shared **bundle group ID**. This means each asset is tracked individually on the operations boards (manifest, journey board, nav board) while the customer sees one unified order. --- ## Redemption tracking Each bundle card on the management page shows: - **Redemption count** -- how many times the bundle has been purchased - **Status** -- active, inactive, or expired (based on dates and max redemptions) - **Discount summary** -- the type and value of the applied discount - **Items included** -- list of inventory items, gift cards, and memberships in the bundle {% callout type="tip" %} Bundles work well for family packages, adventure combos, and corporate group bookings. Individual items within a bundle are managed separately on the manifest and journey board, so your operations team tracks each asset independently. Use the start/end date fields to create limited-time promotional bundles. The max redemptions cap creates scarcity for special offers. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Bundle not showing on the booking widget** -- All inventory items in the bundle must be available on the selected date. If any single item is blocked or sold out, the entire bundle is unavailable for that date. Also verify the bundle is marked as active and the current date falls within any configured start/end date range. **Discount not applying correctly** -- Double-check the discount type and value in the bundle configuration. For percentage discounts, verify the percentage is set on the bundle level, not on individual items (unless using per-item discount mode). **Bundle shows as expired** -- Check whether the end date has passed or the max redemptions cap has been reached. Update the dates or increase the cap to re-activate. {% /callout %} --- # Inventory Source: https://docs.rentaltide.com/fleet/inventory/ > Create and manage your fleet of boats, equipment, and rentable assets Inventory is where you define every asset your customers can book -- jet skis, pontoons, kayaks, paddleboards, or any other equipment. A guided wizard walks you through creating each item across two phases (inventory setup and pricing), and once published, it appears on your booking widget automatically. --- ## Creating an item Click **+ Add Inventory** to open the inventory wizard. The wizard is divided into two phases: inventory setup and pricing configuration. Each phase is broken into categories, and each category contains one or more steps. ### Phase 1: Inventory setup | Category | Steps | What you configure | | ------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Basics | Name & Location, Photos, Description, Booking Type | The item's name, location assignment, category, uploaded images, a rich-text description, and whether it is a **Rental** (customers book a unit for a duration) or a **Tour** (customers book seats on a guided trip) | | Capacity & Assets | Capacity & Quantity, Restrictions & Rules, Asset Tracking | People per unit, total units available, minimum booking quantity, buffer time between bookings, minimum age, staff approval requirement, phone-only booking, and individual asset tracking with serial numbers or unit IDs | | Payments & Deposits | Deposits & Payments, Staff Assignments | Security deposit amount, booking deposit percentage, minimum deposit dollar amount, auto-charge remaining balance days before rental, and required staff skills | | Enhancements | Add-ons, Amenities, Custom Questions, Waiver | Optional extras customers can purchase, highlighted features included with the rental, intake questions collected during checkout, and waiver template selection | | Review | Review & Save | Summary of all settings before saving the inventory item | ### Phase 2: Pricing configuration | Category | Steps | What you configure | | ------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Pricing Model | Pricing Model, Season Details | Choose from Daily, Hourly, Daily + Hourly, Category (per-person), Guest-Count Tiered, or Custom pricing. Set up your default season with name and date range | | Pricing | Opening Schedule, Daily Pricing, Hourly Pricing, Category Pricing, Discounts, Advanced Pricing | Operating hours per day of week, daily rates, hourly rates with time slots, per-person category rates (Adult, Child, Senior), multi-day/multi-hour/quantity discounts, and lead-time pricing rules | | Finalize | Pricing Review & Save | Review the full pricing configuration before saving | {% callout type="tip" %} Steps marked as conditional (Daily Pricing, Hourly Pricing, Category Pricing) only appear when the corresponding pricing model is selected. The wizard automatically skips steps that do not apply to your chosen model. {% /callout %} --- ## Booking types When creating an inventory item, you choose one of two booking types: - **Rental** -- customers book a unit for a duration. Best for boats, kayaks, jet skis, and equipment where each customer gets exclusive use of the asset. - **Tour** -- customers book seats on a guided trip. Best for tours, charters, and excursions where multiple customers share the same departure. Switching between types resets type-specific fields. Tours do not support "Sell By Assets" mode, and rentals do not support private tour settings. --- ## Managing inventory Switch between views depending on your workflow: - **Grid view** -- visual cards with photos, category, capacity, and pricing details at a glance - **List view** -- compact table for scanning many items quickly - **Bulk editor** -- edit multiple items at once using a spreadsheet-style interface for pricing, status, and availability Click any item to open the detail panel, where changes auto-save as you make them. ### Additional management tools | Tool | What it does | | ------------------- | ----------------------------------------------------------------------------------------- | | Bulk Pricing Matrix | Side-by-side pricing comparison and bulk editing across all inventory items | | Dry Dock Manager | Take assets out of service for maintenance, with conflict detection for existing bookings | | Asset Pool Manager | Manage shared asset pools across inventory items | | Pricing Seasons | Create seasonal pricing schedules with date ranges and independent rate tables | | Import from Website | Import inventory details from an existing website using AI-powered extraction | | Reorder | Drag items to change the display order on the booking widget | | Duplicate | Clone an existing item to quickly create similar equipment | --- ## Pricing models RentalTide supports flexible pricing structures that can be combined: | Model | Description | | ------------------ | ------------------------------------------------------------------------------------------------------- | | Daily | Per-day rate with optional multi-day discounts. Different rates for each day of the week | | Hourly | Per-hour rate with fixed time slots. Configure minimum and maximum hours, and multi-hour discount tiers | | Daily + Hourly | Offer both daily and hourly rates simultaneously | | Category | Different prices per person type (Adult, Child, Senior, etc.) | | Guest-Count Tiered | Flat rate tiers based on group size (e.g., 1-4 guests = $200, 5-8 guests = $350) | | Custom | Mix and match any combination of the above pricing modes | ### Pricing seasons Each inventory item supports multiple pricing seasons with independent date ranges and their own rate tables. Seasons can have separate opening schedules, daily rates, hourly rates, and discount structures. When a booking date falls within a season's range, that season's pricing applies automatically. ### Advanced pricing - **Lead-time pricing** -- adjust rates based on how far in advance a customer books - **Schedule exceptions** -- override standard pricing for specific dates (holidays, events) - **Weather rules** -- dynamic pricing adjustments based on weather conditions --- ## Add-ons Add-ons are optional extras that customers can include with their booking. Each add-on has a name, price, and optional configuration: | Setting | Description | | ---------------- | -------------------------------------------- | | Name | Display name shown to customers | | Price | Dollar amount charged | | Required | Automatically included and cannot be removed | | Multiply by time | Price scales with the rental duration | | Per booking | Flat charge regardless of quantity | You can add up to 20 add-ons per inventory item. --- ## Capacity and restrictions | Field | Description | | ------------------------ | -------------------------------------------------------------------- | | People per unit | Maximum number of passengers or riders per unit | | Capacity | Total number of units available for booking | | Minimum booking quantity | Minimum number of tickets a customer must purchase | | Buffer time | Minutes between when one booking ends and the next can begin | | Minimum age | Customers below this age cannot book | | Require staff approval | Bookings require manual staff approval before confirmation | | Phone-only booking | Customers must call to complete a booking (disables online checkout) | --- ## Asset tracking Enable asset tracking to manage individual units with serial numbers or IDs. When enabled: 1. Define **asset types** to categorize units (e.g., "Standard", "Premium", "Electric"). 2. **Generate assets** in bulk by specifying a prefix and count (e.g., "JetSki-1", "JetSki-2", etc.). 3. Individual assets appear on the manifest and can be assigned to specific bookings. --- ## Deposits and payments | Field | Description | | ---------------------------- | --------------------------------------------------------------------------- | | Security deposit ($) | Refundable amount charged for potential damages | | Booking deposit (%) | Percentage of total collected upfront to secure the booking | | Minimum deposit ($) | Floor amount for the booking deposit regardless of percentage | | Auto-charge remaining (days) | Automatically charge the remaining balance this many days before the rental | {% callout type="note" %} **Heads up on platform fees for deposit-plan bookings:** when the customer pays a deposit now and the balance is auto-charged later, the **full booking's platform fee is collected on the deposit charge, not split across both payments**. This is intentional — it guarantees the fee is captured even if the customer eventually pays the balance in cash at the counter. Your deposit payout will look smaller than a simple percentage of the deposit amount would suggest; the balance payout has no platform fee. See [Deposit payment plans](/admin/store-settings#deposit-payment-plans) for a worked example. {% /callout %} {% callout type="tip" %} Use high-quality photos -- they are the first thing customers see. Set buffer time between bookings to allow for cleaning and preparation. Archive items instead of deleting them to preserve booking history. Drag items to reorder how they appear on the booking page. Use the **Duplicate** feature to create a copy of an existing item when adding similar equipment. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Item not appearing on booking widget** -- The item must be published and have available dates configured. Check that the status is set to active and that the schedule includes upcoming dates. **Pricing looks wrong** -- Check whether a promo code, membership discount, or seasonal pricing rule is overriding the base rate. Verify which pricing season is active for the booking date. **Cannot delete an item** -- Items with booking history cannot be deleted. Archive the item instead to remove it from the booking page while preserving records. **Photos not uploading** -- Verify the image format (JPG, PNG, WebP) and file size. Large files may time out on slower connections. **Wizard progress lost** -- The wizard saves your progress at each phase boundary (after "Review & Save"). If you leave mid-step, return to the wizard to resume where you left off. {% /callout %} --- # Memberships Source: https://docs.rentaltide.com/fleet/memberships/ > Create membership tiers with recurring billing, discount rules, credits, and a self-service member portal Memberships let you offer recurring subscription plans to your customers. Define tiers with monthly or annual billing, automatic discounts, booking credits, inventory restrictions, and family plans. Members manage their own subscriptions through a self-service portal, and billing is handled automatically through Stripe. --- ## Page layout The memberships page is organized into two tabs: - **Tiers** -- create and manage membership tier definitions - **Members** -- view and manage all enrolled members across all tiers --- ## Creating a tier Click **+ New Tier** to open the tier form. The form is organized into tabbed sections. ### General settings | Field | Description | | ----------- | ---------------------------------------------------------------------------------------- | | Name | Tier name shown to customers (e.g., "Gold", "Family Plan", "VIP") | | Description | Description displayed on the membership signup page | | Image | Optional hero image for the tier card | | Scope | **All Locations** or **Specific Locations** -- controls where this tier's benefits apply | | Location(s) | When scope is "Specific Locations", select which locations the tier covers | ### Pricing | Field | Description | | ------------- | ------------------------------------------- | | Billing cycle | **Monthly** or **Yearly** recurring billing | | Price | Dollar amount charged per billing cycle | ### Discount rules | Field | Description | | -------------------------- | ------------------------------------------------------------------------------------------ | | Discount type | **Percentage** off booking total or **Fixed Amount** deducted | | Discount value | The percentage or dollar amount of the discount | | Inventory restriction mode | **Only these inventory items** (include list) or **All except these items** (exclude list) | | Inventory items | Select which items the discount applies to or is excluded from | Members receive the discount automatically at checkout when logged into their membership -- no promo code needed. ### Credits | Field | Description | | -------------- | -------------------------------------------------------------------------------------- | | Credit type | **Dollar Credits**, **Free Hours**, or **Free Bookings** | | Credit amount | Number of credits granted per renewal period | | Renewal period | **Monthly** or **Yearly** -- how often credits refresh | | Usage model | **Deduct from total price** or **Free hours** (does not count against rental duration) | | Expiry policy | **Expire at period end**, **Roll over indefinitely**, or **Roll over with cap** | | Rollover cap | Maximum number of credits that can carry forward (when using "Roll over with cap") | ### Family plan | Field | Description | | ----------- | --------------------------------------------------------------------------------------------------- | | Family mode | **Add-on members** (each has own benefits), **Shared credits** (pool credits together), or **Both** | --- ## Managing members The **Members** tab shows a searchable, sortable table of all enrolled members. ### Member table columns | Column | Description | | ------------ | ------------------------------------ | | Member | Name, email, and avatar | | Tier | Which membership tier they belong to | | Status | Current membership status | | Joined | Date the membership started | | Next billing | Date of the next recurring charge | | Actions | Menu with available operations | ### Member actions | Action | Description | | -------------- | ---------------------------------------------------------------------------------------- | | View details | Open the member detail drawer with full profile, credit history, and billing information | | Pause | Temporarily suspend the membership -- billing stops, benefits pause | | Resume | Reactivate a paused membership | | Cancel | End the membership at the current billing period | | Manage billing | Open billing management to update payment method or view payment history | ### Adding a member manually Click **+ Add Member** to enroll a customer directly. Enter the customer's name, email, and select a tier. The system creates the Stripe subscription and sends a welcome notification. --- ## Membership statuses | Status | Description | | --------- | ---------------------------------------------------------------------------- | | Active | Membership is current, billing is up to date, and all benefits are available | | Paused | Temporarily suspended at the member's or admin's request | | Cancelled | Membership has been ended and benefits are no longer available | | Past Due | Payment failed on the most recent billing cycle | --- ## Member portal Members access a self-service portal through a unique URL. After verifying their email with a one-time password (OTP), they can: - View their current tier and benefits - Check remaining credits and booking history - Update their payment method - Pause or cancel their membership --- ## Organizations Memberships support organization-level accounts for corporate or group memberships. Organizations can have multiple members under a single billing entity, with shared or individual credit pools depending on the tier configuration. --- ## Data export Export your member list in **CSV** or **JSON** format from the Members tab. The export includes member name, email, tier, status, join date, and billing information. {% callout type="tip" %} Membership discounts apply automatically on the booking widget when the customer is logged in -- no promo code needed. The "Members Save" badge on the booking page encourages sign-ups by showing the discount amount. Credits refresh on each billing date according to the renewal period. Customers can log into their membership during checkout to apply benefits. Use inventory restrictions to create tier-specific access to premium equipment. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Discount not applying** -- The customer must be logged into their membership account during checkout. The discount does not apply to guest bookings. Also check that the inventory item is not excluded from the tier's discount rules. **Credits not refreshing** -- Credits refresh on the billing date, not the calendar month. Check the member's next billing date in the member detail drawer. **Customer cannot access the portal** -- Share the portal URL with the customer and ensure their email address matches the membership record. The OTP is sent to the email on file. **Payment failed** -- Stripe handles recurring billing. A failed payment moves the membership to "Past Due" status. The customer is notified by email and can update their card through the member portal or the billing management modal. {% /callout %} --- # Waivers Source: https://docs.rentaltide.com/fleet/waivers/ > Build digital waiver templates with drag-and-drop sections, signatures, and multi-language support Waivers let you collect legally binding digital signatures from customers before they arrive. Build custom templates with a drag-and-drop editor, assign them to specific locations or inventory items, and view signed copies with timestamps, signature images, and device information. --- ## Template list The waiver list page shows all templates for the selected location. Each card displays the template title, description, and the location it belongs to. When viewing "All Locations," templates from every location are aggregated and labeled with their respective location names. Available actions on each template card: | Action | Description | | ---------------- | ------------------------------------------------------- | | Edit | Open the template in the waiver builder | | Delete | Permanently remove the template (requires confirmation) | | Copy to Location | Duplicate the template to one or more other locations | --- ## Building a template Click **+ New Template** or **Edit** on an existing template to open the waiver builder. The builder uses a drag-and-drop interface powered by a sortable list. Drag sections to reorder them, click to expand and edit content, or add new sections from the field type palette. ### Field types | Field type | Icon | Description | | -------------- | -------- | ----------------------------------------------------------------------------------------- | | Header | Title | Section heading or title text | | Rich Text | Text | Rich HTML content block for terms, conditions, safety instructions, or any formatted text | | Signature | Draw | Digital signature pad where the signer draws their signature | | Checkbox Group | Checkbox | One or more checkboxes for required or optional agreements | | Radio Group | Radio | Multiple-choice questions where the signer picks one option | | Text Field | Text | Free-form text input (e.g., emergency contact name, medical conditions) | | Date | Calendar | Date picker field | | Number | Numbers | Numeric input field | | Time | Clock | Time picker field | | Dependants | People | Section for listing dependants or minors the signer is responsible for | ### Section properties Each section can be configured with: - **Content** -- the text or HTML displayed to the signer - **Name** -- internal field name for data reference - **Required** -- whether the field must be completed before submission - **Placeholder** -- hint text shown in empty input fields - **Options** -- choices for checkbox groups and radio groups - **Page** -- which page of a multi-page waiver this section belongs to ### Editing workflow 1. Click **Add Section** and choose a field type from the palette. 2. The new section appears at the bottom of the list. Click it to expand. 3. Edit the content using the rich text editor (for Rich Text fields) or configure options. 4. Drag sections to reorder using the drag handle. 5. Use **Duplicate** to clone a section with the same content and settings. 6. Click **Save** to persist all changes. --- ## Multi-language support Templates support multiple languages. The system currently supports: | Language | Code | | ----------------- | ---- | | English | en | | Spanish (Espanol) | es | | French (Francais) | fr | Set a **primary language** on the template, then add translations for the title, description, and each section. The waiver automatically displays in the customer's preferred language when available. --- ## Assigning waivers Waivers can be assigned at two levels: - **Location-level** -- every booking at the location requires the waiver. - **Inventory-level** -- only bookings for specific items require the waiver. Select the waiver template during the inventory wizard's "Waiver" step. When a customer books an item with an assigned waiver, they are prompted to sign during the checkout flow. The waiver can also be signed via: - A **kiosk QR code** on-site that pre-fills customer information from the booking - The **pre-arrival check-in link** sent in the booking confirmation email - The **Next Steps** page accessible from the customer's booking details --- ## Viewing signed waivers Completed waivers are stored with: - Customer name and email - Signature image captured from the signature pad - Timestamp of when the waiver was signed - IP address and device information - The exact template version that was signed Access signed waivers from the booking detail page or from the customer record. --- ## Copying templates across locations Use **Copy to Location** to duplicate a waiver template to one or more locations. The copy operation runs sequentially across selected locations with a progress indicator. Changes to the copy do not affect the original template. {% callout type="tip" %} Keep waiver text concise and focused on the essentials -- long waivers reduce completion rates. Use the preview mode to see exactly what the customer will see before publishing. The dependants field type lets group leaders sign on behalf of minors. Multi-page templates help organize longer waivers into logical sections without overwhelming the signer. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Waiver not appearing during checkout** -- Confirm the waiver is assigned to the correct location or inventory item. Verify the template has been saved (not left in draft state). **Signature not saving** -- The customer must draw a clear mark in the signature field. A blank or nearly blank signature pad will not be accepted. **Template changes were lost** -- Waiver templates must be explicitly saved. Click **Save** after making changes -- navigating away from the builder without saving discards edits. **Rich text formatting looks different** -- The builder uses a rich text editor that produces HTML. Complex formatting from external sources (Word, Google Docs) may not translate perfectly. Paste as plain text and reformat within the editor for best results. **Copy to location failed** -- Ensure you have admin permissions at the target location. The copy operation requires write access to the destination location's waiver templates. {% /callout %} --- # Quick Start Source: https://docs.rentaltide.com/getting-started/ > Get your first RentalTide location up and accepting bookings in four steps # Quick start RentalTide is a full-stack management platform for watercraft rental operators and marinas. It handles bookings, point of sale, fleet tracking, staffing, and financial reporting in one place. Follow these four steps to go from sign-up to accepting your first booking. --- ## 1. Create your account Sign up at [app.rentaltide.com](https://app.rentaltide.com). You will be asked for your name, email, and a password. After verifying your email you will land on the setup wizard. {% callout type="tip" title="Single sign-on" %} RentalTide supports Google and Apple sign-in. You can link these to your account later from the Admin settings page. {% /callout %} --- ## 2. Set up your first location A **location** represents one physical site — a dock, marina, or rental counter. Each location has its own timezone, operating hours, tax rates, and Stripe account. 1. Enter your business name and address. 2. Choose your timezone and set your operating hours for each day of the week. 3. Configure your tax rates and optional fees (fuel surcharge, cleaning fee, etc.). You can add more locations at any time from **Admin > Locations**. {% callout type="note" %} Operating hours control which time slots appear on your public booking page. You can override hours for holidays or special events from the calendar. {% /callout %} --- ## 3. Add your inventory Inventory items are the assets your customers rent — jet skis, pontoons, kayaks, paddleboards, or slip spaces. For each item you will set: - **Name and category** (e.g., "Yamaha EX" in the "Jet Ski" category) - **Pricing** — hourly, half-day, full-day, multi-day, or seasonal rates - **Quantity** — how many units are available for booking - **Photos and description** — shown on the public booking page Head to **Fleet > Inventory** to create your first item. {% callout type="tip" title="Bundles" %} Want to offer a "Sunset Package" that includes a pontoon and a cooler? Create a bundle under **Marketing > Bundles** to group items at a discounted price. {% /callout %} --- ## 4. Accept bookings Once your location and inventory are configured, your public booking page is live. Share the link directly or embed the booking widget on your website. Bookings appear in real time on the **Core > Bookings** page and the **Operations > Journey Board**. From there you can: - Confirm or decline pending bookings - Collect payment or send payment links - Assign specific assets to each reservation - Check customers in and track their journey {% callout type="check" title="You are all set" %} Your location is now accepting bookings. Continue to the [initial setup guide](/getting-started/setup/) for Stripe onboarding, staff invitations, and waiver configuration. {% /callout %} --- # AI access and scraping Source: https://docs.rentaltide.com/getting-started/ai-access/ > How to feed RentalTide docs to AI assistants, scrape them programmatically, and use the per-page AI tools # AI access and scraping We have intentionally made the RentalTide docs friendly for AI assistants and for anyone scraping them programmatically. If you are using Claude, ChatGPT, Perplexity, Cursor, or any other tool that benefits from large amounts of context, this page is for you. --- ## Per-page tools Every page on this site has two buttons immediately below the page title: - **Copy as Markdown** — copies the raw Markdown source of the current page to your clipboard. Paste it into any AI assistant to give it the full context of the page. - **Open in AI** — opens the current page in Claude, ChatGPT, or Perplexity with the page content already in the prompt — no web-browsing tool required. For short pages the full content is inlined into the chat input. For longer pages, the content is copied to your clipboard and the assistant is prompted to ask you to paste it. Also exposes a "View raw Markdown" option that opens the source `.md` file in a new tab. These buttons are designed to make it trivial to bring a docs page into whatever AI workflow you already use, even when the assistant cannot fetch URLs. --- ## Raw Markdown URLs Every page on the site is available as raw Markdown by prefixing the path with `/raw` and appending `.md`: | Rendered page | Raw Markdown | | ---------------------------------------------------------- | -------------------------------------------------------------- | | `https://docs.rentaltide.com/getting-started/caching/` | `https://docs.rentaltide.com/raw/getting-started/caching.md` | | `https://docs.rentaltide.com/fleet/inventory/` | `https://docs.rentaltide.com/raw/fleet/inventory.md` | | `https://docs.rentaltide.com/api-reference/bookings/` | `https://docs.rentaltide.com/raw/api-reference/bookings.md` | The raw files are plain UTF-8 Markdown with YAML frontmatter at the top. They are regenerated on every build, so they always match what is on the rendered page. --- ## llms.txt and llms-full.txt We follow the [llms.txt](https://llmstxt.org/) convention for AI-friendly site indexing. - **[llms.txt](/llms.txt)** — a structured site index with links to every section of the docs, plus key concept summaries and pricing. Intended as a small, high-signal entry point that an LLM can read in one pass. - **[llms-full.txt](/llms-full.txt)** — a single file containing the concatenated Markdown of every page on the site. Use this if you want to feed the entire docs corpus to an LLM in one shot. The file is approximately 800 KB and is regenerated on every build. Both files are linked from our [robots.txt](/robots.txt) using the `Llms-txt` and `Llms-full-txt` extensions. --- ## Crawler policy We **welcome** AI crawlers and large language model indexers. The following user agents are explicitly allowed in our [robots.txt](/robots.txt): - `GPTBot`, `ChatGPT-User`, `OAI-SearchBot` (OpenAI) - `ClaudeBot`, `Claude-Web`, `anthropic-ai` (Anthropic) - `PerplexityBot`, `Perplexity-User` (Perplexity) - `Google-Extended` (Google AI training) - `Applebot-Extended` (Apple Intelligence) - `CCBot` (Common Crawl) - `Bytespider` (ByteDance) - `cohere-ai` (Cohere) - `Meta-ExternalAgent`, `Meta-ExternalFetcher` (Meta) - `DuckAssistBot` (DuckDuckGo) - `YouBot` (You.com) - `Diffbot` If you operate an AI crawler not on this list and you want to index our docs, you are welcome to do so under the default `User-agent: *` allow rule. --- ## Sitemap A complete [sitemap.xml](/sitemap.xml) lists every page on the site. Use it to discover all available URLs programmatically. --- ## Asking the docs AI The docs site itself has a built-in AI assistant. Click the "Ask AI" button in the top navigation, type your question, and it will answer using the full docs corpus as context. Useful when you want a quick answer without leaving the page. If the assistant ever gets something wrong, please use the thumbs-down button on its response — those reports go straight to our team and help improve the bot. --- ## Programmatic API access For programmatic access to RentalTide data itself (not just docs), see the [API reference](/api-reference/). Every endpoint is documented with request and response examples that LLMs can use directly to generate working integration code. If you have feedback on what would make the docs more useful for AI-assisted workflows, [reach out to support](/core/support-channel/) — we will keep iterating. --- # Caching and data freshness Source: https://docs.rentaltide.com/getting-started/caching/ > How RentalTide caches data, why the 5-minute window exists, and how to force a refresh # Caching and data freshness RentalTide caches certain types of data in your browser to keep pages fast and to avoid repeatedly hitting our servers for information that rarely changes. Most caches use a **5-minute window**. If you make a change in one tab and do not see it reflected in another tab right away, the cache is almost always the reason. This page explains what is cached, why, and how to force a refresh when you need the very latest data. --- ## Why we cache Every page in RentalTide reads from many sources at once: bookings, inventory, location settings, pricing rules, customer records, staff lists, tax rates, and more. Loading all of that fresh from the database on every page navigation would make the app feel slow and put unnecessary load on the platform. Caching solves three problems at once: - **Page loads stay fast.** Navigating between bookings, calendar, and the journey board feels instant because shared data is already in memory. - **Server load stays low.** A typical operations day involves thousands of page loads per location. Without caching, every one would re-query the database for the same settings. - **Bandwidth is reduced.** This matters most for staff on mobile devices or slow Wi-Fi at the dock. The tradeoff is a short delay between making a change in the admin panel and seeing it reflected on customer-facing pages or other staff devices. We chose 5 minutes as the default because it is short enough that operational mistakes self-correct quickly, but long enough that the performance benefit is real. --- ## What is cached The list below covers the data that is most commonly cached in the browser. This is not exhaustive — many pages use short-lived (under 30 second) caches for live data like the journey board and rundown. ### 5-minute cache (most common) | Data | Where it shows up | | -------------------------- | ------------------------------------------------------------------ | | Pricing | Booking widget, walk-up flow, POS register, checkout | | Inventory settings | Fleet pages, availability checks, asset detail panels | | Location settings | Tax rates, operating hours, fees, deposit rules | | Customer settings | Admin settings page, staff permissions, terminal configuration | | Analytics dashboards | All eight analytics tabs | | Year-over-year revenue | Operations homepage revenue comparison | | Today's hub | Daily ops page (today's rentals and tour sessions) | ### Longer-lived caches | Data | Cache duration | Why it's longer | | -------------------------- | ------------------------- | ---------------------------------------------------------------- | | Stripe Capital eligibility | 24 hours | Computed from 84 days of charge history; expensive to recompute | | Access tokens | Until token expiry | Auth tokens refresh proactively 5 minutes before they expire | | Logos, photos, attachments | Browser default (often hours) | Static files served from our CDN with long cache headers | ### Short-lived caches (under 1 minute) Some live data uses very short caches that you will rarely notice — pricing calculations dedupe within 500ms, slip waitlist data refreshes every 30 seconds, etc. These exist mainly to prevent the same network request from firing multiple times when several components on the same page need the same data. --- ## What is **not** cached The following data is always fresh and reflects the database immediately: - **Booking creation and payment.** When a customer books or pays, the record is written to the database in the same request. No staff member will ever see a booking go missing because of a cache. - **POS transactions.** Every sale, refund, and till adjustment is recorded immediately. - **Ledger entries and accounting.** Financial state is always read from the source of truth. - **Walk-up state and timing.** The "left" and "back" timestamps update in real time. - **Calendar and journey board status.** These poll for changes frequently so other staff devices see updates within seconds. --- ## When the cache will surprise you The most common moments where the cache becomes visible: - **You changed pricing on an inventory item and the booking widget still shows the old price.** Wait up to 5 minutes, or have the customer hard-refresh the page. - **You updated tax rates in store settings and walk-up still applies the old rate.** Refresh the walk-up tab, or wait 5 minutes. - **You added a new staff member and they don't appear in the schedule dropdown on another device.** Have the other device refresh. - **You created a new promo code and a test booking won't accept it.** Promo codes propagate immediately for new bookings but cached pricing tables on already-open tabs may take up to 5 minutes to refresh. If you are unsure whether you are seeing a cache issue or a real bug, **force a refresh first** (see below). If the problem persists after a forced refresh, it is not a cache issue. --- ## How to force a refresh You have several options, from least to most disruptive: ### 1. Use the page's refresh button Many pages — analytics, reporting, the journey board, and the home dashboard — have a refresh icon in the page header. Clicking it bypasses the cache and fetches the latest data for that page only. ### 2. Navigate away and back Closing a page and reopening it from the sidebar will typically refresh its data. This works because most caches are tied to component mounts. ### 3. Hard-refresh the browser A hard refresh clears the in-memory cache for the current tab and forces all data to reload from the server. - **Mac:** `Cmd + Shift + R` - **Windows / Linux:** `Ctrl + Shift + R` - **iOS / Android:** Pull down to refresh, or close and reopen the tab ### 4. Log out and back in This is the most thorough option. It clears the access token cache as well as the data caches, and is appropriate if you suspect a permissions issue is being cached. {% callout type="tip" title="Customer-facing pages" %} If a customer reports seeing old pricing or stale availability on your booking widget, ask them to hard-refresh their browser. The widget caches data on their device the same way the admin app does on yours. {% /callout %} --- ## Frequently asked **Will my customers see stale pricing for 5 minutes after I change a price?** Possibly, if they had your booking page open when you made the change. New visitors will see the new price immediately. Existing visitors will see it within 5 minutes, or sooner if they navigate or refresh. **Does caching affect payment processing?** No. Payment processing always uses live pricing computed on the server at the moment of payment. A cached price shown on the booking page is only ever a display value — the final charge is recalculated server-side before any money moves. **Can I disable caching for my location?** Not currently. The cache is a platform-wide design choice and turning it off would significantly slow down the app for everyone using your location. If you have a use case where 5 minutes is too long, [contact support](/core/support-channel/) and we can discuss the specifics. **My change still hasn't appeared after 10 minutes. What now?** This is not a cache issue. Check the audit log for the relevant page to confirm the change was saved, and reach out to [support](/core/support-channel/) if needed. --- ## Summary - Most data is cached in your browser for **5 minutes** to keep page loads fast. - Bookings, payments, and POS transactions are **never cached** — they are always live. - To force fresh data: click a page's refresh button, navigate away and back, or hard-refresh the browser. - Final charges are always recalculated server-side, so cached pricing on a display page cannot cause an incorrect charge. --- # Data privacy and AI usage Source: https://docs.rentaltide.com/getting-started/privacy/ > Our commitment that we do not and will not sell your data, what we do collect, how we use it, and how to opt out of AI training # Data privacy and AI usage We treat your business data and your customers' personal information as a responsibility, not an asset to be monetized. This page explains in plain language what we do — and do not — do with the data you store in RentalTide. For the formal legal record, see the [Privacy Policy](https://rentaltide.com/legal/privacy-policy) and [AI Usage and Data Commitment](https://rentaltide.com/legal/ai-usage) on rentaltide.com. If you have any privacy question that this page does not answer, email [privacy@rentaltide.com](mailto:privacy@rentaltide.com) and a human will respond. --- ## We do not sell your data. We never will. This is the simplest and most important commitment we make to you and your customers. - We do not sell customer lists, booking history, payment data, or any other information you put into RentalTide. - We do not share your data with advertisers, marketers, or data brokers. - We do not allow third parties to use your data for their own commercial purposes. - We will not do these things in the future. If our business model ever changes, this page changes first, and you will be notified directly. ### Why we can promise this RentalTide makes money from a transparent platform fee on rentals and from credit card processing. As long as you are running rentals and accepting payments through us, we are aligned: we succeed when you succeed. There is no incentive for us to monetize your data on the side, and we have explicitly chosen a business model that keeps it that way. We believe that businesses that sell their customers' data lose the trust of those customers, eventually lose the customers themselves, and ultimately damage the entire industry. That is not a tradeoff we are willing to make. --- ## What we do collect To operate the platform, we collect and store: - **Business data**: location settings, inventory, pricing, bookings, transactions, staff records, and financial reports. - **Customer data**: names, contact information, payment methods, waiver signatures, and rental history — entered either by you or by your customers during the booking flow. - **Operational telemetry**: page views, performance metrics, and error reports that help us keep the platform fast and reliable. - **Support communications**: messages exchanged with our support team. All of this is used **only** to operate, support, secure, and improve the RentalTide platform for you. --- ## Who we share data with (sub-processors) To run the platform, we share specific data with trusted infrastructure providers, only as required to deliver the service: | Provider | What they handle | | ----------------- | ---------------------------------------------------------------- | | Amazon Web Services | Database hosting, file storage, compute infrastructure | | Stripe | Payment processing, payouts, and Stripe Capital | | Auth0 | Authentication and login | | SendGrid / Twilio | Email and SMS delivery to your customers | | Pinecone | Vector search for documentation and customer support | Each of these providers is contractually bound by their own data processing agreements, and is used only for the specific operational purpose listed above. None of them resell or repurpose your data. --- ## AI usage — full disclosure We are an AI-forward platform. We use machine learning internally to make the product better — for things like: - Improving the docs AI chatbot at the bottom-right of every page. - Suggesting smarter pricing recommendations and demand forecasts. - Detecting unusual patterns that might indicate fraud or operational problems. - Auto-categorizing support requests and routing them to the right person faster. To do this well, we sometimes use **limited, masked data** from the platform to train and evaluate these systems internally. ### What "limited and masked" means When we use platform data for internal AI training or evaluation: - **Personally identifiable information is removed or redacted** before it reaches any training pipeline. This includes customer names, email addresses, phone numbers, physical addresses, payment card numbers, and waiver signatures. - **Free-text fields** (notes, messages, descriptions) are reviewed and redacted of any identifying detail before use. - **Financial figures** may be used in aggregate or scaled form but are never tied back to a specific customer. - We use **only the smallest subset of data needed** to accomplish a specific improvement, not bulk dumps. - This data is **never sold, licensed, or shared** with third-party AI providers for their own model training. ### Your right to opt out If you would prefer that **none** of your business's data — masked or otherwise — be used for our internal AI training or evaluation, you can opt out at any time. Email **[privacy@rentaltide.com](mailto:privacy@rentaltide.com)** with the subject line "AI training opt-out" and include the email address associated with your RentalTide account. We will exclude your data from all future internal AI training runs within 30 days and confirm the change in writing. Opting out has no effect on the quality of service you receive. The platform behaves identically whether you opt in or out. --- ## Customer data and end-user rights Your customers — the people who book rentals through your booking page — have rights under privacy laws like GDPR (Europe) and CCPA (California). If a customer of yours contacts us directly with a privacy request, we will route them back to you, since you are the data controller for your customer data and we are the data processor. To help you respond to those requests, RentalTide provides: - A full data export tool ([Admin → Storage → Export](/admin/storage/)). - Account deletion tools that scrub customer records when requested. - Audit logs so you can see exactly when and how a customer's data was accessed. --- ## Security We use industry-standard practices: encryption in transit (TLS 1.2+), encryption at rest, role-based access controls, audit logging, and continuous vulnerability scanning. Stripe and AWS are PCI DSS compliant, so payment card data never touches our application servers directly. If you discover a security issue, please email [security@rentaltide.com](mailto:security@rentaltide.com) rather than filing a public issue. We respond to all reports within one business day. --- ## Questions, requests, or concerns For anything privacy-related, write to [privacy@rentaltide.com](mailto:privacy@rentaltide.com). A real human reads every message. Specifically, you can email us to: - Opt out of internal AI training. - Request a copy of all data we hold about your business. - Request deletion of your account and all associated data. - Ask for the current list of sub-processors and their contact information. - Report a suspected privacy issue. We aim to respond within two business days and resolve substantive requests within 30 days. --- # Reliability technical appendix Source: https://docs.rentaltide.com/getting-started/reliability-technical/ > Infrastructure details, RTO and RPO targets, and the specific architecture choices behind RentalTide's continuity guarantees # Reliability technical appendix This page is for technical decision makers — IT, security, procurement — who want the architecture behind the [reliability overview](/getting-started/reliability/). It documents where RentalTide runs, what we replicate, how fast we recover, and what dependencies sit outside our direct control. --- ## Hosting and regions RentalTide is hosted entirely on Amazon Web Services. We do not operate any on-premise servers and do not store production data on employee devices. | Component | Primary region | Notes | | ------------------------------------------ | -------------- | ---------------------------------------------------------- | | Application servers (API + web) | `us-east-1` | Containerised, autoscaled across three Availability Zones | | Primary database (PostgreSQL Aurora) | `us-east-1` | Multi-AZ, synchronous replication | | Object storage (photos, attachments, PDFs) | `us-east-1` | S3 with versioning and cross-region replication | | Background jobs and scheduled tasks | `us-east-1` | Lambda, run independently of the web tier | | Static site delivery (booking widget, docs)| Global edge | CloudFront, served from 400+ points of presence worldwide | | Backups (snapshots and continuous) | `us-east-1` + `us-west-2` | Cross-region replication for disaster recovery | The booking widget and customer-facing checkout pages are served from CloudFront's global edge, so a `us-east-1` regional event does not take the booking widget offline immediately — pages still render from cached assets, and any submission queues up against the API layer. --- ## Database resilience The primary database is Aurora Serverless v2 PostgreSQL. - **Synchronous replication across three Availability Zones.** A write is acknowledged only when it is durable on storage in two zones. Loss of a single zone is invisible to the application. - **Automatic failover within a region.** If the writer node becomes unhealthy, Aurora promotes a reader to writer in under 60 seconds. - **Continuous backup.** Every write is shipped to S3 in near-real-time. Point-in-time restore is available for any second within the last seven days. - **Daily automated snapshots.** Retained for 35 days. - **Cross-region snapshot replication.** Daily snapshots are copied to `us-west-2` for full-region disaster recovery. For full-region recovery, the recovery point objective is bounded by replication lag to `us-west-2`, which is typically under five minutes. --- ## Application tier The application tier runs as containers on AWS ECS Fargate behind an Application Load Balancer. - **Autoscaled across three Availability Zones.** A single AZ loss removes roughly one-third of capacity; autoscaling refills within minutes. - **Health checks every 15 seconds.** Failing instances are removed from the load balancer automatically. - **Rolling deploys.** New container versions are rolled out a fraction at a time. Health checks gate progression. A bad deploy stops itself before reaching full fleet. - **Automatic rollback.** Deploys that fail health checks revert to the previous task definition without manual intervention. - **Zero-downtime deploys.** New containers come up healthy before old containers are drained. --- ## Recovery time and recovery point objectives Recovery time objective (RTO) is the maximum acceptable time from incident start to service restoration. Recovery point objective (RPO) is the maximum acceptable data loss measured in time. | Failure scenario | RTO target | RPO target | Mechanism | | ------------------------------------------------- | ----------- | ---------- | ---------------------------------------------------------- | | Single application instance crash | 60 seconds | Zero | Container restart, load balancer reroute | | Single Availability Zone failure | 2 minutes | Zero | Multi-AZ Aurora and ECS distribute traffic to healthy AZs | | Failed deploy | 5 minutes | Zero | Automated rollback to previous task definition | | Aurora writer node failure | 90 seconds | Zero | Aurora failover promotes a reader | | Application-level bug (data corruption) | 30 minutes | Bounded by detection time | Targeted SQL repair using journal_entries audit log | | Catastrophic data corruption | 6 hours | 5 minutes | Point-in-time restore from continuous backup | | Full region outage (`us-east-1`) | 4 hours | 5 minutes | Restore from cross-region snapshot in `us-west-2` | RTO and RPO targets are reviewed quarterly and tested via game days at least twice per year. --- ## Audit and reconstruction Beyond raw database backups, RentalTide maintains an append-only journal (`journal_entries`) for every financial mutation: payments, refunds, fees, taxes, AR adjustments. This is double-entry by design. This means: - The cache columns on `rental_bookings` (amount paid, outstanding balance, amount refunded) can be reconstructed from the journal at any time. - A bug that corrupts cache fields without corrupting the journal is recoverable by re-running the cache sync from journal entries. - Forensic queries (who paid what, when, on which booking) survive any cache corruption. We treat `journal_entries` as the source of truth. The reliability of that table is the reliability of your books. --- ## External dependencies RentalTide depends on a handful of external SaaS providers. We hold ourselves to the union of their availability, so we choose them carefully. | Provider | What we use it for | Their stated SLA | Failure behaviour | | ----------------- | ---------------------------------------- | ---------------- | ---------------------------------------------------------- | | Stripe | Payments, terminals, Connect | 99.99% | Card capture and refunds fail with clear errors; no data loss | | Auth0 | Staff authentication | 99.9% | Existing sessions stay valid; new sign-ins blocked | | SendGrid | Transactional email | 99.95% | Emails queue; retried for 72 hours | | Twilio | SMS and voice | 99.95% | SMS retries; voice routing degrades to next provider | | AWS | Compute, storage, database | 99.99% per AZ | Multi-AZ design tolerates single-AZ loss invisibly | | OpenAI / Anthropic| AI features (docs bot, phone, summaries) | 99.9% | AI features degrade gracefully; core ops unaffected | A failure of any single external provider does not take RentalTide down. The most common visible effect is one feature (typically payment capture or outbound email) showing transient errors while the provider recovers. --- ## Backups and retention | Asset | Frequency | Retention | Storage location | | --------------------------- | ---------------- | --------- | -------------------------- | | Aurora continuous backup | Real-time | 7 days | S3, `us-east-1` | | Aurora daily snapshots | Daily, 04:00 UTC | 35 days | S3, `us-east-1` | | Cross-region snapshot copy | Daily | 35 days | S3, `us-west-2` | | S3 object versioning | Every write | Indefinite | S3, `us-east-1` | | S3 cross-region replication | Every write | Indefinite | S3, `us-west-2` | | Application logs | Real-time | 90 days | CloudWatch, `us-east-1` | | Audit / journal entries | Real-time | Indefinite | Aurora `journal_entries` table | Backups are encrypted at rest with AWS-managed keys. Cross-region replication uses separate keys per region to limit the blast radius of a key compromise. --- ## Security controls relevant to availability Availability is also a security concern. We protect against: - **DDoS** — AWS Shield Standard is always on. CloudFront absorbs volumetric attacks at the edge. - **Credential theft** — Auth0 enforces MFA for staff accounts, and bearer tokens are short-lived (1 hour) with rotating refresh tokens. - **Insider risk** — production database access requires SSO and is audited. No engineer has standing access to customer data; access is just-in-time and logged. - **Ransomware** — backups are immutable for their retention period (S3 Object Lock on snapshots). --- ## Status, communication, and runbooks When something goes wrong, you will hear from us in these places: - **`status.rentaltide.com`** — public status page, updated within minutes of detection. This is the source of truth for the platform overall. - **Your support channel** — Slack (for partner-tier customers) or email, with location-specific impact and ETA. - **Postmortems** — every incident with more than 15 minutes of customer impact gets a written postmortem within 5 business days, in the format described in our [internal COE template](https://github.com/anthropics/claude-code). Internally, we maintain detailed runbooks for each failure mode under `/docs/contingency/` in our codebase. These cover region failover, internet outage procedures, server crash response, and physical-disaster business continuity. They are not public, but the executive summaries are available on request for procurement and security reviews. --- ## Game days and chaos testing Twice a year (typically March and September, ahead of peak season), we run game-day exercises that test: - Aurora writer failover under load - ECS task termination across an entire AZ - Point-in-time database restore into a separate environment - Cross-region snapshot restore - Incident response paging, status page updates, and customer communication Results from each game day are recorded internally and fed back into our recovery time objectives. --- ## Reporting and questions For SOC 2 reports, vendor security assessments, or anything that requires a signed document, reach out to your account contact or `security@rentaltide.com`. We will route the request to the right place. For technical questions about a specific failure mode, reach out in your support channel — we are happy to walk through any of the above in more detail. --- # Reliability and continuity Source: https://docs.rentaltide.com/getting-started/reliability/ > How RentalTide stays available during cloud outages, internet drops, and physical disasters, and what your staff can do in each scenario # Reliability and continuity RentalTide is the system of record for your bookings, payments, and operations. We design it so a single bad day — at AWS, at your dock, or at our office — does not turn into a bad week for your business. This page covers the broad strokes of what we do to keep the platform running and what your team can do when something does go wrong. For deeper technical detail (regions, services, recovery time objectives), see the [technical appendix](/getting-started/reliability-technical/). --- ## What we protect against | Scenario | What it looks like | Who keeps you running | | ------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------ | | A single AWS data centre failing | Brief pause (seconds to a couple of minutes) while traffic reroutes | AWS multi-AZ infrastructure, automatic | | An entire AWS region going down (us-east-1) | Site may be slow or unreachable; we cut over to backups and update status page | RentalTide on-call, manual cutover | | Your local internet drops | Booking widget and admin are unreachable from your dock | You — offline procedures below | | Our servers crash or a deploy goes bad | Errors on specific pages, automatic restart and rollback in under 10 minutes | RentalTide on-call + automated health checks | | Fire, flood, or physical loss at your location | Your devices may be gone; your data and bookings are untouched | Cloud-hosted data, just sign in from any device | | Fire, flood, or physical loss at our office | No impact — we have no on-premise servers | Cloud-hosted everything | --- ## Where your data lives RentalTide runs entirely on Amazon Web Services. We do not store customer data on laptops, in our office, or on any device you cannot access from a browser. That means: - A staff laptop being lost, stolen, or destroyed has **no effect** on your bookings, customers, or payments. - A fire or flood at your location has **no effect** on your data. Sign in from a phone, a hotel laptop, or a tablet and you are back online. - We do not need to ship hardware to recover from any disaster. Your primary database runs in AWS's `us-east-1` region in Northern Virginia with synchronous replication across three independent data centres (called Availability Zones). Continuous backups are taken automatically, and we keep point-in-time recovery for the last seven days plus daily snapshots for thirty-five days. --- ## What happens if AWS has a bad day AWS designs each region to tolerate the loss of one data centre with no customer impact. We rely on that. In the rare cases where an entire region has degraded service: 1. **Detection** — automated monitors alert RentalTide on-call within minutes. 2. **Communication** — we update the status page at `status.rentaltide.com` and post in your support channel. 3. **Triage** — for partial regional outages (one service degraded, others fine), we wait for AWS to recover. AWS regional incidents are typically resolved in under two hours. 4. **Failover** — for a full sustained regional outage (very rare, last meaningful one was December 2021), we restore from cross-region backups into `us-west-2`. Your data is preserved; the site comes back up within the recovery window in the technical appendix. While we recover, your team can still do a lot — see the [offline procedures](#offline-procedures-for-staff) below. --- ## What happens if your internet drops This is the most common outage you will actually experience. RentalTide is a web app, and if the dock has no internet, neither does the booking widget or the admin panel. Here is what to do. ### Take walk-ups on paper Keep a printed walk-up sheet on the counter with these fields: - Customer first name, last name, phone, email - Asset type and quantity - Start time and intended return time - Signature line for liability waiver - Payment method (cash, card terminal direct, "bill me later") Photos of the driver's licence and signed waiver work fine on a phone. Enter them in RentalTide once internet is restored. ### Keep your card reader running Your Stripe terminal can complete payments without RentalTide as long as it has cellular or hotspot connectivity. The transaction will still settle correctly. Reconcile it against the paper sheets when you re-sync. ### Use a hotspot A phone with cellular service is enough to get the admin panel back up on one device. Use the [Today's Hub](/operations/journey-board/) view on that device as your single source of truth until the dock connection returns. ### Don't worry about the booking widget Customers trying to book online during your local outage just see a normal "site unreachable" message from their browser. They will retry. We do not lose their attempts — our analytics show the booking widget itself remains available globally even when individual locations are offline. --- ## What happens if our servers crash Servers fail. Bad deploys ship. We plan for both: - **Automatic restart.** If a server process crashes, our container orchestrator restarts it within seconds and shifts traffic to healthy instances. - **Health checks.** Every server reports a heartbeat every few seconds. Any instance that fails its health check is removed from the rotation automatically. - **Bad deploy rollback.** New code is deployed to a subset of servers first. If error rates spike, the deploy halts and the previous version takes back over. Total exposure on a bad deploy is usually under five minutes. - **Status page.** Major incidents are posted at `status.rentaltide.com` with regular updates until resolution. If you see errors that persist for more than a few minutes, reach out in your support channel. We will already be on it, but it helps us confirm scope. --- ## What happens if there's a fire, flood, or worse The honest answer: your data and bookings are unaffected. Everything that matters is in the cloud, replicated across three data centres. There is no scenario short of a regional AWS catastrophe where you lose customer records, payment history, or future bookings. ### If your location is unsafe to operate - Sign in to RentalTide from any device anywhere in the world. - Mark affected dates as closed under **Operations > Calendar > Block dates**. - Use the bulk messaging tools to email or text every customer with a booking in the affected window. - Use the [cart recovery](/marketing/cart-recovery/) tools to follow up once you reopen. ### If you lose physical hardware (terminals, printers, tablets) - Your data is fine. - A new tablet or laptop with a browser is all you need to be operational again. - Stripe will reissue terminals on request. Reach out and we will help you place the order. ### If we lose office access We have no on-premise servers, no local databases, and no critical infrastructure at our office. Engineering and support work from laptops that can be replaced overnight. The platform runs entirely on AWS and would not notice. --- ## Offline procedures for staff A quick reference your staff can use the moment internet drops, before they have a chance to read this whole page. | Activity | Offline alternative | | ------------------------- | ------------------------------------------------------------------------- | | New walk-up rental | Paper sheet + driver's licence photo. Enter when back online. | | Existing booking arrives | Mark on a printed daily list. Check them in on screen later. | | Take a payment | Use the Stripe terminal directly (cellular or hotspot). | | Look up a customer | Have a phone on a cellular hotspot for one device. | | Sign a waiver | Paper liability waiver, scan or photograph for upload later. | | Manage assets on the lake | Communicate by radio or phone; reconcile in the manifest when back up. | | Reservations on the phone | Take notes, promise a confirmation email once you re-sync. | Print this table and laminate it. We have a printable PDF version on request. --- ## How we communicate during incidents You will hear from us in three places during a real incident: 1. **Status page** — `status.rentaltide.com` is updated within minutes of detection. This is the source of truth for current status, what is affected, and ETA. 2. **Support channel** — your dedicated support channel (Slack or email) gets a message within the same window with anything specific to your location. 3. **Post-incident report** — for any incident with customer impact lasting more than fifteen minutes, we publish a written postmortem within five business days. These are stored under [Operations > Support channel](/core/support-channel/) and are also shared publicly when appropriate. If you ever cannot reach us through your usual channel during an outage, the support email at `support@rentaltide.com` is monitored from outside our normal infrastructure. --- ## Recovery time and recovery point at a glance These are the targets we hold ourselves to. They are described in detail in the [technical appendix](/getting-started/reliability-technical/). | Failure mode | Recovery time objective (RTO) | Recovery point objective (RPO) | | ----------------------------------------------- | ----------------------------- | ------------------------------ | | Single Availability Zone failure | Under 2 minutes | Zero data loss | | Application instance crash | Under 60 seconds | Zero data loss | | Bad deploy | Under 5 minutes | Zero data loss | | Full AWS region outage | Under 4 hours | Under 5 minutes | | Catastrophic data corruption (rare) | Under 6 hours | Under 5 minutes | --- ## Practising your own continuity plan Reliability on our side is half the picture. We recommend every location: - Keep paper walk-up sheets and waivers on hand for the season. - Keep one staff phone with a cellular plan dedicated to RentalTide access during dock-wifi outages. - Identify a backup person who can sign in and run the admin panel if the manager is unavailable. - Test your Stripe terminal on cellular at least once per season. - Know where to find your support channel and the status page. If you would like help drafting a continuity plan specific to your location, reach out in your support channel and we will work through it with you. --- # Initial Setup Source: https://docs.rentaltide.com/getting-started/setup/ > Configure your business information, connect Stripe, and invite your staff # Initial setup This guide walks you through the one-time configuration steps to get your RentalTide location fully operational. Complete these before your first day of business. --- ## Business information Navigate to **Admin > Settings** to fill in your business details. These values appear on receipts, invoices, and customer-facing pages. - **Business name** — your legal or DBA name - **Phone number** — shown on booking confirmations and the public site - **Logo** — uploaded as a square image, at least 256 x 256 px - **Support email** — where customer replies are routed --- ## Location setup Each location needs three things configured before it can accept bookings. ### Timezone Set the timezone that matches your physical location. All booking times, operating hours, and reports use this timezone. You can change it later, but existing bookings will not shift. ### Operating hours Define when your location is open for each day of the week. The public booking page only shows time slots within these hours. You can override individual dates from the calendar for holidays or weather closures. ### Tax rates and fees Under **Location > Taxes & Fees**, add each applicable tax rate (state, county, local) as a percentage. You can also define flat fees such as: - Fuel surcharge - Cleaning fee - Environmental fee - Credit card processing fee pass-through Taxes and fees are calculated automatically at checkout and reported separately in the ledger. --- ## Stripe Connect onboarding RentalTide processes payments through Stripe. Each location connects its own Stripe account so funds deposit directly into your bank. 1. Go to **Admin > Payments**. 2. Click **Connect with Stripe**. 3. Complete the Stripe onboarding flow — you will need your EIN or SSN, bank account details, and a government-issued ID. 4. Once approved, your location can accept credit cards, Apple Pay, and Google Pay. {% callout type="warning" title="Stripe approval" %} Stripe may take 1-2 business days to verify your identity and bank account. You can configure everything else in RentalTide while you wait, but payment collection will not work until Stripe approves your account. {% /callout %} If you already have a Stripe account, RentalTide uses **Stripe Connect** to link it. Your existing Stripe dashboard, payouts, and reporting remain unchanged. ### Terminal setup If you plan to accept in-person payments, register your Stripe terminal hardware under **POS > Terminals**. RentalTide supports Stripe Reader S700, M2, and BBPOS WisePOS E. --- ## Staff invitations Invite your team from **Admin > Staff Management**. Each staff member receives a role: | Role | Permissions | | ----------- | -------------------------------------------------------------------------------- | | **Admin** | Full access to all settings, reporting, and staff management | | **Manager** | Bookings, POS, operations, and reporting. Cannot manage billing or other admins. | | **Staff** | Day-to-day operations — check-in, walk-ups, POS register, journey board | 1. Click **Invite Staff**. 2. Enter their email address and select a role. 3. Assign them to one or more locations. 4. They will receive an email with a link to set their password. {% callout type="tip" title="Scheduling" %} After inviting staff, set up their weekly schedule under **Staffing > Schedule**. Shifts appear on the manifest and control who shows up on the journey board. {% /callout %} --- ## Waivers Most rental operations require customers to sign a liability waiver before departure. Configure your waiver under **Fleet > Waivers**. 1. Paste your waiver text or upload a PDF. 2. Choose when the waiver is presented — at checkout, on the next-steps page, or at check-in. 3. Enable the signature requirement. Customers sign on their phone or a shared tablet. Signed waivers are stored and linked to the booking. You can view or download them from the booking detail page. --- ## Next steps Your location is fully configured. Here are a few things to explore next: - [Embed the booking widget](/public/booking-widget/) on your website - [Create promo codes](/marketing/promo-codes/) for your launch - [Set up the POS register](/pos/register/) for walk-in retail sales - [Configure the manifest](/operations/manifest/) for daily departure tracking --- # Marina Management Source: https://docs.rentaltide.com/marina/ > Full-service marina operations including slips, reservations, waitlist, guest access, haul services, contracts, utility metering, and visual dock maps. RentalTide Marina is a complete marina management module that handles every aspect of running a marina -- from slip inventory and tenant reservations to utility billing and visual dock maps. It is designed for marinas of any size and integrates directly with your existing RentalTide billing, customer records, and accounting system. ## Module overview The Marina module is organized into the following functional areas: | Area | Purpose | | ----------------------------------------- | ---------------------------------------------------------------------- | | [Dashboard](/marina/dashboard/) | Unified operations overview with key metrics and quick links | | [Slips & Storage](/marina/slips/) | Manage physical slip inventory, unit types, amenities, and pricing | | [Reservations](/marina/reservations/) | Create and manage slip reservations with billing cycle support | | [Waitlist](/marina/waitlist/) | Queue customers waiting for slips with matching and offer workflows | | [Guest Access](/marina/guest-access/) | Issue and manage guest passes with access codes and ID verification | | [Haul Services](/marina/haul-services/) | Schedule haul-out, put-in, pressure wash, and relocation services | | [Contracts](/marina/contracts/) | Generate, send, and track lease/rental contracts with e-signatures | | [Meter Readings](/marina/meter-readings/) | Record electric and water meter readings and bill tenants for usage | | [Reports](/marina/reports/) | Occupancy, revenue, and utility usage reports with export capability | | [Marina Maps](/marina/marina-maps/) | Upload dock layouts and place interactive slip pins for visual booking | ## Subscription and pricing Marina management requires an active slip subscription. Slips are billed at **$10 per slip per month** on top of your base RentalTide subscription. When you add a slip that exceeds your current quota, the system will automatically bill for the additional capacity. {% callout type="note" %} You can manage your slip subscription from the **Subscription** page. The system tracks how many units you have active versus your quota and will alert you when you are approaching your limit. {% /callout %} ## Permissions Marina features are governed by the following permission groups: | Permission | Grants access to | | ------------------ | ------------------------------------------------------------------------ | | `inventory_access` | View slips, reservations, and all marina pages | | `inventory_add` | Create new slips, bulk create, upload maps | | `inventory_edit` | Edit slips, manage sections, update statuses, manage waitlist and guests | | `inventory_delete` | Delete slips, remove maps | ## Getting started 1. **Activate your slip subscription** from the Subscription page. 2. **Create your slip inventory** using the [Slips & Storage](/marina/slips/) page -- use Bulk Create for large marinas. 3. **Upload a marina map** from [Marina Maps](/marina/marina-maps/) and place pins on your slips. 4. **Set up contract templates** from [Contracts](/marina/contracts/) so you can send lease agreements. 5. **Start taking reservations** from the [Reservations](/marina/reservations/) page or through the public slip booking page. {% cardGroup cols=2 %} {% card title="Dashboard" href="/marina/dashboard/" /%} {% card title="Slips & Storage" href="/marina/slips/" /%} {% card title="Reservations" href="/marina/reservations/" /%} {% card title="Waitlist" href="/marina/waitlist/" /%} {% card title="Guest Access" href="/marina/guest-access/" /%} {% card title="Haul Services" href="/marina/haul-services/" /%} {% card title="Contracts" href="/marina/contracts/" /%} {% card title="Meter Readings" href="/marina/meter-readings/" /%} {% card title="Reports" href="/marina/reports/" /%} {% card title="Marina Maps" href="/marina/marina-maps/" /%} {% /cardGroup %} --- # Slip Contracts Source: https://docs.rentaltide.com/marina/contracts/ > Generate, send, track, and manage slip lease contracts with templates, e-signatures, PDF generation, and status tracking. The Contracts page lets you manage slip rental agreements with built-in contract generation, e-signature workflows, and PDF archiving. Every reservation can have an associated contract that moves through a defined lifecycle from draft to signed. ## Overview The contracts page shows four stats cards at the top: | Stat | Description | | ------------------- | ------------------------------------------------------------ | | Active Reservations | Total number of non-cancelled, non-completed reservations | | Contracts Signed | Number of reservations with signed contracts | | Pending Signature | Number of reservations with contracts awaiting signature | | Signed Rate | Percentage of active reservations that have signed contracts | ## Contract statuses | Status | Icon | Meaning | | ----------- | --------- | --------------------------------------------------- | | No Contract | Clock | No contract has been generated for this reservation | | Draft | Document | Contract generated but not yet sent to the tenant | | Sent | Email | Contract emailed to the tenant for signing | | Viewed | Open | Tenant has opened the signing link | | Signed | Checkmark | Contract fully executed | | Expired | Cancel | Signing deadline passed without execution | ## Tabs The page is organized into three tabs: 1. **Pending** -- Shows reservations without a signed contract (including Draft, Sent, Viewed, and No Contract statuses). 2. **Signed** -- Shows reservations with signed contracts. 3. **All** -- Shows all reservations regardless of contract status. ## Contract workflow ### Step 1: Generate a contract 1. Find the reservation in the table (on the Pending tab). 2. Click **Generate** in the Actions column. 3. Select a **contract template** from the dropdown. 4. Click **Generate**. The system creates a draft contract using the selected template, populating it with data from the reservation (customer name, slip details, dates, rates, etc.). ### Step 2: Send for signature 1. Find the reservation with a Draft contract. 2. Click **Send** in the Actions column (or use the context menu). 3. Enter the **signer name** and **signer email** (pre-filled from the reservation if available). 4. Click **Send**. The tenant receives an email with a signing link. ### Step 3: Track and follow up - **Resend** -- If the tenant has not responded, use the context menu to resend the signing email. - **Void** -- If you need to cancel an unsigned contract, use Void to invalidate it. You can then generate a new one. ### Step 4: View signed contracts Once signed, the contract appears in the Signed tab. From the context menu: - **View Signed PDF** -- Opens the signed contract PDF in a new tab (if a PDF URL is available). - **Generate PDF** -- If no PDF exists yet, generates one from the signed contract data and opens it. - **View Reservation** -- Navigates to the reservation detail page. ## Contract templates Click **Manage Templates** to open the Contract Template Editor. Templates are reusable contract formats that can include: - Standard lease terms and conditions - Placeholder variables that auto-fill from reservation data (customer name, slip number, dates, rates) - Location-specific provisions - Insurance requirements - Marina rules and regulations Templates are managed per location and can be created, edited, duplicated, and deleted. ## Table columns The contracts table shows: | Column | Description | | ----------------------- | -------------------------------------------------------------------- | | Reservation | Reservation number or ID | | Slip/Unit | Unit number or ID | | Renter | Tenant name | | Start Date | Reservation start date | | Contract Status | Current status with icon and color | | Last Sent / Signed Date | Date the contract was last sent (Pending tab) or signed (Signed tab) | | Actions | Generate, Send, or context menu | ## Context menu actions Right-click or click the three-dot menu on any row: ### For unsigned contracts - **Send for Signature** -- Email the signing link - **Resend** -- Send the email again - **Void Contract** -- Invalidate the current contract (allows generating a new one) ### For signed contracts - **View Signed PDF** -- Open the signed document - **Generate PDF** -- Create a PDF if one doesn't exist - **View Reservation** -- Navigate to the reservation detail {% callout type="tip" %} Create templates for each contract type you use -- annual lease, seasonal, transient, dry storage -- so generating new agreements takes just a few clicks. Monitor the "Signed Rate" stat to ensure all active tenants have executed agreements on file. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **"No Contract" showing for active reservations** -- This means no contract has been generated yet. Click Generate to create one from a template. **Tenant says they didn't receive the email** -- Use the Resend option from the context menu. Also check that the signer email address is correct. **Voided contract but need a new one** -- After voiding, the reservation returns to "No Contract" status and you can generate a fresh contract. {% /callout %} --- # Marina Dashboard Source: https://docs.rentaltide.com/marina/dashboard/ > Unified operations dashboard with occupancy metrics, quick links, recent activity, and attention alerts for marina staff. The Marina Dashboard is the central hub for day-to-day marina operations. It provides a real-time snapshot of your marina's status, surfaces items that need attention, and offers one-click navigation to every sub-module. ## Key metrics The dashboard displays six stat cards across the top of the page. Each card is clickable and navigates to the relevant management page. | Metric | Description | Navigates to | | ----------------- | ------------------------------------------------------------- | --------------- | | Occupancy Rate | Percentage of slips currently occupied out of total inventory | Slips & Storage | | Available | Number of slips with status "available" | Slips & Storage | | Waitlist | Number of active waitlist entries | Waitlist | | Scheduled Hauls | Number of haul services with status "scheduled" | Haul Services | | Pending Contracts | Number of contracts awaiting signature | Contracts | | Unbilled | Dollar amount and count of unbilled meter readings | Meter Readings | {% callout type="tip" %} The occupancy rate is calculated as `occupied / total slips`. Slips in maintenance or out-of-service status are included in the total but not counted as occupied. {% /callout %} ## Quick links Below the metrics, the dashboard shows six quick-link cards that provide fast access to each marina sub-module: - **Slips & Storage** -- Manage slip inventory and availability. Shows a badge if any slips are in maintenance. - **Waitlist** -- Manage slip waitlist entries. Shows a badge with the count of active entries. - **Guest Access** -- Manage visitor passes and access codes. Shows a badge with the count of active guest passes. - **Haul Services** -- Schedule haul-out and haul-in services. Shows a badge with the count of upcoming scheduled services. - **Contracts** -- Manage slip rental contracts. Shows a badge if any contracts are pending signature. - **Meter Readings** -- Record and bill utility usage. Shows a badge with the count of unbilled readings. ## Recent activity The left panel of the bottom section shows the most recent activity across your marina, combining: - Upcoming haul service requests (haul-out or haul-in with vessel name) - New waitlist entries Activity items are sorted by recency and show relative timestamps (e.g., "2 hours ago"). ## Attention required The right panel highlights items that need immediate action: - **Pending contracts** -- Contracts awaiting signature, with a link to follow up or send reminders. - **Unbilled meter readings** -- Readings that have not been invoiced yet, with the total pending dollar amount. - **Slips in maintenance** -- Units that are currently in maintenance status and may need status updates. When there are no items requiring attention, the panel shows an "all caught up" message. ## Location selection The dashboard requires a specific location to be selected. If you are viewing "All Locations," a modal will prompt you to choose one. All metrics, activity, and alerts are scoped to the selected location. {% callout type="note" %} Data on the dashboard updates automatically via SWR (stale-while-revalidate). You do not need to refresh the page to see new data, though there may be a brief delay as the cache refreshes. {% /callout %} --- # Guest Access Source: https://docs.rentaltide.com/marina/guest-access/ > Issue and manage guest passes with access codes, ID verification, vehicle tracking, granular permissions, and check-in logging. Guest Access lets you control who can enter your marina facility beyond your slip tenants. Issue digital guest passes with unique 8-character access codes, verify IDs, track vehicles, and set granular permissions for what each guest can access. ## Access types | Type | Description | | --------- | -------------------------------------------------------------------- | | Temporary | One-time or short-duration access with a fixed end date | | Recurring | Ongoing access for regular visitors (e.g., a captain or crew member) | | Permanent | Indefinite access until manually revoked | ## Guest pass details Each guest pass card displays: - **Status** -- Current pass status (Pending, Active, Expired, Revoked) - **Guest name** -- With a verified badge if ID has been checked - **Reservation link** -- Clickable link to the associated reservation - **Access code** -- Unique 8-character code (with copy-to-clipboard button) - **Access type** -- Temporary, Recurring, or Permanent - **Date range** -- Start and end dates - **Relationship** -- How the guest relates to the tenant (e.g., Friend, Family, Crew) - **Vehicle info** -- Make, model, color, and license plate (if provided) - **Visit count** -- Total number of check-ins and date of last visit - **Permissions** -- Labeled badges showing what the guest can access ### Permissions Each guest pass has four independent permission toggles: | Permission | Meaning | | ---------- | ------------------------------------------------------------------------ | | Slip | Guest can access the slip/dock area | | Vessel | Guest can board the vessel | | Facilities | Guest can use marina facilities (restrooms, showers, laundry) | | Operate | Guest is authorized to operate the vessel (highlighted in warning color) | ## Guest pass statuses | Status | Color | Meaning | | ------- | ------ | ---------------------------------- | | Pending | Orange | Pass created but not yet activated | | Active | Green | Pass is currently valid for entry | | Expired | Gray | End date has passed | | Revoked | Red | Pass manually deactivated by staff | ## Creating a guest pass 1. Click **Add Guest** in the header. 2. Enter the **Reservation ID** and **Unit ID** that the guest is visiting. 3. Fill in guest information: - **Guest Name** (required) - **Relationship** (e.g., Friend, Family, Crew) - **Email** and **Phone** (optional) 4. Set access details: - **Access Type** -- Temporary, Recurring, or Permanent - **Start Date** and **End Date** 5. Configure permissions: - Toggle on/off: Slip Access, Vessel Access, Facilities, Operate Vessel 6. Optionally add vehicle details: - Make, Model, Color, License Plate 7. Add **Notes** if needed. 8. Click **Create Guest Pass**. The system generates a unique 8-character access code automatically. ## Verifying an access code Use the **Verify Code** button in the header to check whether an access code is valid: 1. Click **Verify Code**. 2. Enter the 8-character code (auto-uppercased). 3. Click **Verify**. 4. The system returns whether the code is valid, and if so, shows the guest's name and pass details. This is useful for gate staff to quickly validate a visitor's credentials. ## Managing guest passes From the three-dot menu on each guest card: - **Check In** -- Record that the guest has arrived. Increments the visit count and updates the last check-in timestamp. Only available for Active passes. - **Verify ID** -- Mark that the guest's identification has been verified. Adds a verified badge to the card. Only available if not already verified. - **Revoke Access** -- Immediately deactivate the pass. The guest can no longer use the access code. Only available for Active passes. - **Delete** -- Permanently remove the guest pass record. ## Filtering and search - **Status filter** -- Click status chips to filter by Pending, Active, Expired, or Revoked. Each chip shows the count. - **Search** -- Search by guest name, access code, email, or phone number. - **Reservation filter** -- When navigating from a reservation detail page, the page automatically filters to show only guests for that specific reservation. ## Linking to reservations Guest passes are linked to specific reservations. Clicking a guest card navigates to the associated reservation detail page. You can also access the guest list for a specific reservation from the reservation detail page's "Guest Access" section. {% callout type="tip" %} Use the "Operate Vessel" permission carefully -- it indicates the guest is authorized to take the vessel out, which has liability implications. Keep this off by default and only enable it when the tenant explicitly authorizes it. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Access code not working** -- Check whether the pass has expired or been revoked. Use the Verify Code feature to see the current status. **Guest not appearing in the list** -- Make sure the correct location is selected. Guest passes are scoped to the location of the reservation they are linked to. {% /callout %} --- # Haul Services Source: https://docs.rentaltide.com/marina/haul-services/ > Schedule and manage haul-out, put-in, relocation, pressure wash, and other vessel handling services with status tracking and revenue reporting. Haul Services lets you schedule and track all vessel handling operations -- haul-outs, put-ins, round trips, and relocations. Each service request captures the vessel details, customer, scheduled date, pricing, and special instructions so your yard crew has everything they need. ## Service types | Type | Description | | ----------- | -------------------------------------------------------------- | | Haul Out | Lift the vessel out of the water onto land | | Put In | Return a stored vessel to the water (launch) | | Haul & Hold | Haul out and hold on land for a period (e.g., for maintenance) | | Round Trip | Haul out and put back in on the same day | | Relocation | Move a vessel from one location to another | {% callout type="note" %} Available service types can be configured per location from the Admin settings. The haul service settings (`haulServiceSettings`) on each location control which types are offered, pricing, and scheduling parameters. {% /callout %} ## Service statuses Each service progresses through a defined lifecycle: | Status | Color | Meaning | | ----------- | ------ | ----------------------------------------- | | Requested | Orange | Customer or staff submitted a request | | Scheduled | Blue | Date and time have been assigned | | Confirmed | Blue | Customer has confirmed the scheduled date | | In Progress | Orange | Service is currently underway | | Completed | Green | Service has been finished | | Cancelled | Gray | Service was cancelled | ### Status progression A progress bar on each service card visually indicates how far along the service is: | Status | Progress | | ----------- | -------- | | Requested | 16% | | Scheduled | 33% | | Confirmed | 50% | | In Progress | 75% | | Completed | 100% | | Cancelled | 0% | ## Creating a service request 1. Click **New Request** in the header. 2. Select the **service type** (Haul Out, Put In, Haul & Hold, Round Trip, or Relocation). 3. Enter **vessel information**: - Vessel name, LOA, beam, draft, weight - Registration number 4. Link to a **reservation** (optional) to associate the service with an existing slip reservation. 5. Set the **requested date** and **estimated duration** (30 minutes to 4 hours). 6. Add **customer notes** with any special instructions for the crew. 7. Enter optional **add-on services** (e.g., pressure wash, bottom paint checkboxes). 8. Click **Submit Request**. A unique service number is generated automatically (format: `HS[YY][MM]-[XXXX]`, e.g., HS2604-A3BF). ## Managing services ### From the service card Click any service card to open its detail dialog with full vessel info, dates, notes, and action buttons. ### Status actions Available actions depend on the current status: | Current Status | Available Actions | | -------------- | ------------------------ | | Requested | Schedule, Cancel, Delete | | Scheduled | Confirm, Cancel, Delete | | Confirmed | Start, Cancel, Delete | | In Progress | Complete, Cancel | | Completed | Delete (cleanup only) | | Cancelled | Delete | - **Schedule** -- Assign a specific date and time to a requested service. - **Confirm** -- Mark that the customer has acknowledged the schedule. - **Start** -- Begin the service (typically when the crane/forklift is in position). - **Complete** -- Mark the service as finished. - **Cancel** -- Cancel the service with a reason. - **Delete** -- Permanently remove the service record. ## Filtering and search - **Status filter** -- Click status chips to filter by any status. Each chip shows the count. - **Pending filter** -- Quick filter to show only Requested + Scheduled services. - **Search** -- Search by service number, vessel name, registration number, or customer notes. - **Revenue chip** -- The stats bar shows total revenue from completed services. ## Stats summary The stats bar at the top shows: - **Total** -- All services across all statuses - **Pending** -- Combined count of Requested + Scheduled services - **Per-status counts** -- Requested, Scheduled, Confirmed, In Progress, Completed, Cancelled - **Total Revenue** -- Sum of revenue from completed services ## Pricing Haul service pricing is configured at the location level in Admin settings. Pricing can be structured as: - **Flat rate** per service type - **Per-foot rate** based on vessel LOA - **Custom pricing** set on individual service requests The service detail shows the calculated price and any adjustments. {% callout type="tip" %} Use the "Pending" filter each morning to see what needs to be scheduled or confirmed for the day. Keep the service notes detailed -- your crew on the ground relies on them for special handling instructions like mast height, keel type, or blocking requirements. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Service stuck in "Requested" status** -- A staff member needs to manually schedule it by assigning a date and time. Requested services do not auto-schedule. **Cannot delete a completed service** -- Completed services can be deleted for cleanup, but consider whether you need the record for billing or reporting before removing it. {% /callout %} --- # Marina Maps Source: https://docs.rentaltide.com/marina/marina-maps/ > Upload marina dock layouts, place interactive slip pins, and provide visual slip booking for staff and customers. Marina Maps lets you upload images of your dock layouts and storage areas, then place interactive pins on each slip. The result is a visual, color-coded map that staff can use to manage slips and that customers can use to select slips when booking. ## Maps list The main page shows all uploaded maps for the selected location as image cards. Each card displays: - **Map image** -- A thumbnail preview (click to open the pin editor) - **Name** -- The map name (e.g., "Dock A", "Dry Storage Area") - **Description** -- Optional description text - **Active/Hidden status** -- Whether the map is visible to customers - **Display order** -- The order in which maps appear ## Uploading a new map 1. Click **Add Map** in the header. 2. Enter a **map name** (required) -- e.g., "Dock A", "North Basin", "Dry Storage Lot 2". 3. Add an optional **description**. 4. Click **Select Map Image** and choose a file: - Supported formats: JPEG, PNG, WebP, SVG - Maximum file size: 10MB 5. Preview the image in the dialog. 6. Click **Upload**. {% callout type="tip" %} Use aerial photos, architectural drawings, or schematic diagrams of your docks. Higher resolution images provide a better experience when zooming in. SVG format works well for schematic layouts because it scales without losing quality. {% /callout %} ## Editing map details From the three-dot menu on any map card, select **Edit Details** to change the name, description, or replace the image. ## Pin editor Click a map image or select **Edit Pins** from the context menu to open the interactive pin editor. The editor has two panels: ### Map canvas (main area) - **Zoom controls** -- Zoom in, zoom out, and fit-to-screen buttons - **Slip pins** -- Colored circles placed on the map, one for each assigned slip - **Drag to reposition** -- Click and drag any pin to move it to the correct location on the map ### Slip sidebar (left panel) - **Search** -- Filter slips by unit number - **Slip list** -- Shows all slips for the location with their current status and pin placement status - **Drag indicator** -- Drag a slip from the sidebar onto the map to place a new pin ### Pin colors | Color | Status | | ---------------- | ----------------------------- | | Green (#4CAF50) | Available | | Red (#F44336) | Occupied | | Orange (#FF9800) | Reserved | | Gray (#9E9E9E) | Maintenance or Out of Service | ### Saving pins Click **Save** in the editor toolbar to persist all pin positions. Pin positions are stored as X/Y coordinates relative to the map image dimensions. ## Visibility control Each map has an **Active/Hidden** toggle: - **Active** -- The map is visible to customers on the public booking page and in the customer portal. Staff can always see it. - **Hidden** -- The map is only visible to staff in the admin panel. Useful for maps that are under construction or seasonal. Toggle visibility from the three-dot menu: **Hide from Public** or **Show to Public**. ## Deleting a map From the three-dot menu, select **Delete**. A confirmation dialog warns that all slips will be unlinked from the map. The slips themselves are not deleted -- only the map image and pin positions are removed. ## Multiple maps per location You can upload multiple maps for a single location -- for example, one for each dock, a separate one for dry storage, and another for mooring fields. Each map operates independently with its own set of pins. ## Permissions | Action | Required Permission | | ------------------ | ------------------- | | View maps | `inventory_access` | | Upload new maps | `inventory_add` | | Edit maps and pins | `inventory_edit` | | Delete maps | `inventory_delete` | {% callout type="note" %} Maps are location-specific. You must select a specific location (not "All Locations") to view and manage maps. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Pins not saving** -- Make sure to click the Save button in the editor toolbar. Pin positions are not auto-saved. **Map image appears blurry** -- Try uploading a higher-resolution image. For best results, use images that are at least 2000px wide. **Slip not appearing in the sidebar** -- The sidebar shows slips for the current location. Verify that the slip is assigned to the same location as the map. {% /callout %} --- # Meter Readings Source: https://docs.rentaltide.com/marina/meter-readings/ > Record electric and water utility meter readings per slip, calculate usage and charges, and bill tenants through reservations or external invoices. Meter Readings lets you track electric (kWh) and water (gallon) consumption at each slip or storage unit. Enter the current reading, and the system calculates usage since the last entry and computes the charges based on your configured per-unit rate. ## Overview stats The page displays four stats cards: | Stat | Description | | ----------------- | ------------------------------------------------------------------ | | Total Readings | Total number of meter readings recorded | | Unbilled Readings | Readings that have not yet been invoiced | | Unbilled Amount | Total dollar amount of unbilled readings | | By Type | Split showing total electric usage (kWh) and water usage (gallons) | ## Recording a reading 1. Click **Record Reading** in the header. 2. **Select the slip** from the dropdown (shows unit number and name). 3. **Choose the reading type:** - Electric (kWh) - Water (gallons) 4. **Set the reading date** (defaults to today). 5. **Enter the current reading** -- The system will automatically calculate usage by subtracting the previous reading for this slip and type. 6. **Set the rate** (optional) -- Enter a per-unit rate ($/kWh or $/gallon). If left blank, the slip's default rate is used. 7. **Add notes** (optional) -- Any context about the reading. 8. Click **Record**. The system automatically: - Looks up the previous reading for that slip and type - Calculates usage (current - previous) - Computes the amount due (usage x rate) - Adds the reading to the unbilled queue {% callout type="note" %} Readings are ordered chronologically. The system uses the most recent prior reading for the same slip and type to calculate usage. If there is no prior reading, usage will equal the current reading value. {% /callout %} ## Tabs ### Unbilled tab Shows only readings that have not been billed. This is the primary working view. From here you can select readings and bill them. ### All Readings tab Shows every reading regardless of billing status. Useful for reviewing history and verifying data. ## Billing readings There are two ways to bill unbilled readings: ### Add to Reservation This is the primary billing method. It adds utility charges directly to a slip reservation. 1. Select one or more unbilled readings using the checkboxes. 2. Click **Add to Reservation**. 3. Select the **target reservation** from the dropdown. Only billable reservations (not completed, cancelled, or terminated) are shown. The dropdown displays reservation number, renter name, vessel name, and status. 4. Review the reservation details panel showing: - Current renter and vessel - Current reservation total - Current utility charges (Other Surcharges) - New charges being added - Updated total 5. Click **Add Charges to Reservation**. The selected readings are marked as billed and the charges are added to the reservation's "Other Surcharges" line item. ### Mark as Billed (External) Use this when you have invoiced the customer outside of RentalTide (e.g., through a separate accounting system). 1. Select one or more unbilled readings. 2. Click **Mark as Billed (External)**. 3. Enter the **external invoice or receipt number**. 4. Click **Mark as Billed**. The readings are marked as billed with a reference to the external invoice. ## Readings table The readings table shows the following columns: | Column | Description | | -------- | ------------------------------------------------------------------ | | Unit | Slip/storage unit identifier | | Type | Electric or Water (with icon and color coding) | | Date | Date the reading was taken | | Previous | Previous meter reading value | | Current | Current meter reading value | | Usage | Calculated usage (current - previous) with unit label (kWh or gal) | | Amount | Dollar amount due (usage x rate) | | Status | Billed (green) or Pending (orange) | | Actions | Delete button (only for unbilled readings) | ### Bulk selection On the Unbilled tab, you can: - Check individual readings - Use the header checkbox to select/deselect all unbilled readings - The selected count and total amount are displayed above the table ## Deleting readings Only unbilled readings can be deleted. Click the delete icon in the Actions column. Billed readings are locked and cannot be removed. {% callout type="tip" %} Record readings on a consistent schedule -- the first of every month works well for most marinas. This creates predictable billing cycles for your tenants. When multiple readings need to be billed to the same reservation, select them all at once to batch the operation. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Amount shows $0 or dash** -- The reading may not have a rate configured. Check the per-unit rate field when recording, or verify the slip's default rate in its settings. **Usage appears unexpectedly high** -- Verify the previous reading is correct. An incorrect prior entry will inflate the calculated usage. Check for potential equipment faults or unauthorized usage at the slip. **Cannot delete a reading** -- Only unbilled readings can be deleted. If you need to correct a billed reading, record a new adjustment reading. {% /callout %} --- # Marina Reports Source: https://docs.rentaltide.com/marina/reports/ > Occupancy, revenue, and utility usage reports with date range filtering, breakdown by unit type, and export capability. Marina Reports provides data-driven visibility into how your facility is performing. The page is organized into three report tabs -- Occupancy, Revenue, and Utilities -- with a configurable date range and export functionality. ## Date range selection All reports can be scoped to a specific time period using the date range selector in the header: | Option | Period | | ------------- | --------------------------------------- | | Current Month | First to last day of the current month | | Last Month | First to last day of the previous month | | Last 3 Months | Current month plus two prior months | | Last 6 Months | Current month plus five prior months | | Year to Date | January 1 through today | The selected date range is displayed as a banner below the header showing the exact start and end dates. ## Occupancy report The Occupancy tab provides insight into how full your marina is and which unit types have the most availability. ### Summary cards | Card | Description | | -------------- | --------------------------------------------------- | | Total Slips | Total number of slip/storage units across all types | | Occupied | Number of slips currently in "occupied" status | | Occupancy Rate | Overall percentage (occupied / total) | ### Occupancy by unit type A detailed table breaks down occupancy for each unit type: | Column | Description | | -------------- | ------------------------------------------------------------------------------------- | | Unit Type | Wet Slip, Dry Slip, Covered Slip, Dry Storage, Rack Storage, Trailer Storage, Mooring | | Total | Total units of this type | | Occupied | Number currently occupied | | Available | Number currently available | | Occupancy Rate | Percentage occupied (color-coded: green >= 90%, yellow >= 70%, red < 70%) | The occupancy rate for each type is displayed as a colored chip to quickly identify areas of high or low utilization. ## Revenue report The Revenue tab shows projected income based on currently occupied slips and their monthly rates. ### Summary cards | Card | Description | | --------------------------- | ------------------------------------------- | | Monthly Revenue (Projected) | Sum of monthly rates for all occupied slips | | Annual Revenue (Projected) | Monthly revenue multiplied by 12 | ### Revenue by unit type | Column | Description | | --------------- | --------------------------------------- | | Unit Type | The type of slip/storage unit | | Occupied Units | Number of occupied units of this type | | Monthly Revenue | Sum of monthly rates for occupied units | | Annual Revenue | Projected annual revenue (monthly x 12) | A totals row at the bottom sums all unit types. {% callout type="note" %} Revenue projections are based on the `monthlyRate` field of each occupied slip. If you use per-foot pricing or different billing cycles (seasonal, annual), the actual billed amounts may differ from the projections shown here. These figures represent the recurring rate capacity of your current occupancy. {% /callout %} ## Utilities report The Utilities tab shows electric and water usage data from your meter readings, displayed in a side-by-side layout. ### Electric usage | Metric | Description | | -------------- | ------------------------------------------ | | Total Readings | Number of electric meter readings recorded | | Total Usage | Sum of all electric usage in kWh | | Total Charges | Sum of all electric charges in dollars | | Billed | Count of readings that have been invoiced | | Unbilled | Count of readings not yet invoiced | ### Water usage | Metric | Description | | -------------- | ----------------------------------------- | | Total Readings | Number of water meter readings recorded | | Total Usage | Sum of all water usage in gallons | | Total Charges | Sum of all water charges in dollars | | Billed | Count of readings that have been invoiced | | Unbilled | Count of readings not yet invoiced | Billed and unbilled counts are displayed as colored chips (green for billed, orange for unbilled) for quick visual scanning. ## Exporting Click the **Export** button in the header to download the currently visible report data. This is useful for sharing with stakeholders, importing into accounting software, or offline analysis. {% callout type="tip" %} Run the occupancy report weekly to identify underutilized areas. If certain unit types consistently have low occupancy, consider adjusting pricing or marketing those spaces differently. Compare the utilities report against revenue to understand what percentage of income goes to utility costs. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Revenue shows $0 for occupied slips** -- Check that the monthly rate is set on those slips. Slips without a configured rate will contribute $0 to the projection. **Utility data appears incomplete** -- The report shows all readings in the system. If readings are missing, verify that meter readings are being recorded on schedule from the Meter Readings page. {% /callout %} --- # Slip Reservations Source: https://docs.rentaltide.com/marina/reservations/ > Full lifecycle management for slip reservations including creation, billing cycles, status transitions, payments, and detailed reservation views. Reservations track the full lifecycle of a marina tenant -- from initial booking through active occupancy and eventual departure. Each reservation ties a customer to a slip for a defined period with a configurable billing cycle. ## Reservations list The main reservations page displays all reservations for the selected location in a data grid with the following columns: | Column | Description | | ------------- | ---------------------------------------------------------------- | | Reservation # | Auto-generated identifier (click to open detail page) | | Slip | Unit number and section | | Customer | Renter's full name | | Vessel | Vessel name and LOA | | Start Date | When the reservation begins | | Billing | Billing cycle (Hourly, Daily, Weekly, Monthly, Seasonal, Annual) | | Amount | Total reservation amount | | Payment | Payment status indicator | | Status | Current reservation status | | Contract | Whether a contract has been signed | ## Creating a reservation There are two ways to create a reservation: ### From the admin panel 1. Click **Create Reservation** in the header. This opens the public slip booking page in a new tab, pre-filtered to your location. 2. Select a slip, set dates, enter customer and vessel information, and complete the booking. ### Add Client (quick create) 1. Click **Add Client** to open the quick-create modal. 2. Enter customer information, select a slip, set billing details. 3. The system creates both the customer record and reservation in one step. ## Reservation statuses | Status | Color | Meaning | | ---------- | ------ | ------------------------------------------ | | Pending | Orange | Created but not yet confirmed or paid | | Confirmed | Blue | Confirmed, awaiting start date or check-in | | Active | Green | Tenant is currently occupying the slip | | Completed | Gray | Reservation has ended (checked out) | | Cancelled | Red | Reservation was cancelled | | Terminated | Red | Reservation was terminated early by staff | ### Status transitions The status can be advanced through the status chip on each row. Available transitions depend on the current status: | Current Status | Available Actions | | -------------- | -------------------------- | | Pending | Confirm, Send Bill, Cancel | | Confirmed | Check In, Cancel | | Active | Check Out, Cancel | | Completed | (no further transitions) | | Cancelled | (no further transitions) | Click the status chip on any row to see and select available transitions. ## Payment statuses | Status | Color | Meaning | | -------- | ------ | ------------------------- | | Pending | Orange | No payment received yet | | Partial | Blue | Partial payment received | | Paid | Green | Fully paid | | Overdue | Red | Payment is past due | | Refunded | Gray | Payment has been refunded | ## Billing cycles Reservations support the following billing frequencies: - Hourly - Daily - Weekly - Monthly - Seasonal - Annual The billing cycle determines when recurring charges are generated and how the total is calculated. ## Filtering and search The filter bar provides multiple ways to narrow down reservations: - **Search** -- By reservation number or vessel name. - **Status** -- Select from Pending, Confirmed, Active, Completed, or Cancelled. - **Payment** -- Filter by payment status (Pending, Partial, Paid, Overdue, Refunded). - **Billing** -- Filter by billing cycle. - **Date range** -- Set a "Start From" and "Start To" date to filter by reservation start date. - **Clear Filters** -- Resets all filters at once. ### Stats cards Above the filters, clickable stats cards show the count of reservations by status (Pending, Confirmed, Active, Completed, Cancelled). Click a card to toggle that status filter on or off. ## Reservation detail page Click any reservation row to navigate to the full detail page. The detail page is a two-column layout with: ### Left column - **Hero card** -- Status, reservation number, slip/unit info, dates, and quick-action buttons - **Reservation details** -- Customer info, vessel details, billing cycle, and rates - **Billing** -- Itemized breakdown of charges, taxes, surcharges, and utility charges - **Payment summary** -- Amount due, amount paid, outstanding balance - **Payment history** -- Complete list of all payments and refunds - **Ledger** -- Full accounting ledger for the reservation ### Right column - **Timeline** -- Chronological status history with timestamps - **Contract** -- Contract status and signing details - **Notes** -- Internal staff notes - **Messages** -- Message thread with the customer - **Guest access** -- Guest passes associated with this reservation ### Available actions from the detail page - **Confirm / Check In / Check Out** -- Advance the reservation status - **Send Bill** -- Generate and email an invoice to the customer - **Cancel** -- Cancel with a reason (opens cancel dialog) - **Manage Billing** -- Adjust rates, surcharges, and billing details - **Process Payment** -- Record or process a payment - **Add Card** -- Save a payment method to the reservation - **Issue Refund** -- Process a full or partial refund - **Transfer** -- Move the reservation to a different slip - **Edit Customer** -- Update customer contact information - **Delete** -- Permanently remove the reservation (admin only) {% callout type="tip" %} Use the "Send Bill" action from the Pending status to email an invoice to the customer before confirming. This lets customers pay online before their start date. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Slip appears double-booked** -- The system warns before confirming overlapping reservations. Check whether an expired reservation was not properly checked out. **Billing not processing** -- Verify that the tenant has a valid payment method on file. Check the payment history for declined charges. {% /callout %} --- # Slips & Storage Source: https://docs.rentaltide.com/marina/slips/ > Manage physical slip and storage inventory with unit types, dimensions, amenities, pricing tiers, sections, and bulk operations. The Slips & Storage page is where you manage your marina's physical inventory -- every wet slip, dry storage bay, rack position, mooring, and trailer spot. Each unit has a type, dimensions, amenities, and a full pricing schedule that supports hourly through annual billing. ## Unit types RentalTide supports seven unit types that cover the full range of marina and storage operations: | Type | Description | | --------------- | ---------------------------------------------- | | Wet Slip | In-water berth alongside a dock or finger pier | | Dry Slip | Dry dock berth (not rack storage) | | Covered Slip | In-water berth with overhead cover | | Dry Storage | Indoor or outdoor on-land storage space | | Rack Storage | Stacked dry-rack position for smaller vessels | | Trailer Storage | On-land trailer parking spot | | Mooring | Offshore mooring ball or buoy | ## Adding a slip Click **Add Slip** in the top-right corner to open the slip form. The form is organized into tabs: ### Basic information | Field | Description | | ----------- | --------------------------------------------------------------------------------------------------- | | Unit Number | Required. A unique identifier (e.g., A-12, D3-07). Must be unique within the location. | | Unit Name | Optional friendly name (e.g., "Premium End-Tie") | | Unit Type | One of the seven types listed above | | Section | Organizational group (e.g., "Dock A", "North Basin"). Select an existing section or type a new one. | ### Dimensions | Field | Description | | ---------------- | -------------------------------------------------------------- | | Length (ft) | Physical length of the slip | | Width (ft) | Physical width of the slip | | Depth (ft) | Water depth at the slip | | Max Draft (ft) | Maximum vessel draft the slip can accommodate | | Max Beam (ft) | Maximum vessel beam allowed | | Max LOA (ft) | Maximum vessel length overall | | Max Weight (lbs) | Weight limit (relevant for rack and dry storage) | | Max Height (ft) | Height clearance (relevant for covered slips and rack storage) | ### Amenities | Amenity | Options | | --------------- | ---------------------------------------------------------------------------------------- | | Shore Power | Toggle on/off. If enabled, set amperage (30, 50, or 100 amp) and voltage (120V or 240V). | | Water | Toggle on/off | | WiFi | Toggle on/off. If enabled, optionally specify the network name. | | Bathroom Access | Toggle on/off | | Shower Access | Toggle on/off | ### Pricing Each slip supports flat-rate and per-foot pricing across multiple billing cycles: | Rate Type | Available Cycles | | ---------------- | ----------------------------------------------------------- | | Flat Rate | Hourly, Daily, Weekly, Monthly, Quarterly, Seasonal, Annual | | Per-Foot Rate | Hourly, Daily, Weekly, Monthly, Seasonal, Annual | | Security Deposit | One-time deposit amount | You can also configure quantity-based pricing discounts per cycle. ### Additional settings - **Show to Public** -- Whether this slip appears on the public booking page. - **Internal Notes** -- Staff-only notes not visible to customers. - **Tax Rates** -- Select which of your location's tax rates apply to this slip. ## Bulk create For large marinas, use **Bulk Create** to generate many slips at once: 1. Click **Bulk Create** in the header. 2. Set a **prefix** (e.g., "A-") and a **number range** (e.g., 1 to 50). 3. Optionally enable **zero-padding** (e.g., A-01 through A-50). 4. Set **defaults** for unit type, section, dimensions, amenities, and rates. 5. Click **Create** to generate all slips in a single transaction. {% callout type="note" %} Bulk create is limited to 500 slips per operation. If any unit number already exists, the entire batch is rolled back and no slips are created. The system will auto-bill your subscription if the new slips exceed your current quota. {% /callout %} ## Bulk edit 1. Click **Bulk Edit** in the header to enter selection mode. 2. Check individual slips or use the **Select All** checkbox. 3. Click **Edit** to open the bulk edit modal. 4. Change any combination of fields -- section, unit type, amenities, rates, etc. 5. Click **Apply** to update all selected slips at once. ## Sections Sections are organizational groupings (e.g., "Dock A", "South Basin", "Dry Storage Lot 2"). They help you filter and manage large inventories. - **Create a section** by typing a new name in the Section field when adding or editing a slip. - **Manage sections** by clicking the gear icon in the header. From there you can rename a section (all slips in that section are updated) or delete a section (clears the section assignment from those slips). - **Filter by section** using the Section dropdown in the filter bar. ## Viewing slips Toggle between **Grid view** (cards) and **List view** (compact rows) using the view toggle in the filter bar. ### Filtering - **Search** by unit number, name, or section. - **Status filter** -- Click a status chip (Available, Occupied, Reserved, Maintenance, Out of Service) to filter. Click again to clear. - **Unit Type filter** -- Select a specific type or view all. - **Section filter** -- Select a specific section or view all. ### Status indicators | Status | Color | Meaning | | -------------- | ------ | -------------------------------------------- | | Available | Green | Open and ready for a new reservation | | Occupied | Red | Currently assigned to an active tenant | | Reserved | Orange | Assigned to a confirmed upcoming reservation | | Maintenance | Blue | Temporarily out of service for repairs | | Out of Service | Gray | Permanently decommissioned or soft-deleted | You can change a slip's status directly from the slip card menu without opening the edit form. ## Deleting a slip When you delete a slip, two options are available: - **Soft delete** (default) -- Sets the status to "Out of Service." The slip is hidden from availability searches but its history and reporting data are preserved. - **Hard delete** -- Permanently removes the slip from the database. This is only allowed if the slip has no active reservations. Hard-deleting a slip also decrements your subscription count. {% callout type="warning" %} Hard-deleting a slip is irreversible. If the slip has any active reservations, the deletion will be blocked. Use soft delete to preserve audit trails. {% /callout %} ## Subscription Adding slips requires an active slip subscription. Each slip is billed at $10/month. When you add a slip that exceeds your current quota, the system automatically bills for the overage. The subscription banner at the top of the page shows your current usage and remaining capacity. --- # Slip Waitlist Source: https://docs.rentaltide.com/marina/waitlist/ > Manage a first-come-first-served queue for slip availability with customer preferences, offer workflow, and automatic position tracking. The marina waitlist manages demand when slips are full. Customers join a prioritized queue specifying their vessel requirements and slip preferences. When a matching slip opens up, staff can send offers directly to waitlisted customers. ## Waitlist entries Each waitlist entry is displayed as a card showing: - **Position number** -- Queue position (e.g., #1, #2, #3) - **Status** -- Current entry status - **Customer name** -- Linked from the customer database (clickable to navigate to customer profile) - **Vessel info** -- Vessel name and minimum LOA requirement - **Preferences** -- Preferred unit types and sections - **Start date** -- When the customer wants to move in (with flexible/fixed indicator) - **Budget** -- Maximum monthly rate the customer is willing to pay - **Notes** -- Free-text notes from the customer or staff - **Date added** -- When the entry was created ## Waitlist statuses | Status | Color | Meaning | | --------- | ------ | ------------------------------------------ | | Active | Blue | Customer is actively waiting for a slip | | Offered | Yellow | A slip offer has been sent to the customer | | Accepted | Green | Customer accepted an offer | | Declined | Red | Customer declined an offer | | Expired | Gray | Offer expired without response | | Cancelled | Gray | Entry was cancelled by staff or customer | ## Adding a customer to the waitlist 1. Click **Add to Waitlist** in the header. 2. **Select or create a customer:** - Search for an existing customer by name, email, or phone. - Or click "Create new customer" and enter first name, last name, email, and phone. 3. **Set preferences:** - **Preferred Unit Types** -- Multi-select from Wet Slip, Dry Slip, Covered Slip, Dry Storage, Rack Storage, Trailer Storage, or Mooring. - **Min Length (ft)** -- Minimum slip length required for the vessel. - **Max Monthly Rate** -- Budget cap for monthly rent. - **Desired Start Date** -- When the customer wants to begin. - **Flexible Start** -- Toggle on if the customer is flexible about timing. 4. **Set utility requirements:** - **Requires Water** -- Toggle on if water hookup is needed. - **Requires WiFi** -- Toggle on if WiFi is needed. 5. **Notes** -- Add any additional context. 6. Click **Add to Waitlist**. The customer is placed at the end of the queue. Position is automatically calculated based on the number of existing active entries. ## Making an offer When a slip becomes available, you can offer it to a waitlisted customer: 1. Click the three-dot menu on the waitlist card and select **Make Offer**. 2. **Select Available Slip** -- Choose from slips currently in "available" status. The dropdown shows unit number, type, length, and monthly rate. 3. **Set Offer Expiration** -- Choose how long the customer has to respond: 24 hours, 48 hours, 72 hours, or 1 week. 4. Click **Send Offer**. The entry status changes to "Offered" and an expiration countdown begins. ### Offer outcomes - **Accepted** -- The customer confirms, and the entry status changes to "Accepted." You can then create a reservation for the customer. - **Declined** -- The customer declines. A decline counter increments. After 3 declines (the default `maxDeclines` setting), the entry is automatically removed. - **Expired** -- If the offer expires without response, the entry reverts to "Active" status and can be offered a slip again. ## Matching engine The backend provides a matching endpoint that finds eligible waitlist entries for a specific slip. When a slip becomes available, you can use this to identify which customers match based on: - Unit type preference - Minimum length requirement - Power and water requirements - Section preference - Queue position (first-come-first-served priority) ## Managing entries From the three-dot menu on each card, you can: - **Make Offer** -- Send a slip offer (only available for Active entries) - **Cancel** -- Mark the entry as cancelled. Queue positions are automatically recalculated. - **Delete** -- Permanently remove the entry from the database. Queue positions are recalculated. ## Filtering and search - **Status filter** -- Click status chips (Active, Offered, Accepted, Declined, Expired, Cancelled) to filter. Chips show the count for each status. - **Search** -- Search by customer name, email, renter ID, vessel name, or notes. - **Results count** -- Shows "Showing X of Y entries" below the search bar. {% callout type="tip" %} Set offer expiration to 48 hours as a default balance between giving customers time to decide and keeping the queue moving. Review the waitlist weekly and reach out to customers who have been waiting longest to confirm they are still interested. {% /callout %} {% callout type="note" %} Position numbers are automatically recalculated when entries are cancelled or deleted. There is no way to manually reorder the queue -- it is strictly first-come-first-served. {% /callout %} --- # Marketing Source: https://docs.rentaltide.com/marketing/ > Promo codes, gift cards, affiliates, bundles, private links, cart recovery, templates, and website builder The marketing tools help you attract new customers, recover lost sales, and keep your brand consistent across every touchpoint. From discount codes and referral programs to automated email sequences and a drag-and-drop website builder, everything you need to grow your business lives here. {% cardGroup cols=3 %} {% card title="Promo codes" href="/marketing/promo-codes/" /%} {% card title="Gift cards" href="/marketing/gift-cards/" /%} {% card title="Affiliate program" href="/marketing/affiliate/" /%} {% card title="Bundles" href="/marketing/bundles/" /%} {% card title="Private links" href="/marketing/private-links/" /%} {% card title="Cart recovery" href="/marketing/cart-recovery/" /%} {% card title="Email templates" href="/marketing/email-templates/" /%} {% card title="Text templates" href="/marketing/text-templates/" /%} {% card title="Website builder" href="/marketing/website-builder/" /%} {% /cardGroup %} --- # Affiliate program Source: https://docs.rentaltide.com/marketing/affiliate/ > Referral tracking with unique URLs, commission rates, QR codes, and payout management The affiliate program lets you set up referral partnerships where affiliates earn a commission on bookings they send your way. Each affiliate gets a unique tracking URL, and the system automatically attributes bookings and calculates earnings. --- ## Dashboard overview The top of the affiliates page shows summary stat cards: | Card | What it shows | | ---------------- | ------------------------------------------ | | Total Affiliates | Number of registered affiliates | | Total Earned | Lifetime commissions across all affiliates | | Total Paid | Amount already paid out | | Pending | Commissions earned but not yet paid | --- ## Adding an affiliate 1. Click **+ New Affiliate**. 2. Enter the affiliate's **name** and **email**. 3. Set the **commission percentage** (applied to the base booking amount, before taxes and fees). 4. Optionally enable **pass cost to customer** -- adds the commission as a surcharge on the booking rather than coming out of your revenue. 5. Save. The system generates a **unique tracking URL** with a `?ref=` parameter: ``` https://app.rentaltide.com/booking/customerId/{yourId}?ref={affiliateId} ``` Share this URL with the affiliate. Any booking made through that link is automatically attributed to them. --- ## Affiliate table Each row in the affiliate table shows: | Column | Description | | ------------ | ----------------------------------- | | Name | Affiliate name and email | | Commission | Percentage earned per booking | | Total Earned | Lifetime commissions | | Paid Out | Amount already disbursed | | Pending | Commissions earned but not yet paid | | Actions | Menu with available operations | --- ## Managing affiliates ### Edit Update the affiliate's name, email, or commission rate. Changes apply to future bookings only -- existing commissions are not recalculated. ### Record payout Log a payment to the affiliate to move pending earnings to the paid column: 1. Click the **Record Payout** action. 2. Enter the payout amount. 3. The system deducts the amount from pending and adds it to paid. Payouts are tracked for audit purposes but the actual payment is made outside the system (via check, bank transfer, etc.). ### Location restrictions Click **Manage Locations** to restrict which of your business locations the affiliate can refer bookings to. By default, affiliates can refer to all locations. ### Staff assignments Click **Manage Staff** to assign staff members to an affiliate. Staff members linked to an affiliate can view the affiliate's performance data and manage their account. ### Generate QR code Create a QR code for the affiliate's tracking URL. The QR code opens the booking page with the affiliate's referral parameter pre-filled. Use the **Download** button to save the QR code image. ### Marketing flyer Generate a printable marketing flyer that includes the affiliate's QR code and booking URL. Affiliates can distribute the flyer at hotels, tourism offices, or events. ### Copy URL Copy the affiliate's tracking URL to the clipboard for sharing via email, text, or social media. ### Delete Remove an affiliate from the system. Historical commission data is preserved for reporting purposes. --- ## Linking affiliates to promo codes Affiliates can be linked to promo codes so their referrals receive a discount while you track the source: 1. Go to **Marketing > Promo Codes**. 2. Create or edit a promo code. 3. In the **Affiliate** targeting field, select the affiliate. 4. When a customer uses the promo code, the booking is attributed to the affiliate. This gives affiliates two attribution paths: the tracking URL and the linked promo code. --- ## Commission calculation - Commission is calculated on the **base booking amount** before taxes, fees, and add-ons. - When **pass cost to customer** is enabled, the commission percentage is added as a surcharge on the booking total, so the affiliate's commission comes from the customer rather than your margin. - Commission is accrued at the time of booking and moves to "pending" status. It stays pending until you record a payout. {% callout type="tip" %} Each affiliate gets a unique ID so there is no overlap in attribution. Use QR codes for physical distribution at hotels, concierge desks, and tourism offices. Link affiliates to promo codes to offer referral discounts while tracking the source. Pay affiliates on your own schedule -- the system tracks pending amounts until you record a payout. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Affiliate not getting credit for bookings** -- The customer must use the affiliate's tracking URL or linked promo code to book. Direct bookings or bookings through a different link will not be attributed. **Commission amount seems off** -- Commission is calculated on the base booking amount, not including taxes, fees, or add-ons. Verify the commission percentage on the affiliate's profile. **QR code not generating** -- The QR code is generated using an external service. Ensure your internet connection is active and try again. **Cannot delete an affiliate** -- You must have admin permissions to delete affiliates. The delete action is permanent for the affiliate record, though historical commission data is preserved. {% /callout %} --- # Bundles Source: https://docs.rentaltide.com/marketing/bundles/ > Multi-item discount packages with inventory, gift cards, and memberships Bundles let you package multiple items together at a discounted rate. Customers see the bundle as a single option in the booking widget, select it, and all included items are added to the cart with the discount applied automatically. This page covers creating and managing bundles from the marketing perspective. For fleet-level bundle configuration, see the [Fleet > Bundles](/fleet/bundles) page. --- ## Creating a bundle 1. Click **+ New Bundle**. 2. Enter a **name** and **description**. 3. Choose a **discount type**: | Discount type | How it works | | -------------- | --------------------------------------------------------------------------- | | Percentage off | Reduces the combined price of all items by a percentage | | Fixed price | Sets a flat price for the entire bundle regardless of individual item rates | | Free item | Makes one or more items in the bundle free -- customer pays for the rest | 4. Set the **discount value** (percentage or dollar amount). 5. **Add items** -- select from three item types: | Item type | Description | | ---------- | ------------------------------------------ | | Inventory | A rentable asset from your fleet | | Gift card | A prepaid gift card with a specified value | | Membership | A membership tier enrollment | 6. For each item, set the **quantity** and optionally mark it as a **free item** or set a **per-item discount percentage**. 7. Optionally configure: - **Location** -- restrict the bundle to a specific location - **Start date / End date** -- availability window - **Max redemptions** -- cap on total purchases - **Active** toggle -- enable or disable the bundle 8. Save the bundle. --- ## Bundle cards Each bundle appears as a card on the management page showing: - **Name and description** - **Discount type and value** (with an icon indicator: percentage, dollar, or gift card) - **Included items** with their type icons (boat for inventory, gift card, or membership badge) - **Redemption count** -- how many times the bundle has been purchased - **Status chip** -- Active or Inactive - **Actions menu** -- Edit or Delete --- ## Customer experience 1. Customer opens your booking widget. 2. They see a **Bundle** option alongside individual items. 3. Selecting the bundle adds all included items to the cart with the discount pre-applied. 4. They complete checkout as a single transaction. Each bundled item is managed individually after booking -- separate check-in, separate asset assignment, separate tracking on the manifest and journey board -- but they share a **bundle group ID** that ties them together as a single order. --- ## Scheduling bundles Use the start date, end date, and max redemptions fields to create time-limited promotional bundles: - **Flash sales** -- set a short validity window (e.g., this weekend only) - **Seasonal packages** -- match your bundle dates to your pricing seasons - **Limited availability** -- set max redemptions to create scarcity (e.g., "Only 20 available") When a bundle expires or reaches its redemption cap, it automatically stops appearing on the booking widget. {% callout type="tip" %} Create family packages (e.g., two kayaks + one paddleboard), adventure bundles (e.g., jet ski + snorkel gear), or gift combos (e.g., rental + gift card). Individual items within a bundle are managed separately for operations -- each gets its own booking entry for asset tracking and check-in. The bundle group ID lets you see which bookings belong together. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Bundle not showing in the widget** -- All inventory items included in the bundle must be available for the selected date and time. If even one item is unavailable, the bundle will not appear. Also check that the bundle is active and within its date range. **Discount not applying correctly** -- Double-check the discount type and value in the bundle configuration. For percentage discounts, verify the percentage is set on the bundle, not on individual items (unless using per-item discount mode). **Cannot add a membership or gift card** -- Ensure you have membership tiers or gift card support configured before adding those item types to a bundle. {% /callout %} --- # Cart recovery Source: https://docs.rentaltide.com/marketing/cart-recovery/ > Capture abandoned checkouts and follow up with AI-generated emails, SMS, or recovery links Cart recovery captures booking sessions where a customer entered their personal information but did not complete checkout. Follow up via email, SMS, or a direct recovery link to help them finish their booking. --- ## Viewing abandoned carts The cart recovery table shows all abandoned sessions for the selected location, sorted by most recent first. Each row displays: | Column | Description | | --------- | ------------------------------------------------------------------- | | Customer | First name, last name, and email address | | Phone | Phone number (if provided during checkout) | | Item | The inventory item left in the cart (name from BookingInfo) | | Date | The selected booking start date | | Amount | Total price and deposit due now | | Abandoned | Relative time since the session was abandoned (e.g., "2 hours ago") | | Status | Whether the customer has been contacted | | Actions | Menu with follow-up options | ### Status indicators - **Not contacted** -- the customer has not been reached out to yet. These rows are highlighted for attention. - **Contacted** -- a follow-up has been sent. The timestamp of the last contact is recorded. --- ## Follow-up actions Click the actions menu on any row to access follow-up options: | Action | What it does | | ------------------ | ---------------------------------------------------------------------------------------------------------------------- | | Copy Recovery Link | Copies a URL that restores the customer's cart exactly as they left it -- same dates, quantities, add-ons, and pricing | | Send Email | Opens a dialog to send an AI-generated recovery email personalized with the customer's name and cart contents | | Send SMS | Sends an AI-generated text message with a recovery link (requires phone number and Twilio integration) | | Delete | Removes the abandoned cart from the list | ### AI-generated messages When you choose **Send Email** or **Send SMS**, the system generates a personalized message using AI. The message includes: - The customer's name - The specific item they were booking - The selected dates and times - A direct recovery link You can preview and edit the message before sending. The AI adapts tone and content based on how long ago the cart was abandoned. ### Recovery links Recovery links restore the full cart state, including: - Selected inventory item - Chosen dates and times - Quantity - Add-ons (both inventory-specific and global) - Billing address (if entered) - Deposit amount and pricing The recovery URL format is: `{your-domain}/checkout/recover/{cartId}` --- ## Capture requirements Cart recovery only captures sessions where the customer has entered **personal information** during checkout: - Name (first or last name) - Email address - Phone number Anonymous browsing sessions where the customer only viewed inventory without entering any identifying information are not captured. --- ## Timing Abandoned carts have a **TTL (time to live)** -- they are automatically cleaned up after a set period. The most effective recovery happens quickly, so prioritize carts that were abandoned most recently. --- ## Email template Cart recovery emails use the **Cart Abandoned** (`cart_abandoned`) template in your email templates configuration. Customize the template content, subject line, and styling from **Marketing > Email Templates**. You can also configure scheduling to automatically send cart recovery emails after a set delay (e.g., 30 minutes after abandonment). {% callout type="tip" %} Follow up quickly -- recovery rates drop sharply after the first few hours. The AI-generated messages are personalized with the customer's name and specific items, so they feel natural rather than templated. Recovery links restore the full cart including selected dates, quantities, and add-ons. Set up automated cart recovery emails using the scheduling configuration on your Cart Abandoned email template. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **No abandoned carts showing up** -- Cart recovery requires the customer to have entered at least their name or email during checkout. Sessions where the customer only browsed inventory without entering personal details are not captured. **Recovery link expired** -- If the original availability has changed or the cart TTL has passed, the recovery link may no longer work. Create a private link with the same items and pricing to send instead. **SMS not sending** -- Verify that Twilio is configured in your location settings and that the customer's phone number is valid. The phone number must include a country code. **Customer received the email but the link shows an error** -- The item may no longer be available for the originally selected date. The customer will need to select new dates or contact your team for assistance. {% /callout %} --- # Email templates Source: https://docs.rentaltide.com/marketing/email-templates/ > Automated email templates with trigger-based sending, merge tags, scheduling, and visual editing Email templates let you automate customer communication at every stage of the booking lifecycle. Each template is tied to a trigger event, supports merge tags for personalization, and can be edited with a basic HTML editor or a visual drag-and-drop builder. --- ## Template list The templates page shows a table of all configured email templates for the selected location. Each row displays: | Column | Description | | ---------- | ------------------------------------------------------------ | | Template | Name and trigger type | | Subject | Email subject line | | Status | Enabled or disabled | | Scheduling | Trigger event and timing (e.g., "Immediately after booking") | | Actions | Preview, edit, schedule, style, copy, or delete | Switch between the **Templates** tab and the **Domain Auth** tab using the tab bar at the top. --- ## Available triggers Each template is linked to a trigger event that determines when the email fires: | Trigger | When it sends | | -------------------------------- | ------------------------------------------------------ | | Booking Confirmation | Booking is confirmed | | Booking Approval | Booking requires and receives approval | | Booking Denial | Booking approval is denied | | Booking Cancellation | Booking is cancelled | | Booking Reschedule | Booking is rescheduled | | Booking Weather Reschedule | Booking is rescheduled due to weather | | Booking Reminder | Configurable time before the booking start | | Booking Refund | Refund is issued | | Collect Booking Deposit | Deposit payment is processed | | Release Booking Deposit | Security deposit is released | | Cart Abandoned | Customer abandons checkout | | Request Review | Post-booking review request | | Waitlist Confirmation | Customer is added to the waitlist | | Waitlist Booking Available | Waitlisted date becomes available | | Yearly Reminder | Annual re-engagement email | | Gift Card | Gift card is purchased and delivered | | Tip Confirmation | Tip is received after completion | | Tour Pending Minimum | Tour has not yet met the minimum participant threshold | | Tour Confirmed | Tour reaches minimum and is confirmed | | Tour Cancelled | Tour is cancelled | | Tour Completed (Review Request) | Tour completes and review is requested | | Bundle / Cart Order Confirmation | Bundle order is confirmed | | Booking Link | Private link is sent to a customer | | Slip: Bill Sent | Marina slip bill is issued | | Slip: Hold Expired | Marina slip hold has expired | | Save My Spot Confirmation | Save My Spot reservation is confirmed | | Save My Spot Reminder | Reminder for a Save My Spot reservation | | Payment Plan: Upcoming Payment | Payment plan installment is approaching | | Payment Plan: Payment Charged | Payment plan installment is charged | | Payment Plan: Payment Failed | Payment plan charge fails | | Payment Plan: Plan Cancelled | Payment plan is cancelled | | Post-Trip Summary | Summary sent after trip completion | | Payment Captured | Remaining balance payment is captured | | Invoice: Balance Due | Invoice with outstanding balance is sent | | Manual | Sent manually by staff from the booking detail page | --- ## Editing a template ### Basic HTML editor Open a template to edit the **subject line** and **body** using a rich text editor (ReactQuill). Insert merge tags using the placeholder button, which shows a dropdown of all available variables. ### Visual editor (Unlayer) Click the **Visual Editor** button to open the Unlayer drag-and-drop email builder. Design emails visually with: - Pre-built content blocks (text, images, buttons, dividers, social links) - Drag-and-drop layout with columns and rows - Responsive design that adapts to mobile - Custom merge tag insertion - Design templates and style presets ### Conditional blocks Insert conditional blocks that show or hide content based on booking data. For example, show a damage deposit section only when a deposit is held, or show tour-specific information only for tour bookings. --- ## Merge tags Insert merge tags to personalize each email with real booking data. Tags are replaced at send time. ### Common merge tags | Tag | Description | | ------------------------------------ | -------------------------------------- | | `{{CustomerInfo.FirstName}}` | Customer's first name | | `{{CustomerInfo.LastName}}` | Customer's last name | | `{{CustomerInfo.Email}}` | Customer's email | | `{{BookingInfo.StartDate}}` | Booking start date | | `{{BookingInfo.EndDate}}` | Booking end date | | `{{BookingInfo.CompanyName}}` | Your company name | | `{{BookingInfo.CompanyPhoneNumber}}` | Your company phone | | `{{BookingInfo.Address}}` | Location address | | `{{InventoryInfo.inventoryName}}` | Name of the booked item | | `{{RentalPricing.Total}}` | Total booking price | | `{{RentalPricing.Currency}}` | Currency code | | `{{PaymentInfo.DepositDueNow}}` | Deposit amount due | | `{{PaymentInfo.RemainingBalance}}` | Remaining balance after deposit | | `{{OrderUrl}}` | Link to the customer's Next Steps page | | `{{GiftCard.code}}` | Gift card code (for gift card emails) | | `{{GiftCard.amount}}` | Gift card amount with currency | The full list of available variables is accessible through the placeholder button in both the basic and visual editors. --- ## Scheduling Configure when the email sends relative to the trigger event: | Timing | Description | | ---------------- | --------------------------------------------------------------------------------------- | | Immediately | Sends as soon as the trigger fires | | X minutes before | Sends a set number of minutes before the event (e.g., 120 minutes before booking start) | | X minutes after | Sends a set number of minutes after the event | The scheduling configuration dialog lets you set the delay in minutes and choose the direction (before or after). Each template can be independently enabled or disabled. --- ## Style presets Apply visual themes to your emails from the style presets library. Presets control colors, fonts, button styles, and overall layout. You can also create custom style presets with your brand colors and save them for reuse. --- ## Domain authentication The **Domain Auth** tab provides instructions for configuring SendGrid DNS records to send emails from your own domain. This improves deliverability and prevents emails from landing in spam. Required DNS records: - **SPF** -- Sender Policy Framework - **DKIM** -- DomainKeys Identified Mail - **DMARC** -- Domain-based Message Authentication --- ## Copying templates ### Copy to location Duplicate a single template to one or more other locations. The copy includes the template content, subject, body, scheduling configuration, and style settings. ### Copy all templates Duplicate all templates from the current location to another location in one operation. An overwrite confirmation dialog appears if the destination location already has templates for the same triggers. {% callout type="tip" %} Always send a test email to yourself before activating a template. Use the preview feature to see exactly what customers will receive with real booking data. Keep email content concise and action-oriented -- customers scan, they do not read paragraphs. Check the mobile preview since most customers read email on their phone. Set up per-location branding if your locations have different logos or color schemes. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Emails not sending** -- Verify the template is enabled, the trigger is set correctly, and your domain authentication (SendGrid DNS) is properly configured. **Merge tags showing as raw text** -- Check the tag syntax. Tags must be wrapped in double curly braces with the correct path: `{{CustomerInfo.FirstName}}` not `{{ CustomerInfo.FirstName }}`. **Emails going to spam** -- Complete your domain authentication setup with SPF, DKIM, and DMARC records. Sending from an unauthenticated domain significantly increases spam filtering. **Email looks different in some clients** -- Email rendering varies across clients (Gmail, Outlook, Apple Mail). Use the visual editor's built-in compatibility mode and test across multiple clients. **Template copy failed** -- Ensure you have admin permissions at the destination location. The copy operation may show an overwrite confirmation if templates already exist for the same trigger at the destination. {% /callout %} --- # Gift cards Source: https://docs.rentaltide.com/marketing/gift-cards/ > Create, sell, and redeem digital gift cards with balance tracking, analytics, and promotions Gift cards let you sell prepaid balances that customers can apply toward future bookings. Create and send gift cards directly from the dashboard, let customers purchase them through your booking widget, and run gift card promotions to boost sales. --- ## Dashboard overview The top of the gift cards page shows analytics cards summarizing your gift card program: | Card | What it shows | | ------------------- | ------------------------------------------------------ | | Total Sold | Total dollar amount of all gift cards sold | | Outstanding Balance | Sum of all unredeemed gift card balances | | Redemption Rate | Percentage of sold value that has been redeemed | | Expired Unredeemed | Value of expired gift cards that were never fully used | Additional stats include: - **Fully used** -- count of gift cards with zero remaining balance - **Partially used** -- count of cards with some balance remaining - **Unused** -- count of cards that have never been redeemed A **trends chart** shows monthly gift card sales and redemption activity. --- ## Creating a gift card 1. Click **+ New Gift Card**. 2. Enter the **amount** (dollar value of the card). 3. Enter the **recipient's email** address. 4. Optionally enter the **recipient's name**. 5. Optionally include a **personal message**. 6. Optionally set an **expiry date**. 7. Click **Create** -- the recipient receives an email with their gift card code. The gift card is assigned a unique alphanumeric code and tracked in the system immediately. --- ## Customer-purchased gift cards Customers can buy gift cards directly from your booking widget. They select an amount, enter the recipient's details, and pay at checkout. The gift card is delivered automatically via email with the unique code and any personal message. --- ## Redeeming a gift card During checkout, the customer enters their gift card code in the **Gift Card** field. The balance is applied to the order total. If the order is less than the gift card balance, the remaining amount stays on the card for future use. A single gift card can be used across multiple bookings until the balance reaches zero. --- ## Tracking balances The gift card table shows every issued card with: | Column | Description | | ----------------- | ------------------------------------------------ | | Code | The unique gift card code | | Original Amount | Initial value when the card was created | | Remaining Balance | Current available balance | | Recipient | Recipient's name and email | | Payment Status | Whether the card was paid for or issued manually | | Used Count | Number of times the card has been redeemed | | Last Used | Date of the most recent redemption | | Created | Date the card was issued | ### Aging buckets The analytics section includes aging buckets that group outstanding gift card balances by how long they have been unredeemed (e.g., 0-30 days, 31-60 days, 61-90 days, 90+ days). This helps identify cards that may be at risk of expiring unused. --- ## Gift card promotions The promotions panel lets you create special offers to drive gift card sales. Click the **Promotions** section to manage active promotions. ### Promotion types | Type | Description | | ---------------------- | ------------------------------------------------------------------- | | Buy One Get One (BOGO) | Customer buys a gift card and receives a second one free | | Bonus Value | Customer pays full price but receives extra value added to the card | ### Bonus value modes | Mode | Description | | ------------ | ----------------------------------------------------------------- | | Fixed Amount | A fixed dollar bonus is added (e.g., "Buy $100, get $20 bonus") | | Percentage | A percentage bonus is added (e.g., "Buy any card, get 20% extra") | ### Promotion settings | Field | Description | | ------------------------- | ------------------------------------------------------------------- | | Name | Internal name for the promotion | | Description | Explanation shown to staff | | Promotion type | BOGO or Bonus Value | | Bonus type | Fixed amount or percentage (for Bonus Value promotions) | | Bonus amount / percentage | The bonus value or percentage | | Min purchase amount | Minimum gift card value to qualify for the promotion | | Max purchase amount | Maximum gift card value that qualifies | | Max bonus amount | Cap on the bonus value (for percentage-based bonuses) | | Start / End date | Validity window for the promotion | | Max redemptions | Total number of times the promotion can be used | | Badge text | Custom label shown on the promotion badge (e.g., "Holiday Special") | | Banner color | Color of the promotional banner | | Active | Toggle the promotion on or off | ### Promotion statuses | Status | Meaning | | --------- | --------------------------------- | | Active | Promotion is live and available | | Scheduled | Start date is in the future | | Ended | End date has passed | | Sold Out | Max redemptions have been reached | | Paused | Manually deactivated | {% callout type="tip" %} Gift cards are a great option for holidays and special occasions -- promote them on your website and social media. Partial redemption is fully supported, so a $100 gift card can be used across multiple bookings. Run BOGO promotions during the off-season to generate advance revenue. The aging buckets in analytics help you track breakage (unredeemed balances) for accounting purposes. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Gift card code not working** -- Verify the code exists in the system, has a remaining balance, and has not expired. Codes are case-sensitive. **Promotion not applying** -- Check that the promotion is active, the current date is within the validity window, and the gift card amount meets the minimum purchase requirement. **Balance showing as wrong** -- Gift card balances update in real time as redemptions occur. Check the redemption history on the card detail to see all transactions that reduced the balance. {% /callout %} --- # Private links Source: https://docs.rentaltide.com/marketing/private-links/ > Unique booking URLs with pre-configured inventory, dates, pricing, and customer details Private links are unique booking URLs with pre-set inventory, dates, and custom pricing baked in. When a customer opens the link, everything is pre-filled -- they just confirm and pay. Use them for quotes, VIP pricing, group events, or any scenario where you want to control exactly what the customer sees. --- ## Creating a private link The creation flow is a two-step process: ### Step 1: Select inventory A visual card grid shows all available inventory items for the selected location, with photos, capacity information, and category labels. Click the item you want to include in the private link. ### Step 2: Configure details After selecting an item, fill in the booking details: | Field | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------------- | | Start date | The booking start date | | End date | The booking end date | | Start time | The departure time (with time picker) | | Duration | Rental duration in hours. A dropdown shows available hourly slots from the item's pricing configuration with prices | | Quantity | Number of units to book | | Price override ($) | Custom price that overrides the standard rate. Leave blank to use the item's regular pricing | | Staff note | Internal note visible to staff only (not shown to the customer) | ### Customer pre-fill (optional) Pre-fill the customer's information so it appears when they open the link: | Field | Description | | ---------- | ------------------------ | | First name | Customer's first name | | Last name | Customer's last name | | Email | Customer's email address | | Phone | Customer's phone number | After saving, the system generates a unique URL and displays it for copying. --- ## Managing private links The private links table shows all links for the selected location: | Column | Description | | ---------- | ----------------------------------------------------------- | | Inventory | The item name (and asset type if applicable) | | Customer | Pre-filled customer name (or "No customer info" if not set) | | Dates | Start and end dates of the booking | | Price | Custom price if set, or "Standard pricing" | | Staff Note | Internal note from the link creator | | Status | Pending (unused), Redeemed (booked), or Expired | | Created | Date the link was created and by whom | | Last Sent | Date the link was last emailed to a customer | | Actions | Copy, Send, or Delete | --- ## Actions ### Copy link Click the **Copy** button to copy the private link URL to your clipboard. A confirmation appears when the copy is successful. Share the link via any channel -- email, text, chat, or social media. ### Send via email 1. Click the **Send** action. 2. The recipient's email is pre-filled if customer info was provided. 3. Optionally add a **custom message** that appears in the email body. 4. Click **Send**. The system sends a branded email with the booking link. The "Last Sent" column updates with the timestamp. ### Delete Click **Delete** to remove the private link. A confirmation dialog appears. Deleted links can no longer be accessed by customers. --- ## Link lifecycle Private links have a built-in expiration mechanism: - Links expire based on the **booking start date** or **30 days from creation**, whichever is later. - Once a customer completes a booking through the link, the status changes to **Redeemed**. - Expired and redeemed links return an error page when accessed. --- ## Email template Private link emails use the **Booking Link** (`private_link`) template in your email templates configuration. Customize the email content, subject line, and styling from the **Marketing > Email Templates** page. {% callout type="tip" %} Private links are ideal for sending quotes -- the customer clicks the link and books at the exact price you agreed on. Set the start date close to create urgency. Pre-fill customer information to reduce friction at checkout. Use the staff note field to record context about why the link was created (e.g., "Phone inquiry, quoted $200 for 2-hour rental"). {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Link not working** -- Check whether the link has expired or has already been redeemed. Expired and used links return an error page. **Customer seeing the wrong price** -- Verify the custom price set on the private link. The link price overrides your standard rates entirely. If no custom price is set, the item's regular pricing applies. **Email not sending** -- Verify that the recipient email is valid and that your email templates are configured. Check the **Booking Link** template in Marketing > Email Templates. **Link expired too soon** -- Links expire based on the booking start date or 30 days from creation, whichever is later. For quotes that may take time, set the start date further in the future. {% /callout %} --- # Promo codes Source: https://docs.rentaltide.com/marketing/promo-codes/ > Create discount codes with targeting rules, usage limits, and AI-powered suggestions Promo codes let customers apply discounts at checkout. Each code can be configured with a discount type, validity window, usage limits, and advanced targeting rules to control exactly when and where it applies. The page also includes analytics cards and an AI-powered suggestion engine. --- ## Dashboard overview The top of the promo codes page shows four stat cards: | Card | What it shows | | ----------------- | -------------------------------------------- | | Total Codes | Number of active promo codes | | Total Redemptions | Combined usage count across all codes | | Active Codes | Codes currently within their validity window | | Expiring Soon | Codes approaching their expiry date | A bar chart below the stats shows redemption trends over time. --- ## Creating a promo code 1. Click **+ New Promo Code**. 2. Enter the **code** (e.g., SUMMER25). Click the auto-generate button to create a random code. 3. Add a **description** for internal reference. 4. Choose the **discount type**: | Discount type | How it works | | ------------- | --------------------------------------------------------- | | % Discount | Percentage off the booking total (e.g., 25% off) | | $ Discount | Fixed dollar amount off the booking total (e.g., $50 off) | 5. Set the **discount value** (the percentage or dollar amount). 6. Set the **usage limit** -- maximum total redemptions for this code. 7. Set the **expiry date** -- the code stops working after this date. --- ## Advanced targeting Fine-tune which bookings qualify for the discount using optional targeting fields: | Targeting rule | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Location | Restrict the code to a specific location. Leave empty for all locations | | Inventory | Apply only to certain inventory items. Select "All Inventory" or pick specific items | | Day of week | Limit to specific days (e.g., Monday-Thursday for weekday-only discounts) | | Exclude add-ons | When enabled, the discount applies only to the base rental price, not add-on items | | Affiliate | Tie the code to a specific affiliate's tracking URL for attribution | | ID.me verification | Require ID.me identity verification to use the code. Restrict to specific groups: Veteran, Active Duty, Reservist, Military Family, or Gold Star Family | ### Affiliate-linked codes When you select an affiliate from the dropdown, the code is automatically linked to their tracking URL. Bookings made with the code are attributed to the affiliate for commission purposes. Use the **Generate Affiliate Code** button to auto-create a code using the affiliate's name. --- ## Managing promo codes The promo code table shows all codes with their key details: | Column | Description | | ----------- | --------------------------------------------- | | Code | The promo code string | | Description | Internal description | | Discount | Type and value (e.g., "25% off" or "$50 off") | | Usage | Current redemptions vs. usage limit | | Expiry | Expiration date | | Status | Active, expired, or usage limit reached | | Actions | Edit or delete the code | Click **Edit** to modify any field on an existing code. Click **Delete** to permanently remove a code (requires the `promo_delete` permission). --- ## AI suggestions Click the **AI Suggestions** button to activate the AI recommendation engine. It analyzes your booking patterns -- slow days, seasonal dips, underperforming inventory -- and generates promo code suggestions that target those gaps. Each suggestion includes: - A recommended code name - Discount type and value - Targeting rules (days, inventory, dates) - Rationale explaining why the suggestion was made Review the suggestions and click **Activate** to create the code with one click. --- ## Permissions | Permission | Required for | | -------------- | -------------------------------- | | `promo_create` | Creating and editing promo codes | | `promo_delete` | Deleting promo codes | --- ## Code limits Your plan includes a maximum number of active promo codes, controlled by the `maxDiscountCodes` setting. The current limit is shown when you approach it. Deactivate or delete expired codes to free up slots. {% callout type="tip" %} Promo codes are case-insensitive, so SUMMER25 and summer25 both work. Track each code's redemption count and revenue impact from the table. Set codes to auto-expire so you do not have to remember to deactivate them. Combine promo codes with affiliate tracking for full attribution visibility. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Code not working at checkout** -- Check that the current date falls within the valid window, the usage limit has not been reached, and the booking meets the targeting restrictions (location, inventory, day of week). If ID.me is required, the customer must complete verification first. **Hit the promo code limit** -- Your plan has a maximum number of active codes. Deactivate or delete expired codes to free up slots, or contact support to increase the cap. **Discount not applying to add-ons** -- If the "Exclude add-ons" flag is enabled on the code, add-on items are intentionally excluded from the discount. **Affiliate not getting credit** -- Verify the affiliate is linked to the promo code in the targeting settings. The customer can either use the affiliate's tracking URL or enter the linked promo code -- both attribute the booking. {% /callout %} --- # Text templates Source: https://docs.rentaltide.com/marketing/text-templates/ > Automated SMS templates with trigger-based sending, merge tags, and scheduling Text templates work like email templates but deliver messages via SMS. Each template is tied to a trigger event, supports the same merge tags for personalization, and can be scheduled relative to the trigger. Since SMS is text-only, there is no subject line or HTML formatting -- just the message body. --- ## Template list The text templates page shows a table of all configured SMS templates for the selected location: | Column | Description | | ---------- | ---------------------------------------- | | Template | Name and trigger type | | Message | Preview of the SMS body text | | Status | Enabled or disabled | | Scheduling | Trigger event and timing | | Actions | Preview, edit, schedule, copy, or delete | --- ## Creating a template 1. Click **+ New Template**. 2. Select the **trigger** from the dropdown (same trigger list as email templates -- see [Email Templates](/marketing/email-templates) for the full list). 3. Write the **message body** using plain text and merge tags. 4. Click **Save**. ### Editing Open an existing template to modify the message body. The editor is a plain text area with a **placeholder button** that inserts merge tags from a dropdown menu. --- ## Merge tags The same merge tags available in email templates work in text templates: | Tag | Description | | --------------------------------- | -------------------------------------- | | `{{CustomerInfo.FirstName}}` | Customer's first name | | `{{CustomerInfo.LastName}}` | Customer's last name | | `{{BookingInfo.StartDate}}` | Booking start date | | `{{BookingInfo.EndDate}}` | Booking end date | | `{{BookingInfo.CompanyName}}` | Your company name | | `{{InventoryInfo.inventoryName}}` | Name of the booked item | | `{{RentalPricing.Total}}` | Total booking price | | `{{OrderUrl}}` | Link to the customer's Next Steps page | The full list of available variables is accessible through the placeholder button in the editor. See the [Email Templates](/marketing/email-templates) page for the complete variable reference. --- ## Scheduling Configure when the text sends relative to the trigger event, using the same scheduling system as email templates: | Timing | Description | | ---------------- | --------------------------------------------------------------- | | Immediately | Sends as soon as the trigger fires | | X minutes before | Sends before the event (e.g., 120 minutes before booking start) | | X minutes after | Sends after the event | Click the **Schedule** button on any template to open the scheduling configuration dialog. --- ## Preview Click the **Preview** button to see how the rendered SMS will look with sample booking data filled in. The preview replaces all merge tags with realistic test values so you can verify the message reads naturally. --- ## Copying templates ### Copy to location Duplicate a text template to one or more other locations. The copy includes the message body, trigger configuration, and scheduling settings. An overwrite confirmation dialog appears if the destination already has a template for the same trigger. --- ## SMS character limits SMS messages are limited by carrier standards: | Length | Behavior | | ---------------------- | ----------------------------- | | 160 characters or less | Sent as a single SMS segment | | 161-306 characters | Split into 2 segments | | 307+ characters | Split into 3 or more segments | Multi-segment messages may arrive out of order on some carriers. The character count is displayed in the editor to help you stay within the single-segment limit when possible. --- ## Twilio integration Text templates require Twilio to be configured in your location settings. Without Twilio, SMS messages will not be sent. Configure your Twilio credentials in **Admin > Settings > Integrations**. {% callout type="tip" %} Keep messages under 160 characters when possible to avoid splitting into multiple SMS segments, which can arrive out of order. Include the Next Steps link (`{{OrderUrl}}`) so customers can access their booking details, waiver, and directions from the text. Use the same scheduling options as email -- for example, send a reminder 2 hours before the booking start time. Pair SMS reminders with email reminders for maximum reach. {% /callout %} {% callout type="warning" title="Troubleshooting" %} **Texts not sending** -- Verify that Twilio is configured in your location settings and that the customer has a valid phone number on file. The phone number must include a country code. **Messages arriving as multiple texts** -- SMS messages over 160 characters are split into segments. Shorten the message or accept that longer messages may arrive in pieces. **Merge tags showing as raw text** -- Check the tag syntax and ensure the merge tag path matches the available variables exactly. **Template copy failed** -- Ensure you have admin permissions at the destination location. Check whether a template for the same trigger already exists and handle the overwrite prompt. {% /callout %} --- # Website builder Source: https://docs.rentaltide.com/marketing/website-builder/ > Drag-and-drop website builder with booking widget integration, custom domains, and automatic publishing The website builder lets you create a public-facing website for your rental business without writing any code. Assemble pages from pre-built blocks, customize styling, embed your live booking widget, and publish to a RentalTide subdomain or your own custom domain. --- ## Layout The builder uses a three-panel layout: - **Left panel** -- Block library. Drag blocks onto the page from categorized sections. - **Center panel** -- Live preview. See your site exactly as customers will. Click any block to select it. - **Right panel** -- Property editor. Edit the selected block's content, images, colors, links, and layout. --- ## Block types Blocks are organized into three categories: section blocks, custom blocks, and system blocks. ### Section blocks | Block | What it does | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | Hero Banner | Large banner section with headline, subtitle, call-to-action button, background image, and overlay color. Toggle whether your company name appears | | Booking Widget | Embeds your live booking page directly on the site. Optionally filter to a specific location or inventory item | | Locations | Card grid displaying your locations with names, descriptions, images, and links (either RentalTide booking links or external URLs) | | About | Text and image section for your company story | | FAQ | Expandable question-and-answer accordion with configurable heading | ### Custom blocks | Block | What it does | | ---------------- | ----------------------------------------------------------------------------------- | | Text Block | Rich text content block with heading, body, and alignment options | | Image Block | Full-width or constrained image with alt text, caption, and configurable max width | | Image + Text | Side-by-side image and text layout with configurable image position (left or right) | | Spacer / Divider | Adds vertical spacing with configurable height and optional divider line | ### System blocks | Block | What it does | | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Navigation Bar | Top navigation with logo and configurable links. Links can be anchors (same-page), external URLs, or RentalTide booking links filtered to a specific location or item | | Contact | Contact section with email, phone, address, and optional boat license link | | Footer | Bottom section with social media links (Instagram, Facebook, X), terms of service, and country setting | --- ## Editing blocks 1. Click any block on the preview to select it. A blue highlight indicates the selected block. 2. The right panel shows all editable properties for that block. 3. Modify text content, swap images, update colors, change links, adjust padding and alignment. 4. Changes appear immediately in the live preview. ### Block operations | Operation | How to do it | | --------- | ------------------------------------------------------------------------------------------------ | | Add | Drag a block from the left panel onto the preview | | Remove | Select the block and click the delete button | | Reorder | Drag blocks up or down in the preview to change their position | | Duplicate | Select a block and click the duplicate button to create a copy with the same content and styling | --- ## Navigation links The navbar block supports three link types: | Link type | Description | | ---------- | ------------------------------------------------------------------------------- | | Anchor | Same-page navigation to a section (e.g., `#book` scrolls to the booking widget) | | External | Opens any external URL in a new tab | | RentalTide | Auto-generated booking link filtered to a specific location or inventory item | Configure links by adding entries with a label, type, and URL or location/inventory selection. --- ## Global styles Set site-wide defaults from the global styles panel: | Setting | Description | | ---------------- | ----------------------------------------------------- | | Primary color | Main brand color used for buttons, links, and accents | | Secondary color | Secondary accent color | | Font family | Typography used across the entire site | | Background color | Page background color | Individual blocks can override these global settings with their own color and style values. --- ## Publishing Click **Publish** to make your site live. The site is hosted at your RentalTide subdomain: ``` yourslug.rentaltide.com ``` The system automatically generates a sitemap for search engine indexing. Drafts are saved continuously as you work, so you never lose progress. ### Custom domains Custom domains (e.g., `www.yourcompany.com`) require DNS configuration: - Set up a CNAME record pointing to your RentalTide CloudFront distribution - The domain, CloudFront domain, and DNS settings are stored in the website configuration - Contact support for initial setup assistance ### Infrastructure details Each published website includes: - **S3 bucket** for static file hosting - **CloudFront CDN** for global delivery and caching - **Automatic SSL** for HTTPS - **Favicon and logo** support --- ## Booking widget integration The **Booking Widget** block embeds your real booking page directly on the website. Customers can browse inventory, select dates, and complete checkout without leaving your site. Optionally filter the widget to show only a specific location or inventory item. --- ## SEO The website builder supports basic SEO configuration: | Field | Description | | -------------------- | ---------------------------------------------------------------------- | | Website title | Sets the page `