@cardog/adf
v1.0.0
Published
The definitive ADF (Auto-lead Data Format) parser and builder for automotive leads — parse and create ADF XML for dealer CRMs
Maintainers
Readme
@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.
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
AdfLeadstructure 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/adfQuick 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>[email protected]</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); // "[email protected]"
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: "[email protected]",
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>[email protected]</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
└── contactAdfLead 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>[email protected]</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>[email protected]</email>
</contact>
</customer>
</prospect>
</adf>Provider-Specific Notes
AutoTrader.ca / AutoTrader.com
- Uses uppercase
<?ADF VERSION="1.0"?>processing instruction - Includes
imagetagelement for vehicle photos - Price element has extended attributes:
delta,relativeto,source - Phone
type="voice"instead oftype="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
- Official Specification: adfxml.info/adf_spec.pdf
- ADF XML Info: adfxml.info
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
