Documentação API

API Pública Sourced v1

A API Sourced permite que sistemas ERP (como Calipso, SAP, JD Edwards) se integrem diretamente com a plataforma de compras da Sourced. Use-a para:

  • Enviar requisições de compra do seu ERP para o Sourced
  • Obter ordens de compra de volta no seu ERP após adjudicação
  • Verificar quais requisições já foram sincronizadas
  • Monitorar o status das suas requisições

A API segue as convenções REST, utiliza JSON para os corpos de solicitação e resposta, e se autentica via API keys.

Autenticação

Todos os endpoints (exceto health check) requerem uma API key enviada no header X-API-Key.

As API keys são criadas no painel de administração do Sourced em Configurações → API Keys. Cada key é exibida apenas uma vez ao ser criada. Armazene-a com segurança.

Exemplo: Solicitação autenticada
curl -H "X-API-Key: sk_live_abc123..." \
  https://api.gosourced.ai/api/v1/requisitions
Segurança: Trate sua API key como uma senha. Não a exponha em código do lado do cliente, repositórios públicos ou logs. Se comprometida, revogue-a imediatamente no painel de administração e crie uma nova.

URL Base

AmbienteURL Base
Produçãohttps://api.gosourced.ai
Desenvolvimentohttps://api-dev.gosourced.ai

Todos os endpoints são prefixados com /api/v1.

Limites de taxa

Todos os endpoints são limitados a 60 solicitações por minuto por endpoint. Exceder este limite retorna uma resposta 429 Too Many Requests.

Para operações em massa, use o endpoint de importação em lote (POST /requisitions) que aceita até 100 requisições por solicitação.

Erros

A API utiliza códigos de status HTTP padrão. Erros retornam um corpo JSON com um campo detail.

Formato de resposta de erro
{
  "detail": "Invalid or revoked API key."
}
StatusSignificado
200Sucesso
400Solicitação inválida - erro de validação ou corpo malformado
401Não autorizado - API key ausente ou inválida
404Não encontrado - o recurso não existe ou não é acessível
422Entidade não processável - o corpo da solicitação não passou na validação do esquema
429Muitas solicitações - limite de taxa excedido
500Erro interno do servidor - erro inesperado do nosso lado

Exemplos de erros

401 - API key inválida
{
  "detail": "Invalid or revoked API key."
}
422 - Erro de validação (ex: campo obrigatório ausente)
{
  "detail": [
    {
      "loc": ["body", "requisitions", 0, "items", 0, "quantity"],
      "msg": "Input should be greater than 0",
      "type": "greater_than"
    }
  ]
}
200 - Sucesso parcial (algumas requisições falharam)
{
  "success": false,
  "created_count": 2,
  "updated_count": 0,
  "skipped_count": 0,
  "error_count": 1,
  "errors": [
    {
      "index": 2,
      "external_id": "REQ-2026-0099",
      "error": "Failed to process requisition: duplicate external_line_id within items"
    }
  ],
  "requisition_ids": [1234, 1235]
}

Nota: o endpoint POST /requisitions retorna 200 mesmo com falhas parciais. Sempre verifique o campo success e o array errors para detectar problemas por requisição.

Health Check

GET/api/v1/health

Verificar se a API está acessível. Não requer autenticação.

Solicitação
curl https://api.gosourced.ai/api/v1/health
Resposta - 200 OK
{
  "status": "ok",
  "api": "v1"
}

Enviar um arquivo (Presigned)

POST/api/v1/files/presignAPI Key obrigatória

Obtenha uma URL pré-assinada para enviar um arquivo (um manifesto de requisição ou um anexo) diretamente ao S3. Os bytes não passam pela API, então especificações técnicas pesadas de licitações são enviadas sem atingir os limites de tamanho.

Apenas canal de transporte: os arquivos ficam no armazenamento sob o prefixo da sua organização, sem parsing nem processamento. A URL pré-assinada expira em 10 minutos e aceita arquivos de até 500 MB.

Fluxo de duas etapas (por arquivo)

  1. Chame este endpoint com o nome do arquivo para receber um upload_url e os campos do formulário.
  2. Faça um POST do arquivo como multipart/form-data para upload_url, incluindo primeiro todos os campos de fields e depois um campo file com os bytes.

Corpo da requisição

CampoTipoObrigatórioDescrição
filenamestringobrigatórioNome original do arquivo (ex. 'SOL_106177_LINE_001_SEQ010_adjunto.docx'). Componentes de caminho são removidos.
content_typestringopcionalTipo MIME. Se omitido, qualquer tipo é aceito.
1. Solicitação
curl -X POST https://api.gosourced.ai/api/v1/files/presign \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "SOL_106177_LINE_001_SEQ010_adjunto.docx",
    "content_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
  }'
Resposta - 200 OK
{
  "upload_url": "https://s3.amazonaws.com/your-bucket",
  "fields": {
    "key": "erp-inbound/42/SOL_106177_LINE_001_SEQ010_adjunto.docx",
    "Content-Type": "application/vnd.openxmlformats-...",
    "policy": "eyJleHBpcmF0aW9uIjoi...",
    "x-amz-signature": "a1b2c3..."
  },
  "key": "erp-inbound/42/SOL_106177_LINE_001_SEQ010_adjunto.docx",
  "max_file_size": 524288000,
  "expires_in": 600
}

Campos da resposta

  • upload_url - a URL do S3 para onde o POST do arquivo é feito.
  • fields - campos do formulário que devem ser incluídos no POST multipart, antes do campo file.
  • key - a chave sob a qual o arquivo será armazenado.
  • max_file_size - tamanho máximo permitido em bytes (aplicado pelo S3).
  • expires_in - segundos até a URL pré-assinada expirar.
2. Envio para o S3 (multipart/form-data)
# Use upload_url + every key in "fields", then the file (last).
curl -X POST "$UPLOAD_URL" \
  -F "key=erp-inbound/42/SOL_106177_LINE_001_SEQ010_adjunto.docx" \
  -F "Content-Type=application/vnd.openxmlformats-..." \
  -F "policy=eyJleHBpcmF0aW9uIjoi..." \
  -F "x-amz-signature=a1b2c3..." \
  -F "file=@SOL_106177_LINE_001_SEQ010_adjunto.docx"
# HTTP 204 No Content on success

Importar requisições

POST/api/v1/requisitionsAPI Key obrigatória

Importar uma ou mais requisições de compra do seu ERP. Suporta importação em lote de até 100 requisições por solicitação. Usa external_id para deduplicação.

Corpo da solicitação

O corpo deve conter um array requisitions com 1 a 100 objetos de requisição.

Objeto Requisição

CampoTipoObrigatórioDescrição
external_idstringobrigatórioID único no seu ERP (chave de deduplicação). Máx 250 caracteres.
itemsarrayobrigatórioItens de linha (1-200 itens). Veja o esquema de Item abaixo.
requester_namestringopcionalNome da pessoa solicitante. Máx 200 caracteres.
requester_emailstringopcionalEmail do solicitante. Máx 200 caracteres.
assigned_buyerstringopcionalComprador atribuído no ERP de origem (texto livre). Exibido e filtrável no triage. Máx 200 caracteres.
descriptionstringopcionalTítulo ou resumo da requisição. Máx 500 caracteres.
commentsstringopcionalInstruções adicionais para compradores (HTML suportado). Máx 15.000 caracteres.
delivery_addressstringopcionalEndereço de entrega em texto livre. Máx 500 caracteres.
delivery_address_codestringopcionalCódigo correspondente a um endereço configurado no Sourced. Máx 50 caracteres.
desired_delivery_lead_time_daysintegeropcionalPrazo de entrega desejado em dias a partir da confirmação da OC. Aplicado como padrão a todos os itens.
created_datedatetimeopcionalData de criação da requisição no seu sistema (ISO 8601). Exibida como "Criação" no triage. Não é data de entrega: a necessidade vai por item em desired_delivery_date.
offer_deadlinedatetimeopcionalData limite para cotações de fornecedores (ISO 8601).
total_estimated_valuefloatopcionalValor total estimado. Calculado automaticamente a partir dos itens se omitido.
currencystringopcionalCódigo da moeda (ex: "ARS", "USD"). Padrão "ARS". Máx 10 caracteres.
department_codestringopcionalCódigo de departamento/centro de custo (comparado com departamentos do Sourced). Máx 50 caracteres.
priority_levelstringopcionalPrioridade: "LOW", "MEDIUM", "HIGH" ou "CRITICAL".
attachmentsarrayopcionalAnexos de arquivo (máx 20). Veja o esquema de Anexo abaixo.
raw_dataobjectopcionalJSON arbitrário do seu ERP, armazenado para rastreabilidade.

Objeto Item

CampoTipoObrigatórioDescrição
descriptionstringobrigatórioVisível para o fornecedorNome ou descrição do item. Máx 1.000 caracteres.
quantityfloatobrigatórioVisível para o fornecedorQuantidade necessária (deve ser > 0).
external_line_idstringopcionalID de linha no seu ERP (ex: "REQ-001-L10"). Retornado nas OC para rastreabilidade. Máx 100 caracteres.
unit_of_measurestringopcionalVisível para o fornecedorCódigo de UOM (ex: "KG", "EA", "LT", "M", "UN"). Comparado com o catálogo da sua org. Máx 50 caracteres.
target_pricefloatopcionalPreço alvo/orçamento por unidade.
estimated_pricefloatopcionalPreço total estimado para esta linha.
currencystringopcionalMoeda para preços (ex: "ARS", "USD"). Máx 10 caracteres.
categorystringopcionalCategoria do seu ERP. Máx 200 caracteres.
material_codestringopcionalVisível para o fornecedorCódigo de material/peça no seu ERP (ex: código de material SAP). Máx 100 caracteres.
specificationsobjectopcionalEspecificações técnicas. Veja o esquema de Especificações abaixo.
desired_delivery_datedatetimeopcionalData de entrega desejada para este item (ISO 8601, ex: "2026-05-15T00:00:00Z").
desired_delivery_lead_time_daysintegeropcionalPrazo de entrega desejado em dias para esta linha. Sobrescreve o valor a nível de cabeçalho.
detailstringopcionalVisível para o fornecedorDado técnico ou nota desta linha. Máx 1.000 caracteres. É compartilhado com o fornecedor: aparece no e-mail de solicitação de cotação em “Especificações”, a menos que você envie specifications.technical_specs, que tem prioridade. Não use para notas internas de compras.
raw_dataobjectopcionalJSON arbitrário para este item.

Objeto Especificações

CampoTipoObrigatórioDescrição
manufacturer_codestringopcionalVisível para o fornecedorNúmero de peça do fabricante (ex: "6ES7214-1AG40-0XB0"). Máx 100 caracteres.
manufacturer_namestringopcionalVisível para o fornecedorNome do fabricante (ex: "Siemens"). Máx 200 caracteres.
manufacturer_descriptionstringopcionalDescrição do fabricante. Máx 500 caracteres.
buyer_codestringopcionalVisível para o fornecedorCódigo interno no seu sistema (ex: "MAT-001234"). Máx 100 caracteres.
buyer_code_descriptionstringopcionalDescrição do código interno. Máx 500 caracteres.
buyer_code_systemstringopcionalNome do sistema de origem (ex: "SAP", "Calipso"). Máx 50 caracteres.
technical_specsstringopcionalVisível para o fornecedorEspecificações técnicas em texto livre (ex: "220V, 50Hz, IP55"). Máx 2.000 caracteres. É compartilhado com o fornecedor no e-mail de solicitação de cotação. Tem prioridade sobre o detail do item.
requirementsstringopcionalRequisitos adicionais (ex: "Certificação ISO 9001 obrigatória"). Máx 2.000 caracteres. Uso interno: não é incluído no e-mail ao fornecedor.

