API v1 · REST · JSON

Documentação de Integração

Integre seu sistema de estoque com o Veículos no Sul via endpoints REST, autenticação por API Key e webhooks em tempo real.

api.veiculosnosul.com.br·HTTPS · TLS 1.2+·UTC-3 (America/Sao_Paulo)

Introdução

A API do Veículos no Sul permite que sistemas externos — como softwares de gestão de estoque (DMS), portais parceiros e ERPs — sincronizem anúncios de veículos diretamente no portal, de forma automática e em tempo real.

A integração funciona por meio de chamadas REST com JSON. Cada loja possui uma API Key própria, gerada no painel do revendedor. Com ela, é possível criar, atualizar e remover veículos, além de receber notificações via webhook.

Passo a passo para começar

01

Gere sua API Key

Acesse Configurações → Integração e crie uma nova chave. Copie-a imediatamente — ela só é exibida uma vez.

02

Construa o payload do veículo

Monte o JSON com os campos obrigatórios (externalId, brand, model, year, price) e os opcionais desejados.

03

Chame o endpoint de sync

Envie um POST para /integration/vehicles/sync. O sistema criará ou atualizará o veículo automaticamente.

04

(Opcional) Configure webhooks

Registre uma URL no painel para receber notificações em tempo real de eventos sobre seus veículos.

Autenticação

Todas as requisições devem ser autenticadas via API Key enviada no header X-API-Key.

http
X-API-Key: cds_live_sua_chave_de_32_caracteres_aqui

Nunca exponha sua API Key em código frontend, repositórios públicos ou logs. Trate-a como uma senha. Se comprometida, revogue e gere uma nova no painel imediatamente.

Como gerar sua API Key

01

Acesse o painel da loja e vá em Configurações → Integração.

02

Clique em Nova chave e defina um nome descritivo (ex: "Sistema ERP", "Integração DMS").

03

Copie a chave exibida imediatamente — ela é exibida apenas uma vez por segurança.

04

Armazene em variável de ambiente e use no header X-API-Key de cada requisição.

Formato da chave

cds_live_[32 caracteres a-z0-9]

Ex: cds_live_k8mnp2xw...

Características

  • Múltiplas chaves por loja
  • Nomes descritivos por sistema
  • Revogação instantânea
  • Log de uso detalhado

Endpoints

Envia um único veículo. Se o externalId já existir para a sua loja, o veículo será atualizado; caso contrário, será criado. Imagens são baixadas, armazenadas e comparadas com as existentes automaticamente.

Rate limit: 100 req/min
Auth: X-API-Key obrigatório

Exemplo de requisição

bash (curl)
curl -X POST https://api.veiculosnosul.com.br/integration/vehicles/sync \
  -H "X-API-Key: cds_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "SKU-00123",
    "brand": "Toyota",
    "model": "Corolla",
    "year": 2022,
    "price": 149900,
    "km": 35000,
    "fuel": "FLEX",
    "transmission": "AUTOMATIC",
    "images": [
      { "url": "https://seusite.com.br/fotos/carro-123-1.jpg", "order": 0 }
    ]
  }'

Resposta de sucesso (201 Created)

json
{
  "success": true,
  "action": "created",
  "message": "Veículo criado com sucesso",
  "data": {
    "id": "clx9abc123def456",
    "externalId": "SKU-00123",
    "brand": "Toyota",
    "model": "Corolla",
    "year": 2022,
    "price": 149900,
    "status": "ACTIVE",
    "createdAt": "2026-04-13T10:30:00.000Z"
  }
}
Idempotência: Se o payload for idêntico ao último envio, a resposta incluirá skipped: true e reason: "Conteúdo idêntico (idempotência)". Nenhuma alteração é feita no banco e nenhum webhook é disparado.

Envia múltiplos veículos de uma vez. Cada veículo é processado individualmente — falhas em um item não interrompem os demais. Ideal para sincronização periódica do estoque completo.

Rate limit: 10 req/min
Recomendado: até 50 veículos/request

Exemplo de requisição

bash (curl)
curl -X POST https://api.veiculosnosul.com.br/integration/vehicles/sync/batch \
  -H "X-API-Key: cds_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "vehicles": [
      {
        "externalId": "SKU-00123",
        "brand": "Toyota",
        "model": "Corolla",
        "year": 2022,
        "price": 149900
      },
      {
        "externalId": "SKU-00124",
        "brand": "Honda",
        "model": "Civic",
        "year": 2021,
        "price": 129900
      }
    ]
  }'

Resposta de sucesso (200 OK)

json
{
  "success": true,
  "message": "Sincronização em lote concluída",
  "data": {
    "total": 2,
    "created": 1,
    "updated": 1,
    "failed": 0,
    "results": [
      { "externalId": "SKU-00123", "action": "created", "success": true, "vehicleId": "clx9abc123def456" },
      { "externalId": "SKU-00124", "action": "updated", "success": true, "vehicleId": "clx9def789ghi012" }
    ]
  }
}

Remove permanentemente o veículo com o externalId fornecido, incluindo todas as suas imagens armazenadas. Esta operação é irreversível.

Atenção: A remoção é permanente. As imagens também são apagadas do storage. Não há como desfazer esta ação.

Exemplo de requisição

bash (curl)
curl -X DELETE https://api.veiculosnosul.com.br/integration/vehicles/sync/SKU-00123 \
  -H "X-API-Key: cds_live_sua_chave_aqui"

Resposta de sucesso (200 OK)

json
{ "success": true, "message": "Veículo removido com sucesso" }

Schema do Veículo

Abaixo estão todos os campos aceitos no payload do veículo. Campos marcados com
obrigatório
são necessários para criar o veículo.
CampoTipoDescrição
externalId
obrigatório
stringID único do veículo no seu sistema (até 100 chars). Usado para identificar CREATE vs UPDATE.
brand
obrigatório
stringMarca do veículo. Ex: "Toyota", "Volkswagen".
model
obrigatório
stringModelo do veículo. Ex: "Corolla", "Gol".
year
obrigatório
integerAno do veículo. Mínimo: 1900. Máximo: ano atual + 1.
price
obrigatório
numberPreço em reais (sem centavos). Ex: 149900. Máximo: R$ 10.000.000.
versionstringVersão/trim do veículo. Ex: "XEi 2.0 Flex".
modelYearintegerAno de fabricação, se diferente do ano do modelo. Não pode ser menor que year.
vehicleTypeenumTipo: CAR | MOTORCYCLE | TRUCK. Padrão: CAR.
colorstringCor do veículo. Ex: "Prata", "Preto Pérola".
kmintegerQuilometragem do veículo.
fuelenumGASOLINE | ETHANOL | FLEX | DIESEL | ELECTRIC | HYBRID | GNV.
transmissionenumMANUAL | AUTOMATIC | AUTOMATED | CVT.
doorsintegerNúmero de portas. Entre 2 e 6.
engineSizestringPotência do motor. Ex: "1.0", "2.0".
titlestringTítulo do anúncio (até 200 chars). Gerado automaticamente se omitido.
descriptionstringDescrição detalhada (até 5000 chars).
platestringPlaca do veículo (não exibida publicamente).
licensePlatestringPlaca de licenciamento (campo alternativo a plate, também não exibido publicamente).
vinstringNúmero do chassi (VIN). Deve ser único por loja — erro 400 se duplicado.
featuresstring[]Array de strings com características livres. Ex: ["Único dono", "Manual e chave reserva"].
externalUrlstring (URL)URL do veículo no seu sistema externo (para referência).
webhookUrlstring (URL)URL de webhook específica para esta requisição. Sobrescreve a URL configurada na loja apenas para este sync.
branchIdstring (UUID)ID da filial onde o veículo está localizado. Consulte sua equipe de suporte para obter os UUIDs das filiais da sua loja.
imagesVehicleImage[]Array de imagens (ver seção Imagens).
flagsVehicleFlag[]Array de opcionais/equipamentos (ver seção Opcionais).

Webhook por requisição

O campo webhookUrl permite sobrescrever a URL de webhook configurada na loja apenas para aquele sync específico. Útil quando diferentes sistemas ou filas precisam de endpoints de callback distintos.

Exemplo completo do payload

