Guide
Decoding CBOR (RFC 8949) into readable JSON
CBOR, the Concise Binary Object Representation, is the binary cousin of JSON that the IETF standardised in RFC 8949. It takes the JSON data model and adds byte strings, tags, a wider range of numbers and the option to stream items of unknown length. It appears in WebAuthn and passkeys, in COSE signed and encrypted messages, in CoAP for small devices, and in many IoT formats. Raw CBOR is unreadable, so this page decodes it and also writes JSON as CBOR.
How a CBOR item is built
Each data item starts with one byte whose top three bits are the major type and whose low five bits are additional information. Major type 0 is an unsigned integer, 1 a negative one, 2 a byte string, 3 a text string, 4 an array, 5 a map, 6 a tag and 7 floats and simple values. When the additional information is below 24 it holds the value or length directly; values 24 to 27 mean that the number follows in 1, 2, 4 or 8 bytes; 31 starts an indefinite-length item that ends with a break byte, 0xff.
Using the page
Pick a direction. To decode, paste hex or Base64, or open a file, and read the JSON with its byte count. To encode, paste JSON, choose the output form and copy the bytes. A message that is cut short, uses a reserved value or has bytes left over after the first item produces an error with the offset. Nothing is uploaded; the bytes are decoded in your browser.
Values JSON cannot hold
JSON has no byte strings, tags or large integers, so the page shows them in a way that loses nothing. A byte string is an object with a $binary key holding its hex. A tagged value is an object with $tag (the tag number) and value, so a date tagged 1 appears with its number rather than as a formatted time. Integers beyond 2 to the 53rd power are strings. Half-precision floats are expanded to ordinary numbers. Map keys that are not text are shown as text.
Floats and the shortest form
The CBOR rules recommend that an encoder write each number in the shortest form that preserves it. The integer 500 needs two bytes after the head (19 01 f4), and 1.5 fits a half-precision float (f9 3e 00), whereas 0.1 needs a full double (fb followed by eight bytes). When the page encodes JSON, it follows these rules, which is why two encoders can produce different bytes for the same data and still be correct.
A worked example
The Sample button loads the example from the RFC: a2 61 61 01 61 62 82 02 03, nine bytes. a2 is a map (major type 5) with two pairs. 61 61 is a text string of length 1, “a”, and 01 is the integer 1. 61 62 is the string “b”, and 82 starts an array of two items, 02 and 03. The JSON is {“a”: 1, “b”: [2, 3]}. Encoding {“id”: 7, “ok”: true, “tags”: [“a”, “b”]} gives a362696407626f6bf564746167738261616162, 19 bytes.
Limits
Tags are shown as numbers and not interpreted, so a timestamp tag is not converted to a date and a bignum tag is not turned into a number. Indefinite-length items are read but encoded as definite ones. The page processes one top-level item; sequences of items (CBOR sequences) must be split first. Decoding does not verify signatures or encryption in COSE and WebAuthn structures, it only shows their contents.
Related tools
- MessagePack to JSON covers the other common binary JSON format.
- JWT Decoder shows tokens that use JSON instead of CBOR.
- Base64 Encoder converts the Base64 text in which CBOR is often passed.
- Hex to Binary helps to split a head byte into its type and additional information.
Checked against the current version of the CBOR to JSON on .

