{
  "asyncapi": "3.0.0",
  "info": {
    "title": "NCS Live pipeline watch",
    "version": "1.0.0",
    "description": "Webhook events when vessels behave dangerously near subsea infrastructure on the Norwegian shelf: trawling near a line, anchored over one, or loitering along one. Detections from live AIS (BarentsWatch) against SODIR pipeline geometry every five minutes. Deliveries are CloudEvents v1.0 batches over HTTPS POST — natively ingestable by Azure Event Grid and Azure.Messaging.CloudNative .NET SDKs. Honest limits: positions are AIS self-reports; IMO numbers are not in the open feed and are deliberately absent rather than guessed; cable and customer routes are schematic and say so per event.",
    "contact": {
      "name": "NCS Live",
      "url": "https://ncslive.no/watch",
      "email": "andreas@smpl.no"
    }
  },
  "defaultContentType": "application/cloudevents-batch+json",
  "servers": {
    "production": {
      "host": "ncslive.no",
      "protocol": "https",
      "description": "Publisher. Subscribers register an HTTPS endpoint; onboarding is manual (two-person shop, by design). Azure Event Grid abuse-protection handshakes are completed manually with the subscriber during onboarding rather than automated."
    }
  },
  "channels": {
    "maritimeEvents": {
      "address": "maritime/v1/events",
      "description": "Logical channel: your registered webhook endpoint.",
      "messages": {
        "watchEvent": {
          "$ref": "#/components/messages/watchEvent"
        }
      }
    }
  },
  "operations": {
    "publishWatchEvents": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/maritimeEvents"
      },
      "summary": "POST a CloudEvents batch to the subscriber endpoint",
      "description": "At most one event per vessel/asset/kind per six hours. Anchored and loitering events are confirmed across two five-minute passes before first publication.",
      "messages": [
        {
          "$ref": "#/channels/maritimeEvents/messages/watchEvent"
        }
      ]
    }
  },
  "components": {
    "messages": {
      "watchEvent": {
        "name": "watchEvent",
        "title": "Watch event (CloudEvents v1.0)",
        "contentType": "application/cloudevents-batch+json",
        "payload": {
          "$ref": "#/components/schemas/cloudEvent"
        }
      }
    },
    "schemas": {
      "cloudEvent": {
        "type": "object",
        "description": "CloudEvents v1.0 structured envelope (application/cloudevents-batch+json: deliveries are ARRAYS of these)",
        "required": [
          "specversion",
          "id",
          "source",
          "type",
          "subject",
          "time",
          "data"
        ],
        "properties": {
          "specversion": {
            "const": "1.0"
          },
          "id": {
            "type": "string",
            "description": "UUID, unique per event — the dedup key"
          },
          "source": {
            "const": "https://ncslive.no/watch"
          },
          "type": {
            "type": "string",
            "enum": [
              "no.ncslive.watch.trawling.v1",
              "no.ncslive.watch.anchored.v1",
              "no.ncslive.watch.loitering.v1"
            ]
          },
          "subject": {
            "type": "string",
            "description": "The asset the event concerns"
          },
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Detection time at the watch. The AIS truth-at-sea time is data.vessel.observedAt; the gap between them is satellite backhaul plus polling."
          },
          "datacontenttype": {
            "const": "application/json"
          },
          "data": {
            "$ref": "#/components/schemas/watchEventData"
          }
        }
      },
      "watchEventData": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "https://ncslive.no/watch/schema.json",
        "title": "NCS Live watch event",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "severity",
          "reason",
          "distanceMeters",
          "vessel",
          "asset",
          "mapUrl"
        ],
        "properties": {
          "kind": {
            "enum": [
              "trawling",
              "anchored",
              "loitering"
            ]
          },
          "severity": {
            "enum": [
              "critical",
              "warning",
              "info"
            ],
            "description": "critical < 250 m, warning < 600 m, info within the corridor"
          },
          "reason": {
            "type": "string"
          },
          "distanceMeters": {
            "type": "integer",
            "minimum": 0
          },
          "vessel": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "name",
              "mmsi",
              "speedOverGroundKnots",
              "courseOverGroundDegrees",
              "position"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "mmsi": {
                "type": "integer"
              },
              "lengthMeters": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "speedOverGroundKnots": {
                "type": "number"
              },
              "courseOverGroundDegrees": {
                "type": "number"
              },
              "navigationalStatus": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "ITU-R M.1371; 1 = at anchor, 7 = engaged in fishing"
              },
              "shipType": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "position": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "latitude",
                  "longitude"
                ],
                "properties": {
                  "latitude": {
                    "type": "number",
                    "minimum": -90,
                    "maximum": 90
                  },
                  "longitude": {
                    "type": "number",
                    "minimum": -180,
                    "maximum": 180
                  }
                }
              },
              "observedAt": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "The vessel's own AIS report time — the truth at sea"
              }
            }
          },
          "asset": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "name",
              "schematicRoute"
            ],
            "properties": {
              "type": {
                "enum": [
                  "pipeline",
                  "cable",
                  "custom"
                ]
              },
              "name": {
                "type": "string"
              },
              "medium": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "diameterInches": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "maxDepthMeters": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "schematicRoute": {
                "type": "boolean"
              },
              "nearestPoint": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "latitude",
                  "longitude"
                ],
                "properties": {
                  "latitude": {
                    "type": "number",
                    "minimum": -90,
                    "maximum": 90
                  },
                  "longitude": {
                    "type": "number",
                    "minimum": -180,
                    "maximum": 180
                  }
                },
                "description": "The point on the asset the distance is measured to"
              }
            }
          },
          "mapUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    }
  }
}