ReconOS

API docs

Bolt-on ReconOS to whatever recon tool you already run. Bearer-token API, signed webhooks, embeddable widgets.

Base URL: https://3-130-85-220.sslip.io

Authentication

All requests use a Bearer API key in the Authorization header. Mint a key in Settings → API keys. The plaintext is shown once.

Any /api/v1 request
curl -H "Authorization: Bearer rcn_..." \
  https://3-130-85-220.sslip.io/api/v1/vehicles
Scopes: read allows GET. write allows everything. A read key on a write endpoint returns 403 forbidden.

Vehicles

GET/api/v1/vehiclesread scope

Paginated list, dealership-scoped.

FieldTypeNotes
stagestring?Filter: detail | mechanical | photos | frontline
limitint?Default 50, max 200
offsetint?Default 0
curl -H "Authorization: Bearer rcn_..." \
  "https://3-130-85-220.sslip.io/api/v1/vehicles?stage=detail&limit=20"
{
  "vehicles": [
    {
      "id": "uuid",
      "vin": "1HGBH41JXMN109186",
      "year": 2021,
      "make": "Honda",
      "model": "Accord LX",
      "miles": 42100,
      "color_hex": "#dc2626",
      "stage": "detail",
      "stage_entered_at": "2026-05-12T14:30:00Z",
      "est_retail": 22500,
      "purchased_at_amount": 18200,
      "recon_spend": 1247.50,
      "parts_status": "none",
      "assigned_tech": "Mike R.",
      "created_at": "2026-05-12T14:30:00Z"
    }
  ],
  "pagination": { "total": 18, "limit": 20, "offset": 0, "has_more": false }
}
recon_spend is the running total of parts + labor + sublet for this car (USD). Operator-maintained from the dashboard. Combine with est_retail and purchased_at_amount to compute gross margin per car: retail minus acquired minus recon.
GET/api/v1/vehicles/:id/forecastread scope

Predicted completion date for one in-flight vehicle. Returns optimistic / expected / pessimistic timestamps from per-stage quantiles (p25/median/p75) over the dealership's own last-90-day historical pace, with a cold-start fallback for new accounts. Returns 400 if the vehicle is already on frontline.

curl -H "Authorization: Bearer rcn_..." \
  https://3-130-85-220.sslip.io/api/v1/vehicles/<vehicle_id>/forecast
{
  "vehicle": {
    "id": "uuid",
    "vin": "1HGBH41JXMN109186",
    "year": 2021,
    "make": "Honda",
    "model": "Accord LX",
    "stage": "mechanical"
  },
  "forecast": {
    "vehicle_id": "uuid",
    "current_stage": "mechanical",
    "days_in_current_stage": 2.5,
    "expected_remaining_days": 3.5,
    "optimistic_remaining_days": 2.5,
    "pessimistic_remaining_days": 4.25,
    "expected_ready_at": "2026-05-17T00:14:55Z",
    "optimistic_ready_at": "2026-05-16T00:14:55Z",
    "pessimistic_ready_at": "2026-05-17T18:14:55Z",
    "cold_start": false
  }
}
cold_start: true means we fell back to bundled industry defaults because the dealership doesn't have enough completed cars yet. Treat the timestamps as wider-band estimates in that case.
POST/api/v1/vehicleswrite scope

Create a vehicle. Returns 201 with the inserted row.

VINs aren't deduplicated server-side. Call GET first if your sync job needs idempotency.
curl -X POST \
  -H "Authorization: Bearer rcn_..." \
  -H "Content-Type: application/json" \
  -d '{
    "vin": "1HGBH41JXMN109186",
    "year": 2021,
    "make": "Honda",
    "model": "Accord LX",
    "miles": 42100,
    "stage": "detail",
    "est_retail": 22500,
    "purchased_at_amount": 18200,
    "stock_number": "T5832",
    "intake_source": "trade_in"
  }' \
  https://3-130-85-220.sslip.io/api/v1/vehicles
Required: vin (17 chars), year, make, model. Year must be 1900 to 2100. Everything else is optional with sensible defaults.
Intake-source routing. Omit stage and ReconOS picks the landing stage from your dealership's intake rules. intake_source can be any of: trade_in, auction, dealer_trade, repo, wholesale, customer, other. If omitted but stock_number is present, we apply your configured stock-prefix regex rules to detect source from the prefix (e.g. ^T → trade-in). Configure both in Settings → Intake rules.

Reference benchmarks

GET/api/v1/benchmarksread scope

Your last-30-day median time-to-line against a fixed reference sample of shops in the same volume bucket. The sample is seeded industry data — it is never built from other ReconOS dealerships, so nothing here exposes another customer. Same numbers our dashboard and weekly digest show.

