Objektor - ehitusfirma juhtimise tarkvara

Arendajale

Objektori avalik API

REST-API, millega ühendad Objektori oma süsteemidega: veebivorm loob projekti, välissüsteem saadab tööd, raamatupidamine loeb arveid ja märgib makseid. JSON üle HTTPS, autentimine API-võtmega.

Base URL https://app.objektor.ee/api/v1 Autentimine x-api-key Uuendatud 20.09.2026

1. Alustamine

API-võtme väljastab Objektori haldaja (Voogcraft). Firma ise võtit luua ei saa. Üks võti kuulub ühele firmale ja näeb ainult selle firma andmeid. Võtme küsimiseks kirjuta karl@voogcraft.ee.

curl https://app.objektor.ee/api/v1/projects \
  -H "x-api-key: obj_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

2. Autentimine

Iga päring saadab võtme päises:

x-api-key: obj_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
OlukordVastus
Võti puudub, on vale või tühistatud401
Firma Objektori tellimus on peatatud402
Üle 600 päringu tunnis sama võtmega429
Hoia võti serveri poolel, keskkonnamuutujas. Ära pane seda brauserisse, mobiiliäppi ega avalikku koodihoidlasse. Kahtluse korral lase võti tühistada ja uus väljastada.

3. Vastused ja vead

Loend tagastab data massiivi ja meta lehitsemise infoga:

{
  "data": [ { "id": "...", "name": "..." } ],
  "meta": { "count": 2, "limit": 500, "offset": 0, "has_more": false }
}

Üksik kirje (loomine, muutmine, GET /<resource>/<id>) tagastab objekti:

{ "data": { "id": "...", "name": "..." } }

Viga tuleb alati sama kujuga. Kui andmebaas ütleb välja, milline väli oli vale, on kaasas ka field:

{ "error": "Vigane kuupäev (oodatud kuju YYYY-MM-DD)", "code": "22007" }

{ "error": "Kohustuslik väli on täitmata", "code": "23502", "field": "name" }

Lehitsemine

Kõik loendid toetavad ?limit= (vaikimisi 500, arvetel 200, maksimum 1000) ja ?offset=. Kui meta.has_more on true, tõsta offset sama palju kui limit ja küsi uuesti.

GET /api/v1/works?limit=200&offset=200

Veakoodid

KoodTähendus
400Vigane sisend: puuduv väli, vale staatus, vigane kuupäev või id, võõrasse firmasse viitav id
401Võti puudub, on vale või tühistatud
402Firma tellimus on peatatud
404Kirjet ei leitud, ka siis kui see kuulub teisele firmale
405Meetod pole sellel ressursil lubatud
409Kirje on juba olemas
413Fail liiga suur, foto üle 15 MB
429Päringulimiit täis
500Serveri viga

4. Kliendid

GET /api/v1/clients loend, GET /api/v1/clients/<id> üks klient, POST /api/v1/clients uus klient.

Väljad: id, name, reg_nr, address, contact_name, email, phone, client_type, created_at. Lisa ?expand=contacts, et saada kaasa kontaktisikud.

Otsing: ?reg_nr=10647027 (täpne) või ?search=nordecon (nimi, registrikood või e-post). Kasuta seda enne loomist, et kontrollida, kas klient on juba olemas.

VäliTüüpKohustuslik
namestringjah
reg_nr, address, contact_name, email, phonestringei
client_typeeraisik või ettevoteei, vaikimisi ettevote
contactsmassiiv, kuni 50ei
{
  "name": "Nordecon betoon OÜ",
  "reg_nr": "10647027",
  "client_type": "ettevote",
  "contacts": [
    { "name": "Taavo Land", "phone": "+372 5854 8330" },
    { "name": "Jaanus Kase", "phone": "56213698" }
  ]
}

Kui ükski kontakt pole märgitud peamiseks (is_primary), saab selleks esimene. Klienti API kaudu muuta ega kustutada ei saa.

5. Projektid

GET /api/v1/projects loend (filter ?status=, laiendus ?expand=client), GET /api/v1/projects/<id> üks projekt, POST uus, PATCH /api/v1/projects/<id> muutmine.

