LOSSLINEDocumentation
Browse documentationOverview

Get started

OverviewQuickstartAuthentication

Core concepts

How processing worksAreas and watchesPlatform customersIncidents and billingBackfill

Webhooks

Delivery lifecycleVerify signaturesIncident payloadRetries

API reference

OrganizationAreasWatchesCustomersIncidentsUsageWebhook endpointAPI keys

Resources

Pagination and limitsErrorsOpenAPI 3.1
API v1https://api.lossline.app

Lossline API

Build fire intelligence into your product.

Watch supported fire-department areas, route incidents to your own customer IDs, and receive one finalized record after automatic owner and property enrichment.

Make your first requestGet an API key
Server-side API

Keep Lossline API keys on your backend. Do not embed them in browser code, mobile apps, or public repositories.

Get started

Quickstart

A production integration has four parts: create a key, choose areas, assign those areas to your customer IDs, and register one webhook endpoint.

  1. 01Create an organization and API keyUse the Developer Console. Secret values are displayed once.
  2. 02Find supported areasSearch the complete department catalog and store stable area_ IDs.
  3. 03Configure routingUse organization watches or replace each platform customer’s full area list.
  4. 04Receive finalized incidentsVerify the signature, acknowledge with any 2xx response, then route by matching_customer_ids.

1. Find an area

All API requests use the production base URL and a Bearer token.

curl "https://api.lossline.app/v1/areas?query=miami&limit=10" \
  -H "Authorization: Bearer $LOSSLINE_API_KEY"
{
  "data": [
    {
      "id": "area_MDFR",
      "name": "Miami-Dade Fire Rescue",
      "short_name": "Miami-Dade",
      "location": "Miami-Dade County, Florida",
      "latitude": 25.7617,
      "longitude": -80.1918,
      "timezone": "America/New_York",
      "boundary": [/* GeoJSON coordinates */]
    }
  ],
  "page": 1,
  "limit": 10
}

2. Add a platform customer

This request is idempotent. It creates the customer or replaces the customer’s complete area list.

curl -X PUT "https://api.lossline.app/v1/customers/customer_8421" \
  -H "Authorization: Bearer $LOSSLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "area_ids": ["area_MDFR", "area_FL_BSO"],
    "metadata": { "plan": "pro" }
  }'
{
  "data": {
    "id": "customer_8421",
    "status": "active",
    "area_ids": ["area_FL_BSO", "area_MDFR"],
    "backfill_queued": 4
  }
}

3. Register your webhook

The response includes a new whsec_ signing secret exactly once. Store it in your secret manager.

curl -X PUT "https://api.lossline.app/v1/webhook-endpoint" \
  -H "Authorization: Bearer $LOSSLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://api.example.com/webhooks/lossline" }'

Foundations

Authentication

Authenticate every request with an API key in the Authorization header.

Authorization: Bearer ll_live_••••••••••••••••

Live keys

Keys begin with ll_live_. A key belongs to one organization and inherits that organization’s access.

One-time display

The full key is returned only when it is created. Lossline stores a one-way hash and cannot recover it later.

Rotation

Create the replacement, deploy it, verify its last-used timestamp, then revoke the previous key.

Key limits

Each organization may keep up to 10 active keys. The key authenticating a request cannot revoke itself.

Core concepts

How processing works

Lossline turns many source updates into one stable, organization-scoped incident record.

01Incident matchedA watched department produces a qualifying incident.02$1 reservedThe organization must have enough balance to begin processing.03Automatic enrichmentOwner and property records are searched without a second API call.04Record finalizedThe immutable payload and final charge are frozen.05Webhook deliveredOne signed delivery contains every matching customer ID.

Areas and watches

An area represents one supported department feed. Its stable ID begins with area_.

Organization watches

Use POST /v1/watches when every incident in an area should belong to the organization, even without platform customer routing.

Customer watches

Use customer area lists when one platform account serves many end customers and needs recipient IDs returned in each incident.

Shared collection

An upstream area is collected once even when many Lossline organizations and customers watch it.

Boundaries

Area responses can include coordinates and geographic boundary data for discovery and map interfaces.

Platform customers

Use your own opaque identifier. Lossline stores the routing relationship, not your customer’s name, email, or phone number.

