Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nordicsemi/bluetooth-numbers-database/llms.txt

Use this file to discover all available pages before exploring further.

The GAP Appearance value is a 2-byte field carried as AD Type 0x19 in Bluetooth advertising packets. It gives scanning devices an immediate, standardized way to identify what kind of device is advertising — a heart rate monitor, a keyboard, a smartwatch — without needing to connect and inspect GATT services. The same value is also exposed as the Appearance characteristic (UUID 2A01) within the Generic Access Service (1800) so that connected centrals can query it at any time. The 16-bit appearance value encodes two sub-fields: bits 15–6 (10 bits) carry the category, and bits 5–0 (6 bits) carry the sub-category within that category. This structure allows up to 1,024 distinct device categories each with up to 64 sub-types.

Data Structure

Each entry in gap_appearance.json conforms to the following schema:
category
integer
required
Decimal value identifying the appearance category, corresponding to bits 15–6 of the 2-byte Appearance field. For example, category 2 represents “Computer” and category 13 represents “Heart Rate Sensor”.
name
string
required
Human-readable category name as defined in the Bluetooth Assigned Numbers document (e.g., "Computer", "Phone", "Heart Rate Sensor").
subcategory
array
Optional array of sub-category objects that further refine the device type within a category. Not all categories have sub-categories — for example, “Phone” (category 1) has no subcategories, while “Computer” (category 2) has 15.

Example: Computer Category

{
  "category": 2,
  "name": "Computer",
  "subcategory": [
    { "value": 1,  "name": "Desktop Workstation" },
    { "value": 2,  "name": "Server-class Computer" },
    { "value": 3,  "name": "Laptop" },
    { "value": 4,  "name": "Handheld PC/PDA (clamshell)" },
    { "value": 5,  "name": "Palm-size PC/PDA" },
    { "value": 6,  "name": "Wearable computer (watch size)" },
    { "value": 7,  "name": "Tablet" },
    { "value": 8,  "name": "Docking Station" },
    { "value": 9,  "name": "All in One" },
    { "value": 10, "name": "Blade Server" },
    { "value": 11, "name": "Convertible" },
    { "value": 12, "name": "Detachable" },
    { "value": 13, "name": "IoT Gateway" },
    { "value": 14, "name": "Mini PC" },
    { "value": 15, "name": "Stick PC" }
  ]
}

Decoding Appearance Values

The raw 2-byte Appearance value encodes both the category and the sub-category in a single unsigned 16-bit integer. To decode it:
  • Category = appearanceValue >> 6 (shift right by 6 bits, isolating bits 15–6)
  • Sub-category = appearanceValue & 0x3F (mask with 0b00111111, isolating bits 5–0)
import appearances from './v1/gap_appearance.json';

/**
 * Decode a raw 16-bit GAP Appearance value into category and sub-category names.
 * @param {number} appearanceValue - Raw 16-bit unsigned integer from advertising data.
 * @returns {{ category: string, subcategory: string }} Human-readable names.
 */
function decodeAppearance(appearanceValue) {
  const categoryCode    = appearanceValue >> 6;
  const subcategoryCode = appearanceValue & 0x3F;

  const categoryEntry = appearances.find(a => a.category === categoryCode);
  if (!categoryEntry) {
    return { category: `Unknown (${categoryCode})`, subcategory: 'Unknown' };
  }

  let subcategoryName = 'Generic';
  if (subcategoryCode > 0 && categoryEntry.subcategory) {
    const subEntry = categoryEntry.subcategory.find(
      s => s.value === subcategoryCode
    );
    subcategoryName = subEntry ? subEntry.name : `Unknown (${subcategoryCode})`;
  }

  return {
    category: categoryEntry.name,
    subcategory: subcategoryName
  };
}

// Heart Rate Belt: category 13 (Heart Rate Sensor), subcategory 1
// Raw value = (13 << 6) | 1 = 833
console.log(decodeAppearance(833));
// { category: 'Heart Rate Sensor', subcategory: 'Heart Rate Belt' }

// Generic Laptop: category 2 (Computer), subcategory 3 (Laptop)
// Raw value = (2 << 6) | 3 = 131
console.log(decodeAppearance(131));
// { category: 'Computer', subcategory: 'Laptop' }

// Generic Phone (no subcategory): category 1, subcategory 0
// Raw value = (1 << 6) | 0 = 64
console.log(decodeAppearance(64));
// { category: 'Phone', subcategory: 'Generic' }

Lookup by Category

import appearances from './v1/gap_appearance.json';

// Find a category entry by its numeric code
function getCategoryByCode(code) {
  return appearances.find(a => a.category === code) || null;
}

// Find a category by name (case-insensitive partial match)
function findCategoryByName(query) {
  const lower = query.toLowerCase();
  return appearances.filter(a => a.name.toLowerCase().includes(lower));
}

console.log(getCategoryByCode(13));
// { category: 13, name: 'Heart Rate Sensor', subcategory: [...] }

console.log(findCategoryByName('watch'));
// Returns entries for 'Watch' category

Common Appearances

The following table lists commonly encountered GAP Appearance categories. Devices that do not fit any defined category should use 0 (Unknown).
Category CodeNameHas Sub-categories?
0UnknownNo
1PhoneNo
2ComputerYes (Desktop, Laptop, Tablet, etc.)
3WatchYes (Sports Watch, Smartwatch)
4ClockNo
5DisplayNo
6Remote ControlNo
7Eye-glassesNo
8TagNo
9KeyringNo
10Media PlayerNo
11Barcode ScannerNo
12ThermometerYes (Ear Thermometer)
13Heart Rate SensorYes (Heart Rate Belt)
14Blood PressureYes (Arm, Wrist)
15Human Interface DeviceYes (Keyboard, Mouse, Gamepad, etc.)
The GAP Appearance value is defined in two complementary Bluetooth specification documents: the Bluetooth Core Specification Supplement (CSS) specifies the AD Type 0x19 data structure and the bit-field encoding, while the Bluetooth Assigned Numbers document provides the authoritative list of assigned category and sub-category values. The gap_appearance.json file in this database mirrors the Assigned Numbers listing.

Build docs developers (and LLMs) love