Skip to main content

Private hotlist API reference

REST reference for creating, listing, and managing private hotlists and their plates in a single organization using an organization API key.

Overview

Reference for the REST endpoints used to manage an organization's private hotlists and their plates. It targets external integrations, gate-guard tooling, scheduled syncs, back-office scripts, that authenticate with a Corvid organization API key.

Prerequisite: An organization API key. See How to create API keys for your organization.

Authentication

Send the key on every request

Include the key in the Authorization header using the ApiKey scheme (not Bearer):

Authorization: ApiKey ak_XXXXXXXXXXXXXXXXXXXX
Content-Type: application/json

Requests are attributed in the audit log to the identity API Key. Use descriptive names on each key so the trail stays readable.

Base URL

Production base URL:

https://api.lpr.corvidcloud.com

Common error codes

Status

Meaning

401

Missing / invalid / expired key, or the key is not tied to an organization.

403

Key resolved, but no matching org context.

404

Hotlist or plate not found for this organization.

400

Validation error — see the response body for field-level messages.

409

Duplicate (for example, a hotlist name already exists in the organization).

Manage hotlists

Create a hotlist

POST /v2/hotlists

Request body:

{
  "name": "Gate A watchlist",
  "description": "Vehicles flagged by front-gate guards",
  "isActive": true,
  "isShared": false
}

Field

Type

Required

Notes

name

string

yes

1–255 chars

description

string

no

≤1000 chars

isActive

boolean

yes

false mutes matching

isShared

boolean

no

Defaults to false

Response 201:

{
  "id": "hl_01H...",
  "name": "Gate A watchlist",
  "description": "Vehicles flagged by front-gate guards",
  "isActive": true,
  "isShared": false,
  "createdAt": "2026-08-24T14:22:11Z",
  "createdBy": "api_key"
}

List hotlists

GET /v2/hotlists

Query parameters (all optional):

Param

Type

Default

Notes

limit

int

100

1–100

nextToken

string

—

Opaque pagination cursor from the previous page

shared

boolean

—

Filter to shared / non-shared hotlists

search

string

—

Substring match on name

sortBy

string

—

name, createdAt, updatedAt

sortOrder

string

—

asc or desc

Response 200:

{
  "items": [
    {
      "id": "hl_01H...",
      "name": "Gate A watchlist",
      "description": "...",
      "isActive": true,
      "isShared": false,
      "createdAt": "2026-08-24T14:22:11Z",
      "createdBy": "api_key"
    }
  ],
  "nextToken": "eyJwayI6...",
  "total": 12
}

Get a hotlist by ID

GET /v2/hotlists/{hotlistId}

Returns the same shape as Create a hotlist. Returns 404 if the id does not exist in the caller's organization.

Get a hotlist by name

GET /v2/hotlists/by-name/{hotlistName}

Case-sensitive exact match within the caller's organization. Returns 404 if not found.

Update a hotlist

PUT /v2/hotlists/{hotlistId}

All fields optional — send only what you want to change:

{
  "name": "Gate A watchlist v2",
  "description": "Updated by nightly sync",
  "isActive": true,
  "isShared": false
}

Delete a hotlist

DELETE /v2/hotlists/{hotlistId}

Deletes the hotlist and all its plates. Response 200:

{ "message": "Hotlist deleted successfully" }

Download a hotlist as CSV

GET /v2/hotlists/{hotlistId}/download

Returns a presigned S3 URL, valid for a short window. The CSV is cached in S3 and regenerated on the first request after changes.

{ "url": "https://s3.amazonaws.com/..." }

Manage plates inside a hotlist

List plates

GET /v2/hotlists/{hotlistId}/plates

Param

Type

Default

Notes

limit

int

100

1–100

nextToken

string

—

Pagination cursor

search

string

—

Substring match on plateNumber

Response 200:

{
  "items": [
    {
      "plateNumber": "ABC1234",
      "state": "TX",
      "note": "Banned contractor truck",
      "createdAt": "2026-08-20T09:01:00Z",
      "createdBy": "api_key",
      "expiredAt": 1735689600
    }
  ],
  "nextToken": null,
  "total": 47
}

