P Programa Taller
ESCAT
▶ Demo 3 mesos per 1 €
API REST v1

Referència de l'API

Tots els endpoints de la versió 1 amb els paràmetres, exemples de petició i resposta, permisos, errors i esdeveniments de webhook.

URL baseCada taller té l'API al seu domini: https://<el-seu-domini>/api/v1. L'URL del teu taller apareix a «Desarrolladores / API»; als exemples fem servir https://el-teu-taller.example.

Autenticació

Envia la clau a la capçalera Authorization: Bearer pt_live_… (o X-Api-Key). Les claus pt_test_… poden llegir, però qualsevol operació amb efectes respon 403 test_key_forbidden.

Convencions

Dates en ISO 8601 UTC; imports en euros amb dos decimals i currency: "EUR"; identificadors enters; noms de camp estables en snake_case. El personal del taller apareix només com a { id, name }.

Paginació

Els llistats retornen { data, next_cursor, has_more }. Demana la pàgina següent repetint la crida amb ?cursor=<next_cursor>. limit va d'1 a 200 (50 per defecte).

Format d'error

{
  "error": {
    "code": "insufficient_scope",
    "message": "La clau API no té el permís «budgets:read»."
  }
}
HTTPCodiMissatge
401missing_api_keyFalta la clau API: envia-la a la capçalera Authorization: Bearer pt_… o X-Api-Key.
401invalid_api_keyLa clau API no és vàlida per a aquesta instància.
401revoked_api_keyLa clau API està revocada.
401expired_api_keyLa clau API ha caducat.
403ip_not_allowedL'adreça IP d'origen no és a la llista permesa d'aquesta clau.
403insufficient_scopeLa clau API no té el permís necessari per a aquesta operació.
403test_key_forbiddenUna clau de prova (pt_test_) no pot fer operacions amb efectes: fes servir una clau live.
429rate_limitedHas superat el límit de peticions d'aquesta clau. Espera i torna-ho a provar.
503instance_rate_limitedLa instància està rebent massa peticions per API en aquest moment. Torna-ho a provar d'aquí a uns segons.
429plan_quota_exceededS'ha esgotat la quota diària de peticions del pla d'API del taller. Es renova a les 00:00 UTC; per a més volum, millora el pla.
403plan_scope_not_allowedEl pla d'API del taller no inclou aquest permís. Per fer-lo servir cal millorar el pla.
400bad_requestLa petició no és vàlida.
404not_foundNo s'ha trobat el recurs.
502upstream_errorUn servei extern ha rebutjat l'operació.
500internal_errorError intern.
422validation_errorEl cos de la petició no és vàlid: revisa la llista «fields».
400idempotency_key_requiredFalta la capçalera Idempotency-Key (obligatòria a tot POST; de 8 a 255 caràcters visibles).
409idempotency_conflictAquesta Idempotency-Key ja s'ha fet servir les últimes 24 h amb una altra petició.
409idempotency_in_progressHi ha una altra petició amb la mateixa Idempotency-Key en curs. Torna-ho a provar d'aquí a uns segons.
409client_existsJa existeix un client amb aquest telèfon, email o NIF/CIF.
409client_erasedEl client va demanar l'esborrat de les seves dades (RGPD): la fitxa no admet canvis ni altes associades.
409vehicle_existsAquest client ja té un vehicle amb aquesta matrícula.
409vehicle_belongs_to_other_clientAquesta matrícula ja està donada d'alta a nom d'un altre client.
409budget_lockedEl pressupost està tancat i ja no admet aquest canvi.
409invalid_status_transitionEl pressupost no pot passar a aquest estat des de l'estat actual.
403status_transition_forbiddenAquest canvi d'estat no està disponible per API.
403client_acceptance_requiredPer acceptar un pressupost fes servir POST /budgets/{id}/accept amb el permís budgets:accept i l'evidència de qui accepta.
403booking_mode_propose_onlyEl taller treballa en mode «proposar cita»: només es poden crear propostes que el taller confirma.
409slot_unavailableAquest forat no està disponible.
413payload_too_largeEl fitxer supera la mida màxima (4 MB).
415unsupported_media_typeTipus de fitxer no admès: només JPEG, PNG, WebP o PDF.
422fiscal_data_incompleteFalten dades fiscals del client per poder facturar: revisa la llista «missing».
409budget_not_invoiceableEl pressupost no es pot facturar en el seu estat actual.
422invoice_rejectedEl programa no ha pogut emetre la factura: revisa el missatge.
422whatsapp_window_closedHan passat més de 24 h des de l'últim missatge del client: per WhatsApp només se li pot escriure amb una plantilla aprovada (template_id).
409channel_unavailableAquest canal no està configurat al taller.
404conversation_not_foundNo existeix aquesta conversa.
404sandbox_onlyNomés disponible a l'entorn de proves (https://demo.programa-taller.es).
403client_key_forbiddenAquesta clau està limitada a un client del taller i no pot fer aquesta operació: només treballa amb les dades del seu client.

Escriptures i idempotència

Tot POST exigeix la capçalera Idempotency-Key (un UUID nou per operació). Repetir la mateixa clau amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos respon 409 idempotency_conflict. Els cossos es validen contra el seu esquema i qualsevol camp desconegut es rebutja: 422 validation_error amb la llista fields (path i message).

Claus de client

Claus de client: el taller pot crear claus limitades a un únic client (per exemple, una flota o una empresa). Amb elles cada llistat retorna només les dades d'aquest client, un id d'un altre client respon 404, les operacions de tot el taller responen 403 client_key_forbidden i els webhooks només reben esdeveniments d'aquest client. GET /ping indica a key.client a quin client està limitada.

invoices:write stock:write settings:write reports:read: permisos que una clau de client no pot tenir.

Guies

Primers passos

El mateix recorregut que el tutorial «Comença amb l'API pas a pas» de Desenvolupadors, per a qui programa. Crea una clau de proves (pt_test_…) amb permisos de només lectura, comprova la connexió, llegeix dades reals i, quan la integració estigui a punt, passa a una clau live amb els permisos justos. Els avisos automàtics (webhooks) i les escriptures requereixen el pla Developer o superior.

1. Crea una clau a «Les meves claus»

Tria l'entorn de proves i només permisos de lectura (per exemple budgets:read). La clau completa es mostra una sola vegada: desa-la en un gestor de contrasenyes i envia-la a la capçalera Authorization: Bearer.

2. Comprova la connexió

GET /api/v1/ping

Respon amb el nom de la clau, els seus permisos i l'entorn. Un 401 vol dir clau no vàlida; un 403, que falta un permís.

3. Llegeix les teves dades reals

GET /api/v1/budgets

?limit=3

Els llistats es paginen per cursor: repeteix la crida amb cursor=<next_cursor> fins que has_more sigui false. Cada resposta porta X-RateLimit-Remaining amb les peticions que queden a la finestra.

4. Rep avisos automàtics

POST /api/v1/webhooks

{ "url": "https://el-teu-sistema.com/avisos-del-taller", "events": ["lead.accepted", "vehicle.ready"] }

També es creen des de la pestanya Webhooks sense programar. Desa el secret de signatura i verifica X-PT-Signature a cada lliurament; respon 2xx en menys de 5 segons.

5. Passa a producció

Abans, si vols, prova escriptures, acceptacions, factures i missatges sense risc a l'entorn de proves (https://demo.programa-taller.es/desarrolladores-sandbox): dades fictícies i res no surt de debò. Crea una clau live amb els permisos mínims i, si vas a escriure dades, afegeix Idempotency-Key a cada POST. Els límits i el pla d'API del taller es veuen a la pestanya Consum; els plans Developer i Business es contracten des del mateix programa.

Guia: crear un pressupost i proposar cita

Flux típic d'un CRM o d'una web de reserves. Totes les peticions POST porten Idempotency-Key (un UUID nou per operació; repeteix-lo només quan reintentis la mateixa). Necessites una clau live amb clients:write, vehicles:write, budgets:write, appointments:read i appointments:write.

1. Client: crear-lo o recuperar l'existent

POST /api/v1/clients

{ "name": "Laura Gómez", "phone": "600111222", "email": "laura@ejemplo.com" }

Amb ?on_conflict=return_existing, si el telèfon, l'email o el NIF ja existeixen reps aquest client (200) en lloc de 409.

2. Vehicle del client

POST /api/v1/vehicles

{ "client_id": 1204, "plate": "1234KLM", "brand": "Seat", "model": "León" }

Si la matrícula és d'un altre client: 409 vehicle_belongs_to_other_client (decideix el taller).

3. Pressupost amb les seves partides

POST /api/v1/budgets

{ "client_id": 1204, "vehicle_id": 871, "lines": [{ "description": "Canvi d'oli i filtre", "quantity": 1, "unit_price": 65 }] }

La resposta porta els totals calculats. El client accepta i signa des de l'enllaç que li envia el taller (l'API no dona mai aquest enllaç); si t'ho confirma a tu per una altra via, accepta'l en nom seu amb POST /budgets/{id}/accept (permís budgets:accept).

4. Forats lliures

GET /api/v1/appointments/availability

?from=2026-10-14&to=2026-10-18&duration_minutes=60

5. Proposar la cita

POST /api/v1/appointments

{ "budget_id": 1234, "start": "2026-10-14T09:00:00+02:00", "duration_minutes": 60 }

En mode «proposar» queda status=proposed fins que el taller la confirma (rebràs appointment.confirmed per webhook). Si el forat s'ha ocupat: 409 slot_unavailable amb alternatives.

Guia: el teu bot o IA als canals del taller

El teu assistent contesta per WhatsApp, SMS, xat web o correu en lloc de l'assistent del programa. Necessites una clau live amb communications:read, messages:send, settings:write i webhooks:manage (pla Business o Partner). Prova-ho abans a l'entorn de proves amb POST /sandbox/simulate/message.

1. Subscriu-te a message.received

POST /api/v1/webhooks

{ "url": "https://el-teu-bot.com/taller", "events": ["message.received"] }

Cada missatge del client arriba signat amb la clau de la conversa (key), el client, el pressupost actiu, el text i els adjunts.

2. Activa el mode assistent extern

PUT /api/v1/settings/assistant

{ "external_channels": ["whatsapp", "chat"], "fallback_minutes": 10 }

En aquests canals l'assistent del programa deixa de contestar. Si no respons a temps, o la teva clau o el teu webhook deixen de funcionar, torna a contestar ell a aquest missatge.

3. Llegeix el fil si ho necessites

GET /api/v1/conversations/{key}/messages

4. Respon

POST /api/v1/conversations/{key}/messages

{ "text": "Demà a les 9:00 tenim lloc. Te la reservo?" }

Fora de la finestra de 24 h de WhatsApp envia template_id (GET /messages/templates). Per tancar la venda: POST /appointments, POST /budgets/{id}/accept o POST /budgets/{id}/invoice segons els teus permisos.

Guia: flotes i rènting amb una clau de client

Perquè una flota, una empresa de rènting o un client amb molts vehicles connecti els seus sistemes amb el taller sense veure les dades dels altres clients, el taller li crea una clau limitada a la seva fitxa de client («Limitar a un client» en crear-la a Les meves claus). Amb ella cada llistat retorna només les seves dades, un id d'un altre client respon 404, les operacions de tot el taller responen 403 client_key_forbidden i els seus webhooks només reben els seus esdeveniments.

1. Comprova a quin client està limitada

GET /api/v1/ping

key.client porta l'id i el nom del client; en una clau del taller és null.

2. Els seus vehicles

GET /api/v1/vehicles

?limit=200

Només surten els vehicles de la seva fitxa. Per donar-ne d'alta un de nou, POST /vehicles amb el seu propi client_id.

3. Demanar un pressupost

POST /api/v1/budgets

{ "client_id": 1204, "vehicle_id": 871, "public_notes": "Revisió dels 60.000 km" }

client_id ha de ser el de la clau (si no, 403 client_key_forbidden). El taller el completa i l'envia; la flota l'accepta o el rebutja amb POST /budgets/{id}/accept o /reject (permís budgets:accept).

4. Proposar cita i seguir l'estat

POST /api/v1/appointments

{ "budget_id": 1234, "start": "2026-10-20T09:00:00+02:00", "duration_minutes": 60 }

Subscriu un webhook a lead.status_changed, vehicle.ready i invoice.issued per assabentar-te'n al moment; només arribaran els dels seus vehicles.

Entorn de proves

https://demo.programa-taller.es és una instància amb dades fictícies que es restauren cada 2 hores. Qualsevol clau d'allà és de proves amb tots els permisos: pot escriure, acceptar, facturar i «enviar», però res no surt de debò (els enviaments es registren com a simulats) i els webhooks s'emeten igualment per provar el cicle complet. Les teves claus, webhooks i registre de peticions es conserven entre reinicis.

1. Demana la teva clau

POST /api/v1/sandbox/keys

{ "email": "dev@exemple.com", "company": "Integracions Exemple SL", "name": "Anna Puig" }

També des de la pàgina https://demo.programa-taller.es/desarrolladores-sandbox. La clau es mostra una sola vegada i val 30 dies.

2. Simula un client que escriu

POST /api/v1/sandbox/simulate/message

{ "channel": "whatsapp", "from": "+34600111222", "text": "Teniu lloc demà?" }

3. Canvia a producció

Canvia la URL base per la de la instància del taller i fes servir una clau live creada a la seva secció Desenvolupadors.

Permisos (scopes)

