The check, as an endpoint

POST /api/v1/check runs the same evaluator as the web check: answers in, one finding per duty out. Unauthenticated, CORS-open, JSON both ways. Built for scripts and AI agents acting for landlords. The check on the website is just another client of this endpoint.

Verification: each rule is verified on its own, word for word against its legal source, and some rules are not verified yet. "draftRuleSet" is true while any finding in the response comes from a rule that has not finished being verified, and every finding has its own verified flag. Treat a finding with "verified": false as tracking data, not legal fact, and follow each finding’s sourceUrl.

Request

Yes/no fields take "yes" / "no"; most also accept "unsure" or null — an honest “don’t know” comes back as an ask, never silently green. epcRating / epcExpiryDate and the done-ness pair (depositProtected, rtrChecked) are optional and default to null.

curl -s -X POST https://www.datestone.io/api/v1/check \
  -H 'Content-Type: application/json' \
  -d '{
    "postcode": "LS1 3AD",
    "propertyType": "house",
    "hasGasAppliances": "yes",
    "hasTenancy": "yes",
    "letting": "rooms",
    "occupantsCount": 6,
    "householdsCount": 6,
    "tenancyTiming": "both",
    "infoSheetServed": "yes",
    "writtenStatementServed": "unsure",
    "depositTaken": "yes",
    "depositProtected": "not_yet",
    "rtrChecked": "yes",
    "epcRating": null,
    "epcExpiryDate": null
  }'

The fields

The check asks who lives there and how it is let, and works out whether the home is a house in multiple occupation (HMO) from that and the head-count, as the law does. It asks no start date: when the tenancies began is one answer for any number of them.

  • propertyType: house, flat or unsure.
  • hasTenancy: is anyone living there now (yes / no).
  • letting: who lives there now, or for an empty home how it will be let: one_household (a person, a couple or a family on one agreement), sharers (people who aren’t all one family, on one joint agreement), rooms (room by room, each on their own agreement), lodgers (in the home the landlord lives in, sharing their kitchen or bathroom), company (a company that sublets it or puts its staff there) or unsure (“Not decided yet” for an empty home). Lodgers need hasTenancy yes.
  • occupantsCount, householdsCount: whole numbers or null. Not needed for one_household. With lodgers, count the landlord and their family too.
  • peopleBesidesOwner (two_or_fewer, three_or_more or unsure) and ownerLongInterest (the freehold or a lease of more than 21 years): lodgers only.
  • purposeBuiltBlockFlat: a flat in a block built as flats, with 3 or more flats in it.
  • tenancyTiming: before 1 May 2026, after (on or after), both (some of each: only with rooms or unsure) or unsure. both can owe the information sheet and the written statement at once, each by its own answer (infoSheetServed, writtenStatementServed).
  • depositProtected: yes, no, unsure or not_yet: paid in the last 30 days, so still “Needed”, never “Out of date”.
  • A company let has no tenancy rows: the response’s lettingNote says why, and whose right to rent checks they are.

The fields of the check’s earlier questions are still taken, read as they always were: isHmo, propertyType: "hmo", tenancyStartDate and preRra, in a request without letting. A request that mixes the two shapes, or contradicts itself, is refused with a 400 that says which answers clash.

Response

outOfScope — API callers must read this. Non-null (the country name, e.g. "Wales") when the postcode is outside England: findings and the exposure figures are empty because we don’t cover that country yet, NOT because the property is fine.

