> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fryte.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Vehicles

> Find your Vehicle ID

export const API_BASE = 'https://staging.api.fryte.xyz';

export const VehicleFinder = () => {
  const [token, setToken] = React.useState('');
  const [authed, setAuthed] = React.useState(false);
  const [oems, setOems] = React.useState({});
  const [models, setModels] = React.useState({});
  const [capacities, setCapacities] = React.useState([]);
  const [vehicles, setVehicles] = React.useState([]);
  const [selectedOem, setSelectedOem] = React.useState('');
  const [selectedModel, setSelectedModel] = React.useState('');
  const [selectedCapacity, setSelectedCapacity] = React.useState('');
  const [vehicleId, setVehicleId] = React.useState('');
  const [vehicleDetails, setVehicleDetails] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState('');
  const [copied, setCopied] = React.useState(false);
  const [hoveredId, setHoveredId] = React.useState(null);
  const authHeaders = {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  };
  const connect = () => {
    setError('');
    fetch(`${API_BASE}/vehicles/info/oems`, {
      headers: authHeaders
    }).then(r => {
      if (!r.ok) throw new Error();
      return r.json();
    }).then(data => {
      const mapped = {};
      Object.entries(data).forEach(([code, name]) => {
        mapped[name] = code;
      });
      setOems(mapped);
      setAuthed(true);
    }).catch(() => setError('Invalid API key'));
  };
  const selectOem = name => {
    setSelectedOem(name);
    setSelectedModel('');
    setSelectedCapacity('');
    setCapacities([]);
    setVehicles([]);
    setVehicleId('');
    setVehicleDetails(null);
    setModels({});
    if (!name) return;
    fetch(`${API_BASE}/vehicles/info/models?manufacturer=${encodeURIComponent(name)}`, {
      headers: authHeaders
    }).then(r => r.json()).then(data => {
      const mfr = data[oems[name]] || data[name] || ({});
      setModels(mfr.models || mfr);
    }).catch(() => setError('Failed to load models'));
  };
  const selectModel = modelName => {
    setSelectedModel(modelName);
    setSelectedCapacity('');
    setCapacities([]);
    setVehicles([]);
    setVehicleId('');
    setVehicleDetails(null);
    if (!modelName) return;
    const oemCode = oems[selectedOem];
    const modelCode = Object.entries(models).find(([k, v]) => v === modelName)?.[0];
    if (!oemCode || !modelCode) return;
    fetch(`${API_BASE}/vehicles/info/battery_capacities?search=${oemCode}${modelCode}`, {
      headers: authHeaders
    }).then(r => r.json()).then(data => {
      const mfr = data[oemCode] || ({});
      const mdl = (mfr.models || ({}))[modelCode] || ({});
      const caps = mdl.capacities || [];
      setCapacities(caps.sort((a, b) => a - b));
    }).catch(() => setError('Failed to load battery capacities'));
  };
  const selectCapacity = cap => {
    setSelectedCapacity(cap);
    setVehicles([]);
    setVehicleId('');
    setVehicleDetails(null);
    setError('');
    if (!cap) return;
    const oemCode = oems[selectedOem];
    const modelCode = Object.entries(models).find(([k, v]) => v === selectedModel)?.[0];
    const batteryCode = String(Math.floor(Number(cap) / 10)).padStart(2, '0');
    const prefix = `${oemCode}${modelCode}${batteryCode}`;
    setLoading(true);
    fetch(`${API_BASE}/vehicles/info/vehicle_id?vehicle_id=${prefix}`, {
      headers: authHeaders
    }).then(r => r.json()).then(data => {
      const list = Array.isArray(data) ? data : [];
      setVehicles(list);
      if (list.length === 1) {
        setVehicleId(list[0].vehicle_id);
        setVehicleDetails(list[0]);
      }
      if (list.length === 0) setError('No vehicles found');
    }).catch(() => setError('Failed to load vehicles')).finally(() => setLoading(false));
  };
  const selectVehicle = v => {
    setVehicleId(v.vehicle_id);
    setVehicleDetails(v);
  };
  const copy = () => {
    navigator.clipboard.writeText(vehicleId);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };
  const reset = () => {
    setSelectedOem('');
    setSelectedModel('');
    setSelectedCapacity('');
    setCapacities([]);
    setVehicles([]);
    setVehicleId('');
    setVehicleDetails(null);
    setError('');
  };
  const s = {
    wrap: {
      display: 'flex',
      flexDirection: 'column',
      gap: '20px',
      padding: '24px',
      borderRadius: '12px',
      border: '1px solid rgba(128,128,128,0.25)',
      margin: '24px 0'
    },
    label: {
      display: 'block',
      fontSize: '13px',
      fontWeight: 600,
      marginBottom: '6px',
      color: 'inherit',
      opacity: 0.7,
      textTransform: 'uppercase',
      letterSpacing: '0.5px'
    },
    input: (on = true) => ({
      width: '100%',
      padding: '10px 12px',
      borderRadius: '8px',
      border: '1px solid rgba(128,128,128,0.3)',
      fontSize: '14px',
      backgroundColor: on ? 'transparent' : 'rgba(128,128,128,0.1)',
      color: 'inherit',
      cursor: on ? 'pointer' : 'not-allowed',
      outline: 'none',
      boxSizing: 'border-box'
    }),
    btn: (on = true) => ({
      padding: '12px',
      borderRadius: '8px',
      border: 'none',
      backgroundColor: on ? '#F57C00' : '#9ca3af',
      color: '#fff',
      fontSize: '14px',
      fontWeight: 600,
      cursor: on ? 'pointer' : 'default',
      width: '100%'
    }),
    btnOutline: {
      padding: '8px 16px',
      borderRadius: '6px',
      border: '1px solid rgba(128,128,128,0.3)',
      backgroundColor: 'transparent',
      fontSize: '13px',
      cursor: 'pointer',
      color: 'inherit'
    },
    err: {
      padding: '10px 14px',
      borderRadius: '8px',
      backgroundColor: 'rgba(220,38,38,0.1)',
      border: '1px solid rgba(220,38,38,0.3)',
      color: '#dc2626',
      fontSize: '13px'
    },
    vehicleRow: (selected, hovered) => ({
      padding: '12px 16px',
      borderRadius: '8px',
      cursor: 'pointer',
      border: selected ? '2px solid #F57C00' : hovered ? '1px solid #F57C00' : '1px solid rgba(128,128,128,0.25)',
      backgroundColor: selected ? 'rgba(245,124,0,0.1)' : hovered ? 'rgba(245,124,0,0.05)' : 'transparent',
      display: 'grid',
      gridTemplateColumns: '1fr 1fr 1fr',
      gap: '4px',
      fontSize: '13px',
      color: 'inherit',
      transition: 'all 0.15s'
    })
  };
  if (!authed) return <div style={s.wrap}>
      <label style={s.label}>API Key</label>
      <div style={{
    display: 'flex',
    gap: '10px'
  }}>
        <input type="password" value={token} onChange={e => setToken(e.target.value)} placeholder="Your API key" onKeyDown={e => e.key === 'Enter' && token && connect()} style={{
    ...s.input(),
    flex: 1
  }} />
        <button onClick={connect} disabled={!token} style={{
    ...s.btn(!!token),
    width: 'auto',
    padding: '10px 24px',
    whiteSpace: 'nowrap'
  }}>Authenticate</button>
      </div>
      {error && <div style={s.err}>{error}</div>}
    </div>;
  if (vehicleId && vehicleDetails) return <div style={s.wrap}>
      <div style={{
    textAlign: 'center',
    padding: '8px 0'
  }}>
        <div style={{
    fontSize: '13px',
    color: 'inherit',
    opacity: 0.6,
    marginBottom: '8px'
  }}>Your Vehicle ID</div>
        <code style={{
    fontSize: '28px',
    fontWeight: 700,
    letterSpacing: '2px',
    color: 'inherit'
  }}>{vehicleId}</code>
      </div>
      <div style={{
    display: 'grid',
    gridTemplateColumns: '1fr 1fr 1fr',
    gap: '8px',
    fontSize: '13px',
    color: 'inherit',
    opacity: 0.7,
    padding: '12px 16px',
    backgroundColor: 'rgba(128,128,128,0.06)',
    borderRadius: '8px',
    border: '1px solid rgba(128,128,128,0.25)'
  }}>
        <div><span style={{
    fontWeight: 500
  }}>Manufacturer:</span> {vehicleDetails.manufacturer}</div>
        <div><span style={{
    fontWeight: 500
  }}>Model:</span> {vehicleDetails.model}</div>
        <div><span style={{
    fontWeight: 500
  }}>Battery:</span> {vehicleDetails.battery_capacity} kWh</div>
        <div><span style={{
    fontWeight: 500
  }}>CCS Power:</span> {vehicleDetails.ccs_charging_power} kW</div>
        <div><span style={{
    fontWeight: 500
  }}>MCS Power:</span> {vehicleDetails.mcs_charging_power > 0 ? `${vehicleDetails.mcs_charging_power} kW` : '—'}</div>
        <div><span style={{
    fontWeight: 500
  }}>CCS Position:</span> {vehicleDetails.position_ccs}</div>
      </div>
      <div style={{
    display: 'flex',
    gap: '12px'
  }}>
        <button onClick={copy} style={{
    ...s.btn(),
    flex: 1,
    backgroundColor: copied ? '#16a34a' : '#F57C00'
  }}>
          {copied ? 'Copied!' : 'Copy Vehicle ID'}
        </button>
        <button onClick={reset} style={s.btnOutline}>New lookup</button>
      </div>
    </div>;
  return <div style={s.wrap}>
      <div style={{
    display: 'grid',
    gridTemplateColumns: '1fr 1fr 1fr',
    gap: '16px'
  }}>
        <div>
          <label style={s.label}>Manufacturer</label>
          <select value={selectedOem} onChange={e => selectOem(e.target.value)} style={s.input()}>
            <option value="">Select...</option>
            {Object.keys(oems).sort().map(n => <option key={n} value={n}>{n}</option>)}
          </select>
        </div>
        <div>
          <label style={s.label}>Model</label>
          <select value={selectedModel} onChange={e => selectModel(e.target.value)} disabled={!selectedOem} style={s.input(!!selectedOem)}>
            <option value="">Select...</option>
            {Object.values(models).sort().map(n => <option key={n} value={n}>{n}</option>)}
          </select>
        </div>
        <div>
          <label style={s.label}>Battery (kWh)</label>
          <select value={selectedCapacity} onChange={e => selectCapacity(e.target.value)} disabled={!selectedModel} style={s.input(!!selectedModel)}>
            <option value="">Select...</option>
            {capacities.map(c => <option key={c} value={c}>{c} kWh</option>)}
          </select>
        </div>
      </div>
      {loading && <div style={{
    textAlign: 'center',
    color: 'inherit',
    opacity: 0.6,
    fontSize: '14px'
  }}>Loading vehicles...</div>}
      {vehicles.length > 1 && <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '8px'
  }}>
          <label style={s.label}>Select your configuration</label>
          <div style={{
    display: 'grid',
    gridTemplateColumns: '1fr 1fr 1fr',
    gap: '4px',
    fontSize: '11px',
    color: 'inherit',
    opacity: 0.5,
    padding: '0 16px',
    textTransform: 'uppercase',
    letterSpacing: '0.5px'
  }}>
            <div>CCS Power</div><div>MCS Power</div><div>CCS Position</div>
          </div>
          {vehicles.map(v => <div key={v.vehicle_id} onClick={() => selectVehicle(v)} onMouseEnter={() => setHoveredId(v.vehicle_id)} onMouseLeave={() => setHoveredId(null)} style={s.vehicleRow(vehicleId === v.vehicle_id, hoveredId === v.vehicle_id)}>
              <div style={{
    fontWeight: 500
  }}>{v.ccs_charging_power} kW</div>
              <div>{v.mcs_charging_power > 0 ? `${v.mcs_charging_power} kW` : '—'}</div>
              <div>{v.position_ccs}</div>
            </div>)}
        </div>}
      {error && <div style={s.err}>{error}</div>}
    </div>;
};

Every planner request requires a `vehicle_id`. Authenticate with your [API key](/#authentication) and select your truck below to get yours.

<VehicleFinder />

<Accordion title="Understanding the Vehicle ID">
  The ID is a 12-digit code. Each pair of digits represents a vehicle attribute:

  | Digits | Meaning            | Example                             |
  | ------ | ------------------ | ----------------------------------- |
  | 1–2    | Manufacturer       | `01` = Daimler                      |
  | 3–4    | Model              | `01` = eActros                      |
  | 5–6    | Battery capacity   | `60` = 600 kWh                      |
  | 7–8    | CCS charging power | `70` = 700 kW                       |
  | 9–10   | MCS charging power | `00` = not supported                |
  | 11     | MCS flag           | `0` = no MCS                        |
  | 12     | CCS position       | `1` = right, `2` = left, `3` = both |
</Accordion>
