Connect the drive.
Complete the picture.
Bring dealership inventory, test drives and recorded sales outcomes together through a secure, scoped integration.
Explicit permissions and server-side tenant isolation.
Stable event IDs and receipts for safe retries.
Only the fields your integration is authorized to use.
Base URL
https://www.testdrivepro.co/api/partner/v1Your first request
- Agree on the dealership mapping and required scopes with TestDrivePro.
- Receive your key through an approved private channel and store it on your server.
- Request inventory, then follow
next_cursoruntil it is null.
curl --fail-with-body "https://www.testdrivepro.co/api/partner/v1/inventory?limit=50" \
--header "Authorization: Bearer $TDP_API_KEY" \
--header "Accept: application/json"Use the downloadable OpenAPI 3.1 specification for exact request and response schemas. Examples contain placeholders; never use real license information as a development fixture.
Authentication and access
Send Authorization: Bearer <partner key> with every request. Each key belongs to one partner connection and one dealership. The server derives the tenant from the key; callers cannot select another dealership.
Scopes are explicitly granted. Keys can expire, be revoked or be rotated. Keep credentials in a server secret manager, out of browser code, URLs and logs. All data responses use Cache-Control: no-store.
Retry-After: 60.API reference
Expand an endpoint for its parameters and contract. The schemas below come from the same versioned OpenAPI file you download.
GET/inventoryList inventory
Requires inventory:read. Tenant comes only from the partner key. No implicit available-only inventory filter. Start with no cursor; enumerate again for a fresh reconciliation.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | {"type":"integer","minimum":1,"maximum":2147483647} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InventoryPage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/drivesList drives
Requires drives:read. Tenant comes only from the partner key. No implicit available-only inventory filter. Start with no cursor; enumerate again for a fresh reconciliation.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | {"type":"integer","minimum":1,"maximum":2147483647} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DrivePage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/customersList customers
Requires customers:read. Tenant comes only from the partner key. No implicit available-only inventory filter. Start with no cursor; enumerate again for a fresh reconciliation.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | {"type":"integer","minimum":1,"maximum":2147483647} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomerPage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/notesList notes
Requires notes:read. Tenant comes only from the partner key. No implicit available-only inventory filter. Start with no cursor; enumerate again for a fresh reconciliation.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | {"type":"integer","minimum":1,"maximum":2147483647} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DriveNotePage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}POST/outcomesQueue an exact-drive outcome report
Requires outcomes:write and current dealer mutation access. 202 is an immutable queued receipt only: no drive, inventory or commission is changed. Same event and normalized body replays the receipt; conflicting content returns409. Body limit8KiB. Unknown fields rejected. Operational purposes return 409 unsupported_drive_purpose before queuing.
Request body
{
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OutcomeInput"
}
}
}
}Responses
{
"202": {
"description": "Queued or replayed receipt; not an applied sale",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OutcomeReceipt"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"404": {
"$ref": "#/components/responses/Error"
},
"409": {
"$ref": "#/components/responses/Error"
},
"413": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/outcomes/{external_event_id}Read a queued outcome receipt
Requires outcomes:read. The lookup is scoped to the key connection and tenant.
| Parameter | Location | Contract |
|---|---|---|
external_event_id * | path | {"type":"string","pattern":"^[A-Za-z0-9_.:-]{1,160}$"} |
Responses
{
"200": {
"description": "Receipt status",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OutcomeReceipt"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"404": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/staffList staff
Requires staff:read and activated extended reads. Current tenant salesperson records and their stored user references; no email, pay or commission data.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | {"type":"integer","minimum":1,"maximum":2147483647} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StaffPage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/liveList live
Requires live:read and activated extended reads. Active drives and recorded open tracking sessions only. A session_open state does not prove the phone is online. No GPS, speed, device ID or route data.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | {"type":"integer","minimum":1,"maximum":2147483647} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LivePage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/agreementsList agreements
Requires agreements:read and activated extended reads. Agreement metadata references only. Does not grant content, signature or PDF access; no URLs.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | {"type":"integer","minimum":1,"maximum":2147483647} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AgreementReferencePage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/documentsList documents
Requires documents:read and activated extended reads. Exact drive/customer-associated license capture metadata only. No URLs, image bytes, license fields or verification status.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | {"type":"string","format":"uuid"} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DocumentReferencePage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}GET/note-eventsList note-events
Requires notes:read and activated extended reads. Allowlisted existing audit note/issue changes, recorded actor and time. Current vehicle is labeled current, not historical. Null actor is not inferred. Source audit may be incomplete; no outbound delivery or partner acknowledgment. Continue through empty filtered pages while cursor is non-null.
| Parameter | Location | Contract |
|---|---|---|
cursor | query | Last event ID as an exact integer up to 9007199254740991. Continue until next_cursor is null, including filtered empty pages.{"type":"string","pattern":"^[1-9][0-9]{0,15}$"} |
limit | query | {"type":"integer","minimum":1,"maximum":100,"default":50} |
Responses
{
"200": {
"description": "Tenant-scoped page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NoteEventPage"
}
}
}
},
"400": {
"$ref": "#/components/responses/Error"
},
"401": {
"$ref": "#/components/responses/Error"
},
"403": {
"$ref": "#/components/responses/Error"
},
"429": {
"$ref": "#/components/responses/Error"
},
"503": {
"$ref": "#/components/responses/Error"
}
}All response and request models
Inventory
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"year",
"make",
"model",
"trim",
"price",
"miles",
"vin",
"stock",
"color",
"status",
"days_on_lot",
"created_at",
"updated_at"
],
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"year": {
"type": [
"integer",
"null"
]
},
"make": {
"type": [
"string",
"null"
]
},
"model": {
"type": [
"string",
"null"
]
},
"trim": {
"type": [
"string",
"null"
]
},
"price": {
"type": [
"number",
"null"
]
},
"miles": {
"type": [
"integer",
"null"
]
},
"vin": {
"type": [
"string",
"null"
]
},
"stock": {
"type": [
"string",
"null"
]
},
"color": {
"type": [
"string",
"null"
]
},
"status": {
"type": [
"string",
"null"
]
},
"days_on_lot": {
"type": [
"integer",
"null"
]
},
"created_at": {
"type": [
"string",
"null"
]
},
"updated_at": {
"type": [
"string",
"null"
]
}
}
}InventoryPage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Inventory"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"pattern": "^[1-9][0-9]*$",
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor."
}
}
}Drive
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"customer_id",
"vehicle_id",
"salesperson_id",
"date",
"status",
"outcome",
"created_at",
"updated_at",
"drive_purpose"
],
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"customer_id": {
"type": [
"integer",
"null"
]
},
"vehicle_id": {
"type": [
"integer",
"null"
]
},
"salesperson_id": {
"type": [
"integer",
"null"
]
},
"date": {
"type": [
"string",
"null"
]
},
"status": {
"type": [
"string",
"null"
]
},
"outcome": {
"type": [
"string",
"null"
],
"enum": [
"sold",
"not_sold",
null
]
},
"created_at": {
"type": [
"string",
"null"
]
},
"updated_at": {
"type": [
"string",
"null"
]
},
"drive_purpose": {
"type": "string",
"enum": [
"test_drive",
"ppi",
"employee_use",
"borrowed_vehicle"
],
"description": "Operational uses are not sales test drives and do not accept outcomes. Legacy records are test_drive."
},
"started_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"started_at_source": {
"type": "string",
"enum": [
"start_timestamp",
"not_recorded"
]
},
"returned_at": {
"type": [
"string",
"null"
],
"format": "date-time"
}
}
}DrivePage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Drive"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"pattern": "^[1-9][0-9]*$",
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor."
}
}
}Customer
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"first_name",
"last_name",
"phone",
"email",
"created_at"
],
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"first_name": {
"type": [
"string",
"null"
]
},
"last_name": {
"type": [
"string",
"null"
]
},
"phone": {
"type": [
"string",
"null"
]
},
"email": {
"type": [
"string",
"null"
]
},
"created_at": {
"type": [
"string",
"null"
]
},
"contact_consent": {
"type": [
"boolean",
"null"
]
},
"contact_consent_record": {
"type": "string",
"enum": [
"stored_boolean",
"not_recorded"
],
"description": "A stored false may be a historical default. No timestamp, channel permission or legal consent is inferred."
}
}
}CustomerPage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Customer"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"pattern": "^[1-9][0-9]*$",
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor."
}
}
}DriveNote
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"vehicle_id",
"notes",
"issues",
"updated_at"
],
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"vehicle_id": {
"type": [
"integer",
"null"
]
},
"notes": {
"type": [
"string",
"null"
]
},
"issues": {
"type": [
"string",
"null"
]
},
"updated_at": {
"type": [
"string",
"null"
]
}
}
}DriveNotePage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DriveNote"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"pattern": "^[1-9][0-9]*$",
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor."
}
}
}OutcomeInput
{
"type": "object",
"additionalProperties": false,
"required": [
"external_dealer_id",
"external_event_id",
"external_deal_id",
"tdp_drive_id",
"outcome",
"reported_at"
],
"properties": {
"external_dealer_id": {
"type": "string",
"minLength": 1,
"maxLength": 120,
"description": "Must equal the external dealer configured on this key connection."
},
"external_event_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_.:-]{1,160}$",
"description": "Stable idempotency identity within the connection; survives key rotation."
},
"external_deal_id": {
"type": "string",
"minLength": 1,
"maxLength": 160
},
"tdp_drive_id": {
"type": "integer",
"minimum": 1,
"maximum": 2147483647,
"description": "Exact ID obtained from this tenant; never guessed by customer name."
},
"outcome": {
"type": [
"string",
"null"
],
"enum": [
"sold",
"not_sold",
null
],
"description": "Explicit null reports a request to clear an outcome. None of these values is applied in v1."
},
"reported_at": {
"type": "string",
"format": "date-time",
"description": "Calendar-valid timestamp with seconds and explicit timezone, normalized to UTC."
}
},
"example": {
"external_dealer_id": "sandbox-dealer-1",
"external_event_id": "sale-event-001",
"external_deal_id": "sandbox-deal-001",
"tdp_drive_id": 101,
"outcome": "sold",
"reported_at": "2026-10-02T12:00:00Z"
}
}OutcomeReceipt
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"external_event_id",
"tdp_drive_id",
"status",
"applied",
"created_at"
],
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"external_event_id": {
"type": "string"
},
"tdp_drive_id": {
"type": "integer"
},
"status": {
"type": "string",
"enum": [
"queued",
"applied",
"rejected"
]
},
"applied": {
"type": "boolean",
"description": "False for every newly queued event. No applier is part of this version."
},
"duplicate": {
"type": "boolean",
"description": "Included by POST: same normalized payload replay returned the original receipt."
},
"created_at": {
"type": "string",
"format": "date-time"
},
"reviewed_at": {
"type": [
"string",
"null"
],
"format": "date-time"
}
}
}Error
{
"type": "object",
"additionalProperties": false,
"required": [
"error"
],
"properties": {
"error": {
"type": "object",
"additionalProperties": false,
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
}
}Staff
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"user_id",
"name",
"active"
],
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"user_id": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"active": {
"type": "boolean"
}
}
}Live
{
"type": "object",
"additionalProperties": false,
"required": [
"drive_id",
"vehicle_id",
"salesperson_id",
"status",
"drive_purpose",
"started_at",
"tracking_state",
"session_consented_at",
"last_sample_recorded_at"
],
"properties": {
"drive_id": {
"type": "integer",
"minimum": 1
},
"vehicle_id": {
"type": [
"integer",
"null"
]
},
"salesperson_id": {
"type": [
"integer",
"null"
]
},
"status": {
"const": "active"
},
"drive_purpose": {
"type": "string",
"enum": [
"test_drive",
"ppi",
"employee_use",
"borrowed_vehicle"
],
"description": "Operational uses are not sales test drives and do not accept outcomes. Legacy records are test_drive."
},
"started_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"tracking_state": {
"enum": [
"session_open",
"no_open_session"
],
"description": "Recorded session state only; session_open does not establish that the phone is online."
},
"session_consented_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"last_sample_recorded_at": {
"type": [
"string",
"null"
],
"format": "date-time"
}
}
}AgreementReference
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"test_drive_id",
"customer_id",
"vehicle_id",
"salesperson_id",
"signed_at",
"reference_type",
"content_access"
],
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"test_drive_id": {
"type": "integer",
"minimum": 1
},
"customer_id": {
"type": "integer",
"minimum": 1
},
"vehicle_id": {
"type": "integer",
"minimum": 1
},
"salesperson_id": {
"type": "integer",
"minimum": 1
},
"signed_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"reference_type": {
"const": "agreement_metadata"
},
"content_access": {
"const": "not_granted"
}
}
}DocumentReference
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"test_drive_id",
"customer_id",
"recorded_at",
"available_sides",
"reference_type",
"content_access"
],
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"test_drive_id": {
"type": "integer",
"minimum": 1
},
"customer_id": {
"type": "integer",
"minimum": 1
},
"recorded_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"available_sides": {
"type": "array",
"items": {
"enum": [
"front",
"back"
]
},
"uniqueItems": true,
"minItems": 1,
"description": "Sides with a recorded tenant-owned path reference; not a storage existence or content-access guarantee."
},
"reference_type": {
"const": "license_capture_metadata"
},
"content_access": {
"const": "not_granted"
}
}
}NoteEvent
{
"type": "object",
"additionalProperties": false,
"required": [
"event_id",
"test_drive_id",
"current_vehicle_id",
"recorded_actor_id",
"recorded_actor_role",
"recorded_at",
"changes",
"recorded_actor_name"
],
"properties": {
"event_id": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"test_drive_id": {
"type": "integer",
"minimum": 1
},
"current_vehicle_id": {
"type": [
"integer",
"null"
]
},
"recorded_actor_id": {
"type": [
"string",
"null"
]
},
"recorded_actor_role": {
"type": [
"string",
"null"
]
},
"recorded_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"changes": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"notes": {
"type": [
"string",
"null"
]
},
"issues": {
"type": [
"string",
"null"
]
}
}
},
"recorded_actor_name": {
"type": [
"string",
"null"
],
"description": "Actual actor display name recorded with the audit event; not inferred from the assigned salesperson."
}
}
}PullReceipt
{
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"retrieved_at",
"source_cursor",
"next_cursor",
"item_count",
"page_sha256",
"delivery_status"
],
"properties": {
"kind": {
"const": "read_only_pull"
},
"retrieved_at": {
"type": "string",
"format": "date-time"
},
"source_cursor": {
"type": [
"string",
"null"
]
},
"next_cursor": {
"type": [
"string",
"null"
],
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor."
},
"item_count": {
"type": "integer",
"minimum": 0
},
"page_sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"delivery_status": {
"const": "not_sent"
}
}
}StaffPage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Staff"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor.",
"pattern": "^[1-9][0-9]*$"
}
}
}LivePage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Live"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor.",
"pattern": "^[1-9][0-9]*$"
}
}
}AgreementReferencePage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AgreementReference"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor.",
"pattern": "^[1-9][0-9]*$"
}
}
}DocumentReferencePage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DocumentReference"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor.",
"format": "uuid"
}
}
}NoteEventPage
{
"type": "object",
"additionalProperties": false,
"required": [
"data",
"next_cursor",
"receipt"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/NoteEvent"
}
},
"next_cursor": {
"type": [
"string",
"null"
],
"description": "Continue until next_cursor is null; a filtered page may have no items and still have a cursor.",
"pattern": "^[1-9][0-9]*$"
},
"receipt": {
"$ref": "#/components/schemas/PullReceipt"
}
}
}Read every page
List endpoints accept limit from 1 to 100 (default 50) and an optional cursor. Responses contain data and next_cursor. Pass the returned cursor unchanged. Stop only when it is null—even a short or empty filtered page may have a next cursor.
Most cursors are numeric IDs; document cursors are capture UUIDs. Enumeration is not a frozen snapshot or an incremental change feed. Restart without a cursor for a new reconciliation. Never infer a sale from a missing inventory record.
Report an outcome safely
Submit an exact TestDrivePro drive ID with your stable external event ID. A 202 response means the report is queued for owner review. It does not mean a sale, inventory change or commission has been applied.
{
"external_dealer_id": "YOUR_DEALER_ID",
"external_event_id": "YOUR_UNIQUE_EVENT_ID",
"external_deal_id": "YOUR_DEAL_ID",
"tdp_drive_id": 101,
"outcome": "sold",
"reported_at": "2026-10-03T12:00:00Z"
}The drive ID above is illustrative; replace it with an actual authorized drive ID. Supported outcomes are sold, not_sold and an explicit null to request clearing an outcome. Omitted or extra fields are rejected. Maximum body size is 8 KiB.
Retry the same event ID with the same normalized body to retrieve the same receipt, including after key rotation. Reusing it with changed content returns 409 idempotency_conflict. Operational vehicle uses such as PPI do not accept sales outcomes.
Errors and retries
| Status | What to do |
|---|---|
400 / 413 | Correct the request fields or size before retrying. |
401 / 403 | Check key validity, expiry and assigned scopes. |
404 | Check the resource ID and the key’s dealership connection. |
409 | Resolve an event conflict or unsupported drive purpose; do not invent a new event ID to bypass it. |
429 | Wait at least the Retry-After interval, then retry. |
503 / network failure | Use bounded exponential backoff with jitter. Preserve the outcome event ID and body. |
Error responses follow { "error": { "code": "…", "message": "…" } }. Inspect the code rather than parsing the display message.
Know what the API shares
Inventory and drive scopes expose operational identifiers and fields. Customer contact information requires customers:read. Staff notes require notes:read and may contain sensitive free text.
Agreement and document endpoints return references and metadata. They do not grant access to files. Live state describes recorded tracking-session activity; it does not guarantee a phone is currently transmitting.
Recorded contact consent is a stored value, not proof of authorization for a new purpose. Agree on data use and retention before enabling any personal-data scope.
Ready for technical scoping
For Wayne Reaves or another DMS partner, confirm these details together before connecting production:
- Sandbox access, authentication and external dealership, vehicle, customer and deal identifiers.
- Required data fields, scopes and reconciliation frequency.
- Sale reversals, corrected deals and idempotency rules.
- Inventory/recon note acknowledgments and accepted sales-intake fields.
- Ownership of signup links, the seven-day trial entry point and any SSO.
The current API supports polling. Webhooks, partner-hosted popups, SSO and automatic partner delivery are not part of this contract.