VäliTüüpKohustuslik
namestringjah
statusstaatusjah
address, contract_number, descriptionstringei
client_id, project_manager_iduuid, sinu firmastei
start_date, end_dateYYYY-MM-DDei

Staatused: hinnapakkumises, ettevalmistuses, ootel, toos, tehtud, edasi_lukatud, tuhistatud

6. Tööd

GET /api/v1/works loend (filtrid ?project_id=, ?status=, laiendus ?expand=assignees), GET /api/v1/works/<id> üks töö, POST uus, PATCH /api/v1/works/<id> muutmine.

VäliTüüpKohustuslik
namestringjah
project_iduuid, sinu firmastjah
statusstaatusei, vaikimisi uus
prioritymadal, korge, korgeimei, vaikimisi korge
location, descriptionstringei
start_datetime, end_datetimeISO 8601, nt 2026-06-15T09:00:00+03:00ei
template_iduuid, töömallei
assignee_idsuuid massiiv, sinu firma kasutajadei

Staatused: uus, ootel, vaja_teha, teha, toos, tehtud, tuhistatud

Staatust arveldatud API kaudu määrata ei saa, see tekib ainult töö sidumisel arvega. assignee_ids asendab kogu teostajate nimekirja; võõra firma kasutajad jäetakse välja. Projekti vahetada ei saa.

Töö oma väljad

PUT /api/v1/works/<id>/fields määrab töö enda väljad (näiteks mõõdud või kontrollpunktid) ja nende väärtused. Asendab varasemad väljad.

{
  "fields": [
    { "label": "Mõõt", "type": "number" },
    { "label": "Märkus", "type": "text", "required": true },
    { "label": "Seisukord", "type": "select", "options": ["korras", "vigane"] }
  ],
  "values": [
    { "key": "Mõõt", "value": 42 },
    { "key": "Märkus", "value": "Kontrollitud" }
  ]
}

Tüübid: text, number, date, select, checkbox, photo, signature. Kuni 100 välja ja 300 väärtust korraga.

Töö foto

POST /api/v1/works/<id>/photos

{ "base64": "<pildi sisu base64-na>", "field_key": "Märkus" }

Lubatud on ainult päris pildifail (jpg, png, gif, webp) ja tüüpi kontrollitakse faili sisu, mitte nime järgi. Maksimum 15 MB, üks foto korraga. Kui field_key puudub, läheb foto töö üldfotode alla.

7. Töömallid

GET /api/v1/templates, GET /api/v1/templates/<id>, POST /api/v1/templates. Mall sisaldab nime, kirjeldust, prioriteeti ja väljade kirjeldusi (sama kuju mis punktis 6). Malli saab seejärel kasutada töö loomisel (template_id).

8. Artiklid

Artiklite kataloog, millest pakkumise ja arve read võetakse. GET /api/v1/articles loend (otsing ?code=BET-100 või ?search=betoon), GET /api/v1/articles/<id>, POST /api/v1/articles.

VäliTüüpKohustuslik
namestringjah
codesinu oma kood (SKU)ei, aga soovitatav
unit, descriptionstringei
pricenumberei
typematerial, labor, customei
Kood on artikli tunnus. Kui saadad koodi, mis on juba olemas, uut artiklit ei looda: vastuseks tuleb 200 ja olemasolev artikkel koos väljaga "existing": true. Nii võib sama päringut julgelt korrata. Nime järgi artikleid kokku ei viida.

Piirid: ühe pakkumise või arve päringuga saab kataloogi lisada kuni 50 uut artiklit ja ühe firma kohta kuni 500 uut artiklit päevas. Piiri ületamisel tuleb 400 ja selle päringuga loodud artiklid võetakse tagasi, nii et kataloogi poolikut tulemust ei jää. Piir on selleks, et vigane või tsüklisse läinud integratsioon ei täidaks kataloogi.

9. Pakkumised

API loob ja muudab ainult mustandit (koostamisel). Kinnitamine, saatmine kliendile ja lao broneeringud käivad Objektori sees.

GET /api/v1/quotes loend (filtrid ?status=, ?client_id=, ?since=), GET /api/v1/quotes/<id> koos ridadega, POST uus mustand, PATCH /api/v1/quotes/<id> muutmine.