PermísNomAmb efectesDescripció
clients:readLlegir clientsNoConsultar fitxes de clients i les seves dades de contacte.
clients:writeCrear i editar clientsSíDonar d'alta clients nous i modificar els existents.
vehicles:readLlegir vehiclesNoConsultar vehicles, matrícules i el seu historial.
vehicles:writeCrear i editar vehiclesSíDonar d'alta vehicles i modificar-ne les dades.
budgets:readLlegir pressupostosNoConsultar pressupostos, les seves partides i el seu estat.
budgets:writeCrear i editar pressupostosSíCrear pressupostos, afegir partides i canviar-ne l'estat.
budgets:acceptAcceptar o rebutjar en nom del clientSíAcceptar un pressupost en nom del client amb la seva evidència (qui i per quin canal) o rebutjar-lo amb motiu. Queda a l'historial amb el nom de la clau.
invoices:readLlegir facturesNoConsultar factures emeses, imports i cobraments.
invoices:writeEmetre factures i anotar cobramentsSíEmetre la factura d'un pressupost (numeració, sèrie, Verifactu i comptabilitat del programa) i registrar cobraments.
appointments:readLlegir citesNoConsultar l'agenda de cites i els forats disponibles.
appointments:writeReservar i cancel·lar citesSíCrear, moure i cancel·lar cites a l'agenda.
communications:readLlegir comunicacionsNoConsultar el registre d'emails, SMS, WhatsApp i trucades (inclou el contingut complet).
communications:sendEnviar email i SMSSíEnviar emails i SMS transaccionals des de la instància; consumeix saldo.
messages:sendRespondre conversesSíRespondre com el taller a les converses de WhatsApp, SMS, xat web i correu (bot o IA externa); consumeix saldo.
catalog:readLlegir catàlegNoConsultar serveis, tarifes i conceptes del tarifari.
stock:readLlegir estocNoConsultar existències i referències de recanvis.
stock:writeAjustar estocSíDonar entrades i sortides de recanvis.
webhooks:manageGestionar webhooksSíCrear, llistar i esborrar les subscripcions a esdeveniments d'aquesta clau.
settings:writeAjustos de l'assistent externSíConsultar i canviar en quins canals contesta un assistent extern en lloc de l'assistent del programa.
reports:readLlegir informesNoConsultar xifres agregades de facturació, activitat i rendiment.

General

GET /api/v1/ping Qualsevol clau

Provar la connexió

Retorna el nom de la clau, l'entorn, els permisos, l'estat dels límits i, si és una clau de client, el client al qual està limitada (key.client; null en una clau del taller). Serveix qualsevol clau vàlida.

Qualsevol clau vàlida, sense cap permís concret.

Clau de client: permès (no porta dades d'altres clients).

Exemple

curl "https://el-teu-taller.example/api/v1/ping" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "ok": true,
  "key": {
    "id": 7,
    "name": "CRM",
    "environment": "live",
    "scopes": [
      "clients:read"
    ],
    "expires_at": null,
    "client": {
      "id": 1204,
      "name": "Laura Gómez"
    }
  },
  "rate_limit": {
    "per_minute": {
      "limit": 60,
      "used": 1,
      "remaining": 59
    },
    "per_day": {
      "limit": 20000,
      "used": 1,
      "remaining": 19999
    }
  },
  "instance": "taller.ejemplo.com",
  "server_time": "2026-10-06T09:30:00.000Z",
  "version": "v1"
}

GET /api/v1/workshop Qualsevol clau

Dades públiques del taller

Nom, raó social, CIF, adreça, contacte, horari setmanal de l'agenda, mode de reserva de cites, festius propers i zona horària.

Qualsevol clau vàlida, sense cap permís concret.

Clau de client: permès (no porta dades d'altres clients).

Exemple

curl "https://el-teu-taller.example/api/v1/workshop" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "name": "Taller Ejemplo",
  "legal_name": "Taller Ejemplo S.L.",
  "tax_id": "B12345678",
  "address": "C/ Industria 4",
  "city": "Barcelona",
  "zip": "08020",
  "province": "Barcelona",
  "phone": "+34931234567",
  "whatsapp": "+34600111222",
  "email": "taller@ejemplo.com",
  "web": "https://www.ejemplo.com",
  "logo_url": null,
  "timezone": "Europe/Madrid",
  "currency": "EUR",
  "booking_mode": "propose",
  "schedule": {
    "mon": {
      "start": "08:00",
      "end": "19:00",
      "closed": false
    },
    "tue": {
      "start": "08:00",
      "end": "19:00",
      "closed": false
    },
    "wed": {
      "start": "08:00",
      "end": "19:00",
      "closed": false
    },
    "thu": {
      "start": "08:00",
      "end": "19:00",
      "closed": false
    },
    "fri": {
      "start": "08:00",
      "end": "19:00",
      "closed": false
    },
    "sat": {
      "start": "08:00",
      "end": "19:00",
      "closed": false
    },
    "sun": {
      "start": "08:00",
      "end": "19:00",
      "closed": false
    }
  },
  "lunch_break": {
    "start": "13:30",
    "end": "15:00"
  },
  "min_slot_minutes": 60,
  "upcoming_holidays": [
    {
      "date": "2026-10-12",
      "name": "Fiesta Nacional de España",
      "scope": "nacional"
    }
  ]
}

GET /api/v1/openapi.json Sense autenticació

Especificació OpenAPI 3.1

Fitxer generat des d'aquest mateix catàleg. Sense autenticació. Admet ?lang=es|en|ca|pt|fr|bg per als textos.

Sense autenticació.

Consulta (query)

NomTipusObligatoriDescripció
langstring [es, en, ca, pt, fr, bg] · default esNoIdioma de les descripcions.

Exemple

curl "https://el-teu-taller.example/api/v1/openapi.json"

Resposta

{}

Clients

GET /api/v1/clients clients:read

Llistar clients

Ordenats per id ascendent. Els clients esborrats per RGPD apareixen anonimitzats, amb erased_at informat.

Permís necessari: clients:read.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
searchstringNoCerca al nom, telèfon, email i NIF/CIF (mínim 2 caràcters).
expandstring [vehicles]NoRelacions opcionals que cal incloure.
limitinteger · default 50NoResultats per pàgina (1–200).
cursorstringNoCursor opac retornat a next_cursor de la pàgina anterior.

Exemple

curl "https://el-teu-taller.example/api/v1/clients" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 1204,
      "name": "Laura Gómez",
      "phone": "+34600111222",
      "phone_secondary": null,
      "whatsapp_phone": null,
      "email": "laura@ejemplo.com",
      "billing_email": null,
      "tax_id": "12345678Z",
      "address": "C/ Mayor 12",
      "city": "Barcelona",
      "zip": "08001",
      "province": "Barcelona",
      "country": "ES",
      "preferred_language": "es",
      "preferred_contact_method": "whatsapp",
      "vip": false,
      "marketing_opt_out": false,
      "channel_opt_out": {
        "email": false,
        "sms": false,
        "whatsapp": false,
        "call": false
      },
      "erased_at": null,
      "vehicles": [
        {
          "id": 871,
          "client_id": 1204,
          "client": null,
          "plate": "1234 KLM",
          "vin": "VSSZZZ5FZJR123456",
          "brand": "Seat",
          "model": "León",
          "variant": "1.5 TSI",
          "year": 2019,
          "registration_date": "2019-03-15",
          "fuel": "Gasolina",
          "transmission": "Manual",
          "engine_code": "DADA",
          "horsepower": 130,
          "displacement": "1498",
          "color_code": null,
          "environmental_label": "C",
          "km": 84500,
          "itv_expiry_date": "2027-03-15",
          "status": "activo"
        }
      ]
    }
  ],
  "next_cursor": "aWQ6MTIzNA",
  "has_more": true
}

POST /api/v1/clients clients:write

Crear un client

Mai no fusiona amb una fitxa existent: si el NIF/CIF, l'email o el telèfon ja són en un altre client respon 409 client_exists amb existing_id (o 200 amb aquest client si passes ?on_conflict=return_existing). El telèfon es desa com a la fitxa (Espanya en 9 dígits, altres països amb prefix) i l'email en minúscules; no es corregeixen errades. Les baixes comercials queden al registre de consentiments.

Permís necessari: clients:write.

Clau de client: no disponible (403 client_key_forbidden).

Consulta (query)

NomTipusObligatoriDescripció
on_conflictstring [error, return_existing] · default errorNoerror (per defecte): 409 si ja existeix. return_existing: 200 amb el client existent.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "name": "Laura Gómez",
  "phone": "600111222",
  "phone_secondary": null,
  "email": "laura@ejemplo.com",
  "billing_email": null,
  "tax_id": "12345678Z",
  "address": "C/ Mayor 12",
  "city": "Barcelona",
  "zip": "08001",
  "province": "Barcelona",
  "country": "ES",
  "preferred_language": "es",
  "preferred_contact_method": "whatsapp",
  "marketing_opt_out": false,
  "channel_opt_out": {
    "email": false,
    "sms": false,
    "whatsapp": false,
    "call": false
  }
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/clients" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"Laura Gómez","phone":"600111222","phone_secondary":null,"email":"laura@ejemplo.com","billing_email":null,"tax_id":"12345678Z","address":"C/ Mayor 12","city":"Barcelona","zip":"08001","province":"Barcelona","country":"ES","preferred_language":"es","preferred_contact_method":"whatsapp","marketing_opt_out":false,"channel_opt_out":{"email":false,"sms":false,"whatsapp":false,"call":false}}'

Resposta

{
  "id": 1204,
  "name": "Laura Gómez",
  "phone": "+34600111222",
  "phone_secondary": null,
  "whatsapp_phone": null,
  "email": "laura@ejemplo.com",
  "billing_email": null,
  "tax_id": "12345678Z",
  "address": "C/ Mayor 12",
  "city": "Barcelona",
  "zip": "08001",
  "province": "Barcelona",
  "country": "ES",
  "preferred_language": "es",
  "preferred_contact_method": "whatsapp",
  "vip": false,
  "marketing_opt_out": false,
  "channel_opt_out": {
    "email": false,
    "sms": false,
    "whatsapp": false,
    "call": false
  },
  "erased_at": null,
  "vehicles": [
    {
      "id": 871,
      "client_id": 1204,
      "client": {
        "id": 1204,
        "name": "Laura Gómez"
      },
      "plate": "1234 KLM",
      "vin": "VSSZZZ5FZJR123456",
      "brand": "Seat",
      "model": "León",
      "variant": "1.5 TSI",
      "year": 2019,
      "registration_date": "2019-03-15",
      "fuel": "Gasolina",
      "transmission": "Manual",
      "engine_code": "DADA",
      "horsepower": 130,
      "displacement": "1498",
      "color_code": null,
      "environmental_label": "C",
      "km": 84500,
      "itv_expiry_date": "2027-03-15",
      "status": "activo"
    }
  ]
}

GET /api/v1/clients/{id} clients:read

Detall d'un client

Permís necessari: clients:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del client.

Consulta (query)

NomTipusObligatoriDescripció
expandstring [vehicles]NoRelacions opcionals que cal incloure.

Exemple

curl "https://el-teu-taller.example/api/v1/clients/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "id": 1204,
  "name": "Laura Gómez",
  "phone": "+34600111222",
  "phone_secondary": null,
  "whatsapp_phone": null,
  "email": "laura@ejemplo.com",
  "billing_email": null,
  "tax_id": "12345678Z",
  "address": "C/ Mayor 12",
  "city": "Barcelona",
  "zip": "08001",
  "province": "Barcelona",
  "country": "ES",
  "preferred_language": "es",
  "preferred_contact_method": "whatsapp",
  "vip": false,
  "marketing_opt_out": false,
  "channel_opt_out": {
    "email": false,
    "sms": false,
    "whatsapp": false,
    "call": false
  },
  "erased_at": null,
  "vehicles": [
    {
      "id": 871,
      "client_id": 1204,
      "client": {
        "id": 1204,
        "name": "Laura Gómez"
      },
      "plate": "1234 KLM",
      "vin": "VSSZZZ5FZJR123456",
      "brand": "Seat",
      "model": "León",
      "variant": "1.5 TSI",
      "year": 2019,
      "registration_date": "2019-03-15",
      "fuel": "Gasolina",
      "transmission": "Manual",
      "engine_code": "DADA",
      "horsepower": 130,
      "displacement": "1498",
      "color_code": null,
      "environmental_label": "C",
      "km": 84500,
      "itv_expiry_date": "2027-03-15",
      "status": "activo"
    }
  ]
}

PATCH /api/v1/clients/{id} clients:write

Modificar un client

Només canvien els camps enviats (null buida el camp). Es pot canviar l'email o el telèfon, i queda el valor anterior → nou a l'activitat del client amb el nom de la clau; emet client.updated. No es pot posar l'email, el telèfon o el NIF d'UNA ALTRA fitxa (409 client_exists). Les fitxes esborrades per RGPD responen 409 client_erased.

Permís necessari: clients:write.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del client.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringNoClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "name": "Laura Gómez Ruiz",
  "phone": "600111222",
  "phone_secondary": null,
  "email": "laura@ejemplo.com",
  "billing_email": null,
  "tax_id": "12345678Z",
  "address": "C/ Mayor 12",
  "city": "Barcelona",
  "zip": "08001",
  "province": "Barcelona",
  "country": "ES",
  "preferred_language": "es",
  "preferred_contact_method": "whatsapp",
  "marketing_opt_out": false,
  "channel_opt_out": {
    "email": false,
    "sms": false,
    "whatsapp": false,
    "call": false
  }
}