json
{
  "externalId": "SKU-00123",
  "brand": "Toyota",
  "model": "Corolla",
  "version": "XEi 2.0 Flex",
  "year": 2022,
  "modelYear": 2021,
  "price": 149900,
  "vehicleType": "CAR",
  "color": "Prata",
  "km": 35000,
  "fuel": "FLEX",
  "transmission": "AUTOMATIC",
  "doors": 4,
  "engineSize": "2.0",
  "vin": "9BWZZZ377VT004251",
  "plate": "ABC-1234",
  "description": "Veículo em excelente estado, único dono, manual e chave reserva.",
  "title": "Toyota Corolla XEi 2022 - Impecável",
  "features": ["Único dono", "Manual e chave reserva", "IPVA 2026 pago"],
  "externalUrl": "https://seusite.com.br/veiculos/corolla-sku-00123",
  "webhookUrl": "https://seusite.com.br/webhook/veiculos",
  "images": [
    { "url": "https://seusite.com.br/fotos/carro-123-1.jpg", "order": 0 },
    { "url": "https://seusite.com.br/fotos/carro-123-2.jpg", "order": 1 }
  ],
  "flags": [
    { "type": "OPTIONAL", "name": "Ar Condicionado", "value": true },
    { "type": "OPTIONAL", "name": "Direção Elétrica", "value": true },
    { "type": "OPTIONAL", "name": "Vidros Elétricos", "value": true },
    { "type": "OPTIONAL", "name": "Trava Elétrica", "value": true },
    { "type": "OPTIONAL", "name": "Airbag", "value": true },
    { "type": "OPTIONAL", "name": "ABS", "value": true }
  ]
}

Imagens

Você não precisa fazer upload manual das imagens. Basta enviar as URLs públicas no array images — a API baixa, processa e armazena automaticamente.

Formatos aceitos

  • JPEG / JPG
  • PNG
  • WebP
  • GIF

Limites por imagem

  • Tamanho máximo: 10 MB
  • Tamanho mínimo: 1 KB
  • Download: timeout 30s, 3 tentativas
  • URL deve ser publicamente acessível
json
"images": [
  { "url": "https://seusite.com.br/fotos/carro-123-1.jpg", "order": 0 },
  { "url": "https://seusite.com.br/fotos/carro-123-2.jpg", "order": 1 },
  { "url": "https://seusite.com.br/fotos/carro-123-3.jpg", "order": 2 }
]

Comportamento no update

  • Imagens novas → baixadas e adicionadas
  • Imagens existentes → mantidas (sem re-download)
  • Imagens removidas do array → apagadas do storage

Opcionais e Equipamentos

Opcionais são enviados via array flags. Cada item tem um type, um name e um value. A cada sync, as flags antigas são substituídas pelas novas.

json
"flags": [
  { "type": "OPTIONAL", "name": "Ar Condicionado",   "value": true },
  { "type": "OPTIONAL", "name": "Direção Elétrica",  "value": true },
  { "type": "OPTIONAL", "name": "Vidros Elétricos",  "value": true },
  { "type": "OPTIONAL", "name": "Teto Solar",         "value": false },
  { "type": "OPTIONAL", "name": "Câmera de Ré",      "value": true },
  { "type": "OPTIONAL", "name": "Sensor de Estac.",  "value": true },
  { "type": "OPTIONAL", "name": "Multimídia",         "value": true },
  { "type": "OPTIONAL", "name": "Bancos em Couro",   "value": false }
]

O campo type pode ser qualquer string. Recomendamos usar OPTIONAL para equipamentos padrão. O name é exibido diretamente no anúncio.

Webhooks

Configure uma URL de webhook no painel da sua loja para receber notificações em tempo real sobre eventos de sincronização de veículos via API. Os webhooks são disparados exclusivamente pelas operações de sync — não cobrem outras ações do portal como leads ou visualizações.

Entrega best-effort: webhooks de sync são disparados sem retry automático. Se o seu endpoint retornar erro ou estiver indisponível, o evento não será reenviado. Para maior confiabilidade, confirme o processamento consultando a API.

vehicle.createdVeículo criado com sucesso
vehicle.updatedVeículo atualizado (ou sem mudanças)
vehicle.failedFalha no sync (apenas batch)

Payload enviado ao seu endpoint

json
// vehicle.created
{
  "event": "vehicle.created",
  "externalId": "SKU-00123",
  "action": "created",
  "timestamp": "2026-04-13T10:30:00.000Z"
}

// vehicle.updated
{
  "event": "vehicle.updated",
  "externalId": "SKU-00123",
  "vehicleId": "clx9abc123def456",
  "action": "updated",
  "timestamp": "2026-04-13T10:31:00.000Z"
}

// vehicle.failed  (apenas em batch)
{
  "event": "vehicle.failed",
  "externalId": "SKU-00999",
  "error": "Preço inválido",
  "timestamp": "2026-04-13T10:32:00.000Z"
}

Verificando a assinatura

Cada requisição de webhook inclui o header X-Webhook-Signature com a assinatura HMAC-SHA256 do corpo. Sempre valide a assinatura antes de processar o evento.

javascript (Node.js)
import crypto from 'crypto';

// No seu servidor que recebe o webhook:
app.post('/webhook/veiculosnosul', (req, res) => {
  const sigHeader = req.headers['x-webhook-signature'];
  const rawBody   = JSON.stringify(req.body);

  // Extrair timestamp e assinatura do header (formato: t=<ts>,v1=<hex>)
  const parts     = Object.fromEntries(sigHeader.split(',').map(p => p.split('=')));
  const timestamp = parts['t'];
  const received  = parts['v1'];

  // Rejeitar mensagens com mais de 5 minutos (proteção contra replay)
  const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
  if (age > 300) return res.status(401).json({ error: 'Requisição expirada' });

  // Recriar a assinatura esperada: timestamp.rawBody
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  // Comparação segura contra timing attacks
  if (!crypto.timingSafeEqual(Buffer.from(received, 'hex'), Buffer.from(expected, 'hex'))) {
    return res.status(401).json({ error: 'Assinatura inválida' });
  }

  const { event, externalId, vehicleId, action, error } = req.body;

  if (event === 'vehicle.created') {
    console.log('Veículo criado:', externalId);
  } else if (event === 'vehicle.updated') {
    console.log('Veículo atualizado:', externalId, '| ação:', action);
  } else if (event === 'vehicle.failed') {
    console.error('Falha no sync:', externalId, '| erro:', error);
  }

  res.json({ received: true });
});

Leads — Recebimento na sua plataforma

Quando um usuário do portal demonstra interesse em um veículo da sua loja (clicando em “Entrar em contato”, WhatsApp ou enviando uma mensagem), o portal gera um lead e o encaminha para o seu sistema via webhook.

Para receber leads, é necessário ter uma URL de webhook e um Integration Token configurados no painel da loja em Configurações → Integração.

Payload do webhook de lead

Evento lead.created
Header X-Webhook-Signature (HMAC-SHA256)
3 tentativas com backoff (2s, 4s)
Timeout por tentativa: 10 segundos
json
{
  "event": "lead.created",
  "timestamp": "2026-04-13T10:30:00.000Z",
  "data": {
    "id": "clx9abc123def456",
    "name": "João Silva",
    "phone": "51999990000",
    "email": "joao@email.com",
    "message": "Tenho interesse nesse veículo. Ainda está disponível?",
    "origin": "whatsapp",
    "status": "NEW",
    "storeId": "uuid-da-loja",
    "vehicleId": "uuid-do-veiculo",
    "utmSource": "google",
    "utmMedium": "cpc",
    "utmCampaign": "carros-sul",
    "createdAt": "2026-04-13T10:30:00.000Z",
    "vehicle": {
      "id": "uuid-do-veiculo",
      "brand": "Toyota",
      "model": "Corolla",
      "version": "XEi 2.0 Flex",
      "year": 2022,
      "slug": "toyota-corolla-xei-2022"
    }
  }
}

Campos do lead

CampoTipoDescrição
idstringID único do lead no portal (UUID).
namestringNome do interessado.
phonestringTelefone do interessado.
emailstring | nullE-mail do interessado (opcional).
messagestring | nullMensagem enviada pelo interessado.
originstring | nullCanal de origem: whatsapp, form, phone, etc.
statusstringStatus do lead: NEW | READ | CONTACTED | QUALIFIED | CONVERTED | LOST.
storeIdstringID da loja dona do veículo.
vehicleIdstringID do veículo de interesse.
utmSourcestring | nullParâmetro UTM da campanha (source).
utmMediumstring | nullParâmetro UTM da campanha (medium).
utmCampaignstring | nullParâmetro UTM da campanha (name).
createdAtstring (ISO 8601)Data e hora de criação do lead.
vehicleobject | nullDados resumidos do veículo: id, brand, model, version, year, slug.

Verificando a assinatura do lead

O header X-Webhook-Signature usa HMAC-SHA256 com o seu Integration Token como chave secreta. O formato da assinatura é t=<unix_ts>,v1=<hex>.

javascript (Node.js)
import crypto from 'crypto';

