Platform Docs

Welcome. The Sail API provides on-demand delivery services. Connect to multiple providers (Bolt, Uber, Yango) through a single, unified API.

You're not signed in. Sign in or sign up to manage keys.

All API requests should be made to https://api.sailrides.co. All endpoints are versioned and prefixed with /v1.

1Create your API key

Sign in to your Sail Platform account to manage your API key, or sign up for free to get started.

For security, load your API key from environment variables (for example via .env) and never store it in browser storage.

2Connect delivery providers

Connect the accounts you'll use when booking deliveries via the API. You can disconnect anytime.

SAIL
bolt
uber
yango

3Try a request

Run a quick quote search using your SAIL_API_KEY environment variable.

Get Delivery Quote
Successful responses include header X-Sail-Status: Smooth Sailing.

Overview

The Sail API provides a comprehensive delivery platform that connects you to multiple providers through a single, unified interface. See our pricing for plan limits and rates.

Features

  • Multi-Provider Support: Connect to Bolt, Uber, and Yango through one API
  • Unified Interface: Consistent request/response format across all providers
  • Real-time Pricing: Get accurate quotes and delivery estimates
  • Provider Management: Easily connect and disconnect provider accounts
  • Status Tracking: Monitor delivery progress with comprehensive status updates

Architecture

The Sail API acts as a unified layer on top of multiple delivery providers, abstracting away the differences between their individual APIs. This allows you to integrate once and get access to multiple providers seamlessly.

Base URL

All API requests should be made to https://api.sailrides.co. All endpoints are versioned and prefixed with /v1.

Authentication

All API requests must be authenticated using your API key in the Authorization header:

Getting Your API Key

To get your API key:

  • Sign in to your Sail Platform account
  • Click “Create API Key” in the Getting Started section
  • Copy your key immediately - it won’t be shown again for security reasons
  • Store it securely in your environment variables

API Key Format

API keys follow the format: sail_prod_{theme}_{random_string}

  • sail_prod: Production key prefix
  • {theme}: Nautical theme (anchor, voyage, compass, etc.)
  • {random_string}: Cryptographically secure random string

Security Best Practices

  • Store your API key in environment variables, not in code
  • Never commit API keys to version control
  • Rotate your API key if you suspect it’s been compromised
  • Use HTTPS for all API requests

Rate Limits

API requests are rate limited per API key to ensure fair usage:

  • Quotes: 100 requests per minute
  • Deliveries: 50 requests per minute
  • Status Checks: 200 requests per minute

Rate limit headers are included in all responses:

Handling Rate Limits

When you receive a 429 Too Many Requests response:

  • Check the X-RateLimit-Reset header for when the limit resets
  • Implement exponential backoff for retries
  • Consider webhook notifications instead of polling for high-frequency updates

Get Quotes

Get delivery quotes from all connected providers for a given pickup and destination.

POST /v1/deliveries/quotes

Request Body

  • pickup.lat, pickup.lng: Pickup coordinates (decimal degrees)
  • destination.lat, destination.lng: Destination coordinates (decimal degrees)
  • pickup.address, destination.address: Human-readable addresses for reference

Response

  • quotes.id: Unique quote identifier for booking
  • quotes.provider: Provider name (bolt, uber, yango)
  • quotes.service_name: Display name of the service
  • quotes.price.amount: Total price in local currency
  • quotes.price.currency: 3-letter currency code
  • quotes.eta_minutes: Estimated time to pickup in minutes
  • quotes.expires_at: Quote expiration time (ISO 8601)
  • quotes.configuration: (optional) Available delivery options for this service — only present on courier/cargo services. Pass selected options when booking.
  • meta.delivery_options_count: Total number of delivery quotes returned
  • quotes.base_service_name: (optional) Base service name without urgency modifier — only present on urgency tier quotes
  • quotes.urgency: (optional) Urgency tier details — only present when the quote is a speed/price variant of a base service
  • quotes.urgency.code: Stable identifier (priority, standard, scheduled) for programmatic use
  • quotes.urgency.label: Display-friendly tier name
  • quotes.urgency.provider_label: Original provider wording (e.g. “Urgent”, “A bit longer”, “In 2 hours”)
  • quotes.urgency.description: (optional) Additional context about the tier

