Portal clienți
Endpoint-uri read-only, filtrate pe client, pentru un site terț care își autentifică singur clienții (de exemplu site-ul atelierului) și le afișează vehiculele, istoricul și lucrările în curs din NOTSOWMS. Site-ul se autentifică cu un Bearer token obișnuit (<tenant-slug>.<key>) și cere datele în numele clientului pe care l-a identificat.
Spre deosebire de comenzile de service și devize, răspunsurile de aici sunt deliberat subțiri: fără note interne, fără token-uri, fără id-uri de mecanic, fără liniile de cost ale atelierului. Modulul service trebuie să fie activ pe tenant.
Capabilități
Citirea cere service:read. Aprobarea unui deviz folosește endpoint-ul existent POST /api/v1/service/estimates/{estimate}/respond, care cere service:write. O cheie pentru un portal de clienți are deci exact aceste două capabilități.
Endpoint-uri
| Metodă | Cale | Descriere |
|---|---|---|
| GET | /api/v1/service/portal/customers/by-email?email= | Clientul cu acel email (potrivire exactă, fără majuscule). |
| GET | /api/v1/service/portal/customers/{customer}/overview | Clientul, vehiculele lui și toate comenzile care nu sunt ciornă. |
| GET | /api/v1/service/portal/work-orders/{workOrder}?customer_id= | O comandă: cronologie, devizul curent cu linii, poze vizibile. |
Căutare client după email
curl "https://tenant.notsowms.ro/api/v1/service/portal/customers/by-email?email=radu@example.ro" \
-H "Authorization: Bearer tenant.cheie" -H "Accept: application/json"Răspuns 200:
{
"data": {
"id": 12, "first_name": "Radu", "last_name": "Marin", "company_name": null,
"display_name": "Radu Marin", "email": "radu@example.ro", "phone": "0722000111",
"duplicates": 0
}
}Clienții marcați is_blacklisted sau anonimizați nu sunt returnați. duplicates spune câți alți clienți au același email, ca atelierul să-i poată uni. 404 când nu există, 422 fără email.
Prezentare client
curl https://tenant.notsowms.ro/api/v1/service/portal/customers/12/overview \
-H "Authorization: Bearer tenant.cheie" -H "Accept: application/json"Răspuns 200 (prescurtat):
{
"data": {
"customer": { "id": 12, "display_name": "Radu Marin", "email": "radu@example.ro", "phone": "0722000111" },
"vehicles": [
{ "id": 5, "vehicle_kind": "atv", "make": "CFMoto", "model": "CForce 450", "model_year": 2022,
"model_variant": null, "registration_plate": "TM12ABC", "vin": null, "engine_cc": 450,
"current_odometer": 1200, "odometer_unit": "km", "status": "active" }
],
"work_orders": [
{ "id": 142, "number": "WO-2026-000142", "status": "estimate_sent", "status_label": "Deviz trimis",
"vehicle_id": 5, "title": "Ambreiajul patinează", "created_at": "2026-05-25T09:30:00+00:00",
"updated_at": "2026-05-25T11:05:00+00:00", "promise_date": null, "closed_at": null,
"is_awaiting_customer": false,
"status_path": ["received", "diagnosing", "estimate_pending", "estimate_sent"],
"estimate": { "id": 31, "version": 1, "status": "sent", "total_cents": 119000, "currency": "RON" },
"actual_total_cents": null }
]
}
}status_patheste lista cronologică a statusurilor prin care a trecut comanda, ca un consumator să poată așeza o stare laterală (awaiting_customer,cancelled) pe ultimul pas liniar atins.titleeste prima linie dincomplaint(maxim 120 de caractere) sauLucrare WO-…dacă lipsește.estimateeste cel mai nou deviz care nu e ciornă (o versiune amendată dar netrimisă nu apare).actual_total_centsapare doar când comanda ecompleted,invoicedsaupaid.- Comenzile
draftnu sunt listate. Maxim 100, cele mai noi primele.
Detaliu comandă
customer_id este obligatoriu și trebuie să fie proprietarul comenzii, altfel 404. Ciornele dau tot 404.
curl "https://tenant.notsowms.ro/api/v1/service/portal/work-orders/142?customer_id=12" \
-H "Authorization: Bearer tenant.cheie" -H "Accept: application/json"Răspuns 200 (prescurtat): toate câmpurile din rezumatul de mai sus, plus
{
"data": {
"complaint": "Ambreiajul patinează", "diagnosis": "Discuri uzate", "customer_notes": "Vă sunăm când sosesc piesele.",
"vehicle": { "id": 5, "make": "CFMoto", "model": "CForce 450", "registration_plate": "TM12ABC" },
"timeline": [
{ "at": "2026-05-25T09:30:00+00:00", "status": "received", "label": "Primit" },
{ "at": "2026-05-25T11:05:00+00:00", "status": "estimate_sent", "label": "Deviz trimis" }
],
"estimate": {
"id": 31, "version": 1, "status": "sent", "currency": "RON",
"subtotal_cents": 100000, "tax_cents": 19000, "total_cents": 119000, "deposit_required_cents": 0,
"valid_until": "2026-06-01", "customer_message": null, "sent_at": "2026-05-25T11:05:00+00:00", "responded_at": null,
"lines": [
{ "id": 301, "line_type": "part", "description": "Kit ambreiaj", "quantity": "1.000",
"unit_price_cents": 60000, "total_cents": 60000, "tax_cents": 11400, "customer_decision": "pending" },
{ "id": 302, "line_type": "labor", "description": "Manoperă", "quantity": "2.000",
"unit_price_cents": 20000, "total_cents": 40000, "tax_cents": 7600, "customer_decision": "pending" }
]
},
"photos": [
{ "id": 9, "media_type": "photo", "caption": "Disc uzat", "url": "https://…semnat, valabil 1h…", "created_at": "2026-05-25T10:00:00+00:00" }
]
}
}timelineeste în ordine cronologică (cea mai veche prima) și nu conține motivul intern al schimbării.photosconține doar pozele marcate vizibile pentru client; URL-urile sunt semnate și expiră după o oră.- Nu apar niciodată:
internal_notes,tracking_token,approval_token, id-uri de personal,quoted_total_cents, liniile de manoperă / piese / subcontractare ale atelierului.
Aprobarea devizului din portal
Portalul aprobă cu endpoint-ul standard, trimițând doar liniile încă pending:
curl -X POST https://tenant.notsowms.ro/api/v1/service/estimates/31/respond \
-H "Authorization: Bearer tenant.cheie" -H "Content-Type: application/json" \
-d '{"responded_via":"tap_link","decisions":[{"line_id":301,"decision":"approved"},{"line_id":302,"decision":"approved"}]}'Statusul devizului se recalculează din decizii (approved, partially_approved, declined), iar trigger-ul de automatizare estimate_approved se declanșează ca de obicei.