Objeto Anexo

CampoTipoObrigatórioDescrição
urlstringobrigatórioURL publicamente acessível para baixar o arquivo. Máx 2.000 caracteres.
filenamestringobrigatórioNome do arquivo original (ex: "plano_motor.pdf"). Máx 255 caracteres.
descriptionstringopcionalDescrição do anexo. Máx 500 caracteres.
file_typestringopcionalTipo MIME (detectado automaticamente se não fornecido). Máx 100 caracteres.

Lógica de deduplicação

Cada requisição é identificada pelo seu external_id (dentro do escopo da sua organização):

  • Nova: Se nenhuma requisição existente for encontrada, uma nova é criada com status PENDING.
  • Atualizar: Se uma requisição existente com status PENDING for encontrada, ela é atualizada in-place (os itens são totalmente substituídos).
  • Superseder: Se uma requisição existente com status SUPERSEDED for encontrada, ela é reativada (volta para PENDING) com os novos dados.
  • Lançada: Se a requisição já foi lançada como Solicitação de Compra (LAUNCHED), a importação é rejeitada. A PR ativa não pode ser sobrescrita.
  • Descartada: Se a requisição foi descartada anteriormente (DISCARDED), ela é reativada (volta para PENDING) com os novos dados. Útil quando o ERP a cancela e depois a reenvia.
Solicitação - Exemplo mínimo
curl -X POST https://api.gosourced.ai/api/v1/requisitions \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "requisitions": [
      {
        "external_id": "REQ-2026-0042",
        "items": [
          {
            "description": "Bearing SKF 6205",
            "quantity": 10
          }
        ]
      }
    ]
  }'

Resposta

ERPImportResult

CampoTipoObrigatórioDescrição
successbooleanobrigatóriotrue se error_count for 0.
created_countintegerobrigatórioNúmero de novas requisições criadas.
updated_countintegerobrigatórioNúmero de requisições PENDING existentes atualizadas.
skipped_countintegerobrigatórioNúmero de requisições ignoradas (atualmente sempre 0).
error_countintegerobrigatórioNúmero de requisições que falharam ao importar.
errorsarrayobrigatórioArray de {index, external_id, error} para cada requisição que falhou.
requisition_idsarrayobrigatórioIDs internos do Sourced das requisições criadas/atualizadas.
Resposta - 200 OK
{
  "success": true,
  "created_count": 1,
  "updated_count": 0,
  "skipped_count": 0,
  "error_count": 0,
  "errors": [],
  "requisition_ids": [1234]
}

Listar requisições

GET/api/v1/requisitionsAPI Key obrigatória

Obter uma lista paginada das suas requisições importadas. Retorna apenas requisições criadas via API.

Parâmetros de consulta

CampoTipoObrigatórioDescrição
pageintegeropcionalNúmero da página (padrão: 1).
page_sizeintegeropcionalItens por página, 1-100 (padrão: 20).
statusstringopcionalFiltrar por status: "PENDING", "LAUNCHED", "DISCARDED", "SUPERSEDED".
Solicitação
curl -H "X-API-Key: sk_live_abc123..." \
  "https://api.gosourced.ai/api/v1/requisitions?page=1&page_size=20&status=PENDING"
Resposta - 200 OK
{
  "data": [
    {
      "id": 1234,
      "external_id": "REQ-2026-0042",
      "status": "PENDING",
      "requester_name": "Juan Pérez",
      "description": "Repuestos línea producción",
      "items": [...],
      "imported_at": "2026-03-07T14:30:00Z"
    }
  ],
  "total": 45,
  "page": 1,
  "page_size": 20,
  "has_more": true
}

Detalhe da requisição

GET/api/v1/requisitions/{requisition_id}API Key obrigatória

Obter uma requisição individual pelo seu ID interno do Sourced, incluindo todos os itens.

Solicitação
curl -H "X-API-Key: sk_live_abc123..." \
  https://api.gosourced.ai/api/v1/requisitions/1234

Retorna o objeto completo da requisição. Retorna 404 se não encontrada ou não pertencente à sua organização.

Verificar requisições existentes

POST/api/v1/requisitions/check-existingAPI Key obrigatória

Verificar quais external_ids já existem no Sourced antes de importar. Útil para evitar chamadas desnecessárias à API.

Corpo da solicitação

CampoTipoObrigatórioDescrição
external_idsarrayobrigatórioLista de strings external_id para verificar (1-100 itens).
Solicitação
curl -X POST https://api.gosourced.ai/api/v1/requisitions/check-existing \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_ids": ["REQ-2026-0042", "REQ-2026-0043", "REQ-2026-0044"]
  }'
Resposta - 200 OK
{
  "existing": ["REQ-2026-0042"],
  "not_found": ["REQ-2026-0043", "REQ-2026-0044"]
}

Cancelar requisição

POST/api/v1/requisitions/cancelAPI Key obrigatória

Cancelar uma requisição PENDING enviando o external_id no corpo da requisição.

Apenas requisições com status PENDING podem ser canceladas. Se a requisição já foi lançada como Solicitação de Compra, deve ser cancelada dentro do Sourced.

CampoTipoObrigatórioDescrição
external_idstringobrigatórioO ID da requisição no seu sistema externo (o mesmo usado na importação)
reasonstringopcionalMotivo do cancelamento (armazenado para auditoria)
Solicitação
curl -X POST https://api.gosourced.ai/api/v1/requisitions/cancel \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "REQ-2026-0042",
    "reason": "Cancelado desde ERP"
  }'
Resposta - 200 OK
{
  "status": "discarded",
  "external_id": "REQ-2026-0042"
}

Respostas possíveis:

  • discarded - A requisição foi descartada com sucesso.
  • already_discarded - A requisição já estava descartada (idempotente).
  • already_superseded - A requisição já foi substituída por uma versão mais nova.
  • A requisição já foi lançada - retorna 409 com detalhes.
  • Requisição não encontrada - retorna 404.

Listar ordens de compra

GET/api/v1/purchase-ordersAPI Key obrigatória

Obter ordens de compra da sua organização. Suporta sincronização incremental através do parâmetro 'since'.

Parâmetros de consulta

CampoTipoObrigatórioDescrição
pageintegeropcionalNúmero da página (padrão: 1).
page_sizeintegeropcionalItens por página, 1-100 (padrão: 20).
statusstringopcionalFiltrar por status: "DRAFT", "CREATED", "SENT", "CONFIRMED", "REJECTED", "CANCELLED".
sincedatetimeopcionalTimestamp ISO 8601. Retorna apenas OC criadas ou atualizadas após esta data.
requisition_external_idstringopcionalFiltrar pelo external_id da requisição original no seu ERP. Retorna as OC geradas a partir dessa requisição.
Solicitação - Sincronização incremental
curl -H "X-API-Key: sk_live_abc123..." \
  "https://api.gosourced.ai/api/v1/purchase-orders?since=2026-03-01T00:00:00Z&status=CONFIRMED"
Resposta - 200 OK
{
  "data": [
    {
      "id": 567,
      "code": "PO-2026-0089",
      "status": "CONFIRMED",
      "award_type": "FULL",
      "supplier_name": "Distribuidora Industrial SA",
      "supplier_id": 42,
      "supplier_external_id": "PROV-CAL-00123",
      "supplier_tax_id": "30712345678",
      "supplier_email": "ventas@distribuidora.com",
      "total_price": 285000.00,
      "currency": "ARS",
      "delivery_address": "Av. Corrientes 1234, CABA",
      "delivery_address_code": "001",
      "observations": "Entregar en horario de mañana. Coordinar con depósito.",
      "delivery_lead_time_days": 15,
      "expected_delivery_date": "2026-03-20T16:00:00Z",
      "payment_terms_code": "NET30",
      "awarded_at": "2026-03-05T16:00:00Z",
      "awarded_by_name": "Juan Pérez",
      "awarded_by_email": "juan.perez@empresa.com",
      "created_at": "2026-03-05T16:00:00Z",
      "updated_at": "2026-03-06T10:30:00Z",
      "purchase_request_id": 1234,
      "requisition_external_id": "REQ-2026-00142",
      "exchange_rate_data": {
        "date": "2026-03-05",
        "usdToArs": 1450.0,
        "arsToUsd": 0.00069
      },
      "total_nominal_savings": 15000.00,
      "total_real_savings": 8500.00,
      "savings_currency": "ARS",
      "items": [
        {
          "description": "Bearing SKF 6205",
          "quantity": 10,
          "unit_price": 28500.00,
          "total_price": 285000.00,
          "unit_of_measure": "UN",
          "currency": "ARS",
          "delivery_lead_time_days": 15,
          "expected_delivery_date": "2026-03-20T16:00:00Z",
          "external_line_id": "REQ-2026-0042-L10",
          "buyer_code": "MAT-001234",
          "buyer_code_system": "SAP",
          "manufacturer_code": "6205-2RS",
          "technical_specs": "220V, 50Hz, IP55"
        }
      ]
    }
  ],
  "total": 12,
  "page": 1,
  "page_size": 20,
  "has_more": false
}

Detalhe da ordem de compra

GET/api/v1/purchase-orders/{po_id}API Key obrigatória

Obter uma ordem de compra individual pelo seu ID interno do Sourced, incluindo todos os itens de linha com rastreabilidade de volta ao seu ERP via external_line_id.

Solicitação
curl -H "X-API-Key: sk_live_abc123..." \
  https://api.gosourced.ai/api/v1/purchase-orders/567
Procurando OCs pelo ID da requisição do seu sistema?

Este endpoint requer o ID interno do Sourced (po_id). Se você precisa buscar ordens de compra usando o ID de requisição do seu ERP, use o endpoint GET /purchase-orders com o query parameter requisition_external_id:

GET /api/v1/purchase-orders?requisition_external_id=YOUR-REQ-ID

Você também pode combinar com outros filtros: status para filtrar por status da OC, since para sincronização incremental, e page / page_size para paginação.

Objeto Ordem de Compra