Exemple

curl -X PATCH "https://el-teu-taller.example/api/v1/clients/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"Laura Gómez Ruiz","phone":"600111222","phone_secondary":null,"email":"laura@ejemplo.com","billing_email":null,"tax_id":"12345678Z","address":"C/ Mayor 12","city":"Barcelona","zip":"08001","province":"Barcelona","country":"ES","preferred_language":"es","preferred_contact_method":"whatsapp","marketing_opt_out":false,"channel_opt_out":{"email":false,"sms":false,"whatsapp":false,"call":false}}'

Resposta

{
  "id": 1204,
  "name": "Laura Gómez",
  "phone": "+34600111222",
  "phone_secondary": null,
  "whatsapp_phone": null,
  "email": "laura@ejemplo.com",
  "billing_email": null,
  "tax_id": "12345678Z",
  "address": "C/ Mayor 12",
  "city": "Barcelona",
  "zip": "08001",
  "province": "Barcelona",
  "country": "ES",
  "preferred_language": "es",
  "preferred_contact_method": "whatsapp",
  "vip": false,
  "marketing_opt_out": false,
  "channel_opt_out": {
    "email": false,
    "sms": false,
    "whatsapp": false,
    "call": false
  },
  "erased_at": null,
  "vehicles": [
    {
      "id": 871,
      "client_id": 1204,
      "client": {
        "id": 1204,
        "name": "Laura Gómez"
      },
      "plate": "1234 KLM",
      "vin": "VSSZZZ5FZJR123456",
      "brand": "Seat",
      "model": "León",
      "variant": "1.5 TSI",
      "year": 2019,
      "registration_date": "2019-03-15",
      "fuel": "Gasolina",
      "transmission": "Manual",
      "engine_code": "DADA",
      "horsepower": 130,
      "displacement": "1498",
      "color_code": null,
      "environmental_label": "C",
      "km": 84500,
      "itv_expiry_date": "2027-03-15",
      "status": "activo"
    }
  ]
}

Vehicles

GET /api/v1/vehicles vehicles:read

Llistar vehicles

Inclou l'últim quilometratge anotat i el venciment de la ITV.

Permís necessari: vehicles:read.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
platestringNoMatrícula exacta (s'ignoren espais i guions).
client_idintegerNoFiltrar per client.
statusstring [activo, baja_temporal, baja]NoEstat del vehicle.
limitinteger · default 50NoResultats per pàgina (1–200).
cursorstringNoCursor opac retornat a next_cursor de la pàgina anterior.

Exemple

curl "https://el-teu-taller.example/api/v1/vehicles" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 871,
      "client_id": 1204,
      "client": {
        "id": 1204,
        "name": "Laura Gómez"
      },
      "plate": "1234 KLM",
      "vin": "VSSZZZ5FZJR123456",
      "brand": "Seat",
      "model": "León",
      "variant": "1.5 TSI",
      "year": 2019,
      "registration_date": "2019-03-15",
      "fuel": "Gasolina",
      "transmission": "Manual",
      "engine_code": "DADA",
      "horsepower": 130,
      "displacement": "1498",
      "color_code": null,
      "environmental_label": "C",
      "km": 84500,
      "itv_expiry_date": "2027-03-15",
      "status": "activo"
    }
  ],
  "next_cursor": "aWQ6MTIzNA",
  "has_more": true
}

POST /api/v1/vehicles vehicles:write

Donar d'alta un vehicle

Sempre a nom d'un client existent (client_id). La matrícula es normalitza (majúscules, sense espais ni guions). Si ja és a la fitxa d'UN ALTRE client: 409 vehicle_belongs_to_other_client. Si el mateix client ja la té: 409 vehicle_exists amb existing_id (o 200 amb ?on_conflict=return_existing).

Permís necessari: vehicles:write.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
on_conflictstring [error, return_existing] · default errorNoerror (per defecte) o return_existing.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "client_id": 1204,
  "plate": "1234 KLM",
  "vin": "VSSZZZ5FZJR123456",
  "brand": "Seat",
  "model": "León",
  "variant": "1.5 TSI",
  "year": 2019,
  "registration_date": "2019-03-15",
  "fuel": "Gasolina",
  "transmission": "Manual",
  "engine_code": "DADA",
  "horsepower": 130,
  "displacement": "1498",
  "color_code": null,
  "environmental_label": "C",
  "itv_expiry_date": "2027-03-15",
  "status": "activo"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/vehicles" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"client_id":1204,"plate":"1234 KLM","vin":"VSSZZZ5FZJR123456","brand":"Seat","model":"León","variant":"1.5 TSI","year":2019,"registration_date":"2019-03-15","fuel":"Gasolina","transmission":"Manual","engine_code":"DADA","horsepower":130,"displacement":"1498","color_code":null,"environmental_label":"C","itv_expiry_date":"2027-03-15","status":"activo"}'

Resposta

{
  "id": 871,
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "plate": "1234 KLM",
  "vin": "VSSZZZ5FZJR123456",
  "brand": "Seat",
  "model": "León",
  "variant": "1.5 TSI",
  "year": 2019,
  "registration_date": "2019-03-15",
  "fuel": "Gasolina",
  "transmission": "Manual",
  "engine_code": "DADA",
  "horsepower": 130,
  "displacement": "1498",
  "color_code": null,
  "environmental_label": "C",
  "km": 84500,
  "itv_expiry_date": "2027-03-15",
  "status": "activo"
}

GET /api/v1/vehicles/{id} vehicles:read

Detall d'un vehicle

Permís necessari: vehicles:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del vehicle.

Exemple

curl "https://el-teu-taller.example/api/v1/vehicles/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "id": 871,
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "plate": "1234 KLM",
  "vin": "VSSZZZ5FZJR123456",
  "brand": "Seat",
  "model": "León",
  "variant": "1.5 TSI",
  "year": 2019,
  "registration_date": "2019-03-15",
  "fuel": "Gasolina",
  "transmission": "Manual",
  "engine_code": "DADA",
  "horsepower": 130,
  "displacement": "1498",
  "color_code": null,
  "environmental_label": "C",
  "km": 84500,
  "itv_expiry_date": "2027-03-15",
  "status": "activo"
}

PATCH /api/v1/vehicles/{id} vehicles:write

Modificar un vehicle

Canvis parcials. client_id no admet null: un vehicle mai no es desvincula del titular per API (sí que pot passar a un altre client existent).

Permís necessari: vehicles:write.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del vehicle.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringNoClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "client_id": 1204,
  "plate": "1234 KLM",
  "vin": "VSSZZZ5FZJR123456",
  "brand": "Seat",
  "model": "León",
  "variant": "1.5 TSI",
  "year": 2019,
  "registration_date": "2019-03-15",
  "fuel": "Gasolina",
  "transmission": "Manual",
  "engine_code": "DADA",
  "horsepower": 130,
  "displacement": "1498",
  "color_code": null,
  "environmental_label": "C",
  "itv_expiry_date": "2027-03-15",
  "status": "activo"
}

Exemple

curl -X PATCH "https://el-teu-taller.example/api/v1/vehicles/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"client_id":1204,"plate":"1234 KLM","vin":"VSSZZZ5FZJR123456","brand":"Seat","model":"León","variant":"1.5 TSI","year":2019,"registration_date":"2019-03-15","fuel":"Gasolina","transmission":"Manual","engine_code":"DADA","horsepower":130,"displacement":"1498","color_code":null,"environmental_label":"C","itv_expiry_date":"2027-03-15","status":"activo"}'

Resposta

{
  "id": 871,
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "plate": "1234 KLM",
  "vin": "VSSZZZ5FZJR123456",
  "brand": "Seat",
  "model": "León",
  "variant": "1.5 TSI",
  "year": 2019,
  "registration_date": "2019-03-15",
  "fuel": "Gasolina",
  "transmission": "Manual",
  "engine_code": "DADA",
  "horsepower": 130,
  "displacement": "1498",
  "color_code": null,
  "environmental_label": "C",
  "km": 84500,
  "itv_expiry_date": "2027-03-15",
  "status": "activo"
}

GET /api/v1/vehicles/{id}/history vehicles:read

Historial del vehicle

Visites al taller (tots els seus pressupostos tret dels d'ús intern, del més recent al més antic) amb estat, entrada i sortida, quilometratge, import, núm. de factura i conceptes de les partides; més les intervencions anotades en altres tallers. No inclou mai costos.

Permís necessari: vehicles:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del vehicle.

Consulta (query)

NomTipusObligatoriDescripció
limitinteger · default 50NoMàxim de visites (1–200).

Exemple

curl "https://el-teu-taller.example/api/v1/vehicles/1234/history" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "vehicle": {
    "id": 871,
    "client_id": 1204,
    "client": {
      "id": 1204,
      "name": "Laura Gómez"
    },
    "plate": "1234 KLM",
    "vin": "VSSZZZ5FZJR123456",
    "brand": "Seat",
    "model": "León",
    "variant": "1.5 TSI",
    "year": 2019,
    "registration_date": "2019-03-15",
    "fuel": "Gasolina",
    "transmission": "Manual",
    "engine_code": "DADA",
    "horsepower": 130,
    "displacement": "1498",
    "color_code": null,
    "environmental_label": "C",
    "km": 84500,
    "itv_expiry_date": "2027-03-15",
    "status": "activo"
  },
  "visits": [
    {
      "budget_id": 1234,
      "status": "Facturado",
      "created_at": "2026-10-06T09:30:00.000Z",
      "checked_in_at": "2026-09-02T07:30:00.000Z",
      "delivered_at": "2026-09-03T16:00:00.000Z",
      "km": 84210,
      "amount": 245.3,
      "invoice": "F26/118",
      "category": "Mantenimiento",
      "works": [
        "Canvi d'oli i filtre"
      ]
    }
  ],
  "external_interventions": [
    {
      "title": "Canvi de pneumàtics",
      "date": "2025-11-20",
      "km": 78000,
      "workshop_name": null,
      "warranty_until": null
    }
  ]
}

Pressupostos

GET /api/v1/budgets budgets:read

Llistar pressupostos

Mai no inclou els pressupostos d'ús intern del taller ni dades de cost. El detall (amb partides i totals) és a /budgets/{id}.

Permís necessari: budgets:read.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
statusstringNoEstat exacte, amb el valor en castellà (Pendiente, Enviado, Aprobado, Finalizado, Facturado, Rechazado…).
client_idintegerNoFiltrar per client.
vehicle_idintegerNoFiltrar per vehicle.
fromstring (date-time)NoCreats a partir d'aquesta data.
tostring (date-time)NoCreats fins a aquesta data.
updated_sincestring (date-time)NoNomés registres modificats a partir d'aquesta data (ISO 8601).
limitinteger · default 50NoResultats per pàgina (1–200).
cursorstringNoCursor opac retornat a next_cursor de la pàgina anterior.

Exemple

curl "https://el-teu-taller.example/api/v1/budgets" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 1234,
      "status": "Aprobado",
      "created_at": "2026-10-06T09:30:00.000Z",
      "updated_at": "2026-10-06T09:30:00.000Z",
      "channel": "web",
      "client_id": 1204,
      "client": {
        "id": 1204,
        "name": "Laura Gómez"
      },
      "vehicle_id": 871,
      "vehicle": {
        "id": 871,
        "plate": "1234 KLM",
        "brand": "Seat",
        "model": "León"
      },
      "category": {
        "id": 5,
        "name": "Mantenimiento"
      },
      "subcategory": {
        "id": 5,
        "name": "Mantenimiento"
      },
      "assigned_user": {
        "id": 3,
        "name": "Marta"
      },
      "mechanic": {
        "id": 3,
        "name": "Marta"
      },
      "appointment": {
        "start": "2026-10-06T09:30:00.000Z",
        "end": "2026-10-06T10:30:00.000Z",
        "status": "confirmed",
        "client_confirmed_at": null,
        "estimated_duration_minutes": 60,
        "box": null
      },
      "date_in": null,
      "date_out": null,
      "km": 84500,
      "client_reference": null,
      "waiting_parts": false,
      "on_hold": false,
      "hold_until": null,
      "client_signed_at": null,
      "estimated_ready_at": null,
      "delivered_at": null,
      "reject_reason": null
    }
  ],
  "next_cursor": "aWQ6MTIzNA",
  "has_more": true
}

POST /api/v1/budgets budgets:write

Crear un pressupost