customer_8421Up to 128 characters: letters, numbers, period, underscore, colon, or hyphen.
  • Replace, do not append. Each PUT replaces the complete area list so your system can safely replay its current state.
  • Metadata is optional. Store up to 4 KB of non-sensitive routing metadata.
  • Delivery is consolidated. One incident includes all matching customer IDs in one array.
  • Deactivation is safe. DELETE stops future matching without deleting already finalized incident history.

Incidents and billing

Each unique incident is processed and charged once per organization, even when multiple watched areas or customer subscriptions overlap.

$1.00Verified enrichment match
$0.75No verified match or provider unavailable

A $1.00 reservation is placed when processing starts. If the final result is not a verified match, $0.25 is returned automatically. Repeated reads, webhook retries, later source updates, and additional matching customers do not create another charge.

Backfill

Adding an organization watch or customer area list automatically scans the previous 24 hours.

  • Existing incident records already purchased by the organization are attached to newly matching customer IDs without another charge.
  • New historical records are processed newest first while the organization has enough balance.
  • Customer-scoped history is available from GET /v1/customers/{customer_id}/incidents.
  • Reading historical records never triggers another webhook or charge.

Webhooks

Delivery lifecycle

Lossline sends one POST request for each finalized incident to the organization’s active HTTPS endpoint.

Lossline-Id

Stable delivery ID beginning with dlv_. Use it for deduplication.

Lossline-Timestamp

Unix timestamp in seconds for replay protection.

Lossline-Signature

Versioned Base64 signature in the form v1,signature.

User-Agent

Lossline-Webhooks/1.0

Verify webhook signatures

Use the exact raw request body. Parsing and re-serializing JSON changes the signed bytes.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyLosslineWebhook(req, rawBody, secret) {
  const deliveryId = req.headers["lossline-id"];
  const timestamp = req.headers["lossline-timestamp"];
  const received = req.headers["lossline-signature"]?.replace(/^v1,/, "");

  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!deliveryId || !timestamp || !received || age > 300) return false;

  const key = secret.replace(/^whsec_/, "");
  const expected = createHmac("sha256", key)
    .update(deliveryId + "." + timestamp + "." + rawBody)
    .digest("base64");

  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}
Recommended replay window

Reject timestamps more than five minutes from your server time and ignore delivery IDs you already processed.

Incident payload

The payload is frozen when billing settles. REST reads and every webhook retry return the same record.

{
  "id": "evt_7d9c8b0e-3f6e-4af3-96c1-230ccfb56ac7",
  "type": "incident.finalized",
  "schema_version": "1.0",
  "created_at": "2026-08-17T18:42:11.930Z",
  "matching_customer_ids": ["customer_8421", "customer_9107"],
  "incident": {
    "id": "inc_e12661f9-a182-447f-b8f8-d73c54498a15",
    "event_type": "Structure Fire",
    "severity": "confirmed",
    "status": "active",
    "title": "Residential structure fire",
    "department": {
      "id": "area_MDFR",
      "name": "Miami-Dade Fire Rescue"
    },
    "location": {
      "address": "1400 Collins Avenue",
      "city": "Miami Beach",
      "state": "FL",
      "postal_code": "33139",
      "latitude": 25.7865,
      "longitude": -80.1301
    },
    "dispatch": {
      "reported_at": "2026-08-17T18:39:02.000Z",
      "last_updated_at": "2026-08-17T18:41:44.000Z",
      "closed_at": null,
      "units": ["E1", "E2", "L1"],
      "summary": "3 units responding"
    }
  },
  "enrichment": {
    "status": "matched",
    "owner_name": "Collins 1400 Holdings LLC",
    "owner_type": "business",
    "phones": ["+13055550136"],
    "emails": ["office@example.com"],
    "mailing_address": "1400 Collins Avenue, Miami Beach, FL 33139",
    "property": {
      "estimated_value": 6200000,
      "year_built": 1956,
      "last_sold_year": 2021
    }
  },
  "billing": {
    "amount_cents": 100,
    "amount": 1,
    "currency": "usd"
  }
}

Retries and acknowledgements

Return any 2xx status within 15 seconds. Redirects are not followed.

