P Promec
ENESPT
▶ Demo 3 meses por 1 €
API REST v1

Referência da API

Todos os endpoints da versão 1 com parâmetros, exemplos de requisição e resposta, permissões, erros e eventos de webhook. Alguns valores (como os status de orçamento) são do sistema e aparecem em espanhol.

URL baseCada oficina tem a API no próprio domínio: https://<seu-dominio>/api/v1. A URL da sua oficina aparece em «Desenvolvedores / API»; nos exemplos usamos https://sua-oficina.example.

Autenticação

Envie a chave no cabeçalho Authorization: Bearer pt_live_… (ou X-Api-Key). Chaves pt_test_… podem ler, mas qualquer operação com efeitos responde 403 test_key_forbidden.

Convenções

Datas em ISO 8601 UTC; valores em euros com duas casas decimais e currency: "EUR"; ids inteiros; nomes de campo estáveis em snake_case. A equipe da oficina aparece só como { id, name }.

Paginação

As listagens devolvem { data, next_cursor, has_more }. Peça a próxima página repetindo a chamada com ?cursor=<next_cursor>. limit vai de 1 a 200 (50 por padrão).

Formato de erro

{
  "error": {
    "code": "insufficient_scope",
    "message": "A chave de API não tem a permissão «budgets:read»."
  }
}
HTTPCódigoMensagem
401missing_api_keyFalta a chave de API: envie no cabeçalho Authorization: Bearer pt_… ou X-Api-Key.
401invalid_api_keyA chave de API não é válida para esta instância.
401revoked_api_keyA chave de API foi revogada.
401expired_api_keyA chave de API expirou.
403ip_not_allowedO endereço IP de origem não está na lista permitida desta chave.
403insufficient_scopeA chave de API não tem a permissão necessária para esta operação.
403test_key_forbiddenUma chave de teste (pt_test_) não pode fazer operações com efeitos: use uma chave live.
429rate_limitedVocê excedeu o limite de requisições desta chave. Aguarde e tente de novo.
503instance_rate_limitedA instância está recebendo requisições demais pela API neste momento. Tente de novo em alguns segundos.
429plan_quota_exceededA cota diária de requisições do plano de API da oficina acabou. Ela renova às 00:00 UTC; para mais volume, mude de plano.
403plan_scope_not_allowedO plano de API da oficina não inclui esta permissão. Para usá-la é preciso mudar de plano.
400bad_requestA requisição não é válida.
404not_foundRecurso não encontrado.
502upstream_errorUm serviço externo recusou a operação.
500internal_errorErro interno.
422validation_errorO corpo da requisição não é válido: confira a lista «fields».
400idempotency_key_requiredFalta o cabeçalho Idempotency-Key (obrigatório em todo POST; de 8 a 255 caracteres visíveis).
409idempotency_conflictEssa Idempotency-Key já foi usada nas últimas 24 h com outra requisição.
409idempotency_in_progressHá outra requisição com a mesma Idempotency-Key em andamento. Tente de novo em alguns segundos.
409client_existsJá existe um cliente com esse telefone, e-mail ou NIF/CIF.
409client_erasedO cliente pediu a exclusão dos seus dados (RGPD): o cadastro não aceita alterações nem registros vinculados.
409vehicle_existsEsse cliente já tem um veículo com essa placa.
409vehicle_belongs_to_other_clientEssa placa já está cadastrada em nome de outro cliente.
409budget_lockedO orçamento está fechado e não aceita mais essa alteração.
409invalid_status_transitionO orçamento não pode passar para esse status a partir do atual.
403status_transition_forbiddenEssa mudança de status não está disponível pela API.
403client_acceptance_requiredA aprovação do orçamento precisa ser feita pelo cliente no link de acompanhamento assinado.
403booking_mode_propose_onlyA oficina trabalha no modo «propor agendamento»: só é possível criar propostas que a oficina confirma.
409slot_unavailableEsse horário não está disponível.
413payload_too_largeO arquivo excede o tamanho máximo (4 MB).
415unsupported_media_typeTipo de arquivo não aceito: só JPEG, PNG, WebP ou PDF.