app.post('/webhook/leads', (req, res) => {
  const sigHeader = req.headers['x-webhook-signature'];
  const rawBody   = JSON.stringify(req.body);

  // Extrair timestamp e assinatura do header (formato: t=<ts>,v1=<hex>)
  const parts     = Object.fromEntries(sigHeader.split(',').map(p => p.split('=')));
  const timestamp = parts['t'];
  const received  = parts['v1'];

  // Rejeitar mensagens com mais de 5 minutos (proteção contra replay)
  const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
  if (age > 300) return res.status(401).json({ error: 'Requisição expirada' });

  // Recriar a assinatura esperada: timestamp.rawBody
  const signed = crypto
    .createHmac('sha256', process.env.INTEGRATION_TOKEN)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  // Comparação segura contra timing attacks
  if (!crypto.timingSafeEqual(Buffer.from(received, 'hex'), Buffer.from(signed, 'hex'))) {
    return res.status(401).json({ error: 'Assinatura inválida' });
  }

  const { event, data } = req.body;
  if (event === 'lead.created') {
    console.log('Novo lead:', data.name, data.phone, '| Veículo:', data.vehicle?.model);
    // salvar no seu CRM / DMS aqui
  }

  res.json({ received: true });
});

O secret utilizado para assinar o webhook de leads é o Integration Token da loja (não confundir com a API Key usada no sync de veículos). Guarde-o em variável de ambiente.

Também recebemos por e-mail

Além do webhook, o portal envia automaticamente um e-mail de notificação para o endereço cadastrado na loja a cada novo lead, com os dados do interessado e um link direto para o veículo. Nenhuma configuração adicional é necessária para isso.

Rate Limits

EndpointLimiteJanelaRecomendação
POST /sync100 reqpor minutoUse batch para grandes volumes
POST /sync/batch10 reqpor minutoAté 50 veículos por request
DELETE /sync/:id100 reqpor minuto

Ao atingir o limite, a API retorna 429 Too Many Requests com o header Retry-After indicando quantos segundos aguardar.

Códigos de Erro

CódigoNomeDescrição
400Bad RequestPayload inválido ou campos obrigatórios ausentes.
401UnauthorizedAPI Key ausente, inválida ou expirada.
403ForbiddenA chave não tem permissão para esta operação.
404Not FoundVeículo com o externalId fornecido não encontrado.
429Too Many RequestsLimite de requisições excedido. Aguarde antes de tentar novamente.
500Internal Server ErrorErro interno. Tente novamente em alguns instantes.

Exemplo de resposta de erro

json
{
  "statusCode": 400,
  "message": [
    "externalId should not be empty",
    "price must be a number conforming to the specified constraints"
  ],
  "error": "Bad Request"
}

Ambiente de Homologação (Sandbox)

Antes de integrar em produção, você pode testar livremente usando uma API Key de teste (prefixo cds_test_). Chaves de teste aceitam os mesmos endpoints e payload da produção, mas os veículos criados ficam marcados internamente como sandbox e não impactam dados reais.

Produção

  • Chave: cds_live_...
  • Veículos publicados no portal
  • Webhooks disparados normalmente
  • Imagens armazenadas em definitivo

Sandbox (teste)

  • Chave: cds_test_...
  • Mesmos endpoints e payload
  • Sem impacto em dados de produção
  • Ideal para validar sua integração

Exemplo com chave de teste

Produção

https://api.veiculosnosul.com.br

Sandbox / Teste

https://sandbox.api.veiculosnosul.com.br
bash (curl)
curl -X POST https://sandbox.api.veiculosnosul.com.br/integration/vehicles/sync \
  -H "X-API-Key: cds_test_sua_chave_de_teste_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "SANDBOX-001",
    "brand": "Toyota",
    "model": "Corolla",
    "year": 2022,
    "price": 149900
  }'

Solicite sua chave de teste pelo painel da loja em Configurações → Integração → Nova chave. Chaves de teste podem ser revogadas e recriadas livremente.

Boas práticas para homologação

Teste o ciclo completo

Crie, atualize e delete o mesmo veículo para validar todos os fluxos antes de ir a produção.

Valide as imagens

Envie URLs de imagens reais e confirme que são baixadas e salvas corretamente.

Use um webhook de teste

Ferramentas como webhook.site ou smee.io permitem inspecionar webhooks sem configurar um servidor.

Teste o batch com dados variados

Inclua veículos com campos opcionais ausentes para garantir que sua integração trata bem os casos limite.

Próximo passo

Pronto para integrar?

Solicite sua API Key e comece a sincronizar veículos com o Veículos no Sul.