Skip to content

cardog-ai/adf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@cardog/adf

The definitive TypeScript library for parsing and building ADF (Auto-lead Data Format) — the automotive industry's standard XML format for exchanging lead data between dealerships, CRMs, and lead providers.

npm version License: MIT

Features

  • Zero dependencies — No external packages required, works everywhere
  • TypeScript-first — Full type definitions with strict typing
  • Parse & Build — Bidirectional conversion between ADF XML and structured data
  • Spec-compliant — Implements ADF 1.0 specification with real-world compatibility
  • Normalized output — Flattened AdfLead structure for easy integration
  • Multi-vehicle support — Handles trade-ins and multiple vehicle interests
  • Battle-tested — Parses leads from AutoTrader, CarGurus, Kijiji Autos, and more

Installation

npm install @cardog/adf
# or
pnpm add @cardog/adf
# or
yarn add @cardog/adf

Quick Start

Parse ADF XML

import { parse, parseOne } from "@cardog/adf";

const xml = `<?xml version="1.0"?>
<?adf version="1.0"?>
<adf>
  <prospect status="new">
    <requestdate>2024-03-15T10:30:00Z</requestdate>
    <vehicle interest="buy" status="used">
      <year>2024</year>
      <make>Tesla</make>
      <model>Model 3</model>
      <vin>5YJ3E1EA5PF123456</vin>
    </vehicle>
    <customer>
      <contact>
        <name part="first">John</name>
        <name part="last">Doe</name>
        <email>john@example.com</email>
        <phone type="cellphone">416-555-1234</phone>
      </contact>
    </customer>
    <provider>
      <name>AutoTrader</name>
    </provider>
  </prospect>
</adf>`;

// Parse single lead
const lead = parseOne(xml);

console.log(lead.firstName); // "John"
console.log(lead.email); // "john@example.com"
console.log(lead.vehicleMake); // "Tesla"
console.log(lead.provider); // "AutoTrader"

// Parse multiple prospects
const result = parse(xml);
console.log(result.success); // true
console.log(result.leads); // AdfLead[]
console.log(result.errors); // string[]

Build ADF XML

import { build } from "@cardog/adf";

const xml = build(
  {
    firstName: "John",
    lastName: "Doe",
    email: "john@example.com",
    cellphone: "416-555-1234",
    vehicleMake: "Tesla",
    vehicleModel: "Model 3",
    vehicleYear: "2024",
    vehicleVin: "5YJ3E1EA5PF123456",
    vehiclePrice: 55000,
    vehiclePriceCurrency: "CAD",
  },
  {
    vendor: "Your Dealership",
    provider: "Your Website",
  },
);

Validate ADF

import { validate } from "@cardog/adf";

const result = validate(xml);
console.log(result.valid); // boolean
console.log(result.errors); // string[]

What is ADF?

ADF (Auto-lead Data Format) is an XML-based industry standard for transmitting automotive leads. Developed in the late 1990s by a consortium of 13 automotive internet companies, ADF has become the universal format supported by virtually every dealer CRM, lead provider, and automotive marketplace.

Key Facts

Attribute Value
Version 1.0 (released May 2000)
Format XML with optional DTD validation
Encoding UTF-8 (recommended)
Transmission Email attachment or HTTP POST
Specification adfxml.info/adf_spec.pdf

Who Uses ADF?

Lead Providers:

  • AutoTrader.ca / AutoTrader.com
  • CarGurus
  • Kijiji Autos
  • Cars.com
  • CarsDirect
  • TrueCar
  • Edmunds
  • Facebook Marketplace (via adapters)

CRM Systems:

  • VinSolutions
  • DealerSocket
  • ELEAD
  • Activix
  • PBS Systems
  • CDK Global

ADF 2.0

An updated specification (ADF 2.0) has been proposed with additional elements for chat transcripts, sensitive data URLs, and service/parts leads, but ADF 1.0 remains the dominant standard.


ADF Document Structure

Minimal Valid ADF

<?xml version="1.0" encoding="UTF-8"?>
<?adf version="1.0"?>
<adf>
  <prospect status="new">
    <requestdate>2024-03-15T10:30:00Z</requestdate>
    <vehicle interest="buy">
      <year>2024</year>
      <make>Tesla</make>
      <model>Model 3</model>
    </vehicle>
    <customer>
      <contact>
        <name part="full">John Doe</name>
        <email>john@example.com</email>
      </contact>
    </customer>
  </prospect>
</adf>

Complete Element Reference