Escritas e idempotência

Todo POST exige o cabeçalho Idempotency-Key (um UUID novo por operação). Repetir a mesma chave com o mesmo corpo em 24 h devolve a resposta guardada com Idempotent-Replay: true; com outro corpo responde 409 idempotency_conflict. Os corpos são validados contra o esquema e qualquer campo desconhecido é rejeitado: 422 validation_error com a lista fields (path e message).

Guias

Guia: criar um orçamento e propor um agendamento

Fluxo típico de um CRM ou de um site de agendamento. Todas as requisições POST levam Idempotency-Key (um UUID novo por operação; repita-o só ao tentar de novo a mesma). Você precisa de uma chave live com clients:write, vehicles:write, budgets:write, appointments:read e appointments:write.

1. Cliente: criar ou recuperar o existente

POST /api/v1/clients

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

Com ?on_conflict=return_existing, se o telefone, o e-mail ou o NIF já existirem, você recebe esse cliente (200) em vez de 409.

2. Veículo do cliente

POST /api/v1/vehicles

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

Se a placa for de outro cliente: 409 vehicle_belongs_to_other_client (a oficina decide).

3. Orçamento com seus itens

POST /api/v1/budgets

{ "client_id": 1204, "vehicle_id": 871, "lines": [{ "description": "Troca de óleo e filtro", "quantity": 1, "unit_price": 65 }] }

A resposta traz os totais calculados e tracking_url: compartilhe com o cliente para que ele aprove e assine.

4. Horários livres

GET /api/v1/appointments/availability

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

5. Propor o agendamento

POST /api/v1/appointments

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

No modo «propor» fica status=proposed até a oficina confirmar (você recebe appointment.confirmed por webhook). Se o horário já foi ocupado: 409 slot_unavailable com alternativas.

Permissões (scopes)

PermissãoNomeCom efeitosDescrição
clients:readLer clientesNãoConsultar cadastros de clientes e seus dados de contato.
clients:writeCriar e editar clientesSimCadastrar clientes novos e alterar os existentes.
vehicles:readLer veículosNãoConsultar veículos, placas e seu histórico.
vehicles:writeCriar e editar veículosSimCadastrar veículos e alterar seus dados.
budgets:readLer orçamentosNãoConsultar orçamentos, seus itens e status.
budgets:writeCriar e editar orçamentosSimCriar orçamentos, adicionar itens e mudar o status.
invoices:readLer faturasNãoConsultar faturas emitidas, valores e recebimentos. Emitir faturas não está disponível pela API.
appointments:readLer agendamentosNãoConsultar a agenda e os horários disponíveis.
appointments:writeAgendar e cancelarSimCriar, mover e cancelar agendamentos na agenda.
communications:readLer comunicaçõesNãoConsultar o registro de e-mails, SMS, WhatsApp e ligações (inclui o conteúdo completo).
communications:sendEnviar e-mail e SMSSimEnviar e-mails e SMS transacionais pela instância; consome saldo.
catalog:readLer catálogoNãoConsultar serviços, tarifas e itens da tabela de preços.
stock:readLer estoqueNãoConsultar estoque e códigos de peças.
stock:writeAjustar estoqueSimLançar entradas e saídas de peças.
webhooks:manageGerenciar webhooksSimCriar, listar e excluir as assinaturas de eventos desta chave.
reports:readLer relatóriosNãoConsultar números agregados de faturamento, atividade e desempenho.

Geral

GET /api/v1/ping Qualquer chave

Testar a conexão

Retorna o nome da chave, o ambiente, as permissões e o estado dos limites. Qualquer chave válida serve.

Qualquer chave válida, sem permissão específica.