CampoTipoObrigatórioDescrição
idintegerobrigatórioID interno da OC no Sourced.
codestringobrigatórioCódigo legível da OC.
statusstringobrigatórioStatus da OC: "DRAFT", "CREATED", "SENT", "CONFIRMED", "REJECTED", "CANCELLED".
award_typestringopcionalTipo de adjudicação: FULL (fornecedor único) ou PARTIAL (adjudicação dividida entre múltiplos fornecedores).
supplier_namestringobrigatórioNome do fornecedor.
supplier_idintegeropcionalID interno do fornecedor no Sourced.
total_pricefloatopcionalValor total da OC.
currencystringopcionalCódigo da moeda.
delivery_addressstringopcionalEndereço de entrega.
delivery_address_codestringopcionalCódigo do endereço de entrega da organização vinculado (ex. "001"). Use-o para mapear o destino no seu ERP. Null para endereços de texto livre sem registro vinculado.
observationsstringopcionalComentários ou observações em texto livre inseridos pelo comprador na etapa de revisão pré-adjudicação. Usa as condições especiais cotadas pelo fornecedor quando o comprador não deixou nota. Null quando não há nenhuma.
delivery_lead_time_daysintegeropcionalPrazo de entrega em dias, conforme cotado pelo fornecedor a nível de cabeçalho. Quando o fornecedor cota prazos diferentes por linha, os itens podem ter valores distintos - use items[].delivery_lead_time_days para o valor por linha.
expected_delivery_datedatetimeopcionalData de entrega calculada (awarded_at + delivery_lead_time_days). Null se faltar qualquer um dos dois. Para datas por linha use items[].expected_delivery_date.
payment_terms_codestringopcionalCódigo das condições de pagamento.
awarded_atdatetimeopcionalQuando a OC foi adjudicada (ISO 8601).
awarded_by_namestringopcionalNome do usuário que adjudicou a OC.
awarded_by_emailstringopcionalEmail do usuário que adjudicou a OC.
created_atdatetimeopcionalTimestamp de criação (ISO 8601).
updated_atdatetimeopcionalTimestamp da última atualização (ISO 8601).
purchase_request_idintegeropcionalID da solicitação de compra originária.
requisition_external_idstringopcionalID do documento de origem no seu ERP (ex. número de requisição em Calipso/SAP/JDE). Resolvido a partir da ERPRequisition de origem quando disponível; caso contrário, recorre ao external_id da PR. Pode ser uma lista separada por vírgulas quando uma única PR agrega múltiplas requisições de origem.
exchange_rate_dataobjectopcionalTaxas de câmbio no momento do award. Formato: {"date": "2026-03-05", "usdToArs": 1450.0, "arsToUsd": 0.00069}. Null se não houver dados disponíveis.
total_nominal_savingsfloatopcionalEconomia nominal total vs preços históricos (sem ajuste por inflação).
total_real_savingsfloatopcionalEconomia real total vs preços históricos (ajustada por inflação).
savings_currencystringopcionalMoeda dos valores de economia.
itemsarrayobrigatórioItens de linha da OC. Veja o esquema de Item de OC abaixo.

Objeto Item de OC

CampoTipoObrigatórioDescrição
descriptionstringobrigatórioDescrição do item.
quantityfloatopcionalQuantidade pedida.
unit_pricefloatopcionalPreço por unidade.
total_pricefloatopcionalPreço total da linha (quantidade x preço unitário).
unit_of_measurestringopcionalUnidade de medida.
currencystringopcionalCódigo da moeda.
delivery_lead_time_daysintegeropcionalPrazo de entrega em dias para esta linha, conforme cotado pelo fornecedor. Itens diferentes da mesma OC podem ter prazos distintos.
expected_delivery_datedatetimeopcionalData de entrega calculada para esta linha (PO.awarded_at + delivery_lead_time_days do item). Null se faltar qualquer um dos dois.
external_line_idstringopcionalID de linha original do seu ERP - use para vincular itens da OC às suas linhas de requisição.
buyer_codestringopcionalCódigo interno no seu sistema, exatamente como enviado ao importar a requisição (ex: "MAT-001234").
buyer_code_systemstringopcionalSistema de origem do código interno (ex: "SAP", "JDE", "Calipso").
buyer_code_descriptionstringopcionalDescrição do código interno.
manufacturer_codestringopcionalNúmero de peça do fabricante.
manufacturer_namestringopcionalNome do fabricante (ex: "Siemens").
manufacturer_descriptionstringopcionalDescrição do fabricante para a peça.
technical_specsstringopcionalEspecificações técnicas em texto livre (ex: "220V, 50Hz, IP55").
requirementsstringopcionalRequisitos adicionais (ex: "Certificação ISO 9001 exigida").
Sobre datas e prazos de entrega
  • O prazo de entrega existe em dois níveis: delivery_lead_time_days a nível de cabeçalho (reflete o prazo geral cotado pelo fornecedor) e items[].delivery_lead_time_days a nível de linha (preciso por item). Quando o fornecedor cota prazos diferentes por linha, prefira o valor por linha.
  • expected_delivery_date é um campo calculado: awarded_at + delivery_lead_time_days. O Sourced calcula; o fornecedor não envia uma data diretamente.
  • Se delivery_lead_time_days for null, expected_delivery_date também será null. Isso normalmente significa que o fornecedor não incluiu o prazo na cotação.

Baixar o legajo da OC (dossiê)

GET/api/v1/purchase-orders/{po_id}/legajoAPI Key obrigatória

Obtenha um link temporário para baixar o legajo - um ZIP com a trilha de auditoria completa da adjudicação: PDF/XLSX comparativo, histórico de e-mails e os anexos de todos os fornecedores participantes.

Solicitação
curl -H "X-API-Key: sk_live_abc123..." \
  https://api.gosourced.ai/api/v1/purchase-orders/567/legajo

Objeto de resposta

CampoTipoObrigatórioDescrição
download_urlstringobrigatórioURL pré-assinada temporária para baixar o ZIP do legajo.
filenamestringobrigatórioNome sugerido para o arquivo ZIP.
expires_inintegerobrigatórioSegundos até a URL de download expirar.
size_bytesintegerobrigatórioTamanho do ZIP do legajo em bytes.
Como funciona o legajo
  • A resposta é uma URL pré-assinada temporária (válida ~10 minutos). Baixe o ZIP diretamente dela - essa URL não requer API key.
  • O legajo corresponde à requisição (PR) por trás da OC. Em uma adjudicação dividida (várias OCs de uma mesma requisição), as OCs irmãs retornam o mesmo dossiê.

Comparativo de cotações da OC

GET/api/v1/purchase-orders/{po_id}/quotationsAPI Key obrigatória

Obtenha o comparativo completo de cotações que originou esta adjudicação: todas as linhas cotadas por cada fornecedor para a solicitação (PR) por trás da OC, adjudicadas e não adjudicadas - não apenas as que terminaram nesta OC.

Solicitação
curl -H "X-API-Key: sk_live_abc123..." \
  https://api.gosourced.ai/api/v1/purchase-orders/567/quotations
Resposta - 200 OK
{
  "purchase_order_id": 567,
  "purchase_order_code": "PO-4F2A91C3",
  "requisition_external_id": "REQ-2026-0042",
  "exchange_rate": {
    "date": "2026-07-12",
    "base": "USD",
    "rates": { "ARS": 1450.5, "EUR": 0.92 }
  },
  "currencies": ["ARS", "USD"],
  "items": [
    {
      "material_code": "MAT-000123",
      "external_line_id": "10",
      "description": "Guantes de nitrilo talle L",
      "quantity": 500.0,
      "quotes": [
        {
          "quotation_id": 8123,
          "supplier_id": 42,
          "supplier_name": "Proveedor A S.A.",
          "supplier_tax_id": "30516242775",
          "supplier_erp_code": "PROV-001",
          "unit_price": 11.25,
          "original_price": 12.5,
          "discount": { "type": "percentage", "value": 10, "source": "manual" },
          "currency": "ARS",
          "quantity": 500,
          "awarded": true,
          "awarded_po_code": "PO-4F2A91C3",
          "lead_time_days": 7,
          "payment_term_code": "NET30",
          "response_status": "PARTIAL",
          "received_at": "2026-07-10T14:32:00+00:00"
        },
        {
          "quotation_id": 8124,
          "supplier_id": 57,
          "supplier_name": "Proveedor B S.R.L.",
          "supplier_tax_id": "30709876541",
          "supplier_erp_code": null,
          "unit_price": 0.0095,
          "original_price": 0.0095,
          "discount": null,
          "currency": "USD",
          "quantity": 500,
          "awarded": false,
          "awarded_po_code": null,
          "lead_time_days": 15,
          "payment_term_code": null,
          "response_status": "INCOMPLETE",
          "received_at": "2026-07-11T09:05:00+00:00"
        }
      ]
    }
  ]
}

Objeto de resposta

CampoTipoObrigatórioDescrição
purchase_order_idintegerobrigatórioID da OC consultada.
purchase_order_codestringobrigatórioCódigo da OC consultada (ex.: PO-4F2A91C3).
requisition_external_idstringopcionalID externo da requisição de origem (sistema ERP), se houver.
exchange_rateobjectopcionalTaxas de câmbio salvas na OC no momento da adjudicação, para converter as cotações para uma moeda comum: { date, base: "USD", rates: { BRL: 5.4, ARS: 1450.5 } } (unidades de cada moeda por 1 USD). null se a OC não salvou uma foto (licitação, catálogo ou OC anterior a esta função).
currenciesarrayobrigatórioMoedas distintas que aparecem nas cotações, ordenadas (ex.: ["BRL", "USD"]).
itemsarrayobrigatórioLinhas da solicitação por trás da OC, cada uma com suas cotações.

Objeto item (items[])

CampoTipoObrigatórioDescrição
material_codestringopcionalCódigo de material do comprador, se a linha tiver um.
external_line_idstringopcionalID da linha no sistema de origem (ERP), se houver.
descriptionstringobrigatórioDescrição do item solicitado.
quantityfloatopcionalQuantidade solicitada.
quotesarrayobrigatórioCotações recebidas para esta linha, uma por fornecedor que cotou com preço.

Objeto cotação (items[].quotes[])

CampoTipoObrigatórioDescrição
quotation_idintegerobrigatórioID da cotação. Um fornecedor que cotou mais de uma vez (rodadas) aparece uma vez por cotação.
supplier_idintegeropcionalID interno do fornecedor no Sourced. Identidade estável entre linhas (o nome pode se repetir).
supplier_namestringopcionalNome do fornecedor que cotou.
supplier_tax_idstringopcionalIdentificador fiscal do fornecedor (CNPJ no Brasil, CUIT na Argentina, RUT no Chile/Uruguai).
supplier_erp_codestringopcionalCódigo ERP do fornecedor na sua organização, se configurado.
unit_pricefloatopcionalPreço unitário FINAL, líquido de desconto, na moeda original do fornecedor. É o mesmo número que o comprador vê no comparativo de adjudicação e no Excel do dossiê, e o preço pelo qual a OC foi adjudicada.
original_pricefloatopcionalPreço unitário antes do desconto. Igual a unit_price se não houve desconto.
discountobjectopcionalDesconto aplicado: { type: "percentage" | "fixed", value, source }. source: supplier_quoted (ofertado pelo fornecedor), negotiation, manual (inserido pelo comprador) ou global. null se não houve desconto.
currencystringopcionalMoeda da cotação (código ISO, ex.: BRL, USD). Se o fornecedor não indicou moeda em nenhum lugar, informa-se "USD".
quantityfloatopcionalQuantidade cotada pelo fornecedor.
awardedbooleanobrigatóriotrue se esta linha foi adjudicada a este fornecedor em alguma OC ativa da solicitação.
awarded_po_codestringopcionalCódigo da OC onde a linha foi adjudicada (pode ser uma OC irmã em adjudicação dividida). null se não foi adjudicada.
lead_time_daysintegeropcionalPrazo de entrega ofertado, em dias (inteiro). Um prazo não numérico é informado como null.
payment_term_codestringopcionalCódigo da condição de pagamento ofertada pelo fornecedor.
response_statusstringopcionalClassificação da análise de IA da resposta do fornecedor. Valores como INCOMPLETE, PARTIAL, NEEDS_HUMAN_REVIEW, EXPLICIT_REJECTION.
received_atdatetimeopcionalData e hora de recebimento da cotação (ISO 8601).
Como ler o comparativo
  • O comparativo corresponde à solicitação (PR) por trás da OC: inclui as cotações de todos os fornecedores participantes, mesmo os que não venceram. Uma linha cotada sem preço não gera entrada em quotes; uma OC de catálogo (sem processo de cotação) retorna quotes vazios.
  • awarded é no nível da solicitação: em uma adjudicação dividida, uma linha pode ter sido adjudicada em uma OC irmã diferente da consultada - awarded_po_code sempre indica o código da OC vencedora daquela linha.
  • Os preços são os mesmos que o comprador viu no comparativo de adjudicação e os que constam no Excel do dossiê: unit_price já inclui qualquer desconto negociado (original_price e discount mostram o bruto e o desconto). Uma cotação importada que o usuário ainda não confirmou não aparece.
  • Os preços são retornados na moeda cotada por cada fornecedor - nenhuma conversão é aplicada. exchange_rate traz as taxas salvas na OC ao adjudicar (null se não houver) para que você converta tudo para uma moeda comum.
  • Todas as chaves estão sempre presentes; se um dado faltar, o valor é null (o formato da resposta nunca muda).

