{
 "openapi": "3.1.0",
 "info": {
  "title": "AIS Trail API",
  "version": "1.1.1",
  "description": "Normalized historical AIS vessel positions from open sources: vessel search, tracks by MMSI or IMO, and historical area queries, as JSON, GeoJSON or CSV.\n\nOnly successful (2xx) requests count toward your quota; 4xx and 5xx are free. The most recent 24 hours are not available on the history endpoints (embargo). Real-time data is in early access for part of our coverage and is not included in current plans; write to hello@aistrail.com.\n\nPlans:\n- **Free** (`free`, \u20ac0/month): 1,000 requests/month, 1 request/s, last 7 days of history, area queries not included, up to 1,000 rows per page\n- **Pro** (`pro`, \u20ac79/month or \u20ac790/year): 300,000 requests/month, 10 requests/s, last 12 months of history, area queries up to 2\u00b0 x 2\u00b0, up to 50,000 rows per page\n- **Business** (`business`, \u20ac199/month or \u20ac1,990/year): 1,000,000 requests/month, 20 requests/s, last 12 months of history, area queries up to 10\u00b0 x 10\u00b0, up to 50,000 rows per page\n\nOne query may span at most: raw 24 h, 1m 7 days, 1h 366 days. Longer ranges: split them. Results are paged with an opaque cursor; a page may hold fewer rows than limit, keep following next_cursor until complete is true.\n\nAt most 2 requests per key are processed at a time (429 otherwise). A query that runs longer than the server limit is stopped with 503; narrow the box or the time range. Errors are RFC 9457 problem details.\n\nSources: `digitraffic`, `dma`. Attribution is required when you redistribute data. Source names, licences and the attribution texts are at /v1/sources and on [the sources page](https://aistrail.com/docs/sources/).\n\nTrack and area responses carry the attribution of the sources on that page (meta.attribution; X-Attribution header for CSV). The vessel endpoint names the source of each observation; search and IMO lookups do not carry source fields. The attribution for all of them is at /v1/sources."
 },
 "servers": [
  {
   "url": "https://api.aistrail.com"
  }
 ],
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "API key ap_live_\u2026"
   }
  },
  "schemas": {
   "Position": {
    "type": "object",
    "properties": {
     "mmsi": {
      "type": "integer"
     },
     "ts": {
      "type": "string",
      "format": "date-time",
      "description": "Message timestamp (UTC) as reported by the source"
     },
     "lat": {
      "type": "number"
     },
     "lon": {
      "type": "number"
     },
     "sog": {
      "type": [
       "number",
       "null"
      ],
      "description": "Speed over ground, knots"
     },
     "cog": {
      "type": [
       "number",
       "null"
      ],
      "description": "Course over ground, degrees"
     },
     "heading": {
      "type": [
       "integer",
       "null"
      ],
      "description": "True heading, degrees; null if unavailable (raw 511)"
     },
     "nav_status": {
      "type": [
       "integer",
       "null"
      ],
      "description": "ITU-R M.1371 navigational status 0-14"
     },
     "source": {
      "type": "string",
      "description": "Data source id, see /v1/sources"
     },
     "flags": {
      "type": [
       "string",
       "null"
      ],
      "description": "Quality flag, e.g. implausible_jump (kept, never dropped)"
     }
    }
   },
   "PageMeta": {
    "type": "object",
    "properties": {
     "from": {
      "type": "string",
      "format": "date-time"
     },
     "to": {
      "type": "string",
      "format": "date-time"
     },
     "resolution": {
      "type": "string",
      "enum": [
       "raw",
       "1m",
       "1h"
      ]
     },
     "count": {
      "type": "integer"
     },
     "complete": {
      "type": "boolean",
      "description": "true when there are no more pages"
     },
     "next_cursor": {
      "type": [
       "string",
       "null"
      ],
      "description": "Pass as ?cursor= to get the next page"
     },
     "next": {
      "type": [
       "string",
       "null"
      ],
      "description": "Relative URL of the next page"
     },
     "embargo_cutoff": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time",
      "description": "Data newer than this is not available on your plan"
     },
     "attribution": {
      "type": "array",
      "items": {
       "type": "object"
      },
      "description": "Required attribution for the sources in this page"
     }
    }
   },
   "Problem": {
    "type": "object",
    "properties": {
     "type": {
      "type": "string"
     },
     "title": {
      "type": "string"
     },
     "status": {
      "type": "integer"
     },
     "detail": {
      "type": "string"
     },
     "retry_after_s": {
      "type": "integer"
     },
     "upgrade_url": {
      "type": "string",
      "format": "uri"
     }
    }
   }
  },
  "responses": {
   "Problem": {
    "description": "RFC 9457 problem details",
    "content": {
     "application/problem+json": {
      "schema": {
       "type": "object",
       "properties": {
        "type": {
         "type": "string"
        },
        "title": {
         "type": "string"
        },
        "status": {
         "type": "integer"
        },
        "detail": {
         "type": "string"
        },
        "retry_after_s": {
         "type": "integer"
        },
        "upgrade_url": {
         "type": "string",
         "format": "uri"
        }
       }
      }
     }
    }
   }
  }
 },
 "paths": {
  "/v1/status": {
   "get": {
    "summary": "Service status and data coverage per source (public)",
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/v1/sources": {
   "get": {
    "summary": "Data sources, licences, required attribution and coverage (public)",
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  },
  "/v1/usage": {
   "get": {
    "summary": "Plan, limits and usage in the current billing period",
    "security": [
     {
      "bearer": []
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     },
     "401": {
      "$ref": "#/components/responses/Problem"
     },
     "429": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/vessels/search": {
   "get": {
    "summary": "Search vessels by name, call sign, MMSI prefix or IMO",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": true,
      "description": "At least 2 characters",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "1-50",
      "schema": {
       "type": "integer",
       "default": 20
      }
     },
     {
      "name": "offset",
      "in": "query",
      "required": false,
      "description": "0-1000; use next_offset from the previous page",
      "schema": {
       "type": "integer",
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Most recently seen first"
     },
     "400": {
      "$ref": "#/components/responses/Problem"
     },
     "401": {
      "$ref": "#/components/responses/Problem"
     },
     "429": {
      "$ref": "#/components/responses/Problem"
     },
     "503": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/vessels/{mmsi}": {
   "get": {
    "summary": "Vessel identity by MMSI: current static data and observation log",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "mmsi",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     },
     "400": {
      "$ref": "#/components/responses/Problem"
     },
     "401": {
      "$ref": "#/components/responses/Problem"
     },
     "404": {
      "$ref": "#/components/responses/Problem"
     },
     "429": {
      "$ref": "#/components/responses/Problem"
     },
     "503": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/vessels/{mmsi}/track": {
   "get": {
    "summary": "Track of one MMSI over a time range",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "mmsi",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "from",
      "in": "query",
      "required": false,
      "description": "ISO 8601 start (inclusive); default: to minus 24 h",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "to",
      "in": "query",
      "required": false,
      "description": "ISO 8601 end (exclusive); default: now, or now minus 24 h on embargoed plans. Later values are clamped (see meta.embargo_cutoff)",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "resolution",
      "in": "query",
      "required": false,
      "description": "raw = every fix; 1m / 1h = latest fix per vessel per minute / hour (no interpolation)",
      "schema": {
       "type": "string",
       "enum": [
        "raw",
        "1m",
        "1h"
       ],
       "default": "raw"
      }
     },
     {
      "name": "format",
      "in": "query",
      "required": false,
      "description": "json, geojson (application/geo+json) or csv (text/csv; paging in Link / X-Next-Cursor headers)",
      "schema": {
       "type": "string",
       "enum": [
        "json",
        "geojson",
        "csv"
       ],
       "default": "json"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Rows per page, up to the plan's track_points; the default is 5000, or track_points when that is lower",
      "schema": {
       "type": "integer",
       "default": 5000
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Opaque cursor from meta.next_cursor. When given, from, to, resolution and the subject are taken from the cursor",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Positions ordered by time",
      "headers": {
       "X-RateLimit-Limit": {
        "description": "Monthly request quota",
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Remaining": {
        "description": "Requests left in the billing period",
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Reset": {
        "description": "Unix time when the quota resets",
        "schema": {
         "type": "integer"
        }
       },
       "X-Count": {
        "description": "Rows in this page",
        "schema": {
         "type": "integer"
        }
       },
       "X-Complete": {
        "description": "true when there are no more pages",
        "schema": {
         "type": "string"
        }
       },
       "X-Next-Cursor": {
        "description": "Cursor of the next page (absent on the last page)",
        "schema": {
         "type": "string"
        }
       },
       "Link": {
        "description": "rel=\"next\" URL of the next page",
        "schema": {
         "type": "string"
        }
       },
       "X-Attribution": {
        "description": "Required attribution for the sources in this page",
        "schema": {
         "type": "string"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "meta": {
           "type": "object",
           "properties": {
            "from": {
             "type": "string",
             "format": "date-time"
            },
            "to": {
             "type": "string",
             "format": "date-time"
            },
            "resolution": {
             "type": "string",
             "enum": [
              "raw",
              "1m",
              "1h"
             ]
            },
            "count": {
             "type": "integer"
            },
            "complete": {
             "type": "boolean",
             "description": "true when there are no more pages"
            },
            "next_cursor": {
             "type": [
              "string",
              "null"
             ],
             "description": "Pass as ?cursor= to get the next page"
            },
            "next": {
             "type": [
              "string",
              "null"
             ],
             "description": "Relative URL of the next page"
            },
            "embargo_cutoff": {
             "type": [
              "string",
              "null"
             ],
             "format": "date-time",
             "description": "Data newer than this is not available on your plan"
            },
            "attribution": {
             "type": "array",
             "items": {
              "type": "object"
             },
             "description": "Required attribution for the sources in this page"
            }
           }
          },
          "positions": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "mmsi": {
              "type": "integer"
             },
             "ts": {
              "type": "string",
              "format": "date-time",
              "description": "Message timestamp (UTC) as reported by the source"
             },
             "lat": {
              "type": "number"
             },
             "lon": {
              "type": "number"
             },
             "sog": {
              "type": [
               "number",
               "null"
              ],
              "description": "Speed over ground, knots"
             },
             "cog": {
              "type": [
               "number",
               "null"
              ],
              "description": "Course over ground, degrees"
             },
             "heading": {
              "type": [
               "integer",
               "null"
              ],
              "description": "True heading, degrees; null if unavailable (raw 511)"
             },
             "nav_status": {
              "type": [
               "integer",
               "null"
              ],
              "description": "ITU-R M.1371 navigational status 0-14"
             },
             "source": {
              "type": "string",
              "description": "Data source id, see /v1/sources"
             },
             "flags": {
              "type": [
               "string",
               "null"
              ],
              "description": "Quality flag, e.g. implausible_jump (kept, never dropped)"
             }
            }
           }
          }
         }
        }
       },
       "application/geo+json": {
        "schema": {
         "type": "object"
        }
       },
       "text/csv": {
        "schema": {
         "type": "string"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/Problem"
     },
     "401": {
      "$ref": "#/components/responses/Problem"
     },
     "403": {
      "$ref": "#/components/responses/Problem"
     },
     "429": {
      "$ref": "#/components/responses/Problem"
     },
     "503": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/vessels/imo/{imo}": {
   "get": {
    "summary": "MMSIs that have reported this IMO number, newest first (at most 50; truncated=true beyond that; the /track endpoint is not limited)",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "imo",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     },
     "400": {
      "$ref": "#/components/responses/Problem"
     },
     "401": {
      "$ref": "#/components/responses/Problem"
     },
     "404": {
      "$ref": "#/components/responses/Problem"
     },
     "429": {
      "$ref": "#/components/responses/Problem"
     },
     "503": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/vessels/imo/{imo}/track": {
   "get": {
    "summary": "Track of one IMO across MMSI changes. Each MMSI counts only while it reported this IMO (\u00b110 min)",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "imo",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "from",
      "in": "query",
      "required": false,
      "description": "ISO 8601 start (inclusive); default: to minus 24 h",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "to",
      "in": "query",
      "required": false,
      "description": "ISO 8601 end (exclusive); default: now, or now minus 24 h on embargoed plans. Later values are clamped (see meta.embargo_cutoff)",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "resolution",
      "in": "query",
      "required": false,
      "description": "raw = every fix; 1m / 1h = latest fix per vessel per minute / hour (no interpolation)",
      "schema": {
       "type": "string",
       "enum": [
        "raw",
        "1m",
        "1h"
       ],
       "default": "raw"
      }
     },
     {
      "name": "format",
      "in": "query",
      "required": false,
      "description": "json, geojson (application/geo+json) or csv (text/csv; paging in Link / X-Next-Cursor headers)",
      "schema": {
       "type": "string",
       "enum": [
        "json",
        "geojson",
        "csv"
       ],
       "default": "json"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Rows per page, up to the plan's track_points; the default is 5000, or track_points when that is lower",
      "schema": {
       "type": "integer",
       "default": 5000
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Opaque cursor from meta.next_cursor. When given, from, to, resolution and the subject are taken from the cursor",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Positions ordered by time; each row carries its mmsi",
      "headers": {
       "X-RateLimit-Limit": {
        "description": "Monthly request quota",
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Remaining": {
        "description": "Requests left in the billing period",
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Reset": {
        "description": "Unix time when the quota resets",
        "schema": {
         "type": "integer"
        }
       },
       "X-Count": {
        "description": "Rows in this page",
        "schema": {
         "type": "integer"
        }
       },
       "X-Complete": {
        "description": "true when there are no more pages",
        "schema": {
         "type": "string"
        }
       },
       "X-Next-Cursor": {
        "description": "Cursor of the next page (absent on the last page)",
        "schema": {
         "type": "string"
        }
       },
       "Link": {
        "description": "rel=\"next\" URL of the next page",
        "schema": {
         "type": "string"
        }
       },
       "X-Attribution": {
        "description": "Required attribution for the sources in this page",
        "schema": {
         "type": "string"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "meta": {
           "type": "object",
           "properties": {
            "from": {
             "type": "string",
             "format": "date-time"
            },
            "to": {
             "type": "string",
             "format": "date-time"
            },
            "resolution": {
             "type": "string",
             "enum": [
              "raw",
              "1m",
              "1h"
             ]
            },
            "count": {
             "type": "integer"
            },
            "complete": {
             "type": "boolean",
             "description": "true when there are no more pages"
            },
            "next_cursor": {
             "type": [
              "string",
              "null"
             ],
             "description": "Pass as ?cursor= to get the next page"
            },
            "next": {
             "type": [
              "string",
              "null"
             ],
             "description": "Relative URL of the next page"
            },
            "embargo_cutoff": {
             "type": [
              "string",
              "null"
             ],
             "format": "date-time",
             "description": "Data newer than this is not available on your plan"
            },
            "attribution": {
             "type": "array",
             "items": {
              "type": "object"
             },
             "description": "Required attribution for the sources in this page"
            }
           }
          },
          "positions": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "mmsi": {
              "type": "integer"
             },
             "ts": {
              "type": "string",
              "format": "date-time",
              "description": "Message timestamp (UTC) as reported by the source"
             },
             "lat": {
              "type": "number"
             },
             "lon": {
              "type": "number"
             },
             "sog": {
              "type": [
               "number",
               "null"
              ],
              "description": "Speed over ground, knots"
             },
             "cog": {
              "type": [
               "number",
               "null"
              ],
              "description": "Course over ground, degrees"
             },
             "heading": {
              "type": [
               "integer",
               "null"
              ],
              "description": "True heading, degrees; null if unavailable (raw 511)"
             },
             "nav_status": {
              "type": [
               "integer",
               "null"
              ],
              "description": "ITU-R M.1371 navigational status 0-14"
             },
             "source": {
              "type": "string",
              "description": "Data source id, see /v1/sources"
             },
             "flags": {
              "type": [
               "string",
               "null"
              ],
              "description": "Quality flag, e.g. implausible_jump (kept, never dropped)"
             }
            }
           }
          }
         }
        }
       },
       "application/geo+json": {
        "schema": {
         "type": "object"
        }
       },
       "text/csv": {
        "schema": {
         "type": "string"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/Problem"
     },
     "401": {
      "$ref": "#/components/responses/Problem"
     },
     "403": {
      "$ref": "#/components/responses/Problem"
     },
     "429": {
      "$ref": "#/components/responses/Problem"
     },
     "503": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/area": {
   "get": {
    "summary": "Historical positions inside a bounding box (Pro and Business plans)",
    "security": [
     {
      "bearer": []
     }
    ],
    "description": "Rows are returned day by day and, at raw resolution, 1\u00b0 tile by tile; inside a tile ordered by vessel and time. Follow next_cursor until complete is true.",
    "parameters": [
     {
      "name": "bbox",
      "in": "query",
      "required": false,
      "description": "lat_min,lon_min,lat_max,lon_max; each side at most the plan's max_bbox_deg. Required unless cursor is given",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "from",
      "in": "query",
      "required": false,
      "description": "ISO 8601 start (inclusive); default: to minus 1 h",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "to",
      "in": "query",
      "required": false,
      "description": "ISO 8601 end (exclusive); default: now, or now minus 24 h on embargoed plans. Later values are clamped (see meta.embargo_cutoff)",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "resolution",
      "in": "query",
      "required": false,
      "description": "raw = every fix; 1m / 1h = latest fix per vessel per minute / hour (no interpolation)",
      "schema": {
       "type": "string",
       "enum": [
        "raw",
        "1m",
        "1h"
       ],
       "default": "raw"
      }
     },
     {
      "name": "format",
      "in": "query",
      "required": false,
      "description": "json, geojson (application/geo+json) or csv (text/csv; paging in Link / X-Next-Cursor headers)",
      "schema": {
       "type": "string",
       "enum": [
        "json",
        "geojson",
        "csv"
       ],
       "default": "json"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Rows per page, up to the plan's track_points; the default is 5000, or track_points when that is lower",
      "schema": {
       "type": "integer",
       "default": 5000
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Opaque cursor from meta.next_cursor. When given, from, to, resolution and the subject are taken from the cursor",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Positions in the box",
      "headers": {
       "X-RateLimit-Limit": {
        "description": "Monthly request quota",
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Remaining": {
        "description": "Requests left in the billing period",
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Reset": {
        "description": "Unix time when the quota resets",
        "schema": {
         "type": "integer"
        }
       },
       "X-Count": {
        "description": "Rows in this page",
        "schema": {
         "type": "integer"
        }
       },
       "X-Complete": {
        "description": "true when there are no more pages",
        "schema": {
         "type": "string"
        }
       },
       "X-Next-Cursor": {
        "description": "Cursor of the next page (absent on the last page)",
        "schema": {
         "type": "string"
        }
       },
       "Link": {
        "description": "rel=\"next\" URL of the next page",
        "schema": {
         "type": "string"
        }
       },
       "X-Attribution": {
        "description": "Required attribution for the sources in this page",
        "schema": {
         "type": "string"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "meta": {
           "type": "object",
           "properties": {
            "from": {
             "type": "string",
             "format": "date-time"
            },
            "to": {
             "type": "string",
             "format": "date-time"
            },
            "resolution": {
             "type": "string",
             "enum": [
              "raw",
              "1m",
              "1h"
             ]
            },
            "count": {
             "type": "integer"
            },
            "complete": {
             "type": "boolean",
             "description": "true when there are no more pages"
            },
            "next_cursor": {
             "type": [
              "string",
              "null"
             ],
             "description": "Pass as ?cursor= to get the next page"
            },
            "next": {
             "type": [
              "string",
              "null"
             ],
             "description": "Relative URL of the next page"
            },
            "embargo_cutoff": {
             "type": [
              "string",
              "null"
             ],
             "format": "date-time",
             "description": "Data newer than this is not available on your plan"
            },
            "attribution": {
             "type": "array",
             "items": {
              "type": "object"
             },
             "description": "Required attribution for the sources in this page"
            }
           }
          },
          "positions": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "mmsi": {
              "type": "integer"
             },
             "ts": {
              "type": "string",
              "format": "date-time",
              "description": "Message timestamp (UTC) as reported by the source"
             },
             "lat": {
              "type": "number"
             },
             "lon": {
              "type": "number"
             },
             "sog": {
              "type": [
               "number",
               "null"
              ],
              "description": "Speed over ground, knots"
             },
             "cog": {
              "type": [
               "number",
               "null"
              ],
              "description": "Course over ground, degrees"
             },
             "heading": {
              "type": [
               "integer",
               "null"
              ],
              "description": "True heading, degrees; null if unavailable (raw 511)"
             },
             "nav_status": {
              "type": [
               "integer",
               "null"
              ],
              "description": "ITU-R M.1371 navigational status 0-14"
             },
             "source": {
              "type": "string",
              "description": "Data source id, see /v1/sources"
             },
             "flags": {
              "type": [
               "string",
               "null"
              ],
              "description": "Quality flag, e.g. implausible_jump (kept, never dropped)"
             }
            }
           }
          }
         }
        }
       },
       "application/geo+json": {
        "schema": {
         "type": "object"
        }
       },
       "text/csv": {
        "schema": {
         "type": "string"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/Problem"
     },
     "401": {
      "$ref": "#/components/responses/Problem"
     },
     "403": {
      "$ref": "#/components/responses/Problem"
     },
     "429": {
      "$ref": "#/components/responses/Problem"
     },
     "503": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/vessels/{mmsi}/position": {
   "get": {
    "summary": "Latest live position (early access: not included in current plans; contact hello@aistrail.com)",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "mmsi",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "ok"
     },
     "403": {
      "$ref": "#/components/responses/Problem"
     },
     "404": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/positions": {
   "get": {
    "summary": "Latest live positions in a box (early access: not included in current plans; contact hello@aistrail.com)",
    "security": [
     {
      "bearer": []
     }
    ],
    "parameters": [
     {
      "name": "bbox",
      "in": "query",
      "required": true,
      "description": "lat_min,lon_min,lat_max,lon_max",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "max_age",
      "in": "query",
      "required": false,
      "description": "seconds, default 86400",
      "schema": {
       "type": "number"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "up to 2000 vessels"
     },
     "403": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/stream": {
   "get": {
    "summary": "Live WebSocket stream (early access: not included in current plans; contact hello@aistrail.com). ?key= is accepted here only. First message: {\"bbox\":[[...]]} and/or {\"mmsi\":[...]}",
    "security": [
     {
      "bearer": []
     }
    ],
    "responses": {
     "101": {
      "description": "switching protocols; closed with code 4003 if the plan has no live data"
     },
     "403": {
      "$ref": "#/components/responses/Problem"
     }
    }
   }
  },
  "/v1/openapi.json": {
   "get": {
    "summary": "This document",
    "responses": {
     "200": {
      "description": "ok"
     }
    }
   }
  }
 }
}