Exemplo

curl "https://sua-oficina.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
  },
  "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 Qualquer chave

Dados públicos da oficina

Nome, razão social, CIF (identificação fiscal), endereço, contato, horário semanal da agenda, modo de agendamento, próximos feriados e fuso horário.

Qualquer chave válida, sem permissão específica.

Exemplo

curl "https://sua-oficina.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 Sem autenticação

Especificação OpenAPI 3.1

Arquivo gerado a partir deste mesmo catálogo. Sem autenticação. Aceita ?lang=es|en|ca|pt|fr|bg para os textos.

Sem autenticação.

Consulta (query)

NomeTipoObrigatórioDescrição
langstring [es, en, ca, pt, fr, bg] · default esNãoIdioma das descrições.

Exemplo

curl "https://sua-oficina.example/api/v1/openapi.json"

Resposta

{}

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)

NomeTipoObrigatórioDescrição
searchstringNãoBusca em nome, telefone, e-mail e NIF/CIF (mínimo de 2 caracteres).
expandstring [vehicles]NãoRelações opcionais a incluir.
limitinteger · default 50NãoResultados por página (1–200).
cursorstringNãoCursor 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)

NomeTipoObrigatórioDescrição
on_conflictstring [error, return_existing] · default errorNãoerror (padrão): 409 se já existir. return_existing: 200 com o cliente existente.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringSimChave ú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

NomeTipoObrigatórioDescrição
idintegerSimId do cliente.

Consulta (query)

NomeTipoObrigatórioDescrição
expandstring [vehicles]NãoRelaçõ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

NomeTipoObrigatórioDescrição
idintegerSimId do cliente.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringNãoChave ú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)

NomeTipoObrigatórioDescrição
platestringNãoPlaca exata (espaços e hifens são ignorados).
client_idintegerNãoFiltrar por cliente.
statusstring [activo, baja_temporal, baja]NãoStatus do veículo.
limitinteger · default 50NãoResultados por página (1–200).
cursorstringNãoCursor 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)

NomeTipoObrigatórioDescrição
on_conflictstring [error, return_existing] · default errorNãoerror (padrão) ou return_existing.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringSimChave ú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

NomeTipoObrigatórioDescrição
idintegerSimId 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

NomeTipoObrigatórioDescrição
idintegerSimId do veículo.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringNãoChave ú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)

NomeTipoObrigatórioDescrição
statusstringNãoStatus exato, com o valor em espanhol (Pendiente, Enviado, Aprobado, Finalizado, Facturado, Rechazado…).
client_idintegerNãoFiltrar por cliente.
vehicle_idintegerNãoFiltrar por veículo.
fromstring (date-time)NãoCriados a partir desta data.
tostring (date-time)NãoCriados até esta data.
updated_sincestring (date-time)NãoSó registros modificados a partir desta data (ISO 8601).
limitinteger · default 50NãoResultados por página (1–200).
cursorstringNãoCursor 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

NomeTipoObrigatórioDescrição
Idempotency-KeystringSimChave ú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

NomeTipoObrigatórioDescrição
idintegerSimId 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

NomeTipoObrigatórioDescrição
idintegerSimId do orçamento.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringSimChave ú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

NomeTipoObrigatórioDescrição
idintegerSimId do orçamento.
lineIdintegerSimId do item.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringNãoChave ú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

NomeTipoObrigatórioDescrição
idintegerSimId do orçamento.
lineIdintegerSimId 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

NomeTipoObrigatórioDescrição
idintegerSimId do orçamento.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringSimChave ú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

NomeTipoObrigatórioDescrição
idintegerSimId do orçamento.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringSimChave ú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)

NomeTipoObrigatórioDescrição
filestring (binary)SimFichero
client_visiblestring [true, false]Não
mechanic_visiblestring [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"
}

Faturas

GET /api/v1/invoices invoices:read