Atualizar status da ordem de compra

POST/api/v1/purchase-orders/{po_id}/statusAPI Key obrigatória

Atualizar o status de uma ordem de compra para refletir seu progresso no seu sistema externo (ERP).

Transições de status permitidas:

DeParaSignificado
DRAFTCREATEDA OC foi criada no seu ERP.
CREATEDCONFIRMEDA OC foi totalmente aprovada no seu ERP.
DRAFTCONFIRMEDAtalho quando não é necessário o passo intermediário.
DRAFTREJECTEDA OC foi rejeitada no seu ERP. A PR é reaberta no Sourced.
CREATEDREJECTEDA OC foi rejeitada no seu ERP após ter sido criada. A PR é reaberta no Sourced.
DRAFT / CREATED / SENT / CONFIRMEDCANCELLEDA compra não vai acontecer. A OC é cancelada e a PR também - nada é reaberto. Use REJECTED se a necessidade ainda existir e você quiser readjudicar.

REJECTED e CANCELLED também funcionam quando a OC foi uma adjudicação parcial, desde que seja a única OC ativa da sua PR. Se a PR tiver várias OCs ativas (split entre fornecedores), o endpoint retorna 409 e a reversão deve ser gerenciada dentro do Sourced.

CampoTipoObrigatórioDescrição
statusstringobrigatórioStatus destino: CREATED (OC criada no seu ERP), CONFIRMED (OC aprovada no seu ERP), REJECTED (OC rejeitada, reabre a PR) ou CANCELLED (compra cancelada, cancela a OC e a PR).
external_idstringopcionalNúmero ou código da OC no seu ERP (ex: OC-CAL-00045678). Armazenado para rastreabilidade.
notesstringopcionalNotas opcionais sobre a mudança de status.
Solicitação
curl -X POST https://api.gosourced.ai/api/v1/purchase-orders/567/status \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "status": "CREATED",
    "external_id": "OC-CAL-00045678",
    "notes": "Creada en Calipso"
  }'
Resposta - 200 OK
{
  "status": "created",
  "po_id": 567,
  "code": "PO-A1B2C3D4"
}

Editar uma OC

PATCH/api/v1/purchase-orders/{po_id}API Key obrigatória

Atualiza os campos editáveis de uma ordem de compra. Apenas os campos presentes no body são alterados: número da OC no seu ERP, condição de pagamento, prazo de entrega, observações e status (mesma máquina de transições do POST /status).

Corpo da solicitação

CampoTipoObrigatórioDescrição
external_idstringopcionalNúmero ou código da OC no seu ERP. Sobrescreve o valor armazenado.
payment_terms_codestringopcionalCódigo da condição de pagamento do catálogo (ex.: NET30). Não diferencia maiúsculas; validado contra os códigos ativos - um código desconhecido retorna 400 com a lista permitida.
delivery_lead_time_daysintegeropcionalPrazo de entrega em dias a partir da confirmação da OC. expected_delivery_date deriva deste valor.
observationsstringopcionalTexto livre exibido na OC. Uma string vazia o limpa.
statusstringopcionalStatus de destino: CREATED, CONFIRMED, REJECTED ou CANCELLED - mesmas transições do POST /status. Enviar o status atual é um no-op. REJECTED/CANCELLED retornam o contrato desses fluxos (pr_reopened / pr_code) mais updated_fields.
notesstringopcionalNotas sobre a mudança de status. Válido apenas junto com status.
Solicitação
curl -X PATCH https://api.gosourced.ai/api/v1/purchase-orders/567 \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "OC-CAL-00045678",
    "payment_terms_code": "NET30",
    "status": "CONFIRMED"
  }'
Resposta - 200 OK
{
  "status": "updated",
  "po_id": 567,
  "code": "PO-A1B2C3D4",
  "updated_fields": ["external_id", "payment_terms_code", "status"]
}
Semântica do PATCH
  • Partial update real: apenas os campos presentes no body são aplicados. null explícito é um erro (400) - para deixar um campo como está, omita-o.
  • Toda a validação roda antes de aplicar qualquer coisa: em uma resposta 400 ou 409, nenhum campo do body foi modificado.
  • A mudança de status usa a mesma máquina de transições do POST /purchase-orders/{po_id}/status (que continua disponível). Uma transição inválida retorna 409. Editar uma OC CANCELLED ou REJECTED retorna 409.
  • Alterar a condição de pagamento ou o prazo de entrega não regenera o PDF que o fornecedor já recebeu: o dado é atualizado no Sourced, mas o documento enviado não muda.
  • O conteúdo da adjudicação (preços, itens, moeda, fornecedor) não é editável: para corrigi-lo, rejeite a OC (status REJECTED) e adjudique novamente.

Listar fornecedores

GET/api/v1/suppliersAPI Key obrigatória

Listar os fornecedores da sua organização. Use ?has_erp_code=false para encontrar fornecedores sem mapear. Use ?detail=full para contatos, categorias e cobertura.

Parâmetros de consulta

CampoTipoObrigatórioDescrição
pageintegeropcionalNúmero da página (padrão: 1).
page_sizeintegeropcionalResultados por página (1-200, padrão: 50).
searchstringopcionalBuscar por nome, nome personalizado, email, CNPJ/CPF ou código ERP.
erp_codestringopcionalFiltrar por código ERP exato.
has_erp_codebooleanopcionaltrue = apenas mapeados ao ERP, false = apenas sem mapear.
detailstringopcionalUse 'full' para incluir contatos, categorias e cobertura.
Solicitação
# Light (default)
curl -H "X-API-Key: sk_live_abc123..." \
  "https://api.gosourced.ai/api/v1/suppliers?has_erp_code=false"

# Full detail
curl -H "X-API-Key: sk_live_abc123..." \
  "https://api.gosourced.ai/api/v1/suppliers?search=30712345678&detail=full"
Resposta - 200 OK
// Light response
{
  "data": [
    {
      "id": 42,
      "name": "Distribuidora Industrial SA",
      "email": "ventas@distribuidora.com",
      "tax_id": "30712345678",
      "country_code": "AR",
      "city": "Buenos Aires",
      "is_active": true,
      "is_preferred": true,
      "erp_code": "PROV-CAL-00123",
      "erp_type": "SAP_B1"
    }
  ],
  "total": 85,
  "page": 1,
  "page_size": 50,
  "has_more": true
}

// Full response (?detail=full)
{
  "data": [
    {
      "id": 42,
      "name": "Distribuidora Industrial SA",
      "email": "ventas@distribuidora.com",
      "tax_id": "30712345678",
      "country_code": "AR",
      "city": "Buenos Aires",
      "is_active": true,
      "is_preferred": true,
      "erp_code": "PROV-CAL-00123",
      "erp_type": "SAP_B1",
      "address": "Av. Corrientes 1234",
      "state_code": "CABA",
      "website": "https://distribuidora.com",
      "description": "Distribuidor de insumos industriales",
      "tax_regime": null,
      "employee_count": "50+",
      "years_in_business": "5+",
      "performance_score": 4.2,
      "reliability_score": 4.5,
      "quality_score": 4.0,
      "contacts": [
        {
          "name": "Juan Pérez",
          "email": "juan@distribuidora.com",
          "phone": "+54 11 5555-1234",
          "role": "SALES",
          "is_primary": true
        }
      ],
      "categories": [
        { "id": 15, "code": "IND-001", "name": "Insumos Industriales", "level": "LEVEL_1" }
      ],
      "coverage": [
        { "country": "Argentina", "province": null, "is_nationwide": true }
      ]
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 50,
  "has_more": false
}

Criar fornecedor

POST/api/v1/suppliersAPI Key obrigatória

Criar um fornecedor para sua organização. Pelo menos um de tax_id ou erp_code é obrigatório. Pelo menos um contato com papel 'sales' é obrigatório. Se já existir um fornecedor global com o mesmo CNPJ/CPF, ele é reutilizado e vinculado à sua organização.

Corpo da solicitação

CampoTipoObrigatórioDescrição
namestringobrigatórioNome do fornecedor para sua organização.
tax_idstringopcionalCNPJ, CUIT, RUT, etc. Pelo menos um de tax_id ou erp_code é obrigatório.
erp_codestringopcionalCódigo do fornecedor no seu ERP. Pelo menos um de tax_id ou erp_code é obrigatório.
erp_typestringopcionalTipo de ERP (SAP_B1, JDE, ORACLE_CLOUD, etc.).
country_codestringopcionalCódigo de país ISO 3166-1 (ex: AR, BR, US).
citystringopcionalCidade do fornecedor.
addressstringopcionalEndereço completo.
state_codestringopcionalEstado/província (ex: SP, RJ).
websitestringopcionalSite do fornecedor.
contactsarrayobrigatórioLista de contatos. Pelo menos um com papel 'sales' é obrigatório.
contacts[].namestringobrigatórioNome do contato.
contacts[].emailstringobrigatórioEmail do contato.
contacts[].phonestringopcionalTelefone (opcional).
contacts[].rolestringobrigatórioPapel: SALES ou LOGISTICS.
contacts[].is_primarybooleanopcionaltrue se for o contato principal (padrão: false).
Solicitação
curl -X POST https://api.gosourced.ai/api/v1/suppliers \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Distribuidora Industrial SA",
    "tax_id": "30712345678",
    "erp_code": "PROV-CAL-00123",
    "erp_type": "SAP_B1",
    "country_code": "AR",
    "city": "Buenos Aires",
    "address": "Av. Corrientes 1234",
    "contacts": [
      {
        "name": "Juan Pérez",
        "email": "juan@distribuidora.com",
        "phone": "+54 11 5555-1234",
        "role": "SALES",
        "is_primary": true
      },
      {
        "name": "María García",
        "email": "maria@distribuidora.com",
        "role": "LOGISTICS"
      }
    ]
  }'
Resposta - 200 OK
// 201 Created
{
  "id": 42,
  "name": "Distribuidora Industrial SA",
  "email": null,
  "tax_id": "30712345678",
  "country_code": "AR",
  "city": "Buenos Aires",
  "is_active": true,
  "is_preferred": false,
  "erp_code": "PROV-CAL-00123",
  "erp_type": "SAP_B1",
  "address": "Av. Corrientes 1234",
  "state_code": null,
  "website": null,
  "description": null,
  "tax_regime": null,
  "employee_count": null,
  "years_in_business": null,
  "performance_score": null,
  "reliability_score": null,
  "quality_score": null,
  "contacts": [
    {
      "name": "Juan Pérez",
      "email": "juan@distribuidora.com",
      "phone": "+54 11 5555-1234",
      "role": "SALES",
      "is_primary": true
    },
    {
      "name": "María García",
      "email": "maria@distribuidora.com",
      "phone": null,
      "role": "LOGISTICS",
      "is_primary": false
    }
  ],
  "categories": [],
  "coverage": []
}

Criar ou atualizar acompanhamentos de entrega

POST/api/v1/delivery-trackingsAPI Key obrigatória

Upsert em lote de 1 a 200 linhas de pedido de compra para acompanhar a entrega. A chave natural é (po_code, line_position): reenviar a mesma linha atualiza o acompanhamento existente em vez de duplicá-lo.

Limite de taxa: 30 solicitações por minuto (os demais endpoints deste grupo usam o limite geral de 60 por minuto).

supplier_erp_code é o identificador recomendado para associar o fornecedor. supplier_tax_id é um respaldo. supplier_name é apenas texto de exibição: nunca é usado para associar.
Uma linha cujo fornecedor não pôde ser resolvido fica em SUPPLIER_NOT_FOUND: nenhum e-mail é enviado para ela. Uma vez cadastrado o fornecedor com POST /suppliers, reenvie a mesma linha (mesmo po_code e line_position) para que seja vinculada.

Corpo da solicitação

O corpo deve conter um array lines com 1 a 200 linhas. Cada linha leva a OC, a posição, a data prometida, o fornecedor (código ERP ou CNPJ/CPF) e o detalhe do item (descrição, quantidade pedida e quantidade pendente): sem isso não há o que acompanhar nem o que mostrar ao fornecedor. Um campo obrigatório ausente rejeita o lote inteiro com 422.

Objeto de linha (lines[])

CampoTipoObrigatórioDescrição
po_codestringobrigatórioNúmero do pedido de compra no sistema do cliente. Máximo de 100 caracteres.
line_positionintegerobrigatórioPosição da linha dentro do pedido de compra (10, 20, 30…). Inteiro ≥ 0.
original_delivery_datedateobrigatórioData de entrega combinada (AAAA-MM-DD)
supplier_erp_codestringUm dos doisCódigo do fornecedor no ERP do cliente. Identificador recomendado. Obrigatório se supplier_tax_id não for enviado. Máximo 100 caracteres.
supplier_tax_idstringUm dos doisCNPJ/CPF do fornecedor. Obrigatório se supplier_erp_code não for enviado. Máximo 50 caracteres.
supplier_namestringopcionalApenas texto de exibição; nunca usado para associar o fornecedor. Máximo de 255 caracteres.
material_codestringopcionalCódigo do material ou item no sistema do cliente. Máximo de 100 caracteres.
descriptionstringobrigatórioDescrição do item. É o que o fornecedor vê no e-mail de acompanhamento. Máximo 500 caracteres.
quantity_orderedfloatobrigatórioQuantidade pedida. ≥ 0. É o que o fornecedor vê no e-mail de acompanhamento.
quantity_pendingfloatobrigatórioQuantidade pendente de entrega. ≥ 0. É o que o e-mail cobra do fornecedor: com entregas parciais, envie o que falta, não o pedido.
unit_of_measurestringopcionalUnidade de medida. Máximo de 20 caracteres.
po_datedateopcionalData do pedido de compra (AAAA-MM-DD).
buyer_emailstringopcionalCC de escalonamento e destinatário dos avisos de atraso. Se não for enviado, a notificação in-app de atraso vai para o primeiro usuário com papel logistics da organização (só um, não todos).
is_urgentbooleanopcionalMarca a linha como urgente.

É preciso enviar pelo menos supplier_erp_code ou supplier_tax_id. Para supplier_tax_id, um valor vazio, ou composto só por separadores (por exemplo "-" ou " - "), conta como não enviado, e a correspondência ignora hífens e espaços dos dois lados (30-71234567-8 corresponde a 30712345678). supplier_erp_code só é aparado (trim): um "-" literal CONTA como enviado e é usado tal como está na busca — se não corresponder a nenhum fornecedor, a linha fica em SUPPLIER_NOT_FOUND (não é um erro).

Regras do upsert

  • Linha nova: é criada como PENDING_SCHEDULE se o fornecedor foi resolvido, ou SUPPLIER_NOT_FOUND caso contrário. Nasce ativa (sem pausa).
  • Linha existente, aberta, criada por esta mesma API: seus dados e quantidades são atualizados. Uma mudança em original_delivery_date ou nas quantidades não reinicia o ciclo de acompanhamento.
  • Linha existente já encerrada (DELIVERED ou CANCELLED): é reativada com um novo ciclo de acompanhamento (volta a enviar e-mails de confirmação); o resultado é reactivated e conta dentro de updated_count.
  • Corrigir o fornecedor de uma linha existente: se chegar com um supplier_erp_code ou supplier_tax_id que resolve para OUTRO fornecedor, o acompanhamento é revinculado a essa empresa e o ciclo recomeça (os e-mails anteriores foram para a empresa errada). Uma linha que não resolve nenhum fornecedor nunca desvincula o fornecedor já atribuído.
  • Sem mudanças: o resultado é unchanged; nenhum evento é registrado e updated_at não é alterado.
  • Linha administrada por outra fonte (CSV ou sincronização ERP): é rejeitada com o erro dlvTrackingManagedByOtherSource. Acompanhamentos carregados por CSV ou pela sincronização ERP não podem ser modificados por esta API.
  • Quantidade pendente 0: a linha já foi recebida por completo. Em um acompanhamento aberto ela é encerrada como DELIVERED (evento delivered com reason quantity_pending_zero; a data real fica como o dia do envio — se você a conhece, use mark_delivered com actual_delivery_date). Em um já encerrado é unchanged, nunca uma reativação. Uma linha NOVA com pendente 0 retorna o erro dlvNothingPending.
Solicitação
curl -X POST https://api.gosourced.ai/api/v1/delivery-trackings \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      {
        "po_code": "OC-2026-0455",
        "line_position": 10,
        "original_delivery_date": "2026-10-15",
        "supplier_erp_code": "PROV-CAL-00123",
        "material_code": "MAT-001234",
        "description": "Rodamiento SKF 6205",
        "quantity_ordered": 50,
        "quantity_pending": 50,
        "unit_of_measure": "UN",
        "buyer_email": "compras@empresa.com"
      },
      {
        "po_code": "OC-2026-0455",
        "line_position": 20,
        "original_delivery_date": "2026-10-15",
        "supplier_tax_id": "30-71234567-8",
        "description": "Filtro de aceite",
        "quantity_ordered": 12,
        "quantity_pending": 12,
        "unit_of_measure": "UN"
      }
    ]
  }'

Resposta

Resposta do upsert

CampoTipoObrigatórioDescrição
created_countintegerobrigatórioQuantidade de linhas criadas.
updated_countintegerobrigatórioQuantidade de linhas atualizadas (inclui as reativadas).
unchanged_countintegerobrigatórioQuantidade de linhas sem mudanças.
error_countintegerobrigatórioQuantidade de linhas com erro.
resultsarrayobrigatórioResultado de cada linha do lote, na mesma ordem em que foram enviadas.

Objeto de resultado por linha (results[])

CampoTipoObrigatórioDescrição
po_codestringobrigatórioNúmero do pedido de compra, como enviado.
line_positionintegerobrigatórioPosição da linha, como enviada.
idintegeropcionalId interno do acompanhamento. Ausente quando a linha terminou em erro.
resultstringobrigatóriocreated | updated | unchanged | reactivated | error.
statusstringopcionalStatus do acompanhamento após o upsert. Ausente nas linhas com erro.
codestringopcionalCódigo do erro (dlv*). Presente apenas quando result é error.
messagestringopcionalDetalhe do erro em inglês, pensado para logs. Não traduzir nem exibir ao usuário final: code é a chave i18n.
Resposta - 200 OK
// 200 OK
{
  "created_count": 1,
  "updated_count": 0,
  "unchanged_count": 0,
  "error_count": 1,
  "results": [
    {
      "po_code": "OC-2026-0455",
      "line_position": 10,
      "id": 8821,
      "result": "created",
      "status": "PENDING_SCHEDULE"
    },
    {
      "po_code": "OC-2026-0455",
      "line_position": 20,
      "result": "error",
      "code": "dlvSupplierReferenceAmbiguous",
      "message": "supplier_tax_id '30-71234567-8' matches more than one supplier"
    }
  ]
}

O HTTP é sempre 200, mesmo quando há linhas com erro: verifique error_count e o code de cada linha em results para saber o que falhou.

Listar acompanhamentos de entrega

GET/api/v1/delivery-trackingsAPI Key obrigatória

Obter os acompanhamentos de entrega da sua organização, de todas as fontes (API, CSV e sincronização ERP). Suporta sincronização incremental com since.

Parâmetros de consulta

CampoTipoObrigatórioDescrição
pageintegeropcionalNúmero da página (padrão: 1).
page_sizeintegeropcionalItens por página, 1-200 (padrão: 50).
statusstringopcionalFiltrar por status (ver a tabela de status abaixo). Inclui STAND_BY.
po_codestringopcionalFiltrar pelo po_code exato.
supplier_erp_codestringopcionalFiltrar pelo código ERP do fornecedor.
sincedatetimeopcionalTimestamp ISO 8601. Retorna apenas acompanhamentos criados ou modificados a partir dessa data, em ordem crescente por data de modificação. Sem fuso horário é interpretado como UTC: envie sempre um offset explícito (ou "Z") para evitar ambiguidade.
open_onlybooleanopcionalExclui os acompanhamentos DELIVERED e CANCELLED.
Com since, a ordem é por data de modificação crescente (não por data de criação decrescente, que é a ordem padrão): guarde o updated_at do último registro recebido e use-o como o próximo since. Para não perder linhas gravadas por uma transação longa (um CSV grande, um sync do ERP), o servidor recua o since 10 minutos: você vai receber de novo registros desses minutos, então deduplique por id (o upsert é idempotente do seu lado). Para um acompanhamento que nunca foi modificado, updated_at é igual a created_at (nunca null): o cursor sempre tem um valor utilizável, sem que você precise resolver esse fallback.

Status