curl -H "Authorization: Bearer rcn_..." \
  https://3-130-85-220.sslip.io/api/v1/benchmarks
{
  "bucket": "medium",
  "bucket_label": "15–45 cars/month",
  "you":   { "completed": 22, "median_ttl": 7 },
  "peers": {
    "rooftop_count": 14,
    "completed": 312,
    "median_ttl": 8,
    "p25_ttl": 6,
    "p75_ttl": 11
  },
  "percentile": 73,
  "comparison_source": "reference_sample"
}
percentile is 0 to 100. 100 = fastest in your bucket.null if you have no completions yet in the window.

Webhooks

ReconOS pushes events to your endpoint instead of you polling. Add a subscription in Settings → Webhooks. Each subscription gets a signing secret (shown once).

Event types

FieldTypeNotes
vehicle.createdfiresA new vehicle was added to recon
vehicle.stage_changedfiresAny stage transition (detail → mechanical, etc.)
vehicle.completedfiresStage moved to frontline. Always paired with stage_changed
vehicle.flagged_stuckfiresVehicle exceeded your stage-median threshold. Fires once on the transition INTO stuck, not while still stuck
photo.uploadedfiresA tech uploaded a photo
sms.sentfiresTech or parts-vendor SMS dispatched

Delivery shape

POST to your URL with JSON body. Headers carry the event type and an HMAC-SHA-256 signature you verify against your secret.

Headers + body
POST https://your-server/webhook
Content-Type: application/json
X-ReconOS-Event: vehicle.stage_changed
X-ReconOS-Delivery: <delivery uuid>
X-ReconOS-Signature: sha256=<hex>
User-Agent: ReconOS-Webhook/1

{
  "event_type": "vehicle.stage_changed",
  "delivery_id": "<delivery uuid>",
  "timestamp": "2026-05-13T22:00:00.000Z",
  "payload": {
    "vehicle_id": "<uuid>",
    "vin": "1HGBH41JXMN109186",
    "from_stage": "photos",
    "to_stage": "frontline",
    "to": "frontline",
    "changed_at": "2026-05-13T22:00:00.000Z",
    "actor_user_id": "<uuid>"
  }
}

Verifying the signature

Compute hmac_sha256(secret, raw_body) and compare (constant-time) to the hex after sha256=. If they match, the payload came from us and wasn't tampered with.

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

function verify(rawBody, signatureHeader, secret) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const got = (signatureHeader ?? "").replace(/^sha256=/, "");
  if (got.length !== expected.length) return false;
  return timingSafeEqual(Buffer.from(got), Buffer.from(expected));
}

Retries

Failed deliveries (non-2xx, timeout, network error) retry up to 5 times with the next cron tick (1-minute cadence). After 5 failed attempts the delivery is marked failed and visible in Settings → Webhooks for debugging. Subscribers should be idempotent. The same delivery_id may arrive more than once.

DMS write-back presets

Push ReconOS stage changes back to your DMS so inventory records stay in sync without anyone re-keying. Layered on the webhook outbox: same HMAC signing, same retry policy, same audit log. The payload is rewritten into the vendor's expected shape and the right vendor-specific header is set.

Configure in Settings → DMS write-back. Pick CDK Drive, Reynolds ERA, or Dealertrack; paste the partner endpoint URL; customize the stage → status-code mapping. Owner-only.

Supported presets

FieldTypeNotes
cdkpresetCDK Drive, Fortellis status-push endpoint shape
reynoldspresetReynolds ERA, RCI partner endpoint shape
dealertrackpresetDealertrack partner integration shape

Event coverage

Today we push vehicle.stage_changed only. That's the operationally critical signal for keeping the DMS's inventory status accurate. Other webhook event types pass through the preset transformer; if it returns no mapping they're marked failed-non-retryable with a descriptive error.

CDK Drive payload shape

POST
POST https://your-cdk-partner-endpoint/status
Content-Type: application/json
X-ReconOS-Event: vehicle.stage_changed
X-ReconOS-Delivery: <delivery uuid>
X-ReconOS-Signature: sha256=<hex>
X-CDK-Status-Push: v1
User-Agent: ReconOS-Webhook/1

{
  "vin": "1HGBH41JXMN109186",
  "reconStatus": "RECON_PHOTO",
  "previousStatus": "RECON_MECH",
  "timestamp": "2026-05-13T22:00:00Z",
  "source": "ReconOS"
}

Reynolds ERA payload shape

POST
POST https://your-reynolds-partner-endpoint/status
Content-Type: application/json
X-ReconOS-Event: vehicle.stage_changed
X-Reynolds-Event: vehicle.status

{
  "vin": "1HGBH41JXMN109186",
  "status": "R_PHOTOS",
  "ts": "2026-05-13T22:00:00Z"
}

Dealertrack payload shape

POST
POST https://your-dealertrack-partner-endpoint/status
Content-Type: application/json
X-ReconOS-Event: vehicle.stage_changed
X-Dealertrack-Source: reconos

{
  "vin": "1HGBH41JXMN109186",
  "inventoryStatus": "RECON_PHOTOS",
  "changedAt": "2026-05-13T22:00:00Z"
}

Stage → status-code mapping