adf
└── prospect                    # One or more lead records
    ├── @status                 # "new" | "resend"
    ├── id                      # Unique identifier
    │   └── @sequence, @source
    ├── requestdate             # ISO 8601 timestamp
    ├── vehicle[]               # One or more vehicles
    │   ├── @interest           # "buy" | "lease" | "sell" | "trade-in" | "test-drive"
    │   ├── @status             # "new" | "used" | "certified"
    │   ├── id
    │   ├── year
    │   ├── make
    │   ├── model
    │   ├── trim
    │   ├── vin
    │   ├── stock
    │   ├── bodystyle
    │   ├── doors
    │   ├── transmission        # "automatic" | "manual" | "CVT"
    │   ├── condition
    │   ├── colorcombination
    │   │   ├── interiorcolor
    │   │   └── exteriorcolor
    │   ├── price
    │   │   ├── @type           # "asking" | "offer" | "msrp" | "invoice" | "appraisal"
    │   │   ├── @currency       # ISO 4217 code (CAD, USD)
    │   │   └── @delta, @relativeto, @source
    │   ├── odometer
    │   │   ├── @status         # "actual" | "estimated" | "not-actual"
    │   │   └── @units          # "km" | "mi"
    │   ├── option[]
    │   ├── finance
    │   │   ├── method          # "finance" | "lease" | "cash"
    │   │   └── balance
    │   ├── pricecomments
    │   ├── comments
    │   └── imageurl / imagetag
    ├── customer
    │   ├── contact
    │   │   ├── @primarycontact # "1" for primary
    │   │   ├── name[]
    │   │   │   ├── @part       # "first" | "middle" | "last" | "suffix" | "full"
    │   │   │   └── @type       # "individual" | "business"
    │   │   ├── email[]
    │   │   │   └── @preferredcontact  # "1" if preferred
    │   │   ├── phone[]
    │   │   │   ├── @type       # "phone" | "cellphone" | "fax" | "pager" | "voice"
    │   │   │   └── @time       # "morning" | "afternoon" | "evening" | "day" | "nopreference"
    │   │   └── address[]
    │   │       ├── @type       # "home" | "work" | "delivery"
    │   │       ├── street[]
    │   │       │   └── @line   # Line number for multi-line
    │   │       ├── apartment
    │   │       ├── city
    │   │       ├── regioncode  # Province/State code (ON, CA, NY)
    │   │       ├── postalcode
    │   │       └── country
    │   ├── comments
    │   └── timeframe
    │       ├── description     # "immediate" | "day" | "week" | "month" | "year"
    │       ├── earliestdate
    │       └── latestdate
    ├── vendor                  # The dealership
    │   ├── @id
    │   ├── id
    │   ├── vendorname
    │   ├── url
    │   └── contact             # Same structure as customer/contact
    └── provider                # The lead source
        ├── @id
        ├── id
        ├── name
        ├── service
        ├── url
        ├── email
        ├── phone
        └── contact

AdfLead Type

The normalized, flattened lead structure returned by parse() and parseOne():

interface AdfLead {
  // === Source Information ===
  id?: string; // Unique lead identifier
  requestDate?: Date; // When the lead was submitted
  status: ProspectStatus; // "new" | "resend"
  provider?: string; // Lead source name (AutoTrader, CarGurus)
  providerService?: string; // Service type
  vendor?: string; // Dealership name

  // === Contact Information ===
  firstName?: string;
  middleName?: string;
  lastName?: string;
  fullName?: string; // Computed or from part="full"
  email?: string; // Primary email
  phone?: string; // Primary phone
  cellphone?: string; // Mobile phone
  address?: {
    street?: string;
    city?: string;
    state?: string; // Mapped from regioncode
    postalCode?: string;
    country?: string;
  };

  // === Vehicle of Interest ===
  vehicleInterest?: VehicleInterest; // "buy" | "lease" | "sell" | "trade-in" | "test-drive"
  vehicleStatus?: VehicleStatus; // "new" | "used" | "certified"
  vehicleYear?: string;
  vehicleMake?: string;
  vehicleModel?: string;
  vehicleTrim?: string;
  vehicleVin?: string;
  vehicleStock?: string;
  vehiclePrice?: number; // Parsed numeric value
  vehiclePriceCurrency?: string; // "CAD" | "USD"
  vehicleOdometer?: number; // Parsed numeric value
  vehicleOdometerUnits?: string; // "km" | "mi"
  vehicleExteriorColor?: string;
  vehicleInteriorColor?: string;

  // === Trade-In (if present) ===
  tradeIn?: {
    year?: string;
    make?: string;
    model?: string;
    vin?: string;
    odometer?: number;
    condition?: string;
  };

  // === Additional ===
  comments?: string; // Vehicle comments
  customerComments?: string; // Customer message
  financeMethod?: string; // "finance" | "lease" | "cash"
  timeframe?: string; // Purchase timeframe

