API reference

Everything the site does is available as JSON over HTTPS at https://cv2x.itsroads.com/api/…. The same free quota applies: 5 codec operations per day anonymously (by IP), 500 per day with a free account (session cookie).

Basics

  • Requests and responses are JSON (Content-Type: application/json). Every response has ok: true or ok: false with an error string.
  • Schema: SAE J2735_202409 (1,218 ASN.1 types) plus IEEE 1609.2 Ieee1609Dot2Data. JSON conventions: SEQUENCE = object keyed by ASN.1 member names; CHOICE = object with exactly one key; SEQUENCE OF = array; ENUMERATED = name string; BIT STRING = "0101…" MSB first at the exact declared length; OCTET STRING = uppercase hex; NULL = null.
  • Health check: GET /api/health → {"ok":true,"types":1218,"schema":"J2735_202409","schemas":["2024","2020","2016"]}. Quota: GET /api/quota.
  • Revisions: decodes captures from SAE J2735 2016, 2020 and 2024 devices (auto-detected, newest first), encodes on the current 2024 revision by default, and is built to add the 2026 revision alongside when SAE publishes it. Pass schema to pin a revision.

Decode

POST/api/decode

Accepts hex (spaces, colons, 0x allowed), base64, XER (XML) or codec JSON. Bytes are unwrapped automatically: Ethernet II (0x88DC) → IEEE 1609.3 WSMP → IEEE 1609.2 SPDU → MessageFrame. Counts against the quota.

FieldTypeMeaning
datastring, requiredhex / base64 / XER / JSON, up to 600 kB
typestringType name. Default auto: MessageFrame, or Ieee1609Dot2Data when the bytes look like an SPDU. Setting a type disables layer peeling.
encodingstringuper | coer | xer; default auto (COER for Ieee1609Dot2Data, UPER otherwise)
include_xerboolalso return the XER text
schemastringJ2735 revision: auto (default: newest first, falling back to older revisions until the bytes decode cleanly), 2024, 2020 or 2016
curl -s https://cv2x.itsroads.com/api/decode -H 'Content-Type: application/json' \
  -d '{"data":"00142544A1B2C3D4…","include_xer":false}'

Response:

{
  "ok": true, "input": "bytes", "type": "MessageFrame", "encoding": "uper",
  "hex": "0014…", "bytes": 85, "consumed": 85,
  "messageId": 20, "message": "BasicSafetyMessage", "name": "BSM — Basic Safety Message",
  "json": {"messageId": 20, "value": {"BasicSafetyMessage": {"coreData": {…}}}},
  "summary": {"type": "BasicSafetyMessage", "headline": "BSM from vehicle A1B2C3D4 at 15.00 m/s …",
              "rows": [{"label": "Position", "value": "38.8823410, -77.1757920"}, …],
              "geojson": {"type": "FeatureCollection", "features": […]} },
  "layers": [{"layer": "IEEE 1609.3 WSMP", "detail": "…PSID 0x20…", "bytes": 6}, …],   // when wrappers were peeled
  "ieee1609dot2": {…},            // decoded SPDU when a 1609.2 wrapper was present
  "trailing_bytes": 2,            // only when bytes remain after the message
  "xer": "<MessageFrame>…",      // when include_xer
  "schema": "2020", "schema_label": "SAE J2735_202007",      // revision that produced the decode
  "schema_note": "Decoded with SAE J2735_202007 (older revision) because …",   // only when an older revision was used
  "schema_attempts": ["SAE J2735_202409: decoded 80 of 85 bytes", …],          // only in auto mode when the newest did not decode cleanly
  "standards": {…}, "ms": 12,
  "quota": {"authenticated": false, "limit": 5, "used": 1, "remaining": 4}
}

Encode

POST/api/encode

Encodes codec JSON to UPER with constraint checking, then decodes it back so the response json is the canonical form. Counts against the quota.

FieldTypeMeaning
jsonobject or string, requiredthe message, e.g. {"messageId":19,"value":{"SPAT":{…}}}
typestringdefault MessageFrame; any schema type works
outstringuper (default) | coer (adds coer_hex) | xer | json
checkboolconstraint checking, default true
include_xerboolalso return XER
schemastringJ2735 revision to encode with: 2024 (default), 2020 or 2016. Response includes schema and schema_label.
curl -s https://cv2x.itsroads.com/api/encode -H 'Content-Type: application/json' \
  -d '{"json":{"messageId":20,"value":{"BasicSafetyMessage":{"coreData":{…}}}},"type":"MessageFrame","check":true}'
{"ok": true, "type": "MessageFrame", "hex": "0014…", "base64": "ABQl…", "bytes": 85, "json": {…},
 "messageId": 20, "message": "BasicSafetyMessage", "name": "BSM — Basic Safety Message", "summary": {…},
 "warning": "messageId 19 does not match the BasicSafetyMessage payload…",   // only when inconsistent
 "ms": 9, "quota": {…}}

A constraint violation returns HTTP 422 with the offending field, e.g. Constraint violation in coreData.lat: the value is outside the range….

Templates

GET/api/templates