VäliTüüpKohustuslik
client_id, recipient_client_id, project_iduuid, sinu firmastei
dateYYYY-MM-DDei, vaikimisi täna
due_dateYYYY-MM-DDei, vaikimisi 14 päeva
email_to, notes, reference_numberstringei
itemsmassiiv, kuni 200 ridaei

Number tuleb serverist, sama loogikaga mis rakenduses. Muutmine toimib ainult seni, kuni pakkumine on koostamisel; muidu tuleb 409. Kui saadad items, asendatakse kõik read.

Ridade kuju (kehtib nii pakkumisel kui arvel)

// 1. Olemasolev artikkel koodi järgi (nimi, ühik ja hind tulevad kataloogist)
{ "code": "BET-100", "quantity": 12 }

// 2. Vaba rida, mis kataloogi ei puuduta
{ "name": "Transport", "price": 120, "quantity": 1 }

// 3. Uus artikkel: lisatakse kataloogi ja seotakse reaga
{ "name": "Eritellimus raam", "code": "RAAM-9", "price": 89, "unit": "tk", "quantity": 2, "create_article": true }

Rea väljad: type, name, description, code, quantity, unit, price, margin_percent, discount_percent, tax_rate, reverse_vat. Käibemaksumäär tuleb vaikimisi firma seadetest. Summasid ei saadeta, need arvutab Objektor ise. Vastuses on created_articles, kus on kõik selle päringuga kataloogi lisatud artiklid.

10. Arved

Arve saab luua ja muuta ainult mustandina (maksmata): POST /api/v1/invoices ja PATCH /api/v1/invoices/<id>. Väljad on samad mis pakkumisel, read sama kujuga. Arve number tuleb alati serverist, staatus on alati maksmata ja e-kirja ei saadeta. Muutmine toimib ainult siis, kui arve on maksmata ja tehtud otse (mitte pakkumisest); muidu tuleb 409.

GET /api/v1/invoices loend, filtrid ?status= (maksmata, osaliselt_makstud, makstud, tuhistatud), ?since=YYYY-MM-DD, ?limit=, ?offset=.

GET /api/v1/invoices/<id> annab terve arve:

VäliSisu
päisinvoice_number, date, due_date, delivery_date, status, paid_amount, reference_number, notes
clientmaksja: nimi, registrikood, aadress, e-post, telefon
recipientsaaja, kui erineb maksjast
client_contact, recipient_contactkontaktisikud
items[]read: code, type, name, description, quantity, unit, price, margin_percent, discount_percent, tax_rate, reverse_vat, sort_order ja performer (rea teostaja nimi)
contract_numberprojekti lepingu number
assignees[]arvega seotud tööde teostajate nimed

11. Makse märkimine

Need kaks otspunkti ei ole /api/v1 all, aga kasutavad sama võtit.

# Täismakse
POST https://app.objektor.ee/.netlify/functions/mark-invoice-paid
{ "invoiceId": "<arve uuid>" }

# Osaline makse
POST https://app.objektor.ee/.netlify/functions/mark-invoice-payment
{ "invoiceId": "<arve uuid>", "paidAmount": 250.00, "total": 1000.00 }

Mõlemad on idempotentsed: juba makstud arvet uuesti ei avata ja tühistatut ei puututa. Vastus on alati 200 koos väljaga updated: true/false. false tähendab, et arve oli juba selles seisus, oli tühistatud või ei kuulu sinu firmale.

12. Näited

Veebivorm loob projekti ja esimese töö

const API = 'https://app.objektor.ee/api/v1'
const headers = { 'x-api-key': process.env.OBJEKTOR_API_KEY, 'Content-Type': 'application/json' }

// 1. Klient
const c = await (await fetch(`${API}/clients`, {
  method: 'POST', headers,
  body: JSON.stringify({ name: 'Tellija OÜ', email: 'info@tellija.ee' }),
})).json()

// 2. Projekt
const p = await (await fetch(`${API}/projects`, {
  method: 'POST', headers,
  body: JSON.stringify({ name: 'Jakobsoni 12', status: 'ettevalmistuses', client_id: c.data.id }),
})).json()

// 3. Töö projekti alla
await fetch(`${API}/works`, {
  method: 'POST', headers,
  body: JSON.stringify({ name: 'Ülevaatus', project_id: p.data.id, priority: 'korge' }),
})