  // === Raw Data ===
  raw: {
    prospect: AdfProspect; // Full parsed prospect
    xml?: string; // Original XML
  };
}

Real-World Examples

AutoTrader.ca Lead

<?xml version="1.0" encoding="UTF-8"?>
<?ADF VERSION="1.0"?>
<adf>
  <prospect status="new">
    <requestdate>2024-03-18T16:45:00Z</requestdate>
    <vehicle interest="buy" status="used">
      <id sequence="1" source="AutoTrader.ca">AT-98765</id>
      <year>2024</year>
      <make>Hyundai</make>
      <model>IONIQ 5</model>
      <vin>KM8KRDAF3NU123456</vin>
      <price type="asking" currency="CAD">54995</price>
    </vehicle>
    <customer>
      <contact primarycontact="1">
        <name part="first">David</name>
        <name part="last">Williams</name>
        <email>david@example.com</email>
        <phone type="cellphone">416-555-1234</phone>
      </contact>
      <comments>Is this vehicle still available?</comments>
    </customer>
    <vendor>
      <vendorname>Your Dealership</vendorname>
    </vendor>
    <provider>
      <name part="full">AutoTrader.ca</name>
      <service>New Lead Notification</service>
    </provider>
  </prospect>
</adf>

Trade-In / Sell Request

<?xml version="1.0"?>
<?adf version="1.0"?>
<adf>
  <prospect status="new">
    <vehicle interest="buy" status="new">
      <year>2025</year>
      <make>Tesla</make>
      <model>Model Y</model>
    </vehicle>
    <vehicle interest="trade-in" status="used">
      <year>2020</year>
      <make>Honda</make>
      <model>Civic</model>
      <vin>2HGFC2F59LH123456</vin>
      <odometer units="km">85000</odometer>
      <condition>Good</condition>
    </vehicle>
    <customer>
      <contact>
        <name part="full">Jane Smith</name>
        <email>jane@example.com</email>
      </contact>
    </customer>
  </prospect>
</adf>

Provider-Specific Notes

AutoTrader.ca / AutoTrader.com

  • Uses uppercase <?ADF VERSION="1.0"?> processing instruction
  • Includes imagetag element for vehicle photos
  • Price element has extended attributes: delta, relativeto, source
  • Phone type="voice" instead of type="phone"

CarGurus

  • May omit <?adf version="1.0"?> processing instruction
  • Uses street line="1" attribute for multi-line addresses
  • Includes deal rating and price analysis data in comments

Kijiji Autos

  • Similar format to AutoTrader (same parent company)
  • May include listing URL in provider section

Facebook Marketplace

  • Requires adapter/middleware to convert to ADF
  • Contact info may be limited based on user privacy settings

API Reference

parse(xml: string): ParseResult

Parse ADF XML into normalized leads.

interface ParseResult {
  success: boolean; // True if no errors
  leads: AdfLead[]; // Normalized leads
  errors: string[]; // Error messages
  document?: AdfDocument; // Raw parsed document
}

parseOne(xml: string): AdfLead | null

Parse ADF XML and return the first lead, or null if parsing fails.

validate(xml: string): { valid: boolean; errors: string[] }

Validate ADF XML structure without normalizing.

build(lead: Partial<AdfLead>, options?: BuildOptions): string

Build ADF XML from a lead object.

interface BuildOptions {
  xmlDeclaration?: boolean; // Include <?xml ...?> (default: true)
  adfPi?: boolean; // Include <?adf version="1.0"?> (default: true)
  pretty?: boolean; // Pretty print (default: true)
  vendor?: string; // Default vendor name
  provider?: string; // Default provider name
  providerService?: string; // Default service name
}

buildFromProspect(prospect: Partial<AdfProspect>, options?: BuildOptions): string

Build ADF XML from a raw prospect structure (advanced use).


Low-Level XML Utilities

For advanced use cases, the XML parser is exposed:

import {
  parseXml,
  findChild,
  findChildren,
  getChildText,
  getAttr,
} from "@cardog/adf";
import type { XmlNode } from "@cardog/adf";

const root = parseXml(xml);
const prospect = findChild(root, "prospect");
const vehicles = findChildren(prospect, "vehicle");
const year = getChildText(vehicles[0], "year");
const status = getAttr(vehicles[0], "status");

Specification References


Contributing

Contributions are welcome! If you encounter an ADF format from a provider that doesn't parse correctly, please open an issue with a sample (with personal info redacted).

License

MIT - see LICENSE


Built by Cardog

About

The definitive ADF (Auto-lead Data Format) parser for automotive leads

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors