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
| Olukord | Vastus |
|---|---|
| Võti puudub, on vale või tühistatud | 401 |
| Firma Objektori tellimus on peatatud | 402 |
| Üle 600 päringu tunnis sama võtmega | 429 |
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
| Kood | Tähendus |
|---|---|
400 | Vigane sisend: puuduv väli, vale staatus, vigane kuupäev või id, võõrasse firmasse viitav id |
401 | Võti puudub, on vale või tühistatud |
402 | Firma tellimus on peatatud |
404 | Kirjet ei leitud, ka siis kui see kuulub teisele firmale |
405 | Meetod pole sellel ressursil lubatud |
409 | Kirje on juba olemas |
413 | Fail liiga suur, foto üle 15 MB |
429 | Päringulimiit täis |
500 | Serveri 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äli | Tüüp | Kohustuslik |
|---|---|---|
name | string | jah |
reg_nr, address, contact_name, email, phone | string | ei |
client_type | eraisik või ettevote | ei, vaikimisi ettevote |
contacts | massiiv, kuni 50 | ei |
{
"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äli | Tüüp | Kohustuslik |
|---|---|---|
name | string | jah |
status | staatus | jah |
address, contract_number, description | string | ei |
client_id, project_manager_id | uuid, sinu firmast | ei |
start_date, end_date | YYYY-MM-DD | ei |
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äli | Tüüp | Kohustuslik |
|---|---|---|
name | string | jah |
project_id | uuid, sinu firmast | jah |
status | staatus | ei, vaikimisi uus |
priority | madal, korge, korgeim | ei, vaikimisi korge |
location, description | string | ei |
start_datetime, end_datetime | ISO 8601, nt 2026-06-15T09:00:00+03:00 | ei |
template_id | uuid, töömall | ei |
assignee_ids | uuid massiiv, sinu firma kasutajad | ei |
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äli | Tüüp | Kohustuslik |
|---|---|---|
name | string | jah |
code | sinu oma kood (SKU) | ei, aga soovitatav |
unit, description | string | ei |
price | number | ei |
type | material, labor, custom | ei |
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äli | Tüüp | Kohustuslik |
|---|---|---|
client_id, recipient_client_id, project_id | uuid, sinu firmast | ei |
date | YYYY-MM-DD | ei, vaikimisi täna |
due_date | YYYY-MM-DD | ei, vaikimisi 14 päeva |
email_to, notes, reference_number | string | ei |
items | massiiv, kuni 200 rida | ei |
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äli | Sisu |
|---|---|
| päis | invoice_number, date, due_date, delivery_date, status, paid_amount, reference_number, notes |
client | maksja: nimi, registrikood, aadress, e-post, telefon |
recipient | saaja, kui erineb maksjast |
client_contact, recipient_contact | kontaktisikud |
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_number | projekti 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 saa | Selgitus |
|---|---|
| Kustutada | Kustutamise otspunkte ei ole. Ainus erand on töö teostajate nimekirja asendamine. |
| Saata pakkumist või arvet kliendile | API teeb mustandi, saatmine käib Objektori sees. |
| Kinnitada pakkumist | Kinnitamine loob lao broneeringud ja muudab projekti seisu. |
| Muuta saadetud pakkumist või makstud arvet | Mustand lukustub, edasine käib Objektoris. |
| Lugeda tööaega, palku, kulusid ja ladu | Need ei ole avalikus API-s. |
| Hallata kasutajaid ega firma seadeid | Kä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
| Reegel | Selgitus |
|---|---|
| Võti serveri poolel | Ära pane võtit brauserisse ega avalikku koodi |
| Üks võti, üks firma | Võõra kirje id annab 404, võõras viide 400 |
id käib URL-i | PATCH /works/<id>, mitte id kehas |
| Staatused on fikseeritud | Vale väärtus annab 400 |
| Kuupäevad | Projektil YYYY-MM-DD, töö ajatemplitel ISO 8601 koos ajavööndiga |
| Lehitsemine | Suurte loendite puhul kasuta limit ja offset, jälgi meta.has_more |
| Päringulimiit | 600 päringut tunnis võtme kohta |
| Korduskäivitus | API 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.