StatusDescrição
PENDING_SCHEDULEPendente de agendamento. Carregada; ainda não foi pedida confirmação ao fornecedor.
READY_TO_SENDPronta para enviar. Pronta para enviar o pedido de confirmação de entrega.
SENT_PENDING_RESPONSEEnviada, aguardando resposta. Confirmação solicitada; o fornecedor ainda não respondeu.
NO_RESPONSESem resposta. Não respondeu após os lembretes.
CONFIRMED_ON_TIMEConfirmada no prazo. O fornecedor confirmou a data original.
CONFIRMED_DELAYEDConfirmada com atraso. Confirmou, mas com data posterior à original.
CONFIRMED_EARLYConfirmada antecipada. Confirmou uma data anterior à original.
REQUIRES_REVIEWRequer revisão. Respondeu algo ambíguo ou mudou condições; precisa ser revisado.
DELIVEREDEntregue. Já entregue.
CANCELLEDCancelada. O pedido de compra foi cancelado.
SUPPLIER_NOT_FOUNDFornecedor não identificado. Não foi possível associar o fornecedor (por código ERP ou CNPJ).
STAND_BYPausada manualmente e ainda aberta: nenhum e-mail é enviado para ela. É um status virtual — um status terminal (DELIVERED/CANCELLED) sempre prevalece sobre STAND_BY, então uma linha pausada que é entregue ou cancelada é informada pelo seu status real.
Solicitação - Sincronização incremental
curl -H "X-API-Key: sk_live_abc123..." \
  "https://api.gosourced.ai/api/v1/delivery-trackings?since=2026-09-01T00:00:00Z&open_only=true"

Objeto de acompanhamento (delivery tracking)