Each finding’s word is the state, in the product’s one vocabulary, and stateLabel is what a landlord reads for it: need_now (“Out of date”), need (“Due soon” over a date ahead, “Needed” without one: it applies here, and its date or record is not in yet), might_need (“Ask the council”: licensing only the council can confirm), one_answer (“Not known yet”: whether it applies turns on one fact the landlord hasn’t given; never a missing date alone), covered (“In date” over a date ahead, “Done” without one; a duty your answers settled, such as a deposit you say is protected or a right-to-rent check you say was done, reads “Done, based on your answers” whatever date it keeps), watching (“Not law yet”) or good_practice (“Good practice”). The word never moves with the date and the label can, so group by word and print stateLabel. rag and cantTell are kept for API-shape stability. cantTell: true means the answers couldn’t settle the finding; read the state from word. stateNote, when present, is the sentence after the label that says why it reads so: on need without a date, that every rental like this one needs it; on one_answer, the question it turns on. needsDate: true marks a duty that applies whose certificate date the check doesn’t ask for. Both are additive (7 Oct 2026). need_now (“Out of date”) comes only from an answer you sent as "no" (deposit protected, right to rent, the information sheet, the written statement) or from the epcExpiryDate you sent: a date the check works out itself, such as a tenancy’s start, never makes one (7 Oct 2026). paper: true marks a certificate or licence the home holds (additive, 7 Oct 2026).

Money is integer pence. penaltyMaxPence: null means the penalty is unlimited or scales with the case — such findings are counted in unquantifiableCount, never summed as zero. exposureFirstPence is what first breaches of the out-of-date findings can cost (a tiered rule’s first tier); exposureMaxPence sums the higher tiers. Each finding carries both, as penaltyFirstPence and penaltyMaxPence, with its penaltyPhrase.

ruleSetUpdatedAt is when any rule last moved (verified or edited) — refresh cached results when it changes. continueUrl turns this check into a prefilled signup: send a person there and their first property form arrives with these answers already in place.

{
  "version": "v1",
  "draftRuleSet": true,
  "ruleSetUpdatedAt": "2026-08-12T09:30:00.000Z",
  "disclaimer": "Tracking data, not legal advice. …",
  "outOfScope": null,
  "councilName": "Leeds",
  "exposureMaxPence": 9700000,
  "exposureFirstPence": 3700000,
  "unquantifiableCount": 2,
  "findings": [
    {
      "templateKey": "deposit_protection",
      "name": "Deposit protection + prescribed information",
      "rag": "amber",
      "cantTell": true,
      "word": "need",
      "stateLabel": "Needed",
      "stateNote": "Every tenancy like yours needs this.",
      "actionText": "Confirm the deposit is protected in a government-approved scheme…",
      "penaltyText": "Court can order 1–3× the deposit to the tenant…",
      "penaltyMaxPence": null,
      "advisory": false,
      "sourceUrl": "https://www.gov.uk/deposit-protection-schemes-and-landlords",
      "verified": false,
      "aiCheckedAt": "2026-08-10T07:00:00.000Z",
      "lastVerifiedAt": null,
      "dueDateIso": null
    }
  ],
  "lettingNote": null,
  "continueUrl": "https://www.datestone.io/api/v1/continue?token=…"
}

Limits & conduct

  • 20 requests per minute per IP; you’ll get a 429 with Retry-After.
  • A generous daily cap per origin sits behind that — agents checking real properties will never see it; bulk dumps will.
  • No key needed today. If that changes, v1 stays available and documented.
  • Responses are per-request computations — cache on your side if you’re checking the same answers repeatedly, and refresh when ruleSetUpdatedAt moves.

For connected apps

Five calls for an app that manages properties and wants Datestone to track what each one owes the law. They take an API key issued by Datestone (Authorization: Bearer <your key>), and a key only ever reaches the properties in its own account. Datestone takes compliance facts only: no tenant, rent or contact details, ever.

  • POST /api/v1/properties — register a property. Returns its ref and the openQuestions still open. The same address twice returns the same ref and changes nothing; each flat is its own property, whichever address line names it.
  • PATCH /api/v1/properties/{ref} — set or correct the facts. Only the fields you send change; anything else is refused whole.
  • GET /api/v1/properties/{ref}/obligations — what the property owes, one row per duty.
  • POST /api/v1/properties/{ref}/certificates — this got done, here is the date.
  • DELETE /api/v1/properties/{ref}/certificates/{type} — take a filed date back.
