REST API
Integrate the V2X codec.
Decode a hex UPER message to JSON, or encode JSON back to the wire. One POST endpoint, both directions, no SDK. Copy-paste examples below.
The endpoint
| Field | Type | Default | Notes |
|---|---|---|---|
hex | string | required | UPER message as hex. Spaces are ignored. |
type | string | auto | Auto-detects by messageID, or force one: cam, denm, cpm, vam, mapem, spatem, srem, ssem, ivim, rtcmem. |
enrich | boolean | true | Adds readable sibling fields (enum names, decoded values, units). Set false for raw ETSI JSON. |
Rate limit: 120 requests/minute per IP. Response: 200 with { message_type, metadata, payload }.
Examples
Each call decodes the same real Collective Perception Message (CPM). Swap the hex for your own.
curl -s -X POST https://v2json.skyv2x.com/api/v1/decode \
-H 'Content-Type: application/json' \
-d '{"hex":"020E0001869F028251F6C74A962F0B0365833700320190000D6D83100300001241E008058010000C00603E80638032018E0000063E045897FFE25FFF890419E0","type":"auto","enrich":true}'
import requests
resp = requests.post(
"https://v2json.skyv2x.com/api/v1/decode",
json={
"hex": "020E0001869F028251F6C74A962F0B0365833700320190000D6D83100300001241E008058010000C00603E80638032018E0000063E045897FFE25FFF890419E0",
"type": "auto", # or "cpm"
"enrich": True, # False for raw ETSI JSON
},
timeout=5,
)
resp.raise_for_status()
data = resp.json()
print(data["message_type"]) # CPM
print(data["payload"]) # decoded message
const res = await fetch("https://v2json.skyv2x.com/api/v1/decode", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
hex: "020E0001869F028251F6C74A962F0B0365833700320190000D6D83100300001241E008058010000C00603E80638032018E0000063E045897FFE25FFF890419E0",
type: "auto",
enrich: true,
}),
});
const data = await res.json();
console.log(data.message_type, data.payload);
// Node 18+ ships fetch natively; no dependency needed.
const res = await fetch("https://v2json.skyv2x.com/api/v1/decode", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ hex: process.argv[2], type: "auto", enrich: true }),
});
if (!res.ok) throw new Error(`decode failed: HTTP ${res.status}`);
const { message_type, payload } = await res.json();
console.log(message_type, JSON.stringify(payload, null, 2));
The response
The decode lives under payload; metadata carries the decode status and sizing.
{
"message_type": "CPM",
"metadata": {
"decode_status": "success",
"decoded_bytes": 64,
"message_type": "CPM",
"encoding": "UPER",
"tool_version": "..."
},
"payload": {
"header": { "protocolVersion": 2, "messageId": 14, "stationId": ... },
"payload": { "...": "decoded CPM containers" }
}
}
With enrich: true, readable siblings sit next to the coded fields: enum names,
GNSS in 7-decimal degrees, ETSI unavailable markers as explicit null.
Send enrich: false for raw ETSI JSON.
Encode JSON back to the wire
Send a decoded message as JSON, get the UPER hex back. The input is the bare message:
the payload from a decode with enrich: false, no
{ metadata, message } wrapper, no _* keys.
| Field | Type | Default | Notes |
|---|---|---|---|
payload | object | required | The bare decoded message (raw, no _* keys). |
type | string | required | Explicit — no auto. CAM needs a variant, cam_en or cam_ts (they share messageID 2); others are denm, cpm, vam, mapem, spatem, srem, ssem, ivim, rtcmem. |
Response: 200 with { hex, metadata }. An unchanged message round-trips byte-for-byte. A value outside the ASN.1 range is rejected (400) rather than producing invalid hex.
Decode, edit, encode. The round-trip in each language:
HEX=020E0001869F028251F6C74A962F0B0365833700320190000D6D83100300001241E008058010000C00603E80638032018E0000063E045897FFE25FFF890419E0
# decode → take the raw message → encode it back
MSG=$(curl -s -X POST https://v2json.skyv2x.com/api/v1/decode \
-H 'Content-Type: application/json' \
-d "{\"hex\":\"$HEX\",\"type\":\"auto\",\"enrich\":false}" | jq -c '.payload')
curl -s -X POST https://v2json.skyv2x.com/api/v1/encode \
-H 'Content-Type: application/json' \
-d "{\"payload\":$MSG,\"type\":\"cpm\"}" | jq -r '.hex'
import requests
API = "https://v2json.skyv2x.com/api/v1"
hex_in = "020E0001869F028251F6C74A962F0B0365833700320190000D6D83100300001241E008058010000C00603E80638032018E0000063E045897FFE25FFF890419E0"
# 1 · decode to the raw message (no _* enrichments)
msg = requests.post(f"{API}/decode",
json={"hex": hex_in, "type": "auto", "enrich": False}, timeout=5).json()["payload"]
# 2 · edit msg here …
# 3 · encode it back to hex
hex_out = requests.post(f"{API}/encode",
json={"payload": msg, "type": "cpm"}, timeout=5).json()["hex"]
print(hex_out) # identical to hex_in if msg is unchanged
const API = "https://v2json.skyv2x.com/api/v1";
const post = (p, body) => fetch(`${API}/${p}`, {
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
}).then((r) => r.json());
const { payload } = await post("decode", { hex: hexIn, type: "auto", enrich: false });
// edit payload …
const { hex } = await post("encode", { payload, type: "cpm" });
console.log(hex); // round-trips if unchanged
const API = "https://v2json.skyv2x.com/api/v1";
const post = (p, body) => fetch(`${API}/${p}`, {
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
}).then((r) => r.json());
const { payload } = await post("decode", { hex: process.argv[2], type: "auto", enrich: false });
const { hex } = await post("encode", { payload, type: "cpm" });
console.log(hex);
Verify a round-trip
One call decodes a hex, re-encodes it, and reports whether the wire bytes match, so the codec checks itself.
curl -s -X POST https://v2json.skyv2x.com/api/v1/verify \
-H 'Content-Type: application/json' \
-d '{"hex":"020E0001869F028251F6C74A962F0B0365833700320190000D6D83100300001241E008058010000C00603E80638032018E0000063E045897FFE25FFF890419E0"}'
# → { "round_trip": true, "message_type": "CPM", "original_bytes": 64, "reencoded_bytes": 64 }
Common questions
How do I decode an ETSI V2X message?
Send the hex UPER stream in a POST to /api/v1/decode. The response is structured JSON, with the message type auto-detected from the messageID. No SDK or local ASN.1 toolchain.
Can I encode JSON back to V2X UPER hex?
Yes. POST the decoded message to /api/v1/encode with its type. The same service runs both directions, and an unchanged message round-trips byte-for-byte.
Which V2X message types are supported?
The ETSI C-ITS message set: CAM, DENM, CPM, VAM, MAPEM, SPATEM, SREM, SSEM and IVIM.
Do I need an API key or a signup?
No. The API is free and open, rate-limited to 120 requests per minute per IP.
How do I check my own V2X encoder against a reference?
POST a hex to /api/v1/verify. It decodes and re-encodes the message and reports whether the wire bytes match, so you can validate an encoder against a byte-exact reference.
Other endpoints
The message types the running binary supports.
A ground-truth sample hex per type, to try the API without your own capture.
Liveness and the binary version.
Interactive OpenAPI (Swagger UI): the full schema.