Partner API Integration Guide
This guide is for third-party partners, DMS/CRM vendors, and dealer data teams who want to exchange data with CarBuddy — either by pushing customer records into CarBuddy for AI-led follow-up, or by pulling lead and conversation data out for their own systems.
If you are a dealer looking to upload a one-off file, see CSV Upload. If you want a recurring automated file feed, see SFTP. This page focuses on the programmatic REST API and how the pieces fit together.
Integration at a glance
CarBuddy supports integration in both directions:
| Direction | What it does | Method |
|---|---|---|
| Partner → CarBuddy | Push customer/vehicle records so CarBuddy runs AI-led email/SMS follow-up | Enrollment REST API or SFTP / CSV feed |
| CarBuddy → Partner | Send you qualified leads and event notifications as they happen | Outbound webhooks |
| Partner → CarBuddy (read) | Pull your leads, conversations and outcomes into your CRM/BI | Portal Data API |
Choose the method that fits your systems:
- Real-time / event-driven → Enrollment REST API (one HTTP call per batch).
- Scheduled bulk files → SFTP feed (drop a CSV, we collect it).
- Manual / occasional → CSV upload in the portal.
You can combine methods — for example, push records via the API and pull outcomes via the Portal Data API.
Authentication
All programmatic access uses an API key presented in a request header. Keys are issued per client by your CarBuddy account manager — there is no public self-service signup.
| Surface | Base URL | Header | Key format |
|---|---|---|---|
| Enrollment / Campaign API | https://engine.carbuddyai.com | X-Api-Key | cb_… |
| Portal Data API | https://portal.carbuddyai.com | X-API-Key | cb_live_… |
| SFTP feed | sftp.carbuddy.uk:30022 | SSH key | ED25519 / RSA |
API keys are equivalent to a password. Send them only over HTTPS, store them in a secret manager (never in source control), and rotate them if you suspect exposure. Contact your account manager to revoke or reissue a key.
Keys are scoped to a single client slug (e.g. acme-motors); presenting a key against a different slug returns 401. Each key carries its own request rate limit — 60 requests per minute by default, raisable per integration on request. Requests over the limit receive 429 Too Many Requests with a Retry-After header giving the seconds to wait.
Because a single request accepts up to 10,000 recipients, the default limit is generous for batch enrolment; it constrains per-record chatter, not volume.
1. Enrollment REST API
The enrollment endpoint is the primary way to push customer records into a CarBuddy campaign. One call enrolls a batch of recipients; CarBuddy then runs the AI-led email/SMS sequence, handles replies, classifies leads, and hands qualified leads back to your team.
Endpoint
POST https://engine.carbuddyai.com/webhook/{clientSlug}/enroll
| Header | Value |
|---|---|
X-Api-Key | Your cb_… key (scoped to {clientSlug}) |
Content-Type | application/json |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
campaign_type | string | ✅ | The campaign to enrol into, e.g. lost-sales, evhc. Your account manager confirms the value(s) available to you. |
label | string | — | A free-text batch label for your own tracking, e.g. "March 2026 Service Due". |
recipients | array | ✅ | 1–10,000 recipient objects (see below). |
Each object in recipients:
| Field | Type | Required | Description |
|---|---|---|---|
first_name | string | ✅ | Customer first name. If you only have a title (e.g. "Mr Patel") with no forename, put the title here rather than duplicating the surname. |
last_name | string | — | Customer surname. |
email | string | ✅ | Customer email. Rows with a malformed email are skipped (reported back), not fatal to the batch. |
mobile | string | — | Mobile number, ideally E.164 (+447700123456). Other formats are normalised. |
vehicle | string | — | Vehicle description, e.g. "Volkswagen Golf 1.5 TSI". |
vehicle_reg | string | — | Registration plate. If make/model are blank, this triggers a DVLA/MOT lookup to enrich the record. |
manufacturer | string | — | Vehicle make, e.g. "Volkswagen". |
dealer | string | — | Dealership or selling site name. |
assigned_branch | string | — | Branch name used for lead routing in the portal. |
metadata | object | — | Free-form JSON object of extra attributes about the customer or enquiry (e.g. charger model, property type, survey answers). Stored on the recipient exactly as sent; unknown keys are kept, not dropped. The AI conversation only sees the metadata keys agreed for your campaign during setup (EVHC campaigns also use findings), and those agreed keys are also shown in the lead handed to the dealer team. Any other key is stored for reporting and reconciliation, not shown to the AI. |
service_date | string | — | Date anchor for countdown-scheduled campaigns (ISO YYYY-MM-DD). |
salutation | string | — | Title (Mr/Mrs/Ms/…). |
opted_in_sms | boolean | — | SMS marketing consent (PECR). Send false to suppress SMS for this contact. Omitting the field does not suppress SMS — see Consent & data protection. |
opted_in_email | boolean | — | Email marketing consent. Send false to suppress email for this contact. Omitting the field does not suppress email. |
false, not an omissionBoth consent flags are three-state: true (consented), false (blocked — we will not send on that channel), and absent/null (unknown — we will send). If your export cannot distinguish "not consented" from "no data", filter non-consenting contacts out of the payload before you send it rather than relying on the field being omitted.
For electronic vehicle health check (EVHC) campaigns, additional per-row fields drive routing and message content — vhc_date, sale_outcome, rag_status, work_details, and a metadata.findings[] array. Ask your account manager for the EVHC field spec if you are integrating that campaign type.
Example request
curl -X POST https://engine.carbuddyai.com/webhook/acme-motors/enroll \
-H "X-Api-Key: cb_xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"campaign_type": "lost-sales",
"label": "March 2026 Import",
"recipients": [
{
"first_name": "Sarah",
"last_name": "Jones",
"email": "sarah.jones@example.com",
"mobile": "+447700900456",
"vehicle": "Ford Kuga 1.5 EcoBoost",
"vehicle_reg": "DK21 ABC",
"manufacturer": "Ford",
"dealer": "Acme Motors",
"opted_in_sms": true,
"opted_in_email": true
}
]
}'
Responses
| Status | Meaning |
|---|---|
201 Created | Batch accepted — see the response body below. A 201 does not mean every row enrolled. |
400 Bad Request | Validation error — the body's details array names the failing field and row index. |
401 Unauthorized | Missing, invalid, expired, or wrong-client API key. |
404 Not Found | Unknown client slug. |
409 Conflict | The campaign has an end date that has passed. Body carries campaign_type and end_date. |
422 Unprocessable Entity | Batch failed a data-quality gate (e.g. the surname mapped into both name columns). Nothing was enrolled — fix the batch and resubmit. |
429 Too Many Requests | Rate limit exceeded; retry after the Retry-After header value. |
Success body
{
"batchId": 123,
"enrolled": 96,
"duplicates": 3,
"suppressed": 1,
"skipped": [
{ "idx": 12, "reason": "invalid_email" },
{ "idx": 40, "reason": "duplicate_email_active" },
{ "idx": 71, "reason": "suppressed" }
]
}
| Field | Description |
|---|---|
batchId | Batch identifier — use it to find the upload in the portal. |
enrolled | Rows that actually entered the campaign. |
duplicates | Rows collapsed as duplicates (already-active contacts + repeats within this batch). |
suppressed | Rows dropped because the contact has previously opted out or bounced. |
skipped | Per-row drop list. idx is the zero-based index into the recipients array you sent, so you can map each entry back to your source record. |
warning | Present when rows enrolled without dealer or assigned_branch — those messages render with a blank dealer name. |
skippedenrolled is frequently lower than the number of recipients you sent, and that is usually correct behaviour (duplicates, opt-outs, malformed emails). Log the skipped array on your side — it is the only place the per-row reason is reported.
Common skipped reasons: invalid_email, duplicate_email_active, duplicate_email_within_batch, duplicate_branch_service_date, suppressed, lost_sales_cycle_cap.
What happens after enrollment
CarBuddy automatically:
- De-duplicates against customers already active in a campaign (by email or mobile).
- Suppresses anyone who has unsubscribed or is active in a conflicting campaign.
- Enriches records from the registration plate where make/model are missing.
- Starts the AI-led email/SMS sequence and handles inbound replies.
- Classifies each conversation and hands qualified leads to your team (see Outbound webhooks).
1b. Recipient events (after enrolment)
Tell the journey that something happened to a customer after you enrolled them — a survey completed, a quote requested, a booking made. Do not re-post the customer to /enroll for this; an active recipient is deliberately skipped as a duplicate.
POST https://engine.carbuddyai.com/webhook/{your-client-slug}/recipients/events
X-Api-Key: cb_your_api_key_here
Content-Type: application/json
{
"email": "jane.doe@example.com",
"event": "survey_completed",
"occurred_at": "2026-09-04T10:15:00Z",
"metadata": { "charger_model": "myenergi Zappi 7kW" }
}
- Identify the customer by
email(case-insensitive) ormobile; addcampaign_typeif they are active in more than one campaign. - For a project-scoped campaign, also send
partner_referencewith the project ID supplied in enrolmentmetadata, and always includecampaign_type. Events cannot close or hand over an unknown or ambiguous project. eventissnake_case, 1–64 characters. The accepted names for your campaign — and whether an event resumes the remaining journey fromoccurred_at— are agreed during setup.metadatais merged into the customer record. Keys agreed for your campaign during setup reach the AI conversation from its next reply; any other key is stored only.occurred_atis also the idempotency key: retain the same timestamp on retries. The same(customer, event, occurred_at)posted twice is a200with"status": "duplicate"; it does not apply the event again. A handover whose queue admission previously failed may be retried within 23 hours. An older ambiguous admission needs manual reconciliation.- Configured archive events stop the remaining journey. Configured handover events pause it and route the lead to the agreed contact.
Responses: 200 applied (body reports applied.metadata_merged / applied.schedule_resumed; archive and handover events also report applied.closed / applied.handover_requested) · 400 validation, campaign_type_required_for_partner_reference or campaign_type_and_partner_reference_required · 401 wrong or missing key · 404 recipient_not_found · 409 recipient_not_active, ambiguous_partner_reference or handover_admission_reconciliation_required · 422 event_not_allowed (body lists allowed), partner_reference_not_configured, partner_reference_immutable, conflicting_event_actions or handover_reason_required.
2. Checking batch and recipient status
Once a batch is enrolled you can monitor progress in the portal under Upload History and Leads.
Programmatic read endpoints also exist under https://engine.carbuddyai.com/api/v1/{clientSlug}/… for batch summaries and recipient lookups. Programmatic read access is enabled per-partner — contact your account manager to have it turned on for your key, and they will confirm the exact endpoints and headers for your account.
3. SFTP / CSV data feed
If you prefer to deliver scheduled files rather than call the API, use the SFTP gateway. Your DMS or data team drops CSV files onto a dedicated endpoint and CarBuddy collects and processes them automatically — no per-row API calls.
- Connection details, key setup, and folder layout: SFTP Integration.
- Column format and quality rules: CSV Upload.
Column mapping
The importer maps your column headers to CarBuddy fields case-insensitively and accepts common aliases, so most DMS exports work without renaming columns. Representative aliases:
| CarBuddy field | Accepted headers (any case) |
|---|---|
first_name | first name, firstname, forename, first |
last_name | last name, lastname, surname, last |
email | email, email address, e-mail, email_address |
mobile | mobile, phone, tel, telephone, cell, phone number |
manufacturer | make, manufacturer, vehicle make |
vehicle | model, vehicle, vehicle model, car |
vehicle_reg | reg, registration, vrm, number plate, reg number |
dealer | dealer, dealership, branch, location |
assigned_branch | branch, assigned branch, location |
salutation | salutation, title, honorific |
opted_in_email | opted in email, email opt in, email consent |
opted_in_sms | opted in sms, sms opt in, sms consent |
Files may be comma-, tab-, or pipe-delimited — the delimiter is auto-detected. Raw DMS EVHC exports with dotted headers (e.g. Contacts.RegNo, Contacts.Salute) are recognised automatically. Health-check feeds may additionally carry Sale Outcome / RAG Status (or Outcome / Urgency) columns used for EVHC routing.
If a header isn't recognised it is ignored; contact your account manager to add a mapping.
4. Outbound webhooks & lead delivery
CarBuddy can push events to your systems as they happen — for example when a customer is classified as a qualified lead — so your CRM or DMS receives lead data without polling. Event types, payload format, and HMAC signature verification are documented on the API & Webhooks page.
DMS lead push (we integrate with your system)
Where a partner runs a lead-management system, CarBuddy can deliver qualified leads directly into it rather than (or as well as) firing a webhook. We already integrate outbound with lead-management platforms such as Dealerweb LMS and Cooper Solutions. If you operate a DMS/CRM you'd like CarBuddy to push leads into, contact your account manager with the endpoint and authentication details and we'll scope the connector.
5. Portal Data API
To pull your leads and conversations into your own CRM, BI, or data warehouse, use the Portal Data API.
GET https://portal.carbuddyai.com/api/leads
GET https://portal.carbuddyai.com/api/conversations
| Header | Value |
|---|---|
X-API-Key | Your cb_live_… portal key |
These endpoints return the same lead and conversation records visible in the portal — recipient details, message history, AI classifications, and conversation outcomes — filtered to your account. Ask your account manager to issue a portal API key and confirm the query parameters (date range, status filters, pagination) available for your integration.
For occasional exports without an integration, the portal also offers CSV export from the UI.
Interactive API reference
A machine-readable OpenAPI 3 spec for the Campaign API is available to integration partners. It is not published on a public URL: ask your CarBuddy contact for a copy.
You can import the OpenAPI spec into Postman, Insomnia, or your code generator of choice.
Consent & data protection
CarBuddy sends marketing communications under UK PECR/GDPR rules. You are responsible for ensuring every contact you enrol has a lawful basis for contact — the API enrols what you send it.
How the consent flags are evaluated at send time:
| Value you send | Behaviour |
|---|---|
true | Consented — we send on that channel. |
false | Blocked — we never send on that channel, for the life of the record. |
Omitted / null | Unknown — we send. Treated as legacy/soft opt-in data. |
An absent opted_in_sms / opted_in_email does not stop a send. This is deliberate — recurring DMS feeds rarely carry a consent column, and their operators strip non-consenting customers at source. If your feed can express non-consent, send an explicit false; if it cannot, exclude those contacts from the payload.
Regardless of the flags:
- Unsubscribes, STOP replies, hard bounces, and spam reports are honoured automatically and suppressed from all future campaigns for that client, not just the current one.
- Every outbound email carries an unsubscribe link; every SMS honours STOP.
- Contacts who complete repeated re-engagement cycles are permanently suppressed automatically.
See Data Privacy for how CarBuddy stores and processes customer data.
Getting connected
- Tell your CarBuddy account manager which method you want to use (API, SFTP, or both) and which direction (push, pull, or both).
- For the API, we issue you a per-client
cb_…key scoped to your client slug and campaign type(s). - For SFTP, we provision your account and folder and register your SSH key — see SFTP Integration.
- Test against a small batch, confirm the records appear in the portal, then go live. For a new partner integration we can first issue a separate sandbox key, base URL and client slug on our staging environment. Nothing sent there reaches customers or dealers, and the production key follows once the sandbox run has passed.
Questions? Raise a ticket via the portal support system or contact your account manager.