ClearCodesv1

Bouwcodes uit een foto, die kloppen

Stuur een foto en u krijgt de STABU-, NL/SfB-, RAW- en NAA.K.T.-code terug die erbij hoort. Staat er iets bij dat niet uit de foto volgt, dan zeggen we dat erbij in plaats van het te gokken. Eén tarief per foto, ongeacht hoeveel objecten we erop herkennen.

curl
curl -X POST https://api.clearcodes.nl/v1/analyse \
  -H "Authorization: Bearer ck_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "fotoBase64": "<jpeg in base64>", "mimeType": "image/jpeg" }'

Hoe zeker is het antwoord

De code moet kloppen, u neemt hem over in uw bestek. Nagemeten op negenennegentig foto’s door een bestekschrijver: in 95% van de gevallen zit de juiste code in wat ClearCodes teruggeeft, en klopt het STABU-hoofdstuk.

Daarom staat het hoofdstuk altijd los in het antwoord (stabuHoofdstuk), en zegt zeker of de paragraaf uit de fóto volgt of alleen uit de catalogus. Verwerkt u liever alleen zekerheden, dan heeft u aan die twee velden genoeg. Bij twijfel krijgt u er een advies bij: één zin over wat wél vaststaat en wat een tweede foto zou oplossen.

En de ondergrens: het model mag alleen een id uit uw catalogus kiezen, en alles wat er niet in staat gooien we weg en geven we apart terug in afgewezen. Die controle staat buiten de AI-koppeling, dus er komt nooit een verzonnen code uit, welk model er ook onder draait.

Aan de slag

Van niets naar uw eerste antwoord. Er valt niets te installeren, het is één HTTP-aanroep, dus uw bestaande client is genoeg.

Vraag een sleutel aan

Mail hallo@clearline.nl. U krijgt een sleutel die begint met ck_live_, en 30 gratis foto's per maand om te kijken of het werkt.

Zet hem op uw server, nooit in een browser of mobiele app. Wie de sleutel heeft, scant op uw rekening. Bewaar hem als omgevingsvariabele en niet in uw broncode.

Doe één aanroep met curl

Eerst zien dat de sleutel werkt, dan pas code schrijven. Dit stuurt een foto en geeft de codes terug.

bash
curl -X POST https://api.clearcodes.nl/v1/analyse \
  -H "Authorization: Bearer $CLEARCODES_SLEUTEL" \
  -H "Content-Type: application/json" \
  -d "{\"fotoBase64\": \"$(base64 -w0 deur.jpg)\", \"mimeType\": \"image/jpeg\"}"

Op macOS heet het base64 -i deur.jpg zonder -w0. Krijg u een 401, dan komt de sleutel niet aan; een 422 betekent dat wat u stuurde geen afbeelding is.

Krijgt uw foto naar base64

Dit is de stap waar de meeste tijd in gaat zitten. Stuur de inhoud van het bestand, base64-gecodeerd. Een data:-voorvoegsel mag blijven staan, dat halen we eraf.

javascript
import { readFile } from "node:fs/promises";

const foto = await readFile("deur.jpg");

const res = await fetch("https://api.clearcodes.nl/v1/analyse", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLEARCODES_SLEUTEL}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    fotoBase64: foto.toString("base64"),
    mimeType: "image/jpeg",
  }),
});

const uitslag = await res.json();
python
import base64, os, requests

with open("deur.jpg", "rb") as f:
    foto = base64.b64encode(f.read()).decode()

res = requests.post(
    "https://api.clearcodes.nl/v1/analyse",
    headers={"Authorization": f"Bearer {os.environ['CLEARCODES_SLEUTEL']}"},
    json={"fotoBase64": foto, "mimeType": "image/jpeg"},
)

uitslag = res.json()

Ondersteund: JPEG, PNG, WebP en GIF, tot 10 MB. We kijken naar de bytes en niet naar het label, dus een PNG die zich als JPEG voordoet krijgt een 422 in plaats van een vaag antwoord van het model.

Lees het antwoord uit

Wilt u één regel voorstellen aan uw gebruiker, neem dan de kandidaat met hoofdonderwerp: true. Dat is waar de foto volgens het model over gaat, en het is altijd de eerste in de lijst.

javascript
const onderwerp = uitslag.kandidaten.find((k) => k.hoofdonderwerp);

console.log(onderwerp.item.naam);          // "Binnendeur"
console.log(onderwerp.codes.stabu);        // "30.33"
console.log(onderwerp.codes.stabuOmschrijving); // "DEUREN"

Sorteer de lijst niet op zekerheid. Dat getal zegt “dit staat er echt”, niet “dit bedoelde u”, een vloer staat met 95% zekerheid op een foto van een deur. De volgorde die u krijgt is het antwoord van het model op de vraag waar de fotograaf op richtte. De volledige vorm staat hieronder bij Het antwoord.

