Skip to main content
cohost
Compliance API · v1 · public reference

Real-time sync for jurisdictions.

Cohost exposes a read-only REST API + signed webhook events so your county systems stay in sync without manual exports. Same auth model as Stripe — Bearer tokens for the API; HMAC-SHA256 signatures on every webhook body.

Why this matters
Cohost is the only major STR-compliance vendor with a public API. Granicus, Deckard, Avenu, and GovOS all ship closed systems — your data lives in their UI, not yours. With cohost you sync compliance state into your existing data warehouse, BI tooling, or case-management system on day one. See the head-to-head comparison.

Authentication

Every API request carries a bearer token scoped to a single jurisdiction. Generate keys at /compliance/dashboard/<slug>/integrations — the plaintext is shown once, then hashed and stored. Lost it? Revoke + create a new one.

curl -H "Authorization: Bearer cohost_pk_…" \
     https://cohost.com/api/v1/jurisdictions/nashville-metro/shadow-listings

Endpoints

GET /api/v1/jurisdictions/<slug>/shadow-listings
Every flagged listing in the jurisdiction with its evidence packet. Filter with ?confidence=none|low|medium|high.
GET /api/v1/jurisdictions/<slug>/permits
Permit registry for this jurisdiction with the most recent sync timestamp.
GET /api/v1/jurisdictions/<slug>/listings/<listing_id>
One flagged listing's full evidence packet + current resolution status + audit log.
GET /api/v1/jurisdictions/<slug>/feeds
Health check — permit count, last sync, the upstream data feed cohost is using for this jurisdiction.

Webhooks

Register a destination URL on the integrations page. Cohost POSTs JSON event envelopes to your URL as compliance state changes. Bodies are signed with HMAC-SHA256 using a per-subscription secret cohost shows once at create time.

Event types

  • shadow_listing.flagged A scan detected a new shadow STR.
  • shadow_listing.status_changed Operator dispositioned a case.
  • permit.ingested Permits were added or updated.
  • permit.refreshed Registry refreshed from live feed.
  • rule.changed A jurisdiction's STR rule changed (tax rate, permit requirement, fines).

Envelope

{
  "id": "evt_AbCdEfGhIjKlMnOpQrStUv",
  "type": "shadow_listing.status_changed",
  "jurisdiction": "nashville-metro",
  "created_at": "2026-05-12T18:30:00Z",
  "data": {
    "listing_id": "8472199",
    "from_status": "notice_sent",
    "to_status": "paid",
    "user_id": 7
  },
  "api_version": "2026-05-01"
}

Headers

  • Cohost-Signature · t=<unix_ts>,v1=<hex_sig> — verify within 5 minutes.
  • Cohost-Event-Type · duplicate of envelope type for fast routing.
  • Cohost-Event-Id · use to dedupe — events may be redelivered.

Verifying signatures (Python)

import hmac, hashlib, time

def verify(payload: bytes, header: str, secret: str, tolerance=300):
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    ts = int(parts["t"])
    if abs(int(time.time()) - ts) > tolerance:
        raise ValueError("timestamp outside tolerance")
    expected = hmac.new(
        secret.encode(), f"{ts}.".encode() + payload, hashlib.sha256,
    ).hexdigest()
    if not hmac.compare_digest(expected, parts["v1"]):
        raise ValueError("signature mismatch")

Retry behavior

Non-2xx responses + network failures trigger automatic retries with exponential backoff: 30s · 5m · 30m · 1h · 4h · 12h · 24h. After the final attempt, the delivery is marked failed and stops retrying. Permanent 4xx responses (your endpoint rejected the body) skip retries.

Idempotency

Events MAY be redelivered. Use Cohost-Event-Id (the same as envelope id) as your dedupe key. Handlers should be idempotent — applying the same event twice should leave your state unchanged the second time.

Rate limits + freshness

The API doesn't currently rate-limit per key — cohost is designed for low-volume county integrations, not high-traffic public surfaces. Data freshness: shadow-listings recompute on demand on every call (no cache); the permit list reflects the most recent successful refresh from the upstream feed.

Public data API + MCP

Separate from the municipal integration API above, cohost exposes a public, unauthenticated compliance data endpoint for machine clients — jurisdiction-level rules only, with no per-property data and no PII.

GET /api/v1/compliance

curl "https://cohost.com/api/v1/compliance?jurisdiction=nashville-metro"

Returns whether a permit is required, permit-registry availability + URL, the lodging-tax rate, disallowed property types, the per-violation fine, a regulations summary, and the source / last-verified date. CORS-enabled; 400 when jurisdiction is missing, 404 for an unknown market.

MCP server

The same data is available to Claude / agents as MCP tools. Install the optional extra and register the stdio server:

pip install "cohost[mcp]"
claude mcp add cohost -- cohost mcp-serve

Tools: get_compliance_rules, estimate_lodging_tax, analyze_property, get_market_pulse, get_market_accuracy, list_markets.

Ready to wire it up?
Request municipal access.
The first step is a free 30-day pilot scan against your jurisdiction's actual permit registry. We respond within 2 business days.