1Immediate21 min35 min430 min52 hours68 hours78 hours88 hours

After eight unsuccessful attempts, the delivery is marked failed. Delivery state and attempt history remain visible in the Developer Console.

API reference

Every v1 endpoint.

All request and response bodies use JSON unless the endpoint returns 204 No Content.

Organization

GET/v1/me200

Retrieve the current organization

Returns the organization attached to the API key, including status, balance, and currency.

Areas

GET/v1/areas200

List supported areas

Search enabled, discoverable department areas in alphabetical order.

  • query: optional search string up to 80 characters
  • page: integer from 1 to 10,000
  • limit: 1 to 100, default 50

Organization watches

GET/v1/watches200

List watched areas

Returns every organization-wide area watch with its creation time and area details.

POST/v1/watches201

Watch areas

Adds one to 500 valid area IDs. Existing watches are ignored safely and a 24-hour backfill is queued.

  • Body: { area_ids: string[] }
  • Response includes watched and backfill_queued
DELETE/v1/watches/{area_id}204

Stop watching an area

Removes one organization-wide watch. Customer-specific watches are unaffected.

Platform customers

GET/v1/customers200

List customers

Returns opaque IDs, current status, metadata, and complete area assignments.

  • page: integer from 1 to 10,000
  • limit: 1 to 100, default 50
GET/v1/customers/{customer_id}200

Retrieve a customer

Returns one customer belonging to the authenticated organization.

PUT/v1/customers/{customer_id}200

Create or replace a customer

Idempotently upserts the customer and replaces its complete area list.

  • area_ids: required array, maximum 500
  • metadata: optional object, maximum 4 KB
  • Queues a 24-hour backfill
DELETE/v1/customers/{customer_id}204

Deactivate a customer

Stops future matching and removes current area assignments while preserving prior history.

GET/v1/customers/{customer_id}/incidents200

List customer incidents

Returns finalized records matched to one customer. This read creates no delivery or charge.

  • page: integer from 1 to 10,000
  • limit: 1 to 100, default 25

Incidents

GET/v1/incidents200

List finalized incidents

Returns organization-scoped records ordered by finalization time, newest first.

  • page: integer from 1 to 10,000
  • limit: 1 to 100, default 25
GET/v1/incidents/{incident_id}200

Retrieve an incident

Returns one completed incident already purchased by the authenticated organization.

Usage

GET/v1/usage200

Retrieve balance and ledger

Returns the current balance, currency, and immutable reservation, refund, credit, and adjustment entries.

  • limit: 1 to 100, default 50

Webhook endpoint

GET/v1/webhook-endpoint200

Retrieve webhook configuration

Returns endpoint metadata without the signing secret.

PUT/v1/webhook-endpoint200

Create or replace the endpoint

Requires a public HTTPS URL. Replacing the endpoint rotates the signing secret.

  • Body: { url: string }
  • The new whsec_ value is displayed once
DELETE/v1/webhook-endpoint204

Disable delivery

Disables the endpoint without deleting delivery history.

API keys

GET/v1/keys200

List key metadata

Returns names, prefixes, last-used timestamps, expiry, revocation, and creation times. Secret values are never returned.

POST/v1/keys201

Create an API key

Creates a live key and returns the complete secret exactly once.

  • Body: { name?: string }
  • Maximum 10 active keys per organization
DELETE/v1/keys/{key_id}204

Revoke an API key

Revokes a key belonging to the organization. A key cannot revoke itself.

Resources

Pagination and request limits

List page size1 to 100Maximum page10,000Areas per request500Customer metadata4 KBActive API keys10Webhook timeout15 seconds

List endpoints return page and limit. Request the next page until data.length is smaller than the requested limit.

Errors

Errors use an HTTP status and a stable machine-readable string.

{
  "error": "one_or_more_areas_not_found"
}
400Validation failed

Inspect the error string and correct the request.

401Invalid or expired API key

Create a new key or replace the Authorization header.

403Organization is not active

Contact the organization owner or Lossline support.

404Resource or area not found

Confirm the ID belongs to your organization.

409Request conflicts with current state

Key limits and current-key revocation use this status.

503A dependent service is unavailable

Retry with exponential backoff.