Example Request

Get Delivery Quote

Error Handling

Common Error Responses:

  • invalid_location: Coordinates outside service area
  • no_providers_available: No providers servicing the route
  • rate_limited: Too many quote requests
  • invalid_coordinates: Malformed coordinate data

Deliveries

Manage delivery lifecycle from booking to completion.

Create Delivery

Book a delivery using a previously obtained quote ID.

POST /v1/deliveries
  • quoteId: Valid quote ID (must not be expired)
  • webhookUrl: URL to receive status updates
  • customerInfo: Customer contact information
  • options: (optional) Delivery configuration overrides — use option IDs from the quote’s configuration.options (e.g. vehicle_size, assistance). If omitted, provider defaults apply.

Note: Pickup and destination must be at least 100 meters apart. Requests with locations closer than this will be rejected with a distance_too_short error.

Create Delivery

Get Delivery

Retrieve delivery status and details.

GET /v1/deliveries/{deliveryId}
  • id: Delivery identifier
  • provider: Provider handling the delivery (bolt, yango, uber)
  • status: Current delivery status
  • pickup, destination: Coordinates for each location
  • estimated_fare: Price and currency
  • tracking: (present while delivery is active) Real-time tracking details
  • tracking.driver: Driver name, photo URL, rating, and phone number (when available)
  • tracking.vehicle: Vehicle make, model, and license plate
  • tracking.eta: Estimated time of arrival
  • tracking.pin: Verification PIN (if required by provider)
  • tracking.live_position: Driver’s current lat/lng and bearing
  • tracking.sharing_url: (optional) Provider’s live tracking link — shareable with recipients
Get Delivery

Delivery Status Flow

Deliveries progress through these statuses:

  • booking - Order placed, searching for a driver
  • accepted - Driver assigned to your delivery
  • arriving - Driver is heading to or at the pickup location
  • picked_up - Package collected by driver
  • in_transit - Driver en route to destination
  • delivered - Delivery completed successfully
  • cancelled - Delivery cancelled by user or driver
  • failed - Delivery failed (no drivers available, expired, payment issue)

Cancel Delivery

Cancel an active delivery.

POST /v1/deliveries/{deliveryId}/cancel

Note: Only deliveries in booking, accepted, or arriving status can be cancelled.

No request body required.

Cancel Delivery

Status Polling

Status polling is an alternative to webhooks for monitoring delivery progress. Poll delivery status at regular intervals to track updates.

When to Use Polling

Use polling when:

  • Your application cannot receive webhook requests
  • You need real-time status updates for specific deliveries
  • You want to supplement webhook notifications
  • You’re building a simple application without webhook infrastructure

Use webhooks when:

  • You need to monitor many deliveries simultaneously
  • You want instant notifications
  • You want to reduce API calls
  • You have server infrastructure to receive webhooks

Polling Strategy

Recommended polling frequencies based on delivery status:

  • Active statuses (booking, accepted, arriving): Every 30-60 seconds
  • In-transit (picked_up, in_transit): Every 60-120 seconds
  • Final statuses (delivered, cancelled, failed): Stop polling

Rate Limiting Considerations:

  • Respect the 200 requests/minute limit for delivery tracking endpoints
  • Implement backoff for failed requests
  • Use exponential backoff for rate-limited responses

Implementation Example

Delivery Status Polling

Best Practices

  • Set reasonable timeouts - Don’t wait indefinitely for responses
  • Handle rate limits gracefully - Implement exponential backoff
  • Store delivery state - Avoid re-polling completed deliveries
  • Use conditional requests - Check Last-Modified headers if available
  • Monitor API usage - Track your request count to avoid hitting limits
  • Have a fallback - Consider switching to webhooks if polling becomes inefficient

Webhooks

Webhooks provide real-time notifications when delivery status changes occur. Instead of constantly polling our API, you can receive immediate updates by configuring webhook endpoints.

Configuration

Configure your webhook URL when creating a delivery by including the webhookUrl field in your request. The webhook URL will receive real-time notifications for that delivery.