Upsert a single plate

PUT /v2/hotlists/{hotlistId}/plates/{plateNumber}

Request body — all fields optional; newPlateNumber renames the plate:

{
  "newPlateNumber": "ABC1234",
  "state": "TX",
  "note": "Renewed 2026-Q4",
  "expiredAt": 1767225600
}

Field

Type

Notes

newPlateNumber

string

1–20 chars; rename the plate

state

string

2-letter code; sending the key at all clears or updates it

note

string

≤1000 chars

expiredAt

int

Unix seconds; after this, the plate stops matching

Bulk add or upsert plates

POST /v2/hotlists/{hotlistId}/plates/bulk

Use this for the "quickly add" use case — the caller can push a batch instead of one call per plate.

Request body:

{
  "plates": [
    { "plateNumber": "ABC1234", "state": "TX", "note": "Watch — Gate A" },
    { "plateNumber": "XYZ7890", "state": "CA", "note": "Watch — Gate B", "expiredAt": "2026-12-31" }
  ]
}

expiredAt on each item accepts a YYYY-MM-DD string (converted to a future Unix timestamp; past dates are rejected) or an integer Unix timestamp.

Response 200:

{
  "successful": 2,
  "failed": 0,
  "errors": [],
  "createdPlates": [
    { "plateNumber": "ABC1234", "state": "TX", "note": "Watch — Gate A", "createdAt": "...", "createdBy": "api_key" }
  ],
  "createdCount": 1,
  "updatedCount": 1
}

createdCount = newly inserted; updatedCount = plates that already existed and were overwritten. failed and errors[] capture per-row validation issues without aborting the batch.

Bulk delete plates

DELETE /v2/hotlists/{hotlistId}/plates/bulk

Max 100 items per call.

{
  "plates": [
    { "plateNumber": "ABC1234", "state": "TX" },
    { "plateNumber": "XYZ7890", "state": "CA" }
  ]
}

Response 200:

{
  "successful": 2,
  "failed": 0,
  "errors": [],
  "deletedPlates": [
    { "plateNumber": "ABC1234", "state": "TX" }
  ]
}

Search for a plate

GET /v2/hotlists/global/plates/{plateNumber}

Searches for a plate across the caller org's private hotlists and every public hotlist the org is subscribed to. This is the endpoint gate guards should call to answer "is this plate flagged?".

Param

Type

Default

Notes

state

string

—

2-letter code; strongly recommended

orgId

string

—

Only needed for cross-org superuser lookups

limit

int

—

1–100; omit for full merged list

nextToken

string

—

Opaque cursor for paginated mode

Response 200:

{
  "items": [
    {
      "hotlistId": "hl_01H...",
      "hotlistName": "Gate A watchlist",
      "plateNumber": "ABC1234",
      "state": "TX",
      "reason": "Banned contractor truck",
      "hotlistType": "private",
      "expiredAt": 1767225600
    }
  ],
  "lastKey": null
}

End-to-end flow

A typical onboarding sequence for a gate-guard integration:

  1. POST /v2/hotlists — create a new private hotlist and capture the returned id.

  2. POST /v2/hotlists/{hotlistId}/plates/bulk — load the initial batch of plates.

  3. GET /v2/hotlists/global/plates/{plateNumber}?state=XX — for each observed plate, check whether it is flagged.

  4. Keep the hotlist current with PUT / DELETE plate endpoints (single or bulk), and periodically fetch GET /v2/hotlists/{hotlistId}/plates to reconcile against the source of truth.

Operational notes

  • Rate limits — API Gateway usage plans apply per stage. Ask the Corvid team for the current quota if you plan to sync more than a few thousand plates per minute.

  • Audit trail — mutations show up under the identity API Key. Issue one key per external integrator if you need to distinguish callers.

  • Key scope — an organization API key currently also reaches public-hotlist and subscription endpoints. If your integration only needs private management, restrict its use to the routes in this document.

  • Key rotation — rotate keys through Manage Organization → API Keys. Revoked keys stop authenticating immediately; there is no server-side cache.

Related articles

  • How to create API keys for your organization

  • What environments does we support?

  • What is the Permissions System?

Did this answer your question?