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
labelis no longer "Dealer N". Where the dealership is not named,labelis 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 forchosenLabels,dealerLabel, the offer sheet'slabeland the unlock answer'sdealer.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 byrowKey. 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.trimand the single check'scar.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) andlistPriceUsd(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/requestsand its preview are planned, not yet open to developers; this entry says what holds once they are. Every send ofPOST /api/v1/requestsnow needs thepreviewTokenthatPOST /api/v1/requests/previewreturned for the same request and the sameIdempotency-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 withpreview_required(428) and nothing is sent; apreviewTokenthat 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 andIdempotency-Keyas a send, writes nothing and contacts nobody, and answers the request as sent,upTo(thelimitthe caller sent, never a count we found), the next steps and apreviewTokenvalid for 15 minutes.POST /api/v1/requeststakes an optionalpreviewTokenin 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) andpreview_mismatch(409) are new error codes; a request already sent is replayed with no token. The success answer of a send now also carriesnextSteps,requestUrlandaskMe. An account that does not require a preview still sends without a token, and apreviewTokensuch an account sends is still checked: a bad one is refused. Added the error codetest_dealer_not_ready(HTTP 409) forPOST /api/v1/requestsandPOST /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 carriestestMode: true(the field is absent for every other account) and itsnextStepssay 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_sentgains fourdetails.reasonvalues: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 carriesnextOpenAt, 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) andchannel_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 ofconfiguration,radiusMiles,limitandacceptFewer, a body may carrylistingId(an opaque handle for one listed car that the assistant's car search returned; that search is not part of this API),zipand an optionalnote, 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 (400invalid_request). AlistingIdthat 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 422no_request_sentwith the new reasonlisting_unavailable, and nothing is sent. The sameIdempotency-Keywith a differentlistingIdis a 409idempotency_conflict, never a replay. This is additive: a body withconfigurationworks exactly as before. - 2026-10-08: Added the
no_request_sentreasoncap_reachedforPOST /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-setsnow means the buyer's ACTIVE requests, andRidekick-Moreistrueon 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 withsince=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 withsince=1970-01-01T00:00:00Z. - 2026-10-06: The list
GET /api/v1/deal-setsnow ALSO holds requests to one dealership, and every entry carries akind:deal_setorsingle. Its meaning changed: it is every request the buyer's requests page shows, not only grid sets. A single entry'sidis a price-check id, not a deal-set id, andGET /api/v1/deal-sets/{id}does not accept it. A client MUST skip an entry whosekindit does not know. Every successful list answer carries the headerRidekick-More(truewhen more entries exist beyond this response;truewhen that cannot be told). To read everything, call withsince=1970-01-01T00:00:00Z, then repeat withsinceset to theRidekick-As-Ofyou got, untilRidekick-Moreisfalse. An entry can appear on more than one page: keep one entry per id andkind. 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-setstakes an optionalsincequery 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. Withsince, the answer carries aRidekick-As-Ofheader, the time to pass as the nextsince; it is set slightly before the read began, so an entry can be returned twice but is never skipped. Asincethat is not a valid time is a400invalid_request, never an empty list. Withoutsincethe answer is exactly what it was. The MCP tooldeal_sets_listtakes the same optionalsinceand returns the header asasOf. Additive: no version bump. - 2026-10-03:
distanceMiles(on a deal-set result row and on its hero) andlowestWrittenDistanceMiles(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 newdistanceBand(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,distanceBandandlowestWrittenDistanceMiles) 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 Nin every field that labels a dealer:labelon rows, the hero and the questions and drafts that need the buyer, the questionheading(rebuilt from its category with that label), andchosenLabels. 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 oflabelandchosenLabelschanged. - 2026-10-03:
POST /api/v1/requests(planned, not yet callable): if a request fails with a 500internaland nothing was sent to any dealership, send the request again with the sameIdempotency-Keyand the same body and it runs again, each time it fails that way (two concurrent retries: one runs, the other getsidempotency_in_progress). A different body with the same key is stillidempotency_conflict. If the request was already answered (a success, ano_request_sentrefusal, orcontact_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) forPOST /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.reasonis one ofalready_asked_today,already_requested,no_eligible_dealer,not_available_for_this_account; it is the onlydetailsfield that is a string, and it comes from this closed set. Until now these cases answered a false 500internal. 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 therequests:submitscope, with a requiredIdempotency-Keyheader. 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_interruptedandnote_refused. The request body can also carry an optionalnote(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 codenote_refusedand nothing is sent when it does not, and it never says why). The success answer can also carryskipped: 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) anddealerCount(deal-set entry) are nowinteger | null,string | nullandinteger | 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,distanceMilesandquotedTrimon every row,hero.listedAtCents,hero.distanceMiles,hero.trim,ranked[].trim, entrylowestWrittenDistanceMiles, andcar.trim,exteriorColor,interiorColor,drivetrainandpowertrain, andsearch.radiusMiles(those fields were already nullable). The OpenAPI document marks every such fieldx-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
| Header | Value |
|---|---|
Cache-Control | private, no-store |
Ridekick-API-Version | 2026-10-13 |
Last updated