Vang de fouten af die er echt zijn

Elke fout draagt een code die u kunt uitlezen, en een verzoekId dat u bij een supportvraag meestuurt. Drie zijn het belangrijkst: 402 (proef op of factuur open), 429 (te snel achter elkaar, wacht en probeer opnieuw) en 502 (de modelaanbieder ligt eruit, die aanroep kost u niets). De hele lijst staat bij Foutafhandeling.

Een lege kandidaten-lijst is geen fout. Dat betekent: er staat niets op deze foto dat in uw catalogus voorkomt. Toon dat als een antwoord, niet als een storing.

Van proef naar abonnement

Na 30 gratis foto's in een maand krijgt u een 402 met niet_betaald. Roep dan POST /v1/stripe/checkout aan; die geeft een betaallink terug. Daarna loopt het door en factureren we per maand op basis van wat u gescand hebt.

Uw eigen stand ziet u altijd op GET /v1/gebruik.

Het antwoord

Eén object per herkend item, met de codes al samengesteld. kader is de uitsnede op de foto in fracties van 0 tot 1, en is null als het model niet kon aanwijzen waar het object staat.

json
{
  "verzoekId": "req_gj5nztuc81",
  "duurMs": 1449,
  "domein": "woningbouw",
  "hint": null,
  "regelsInLijst": 187,
  "kandidaten": [
    {
      "catalogId": "binnendeur",
      "label": "houten binnendeur",
      "materiaal": "eiken",
      "zekerheid": 0.9,
      "kader": { "x0": 0.13, "y0": 0.29, "x1": 0.35, "y1": 0.88 },
      "item": { "id": "binnendeur", "naam": "Binnendeur" },
      "codes": {
        "nlSfb": "32.31",
        "stabu": "30.33",
        "stabuHoofdstuk": "30",
        "stabuOmschrijving": "DEUREN",
        "zeker": true,
        "twijfelTussen": [],
        "raw": null,
        "rawOmschrijving": null,
        "naakt": "hout_eiken_element"
      },
      "advies": null
    }
  ],
  "afgewezen": []
}

Nul kandidaten is geen fout. Een foto van een lege muur levert terecht een lege lijst op; dan krijgt u 200 met kandidaten: []. Reserveer uw foutafhandeling voor de statuscodes hieronder.

domein is het domein waarop is gefilterd (null als er niet is gefilterd) en regelsInLijst is hoeveel regels van uw catalogus in de prompt zijn meegestuurd, zo ziet u meteen wat een aanroep gekost heeft.

stabuOmschrijving en rawOmschrijving zijn de tekst bij de code, zodat u 30.33. DEUREN kunt tonen zonder zelf een codelijst te hoeven bijhouden. Ze zijn null bij een code die wij niet kennen, bijvoorbeeld een code uit uw eigen lijst. raw hoort binnen een gebouw leeg te zijn: RAW is een infrabestek en vullen we alleen waar het klopt.

zeker zegt of u de paragraaf mag geloven. Staat er false, dan is de paragraaf niet uit deze foto af te leiden, een losse deur is niet te zien als binnen- of buitendeur. U krijgt dantwijfelTussen met de codes waar het tussen ging, en advies in gewone taal: laten nakijken, of een tweede foto van iets specifieks. Het hoofdstuk staat altijd los in stabuHoofdstuk en klopt aantoonbaar veel vaker dan de paragraaf, dus verwerkt u alleen zekerheden, gebruik dan dat veld.

Wat het kost

€ 0,65 per gescande foto. Niet per aanroep en niet per herkend object: een foto waarop we drie dingen herkennen kost hetzelfde als een foto met één. U weet hoeveel foto's u maakt; hoeveel dingen erop staan weet u vooraf niet.

SituatieKost hetWaarom
Foto met één of meer treffers€ 0,65
Foto zonder treffers€ 0,65Gescand is gescand. “Hier staat niets uit uw lijst op” is ook een antwoord.
Opnieuw scannen met een hint€ 0,65Tweede scan, tweede foto.
Modelaanbieder ligt eruit (502)gratisU kreeg niets. Die kosten zijn van ons.
Kapotte of te grote foto (400 / 413 / 422)gratisEr is geen model aan te pas gekomen.
Alle andere endpointsgratisUw lijst lezen, vervangen en uw verbruik opvragen kosten niets.

De eerste 30 foto's per maand zijn gratis, zonder dat u een kaart hoeft achter te laten. Daarna een 402 met een link naar de checkout. Er is geen opzegtermijn en geen minimumafname: scan u een maand niets, dan betaalt u niets.

Een eigen codelijst