curl -s -X POST https://www.datestone.io/api/v1/properties \
  -H 'Authorization: Bearer <your key>' \
  -H 'Content-Type: application/json' \
  -d '{
    "addressLine1": "Leeds Town Hall, The Headrow",
    "postcode": "LS1 3AD",
    "propertyType": "house",
    "bedrooms": 3
  }'

Facts

  • hasGasAppliances: true, false, or null for not known. Leave it out and it is not known — the gas safety duty stays on the property as an open question. Only false switches it off.
  • isHmo: true, false, or null for not known, the same way. Leave it out and the HMO licence rows stay as open questions. A false is refused, on registration and on update, where 3 or more people or 2 or more households live with the other count at the test or missing, or where the home is let room by room or to lodgers and the counts are missing, unless hmoException gives a reason the law states.
  • hmoException: two_people (only two people live there, forming two households) or resident_owner (the owner lives there with members of their household and no more than two other people). Send ownerLongInterest: true with resident_owner: it answers whether the owner holds the freehold or a lease of more than 21 years, the reason is refused without it, and it is never stored. Kept only beside isHmo: false, and cleared when the head-counts change unless it is sent again.
  • lettingStructure: whole, rooms, lodgers (the owner, a person, lives there as their main home and shares a kitchen, bathroom, toilet or living room with them: no deposit protection, Renters’ Rights Act papers, selective licence, electrical safety report or 2015 alarm rules) or company (let to a company that lets the rooms out: no deposit protection or Renters’ Rights Act papers on that agreement). purposeBuiltBlockFlat: whether a flat is in a purpose-built block of 3 or more flats. Both take null for not known.
  • propertyType (house, flat or hmo), bedrooms, occupantsCount, householdsCount.

A home outside England is registered, but Datestone covers England’s rules for now, so it has no obligations. Registration and GET say so in outsideEngland: the country and a note you may print.

A row still waiting on a fact carries question: the fact, the field that answers it, and ask, the question in words. Registration, PATCH and GET also return the property’s openQuestions. Ask your user, then send the answer with PATCH.

detail is a plain sentence about the duty that you may print. legalBasis, when Datestone holds one, names the law behind it. Earlier responses carried that citation in detail, so if you show the law, read it from legalBasis now. status.label follows the date the same way stateLabel does; group by status.word.

{
  "key": "4b0c…",
  "type": "gas_safety_cert",
  "title": "Gas safety certificate (CP12)",
  "detail": "Annual gas safety check (CP12) by a Gas Safe engineer.",
  "legalBasis": "Gas Safety (Installation and Use) Regulations 1998, regulation 36",
  "dueDate": null,
  "confidence": "unknown",
  "status": { "word": "one_answer", "label": "Not known yet", "tone": "ask" },
  "timing": null,
  "dueDateReason": {
    "code": "needs_answer",
    "text": "We can't tell whether this applies until one question is answered: Does it have gas appliances?"
  },
  "question": {
    "fact": "has_gas_appliances",
    "field": "hasGasAppliances",
    "ask": "Does it have gas appliances?"
  }
}

Dates