Listar faturas

Faturas emitidas e rascunhos, ordenados por id. Inclui o resumo de recebimentos.

Permissão necessária: invoices:read.

Consulta (query)

NomeTipoObrigatórioDescrição
fromstring (date-time)NãoData da fatura a partir de.
tostring (date-time)NãoData da fatura até.
client_idintegerNãoFiltrar por cliente.
statusstring [draft, issued, cancelled]NãoStatus da fatura.
seriesstringNãoSérie exata.
limitinteger · default 50NãoResultados por página (1–200).
cursorstringNãoCursor opaco devolvido em next_cursor da página anterior.

Exemplo

curl "https://sua-oficina.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…"
    }
  ],
  "next_cursor": "aWQ6MTIzNA",
  "has_more": true
}

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

Detalhe de uma fatura

Permissão necessária: invoices:read.

Parâmetros

NomeTipoObrigatórioDescrição
idintegerSimId da fatura.

Exemplo

curl "https://sua-oficina.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…",
  "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": "Troca de óleo e filtro",
      "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 de uma fatura

O mesmo PDF que o programa gera (com QR Verifactu se a fatura estiver emitida). Resposta application/pdf.

Permissão necessária: invoices:read.

Parâmetros

NomeTipoObrigatórioDescrição
idintegerSimId da fatura.

Exemplo

curl "https://sua-oficina.example/api/v1/invoices/1234/pdf" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

(application/pdf)

Agendamentos

GET /api/v1/appointments appointments:read

Agendamentos confirmados e propostas

Por padrão, os próximos 30 dias. status=confirmed são agendamentos fixados na agenda; status=proposed são propostas do cliente esperando a confirmação da oficina (bloqueiam o horário).

Permissão necessária: appointments:read.

Consulta (query)

NomeTipoObrigatórioDescrição
fromstring (date-time)NãoInício do intervalo (padrão: agora).
tostring (date-time)NãoFim do intervalo (padrão: +30 dias, máximo 1 ano).
statusstring [confirmed, proposed]NãoSó um tipo.
box_idintegerNãoFiltrar por elevador/box.

Exemplo

curl "https://sua-oficina.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

Propor ou confirmar um agendamento

Segue o modo de agendamento da oficina (booking_mode em GET /workshop). Em «propose» é criada uma proposta (status=proposed) que bloqueia o horário até a oficina confirmar; pedir mode=book responde 403 booking_mode_propose_only. Em «book» o agendamento fica firme (status=confirmed), a menos que você peça mode=propose. O horário é validado com a mesma lógica de /appointments/availability; se não estiver livre, 409 slot_unavailable com até 3 alternativas em error.alternatives. O cliente não é avisado, exceto com notify_client=true e communications:send.

Permissão necessária: appointments:write.

Cabeçalhos

NomeTipoObrigatórioDescrição
Idempotency-KeystringSimChave ú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

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

Exemplo

curl -X POST "https://sua-oficina.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

Horários livres

Mesma lógica da agenda e do site de acompanhamento: horário da oficina e de cada box, almoço, feriados nacionais, regionais e locais, agendamentos abertos e propostas pendentes (que bloqueiam o horário). Inícios a cada 30 min e com pelo menos 1 h de antecedência. Padrão: os próximos 7 dias (máximo 31). Inclui o booking_mode da oficina.

Permissão necessária: appointments:read.

Consulta (query)

NomeTipoObrigatórioDescrição
fromstring (date-time)NãoDe (padrão: agora).
tostring (date-time)NãoAté (padrão: +7 dias; máximo 31 dias).
duration_minutesintegerNãoDuração do agendamento (15–720). Padrão: a mínima da agenda.
box_idintegerNãoSó esse box.
limitinteger · default 100NãoMáximo de horários (1–500).

Exemplo

curl "https://sua-oficina.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

Cancelar um agendamento ou retirar uma proposta