Webhook URL Requirements:

  • Must be publicly accessible
  • Should respond within 5 seconds
  • Must return a 200 status code
  • Supports HTTPS endpoints only

Webhook Events

The following events are sent via webhooks:

EventDescriptionWhen Sent
delivery.createdNew delivery bookedImmediately after successful booking
delivery.status_updatedDelivery status changedWhen status transitions (e.g. accepted to arriving)
delivery.completedDelivery completedWhen delivery reaches destination
delivery.cancelledDelivery cancelledWhen delivery is cancelled by user or provider
delivery.failedDelivery failedWhen delivery fails (no driver, payment issue, etc.)

Webhook Payload

All webhook payloads contain this structure:

Payload Fields:

  • event: Type of event (see Webhook Events above)
  • data.deliveryId: The delivery ID
  • data.status: New delivery status
  • data.provider: Provider handling the delivery (bolt, yango, uber)
  • data.trackingInfo: Tracking details if available
  • timestamp: When the webhook was sent (ISO 8601)

Security

All webhook requests include security headers:

Headers:

  • X-Sail-Signature: HMAC-SHA256 signature of the payload
  • X-Sail-Timestamp: Unix timestamp of when webhook was sent

Signature Verification: The signature is calculated as: HMAC-SHA256(secret, payload)

Webhook Signature Verification

Security Best Practices:

  • Always verify webhook signatures
  • Reject requests older than 5 minutes
  • Use HTTPS for webhook endpoints
  • Keep your webhook secret secure
  • Implement rate limiting

Delivery and Retries

Reliability Features:

  • Automatic retries for failed deliveries
  • Exponential backoff retry strategy
  • Maximum of 5 retry attempts
  • 5-second timeout per attempt

Retry Schedule:

  • Attempt 1: Immediate
  • Attempt 2: 30 seconds later
  • Attempt 3: 2 minutes later
  • Attempt 4: 10 minutes later
  • Attempt 5: 30 minutes later

If all retries fail, the webhook is marked as failed and you can view failed attempts in your dashboard.

Testing Webhooks

Use these tools to test webhooks during development:

  • ngrok - Expose local endpoints to the internet
  • webhook.site - Temporary webhook URL for testing
  • RequestBin - Inspect webhook requests
  • Local testing - Use curl to simulate webhook calls

Best Practices

  • Respond quickly - Return within 5 seconds
  • Process asynchronously - Don’t block webhook delivery
  • Idempotency - Handle duplicate webhook deliveries
  • Retry strategy - Implement your own retry logic for critical failures
  • Logging - Log all webhook events for debugging
  • Have fallback - Use polling if webhooks fail repeatedly

Error Handling

The API uses conventional HTTP status codes and returns detailed error information in JSON format to help you troubleshoot issues.

Error Response Format

All error responses follow this consistent format:

  • error.code: Machine-readable error code for programmatic handling
  • error.message: Human-readable error description
  • request_id: Unique identifier for the request (useful for support)

HTTP Status Codes

  • 200 OK: Request successful
  • 201 Created: Resource created successfully
  • 400 Bad Request: Invalid JSON or missing required fields
  • 401 Unauthorized: Invalid or missing API key
  • 403 Forbidden: Insufficient permissions
  • 404 Not Found: Invalid delivery ID or endpoint
  • 409 Conflict: Resource state conflict
  • 422 Unprocessable Entity: Valid JSON but invalid data
  • 429 Rate Limited: Too many requests
  • 500 Internal Server Error: Unexpected server error
  • 502 Bad Gateway: Upstream provider error
  • 503 Service Unavailable: Temporary service outage

Common Error Codes

Authentication Errors

  • auth_failed (401): Invalid or missing API key

Quote Errors

  • missing_fields (400): Both pickup and destination are required
  • invalid_coordinates (400): Coordinates must have lat and lng
  • client_not_found (400): Invalid client configuration
  • no_connected_accounts (403): No provider accounts connected
  • quote_failed (500): Failed to retrieve delivery quotes