U hebt er geen nodig: zonder eigen lijst draai u op onze meegeleverde lijst. Upload u die van uzelf, dan antwoordt de API met uw codes en gaat uw lijst vóór de onze. Uw lijst is van u en komt nooit bij een andere klant terecht.

curl
curl -X PUT https://api.clearcodes.nl/v1/catalogus \
  -H "Authorization: Bearer ck_live_..." \
  -H "Content-Type: text/csv" \
  --data-binary @mijn-lijst.csv

Koppen: id, naam, synoniemen, nl_sfb, stabu, raw, naakt_toepassing. Alleen naam is verplicht; volgorde, hoofdletters en extra kolommen maken niet uit. Een upload vervangt de hele lijst.

Eén verschil tussen de twee lijsten: GET /v1/catalogus geeft stabu en raw leeg terug zolang u op de meegeleverde lijst draait, dat zijn codes van derden en die geven we niet als lijst weg. U krijgt ze bij de objecten die /v1/analyse op uw foto herkent. Bij een eigen lijst is er niets om weg te laten: dat zijn uw codes.

Endpoints

/v1/analyse en /v1/catalogus accepteren allebei twee optionele velden: domein (bijvoorbeeld "woningbouw") en codesysteem (nlSfb, stabu, raw of naakt). Meegeven zonder domein te zetten op uw account blijft werken zoals nu. /v1/analyse heeft er één die de andere twee niet hebben: hint.

Eén ding om te weten voordat u bouwt. Op GET /v1/catalogus geven codesysteem=stabu en raw een 400 zolang u op de meegeleverde lijst zit: dat endpoint geeft die codes niet terug, dus het filter zou alleen verklappen wélke regels er een hebben. Op /v1/analyse werken ze wel, en met uw eigen geüploade lijst ook hier.

De catalogus gaat bij elke aanroep mee in de prompt aan het model: meer regels betekent meer tokens en een grotere kans dat het model de verkeerde regel kiest. Stuurt u een domein mee, dan ziet het model alleen de regels die daarbij horen, kleinere prompt, lagere kosten, betere keuze. Wilt u dat niet elke aanroep apart doen, zet dan een standaarddomein op de tenant (npm run tenant -- domein <tenantId> <domein>); een veld in het verzoek zelf gaat daar altijd voor.

hint vertelt het model waar de foto voor gemaakt is. Weet de gebruiker dat hij een doucheput fotografeert, stuur dan "hint": "douche" mee. Het model zoekt dat als eerste op en zet het vooraan in het antwoord. Maximaal 100 tekens, meerdere woorden mogen. Het kort de catalogus niet in, u krijgt nog steeds elk ander object dat op de foto staat, en staat het genoemde er niet op, dan wordt het weggelaten in plaats van bevestigd. Een hint kan dus geen verkeerd antwoord afdwingen. Opnieuw scannen met een hint is wel een tweede foto op uw rekening , er gaat een tweede modelaanroep overheen.

MethodePadWaarvoor
POST/v1/analyseFoto erin, gevalideerde codes eruit.€ 0,65
GET/v1/catalogusUw lijst lezen en doorzoeken met ?q=.gratis
PUT/v1/catalogusUw lijst vervangen. Het lichaam is de CSV.gratis
GET/v1/gebruikHoeveel foto's u deze maand hebt gescand.gratis
GET/v1/openapi.jsonHet contract, machineleesbaar.gratis

Foutafhandeling

De statuscode zegt of het gelukt is; u hoeft het lichaam niet te lezen om dat te weten. De code is machineleesbaar en verandert nooit, het bericht is voor mensen en mag wel wijzigen.

json
{ "fout": { "code": "niet_betaald", "bericht": "Er staat een factuur open." } }
400ongeldig_verzoekHet lichaam klopt niet.
401geen_sleutelGeen Authorization-kop meegestuurd.
401ongeldige_sleutelSleutel bestaat niet of is ingetrokken.
402niet_betaaldFactuur open of abonnement gestopt.
404niet_gevondenDat id staat niet in uw catalogus.
413foto_te_grootFoto boven de 10 MB.
422onbruikbare_fotoGeen geldige afbeelding ontvangen.
422geen_catalogusUw lijst is leeg, of uw domein-/codesysteemfilter laat niets over.
429te_veel_verzoekenBoven de limiet per minuut.
502model_onbereikbaarDe modelaanbieder ligt eruit.

Limieten en privacy

  • Elk antwoord draagt X-RateLimit-Remaining. Bij een 429 staat er ook Retry-After bij, in seconden.
  • Foto's tot 10 MB. Verklein ze eerst tot zo'n 1600 pixels, dat scheelt wachttijd en kosten, en een kozijn blijft prima herkenbaar.
  • Wij bewaren uw foto's niet. Analyseren en vergeten. Er is geen archief om te lekken.
  • Aanroepen die mislukken door een storing bij ons tellen niet mee voor uw factuur.