b<orçamento>: cancela o agendamento confirmado (igual a «Cancelar agendamento» no cadastro). p<proposta>: retira a proposta pendente; com notify_client=true e communications:send o cliente é convidado a escolher outro horário. Emite appointment.cancelled.

Permissão necessária: appointments:write.

Parâmetros

NomeTipoObrigatórioDescrição
idstringSimId do agendamento (b1234 ou p88).

Consulta (query)

NomeTipoObrigatórioDescrição
notify_clientboolean · default falseNãoSó propostas: avisar o cliente.

Exemplo

curl -X DELETE "https://sua-oficina.example/api/v1/appointments/b1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

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

Comunicações

GET /api/v1/communications communications:read

Registro de comunicações

Últimas comunicações (e-mail, SMS, WhatsApp, ligações, push) com o conteúdo completo, da mais recente para a mais antiga.

Permissão necessária: communications:read.

Consulta (query)

NomeTipoObrigatórioDescrição
channelstring [all, email, sms, whatsapp, call, push] · default allNãoCanal.
limitinteger · default 50NãoMáximo de resultados (1–200).

Exemplo

curl "https://sua-oficina.example/api/v1/communications" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "data": [
    {
      "id": "c_8812",
      "channel": "email",
      "direction": "out",
      "recipient": "laura@ejemplo.com",
      "subject": "Seu orçamento",
      "body": "Olá, 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

Registro de comunicações (alias antigo)

Mesma consulta de /communications, mas devolve { items }. Mantido por compatibilidade; use /communications.

Permissão necessária: communications:read.

Consulta (query)

NomeTipoObrigatórioDescrição
channelstring [all, email, sms, whatsapp, call, push] · default allNãoCanal.
limitinteger · default 50NãoMáximo de resultados (1–200).

Exemplo

curl "https://sua-oficina.example/api/v1/logs" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "items": [
    {
      "id": "c_8812",
      "channel": "email",
      "direction": "out",
      "recipient": "laura@ejemplo.com",
      "subject": "Seu orçamento",
      "body": "Olá, 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 um email transacional

Sai pela conta de e-mail configurada na oficina e fica no registro de Comunicação. Se voltar, o e-mail do cliente é marcado como inválido.

Permissão necessária: communications:send.

Corpo

{
  "to": "cliente@ejemplo.com",
  "subject": "Seu veículo está pronto",
  "text": "Você já pode retirá-lo.",
  "html": "<p>Você já pode retirá-lo.</p>",
  "fromName": "Taller",
  "replyTo": "taller@ejemplo.com"
}

Exemplo

curl -X POST "https://sua-oficina.example/api/v1/email" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"to":"cliente@ejemplo.com","subject":"Seu veículo está pronto","text":"Você já pode retirá-lo.","html":"<p>Você já pode retirá-lo.</p>","fromName":"Taller","replyTo":"taller@ejemplo.com"}'

Resposta

{
  "ok": true
}

POST /api/v1/sms communications:send

Enviar um SMS transacional

Sai pelo serviço de SMS configurado na oficina e fica no registro de Comunicação, onde o status de entrega é atualizado.

Permissão necessária: communications:send.

Corpo

{
  "to": "+34600111222",
  "body": "Seu veículo está pronto para retirada."
}

Exemplo

curl -X POST "https://sua-oficina.example/api/v1/sms" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"to":"+34600111222","body":"Seu veículo está pronto para retirada."}'

Resposta

{
  "ok": true
}

Webhooks

GET /api/v1/webhooks webhooks:manage

Listar os webhooks da chave

Inclui o catálogo de eventos disponíveis. Nunca devolve os segredos.

Permissão necessária: webhooks:manage.

Exemplo

curl "https://sua-oficina.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": "Orçamento aprovado",
      "description": "texto"
    }
  ]
}

POST /api/v1/webhooks webhooks:manage

Criar um webhook