Booking Errors

  • missing_quote_id (400): quoteId is required
  • quote_expired (404): Quote not found or has expired
  • quote_unauthorized (403): Quote belongs to a different client
  • quote_stale (400): Quote is missing required data
  • provider_not_connected (403): No provider account connected for this client
  • no_active_connections (403): All provider connections are inactive
  • booking_limit_reached (429): Monthly booking limit reached
  • distance_too_short (400): Pickup and destination are too close (minimum 100m apart)
  • booking_failed (502): Provider could not complete the booking

Delivery Errors

  • delivery_not_found (404): Delivery does not exist or you do not have access
  • not_cancellable (409): Delivery status does not allow cancellation
  • provider_config_error (400): Provider configuration is incomplete
  • cancel_not_supported (400): Cancellation not yet supported for this provider
  • cancel_failed (502): Provider could not cancel the delivery
  • tracking_failed (500): Failed to retrieve delivery status

Rate Limiting

  • rate_limited (429): Too many requests per minute

Response Headers

All responses include these important headers:

Success Responses:

Error Responses:

  • Smooth Sailing: Request successful (2xx)
  • Rough Seas: Error response (4xx, 5xx)
  • Becalmed: Rate limited (429)

Error Handling Best Practices

1. Always Check HTTP Status

2. Handle Rate Limits Gracefully

3. Use Request IDs for Debugging

4. Implement Exponential Backoff

5. Log Errors Appropriately

Troubleshooting Guide

Common Issues and Solutions

  • 401 Unauthorized: Verify API key in Authorization header format: ApiKey your_key
  • 429 Rate Limited: Implement rate limiting and exponential backoff
  • Quote expired: Request a new quote before creating delivery
  • Delivery not found: Verify delivery ID and account permissions
  • Provider unavailable: Try again later or use different provider

Embeddable Widget

The Sail widget allows you to embed ride price comparison directly into your website. Visitors can see real-time prices from multiple providers (Bolt, Uber, Yango) to reach your location.

Use cases:

  • Event venues showing attendees how much it costs to get there
  • Hotels displaying ride prices from airports
  • Restaurants helping customers plan their visit
  • Conference centers with pre-configured destination

Getting Started

  1. Go to the Widgets page in your Platform dashboard
  2. Click Create Widget and configure:
    • Name: A friendly name (e.g., “Homepage Widget”)
    • Allowed Domains: Enter up to 3 domains (e.g., yoursite.com). Press Enter or Comma to add each domain.
    • Destination (optional): Pre-configure your venue’s location
  3. Use the Mobile Preview (eye icon on mobile) to test how it looks on smaller screens
  4. Copy your Public Key (sail_pub_...)
  5. Add the embed code to your website

Configuration

ParameterDescriptionRequired
dest_lat / dest-latDestination latitudeYes*
dest_lng / dest-lngDestination longitudeYes*
dest_place_id / dest-place-idGoogle Place ID (alternative to lat/lng)Yes*
dest_name / dest-nameDestination name for displayNo
dest_address / dest-addressDestination address for displayNo
themelight, dark, or autoNo (default: auto)
widget_key / widget-keyYour widget public keyYes

* Either lat/lng or place_id is required. You can also set these in your widget configuration.

Iframe Integration

The simplest way to embed the widget. Copy and paste this HTML, customizing the destination and widget key.

Iframe Embed

Important: Include allow="geolocation" for the widget to access the user’s location.

Web Component

A cleaner approach using a custom HTML element. Include the widget script and use the <sail-ride-widget> element.

Web Component

JavaScript API

For programmatic control, use the JavaScript API to create and configure widgets dynamically.

JavaScript API

API Options:

Using Place ID

Instead of coordinates, you can use a Google Place ID for your destination:

Place ID Example

Find your venue’s Place ID using the Google Place ID Finder.

Important Notes

Geolocation Permission The widget requires user location permission to calculate ride prices. Users will be prompted when they click “Compare Ride Prices”.

Domain Whitelisting Your widget key is tied to specific domains (max 3 allowed per widget). You can manage allowed domains from the Widgets dashboard.

HTTPS Required The widget must be embedded on HTTPS pages for geolocation to work properly.

Rate Limiting Widget requests are rate-limited per key. For high-traffic sites, contact us for increased limits.

Mobile Support The widget is fully responsive and works on mobile devices. The iframe adapts to the container width.