Entra a «Pendiente» amb canal «API», igual que una alta des del programa (registre, fita d'obertura, webhook lead.created). Els totals es calculen al servidor amb l'impost del taller; si una partida no porta tax_rate, s'usa el del taller. El vehicle, si s'indica, ha de ser del client. No admet categories d'ús intern. Amb notify_client=true (i permís communications:send) s'envia al client l'acusament amb el seu enllaç de seguiment.

Permís necessari: budgets:write.

Clau de client: només retorna o crea dades del seu client.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "client_id": 1204,
  "vehicle_id": 871,
  "category_id": 5,
  "subcategory_id": 51,
  "client_reference": "PED-2026-118",
  "public_notes": "Revisar també el soroll de la suspensió.",
  "lines": [
    {
      "description": "Canvi d'oli i filtre",
      "quantity": 1,
      "unit_price": 65,
      "tax_rate": 21,
      "discount_pct": 0,
      "line_type": "labor",
      "reference": null,
      "group_title": null
    }
  ],
  "notify_client": false
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/budgets" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"client_id":1204,"vehicle_id":871,"category_id":5,"subcategory_id":51,"client_reference":"PED-2026-118","public_notes":"Revisar també el soroll de la suspensió.","lines":[{"description":"Canvi d'oli i filtre","quantity":1,"unit_price":65,"tax_rate":21,"discount_pct":0,"line_type":"labor","reference":null,"group_title":null}],"notify_client":false}'

Resposta

{
  "id": 1234,
  "status": "Aprobado",
  "created_at": "2026-10-06T09:30:00.000Z",
  "updated_at": "2026-10-06T09:30:00.000Z",
  "channel": "web",
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "vehicle_id": 871,
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "category": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "subcategory": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "assigned_user": {
    "id": 3,
    "name": "Marta"
  },
  "mechanic": {
    "id": 3,
    "name": "Marta"
  },
  "appointment": {
    "start": "2026-10-06T09:30:00.000Z",
    "end": "2026-10-06T10:30:00.000Z",
    "status": "confirmed",
    "client_confirmed_at": null,
    "estimated_duration_minutes": 60,
    "box": {
      "id": 2,
      "name": "Elevador 2"
    }
  },
  "date_in": null,
  "date_out": null,
  "km": 84500,
  "client_reference": null,
  "waiting_parts": false,
  "on_hold": false,
  "hold_until": null,
  "client_signed_at": null,
  "estimated_ready_at": null,
  "delivered_at": null,
  "reject_reason": null,
  "public_notes": "Revisar també el soroll a la suspensió.",
  "totals": {
    "base": 65,
    "tax": 13.65,
    "total": 78.65,
    "currency": "EUR",
    "tax_rates": [
      {
        "rate": 21,
        "base": 65,
        "quota": 13.65
      }
    ]
  },
  "lines": [
    {
      "id": 5501,
      "description": "Canvi d'oli i filtre",
      "extended_detail": null,
      "reference": null,
      "line_type": "labor",
      "group_title": null,
      "quantity": 1,
      "unit_price": 65,
      "discount_pct": 0,
      "tax_rate": 21,
      "tax_exempt_code": null,
      "price_estimated": false,
      "base": 65,
      "currency": "EUR",
      "sort_order": 0
    }
  ]
}

GET /api/v1/budgets/{id} budgets:read

Detall d'un pressupost

Partides (descripció, quantitat, preu unitari, impost, tipus, descompte), totals amb desglossament, cita, dates d'entrada i sortida, responsable i URL pública de seguiment.

Permís necessari: budgets:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.

Exemple

curl "https://el-teu-taller.example/api/v1/budgets/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "id": 1234,
  "status": "Aprobado",
  "created_at": "2026-10-06T09:30:00.000Z",
  "updated_at": "2026-10-06T09:30:00.000Z",
  "channel": "web",
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "vehicle_id": 871,
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "category": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "subcategory": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "assigned_user": {
    "id": 3,
    "name": "Marta"
  },
  "mechanic": {
    "id": 3,
    "name": "Marta"
  },
  "appointment": {
    "start": "2026-10-06T09:30:00.000Z",
    "end": "2026-10-06T10:30:00.000Z",
    "status": "confirmed",
    "client_confirmed_at": null,
    "estimated_duration_minutes": 60,
    "box": {
      "id": 2,
      "name": "Elevador 2"
    }
  },
  "date_in": null,
  "date_out": null,
  "km": 84500,
  "client_reference": null,
  "waiting_parts": false,
  "on_hold": false,
  "hold_until": null,
  "client_signed_at": null,
  "estimated_ready_at": null,
  "delivered_at": null,
  "reject_reason": null,
  "public_notes": "Revisar també el soroll a la suspensió.",
  "totals": {
    "base": 65,
    "tax": 13.65,
    "total": 78.65,
    "currency": "EUR",
    "tax_rates": [
      {
        "rate": 21,
        "base": 65,
        "quota": 13.65
      }
    ]
  },
  "lines": [
    {
      "id": 5501,
      "description": "Canvi d'oli i filtre",
      "extended_detail": null,
      "reference": null,
      "line_type": "labor",
      "group_title": null,
      "quantity": 1,
      "unit_price": 65,
      "discount_pct": 0,
      "tax_rate": 21,
      "tax_exempt_code": null,
      "price_estimated": false,
      "base": 65,
      "currency": "EUR",
      "sort_order": 0
    }
  ]
}

GET /api/v1/budgets/{id}/activity budgets:read

Historial del pressupost

El que ha passat al pressupost, del més recent al més antic: canvis d'estat, enviaments, acceptació (amb el seu origen), cites, missatges del client, facturació… El mateix registre que veu el taller a la fitxa; public=true marca el que també veu el client. Els codis d'un sol ús i els enllaços d'accés surten ocults.

Permís necessari: budgets:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.

Consulta (query)

NomTipusObligatoriDescripció
visibilitystring [all, public] · default allNoall: tot el registre; public: només el que veu el client.
limitinteger · default 50NoResultats per pàgina (1–200).
cursorstringNoCursor opac retornat a next_cursor de la pàgina anterior.

Exemple

curl "https://el-teu-taller.example/api/v1/budgets/1234/activity" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 88123,
      "at": "2026-10-06T09:30:00.000Z",
      "event": "web_accepted",
      "detail": "Pressupost acceptat pel client des de la web de seguiment",
      "source": "client",
      "public": true,
      "user": {
        "id": 3,
        "name": "Marta"
      }
    }
  ],
  "next_cursor": "aWQ6MTIzNA",
  "has_more": true
}

POST /api/v1/budgets/{id}/lines budgets:write

Afegir una partida

La resta de partides conserven el seu id. Deixa instantània prèvia i registre com qualsevol edició del programa. 409 budget_locked si el pressupost és Facturado, Facturado externamente, Cancelado, Rechazado, Desistido o ja té factura.

Permís necessari: budgets:write.

Clau de client: no disponible (403 client_key_forbidden).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "description": "Canvi d'oli i filtre",
  "quantity": 1,
  "unit_price": 65,
  "tax_rate": 21,
  "discount_pct": 0,
  "line_type": "labor",
  "reference": null,
  "group_title": null
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/budgets/1234/lines" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"description":"Canvi d'oli i filtre","quantity":1,"unit_price":65,"tax_rate":21,"discount_pct":0,"line_type":"labor","reference":null,"group_title":null}'

Resposta

{
  "line": {
    "id": 5501,
    "description": "Canvi d'oli i filtre",
    "extended_detail": null,
    "reference": null,
    "line_type": "labor",
    "group_title": null,
    "quantity": 1,
    "unit_price": 65,
    "discount_pct": 0,
    "tax_rate": 21,
    "tax_exempt_code": null,
    "price_estimated": false,
    "base": 65,
    "currency": "EUR",
    "sort_order": 0
  },
  "budget": {
    "id": 1234,
    "status": "Aprobado",
    "created_at": "2026-10-06T09:30:00.000Z",
    "updated_at": "2026-10-06T09:30:00.000Z",
    "channel": "web",
    "client_id": 1204,
    "client": {
      "id": 1204,
      "name": "Laura Gómez"
    },
    "vehicle_id": 871,
    "vehicle": {
      "id": 871,
      "plate": "1234 KLM",
      "brand": "Seat",
      "model": "León"
    },
    "category": {
      "id": 5,
      "name": "Mantenimiento"
    },
    "subcategory": {
      "id": 5,
      "name": "Mantenimiento"
    },
    "assigned_user": {
      "id": 3,
      "name": "Marta"
    },
    "mechanic": {
      "id": 3,
      "name": "Marta"
    },
    "appointment": {
      "start": "2026-10-06T09:30:00.000Z",
      "end": "2026-10-06T10:30:00.000Z",
      "status": "confirmed",
      "client_confirmed_at": null,
      "estimated_duration_minutes": 60,
      "box": {
        "id": null,
        "name": null
      }
    },
    "date_in": null,
    "date_out": null,
    "km": 84500,
    "client_reference": null,
    "waiting_parts": false,
    "on_hold": false,
    "hold_until": null,
    "client_signed_at": null,
    "estimated_ready_at": null,
    "delivered_at": null,
    "reject_reason": null,
    "public_notes": "Revisar també el soroll a la suspensió.",
    "totals": {
      "base": 65,
      "tax": 13.65,
      "total": 78.65,
      "currency": "EUR",
      "tax_rates": [
        {
          "rate": 21,
          "base": 65,
          "quota": 13.65
        }
      ]
    },
    "lines": [
      {
        "id": 5501,
        "description": "Canvi d'oli i filtre",
        "extended_detail": null,
        "reference": null,
        "line_type": "labor",
        "group_title": null,
        "quantity": 1,
        "unit_price": 65,
        "discount_pct": 0,
        "tax_rate": 21,
        "tax_exempt_code": null,
        "price_estimated": false,
        "base": 65,
        "currency": "EUR",
        "sort_order": 0
      }
    ]
  }
}

PATCH /api/v1/budgets/{id}/lines/{lineId} budgets:write

Modificar una partida

Permís necessari: budgets:write.

Clau de client: no disponible (403 client_key_forbidden).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.
lineIdintegerSíId de la partida.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringNoClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "description": "Canvi d'oli i filtre",
  "quantity": 1,
  "unit_price": 65,
  "tax_rate": 21,
  "discount_pct": 0,
  "line_type": "labor",
  "reference": null,
  "group_title": null
}

Exemple

curl -X PATCH "https://el-teu-taller.example/api/v1/budgets/1234/lines/5501" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"description":"Canvi d'oli i filtre","quantity":1,"unit_price":65,"tax_rate":21,"discount_pct":0,"line_type":"labor","reference":null,"group_title":null}'

Resposta

{
  "line": {
    "id": 5501,
    "description": "Canvi d'oli i filtre",
    "extended_detail": null,
    "reference": null,
    "line_type": "labor",
    "group_title": null,
    "quantity": 1,
    "unit_price": 65,
    "discount_pct": 0,
    "tax_rate": 21,
    "tax_exempt_code": null,
    "price_estimated": false,
    "base": 65,
    "currency": "EUR",
    "sort_order": 0
  },
  "budget": {
    "id": 1234,
    "status": "Aprobado",
    "created_at": "2026-10-06T09:30:00.000Z",
    "updated_at": "2026-10-06T09:30:00.000Z",
    "channel": "web",
    "client_id": 1204,
    "client": {
      "id": 1204,
      "name": "Laura Gómez"
    },
    "vehicle_id": 871,
    "vehicle": {
      "id": 871,
      "plate": "1234 KLM",
      "brand": "Seat",
      "model": "León"
    },
    "category": {
      "id": 5,
      "name": "Mantenimiento"
    },
    "subcategory": {
      "id": 5,
      "name": "Mantenimiento"
    },
    "assigned_user": {
      "id": 3,
      "name": "Marta"
    },
    "mechanic": {
      "id": 3,
      "name": "Marta"
    },
    "appointment": {
      "start": "2026-10-06T09:30:00.000Z",
      "end": "2026-10-06T10:30:00.000Z",
      "status": "confirmed",
      "client_confirmed_at": null,
      "estimated_duration_minutes": 60,
      "box": {
        "id": null,
        "name": null
      }
    },
    "date_in": null,
    "date_out": null,
    "km": 84500,
    "client_reference": null,
    "waiting_parts": false,
    "on_hold": false,
    "hold_until": null,
    "client_signed_at": null,
    "estimated_ready_at": null,
    "delivered_at": null,
    "reject_reason": null,
    "public_notes": "Revisar també el soroll a la suspensió.",
    "totals": {
      "base": 65,
      "tax": 13.65,
      "total": 78.65,
      "currency": "EUR",
      "tax_rates": [
        {
          "rate": 21,
          "base": 65,
          "quota": 13.65
        }
      ]
    },
    "lines": [
      {
        "id": 5501,
        "description": "Canvi d'oli i filtre",
        "extended_detail": null,
        "reference": null,
        "line_type": "labor",
        "group_title": null,
        "quantity": 1,
        "unit_price": 65,
        "discount_pct": 0,
        "tax_rate": 21,
        "tax_exempt_code": null,
        "price_estimated": false,
        "base": 65,
        "currency": "EUR",
        "sort_order": 0
      }
    ]
  }
}

DELETE /api/v1/budgets/{id}/lines/{lineId} budgets:write

Treure una partida

Retorna el pressupost amb els totals recalculats. La partida queda a la instantània prèvia de l'historial de versions.

Permís necessari: budgets:write.

Clau de client: no disponible (403 client_key_forbidden).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.
lineIdintegerSíId de la partida.

Exemple

curl -X DELETE "https://el-teu-taller.example/api/v1/budgets/1234/lines/5501" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "id": 1234,
  "status": "Aprobado",
  "created_at": "2026-10-06T09:30:00.000Z",
  "updated_at": "2026-10-06T09:30:00.000Z",
  "channel": "web",
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "vehicle_id": 871,
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "category": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "subcategory": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "assigned_user": {
    "id": 3,
    "name": "Marta"
  },
  "mechanic": {
    "id": 3,
    "name": "Marta"
  },
  "appointment": {
    "start": "2026-10-06T09:30:00.000Z",
    "end": "2026-10-06T10:30:00.000Z",
    "status": "confirmed",
    "client_confirmed_at": null,
    "estimated_duration_minutes": 60,
    "box": {
      "id": 2,
      "name": "Elevador 2"
    }
  },
  "date_in": null,
  "date_out": null,
  "km": 84500,
  "client_reference": null,
  "waiting_parts": false,
  "on_hold": false,
  "hold_until": null,
  "client_signed_at": null,
  "estimated_ready_at": null,
  "delivered_at": null,
  "reject_reason": null,
  "public_notes": "Revisar també el soroll a la suspensió.",
  "totals": {
    "base": 65,
    "tax": 13.65,
    "total": 78.65,
    "currency": "EUR",
    "tax_rates": [
      {
        "rate": 21,
        "base": 65,
        "quota": 13.65
      }
    ]
  },
  "lines": [
    {
      "id": 5501,
      "description": "Canvi d'oli i filtre",
      "extended_detail": null,
      "reference": null,
      "line_type": "labor",
      "group_title": null,
      "quantity": 1,
      "unit_price": 65,
      "discount_pct": 0,
      "tax_rate": 21,
      "tax_exempt_code": null,
      "price_estimated": false,
      "base": 65,
      "currency": "EUR",
      "sort_order": 0
    }
  ]
}

POST /api/v1/budgets/{id}/status budgets:write

Canviar l'estat

Admet Pendiente/En cotización/Enviado (abans de l'acceptació), En curso (pressupost ja acceptat), Finalizado (des d'Aprobado, En curso o En espera) i Cancelado. «Aprobado» respon 403 client_acceptance_required: l'acceptació la signa el client des de l'enllaç que li envia el taller, o la registra la teva integració en nom seu amb POST /budgets/{id}/accept (permís budgets:accept). Rebutjar va per POST /budgets/{id}/reject i facturar per POST /budgets/{id}/invoice (permís invoices:write); aquí responen 403 status_transition_forbidden, igual que desistir; des de Facturado o Cancelado, 409 budget_locked. «Enviado» exigeix a més communications:send i sent_via: registra que EL TEU sistema ja l'ha enviat (no l'envia). Cap canvi avisa el client excepte Finalizado amb notify_client=true i communications:send.

Permís necessari: budgets:write.

Clau de client: no disponible (403 client_key_forbidden).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "status": "En curso",
  "sent_via": "email",
  "notify_client": false
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/budgets/1234/status" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"status":"En curso","sent_via":"email","notify_client":false}'

Resposta

{
  "id": 1234,
  "status": "Aprobado",
  "created_at": "2026-10-06T09:30:00.000Z",
  "updated_at": "2026-10-06T09:30:00.000Z",
  "channel": "web",
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "vehicle_id": 871,
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "category": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "subcategory": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "assigned_user": {
    "id": 3,
    "name": "Marta"
  },
  "mechanic": {
    "id": 3,
    "name": "Marta"
  },
  "appointment": {
    "start": "2026-10-06T09:30:00.000Z",
    "end": "2026-10-06T10:30:00.000Z",
    "status": "confirmed",
    "client_confirmed_at": null,
    "estimated_duration_minutes": 60,
    "box": {
      "id": 2,
      "name": "Elevador 2"
    }
  },
  "date_in": null,
  "date_out": null,
  "km": 84500,
  "client_reference": null,
  "waiting_parts": false,
  "on_hold": false,
  "hold_until": null,
  "client_signed_at": null,
  "estimated_ready_at": null,
  "delivered_at": null,
  "reject_reason": null,
  "public_notes": "Revisar també el soroll a la suspensió.",
  "totals": {
    "base": 65,
    "tax": 13.65,
    "total": 78.65,
    "currency": "EUR",
    "tax_rates": [
      {
        "rate": 21,
        "base": 65,
        "quota": 13.65
      }
    ]
  },
  "lines": [
    {
      "id": 5501,
      "description": "Canvi d'oli i filtre",
      "extended_detail": null,
      "reference": null,
      "line_type": "labor",
      "group_title": null,
      "quantity": 1,
      "unit_price": 65,
      "discount_pct": 0,
      "tax_rate": 21,
      "tax_exempt_code": null,
      "price_estimated": false,
      "base": 65,
      "currency": "EUR",
      "sort_order": 0
    }
  ]
}

POST /api/v1/budgets/{id}/accept budgets:accept

Acceptar en nom del client

Per quan el client accepta per una altra via (telèfon, en persona, la teva web…). L'evidència és obligatòria: accepted_by i channel (phone, in_person, email, whatsapp, other); note és opcional. Passa per la mateixa regla d'acceptació del programa: estat Aprobado, transició, dades fiscals mínimes del client (si falten, 422 fiscal_data_incomplete amb missing), descompte de campanya, avís intern al taller, confirmació al client segons els avisos configurats i webhooks lead.accepted i budget.accepted. Només des d'«Enviado» (si ja està acceptat respon 200 amb already=true); Cancelado, Rechazado, Desistido, Finalizado o Facturado responen 409 budget_locked. Si el taller exigeix signatura, l'API accepta igualment i ho deixa escrit: «sense signatura, acceptat per integració» (signature=waived_by_integration). A l'historial queda «Acceptat per API en nom del client · integració <clau> · <accepted_by> per <canal>».

Permís necessari: budgets:accept.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "accepted_by": "Laura Gómez",
  "channel": "phone",
  "note": "Confirmat per telèfon a les 10:12"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/budgets/1234/accept" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"accepted_by":"Laura Gómez","channel":"phone","note":"Confirmat per telèfon a les 10:12"}'

Resposta

{
  "budget": {
    "id": 1234,
    "status": "Aprobado",
    "created_at": "2026-10-06T09:30:00.000Z",
    "updated_at": "2026-10-06T09:30:00.000Z",
    "channel": "web",
    "client_id": 1204,
    "client": {
      "id": 1204,
      "name": "Laura Gómez"
    },
    "vehicle_id": 871,
    "vehicle": {
      "id": 871,
      "plate": "1234 KLM",
      "brand": "Seat",
      "model": "León"
    },
    "category": {
      "id": 5,
      "name": "Mantenimiento"
    },
    "subcategory": {
      "id": 5,
      "name": "Mantenimiento"
    },
    "assigned_user": {
      "id": 3,
      "name": "Marta"
    },
    "mechanic": {
      "id": 3,
      "name": "Marta"
    },
    "appointment": {
      "start": "2026-10-06T09:30:00.000Z",
      "end": "2026-10-06T10:30:00.000Z",
      "status": "confirmed",
      "client_confirmed_at": null,
      "estimated_duration_minutes": 60,
      "box": null
    },
    "date_in": null,
    "date_out": null,
    "km": 84500,
    "client_reference": null,
    "waiting_parts": false,
    "on_hold": false,
    "hold_until": null,
    "client_signed_at": null,
    "estimated_ready_at": null,
    "delivered_at": null,
    "reject_reason": null,
    "public_notes": "Revisar també el soroll a la suspensió.",
    "totals": {
      "base": 65,
      "tax": 13.65,
      "total": 78.65,
      "currency": "EUR",
      "tax_rates": [
        {
          "rate": 21,
          "base": 65,
          "quota": 13.65
        }
      ]
    },
    "lines": [
      {
        "id": 5501,
        "description": "Canvi d'oli i filtre",
        "extended_detail": null,
        "reference": null,
        "line_type": "labor",
        "group_title": null,
        "quantity": 1,
        "unit_price": 65,
        "discount_pct": 0,
        "tax_rate": 21,
        "tax_exempt_code": null,
        "price_estimated": false,
        "base": 65,
        "currency": "EUR",
        "sort_order": 0
      }
    ]
  },
  "already": false,
  "signature": "not_required"
}

POST /api/v1/budgets/{id}/reject budgets:accept

Rebutjar en nom del client

Amb motiu obligatori. Mateixa regla que el rebuig del client: des de Pendiente, En cotización, Enviado o Aprobado (si el vehicle encara no ha entrat). Emet lead.rejected i budget.rejected i queda a l'historial amb el nom de la clau.

Permís necessari: budgets:accept.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "reason": "Ho farà en un altre taller",
  "rejected_by": "Laura Gómez",
  "channel": "phone"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/budgets/1234/reject" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Ho farà en un altre taller","rejected_by":"Laura Gómez","channel":"phone"}'

Resposta

{
  "budget": {
    "id": 1234,
    "status": "Aprobado",
    "created_at": "2026-10-06T09:30:00.000Z",
    "updated_at": "2026-10-06T09:30:00.000Z",
    "channel": "web",
    "client_id": 1204,
    "client": {
      "id": 1204,
      "name": "Laura Gómez"
    },
    "vehicle_id": 871,
    "vehicle": {
      "id": 871,
      "plate": "1234 KLM",
      "brand": "Seat",
      "model": "León"
    },
    "category": {
      "id": 5,
      "name": "Mantenimiento"
    },
    "subcategory": {
      "id": 5,
      "name": "Mantenimiento"
    },
    "assigned_user": {
      "id": 3,
      "name": "Marta"
    },
    "mechanic": {
      "id": 3,
      "name": "Marta"
    },
    "appointment": {
      "start": "2026-10-06T09:30:00.000Z",
      "end": "2026-10-06T10:30:00.000Z",
      "status": "confirmed",
      "client_confirmed_at": null,
      "estimated_duration_minutes": 60,
      "box": null
    },
    "date_in": null,
    "date_out": null,
    "km": 84500,
    "client_reference": null,
    "waiting_parts": false,
    "on_hold": false,
    "hold_until": null,
    "client_signed_at": null,
    "estimated_ready_at": null,
    "delivered_at": null,
    "reject_reason": null,
    "public_notes": "Revisar també el soroll a la suspensió.",
    "totals": {
      "base": 65,
      "tax": 13.65,
      "total": 78.65,
      "currency": "EUR",
      "tax_rates": [
        {
          "rate": 21,
          "base": 65,
          "quota": 13.65
        }
      ]
    },
    "lines": [
      {
        "id": 5501,
        "description": "Canvi d'oli i filtre",
        "extended_detail": null,
        "reference": null,
        "line_type": "labor",
        "group_title": null,
        "quantity": 1,
        "unit_price": 65,
        "discount_pct": 0,
        "tax_rate": 21,
        "tax_exempt_code": null,
        "price_estimated": false,
        "base": 65,
        "currency": "EUR",
        "sort_order": 0
      }
    ]
  },
  "already": false,
  "signature": "not_required"
}

POST /api/v1/budgets/{id}/documents budgets:write

Adjuntar un document

multipart/form-data amb el camp «file» (JPEG, PNG, WebP o PDF, comprovat pel contingut; màxim 4 MB). Per defecte només el veu el taller; client_visible=true el mostra a l'enllaç de seguiment i mechanic_visible=true a l'app del mecànic. L'empremta d'idempotència inclou el fitxer.

Permís necessari: budgets:write.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos (multipart/form-data)

NomTipusObligatoriDescripció
filestring (binary)SíFichero
client_visiblestring [true, false]No
mechanic_visiblestring [true, false]No

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/budgets/1234/documents" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "file=@fichero.pdf" \
  -F "client_visible=false" \
  -F "mechanic_visible=false"

Resposta

{
  "id": 991,
  "budget_id": 1234,
  "filename": "parte-de-trabajo.pdf",
  "content_type": "application/pdf",
  "size": 182044,
  "url": "https://…/leads/1234/api-parte-de-trabajo.pdf",
  "client_visible": false,
  "mechanic_visible": false,
  "created_at": "2026-10-06T09:30:00.000Z"
}

Factures

POST /api/v1/budgets/{id}/invoice invoices:write

Facturar un pressupost

Fa servir la mateixa facturació del programa: copia les partides i l'impost del pressupost, crea l'assentament comptable i, amb issue=true (per defecte), emet la factura amb la sèrie que toqui, número correlatiu, hash encadenat i QR Verifactu. El pressupost passa a Facturado. Es factura un d'Aprobado, En curso, En espera o Finalizado amb import; si ja té factura es retorna aquesta (created=false), mai una segona. Emet invoice.created i invoice.issued. Les factures rectificatives no estan disponibles per API: es fan des del programa.

Permís necessari: invoices:write.

Clau de client: no disponible (403 client_key_forbidden).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del pressupost.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "issue": true,
  "pay_method": "TARJETA"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/budgets/1234/invoice" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"issue":true,"pay_method":"TARJETA"}'

Resposta

{
  "invoice": {
    "id": 412,
    "number": 87,
    "series": "F26",
    "full_number": "F2687",
    "kind": "invoice",
    "rectifies_number": null,
    "status": "issued",
    "date": "2026-10-06",
    "issued_at": "2026-10-06T09:30:00.000Z",
    "client_id": 1204,
    "client": {
      "id": 1204,
      "name": "Laura Gómez"
    },
    "budget_id": 1234,
    "vehicle_id": 871,
    "vehicle": {
      "id": 871,
      "plate": "1234 KLM",
      "brand": "Seat",
      "model": "León"
    },
    "plate": "1234 KLM",
    "km": 84500,
    "total": 78.65,
    "currency": "EUR",
    "payments": {
      "paid": 78.65,
      "pending": 0,
      "settled": true
    },
    "payment_method": "Tarjeta",
    "rebu": false,
    "verifactu_hash": "3f9a…",
    "registered_externally": false,
    "billing": {
      "name": "Laura Gómez",
      "tax_id": "12345678Z",
      "address": "C/ Mayor 12",
      "city": "Barcelona",
      "zip": "08001",
      "province": "Barcelona"
    },
    "date_in": null,
    "date_out": null,
    "public_notes": null,
    "totals": {
      "base": 65,
      "tax": 13.65,
      "total": 78.65,
      "currency": "EUR",
      "tax_rates": [
        {
          "rate": 21,
          "base": 65,
          "quota": 13.65
        }
      ]
    },
    "lines": [
      {
        "id": 9001,
        "description": "Canvi d'oli i filtre",
        "reference": null,
        "group_title": null,
        "quantity": 1,
        "unit_price": 65,
        "discount_pct": 0,
        "tax_rate": 21,
        "tax_exempt_code": null,
        "base": 65,
        "currency": "EUR",
        "sort_order": 0
      }
    ],
    "payment_list": [
      {
        "id": 77,
        "date": "2026-10-06T09:30:00.000Z",
        "amount": 78.65,
        "currency": "EUR",
        "method": "Tarjeta"
      }
    ]
  },
  "created": true
}

GET /api/v1/invoices invoices:read

Llistar factures

Factures emeses i esborranys, ordenades per id. Inclou el resum de cobraments.

Permís necessari: invoices:read.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
fromstring (date-time)NoData de factura des de.
tostring (date-time)NoData de factura fins a.
client_idintegerNoFiltrar per client.
statusstring [draft, issued, cancelled]NoEstat de la factura.
seriesstringNoSèrie exacta.
limitinteger · default 50NoResultats per pàgina (1–200).
cursorstringNoCursor opac retornat a next_cursor de la pàgina anterior.

Exemple

curl "https://el-teu-taller.example/api/v1/invoices" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 412,
      "number": 87,
      "series": "F26",
      "full_number": "F2687",
      "kind": "invoice",
      "rectifies_number": null,
      "status": "issued",
      "date": "2026-10-06",
      "issued_at": "2026-10-06T09:30:00.000Z",
      "client_id": 1204,
      "client": {
        "id": 1204,
        "name": "Laura Gómez"
      },
      "budget_id": 1234,
      "vehicle_id": 871,
      "vehicle": {
        "id": 871,
        "plate": "1234 KLM",
        "brand": "Seat",
        "model": "León"
      },
      "plate": "1234 KLM",
      "km": 84500,
      "total": 78.65,
      "currency": "EUR",
      "payments": {
        "paid": 78.65,
        "pending": 0,
        "settled": true
      },
      "payment_method": "Tarjeta",
      "rebu": false,
      "verifactu_hash": "3f9a…",
      "registered_externally": false
    }
  ],
  "next_cursor": "aWQ6MTIzNA",
  "has_more": true
}

GET /api/v1/invoices/{id} invoices:read

Detall d'una factura

Permís necessari: invoices:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId de la factura.

Exemple

curl "https://el-teu-taller.example/api/v1/invoices/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "id": 412,
  "number": 87,
  "series": "F26",
  "full_number": "F2687",
  "kind": "invoice",
  "rectifies_number": null,
  "status": "issued",
  "date": "2026-10-06",
  "issued_at": "2026-10-06T09:30:00.000Z",
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "budget_id": 1234,
  "vehicle_id": 871,
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "plate": "1234 KLM",
  "km": 84500,
  "total": 78.65,
  "currency": "EUR",
  "payments": {
    "paid": 78.65,
    "pending": 0,
    "settled": true
  },
  "payment_method": "Tarjeta",
  "rebu": false,
  "verifactu_hash": "3f9a…",
  "registered_externally": false,
  "billing": {
    "name": "Laura Gómez",
    "tax_id": "12345678Z",
    "address": "C/ Mayor 12",
    "city": "Barcelona",
    "zip": "08001",
    "province": "Barcelona"
  },
  "date_in": null,
  "date_out": null,
  "public_notes": null,
  "totals": {
    "base": 65,
    "tax": 13.65,
    "total": 78.65,
    "currency": "EUR",
    "tax_rates": [
      {
        "rate": 21,
        "base": 65,
        "quota": 13.65
      }
    ]
  },
  "lines": [
    {
      "id": 9001,
      "description": "Canvi d'oli i filtre",
      "reference": null,
      "group_title": null,
      "quantity": 1,
      "unit_price": 65,
      "discount_pct": 0,
      "tax_rate": 21,
      "tax_exempt_code": null,
      "base": 65,
      "currency": "EUR",
      "sort_order": 0
    }
  ],
  "payment_list": [
    {
      "id": 77,
      "date": "2026-10-06T09:30:00.000Z",
      "amount": 78.65,
      "currency": "EUR",
      "method": "Tarjeta"
    }
  ]
}

GET /api/v1/invoices/{id}/pdf invoices:read

PDF d'una factura

El mateix PDF que genera el programa (amb QR Verifactu si la factura està emesa). Resposta application/pdf.

Permís necessari: invoices:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId de la factura.

Exemple

curl "https://el-teu-taller.example/api/v1/invoices/1234/pdf" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

(application/pdf)

POST /api/v1/invoices/{id}/payments invoices:write

Anotar un cobrament

Mateix registre de cobraments que la fitxa de la factura: la marca «pagada» es recalcula amb tots els cobraments (mai es força) i s'emet payment.received. Només sobre factures emeses. Retorna la factura amb el seu resum de cobraments.

Permís necessari: invoices:write.

Clau de client: no disponible (403 client_key_forbidden).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId de la factura.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "amount": 245.3,
  "date": "2026-10-13",
  "pay_method": "TARJETA",
  "notes": "Cobrat al taulell"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/invoices/1234/payments" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"amount":245.3,"date":"2026-10-13","pay_method":"TARJETA","notes":"Cobrat al taulell"}'

Resposta

{
  "id": 412,
  "number": 87,
  "series": "F26",
  "full_number": "F2687",
  "kind": "invoice",
  "rectifies_number": null,
  "status": "issued",
  "date": "2026-10-06",
  "issued_at": "2026-10-06T09:30:00.000Z",
  "client_id": 1204,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "budget_id": 1234,
  "vehicle_id": 871,
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "plate": "1234 KLM",
  "km": 84500,
  "total": 78.65,
  "currency": "EUR",
  "payments": {
    "paid": 78.65,
    "pending": 0,
    "settled": true
  },
  "payment_method": "Tarjeta",
  "rebu": false,
  "verifactu_hash": "3f9a…",
  "registered_externally": false,
  "billing": {
    "name": "Laura Gómez",
    "tax_id": "12345678Z",
    "address": "C/ Mayor 12",
    "city": "Barcelona",
    "zip": "08001",
    "province": "Barcelona"
  },
  "date_in": null,
  "date_out": null,
  "public_notes": null,
  "totals": {
    "base": 65,
    "tax": 13.65,
    "total": 78.65,
    "currency": "EUR",
    "tax_rates": [
      {
        "rate": 21,
        "base": 65,
        "quota": 13.65
      }
    ]
  },
  "lines": [
    {
      "id": 9001,
      "description": "Canvi d'oli i filtre",
      "reference": null,
      "group_title": null,
      "quantity": 1,
      "unit_price": 65,
      "discount_pct": 0,
      "tax_rate": 21,
      "tax_exempt_code": null,
      "base": 65,
      "currency": "EUR",
      "sort_order": 0
    }
  ],
  "payment_list": [
    {
      "id": 77,
      "date": "2026-10-06T09:30:00.000Z",
      "amount": 78.65,
      "currency": "EUR",
      "method": "Tarjeta"
    }
  ]
}

Cites

GET /api/v1/appointments appointments:read

Cites confirmades i proposades

Per defecte els propers 30 dies. status=confirmed són cites fixades a l'agenda; status=proposed són propostes del client pendents que el taller confirmi (bloquegen el forat).

Permís necessari: appointments:read.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
fromstring (date-time)NoInici del rang (per defecte ara).
tostring (date-time)NoFi del rang (per defecte +30 dies, màxim 1 any).
statusstring [confirmed, proposed]NoNomés un tipus.
box_idintegerNoFiltrar per elevador/box.

Exemple

curl "https://el-teu-taller.example/api/v1/appointments" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": "b1234",
      "budget_id": 1234,
      "status": "confirmed",
      "start": "2026-10-06T09:30:00.000Z",
      "end": "2026-10-06T10:30:00.000Z",
      "estimated_duration_minutes": 60,
      "client_confirmed_at": null,
      "budget_status": "Aprobado",
      "checked_in_at": null,
      "client": {
        "id": 1204,
        "name": "Laura Gómez"
      },
      "vehicle": {
        "id": 871,
        "plate": "1234 KLM",
        "brand": "Seat",
        "model": "León"
      },
      "mechanic": {
        "id": 3,
        "name": "Marta"
      },
      "box": {
        "id": 2,
        "name": "Elevador 2"
      },
      "category": {
        "id": 5,
        "name": "Mantenimiento"
      },
      "channel": null,
      "proposed_at": null
    }
  ],
  "from": "2026-10-06T00:00:00.000Z",
  "to": "2026-11-05T00:00:00.000Z"
}

POST /api/v1/appointments appointments:write

Proposar o reservar una cita

Segueix el mode de reserva del taller (booking_mode a GET /workshop). En «propose» es crea una proposta (status=proposed) que bloqueja el forat fins que el taller la confirma; demanar mode=book respon 403 booking_mode_propose_only. En «book» la cita queda ferma (status=confirmed), tret que demanis mode=propose. El forat es valida amb la mateixa lògica que /appointments/availability; si no és lliure, 409 slot_unavailable amb fins a 3 alternatives a error.alternatives. No s'avisa el client tret de notify_client=true amb communications:send.

Permís necessari: appointments:write.

Clau de client: només retorna o crea dades del seu client.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "budget_id": 1234,
  "start": "2026-10-14T09:00:00+02:00",
  "box_id": 2,
  "duration_minutes": 60,
  "mode": "propose",
  "notify_client": false
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/appointments" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"budget_id":1234,"start":"2026-10-14T09:00:00+02:00","box_id":2,"duration_minutes":60,"mode":"propose","notify_client":false}'

Resposta

{
  "id": "b1234",
  "budget_id": 1234,
  "status": "confirmed",
  "start": "2026-10-06T09:30:00.000Z",
  "end": "2026-10-06T10:30:00.000Z",
  "estimated_duration_minutes": 60,
  "client_confirmed_at": null,
  "budget_status": "Aprobado",
  "checked_in_at": null,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "mechanic": {
    "id": 3,
    "name": "Marta"
  },
  "box": {
    "id": 2,
    "name": "Elevador 2"
  },
  "category": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "channel": null,
  "proposed_at": null
}

GET /api/v1/appointments/availability appointments:read

Forats lliures

Mateixa lògica que l'agenda i la web de seguiment: horari del taller i de cada box, dinar, festius nacionals, autonòmics i locals, cites obertes i propostes pendents (que bloquegen el seu forat). Inicis cada 30 min i com a mínim 1 h des d'ara. Per defecte els propers 7 dies (màxim 31). Inclou el booking_mode del taller.

Permís necessari: appointments:read.

Clau de client: permès (no porta dades d'altres clients).

Consulta (query)

NomTipusObligatoriDescripció
fromstring (date-time)NoDes de (per defecte ara).
tostring (date-time)NoFins a (per defecte +7 dies; màxim 31 dies).
duration_minutesintegerNoDurada de la cita (15–720). Per defecte la mínima de l'agenda.
box_idintegerNoNomés aquest box.
limitinteger · default 100NoMàxim de forats (1–500).

Exemple

curl "https://el-teu-taller.example/api/v1/appointments/availability" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "start": "2026-10-14T07:00:00.000Z",
      "end": "2026-10-14T08:00:00.000Z",
      "box": {
        "id": 2,
        "name": "Elevador 2"
      }
    }
  ],
  "duration_minutes": 60,
  "from": "2026-10-14T00:00:00.000Z",
  "to": "2026-10-21T00:00:00.000Z",
  "booking_mode": "propose"
}

DELETE /api/v1/appointments/{id} appointments:write

Anul·lar una cita o retirar una proposta

b<pressupost>: anul·la la cita confirmada (igual que «Cancel·lar cita» a la fitxa). p<proposta>: retira la proposta pendent; amb notify_client=true i communications:send es convida el client a triar una altra hora. Emet appointment.cancelled.

Permís necessari: appointments:write.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idstringSíId de la cita (b1234 o p88).

Consulta (query)

NomTipusObligatoriDescripció
notify_clientboolean · default falseNoNomés propostes: avisar el client.

Exemple

curl -X DELETE "https://el-teu-taller.example/api/v1/appointments/b1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "ok": true,
  "id": "b1234",
  "status": "cancelled"
}

GET /api/v1/appointments/{id} appointments:read

Detall d'una cita

Permís necessari: appointments:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idstringSíId de la cita (b1234 o p88).

Exemple

curl "https://el-teu-taller.example/api/v1/appointments/b1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "id": "b1234",
  "budget_id": 1234,
  "status": "confirmed",
  "start": "2026-10-06T09:30:00.000Z",
  "end": "2026-10-06T10:30:00.000Z",
  "estimated_duration_minutes": 60,
  "client_confirmed_at": null,
  "budget_status": "Aprobado",
  "checked_in_at": null,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "mechanic": {
    "id": 3,
    "name": "Marta"
  },
  "box": {
    "id": 2,
    "name": "Elevador 2"
  },
  "category": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "channel": null,
  "proposed_at": null
}

PATCH /api/v1/appointments/{id} appointments:write

Moure una cita o canviar una proposta

b<pressupost>: mou la cita en ferm a la nova hora (només si el taller treballa en mode «reservar»; en mode «proposar» respon 403 booking_mode_propose_only: crea una proposta amb POST /appointments). p<proposta>: retira la proposta i en crea una altra a la nova hora (canvia l'id). El forat es valida igual que en crear (409 slot_unavailable amb alternatives). Emet appointment.updated.

Permís necessari: appointments:write.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
idstringSíId de la cita (b1234 o p88).

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringNoClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "start": "2026-10-15T10:00:00+02:00",
  "box_id": 2,
  "duration_minutes": 60,
  "notify_client": false
}