Each preset ships with default mappings between ReconOS stages and DMS status codes. Most CDK installs use slightly different codes per dealer; override per-stage in the settings UI. The value sent in reconStatus / status /inventoryStatus is whatever you configure (the defaults below are starting points, not contracts).

FieldTypeNotes
detaildefault → CDKRECON_DETAIL
mechanicaldefault → CDKRECON_MECH
photosdefault → CDKRECON_PHOTO
frontlinedefault → CDKFRONTLINE_READY
Verifying the signature works exactly like a generic webhook. Compute hmac_sha256(secret, raw_body) and compare against X-ReconOS-Signature. The vendor-specific headers (X-CDK-Status-Push etc.) are advisory metadata; only the ReconOS headers participate in auth.

Inbound integrations

ReconOS can receive stage-change events from whatever recon tool your dealership runs today (Rapid Recon, CDK, Reynolds). Use that tool as your daily kanban; let ReconOS layer analytics + audit + the bolt-on API surface on top. We do not write back. This is one-way ingest.

1. Set up the signing secret

In ReconOS, go to Settings → Rapid Recon integration and click Generate signing secret. You'll see the plaintext exactly once. Copy it into Rapid Recon's outbound-webhook config along with the URL below.

2. Endpoint shape

POST
POST https://3-130-85-220.sslip.io/api/integrations/rapid-recon/webhook/<your-dealership-id>
X-Signature: sha256=<hex-hmac-of-raw-body>
Content-Type: application/json

{
  "event_id": "evt_abc123",
  "event_type": "vehicle.synced",
  "occurred_at": "2026-05-14T15:23:00Z",
  "vehicle": {
    "external_id": "T5832",
    "vin": "1HGBH41JXMN109186",
    "year": 2021,
    "make": "Honda",
    "model": "Accord LX",
    "miles": 42100,
    "current_step": "Photography",
    "purchased_at_amount": 18200,
    "internet_price": 22500,
    "assigned_tech": "Mike Rodriguez"
  }
}
Required: event_id, event_type, occurred_at, vehicle.external_id, vehicle.vin (17 chars). Everything else is optional. Step names auto-translate via aliases. "Body Shop" → mechanical, "Lot Ready" → frontline, etc.

3. Idempotency

Upserted by (dealership_id, external_source, external_id). Calling twice with the same external_id updates the same row, so your retry logic doesn't duplicate cars. The first event creates the vehicle and logs a stage_change recon_event; subsequent events update fields and log a stage_change only when the stage actually transitions.

Response codes

FieldTypeNotes
200okReceived. Body: { received: true, vehicle_id, was_created, stage_changed }
400shapeBody malformed or required fields missing
401authX-Signature header missing or doesn't match
404configNo active integration credential for this dealership. Generate one in Settings.
500serverDatabase error. Safe to retry.
Returning non-2xx tells Rapid Recon's webhook engine to retry. We use 5xx for transient server-side errors and 4xx for permanent client errors (don't retry; body will fail again until you fix it).

Embed widgets

Not yet available. The widget routes below are not built — /embed/* currently resolves to the sign-in redirect, so a <script> tag pointing at one loads HTML where JavaScript is expected and fails silently on your page. This section documents the intended shape so integrators can plan against it; it is not a working integration today. Read the same numbers from /api/v1/benchmarks in the meantime.

Drop a <script> tag into any HTML page (your existing recon tool's custom panel, intranet, Slack canvas, Confluence page) and a ReconOS panel renders inline. Auth is a read-scope API key in the URL (same trust model as a Stripe Publishable Key).

Reference benchmark

HTML
<script src="https://3-130-85-220.sslip.io/embed/benchmark.js"
        data-key="rcn_..."
        data-width="420"
        data-height="320"></script>

Bottleneck this week

HTML
<script src="https://3-130-85-220.sslip.io/embed/bottleneck.js"
        data-key="rcn_..."
        data-width="420"
        data-height="320"></script>
The iframe is sandboxed (allow-scripts allow-same-origin only, no top-navigation). The widget is read-only and rotatable. Revoke the key in Settings to instantly disable every embed using it.

Rate limits

Each API key gets 120 requests per minute. The limit is per-key (not per-IP), so a noisy script on one machine doesn't affect the same dealership's other integrations.

When you hit the limit, you get a 429 with a Retry-After header (seconds until the window resets). Well-behaved clients back off until that time.

Headers on 429
Retry-After: 27
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
Need higher limits? Email hello@reconos.com with the use case. We tier per-key for partners with real sync workloads.

Error shape

All errors share a JSON shape. The HTTP status code tells you what kind:

FieldTypeNotes
400shapeBody missing required fields, malformed JSON, etc.
401authNo Authorization header, malformed key, or revoked key
403scopeKey is read-scope but the route needs write
404shapeResource doesn't exist OR doesn't belong to your dealership
429rateRate limited. See Rate limits section
500serverOur bug. Surface this to support with the X-Request-ID if present.
Body
{ "error": "human-readable message",
  "details": { "optional": "context" } }

Questions or missing endpoint? hello@reconos.com. Design-partner dealers get endpoint requests prioritized.