Tööde seisu lugemine, kõik lehed

let offset = 0, koik = []
for (;;) {
  const r = await (await fetch(`${API}/works?status=tehtud&limit=500&offset=${offset}`, { headers })).json()
  koik.push(...r.data)
  if (!r.meta.has_more) break
  offset += 500
}

Pakkumise mustand oma hinnakirjast

// 1. Kas klient on juba olemas?
const otsing = await (await fetch(`${API}/clients?reg_nr=10647027`, { headers })).json()
const klient = otsing.data[0] ?? (await (await fetch(`${API}/clients`, {
  method: 'POST', headers, body: JSON.stringify({ name: 'Nordecon betoon OÜ', reg_nr: '10647027' }),
})).json()).data

// 2. Pakkumine: read kataloogist, üks uus artikkel, üks vaba rida
const p = await (await fetch(`${API}/quotes`, {
  method: 'POST', headers,
  body: JSON.stringify({
    client_id: klient.id,
    due_date: '2026-10-15',
    items: [
      { code: 'BET-100', quantity: 12 },
      { name: 'Eritellimus raam', code: 'RAAM-9', price: 89, unit: 'tk', quantity: 2, create_article: true },
      { name: 'Transport', price: 120, quantity: 1 },
    ],
  }),
})).json()

console.log(p.data.quote_number, p.created_articles)

Arvete sünkroonimine raamatupidamisse

# 1. Mis on uut alates kuupäevast
curl -s "https://app.objektor.ee/api/v1/invoices?since=2026-09-01" \
  -H "x-api-key: $OBJEKTOR_API_KEY"

# 2. Üks arve täies mahus
curl -s "https://app.objektor.ee/api/v1/invoices/<id>" \
  -H "x-api-key: $OBJEKTOR_API_KEY"

# 3. Kui makse laekus
curl -X POST "https://app.objektor.ee/.netlify/functions/mark-invoice-paid" \
  -H "x-api-key: $OBJEKTOR_API_KEY" -H "Content-Type: application/json" \
  -d '{"invoiceId":"<id>"}'

Töö märkimine tehtuks

curl -X PATCH "https://app.objektor.ee/api/v1/works/<id>" \
  -H "x-api-key: $OBJEKTOR_API_KEY" -H "Content-Type: application/json" \
  -d '{"status":"tehtud"}'

13. Mida API ei tee

Ei saaSelgitus
KustutadaKustutamise otspunkte ei ole. Ainus erand on töö teostajate nimekirja asendamine.
Saata pakkumist või arvet kliendileAPI teeb mustandi, saatmine käib Objektori sees.
Kinnitada pakkumistKinnitamine loob lao broneeringud ja muudab projekti seisu.
Muuta saadetud pakkumist või makstud arvetMustand lukustub, edasine käib Objektoris.
Lugeda tööaega, palku, kulusid ja laduNeed ei ole avalikus API-s.
Hallata kasutajaid ega firma seadeidKäib ainult Objektori sees.
Laadida üles dokumenteÜles saab laadida ainult pilte töö juurde.

Kui vajad midagi, mida siin ei ole, kirjuta karl@voogcraft.ee. Liidestusi ja erifunktsioone ehitame tellimusel.

14. Reeglid ja lõksud

ReegelSelgitus
Võti serveri poolelÄra pane võtit brauserisse ega avalikku koodi
Üks võti, üks firmaVõõra kirje id annab 404, võõras viide 400
id käib URL-iPATCH /works/<id>, mitte id kehas
Staatused on fikseeritudVale väärtus annab 400
KuupäevadProjektil YYYY-MM-DD, töö ajatemplitel ISO 8601 koos ajavööndiga
LehitsemineSuurte loendite puhul kasuta limit ja offset, jälgi meta.has_more
Päringulimiit600 päringut tunnis võtme kohta
KorduskäivitusAPI ei tuvasta dublikaate nime järgi, hoia oma pool viidet loodud id-le

Küsimused ja võtme tellimine

Kirjuta karl@voogcraft.ee või helista +372 5668 3177. Vastame ka integratsiooni ehitamise küsimustele.