Lists the validated example messages (id, name, messageId, type, bytes, description, standard) and packets — wrapped frame samples for the decoder. No quota.

GET/api/templates/{id}

One template with its full json, on-air hex and notes. Ids: bsm, spat, map, tim-workzone, tim-speed-limit, rsm-workzone, psm, srm, ssm, sdsm, eva, rtcm.

curl -s https://cv2x.itsroads.com/api/templates/spat | jq .template.json

Types

GET/api/types

All type names in the selected revision (?schema=2024|2020|2016, default 2024: 1,218 types; 2020 and 2016: 702) plus messageIds (DSRCmsgID → MessageFrame alternative) and schemas: [{id, label, types}] for every revision installed. No quota.

curl -s https://cv2x.itsroads.com/api/types?schema=2020 | jq '.schemas, .count'

Random example

GET/api/example/{type}

A randomly generated, constraint-valid instance of any type — useful to see the exact JSON shape of a rarely used structure. Values are not meaningful. Counts against the quota.

curl -s https://cv2x.itsroads.com/api/example/RoadSafetyMessage | jq .json
curl -s "https://cv2x.itsroads.com/api/example/BasicSafetyMessage?schema=2016" | jq .json     # older revision

Accounts

POST/api/auth/register

{"email","password","name","org"} — password ≥ 10 characters. Sets the cv2x_session HttpOnly cookie (14 days).

POST/api/auth/login

{"email","password"} → {"ok":true,"user":{id,email,name,org,role}} and the cookie.

POST/api/auth/logout

Clears the cookie.

GET/api/auth/me

Current user (or null) and quota.

curl -s -c cookies.txt https://cv2x.itsroads.com/api/auth/login -H 'Content-Type: application/json' \
  -d '{"email":"you@agency.gov","password":"…"}'
curl -s -b cookies.txt https://cv2x.itsroads.com/api/rsus

Register/login and all RSU writes check the Origin header when present; scripts without an Origin header are fine.

RSU registry (signed in)

GET/api/rsus

Your devices (credentials never returned; has_auth_key etc. flags instead) and the vendor list.

POST/api/rsus PUT/api/rsus/{id}

Create / update. Fields: name (required), agency, vendor, model, firmware, lat, lon, elev, ip (IPv4/IPv6 literal, publicly routable on the free tier), port (161), snmp_version (3 | 2c), snmp_user, auth_proto (SHA, SHA-256, SHA-512, MD5), auth_key, priv_proto (AES, AES-256, DES), priv_key, community (v2c), mib (ntcip1218, NTCIP 1218 v01A), public (bool: coarse pin on the public map), notes. Secrets are encrypted at rest; omit them on update to keep the stored values. Up to 25 devices per account.

DELETE/api/rsus/{id}

Removes the device and its push log.

POST/api/rsus/{id}/test

SNMP GET of sysDescr and the RSU ID object → {"ok", "result"}.

GET/api/rsus/{id}/pushes

Last 50 pushes and probes: {id, kind, msg_type, psid, params, result, ok, created}.

Push a message (NTCIP 1218)

POST/api/rsus/{id}/push

Validates the payload as a J2735 MessageFrame, then writes one row of rsuMsgRepeatTable (store-and-repeat) or rsuIFMTable (immediate forward) over SNMPv3 authPriv.

FieldDefaultMeaning
hexrequiredUPER MessageFrame bytes
psidby message typeinteger PSID (BSM 0x20, PSM 0x27, RTCM 0x8000, SPaT 0x8002, TIM 0x8003, MAP 0xE0000017, SSM 0xE0000015, SRM 0xE0000016, SDSM 0x8010)
channel183C-V2X 20 MHz channel
interval_ms1000repeat period
priority60–7
start, endnow, +60 minISO-8601 UTC, e.g. 2026-10-08T14:00:00
row1table row index
moderepeatrepeat | ifm
signedfalseRSU signs with its SCMS certificate
curl -s -b cookies.txt https://cv2x.itsroads.com/api/rsus/12/push -H 'Content-Type: application/json' \
  -d '{"hex":"001380A0…","channel":183,"interval_ms":1000,"mode":"repeat"}'
# → {"ok": true, "result": "…snmpset output…", "psid": 32770, "message": "SPAT"}

Public map

GET/api/map/rsus

RSUs whose owners opted in, with coarse (3-decimal) coordinates and no network details. No quota.

Errors, CORS and limits

  • 400 bad input (not hex/base64/JSON/XER, bad JSON); 401 sign in required; 403 origin not allowed; 404 not found; 422 codec/constraint error with a descriptive message; 429 daily quota reached.
  • CORS: browser calls are allowed from itsroads.com, www.itsroads.com, atosinc.com and this origin, with credentials. Server-side scripts are unaffected. The embeddable widget (/static/cv2x-embed.js) uses this to decode on itsroads.com pages.
  • Rate limits: nginx limits /api/ to 10 requests/s per IP (burst 30) in addition to the daily quota. Inputs over 256 kB are rejected by the codec; each codec call runs sandboxed with a 5 s timeout.
  • Terms: free for engineering use; no uptime guarantee. For production integrations, SCMS or managed RSUs, contact ITS Roads.