Skip to content
ridekickDevelopersv1 · 2026-10-13

Versioning

Every answer carries Ridekick-API-Version: 2026-10-13.

Changes are additive within a version

New fields can appear within a version, so ignore fields you don't recognize: the published schema allows additional properties.

Branch on structure, not on words

Display text (label, statusLabel, headerLine, heading, question, preview, href, chosenLabels) may change wording within a version, so never branch on it. Branch on the structured fields instead: state, ended, canChoose, chosen, hidden, counts, answerMode, kind.

Breaking changes

Most version-date bumps only add fields. A breaking change to what an existing field means, not its type, still ships as a new revision date within v1, with an entry in the changelog below, rather than a new major version. Once an external client exists, a meaning change also gets a deprecation window on the old meaning before the new one goes live; the changelog entry says whether one applied and why. A field that is removed, or that changes type, is a new major version, /api/v2, instead.

Changelog

Newest first. A version-date bump that changes meaning always gets an entry here.

  • 2026-10-13: A row's label is no longer "Dealer N". Where the dealership is not named, label is now a description of the CAR: {colour} {year} {model} {trim} · about {miles} mi · listed {price} (the price is always marked "listed": it is the listing's asking price before taxes and dealer fees, never an offer or a written price; a part we do not have is left out; the miles are the listing's, rounded to the nearest 100). Once the buyer has unlocked or chosen a dealership its label is that dealership's name where an assistant is allowed to read it, otherwise the same car description. The same holds for chosenLabels, dealerLabel, the offer sheet's label and the unlock answer's dealer.label. A label is display text: its words may change, never branch on it, and two rows can read the same; refer to a row by rowKey. These listing fields are no longer withheld from an assistant: listedAtCents (the listed price, never an offer or a written price) and the listing trim (quotedTrim, ranked[].trim, hero.trim and the single check's car.trim). The listing's distance, the found count and the unchosen configuration stay withheld, and the exact miles are never sent. Email, text and push never carry a price. The car search (GET /api/v1/listings/search) gains three keys on each match, for every account: label (the same car description, from the one label function), miles (the listing's miles rounded to the nearest 100, or null) and listPriceUsd (the asking price in whole dollars, or null); it still carries no dealership and no distance. The key set of every other response is unchanged.
  • 2026-10-12: POST /api/v1/requests and its preview are planned, not yet open to developers; this entry says what holds once they are. Every send of POST /api/v1/requests now needs the previewToken that POST /api/v1/requests/preview returned for the same request and the same Idempotency-Key, for every account. Until now only an account named by a switch needed it; an account that did not require a preview sent without a token (the 2026-10-08 entry says so, and that was true then). A send without a token is now refused with preview_required (428) and nothing is sent; a previewToken that does not open, is older than 15 minutes, or belongs to another request or key is refused as before (preview_invalid, preview_expired, preview_mismatch). A request already sent is still replayed with no token. The preview is what the assistant shows the buyer before a send. Asking for the buyer's yes is the assistant's step: the token proves the order, not the yes. Additive for a client that previews first; a client that sent without a preview must now preview.
  • 2026-10-08: Added POST /api/v1/requests/preview (planned: not yet open to developers; it needs the read scope, and it already answers a connected assistant whose buyer has been let in and has grid access): it takes the same body and Idempotency-Key as a send, writes nothing and contacts nobody, and answers the request as sent, upTo (the limit the caller sent, never a count we found), the next steps and a previewToken valid for 15 minutes. POST /api/v1/requests takes an optional previewToken in its body. On an account that requires a preview, a send without a valid token is refused and nothing is sent: preview_required (428), preview_invalid (422), preview_expired (409) and preview_mismatch (409) are new error codes; a request already sent is replayed with no token. The success answer of a send now also carries nextSteps, requestUrl and askMe. An account that does not require a preview still sends without a token, and a previewToken such an account sends is still checked: a bad one is refused. Added the error code test_dealer_not_ready (HTTP 409) for POST /api/v1/requests and POST /api/v1/requests/preview (planned, not yet open to developers): the buyer's account is in test mode and the test dealership is not ready, so nothing was sent. The answer does not say why, nothing in the request is wrong, and a retry does not help until the test setup is ready. Additive: an account that is not in test mode never sees it. The preview answer of an account in test mode also carries testMode: true (the field is absent for every other account) and its nextSteps say the request asks only the test dealership.
  • 2026-10-08: Added the error code and refusal reasons of a buyer's assistant writing to a dealership (the routes are planned, not yet callable). thread_not_yours (409): Ridekick's own system is handling the conversation with this dealership right now, so the assistant cannot send a message to it; nothing was sent. no_request_sent gains four details.reason values: text_refused (the message was not accepted; it never says why and never repeats the text), dealer_quiet_hours (a message cannot be sent to this dealership right now; this reason also carries nextOpenAt, the ISO time in UTC when one can be sent; nothing was stored to send later), message_limit_reached (a limit on messages was reached; the answer says nothing more) and channel_not_open. Nothing in the existing endpoints changed.
  • 2026-10-08: POST /api/v1/requests (planned, not yet open to developers) takes ONE more body shape: instead of configuration, radiusMiles, limit and acceptFewer, a body may carry listingId (an opaque handle for one listed car that the assistant's car search returned; that search is not part of this API), zip and an optional note, to ask the ONE dealership that lists that car. The answer is the same { dealSetId, requested: 1 }. A body that fits neither shape is refused (400 invalid_request). A listingId that is not valid for this grant is the same 400 and never says why. A car that cannot be requested right now (its handle is older than 24 hours, or it is no longer listed) is a 422 no_request_sent with the new reason listing_unavailable, and nothing is sent. The same Idempotency-Key with a different listingId is a 409 idempotency_conflict, never a replay. This is additive: a body with configuration works exactly as before.
  • 2026-10-08: Added the no_request_sent reason cap_reached for POST /api/v1/deal-sets/{id}/rows/{rowKey}/unlock (planned, not yet callable): the buyer has already used the three unlocks one request allows, so nothing was unlocked. It is a refusal, not a server error: do not send it again with this key or a new one. The unlock is a preview then a confirmation, and cannot be undone.
  • 2026-10-07: A plain GET /api/v1/deal-sets now means the buyer's ACTIVE requests, and Ridekick-More is true on a plain call only when an active request was left out. Before, any ended (inactive) request made every plain call say there was more, so a client that read to the end never finished. Now ended requests, including a request whose price is not in writing, come only with since=1970-01-01T00:00:00Z. A plain call also looks at up to 12 grid requests that have not ended (it was the newest 4). No field was added, removed or renamed. A client that counted ended requests from a plain call must read them with since=1970-01-01T00:00:00Z.
  • 2026-10-06: The list GET /api/v1/deal-sets now ALSO holds requests to one dealership, and every entry carries a kind: deal_set or single. Its meaning changed: it is every request the buyer's requests page shows, not only grid sets. A single entry's id is a price-check id, not a deal-set id, and GET /api/v1/deal-sets/{id} does not accept it. A client MUST skip an entry whose kind it does not know. Every successful list answer carries the header Ridekick-More (true when more entries exist beyond this response; true when that cannot be told). To read everything, call with since=1970-01-01T00:00:00Z, then repeat with since set to the Ridekick-As-Of you got, until Ridekick-More is false. An entry can appear on more than one page: keep one entry per id and kind. A plain call returns the newest grid sets (at most 4) and the newest 25 requests to one dealership that are not inactive.
  • 2026-10-03: GET /api/v1/deal-sets takes an optional since query parameter (an ISO 8601 time with a time zone) and then returns only the requests with activity after it, so an assistant can ask what changed. This is a poll: it does not notify. With since, the answer carries a Ridekick-As-Of header, the time to pass as the next since; it is set slightly before the read began, so an entry can be returned twice but is never skipped. A since that is not a valid time is a 400 invalid_request, never an empty list. Without since the answer is exactly what it was. The MCP tool deal_sets_list takes the same optional since and returns the header as asOf. Additive: no version bump.
  • 2026-10-03: distanceMiles (on a deal-set result row and on its hero) and lowestWrittenDistanceMiles (on a deal-set entry, which copies the distance of the hero) changed meaning for a row the buyer has not yet unlocked or chosen: it is now the LOWER BOUND of the distance band (0, 10, 25, 50 or 100), not the distance, because an exact distance with her ZIP can identify a dealership. After the row is unlocked or chosen it is the exact distance, as before. Read the new distanceBand (additive, string | null: Under 10 miles, 10 to 25 miles, 25 to 50 miles, 50 to 100 miles or Over 100 miles) for the words. All three (distanceMiles, distanceBand and lowestWrittenDistanceMiles) are null for an AI assistant acting for the buyer.
  • 2026-10-03: A connected assistant (a delegated agent) now ALWAYS receives the numbered Dealer N in every field that labels a dealer: label on rows, the hero and the questions and drafts that need the buyer, the question heading (rebuilt from its category with that label), and chosenLabels. The number is the same one the row had while hidden. The buyer's own session is unchanged. The type is unchanged, so there is no version bump; only the text of label and chosenLabels changed.
  • 2026-10-03: POST /api/v1/requests (planned, not yet callable): if a request fails with a 500 internal and nothing was sent to any dealership, send the request again with the same Idempotency-Key and the same body and it runs again, each time it fails that way (two concurrent retries: one runs, the other gets idempotency_in_progress). A different body with the same key is still idempotency_conflict. If the request was already answered (a success, a no_request_sent refusal, or contact_interrupted), the same key returns that same answer instead. A failure where a request was already created is also final.
  • 2026-10-03: Added the error code no_request_sent (HTTP 422) for POST /api/v1/requests (planned, not yet callable): the request was understood and refused, and nothing was sent to any dealership. It is not a server error. error.details.reason is one of already_asked_today, already_requested, no_eligible_dealer, not_available_for_this_account; it is the only details field that is a string, and it comes from this closed set. Until now these cases answered a false 500 internal. Sending the same key again returns the same answer.
  • 2026-10-03: Added POST /api/v1/requests (planned, not yet callable): a buyer's connected assistant sends a request for written offers under a grant with the requests:submit scope, with a required Idempotency-Key header. These error codes are now returned by it: forbidden, invalid_request, idempotency_key_required, idempotency_conflict, idempotency_in_progress, agent_stopped, buyer_profile_incomplete, fewer_rooftops_found, contact_interrupted and note_refused. The request body can also carry an optional note (1 to 600 characters of plain text, sent to the dealerships as the buyer's own message when it passes our check; the answer is the code note_refused and nothing is sent when it does not, and it never says why). The success answer can also carry skipped: how many dealerships were not contacted because this buyer's assistant already asked about that car in the last 24 hours. It is a count, present only when above 0. Nothing in the existing endpoints changed.
  • 2026-10-03: counts.total, headerLine (deal-set result) and dealerCount (deal-set entry) are now integer | null, string | null and integer | null. A client must accept null. Null means withheld from an AI assistant acting for the buyer (the three fields state how many dealerships the search found, which is third-party listing data), or unknown; the buyer's own session still always receives a value. The same release withholds, for an AI assistant only, listedAtCents, distanceMiles and quotedTrim on every row, hero.listedAtCents, hero.distanceMiles, hero.trim, ranked[].trim, entry lowestWrittenDistanceMiles, and car.trim, exteriorColor, interiorColor, drivetrain and powertrain, and search.radiusMiles (those fields were already nullable). The OpenAPI document marks every such field x-ridekick-withheld-from-agent.
  • 2026-10-02: canChoose (on a deal-set result row, and on its hero) changed meaning: it now means the row has been unlocked (the dealer is no longer hidden), is not yet chosen, carries a written price for the same car, and is not archived. In the prior version it meant the dealer was still hidden on such a row; that condition is now its own field, canUnlock, which is true while hidden and false once unlocked (or chosen). No deprecation window: v1 accepts only the buyer's own signed-in session, so our own website was the only client reading the old meaning, and it moved to the new one in the same deploy as this version.

Headers on every answer

HeaderValue
Cache-Controlprivate, no-store
Ridekick-API-Version2026-10-13

Last updated