O segredo de assinatura (whsec_…) só vem nesta resposta.

Permissão necessária: webhooks:manage.

Corpo

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

Exemplo

curl -X POST "https://sua-oficina.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

Detalhe de um webhook e últimas entregas

Permissão necessária: webhooks:manage.

Parâmetros

NomeTipoObrigatórioDescrição
idintegerSimId do webhook.

Exemplo

curl "https://sua-oficina.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 um webhook

Permissão necessária: webhooks:manage.

Parâmetros

NomeTipoObrigatórioDescrição
idintegerSimId do webhook.

Corpo

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

Exemplo

curl -X PATCH "https://sua-oficina.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

Excluir um webhook

Permissão necessária: webhooks:manage.

Parâmetros

NomeTipoObrigatórioDescrição
idintegerSimId do webhook.

Exemplo

curl -X DELETE "https://sua-oficina.example/api/v1/webhooks/1234" \
  -H "Authorization: Bearer pt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Resposta

{
  "ok": true
}

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

Enviar uma entrega de teste (test.ping) ao webhook

Permissão necessária: webhooks:manage.

Parâmetros

NomeTipoObrigatórioDescrição
idintegerSimId do webhook.

Exemplo

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

Resposta

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

Webhooks

Carga

{
  "event": "lead.accepted",
  "timestamp": "2026-10-06T10:15:00.000Z",
  "data": {
    "leadId": 1234,
    "from": "Enviado",
    "to": "Aprobado"
  }
}
EventoDescrição
lead.createdOrçamento criado
Um novo orçamento foi criado (pelo programa, pelo site, por e-mail ou pelo assistente).
lead.sentOrçamento enviado
O orçamento foi enviado ao cliente.
lead.acceptedOrçamento aprovado
O cliente ou a oficina aprovou o orçamento.
lead.rejectedOrçamento recusado
O orçamento foi recusado, com o motivo, se houver.
lead.status_changedMudança de status
Qualquer mudança de status do orçamento (inclui as anteriores).
appointment.proposedAgendamento proposto
Um cliente propõe um agendamento que a oficina ainda precisa confirmar.
appointment.confirmedAgendamento confirmado
Um agendamento fica confirmado na agenda.
appointment.cancelledAgendamento cancelado
O agendamento de um orçamento foi cancelado.
vehicle.checked_inVeículo recebido
O veículo entrou na oficina (recepção ou sem agendamento).
vehicle.readyVeículo pronto
O reparo terminou e o veículo está pronto para retirada.
vehicle.deliveredVeículo entregue
O cliente retirou o veículo.
invoice.issuedFatura emitida
Uma fatura foi emitida com número definitivo.
payment.receivedRecebimento registrado
Um recebimento foi lançado em uma fatura.
client.createdCliente criado
Um cliente foi cadastrado.
client.updatedCliente atualizado
Os dados de um cliente foram alterados.
communication.inboundMensagem recebida
Chegou um e-mail, SMS, WhatsApp ou ligação de um cliente.
email.sentE-mail enviado
Um e-mail foi enviado (campanhas, avisos ou API).
email.failedE-mail com falha
Não foi possível enviar um e-mail.
email.openedE-mail aberto
O destinatário abriu o e-mail.
email.clickedClique no e-mail
O destinatário clicou em um link do e-mail.
email.unsubscribedDescadastro de e-mail
O destinatário cancelou o recebimento de e-mails.
email.bouncedE-mail devolvido
O e-mail foi devolvido.
sms.sentSMS enviado
Um SMS foi enviado.
sms.failedSMS com falha
Não foi possível enviar um SMS.
sms.unsubscribedDescadastro de SMS
O destinatário pediu para não receber SMS.
whatsapp.unsubscribedDescadastro de WhatsApp
O destinatário pediu para não receber WhatsApp.
campaign.finishedCampanha concluída
Uma campanha terminou de ser enviada.
3 meses por 1 € →