Every date is a plain date (2027-03-14), in both directions; a timestamp is refused. A null dueDate is never “not required”, and must never be aged from any other date. Whether a row is late is timing’s answer, not the date’s: an energy performance certificate (EPC) rated below E is out of date on its rating while it is valid, and an EPC that has run out is no breach until the home is next marketed, sold or let to a tenancy that began after it. Each row says why, where from, and how it stands:

  • dueDateReason, when there is no date — and on an EPC out of date on its rating: settled, needs_answer, council_to_confirm, fire_service_to_confirm, no_certificate_date, no_epc_date, epc_rating_below_e, no_record_yet, law_sets_no_date or not_dated, with a sentence you may print. On an EPC that has run out: epc_ran_out_while_let, epc_needed_before_letting, epc_let_after_it_ran_out or epc_was_below_e, with its dated sentence; its timing is overdue only for the third, and none otherwise.
  • epcRating, on the EPC row only: the rating (A to G) of the EPC whose date the row carries, when the register says so.
  • dueDateSource, only when there is a date: certificate_on_file, date_entered, epc_register, check_recorded, tenancy_start or fixed_in_law.
  • timing: overdue, due_soon or in_date — Datestone’s reading of the row, independent of the word. An advisory row keeps the word good_practice whatever its timing.
  • lapsedBeforeAdded: true when the date passed before the property was added to Datestone. Say when it ran out, not how long it has been late.

Filing a date

  • A certificate or licence takes expiresDate, and optionally issuedDate.
  • A check — smoke and carbon monoxide alarms, legionella, inspections, inventories — takes issuedDate, the day it was done: not in the future, and not before its tenancy began. An inspection can also carry cadenceMonths, its own cycle. Sent with only expiresDate, a check is recorded as done on the day it first arrives, and the reply says so in note.
  • The EPC takes expiresDate; the later of this and the EPC register counts.
  • A duty whose date comes from somewhere else — deposit protection, right to rent, tenancy papers served — is refused with 422 date_comes_from_elsewhere, never accepted and ignored.

The reply carries the row exactly as the next GET returns it (obligation), and unchanged: true only when every value it wrote was already held. Type keys are Datestone’s own and there are no synonyms: a type this property does not carry comes back 404 type_not_on_property with the property’s validTypes and, where it helps, the reason.

Type keys

typedutyapplies to
additional_licenceAdditional HMO licence (council designation)the property
eicrElectrical safety report (EICR)the property
epc_c_2030Energy standard equivalent to energy performance certificate (EPC) C by 2030: a plan, not law yet (not in force yet)the property
epc_min_eEnergy performance certificate (EPC): valid, rated E or betterthe property
epc_room_letsEPC when you let roomsthe property
fire_risk_assessment_shared_areasFire risk assessment of the shared areasthe property
gas_safety_certGas safety certificate (CP12)the property
hmo_common_partsHMO: shared areas and shared appliances kept safe, clean, lit and in repairthe property
hmo_fire_safety_managementHMO: escape routes clear, fire alarms and equipment working, escape noticesthe property
hmo_licence_alarmsSmoke and carbon monoxide alarms (licence condition)the property
hmo_licence_electricalElectrical installations safe (HMO licence condition)the property
hmo_manager_contact_detailsHMO: your name, address and phone number to every household, and on displaythe property
hmo_waste_binsHMO: enough bins for every householdthe property
landlord_ombudsmanLandlord ombudsman membership (not in force yet)the property
legionella_assessmentLegionella risk assessmentthe property
mandatory_hmo_licenceMandatory HMO licence (5+ people, 2+ households)the property
periodic_inspectionPeriodic inspectionthe property
prs_database_empty_homePrivate rented sector (PRS) database: registering an empty home (not in force yet)the property
prs_database_registrationPrivate rented sector (PRS) database: register yourself and each home (not in force yet)the property
selective_licenceSelective licence (council designation)the property
smoke_co_alarmsSmoke and carbon monoxide alarmsthe property
alarms_working_at_tenancy_startAlarms proven working at check-ineach tenancy
deposit_protectionDeposit protection + prescribed informationeach tenancy
hmo_room_clean_at_startHMO: each room clean when a new occupier moves in, and kept in repaireach tenancy
inventory_checkinCheck-in inventoryeach tenancy
inventory_checkoutCheck-out inventoryeach tenancy
right_to_rentRight to rent checkseach tenancy
rra_info_sheetRenters' Rights Act information sheet (tenancies before 1 May 2026)each tenancy
rra_written_statementRenters' Rights Act written statement of terms (new tenancies)each tenancy