Clientes
GET /api/v1/clients clients:read
Listar clientes
Ordenados por id crescente. Os clientes apagados a pedido (RGPD) aparecem anonimizados, com erased_at preenchido.
Permissão necessária: clients:read.
Consulta (query)
| Nome | Tipo | Obrigatório | Descrição |
|---|
search | string | Não | Busca em nome, telefone, e-mail e NIF/CIF (mínimo de 2 caracteres). |
expand | string [vehicles] | Não | Relações opcionais a incluir. |
limit | integer · default 50 | Não | Resultados por página (1–200). |
cursor | string | Não | Cursor opaco devolvido em next_cursor da página anterior. |
Exemplo
curl "https://sua-oficina.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
Criar um cliente
Nunca mescla com um cadastro existente: se o NIF/CIF, o e-mail ou o telefone já estiverem em outro cliente, responde 409 client_exists com existing_id (ou 200 com esse cliente se você passar ?on_conflict=return_existing). O telefone é salvo como no cadastro (Espanha com 9 dígitos, outros países com o código do país) e o e-mail em minúsculas; erros de digitação não são corrigidos. As recusas de marketing ficam no registro de consentimentos.
Permissão necessária: clients:write.
Consulta (query)
| Nome | Tipo | Obrigatório | Descrição |
|---|
on_conflict | string [error, return_existing] · default error | Não | error (padrão): 409 se já existir. return_existing: 200 com o cliente existente. |
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Sim | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo
{
"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
}
}
Exemplo
curl -X POST "https://sua-oficina.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
Detalhe de um cliente
Permissão necessária: clients:read.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do cliente. |
Consulta (query)
| Nome | Tipo | Obrigatório | Descrição |
|---|
expand | string [vehicles] | Não | Relações opcionais a incluir. |
Exemplo
curl "https://sua-oficina.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 um cliente
Só mudam os campos enviados (null limpa o campo). É possível mudar e-mail ou telefone, e fica o valor anterior → novo na atividade do cliente com o nome da chave; emite client.updated. Não é possível usar o e-mail, telefone ou NIF de OUTRO cadastro (409 client_exists). Cadastros apagados a pedido (RGPD) respondem 409 client_erased.
Permissão necessária: clients:write.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do cliente. |
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Não | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo
{
"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
}
}
Exemplo
curl -X PATCH "https://sua-oficina.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"
}
]
}
Veículos
GET /api/v1/vehicles vehicles:read
Listar veículos
Inclui a última quilometragem registrada e o vencimento da inspeção técnica (ITV).
Permissão necessária: vehicles:read.
Consulta (query)
| Nome | Tipo | Obrigatório | Descrição |
|---|
plate | string | Não | Placa exata (espaços e hifens são ignorados). |
client_id | integer | Não | Filtrar por cliente. |
status | string [activo, baja_temporal, baja] | Não | Status do veículo. |
limit | integer · default 50 | Não | Resultados por página (1–200). |
cursor | string | Não | Cursor opaco devolvido em next_cursor da página anterior. |
Exemplo
curl "https://sua-oficina.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
Cadastrar um veículo
Sempre em nome de um cliente existente (client_id). A placa é normalizada (maiúsculas, sem espaços nem hifens). Se já estiver no cadastro de OUTRO cliente: 409 vehicle_belongs_to_other_client. Se o mesmo cliente já a tiver: 409 vehicle_exists com existing_id (ou 200 com ?on_conflict=return_existing).
Permissão necessária: vehicles:write.
Consulta (query)
| Nome | Tipo | Obrigatório | Descrição |
|---|
on_conflict | string [error, return_existing] · default error | Não | error (padrão) ou return_existing. |
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Sim | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo
{
"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"
}
Exemplo
curl -X POST "https://sua-oficina.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
Detalhe de um veículo
Permissão necessária: vehicles:read.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do veículo. |
Exemplo
curl "https://sua-oficina.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 um veículo
Alterações parciais. client_id não aceita null: um veículo nunca é desvinculado do proprietário pela API (mas pode passar para outro cliente existente).
Permissão necessária: vehicles:write.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do veículo. |
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Não | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo
{
"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"
}
Exemplo
curl -X PATCH "https://sua-oficina.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"
}
Orçamentos
GET /api/v1/budgets budgets:read
Listar orçamentos
Nunca inclui os orçamentos de uso interno da oficina nem dados de custo. O detalhe (com itens e totais) está em /budgets/{id}.
Permissão necessária: budgets:read.
Consulta (query)
| Nome | Tipo | Obrigatório | Descrição |
|---|
status | string | Não | Status exato, com o valor em espanhol (Pendiente, Enviado, Aprobado, Finalizado, Facturado, Rechazado…). |
client_id | integer | Não | Filtrar por cliente. |
vehicle_id | integer | Não | Filtrar por veículo. |
from | string (date-time) | Não | Criados a partir desta data. |
to | string (date-time) | Não | Criados até esta data. |
updated_since | string (date-time) | Não | Só registros modificados a partir desta data (ISO 8601). |
limit | integer · default 50 | Não | Resultados por página (1–200). |
cursor | string | Não | Cursor opaco devolvido em next_cursor da página anterior. |
Exemplo
curl "https://sua-oficina.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,
"delivered_at": null,
"reject_reason": null,
"tracking_url": "https://taller.ejemplo.com/public/seguimiento?id=…"
}
],
"next_cursor": "aWQ6MTIzNA",
"has_more": true
}
POST /api/v1/budgets budgets:write
Criar um orçamento
Entra como «Pendiente» com canal «API», igual a um cadastro feito no programa (registro, marco de abertura, webhook lead.created). Os totais são calculados no servidor com o imposto da oficina; se um item não trouxer tax_rate, usa o da oficina. O veículo, se informado, deve ser do cliente. Não aceita categorias de uso interno. Com notify_client=true (e permissão communications:send) o cliente recebe a confirmação com o link de acompanhamento.
Permissão necessária: budgets:write.
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Sim | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo
{
"client_id": 1204,
"vehicle_id": 871,
"category_id": 5,
"subcategory_id": 51,
"client_reference": "PED-2026-118",
"public_notes": "Verificar também o ruído da suspensão.",
"lines": [
{
"description": "Troca de óleo e filtro",
"quantity": 1,
"unit_price": 65,
"tax_rate": 21,
"discount_pct": 0,
"line_type": "labor",
"reference": null,
"group_title": null
}
],
"notify_client": false
}
Exemplo
curl -X POST "https://sua-oficina.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":"Verificar também o ruído da suspensão.","lines":[{"description":"Troca de óleo e filtro","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,
"delivered_at": null,
"reject_reason": null,
"tracking_url": "https://taller.ejemplo.com/public/seguimiento?id=…",
"public_notes": "Verificar também o ruído na suspensão.",
"totals": {
"base": 65,
"tax": 13.65,
"total": 78.65,
"currency": "EUR",
"tax_rates": [
{
"rate": 21,
"base": 65,
"quota": 13.65
}
]
},
"lines": [
{
"id": 5501,
"description": "Troca de óleo e filtro",
"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
Detalhe de um orçamento
Itens (descrição, quantidade, preço unitário, imposto, tipo, desconto), totais detalhados, agendamento, datas de entrada e saída, responsável e URL pública de acompanhamento.
Permissão necessária: budgets:read.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do orçamento. |
Exemplo
curl "https://sua-oficina.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,
"delivered_at": null,
"reject_reason": null,
"tracking_url": "https://taller.ejemplo.com/public/seguimiento?id=…",
"public_notes": "Verificar também o ruído na suspensão.",
"totals": {
"base": 65,
"tax": 13.65,
"total": 78.65,
"currency": "EUR",
"tax_rates": [
{
"rate": 21,
"base": 65,
"quota": 13.65
}
]
},
"lines": [
{
"id": 5501,
"description": "Troca de óleo e filtro",
"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}/lines budgets:write
Adicionar um item
Os demais itens mantêm o id. Deixa um instantâneo anterior e registro como qualquer edição no programa. 409 budget_locked se o orçamento estiver Facturado, Facturado externamente, Cancelado, Rechazado, Desistido ou já tiver fatura.
Permissão necessária: budgets:write.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do orçamento. |
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Sim | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo
{
"description": "Troca de óleo e filtro",
"quantity": 1,
"unit_price": 65,
"tax_rate": 21,
"discount_pct": 0,
"line_type": "labor",
"reference": null,
"group_title": null
}
Exemplo
curl -X POST "https://sua-oficina.example/api/v1/budgets/1234/lines" \
-H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"description":"Troca de óleo e filtro","quantity":1,"unit_price":65,"tax_rate":21,"discount_pct":0,"line_type":"labor","reference":null,"group_title":null}'
Resposta
{
"line": {
"id": 5501,
"description": "Troca de óleo e filtro",
"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,
"delivered_at": null,
"reject_reason": null,
"tracking_url": "https://taller.ejemplo.com/public/seguimiento?id=…",
"public_notes": "Verificar também o ruído na suspensão.",
"totals": {
"base": 65,
"tax": 13.65,
"total": 78.65,
"currency": "EUR",
"tax_rates": [
{
"rate": 21,
"base": 65,
"quota": 13.65
}
]
},
"lines": [
{
"id": 5501,
"description": "Troca de óleo e filtro",
"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 um item
Permissão necessária: budgets:write.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do orçamento. |
lineId | integer | Sim | Id do item. |
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Não | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo
{
"description": "Troca de óleo e filtro",
"quantity": 1,
"unit_price": 65,
"tax_rate": 21,
"discount_pct": 0,
"line_type": "labor",
"reference": null,
"group_title": null
}
Exemplo
curl -X PATCH "https://sua-oficina.example/api/v1/budgets/1234/lines/5501" \
-H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"description":"Troca de óleo e filtro","quantity":1,"unit_price":65,"tax_rate":21,"discount_pct":0,"line_type":"labor","reference":null,"group_title":null}'
Resposta
{
"line": {
"id": 5501,
"description": "Troca de óleo e filtro",
"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,
"delivered_at": null,
"reject_reason": null,
"tracking_url": "https://taller.ejemplo.com/public/seguimiento?id=…",
"public_notes": "Verificar também o ruído na suspensão.",
"totals": {
"base": 65,
"tax": 13.65,
"total": 78.65,
"currency": "EUR",
"tax_rates": [
{
"rate": 21,
"base": 65,
"quota": 13.65
}
]
},
"lines": [
{
"id": 5501,
"description": "Troca de óleo e filtro",
"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
Remover um item
Devolve o orçamento com os totais recalculados. O item fica no instantâneo anterior do histórico de versões.
Permissão necessária: budgets:write.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do orçamento. |
lineId | integer | Sim | Id do item. |
Exemplo
curl -X DELETE "https://sua-oficina.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,
"delivered_at": null,
"reject_reason": null,
"tracking_url": "https://taller.ejemplo.com/public/seguimiento?id=…",
"public_notes": "Verificar também o ruído na suspensão.",
"totals": {
"base": 65,
"tax": 13.65,
"total": 78.65,
"currency": "EUR",
"tax_rates": [
{
"rate": 21,
"base": 65,
"quota": 13.65
}
]
},
"lines": [
{
"id": 5501,
"description": "Troca de óleo e filtro",
"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
Alterar o status
Aceita Pendiente/En cotización/Enviado (antes da aprovação), En curso (orçamento já aprovado), Finalizado (a partir de Aprobado, En curso ou En espera) e Cancelado. «Aprobado» responde 403 client_acceptance_required com a tracking_url: quem assina a aprovação é o cliente. Faturar, recusar ou desistir respondem 403 status_transition_forbidden; a partir de Facturado ou Cancelado, 409 budget_locked. «Enviado» exige também communications:send e sent_via: registra que o SEU sistema já enviou (não envia). Nenhuma mudança avisa o cliente, exceto Finalizado com notify_client=true e communications:send.
Permissão necessária: budgets:write.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do orçamento. |
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Sim | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo
{
"status": "En curso",
"sent_via": "email",
"notify_client": false
}
Exemplo
curl -X POST "https://sua-oficina.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,
"delivered_at": null,
"reject_reason": null,
"tracking_url": "https://taller.ejemplo.com/public/seguimiento?id=…",
"public_notes": "Verificar também o ruído na suspensão.",
"totals": {
"base": 65,
"tax": 13.65,
"total": 78.65,
"currency": "EUR",
"tax_rates": [
{
"rate": 21,
"base": 65,
"quota": 13.65
}
]
},
"lines": [
{
"id": 5501,
"description": "Troca de óleo e filtro",
"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}/documents budgets:write
Anexar um documento
multipart/form-data com o campo «file» (JPEG, PNG, WebP ou PDF, verificado pelo conteúdo; máximo de 4 MB). Por padrão só a oficina vê; client_visible=true mostra no link de acompanhamento e mechanic_visible=true no app do mecânico. A impressão de idempotência inclui o arquivo.
Permissão necessária: budgets:write.
Parâmetros
| Nome | Tipo | Obrigatório | Descrição |
|---|
id | integer | Sim | Id do orçamento. |
Cabeçalhos
| Nome | Tipo | Obrigatório | Descrição |
|---|
Idempotency-Key | string | Sim | Chave única por operação (UUID recomendado). Repeti-la com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo, 409 idempotency_conflict. |
Corpo (multipart/form-data)
| Nome | Tipo | Obrigatório | Descrição |
|---|
file | string (binary) | Sim | Fichero |
client_visible | string [true, false] | Não | |
mechanic_visible | string [true, false] | Não | |
Exemplo
curl -X POST "https://sua-oficina.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"
}