CampoTipoObrigatórioDescrição
idintegerobrigatórioId interno do acompanhamento.
po_codestringobrigatórioNúmero do pedido de compra no sistema do cliente.
line_positionintegerobrigatórioPosição da linha dentro do pedido de compra.
sourcestringobrigatórioOrigem do acompanhamento: api, csv ou erp_sync. A API lê acompanhamentos das três fontes.
supplierobjectopcionalFornecedor vinculado, ou null se ainda não pôde ser resolvido (SUPPLIER_NOT_FOUND).
supplier.idintegeropcionalId interno do fornecedor.
supplier.namestringopcionalRazão social do fornecedor.
supplier.tax_idstringopcionalCNPJ/CPF do fornecedor.
supplier.erp_codestringopcionalCódigo ERP do fornecedor para a sua organização.
supplier_namestringopcionalNome do fornecedor como foi carregado (pode diferir de supplier.name se veio apenas como texto livre).
material_codestringopcionalCódigo do material ou item.
descriptionstringopcionalDescrição do item.
quantity_orderedfloatopcionalQuantidade pedida.
quantity_pendingfloatopcionalQuantidade pendente de entrega.
unit_of_measurestringopcionalUnidade de medida.
po_datedateopcionalData do pedido de compra.
original_delivery_datedateopcionalData de entrega combinada originalmente.
etadateopcionalData estimada de entrega. Um comprador a preenche manualmente pelo app (o modal de detalhe); nenhum processo automático a calcula. Em uma linha criada pela API, fica null até alguém preenchê-la.
actual_delivery_datedateopcionalData real de entrega, quando o status é DELIVERED.
received_quantityfloatopcionalQuantidade recebida, quando o status é DELIVERED.
statusstringopcionalStatus público (ver a tabela de status).
is_urgentbooleanobrigatórioSe a linha está marcada como urgente.
pausedbooleanobrigatórioSe o envio de acompanhamento está pausado (sending_paused).
pause_reasonstringopcionalMotivo da pausa. null se não estiver pausada.
followup_countintegerobrigatórioQuantidade de lembretes enviados no ciclo atual.
last_followup_sent_atdatetimeopcionalData e hora do último lembrete enviado.
next_followup_datedatetimeopcionalData planejada para o próximo lembrete.
supplier_responseobjectobrigatórioÚltima resposta do fornecedor.
supplier_response.responded_atdatetimeopcionalData e hora em que o fornecedor respondeu.
supplier_response.confirmed_delivery_datedateopcionalData de entrega que o fornecedor confirmou.
supplier_response.delay_reasonstringopcionalMotivo do atraso, se o fornecedor indicou.
supplier_response.notesstringopcionalNotas da última resposta do fornecedor. null se ainda não respondeu.
supplier_response.qualitystringopcionalQualidade da resposta interpretada pela IA (por exemplo, concrete).
created_atdatetimeopcionalData de criação do acompanhamento.
updated_atdatetimeopcionalData da última modificação.
Resposta - 200 OK
{
  "data": [
    {
      "id": 8821,
      "po_code": "OC-2026-0455",
      "line_position": 10,
      "source": "api",
      "supplier": {
        "id": 42,
        "name": "Distribuidora Industrial SA",
        "tax_id": "30712345678",
        "erp_code": "PROV-CAL-00123"
      },
      "supplier_name": "Distribuidora Industrial SA",
      "material_code": "MAT-001234",
      "description": "Rodamiento SKF 6205",
      "quantity_ordered": 50,
      "quantity_pending": 20,
      "unit_of_measure": "UN",
      "po_date": null,
      "original_delivery_date": "2026-10-15",
      "eta": "2026-10-18",
      "actual_delivery_date": null,
      "received_quantity": null,
      "status": "CONFIRMED_DELAYED",
      "is_urgent": false,
      "paused": false,
      "pause_reason": null,
      "followup_count": 2,
      "last_followup_sent_at": "2026-09-10T13:05:00+00:00",
      "next_followup_date": "2026-09-17T00:00:00+00:00",
      "supplier_response": {
        "responded_at": "2026-09-12T09:40:00+00:00",
        "confirmed_delivery_date": "2026-10-18",
        "delay_reason": "Demora del fabricante",
        "notes": "Se despachó el lote parcial, el resto llega la semana próxima.",
        "quality": "concrete"
      },
      "created_at": "2026-09-01T12:00:00+00:00",
      "updated_at": "2026-09-12T09:40:00+00:00"
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 50,
  "has_more": false
}

Obter um acompanhamento de entrega

GET/api/v1/delivery-trackings/{id}API Key obrigatória

Obter o detalhe de um acompanhamento pelo seu id interno.

Mesmo formato de cada elemento de GET /delivery-trackings.

Solicitação
curl -H "X-API-Key: sk_live_abc123..." \
  https://api.gosourced.ai/api/v1/delivery-trackings/8821
Resposta - 200 OK
{
  "id": 8821,
  "po_code": "OC-2026-0455",
  "line_position": 10,
  "source": "api",
  "supplier": {
    "id": 42,
    "name": "Distribuidora Industrial SA",
    "tax_id": "30712345678",
    "erp_code": "PROV-CAL-00123"
  },
  "supplier_name": "Distribuidora Industrial SA",
  "material_code": "MAT-001234",
  "description": "Rodamiento SKF 6205",
  "quantity_ordered": 50,
  "quantity_pending": 20,
  "unit_of_measure": "UN",
  "po_date": null,
  "original_delivery_date": "2026-10-15",
  "eta": "2026-10-18",
  "actual_delivery_date": null,
  "received_quantity": null,
  "status": "CONFIRMED_DELAYED",
  "is_urgent": false,
  "paused": false,
  "pause_reason": null,
  "followup_count": 2,
  "last_followup_sent_at": "2026-09-10T13:05:00+00:00",
  "next_followup_date": "2026-09-17T00:00:00+00:00",
  "supplier_response": {
    "responded_at": "2026-09-12T09:40:00+00:00",
    "confirmed_delivery_date": "2026-10-18",
    "delay_reason": "Demora del fabricante",
    "notes": "Se despachó el lote parcial, el resto llega la semana próxima.",
    "quality": "concrete"
  },
  "created_at": "2026-09-01T12:00:00+00:00",
  "updated_at": "2026-09-12T09:40:00+00:00"
}

Um id que não existe, ou que pertence a outra organização, retorna 404 dlvTrackingNotFound: a existência de um acompanhamento de outra organização nunca é confirmada.

Histórico de eventos de um acompanhamento

GET/api/v1/delivery-trackings/{id}/eventsAPI Key obrigatória

Linha do tempo paginada dos eventos de um acompanhamento, do mais antigo ao mais recente.

Parâmetros de consulta

CampoTipoObrigatórioDescrição
pageintegeropcionalNúmero da página (padrão: 1).
page_sizeintegeropcionalItens por página, 1-200 (padrão: 50).

Objeto de evento

CampoTipoObrigatórioDescrição
idintegerobrigatórioId interno do evento.
typestringobrigatórioTipo de evento (ver a tabela de tipos).
occurred_atdatetimeopcionalData e hora em que ocorreu.
actor_typestringobrigatórioQuem gerou: system, supplier, user ou api.
dataobjectobrigatórioDetalhe do evento; o formato depende de type (ver a tabela de tipos).

Tipos de evento

Tipodata
created{ source }
reactivated{ source } (o caminho da API também adiciona from com o status anterior)
cycle_reset{ reason? } — reinício do ciclo de acompanhamento sem reativação (troca de fornecedor pela API, reinício manual pelo app): a resposta do fornecedor anterior a este evento deixa de ser a vigente
line_updated{ <campo>: { from, to } } — uma chave para cada campo alterado (exceção: buyer_email leva só to, sem from)
followup_sent{ followup_number, escalated }
response_received{ confirmed_delivery_date, delay_reason, notes, quality, summary, outcome }
status_changed{ from, to, reason? }
paused{ reason }
resumed{ reason? }
urgency_changed{ is_urgent }
delivered{ from, to, reason?, actual_delivery_date, received_quantity }
cancelled{ from, to, reason? }
O evento note_added (notas manuais do comprador feitas pela UI) e o campo actor_ref nunca saem por esta API.
O histórico começa na data de lançamento desta funcionalidade: acompanhamentos que já existiam antes não têm eventos anteriores a esse momento (sem backfill).
Resposta - 200 OK
{
  "data": [
    {
      "id": 55009,
      "type": "created",
      "occurred_at": "2026-09-01T12:00:00+00:00",
      "actor_type": "api",
      "data": { "source": "api" }
    },
    {
      "id": 55012,
      "type": "status_changed",
      "occurred_at": "2026-09-12T09:40:00+00:00",
      "actor_type": "supplier",
      "data": {
        "from": "SENT_PENDING_RESPONSE",
        "to": "CONFIRMED_DELAYED",
        "reason": "supplier_response"
      }
    }
  ],
  "total": 2,
  "page": 1,
  "page_size": 50,
  "has_more": false
}

Ações em lote

POST/api/v1/delivery-trackings/actionsAPI Key obrigatória

Aplicar uma ação a até 200 linhas de uma vez. Se targets omitir line_position, a ação se aplica a todas as linhas abertas daquele pedido de compra.

Corpo da solicitação

CampoTipoObrigatórioDescrição
actionstringobrigatóriomark_delivered, cancel, pause, resume, set_urgent ou unset_urgent.
targetsarrayobrigatórioLinhas às quais aplicar a ação. Entre 1 e 200.
targets[].po_codestringobrigatórioNúmero do pedido de compra. Máximo de 100 caracteres.
targets[].line_positionintegeropcionalPosição da linha. Se omitida, aplica-se a todas as linhas abertas do pedido de compra.
reasonstringopcionalMotivo. Obrigatório para pause; opcional para as demais. Máximo de 500 caracteres.
actual_delivery_datedateopcionalData real de entrega. Usada apenas com mark_delivered; padrão: hoje.
received_quantityfloatopcionalQuantidade recebida. Usada apenas com mark_delivered. ≥ 0.
Omitir targets[].line_position aplica a ação a todas as linhas abertas (não entregues nem canceladas) daquele pedido de compra.

Ações disponíveis

actionEfeitoRequer
mark_deliveredPassa para DELIVERED. actual_delivery_date usa a data enviada ou, se não enviada, a data de hoje da organização. received_quantity é registrada se enviada.
cancelPassa para CANCELLED.
pausePausa o envio de lembretes (sending_paused = true).reason
resumeRetoma o envio de lembretes (sending_paused = false).
set_urgentMarca a linha como urgente.
unset_urgentDesmarca a linha como urgente.
  • As ações são idempotentes: repetir uma ação já aplicada não registra um evento novo e retorna um destes seis resultados: already_delivered, already_cancelled, already_paused, already_active (para resume), already_urgent ou already_not_urgent.
  • Não é possível cruzar entre status terminais: mark_delivered em uma linha CANCELLED, ou cancel em uma linha DELIVERED, retorna o erro dlvActionNotAllowedInStatus. Para reabrir uma linha encerrada é preciso reenviá-la pelo POST /delivery-trackings (o upsert reativa).
  • mark_delivered e cancel só se aplicam a linhas criadas por esta API (source = "api"); em linhas de CSV ou da sincronização ERP retornam dlvTrackingManagedByOtherSource. pause, resume, set_urgent e unset_urgent se aplicam a linhas de qualquer fonte, desde que não estejam em um status terminal.
Solicitação
curl -X POST https://api.gosourced.ai/api/v1/delivery-trackings/actions \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "action": "mark_delivered",
    "targets": [
      { "po_code": "OC-2026-0455", "line_position": 10 }
    ],
    "received_quantity": 50
  }'

Resposta das ações

CampoTipoObrigatórioDescrição
applied_countintegerobrigatórioQuantidade de acompanhamentos (não de targets da requisição) em que a ação foi aplicada. Os resultados already_* não entram nessa conta.
error_countintegerobrigatórioQuantidade de acompanhamentos (não de targets da requisição) com erro. Os resultados already_* não entram nessa conta.
resultsarrayobrigatórioResultado de cada target.

Objeto de resultado por target (results[])

CampoTipoObrigatórioDescrição
po_codestringobrigatórioNúmero do pedido de compra.
line_positionintegeropcionalPosição da linha afetada. Pode diferir do target se foi omitida (um pedido de compra inteiro pode gerar vários resultados).
idintegeropcionalId interno do acompanhamento. Ausente quando o target não corresponde a nenhum acompanhamento.
resultstringobrigatórioapplied, error, ou um dos seis resultados idempotentes: already_delivered, already_cancelled, already_paused, already_active, already_urgent, already_not_urgent.
statusstringopcionalStatus do acompanhamento após a ação.
codestringopcionalCódigo do erro (dlv*). Presente apenas quando result é error.
messagestringopcionalDetalhe do erro em inglês, pensado para logs.
Resposta - 200 OK
// 200 OK
{
  "applied_count": 1,
  "error_count": 0,
  "results": [
    {
      "po_code": "OC-2026-0455",
      "line_position": 10,
      "id": 8821,
      "status": "DELIVERED",
      "result": "applied"
    }
  ]
}

Códigos de erro — acompanhamento de entregas

Códigos próprios dos endpoints de acompanhamento de entregas. Cinco são erros da solicitação inteira (coluna HTTP 400/403/404); o resto viaja no campo code de cada linha ou target com erro, dentro de uma resposta 200. Um campo obrigatório ausente ou inválido (descrição, quantidades, fornecedor sem código ERP nem CNPJ/CPF, pendente maior que o pedido) não tem código próprio: rejeita o lote inteiro com 422 e o detalhe padrão de validação.

CódigoHTTPSignificado
dlvFeatureDisabled403O acompanhamento de entregas não está habilitado para esta organização.
dlvTrackingNotFound404Acompanhamento não encontrado (id inexistente, ou de outra organização).
dlvTrackingManagedByOtherSource200Esta linha é gerenciada por outra fonte (CSV ou sincronização ERP).
dlvSupplierReferenceConflict200O código ERP e o CNPJ/CPF correspondem a fornecedores diferentes.
dlvSupplierReferenceAmbiguous200O código ERP ou o CNPJ/CPF corresponde a mais de um fornecedor.
dlvDuplicateLineInBatch200A linha (mesmo po_code e line_position) aparece mais de uma vez no envio.
dlvNothingPending200A linha não tem nada pendente (quantity_pending = 0): não se cria um acompanhamento para cobrar 0 unidades.
dlvConcurrentUpsert200A linha foi modificada ao mesmo tempo por outro envio. Tente novamente.
dlvLineWriteFailed200Não foi possível salvar a linha. Verifique os dados e tente novamente (não é um problema de concorrência).
dlvInvalidStatusFilter400O valor de status não é um status válido.
dlvInvalidSince400Data inválida em since. Use o formato ISO 8601.
dlvPauseReasonRequired400pause exige que um reason seja informado.
dlvActionNotAllowedInStatus200A ação não se aplica ao status atual do acompanhamento.
200 = viaja dentro da resposta, no code de uma linha ou target específico (o resto do lote segue normalmente). 400/403/404 = rejeita a solicitação inteira. 422 = validação do corpo (campo obrigatório ausente ou inválido), também rejeita a solicitação inteira. dlvTrackingNotFound é as duas coisas: 404 nos GET por id, e por target dentro de /actions.

Webhooks

Receba eventos em tempo real quando um pedido de compra, uma requisição ou um acompanhamento de entrega muda, em vez de precisar consultá-los por polling.

Configuração

Os webhooks são configurados a partir do aplicativo (não por API key), por um usuário organization_admin, em Minha Organização → Desenvolvedores → Webhooks.

Ao criar um webhook, um secret é gerado e exibido apenas uma vez: guarde-o, ele é usado para verificar a assinatura de cada entrega.

Envelope

Todo evento chega com este formato:

Evento
{
  "id": "evt_5f2a1c9b8e7d4a3f1b2c",
  "type": "delivery_tracking.status_changed",
  "created_at": "2026-09-17T14:05:00+00:00",
  "data": {
    "tracking": "... mismo objeto que devuelve GET /delivery-trackings/{id} ...",
    "event": "... mismo objeto que devuelve GET /delivery-trackings/{id}/events ..."
  }
}

Headers

CampoTipoObrigatórioDescrição
X-Webhook-SignaturestringobrigatórioAssinatura HMAC-SHA256 do corpo bruto da solicitação, com o prefixo sha256=.
X-Webhook-EventstringobrigatórioO type do evento — o mesmo valor do campo type de nível superior do envelope. Não existe data.type: data é diretamente o recurso (ou o par tracking / event nos eventos de acompanhamento).
X-Webhook-Delivery-IdstringobrigatórioId do evento, não deste intento de entrega específico: é o mesmo valor em cada nova tentativa e em cada webhook inscrito que o recebe (igual a id no corpo) — por isso serve para deduplicar.

Verificar a assinatura

Calcule o HMAC-SHA256 sobre os bytes brutos do corpo da solicitação (antes de fazer o parse do JSON) usando o secret do webhook, e compare com X-Webhook-Signature usando uma comparação de tempo constante.

Python
import hashlib
import hmac

def verify_webhook(secret: str, raw_body: bytes, signature_header: str | None) -> bool:
    if not signature_header or not signature_header.startswith("sha256="):
        return False
    expected = "sha256=" + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    # compare_digest on two strings of different length returns False,
    # it never raises — safe even with a malformed header.
    return hmac.compare_digest(expected, signature_header)
Node.js
const crypto = require("crypto");

function verifyWebhook(secret, rawBody, signatureHeader) {
  if (!signatureHeader || !signatureHeader.startsWith("sha256=")) {
    return false;
  }
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const expectedBuf = Buffer.from(expected);
  const receivedBuf = Buffer.from(signatureHeader);
  // timingSafeEqual throws on a length mismatch — check first, it never
  // needs to be constant-time (the lengths themselves aren't a secret).
  if (expectedBuf.length !== receivedBuf.length) {
    return false;
  }
  return crypto.timingSafeEqual(expectedBuf, receivedBuf);
}
A assinatura é calculada sobre o corpo BRUTO, exatamente como chega pela rede. Se o seu framework já fez o parse do JSON antes de você acessar os bytes originais, a verificação vai falhar mesmo que a assinatura esteja correta — você precisa do corpo sem processamento.

Novas tentativas

Se a entrega falhar (timeout, erro de rede, ou uma resposta que não seja 2xx), ela é tentada novamente até 3 vezes (4 tentativas no total: o envio inicial mais 3 novas tentativas). Cada tentativa que falha agenda a próxima com um backoff mínimo de 1, 5 e 15 minutos; essa nova tentativa só é disparada na próxima vez que a varredura de novas tentativas (reminder-checker) roda, o que acontece a cada hora em horário comercial argentino — o tempo real até a próxima tentativa pode ser maior que esses minutos.

  • Tentativa 1: imediata, no momento do evento.
  • Tentativa 2: não antes de 1 minuto depois.
  • Tentativa 3: não antes de 5 minutos depois.
  • Tentativa 4: não antes de 15 minutos depois. Se também falhar, a entrega fica FAILED.
O mesmo evento pode chegar mais de uma vez (novas tentativas, reprocessamento). Deduplique pelo id do evento (ou pelo header X-Webhook-Delivery-Id, que é o mesmo valor).

Eventos disponíveis

typedataQuando dispara
purchase_order.createdobjeto do pedido de compraUm novo pedido de compra foi criado.
purchase_order.updatedid, code, status, reasonO status ou outro dado de um pedido de compra mudou (por exemplo, uma rejeição).
requisition.discardedexternal_id, requisition_id, discarded_by, discarded_atUma requisição foi descartada (por exemplo, cancelada a partir do ERP com POST /requisitions/cancel).
delivery_tracking.response_receivedtracking, eventO fornecedor respondeu ao pedido de confirmação de entrega.
delivery_tracking.status_changedtracking, eventO status do acompanhamento mudou (inclui a reativação de uma linha previamente encerrada, que volta a PENDING_SCHEDULE).
delivery_tracking.deliveredtracking, eventO acompanhamento passou para DELIVERED.
delivery_tracking.cancelledtracking, eventO acompanhamento passou para CANCELLED.
O formato exato de data em purchase_order.created/updated nem sempre é o mesmo: depende do que disparou o evento (uma adjudicação dividida manda um resumo menor do que uma completa; alguns gatilhos usam po_id em vez de id, ou adicionam updated_fields em vez de reason). Trate-os como um sinal para reler o recurso via GET, não como um contrato fixo.
No payload do webhook, o bloco supplier do tracking NÃO inclui erp_code (diferente do GET /delivery-trackings, que inclui).
Eventos originados em um processo em segundo plano (por exemplo, a resposta de um fornecedor por e-mail) podem demorar até uma hora para disparar. Não dependa só do webhook — use GET /delivery-trackings?since= como reconciliação periódica.

Referência de esquemas

Referência rápida de todos os esquemas de solicitação/resposta usados nos endpoints.

Status de requisição

StatusDescrição
PENDINGImportada, aguardando revisão de um comprador no Sourced.
LAUNCHEDO comprador lançou a requisição como Solicitação de Compra.
DISCARDEDA requisição foi descartada manualmente.
SUPERSEDEDUma versão mais nova foi importada com o mesmo external_id.

Status de ordem de compra

StatusDescrição
DRAFTOC criada no Sourced, pendente de sincronização com o ERP externo.
CREATEDOC criada no ERP externo, pendente de aprovação.
CONFIRMEDOC totalmente aprovada no ERP externo.
REJECTEDOC rejeitada no ERP externo. A PR associada é reaberta automaticamente.
CANCELLEDCompra cancelada. A OC é cancelada e a PR associada também - nada é reaberto.
Em breve serão adicionados estados adicionais como RECEIVED (mercadoria recebida), INVOICED (faturada), entre outros, para refletir o ciclo de vida completo da ordem de compra.

Códigos de referência

Catálogos de moedas e tipos de pagamento aceitos pela API. Use-os para mapear esses valores com os códigos equivalentes no seu ERP.

Moedas

O Sourced não usa um enum fechado de moedas: qualquer código ISO 4217 válido de 3 letras é aceito. As moedas mais utilizadas pelos nossos clientes são:

CódigoMoeda
ARSPeso argentino
USDDólar americano
EUREuro
BRLReal brasileiro
Envie o código ISO 4217 diretamente no campo currency (ex: "currency": "EUR"). O Sourced o persiste como está e o devolve igual nas respostas.

Tipos de pagamento (payment_terms_code)

Catálogo global de condições de pagamento disponíveis no Sourced. Use o código (coluna Code) no campo payment_terms_code da PO. O campo type indica a natureza do pagamento: IMMEDIATE (no recebimento), ADVANCE (antecipado), NET_DAYS (a X dias da fatura).

CódigoNomeTipoDiasDescrição
CODCash On DeliveryIMMEDIATE0Pagamento contra entrega
IMMImmediate PaymentIMMEDIATE0Pagamento imediato / À vista
ADV100Advance PaymentADVANCE0Pagamento antecipado 100%
ADV50Advance 50%ADVANCE0Pagamento antecipado de 50%
NET7Net 7 DaysNET_DAYS7Pagamento em 7 dias da data da fatura
NET10Net 10 DaysNET_DAYS10Pagamento em 10 dias da data da fatura
NET15Net 15 DaysNET_DAYS15Pagamento em 15 dias da data da fatura
NET20Net 20 DaysNET_DAYS20Pagamento em 20 dias da data da fatura
NET21Net 21 DaysNET_DAYS21Pagamento em 21 dias da data da fatura
NET30Net 30 DaysNET_DAYS30Pagamento em 30 dias da data da fatura
EOM30End of Month + 30NET_DAYS30Pagamento no fim do mês mais 30 dias
NET35Net 35 DaysNET_DAYS35Pagamento em 35 dias da data da fatura
NET40Net 40 DaysNET_DAYS40Pagamento em 40 dias da data da fatura
NET45Net 45 DaysNET_DAYS45Pagamento em 45 dias da data da fatura
NET60Net 60 DaysNET_DAYS60Pagamento em 60 dias da data da fatura
NET75Net 75 DaysNET_DAYS75Pagamento em 75 dias da data da fatura
NET90Net 90 DaysNET_DAYS90Pagamento em 90 dias da data da fatura
O catálogo é global em toda a plataforma e raramente muda. Se você precisar de um código adicional para sua integração, entre em contato.

Unidades de medida

Códigos de unidade de medida aceitos em itens de requisições. Enviados no campo unit_of_measure.

CódigoUnidade
EAUnidade
PCSPeças
KGQuilograma
GGrama
LBLibra
OZOnça
MMetro
CMCentímetro
MMMilímetro
INPolegada
FT
YDJarda
LLitro
MLMililitro
GALGalão
QTQuarto de galão
PTPinta
FL_OZOnça líquida
M2Metro quadrado
CM2Centímetro quadrado
FT2Pé quadrado
IN2Polegada quadrada
YD2Jarda quadrada
M3Metro cúbico
CM3Centímetro cúbico
FT3Pé cúbico
IN3Polegada cúbica
YD3Jarda cúbica
BOXCaixa
CASECaixa/Estojo
PACKPacote
SETJogo
KITKit
BUNDLEFardo
ROLLRolo
SHEETFolha/Lâmina
PALLETPalete
DRUMTambor/Barril
BAGSaco
BOTTLEGarrafa
CENCentena
Se você enviar uma unidade não reconhecida, ela é armazenada como está. Recomendamos usar os códigos padrão para que a IA possa normalizar corretamente as cotações dos fornecedores.

Fluxo de integração

Padrão de integração típico para uma sincronização ERP agendada (ex: cron job a cada 15 minutos):

1

Coletar novas requisições do ERP

Consultar seu ERP por requisições aprovadas que ainda não foram sincronizadas com o Sourced.

2

Verificar existentes (opcional)

Chamar POST /requisitions/check-existing com external_ids para filtrar requisições já sincronizadas.

3

Importar requisições

Chamar POST /requisitions com o lote de requisições novas/atualizadas. Armazenar os requisition_ids retornados.

4

Consultar novas ordens de compra

Chamar GET /purchase-orders?since={last_sync_timestamp}&status=DRAFT para obter OC novas. Criar a OC no seu ERP e chamar POST /purchase-orders/{id}/status com {"status": "CREATED"}.

5

Confirmar OCs aprovadas

Quando a OC for aprovada no seu ERP, chamar POST /purchase-orders/{id}/status com {"status": "CONFIRMED"} para fechar o ciclo.

Pseudocódigo - Cron job de sincronização
# Step 1: Get pending requisitions from ERP
ERP_REQS=$(query_erp_pending_requisitions)

# Step 2: Check which ones already exist in Sourced
EXISTING=$(curl -s -X POST \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{"external_ids": $ERP_REQ_IDS}" \
  https://api.gosourced.ai/api/v1/requisitions/check-existing)

# Step 3: Import only new requisitions
NEW_REQS=$(filter_not_found $ERP_REQS $EXISTING)
curl -s -X POST \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{"requisitions": $NEW_REQS}" \
  https://api.gosourced.ai/api/v1/requisitions

# Step 4: Pull new POs since last sync
POS=$(curl -s \
  -H "X-API-Key: $API_KEY" \
  "https://api.gosourced.ai/api/v1/purchase-orders?since=$LAST_SYNC&status=DRAFT")

# Step 5: Create POs in ERP and update status
for PO in $POS; do
  create_po_in_erp $PO
  curl -s -X POST \
    -H "X-API-Key: $API_KEY" \
    -H "Content-Type: application/json" \
    -d "{"status": "CREATED", "external_id": "$ERP_PO_NUMBER"}" \
    "https://api.gosourced.ai/api/v1/purchase-orders/$PO_ID/status"
done

# Step 6: When PO is approved in ERP, confirm it
curl -s -X POST \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "CONFIRMED"}' \
  "https://api.gosourced.ai/api/v1/purchase-orders/$PO_ID/status"

Exemplo completo

Uma solicitação de importação realista com todos os campos disponíveis preenchidos:

Solicitação de importação completa
curl -X POST https://api.gosourced.ai/api/v1/requisitions \
  -H "X-API-Key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "requisitions": [
      {
        "external_id": "CAL-REQ-2026-0042",
        "description": "Repuestos bomba centrífuga Planta Norte",
        "requester_name": "Carlos Rodríguez",
        "requester_email": "carlos.rodriguez@empresa.com",
        "comments": "Urgente: bomba fuera de servicio desde 03/03. <b>Necesitamos entrega express.</b>",
        "delivery_address": "Av. Industrial 4500, Parque Industrial Pilar, Buenos Aires",
        "delivery_address_code": "PLANTA-NORTE",
        "desired_delivery_lead_time_days": 7,
        "created_date": "2026-03-04T09:15:00Z",
        "offer_deadline": "2026-03-10T18:00:00Z",
        "total_estimated_value": 1250000.00,
        "currency": "ARS",
        "department_code": "MANT-001",
        "priority_level": "HIGH",
        "items": [
          {
            "external_line_id": "CAL-REQ-2026-0042-L10",
            "description": "Sello mecánico para bomba centrífuga KSB ETA 50-200",
            "quantity": 2,
            "unit_of_measure": "UN",
            "target_price": 185000.00,
            "estimated_price": 370000.00,
            "currency": "ARS",
            "category": "Repuestos Bombas",
            "material_code": "MAT-BOM-0234",
            "desired_delivery_lead_time_days": 10,
            "detail": "Sello tipo cartucho, material: carburo de silicio / carburo de silicio",
            "specifications": {
              "manufacturer_code": "KSB-SEAL-50200",
              "manufacturer_name": "KSB",
              "manufacturer_description": "Mechanical seal for ETA 50-200 centrifugal pump",
              "buyer_code": "MAT-BOM-0234",
              "buyer_code_description": "Sello mecánico bomba KSB ETA 50-200",
              "buyer_code_system": "Calipso",
              "technical_specs": "Diámetro eje: 35mm, Material caras: SiC/SiC, Elastómeros: Viton",
              "requirements": "Debe incluir certificado de calidad. Preferencia por repuesto original KSB."
            }
          },
          {
            "external_line_id": "CAL-REQ-2026-0042-L20",
            "description": "Rodamiento SKF 6310-2RS",
            "quantity": 4,
            "unit_of_measure": "UN",
            "target_price": 45000.00,
            "currency": "ARS",
            "category": "Rodamientos",
            "material_code": "MAT-ROD-0089",
            "specifications": {
              "manufacturer_code": "6310-2RS1",
              "manufacturer_name": "SKF",
              "technical_specs": "50x110x27mm, sellado ambos lados, grasa estándar"
            }
          },
          {
            "external_line_id": "CAL-REQ-2026-0042-L30",
            "description": "Aceite lubricante ISO VG 68",
            "quantity": 20,
            "unit_of_measure": "LT",
            "target_price": 5500.00,
            "currency": "ARS",
            "category": "Lubricantes",
            "specifications": {
              "technical_specs": "Aceite mineral ISO VG 68, índice de viscosidad > 95"
            }
          }
        ],
        "attachments": [
          {
            "url": "https://erp.empresa.com/files/plano-bomba-eta-50-200.pdf",
            "filename": "plano_bomba_KSB_ETA_50-200.pdf",
            "description": "Plano de despiece bomba KSB ETA 50-200",
            "file_type": "application/pdf"
          }
        ],
        "raw_data": {
          "calipso_doc_type": "SOL",
          "calipso_doc_number": "0042",
          "calipso_branch": "001",
          "approved_by": "María González",
          "cost_center": "CC-MANT-NORTE"
        }
      }
    ]
  }'
Resposta - 200 OK
{
  "success": true,
  "created_count": 1,
  "updated_count": 0,
  "skipped_count": 0,
  "error_count": 0,
  "errors": [],
  "requisition_ids": [1234]
}