Exemple

curl -X PATCH "https://el-teu-taller.example/api/v1/appointments/b1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"start":"2026-10-15T10:00:00+02:00","box_id":2,"duration_minutes":60,"notify_client":false}'

Resposta

{
  "id": "b1234",
  "budget_id": 1234,
  "status": "confirmed",
  "start": "2026-10-06T09:30:00.000Z",
  "end": "2026-10-06T10:30:00.000Z",
  "estimated_duration_minutes": 60,
  "client_confirmed_at": null,
  "budget_status": "Aprobado",
  "checked_in_at": null,
  "client": {
    "id": 1204,
    "name": "Laura Gómez"
  },
  "vehicle": {
    "id": 871,
    "plate": "1234 KLM",
    "brand": "Seat",
    "model": "León"
  },
  "mechanic": {
    "id": 3,
    "name": "Marta"
  },
  "box": {
    "id": 2,
    "name": "Elevador 2"
  },
  "category": {
    "id": 5,
    "name": "Mantenimiento"
  },
  "channel": null,
  "proposed_at": null
}

Catàleg

GET /api/v1/catalog/services catalog:read

Categories i subcategories de servei

Permís necessari: catalog:read.

Clau de client: permès (no porta dades d'altres clients).

Exemple

curl "https://el-teu-taller.example/api/v1/catalog/services" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 5,
      "name": "Mantenimiento",
      "reference_price": 120,
      "currency": "EUR",
      "subcategories": [
        {
          "id": 51,
          "name": "Canvi d'oli",
          "reference_price": 65,
          "currency": "EUR"
        }
      ]
    }
  ]
}

GET /api/v1/catalog/rates catalog:read

Tarifes de mà d'obra

Només el preu de venda per hora; el cost mai no surt per l'API.

Permís necessari: catalog:read.

Clau de client: permès (no porta dades d'altres clients).

Exemple

curl "https://el-teu-taller.example/api/v1/catalog/rates" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 1,
      "name": "Mà d'obra general",
      "price_per_hour": 48,
      "currency": "EUR",
      "is_default": true
    }
  ]
}

Recanvis

GET /api/v1/stock stock:read

Llistar recanvis de l'estoc

Existències amb unitat i decimals (quantity, reserved, pending, planned, available, min_stock), referències i preu de venda. Disponible = quantitat − bloquejat per pressupostos acceptats o en curs; el previst en pressupostos oberts no bloqueja. El cost de compra, el proveïdor i l'historial de compres no surten mai per l'API. Per defecte només peces actives.

Permís necessari: stock:read.

Clau de client: permès (no porta dades d'altres clients).

Consulta (query)

NomTipusObligatoriDescripció
searchstringNoCerca al nom, la marca, les referències i l'EAN (les referències sense tenir en compte espais, punts ni guions).
referencestringNoReferència o EAN exactes, sense tenir en compte espais, punts ni guions.
in_stockbooleanNotrue: només amb quantitat al magatzem; false: només amb quantitat zero (mira available per al que és lliure).
include_inactiveboolean · default falseNoIncloure peces donades de baixa de l'inventari.
updated_sincestring (date-time)NoNomés registres modificats a partir d'aquesta data (ISO 8601).
limitinteger · default 50NoResultats per pàgina (1–200).
cursorstringNoCursor opac retornat a next_cursor de la pàgina anterior.

Exemple

curl "https://el-teu-taller.example/api/v1/stock" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 88,
      "name": "Turbocompresor",
      "brand": "Garrett",
      "references": {
        "original": "A6540907801",
        "supplier": "MA654 090 78 01",
        "own": null
      },
      "ean": null,
      "unit": "ud",
      "quantity": 4.5,
      "reserved": 1,
      "pending": 0,
      "planned": 2,
      "available": 3.5,
      "min_stock": 2,
      "location": "Prestatgeria B3",
      "status": "nuevo",
      "sale_price": 2190,
      "currency": "EUR",
      "active": true,
      "updated_at": "2026-10-06T09:30:00.000Z"
    }
  ],
  "next_cursor": "aWQ6MTIzNA",
  "has_more": true
}

GET /api/v1/stock/{id} stock:read

Detall d'un recanvi

Permís necessari: stock:read.

Clau de client: permès (no porta dades d'altres clients).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del recanvi.

Exemple

curl "https://el-teu-taller.example/api/v1/stock/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "id": 88,
  "name": "Turbocompresor",
  "brand": "Garrett",
  "references": {
    "original": "A6540907801",
    "supplier": "MA654 090 78 01",
    "own": null
  },
  "ean": null,
  "unit": "ud",
  "quantity": 4.5,
  "reserved": 1,
  "pending": 0,
  "planned": 2,
  "available": 3.5,
  "min_stock": 2,
  "location": "Prestatgeria B3",
  "status": "nuevo",
  "sale_price": 2190,
  "currency": "EUR",
  "active": true,
  "updated_at": "2026-10-06T09:30:00.000Z"
}

Comunicacions

GET /api/v1/communications communications:read

Registre de comunicacions

Últimes comunicacions (email, SMS, WhatsApp, trucades, push) amb el contingut complet, de la més recent a la més antiga.

Permís necessari: communications:read.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
channelstring [all, email, sms, whatsapp, call, push] · default allNoCanal.
limitinteger · default 50NoMàxim de resultats (1–200).

Exemple

curl "https://el-teu-taller.example/api/v1/communications" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": "c_8812",
      "channel": "email",
      "direction": "out",
      "recipient": "laura@ejemplo.com",
      "subject": "El seu pressupost",
      "body": "Hola Laura, …",
      "idlead": 1234,
      "idclient": 1204,
      "clientName": "Laura Gómez",
      "status": "sent",
      "created_at": "2026-10-06T09:30:00.000Z",
      "duration": null,
      "recordingUrl": null,
      "agent": null,
      "fromNumber": null,
      "toNumber": null
    }
  ],
  "next_cursor": null,
  "has_more": false
}

GET /api/v1/logs communications:read

Registre de comunicacions (àlies antic)

Mateixa consulta que /communications però retorna { items }. Es manté per compatibilitat; fes servir /communications.

Permís necessari: communications:read.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
channelstring [all, email, sms, whatsapp, call, push] · default allNoCanal.
limitinteger · default 50NoMàxim de resultats (1–200).

Exemple

curl "https://el-teu-taller.example/api/v1/logs" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "items": [
    {
      "id": "c_8812",
      "channel": "email",
      "direction": "out",
      "recipient": "laura@ejemplo.com",
      "subject": "El seu pressupost",
      "body": "Hola Laura, …",
      "idlead": 1234,
      "idclient": 1204,
      "clientName": "Laura Gómez",
      "status": "sent",
      "created_at": "2026-10-06T09:30:00.000Z",
      "duration": null,
      "recordingUrl": null,
      "agent": null,
      "fromNumber": null,
      "toNumber": null
    }
  ]
}

POST /api/v1/email communications:send

Enviar un email transaccional

Només text: surt amb la plantilla, el nom i el compte de correu del taller, i queda al registre de Comunicació. No admet HTML ni remitent propi, i replyTo només pot ser una adreça del domini del taller. Si rebota, l'email del client es marca com a no vàlid.

Permís necessari: communications:send.

Clau de client: no disponible (403 client_key_forbidden).

Cos

{
  "to": "cliente@ejemplo.com",
  "subject": "El seu vehicle és a punt",
  "text": "Ja el pot passar a recollir.",
  "replyTo": "taller@ejemplo.com"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/email" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"to":"cliente@ejemplo.com","subject":"El seu vehicle és a punt","text":"Ja el pot passar a recollir.","replyTo":"taller@ejemplo.com"}'

Resposta

{
  "ok": true
}

POST /api/v1/sms communications:send

Enviar un SMS transaccional

Surt amb el servei d'SMS configurat al taller i queda al registre de Comunicació, on s'actualitza l'estat de lliurament.

Permís necessari: communications:send.

Clau de client: no disponible (403 client_key_forbidden).

Cos

{
  "to": "+34600111222",
  "body": "El seu vehicle ja es pot recollir."
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/sms" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"to":"+34600111222","body":"El seu vehicle ja es pot recollir."}'

Resposta

{
  "ok": true
}

Converses

GET /api/v1/conversations communications:read

Converses de la Safata

WhatsApp, SMS, xat de la web i correu en una llista, com la Safata d'entrada del programa, de la més recent a la més antiga. Cadascuna amb la seva clau estable (canal:número o canal:id), client, pressupost actiu, estat (open: falta contestar; pending: esperant el client; closed) i si la finestra de 24 h de WhatsApp continua oberta.

Permís necessari: communications:read.

Clau de client: només retorna o crea dades del seu client.

Consulta (query)

NomTipusObligatoriDescripció
channelstring [whatsapp, sms, chat, email]NoNomés un canal.
statusstring [open, pending, closed]NoNomés un estat.
daysinteger · default 30NoActivitat dels darrers N dies (1–365).
limitinteger · default 50NoMàxim de resultats (1–200).

Exemple

curl "https://el-teu-taller.example/api/v1/conversations" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "key": "whatsapp:600111222",
      "channel": "whatsapp",
      "contact": "+34600111222",
      "client": {
        "id": 1204,
        "name": "Laura Gómez"
      },
      "budget_id": 1234,
      "status": "open",
      "unread": 1,
      "last_message_at": "2026-10-06T09:30:00.000Z",
      "last_inbound_at": "2026-10-06T09:29:00.000Z",
      "last_message": "A quina hora puc passar a recollir el cotxe?",
      "last_author": "cliente",
      "whatsapp_window_open": true,
      "external_bot": false
    }
  ]
}

GET /api/v1/conversations/{key}/messages communications:read

Missatges d'una conversa

El fil complet en ordre cronològic, amb autor (cliente, ia, equipo, automatico o integracion), text i adjunts. Els correus es retornen com a text, sense la plantilla ni el fil citat.

Permís necessari: communications:read.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
keystringSíClau de la conversa: whatsapp:<9 dígits>, sms:<9 dígits>, chat:<id> o email:<id>.

Exemple

curl "https://el-teu-taller.example/api/v1/conversations/whatsapp:600111222/messages" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "conversation": {
    "key": "whatsapp:600111222",
    "channel": "whatsapp",
    "contact": "+34600111222",
    "client": {
      "id": 1204,
      "name": "Laura Gómez"
    },
    "budget_id": 1234,
    "status": "open",
    "unread": 1,
    "last_message_at": "2026-10-06T09:30:00.000Z",
    "last_inbound_at": "2026-10-06T09:29:00.000Z",
    "last_message": "A quina hora puc passar a recollir el cotxe?",
    "last_author": "cliente",
    "whatsapp_window_open": true,
    "external_bot": false
  },
  "data": [
    {
      "id": 77120,
      "at": "2026-10-06T09:30:00.000Z",
      "direction": "in",
      "author": "cliente",
      "author_name": null,
      "subject": null,
      "text": "A quina hora puc passar a recollir el cotxe?",
      "attachments": [
        "https://…/whatsapp-in/SM123-0.jpg"
      ],
      "status": "received"
    }
  ]
}

POST /api/v1/conversations/{key}/messages messages:send

Respondre en una conversa

Surt pel mateix canal i amb les mateixes funcions que la Safata d'entrada del programa. WhatsApp: text i/o media_url dins de la finestra de 24 h; fora d'ella només amb template_id (plantilla aprovada; si no, 422 whatsapp_window_closed). SMS: text (un fragment). Xat web: text. Correu: subject i text. El missatge queda registrat amb la integració com a autora («API · <clau>»), marca la conversa com a contestada i, si hi havia un missatge esperant l'assistent extern, cancel·la la resposta de reserva de l'assistent del programa. A l'entorn de proves res no surt de debò (simulated=true).

Permís necessari: messages:send.

Clau de client: només si el recurs és del seu client; si no, 404.

Paràmetres

NomTipusObligatoriDescripció
keystringSíClau de la conversa: whatsapp:<9 dígits>, sms:<9 dígits>, chat:<id> o email:<id>.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "text": "El seu cotxe estarà a punt a les 18:00.",
  "media_url": null,
  "template_id": 12,
  "template_variables": {
    "#nombre#": "Laura"
  },
  "subject": "El seu pressupost"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/conversations/whatsapp:600111222/messages" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"text":"El seu cotxe estarà a punt a les 18:00.","media_url":null,"template_id":12,"template_variables":{"#nombre#":"Laura"},"subject":"El seu pressupost"}'

Resposta

{
  "ok": true,
  "key": "whatsapp:600111222",
  "channel": "whatsapp",
  "mode": "free",
  "simulated": false
}

GET /api/v1/messages/templates communications:read

Plantilles de WhatsApp aprovades

Les plantilles que Meta ha aprovat al taller: el seu id és el template_id per escriure fora de la finestra de 24 h.

Permís necessari: communications:read.

Clau de client: permès (no porta dades d'altres clients).

Exemple

curl "https://el-teu-taller.example/api/v1/messages/templates" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": 12,
      "name": "presupuesto_listo",
      "language": "es",
      "category": "UTILITY",
      "body": "Hola #nombre#, el seu pressupost està llest.",
      "variables": [
        "#nombre#"
      ]
    }
  ]
}

Ajustos

GET /api/v1/settings/assistant settings:write

Mode assistent extern

En quins canals contesta el teu assistent en lloc de l'assistent del programa, la clau que els atén, els minuts de reserva i els canals que de debò atens ara (clau viva i webhook message.received actiu).

Permís necessari: settings:write.

Clau de client: no disponible (403 client_key_forbidden).

Exemple

curl "https://el-teu-taller.example/api/v1/settings/assistant" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "external_channels": [
    "whatsapp"
  ],
  "api_key_id": 7,
  "fallback_minutes": 10,
  "channels_owned_now": [
    "whatsapp"
  ]
}

PUT /api/v1/settings/assistant settings:write

Canviar el mode assistent extern

Als canals d'external_channels l'assistent del programa deixa de contestar: cada missatge entra, es registra, s'emet message.received i s'espera la teva resposta (POST /conversations/{key}/messages). Salvaguarda: si no respons en fallback_minutes (10 per defecte), si la clau es revoca o caduca, o si no hi ha cap webhook message.received actiu o el seu lliurament falla, contesta l'assistent del programa a aquest missatge i queda registrat. La clau que fa la petició passa a ser la que atén. external_channels=[] ho torna tot al programa.

Permís necessari: settings:write.

Clau de client: no disponible (403 client_key_forbidden).

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringNoClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "external_channels": [
    "whatsapp"
  ],
  "fallback_minutes": 10
}

Exemple

curl -X PUT "https://el-teu-taller.example/api/v1/settings/assistant" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"external_channels":["whatsapp"],"fallback_minutes":10}'

Resposta

{
  "external_channels": [
    "whatsapp"
  ],
  "api_key_id": 7,
  "fallback_minutes": 10,
  "channels_owned_now": [
    "whatsapp"
  ]
}

Entorn de proves

POST /api/v1/sandbox/keys Sense autenticació

Demanar una clau de proves

Només a https://demo.programa-taller.es (a les altres instàncies respon 404 sandbox_only). Sense autenticació ni cost: retorna una clau pt_test_… amb tots els permisos, vàlida 30 dies, que només es mostra una vegada. Màxim 3 al dia per IP i per correu. Les dades de la demo són fictícies i es restauren cada 2 hores; les claus, els webhooks i el registre de peticions es conserven. Res no surt de debò: els enviaments es registren com a simulats i els webhooks s'emeten igualment.

Sense autenticació.

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringNoClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "email": "dev@ejemplo.com",
  "company": "Integracions Exemple SL",
  "name": "Anna Puig"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/sandbox/keys" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"email":"dev@ejemplo.com","company":"Integracions Exemple SL","name":"Anna Puig"}'

Resposta

{
  "key": "pt_test_0123456789abcdef0123456789abcdef",
  "expires_at": "2026-11-12T10:00:00.000Z",
  "base_url": "https://demo.programa-taller.es/api/v1",
  "docs_url": "https://demo.programa-taller.es/api/v1/openapi.json",
  "scopes": [
    "budgets:accept"
  ]
}

POST /api/v1/sandbox/simulate/message Qualsevol clau

Simular un missatge entrant

Només a l'entorn de proves. Crea un missatge entrant fictici per WhatsApp, SMS, xat web o correu, com si l'hagués escrit un client: entra a la Safata d'entrada, s'enllaça amb el client si el telèfon o el correu coincideixen i dispara message.received per provar el teu bot de punta a punta. Contesta amb POST /conversations/{key}/messages (la resposta es simula).

Qualsevol clau vàlida, sense cap permís concret.

Clau de client: no disponible (403 client_key_forbidden).

Capçaleres

NomTipusObligatoriDescripció
Idempotency-KeystringSíClau única per operació (es recomana un UUID). Repetir-la amb el mateix cos en 24 h retorna la resposta desada amb Idempotent-Replay: true; amb un altre cos, 409 idempotency_conflict.

Cos

{
  "channel": "whatsapp",
  "from": "+34600111222",
  "text": "Hola, teniu lloc demà per canviar l'oli?",
  "subject": "Consulta",
  "media_urls": []
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/sandbox/simulate/message" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"channel":"whatsapp","from":"+34600111222","text":"Hola, teniu lloc demà per canviar l'oli?","subject":"Consulta","media_urls":[]}'

Resposta

{
  "ok": true,
  "key": "whatsapp:600111222"
}

Webhooks

GET /api/v1/webhooks webhooks:manage

Llistar els webhooks de la clau

Inclou el catàleg d'esdeveniments disponibles. Mai no retorna els secrets.

Permís necessari: webhooks:manage.

Clau de client: permès (no porta dades d'altres clients).

Exemple

curl "https://el-teu-taller.example/api/v1/webhooks" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "items": [
    {
      "id": 3,
      "url": "https://tu-sistema.com/webhooks/taller",
      "events": [
        "lead.accepted"
      ],
      "description": "CRM",
      "active": true,
      "created_at": "2026-10-06T09:30:00.000Z"
    }
  ],
  "events": [
    {
      "event": "lead.accepted",
      "label": "Pressupost acceptat",
      "description": "texto"
    }
  ]
}

POST /api/v1/webhooks webhooks:manage

Crear un webhook

El secret de signatura (whsec_…) només viatja en aquesta resposta. Amb una clau de client només s'entreguen els esdeveniments d'aquest client (els que no pertanyen a cap client, com campaign.finished, no arriben).

Permís necessari: webhooks:manage.

Clau de client: permès (no porta dades d'altres clients).

Cos

{
  "url": "https://tu-sistema.com/webhooks/taller",
  "events": [
    "lead.accepted"
  ],
  "description": "CRM"
}

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/webhooks" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://tu-sistema.com/webhooks/taller","events":["lead.accepted"],"description":"CRM"}'

Resposta

{
  "item": {
    "id": 3,
    "url": "https://tu-sistema.com/webhooks/taller",
    "events": [
      "lead.accepted"
    ],
    "description": "CRM",
    "active": true,
    "created_at": "2026-10-06T09:30:00.000Z"
  },
  "secret": "whsec_…"
}

GET /api/v1/webhooks/{id} webhooks:manage

Detall d'un webhook i les darreres entregues

Permís necessari: webhooks:manage.

Clau de client: permès (no porta dades d'altres clients).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del webhook.

Exemple

curl "https://el-teu-taller.example/api/v1/webhooks/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "item": {
    "id": 3,
    "url": "https://tu-sistema.com/webhooks/taller",
    "events": [
      "lead.accepted"
    ],
    "description": "CRM",
    "active": true,
    "created_at": "2026-10-06T09:30:00.000Z"
  },
  "deliveries": [
    {}
  ]
}

PATCH /api/v1/webhooks/{id} webhooks:manage

Modificar un webhook

Permís necessari: webhooks:manage.

Clau de client: permès (no porta dades d'altres clients).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del webhook.

Cos

{
  "url": "texto",
  "events": [
    "texto"
  ],
  "description": "texto",
  "active": false
}

Exemple

curl -X PATCH "https://el-teu-taller.example/api/v1/webhooks/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"url":"texto","events":["texto"],"description":"texto","active":false}'

Resposta

{
  "item": {
    "id": 3,
    "url": "https://tu-sistema.com/webhooks/taller",
    "events": [
      "lead.accepted"
    ],
    "description": "CRM",
    "active": true,
    "created_at": "2026-10-06T09:30:00.000Z"
  }
}

DELETE /api/v1/webhooks/{id} webhooks:manage

Esborrar un webhook

Permís necessari: webhooks:manage.

Clau de client: permès (no porta dades d'altres clients).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del webhook.

Exemple

curl -X DELETE "https://el-teu-taller.example/api/v1/webhooks/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "ok": true
}

POST /api/v1/webhooks/{id}/test webhooks:manage

Enviar una entrega de prova (test.ping) al webhook

Permís necessari: webhooks:manage.

Clau de client: permès (no porta dades d'altres clients).

Paràmetres

NomTipusObligatoriDescripció
idintegerSíId del webhook.

Exemple

curl -X POST "https://el-teu-taller.example/api/v1/webhooks/1234/test" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "ok": true,
  "status": 200
}

Webhooks

Càrrega

{
  "event": "lead.accepted",
  "timestamp": "2026-10-06T10:15:00.000Z",
  "data": {
    "leadId": 1234,
    "from": "Enviado",
    "to": "Aprobado"
  }
}
EsdevenimentDescripció
lead.createdPressupost creat
S'ha creat un pressupost nou (des del programa, la web, l'email o l'assistent).
lead.sentPressupost enviat
El pressupost s'ha enviat al client.
lead.acceptedPressupost acceptat
El client o el taller han aprovat el pressupost.
lead.rejectedPressupost rebutjat
El pressupost s'ha rebutjat, amb el motiu si n'hi ha.
lead.status_changedCanvi d'estat
Qualsevol canvi d'estat del pressupost (inclou els anteriors).
budget.acceptedPressupost acceptat (budget)
Igual que lead.accepted, amb el nom nou: el pressupost ha passat a Aprobado (client, taller o integració amb budgets:accept).
budget.rejectedPressupost rebutjat (budget)
Igual que lead.rejected, amb el nom nou: el pressupost ha passat a Rechazado, amb el seu motiu.
appointment.proposedCita proposada
Un client proposa una cita pendent que el taller la confirmi.
appointment.confirmedCita confirmada
Una cita queda confirmada a l'agenda.
appointment.cancelledCita cancel·lada
S'ha anul·lat la cita d'un pressupost.
appointment.createdCita creada
Un pressupost que no tenia cita queda citat a l'agenda (programa, web de seguiment, assistent o API).
appointment.updatedCita canviada
S'ha mogut la data, l'hora o el box d'una cita existent, o s'ha canviat una proposta per API.
vehicle.createdVehicle creat
S'ha donat d'alta un vehicle.
vehicle.updatedVehicle actualitzat
S'han modificat les dades o l'estat d'un vehicle.
vehicle.checked_inVehicle rebut
El vehicle ha entrat al taller (recepció o sense cita).
vehicle.arrivingClient de camí
El client avisa des del seu enllaç d'entrada que va de camí, arribarà tard o ja és al taller, amb l'hora estimada.
vehicle.readyVehicle a punt
La reparació ha acabat i el vehicle es pot recollir.
vehicle.arrivedClient a la recepció
El personal marca a la cua de recepció que el client ha arribat al taulell (l'entrada per quiosc o mòbil emet vehicle.checked_in).
vehicle.eta_updatedHora prevista de lliurament
S'ha fixat o canviat l'hora prevista de lliurament del vehicle; indica si s'ha comunicat al client.
vehicle.deliveredVehicle lliurat
El client ha recollit el vehicle.
vehicle.pickup_plannedRecollida planificada
El client ha triat el dia i la franja en què passarà a recollir el vehicle (date, slot: morning/afternoon/any).
courtesy_car.loanedCotxe de cortesia prestat
S'ha lliurat un cotxe de cortesia al client amb contracte signat (matrícula, km i combustible de sortida, devolució prevista).
courtesy_car.returnedCotxe de cortesia retornat
El client ha retornat el cotxe de cortesia (km i combustible d'entrada, dies i km extra calculats).
stock.lowEstoc baix
El disponible d'un recanvi ha baixat del seu estoc mínim (partId, name, unit, quantity, available, reserved, planned, minStock). S'envia un cop per canvi de nivell, no cada dia.
stock.outRecanvi esgotat
No queda res disponible d'un recanvi amb mínim o amb consum (mateixos camps que stock.low). Un cop per canvi de nivell.
stock.shortageFalta estoc en un pressupost acceptat
Un pressupost ha passat a Aprovat o En curs i no hi havia prou estoc: leadId i items [{ partId, missing }] amb el que queda pendent de demanar.
invoice.createdFactura creada
S'ha creat una factura (encara en esborrany, sense número definitiu), per exemple en facturar un pressupost.
invoice.issuedFactura emesa
S'ha emès una factura amb número definitiu.
payment.receivedCobrament registrat
S'ha anotat un cobrament d'una factura.
client.createdClient creat
S'ha donat d'alta un client.
client.updatedClient actualitzat
S'han modificat les dades d'un client.
communication.inboundMissatge entrant
Ha arribat un email, SMS, WhatsApp o trucada d'un client.
message.receivedMissatge rebut en una conversa
Un client ha escrit per WhatsApp, SMS, xat web o correu: conversa (clau estable canal:número), client, pressupost actiu, text, adjunts i data, i si l'assistent extern ha de contestar (external_bot).
call.completedTrucada analitzada
Una trucada (de l'equip o de l'assistent de veu) ha acabat i s'ha analitzat: resum, motiu, sentiment i transcripció si n'hi ha.
email.sentEmail enviat
S'ha enviat un email (campanyes, avisos o API).
email.failedEmail fallit
Un email no s'ha pogut enviar.
email.openedEmail obert
El destinatari ha obert l'email.
email.clickedClic a l'email
El destinatari ha clicat un enllaç de l'email.
email.unsubscribedBaixa d'email
El destinatari s'ha donat de baixa dels emails.
email.bouncedEmail rebotat
L'email ha rebotat.
sms.sentSMS enviat
S'ha enviat un SMS.
sms.failedSMS fallit
Un SMS no s'ha pogut enviar.
sms.unsubscribedBaixa d'SMS
El destinatari ha demanat no rebre SMS.
whatsapp.unsubscribedBaixa de WhatsApp
El destinatari ha demanat no rebre WhatsApp.
campaign.finishedCampanya acabada
Una campanya ha acabat d'enviar-se.
3 mesos per 1 € →