Pular para o conteúdo principal

Sipru — API de parceiros ERP (1.1.0)

Download OpenAPI specification:Download

Handoff SSO, canal ao vivo e push de produto para a tela do vendedor.

Contrato server-to-server entre o ERP parceiro e o Sipru. Cobre os dois modos de integração hoje em produção:

  • Handoff SSO (POST /api/erp/sso/session): o ERP pede uma URL de entrada e o vendedor "sai" do ERP já logado na conta Sipru real dele, com a cotação e o cliente anexados ao contexto.
  • Canal ao vivo (POST /api/erp/channel): além do handoff, abre um canal em que o ERP adiciona produtos à tela que o vendedor está olhando (POST /api/erp/channel/{channelId}/product), até o ERP fechar a cesta (POST /api/erp/channel/{channelId}/cesta).

O caminho de volta — o Sipru avisando o ERP que um item entrou no carrinho — está descrito em webhooks, e é implementado pelo parceiro.

POST /api/erp/cart/orcamento é a rota de fechamento usada pela tela do Sipru, com a sessão do vendedor — não pelo ERP. Veja a nota de segurança na própria operação.

Autenticação. Salvo o fechamento, toda rota exige as duas credenciais juntas: X-Sipru-Partner-Id: <id do parceiro> e Authorization: Bearer <segredo do parceiro>. O segredo é emitido em /integracoes/erp-sso e guardado apenas como hash — ele é exibido uma única vez, na emissão ou na rotação.

environment é sandbox por padrão. Esse campo decide qual banco do ERP recebe a gravação do pedido, e apenas o literal "prd" seleciona a base real. Qualquer outro valor, incluindo o campo ausente, cai no sandbox — deliberadamente, para que um campo esquecido nunca escreva na base de produção do parceiro.

Handoff SSO

Entrada do vendedor no catálogo Sipru a partir de uma cotação do ERP.

Emite a URL de entrada do vendedor

Devolve uma URL de uso único, válida por ERP_SSO_TOKEN_TTL_SEC (600 s por padrão). A URL funciona exatamente uma vez: um segundo clique ou um reload da mesma URL responde ERP_SSO_TOKEN_ALREADY_USED. Para testar de novo, gere uma URL nova — esse comportamento é a proteção contra replay, não um defeito.

Authorizations:
(partnerIdpartnerSecret)
Request Body schema: application/json
required
tempQuoteId
required
string <= 120 characters

Identificador da cotação em aberto no ERP. É a chave de idempotência da gravação do pedido — uma gravação por cotação.

quoteId
string <= 120 characters

Número definitivo da cotação/orçamento, quando já existe.

required
object non-empty

Identificação do vendedor no ERP. Envie pelo menos um identificador — os mais comuns estão abaixo, mas qualquer outro com que o ERP trabalhe também é aceito. É por eles que o Sipru encontra a conta real do vendedor; se nenhum corresponder a uma conta, a chamada falha com VENDEDOR_NOT_FOUND.

object (Cliente)

Cliente da cotação. Todos os campos são opcionais.

companyId
string <= 120 characters

Tenant no Sipru. Opcional: se omitido, locationId é tratado como o CNPJ da loja e o Sipru resolve companyId e o locationId real pelo catálogo de lojas.

locationId
required
string <= 120 characters

Loja. Com companyId presente, é o identificador da loja no tenant; sem ele, é o CNPJ da loja (string opaca, aceita caracteres não numéricos).

pricingPolicy
string (PricingPolicy)
Enum: "margem1" "margem2" "margem3" "margem4"

Faixa de margem aplicada aos preços exibidos. Valor desconhecido ou ausente cai em margem1.

environment
string (Environment)
Enum: "dev" "prd"

Banco do ERP que recebe a gravação do pedido. Só o literal "prd" seleciona a base real; qualquer outro valor, ou o campo ausente, significa sandbox.

redirectPath
string

Caminho interno para onde mandar o vendedor depois do login. Precisa começar com / e não pode começar com // — qualquer outro valor é ignorado.

Responses

Request samples

Content type
application/json
{
  • "tempQuoteId": "temp-12345",
  • "quoteId": "ORC-2026-1187",
  • "vendedor": {
    },
  • "cliente": {
    },
  • "locationId": "07526557000100",
  • "pricingPolicy": "margem2",
  • "environment": "dev"
}

Response samples

Content type
application/json
{
  • "expiresAt": "2019-08-24T14:15:22Z",
  • "contextId": "84451116-c600-49e2-9c60-5b6b34fae0d6"
}

Canal ao vivo

Canal por cotação em que o ERP adiciona produtos à tela do vendedor, do primeiro item ao fechamento da cesta.

Abre um canal ao vivo para a cotação

Faz tudo o que /api/erp/sso/session faz e ainda cria um canal identificado por channelId, com os endpoints para o ERP adicionar produtos e para a tela do vendedor consumir os eventos.

Se cartCallbackUrl for informado, ele tem precedência sobre a configuração de loja em /integracoes/cart-erp para o webhook de item adicionado (ver webhooks).

Authorizations:
(partnerIdpartnerSecret)
Request Body schema: application/json
required
tempQuoteId
required
string <= 120 characters

Identificador da cotação em aberto no ERP. É a chave de idempotência da gravação do pedido — uma gravação por cotação.

quoteId
string <= 120 characters

Número definitivo da cotação/orçamento, quando já existe.

required
object non-empty

Identificação do vendedor no ERP. Envie pelo menos um identificador — os mais comuns estão abaixo, mas qualquer outro com que o ERP trabalhe também é aceito. É por eles que o Sipru encontra a conta real do vendedor; se nenhum corresponder a uma conta, a chamada falha com VENDEDOR_NOT_FOUND.

object (Cliente)

Cliente da cotação. Todos os campos são opcionais.

companyId
string <= 120 characters

Tenant no Sipru. Opcional: se omitido, locationId é tratado como o CNPJ da loja e o Sipru resolve companyId e o locationId real pelo catálogo de lojas.

locationId
required
string <= 120 characters

Loja. Com companyId presente, é o identificador da loja no tenant; sem ele, é o CNPJ da loja (string opaca, aceita caracteres não numéricos).

pricingPolicy
string (PricingPolicy)
Enum: "margem1" "margem2" "margem3" "margem4"

Faixa de margem aplicada aos preços exibidos. Valor desconhecido ou ausente cai em margem1.

environment
string (Environment)
Enum: "dev" "prd"

Banco do ERP que recebe a gravação do pedido. Só o literal "prd" seleciona a base real; qualquer outro valor, ou o campo ausente, significa sandbox.

cartCallbackUrl
string <uri> <= 500 characters

Endpoint do parceiro que recebe o webhook cart_item_added desta cotação. Precisa ser HTTPS em produção (HTTP é aceito apenas em DEV, para apontar para host interno na homologação).

object

Credencial estática do callback. OAuth não é suportado aqui.

ttlSec
integer [ 1 .. 86400 ]

Validade do canal em segundos. Valores acima de 86400 são truncados; valor inválido cai no padrão do servidor.

Responses

Request samples

Content type
application/json
{
  • "tempQuoteId": "temp-12345",
  • "vendedor": {
    },
  • "locationId": "07526557000100",
  • "pricingPolicy": "margem1",
  • "environment": "dev",
  • "cartCallbackAuth": {
    },
  • "ttlSec": 14400
}

Response samples

Content type
application/json
{}

Adiciona um produto à tela do vendedor

Resolve o termo no catálogo da loja do canal — com o tenant e a política de preço do próprio canal — e publica o resultado na tela que o vendedor está olhando, junto com substitutos, cross-sell e upsell.

A precedência do termo é productCode → ean → query, porque o código do parceiro é o mais determinístico e o texto livre é o último recurso (cobre o caso de o código não existir no catálogo Sipru).

Authorizations:
(partnerIdpartnerSecret)
path Parameters
channelId
required
string <uuid>

O channelId devolvido por POST /api/erp/channel.

Request Body schema: application/json
required
Any of
productCode
required
string <= 120 characters

Código do produto no ERP do parceiro.

ean
string <= 60 characters
query
string <= 120 characters

Texto livre.

quantity
integer [ 1 .. 9999 ]
Default: 1

Valor inválido ou ausente vira 1; acima de 9999 é truncado.

Responses

Request samples

Content type
application/json
Example
{
  • "productCode": "72411",
  • "quantity": 4
}

Response samples

Content type
application/json
{
  • "seq": 0,
  • "product": {
    },
  • "recommendations": {
    }
}

O ERP fecha a cesta

Quando a cesta é fechada no ERP, o ERP registra no Sipru o que foi fechado, com os itens finais. Credencial de parceiro, como as demais rotas do canal.

Não é uma segunda gravação do pedido: os itens já chegaram ao ERP durante o atendimento. Refechar a cesta sobrescreve o registro anterior em vez de recusar, e o canal continua aberto depois do fechamento.

Campos além de items, no corpo ou em cada linha, são guardados como vieram e nunca recusam o fechamento.

POST /api/erp/channel/{channelId}/orcamento é o nome antigo desta rota, mantido por compatibilidade e com a mesma resposta. Integrações novas usam /cesta.

Authorizations:
(partnerIdpartnerSecret)
path Parameters
channelId
required
string <uuid>

O channelId devolvido por POST /api/erp/channel.

Request Body schema: application/json
required
required
Array of objects [ 1 .. 200 ] items

Os itens finais da cesta no ERP.

cestaId
string

Identificador da cesta no ERP.

cestaCodigoExterno
string

Código externo da cesta no ERP.

cestaAbertaEm
string <date-time>

Quando a cesta foi aberta no ERP.

cestaFechadaEm
string <date-time>

Quando a cesta foi fechada no ERP.

property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "items": [
    ],
  • "cestaId": "CST-55120",
  • "cestaCodigoExterno": "000123",
  • "cestaAbertaEm": "2026-09-03T18:02:10-03:00",
  • "cestaFechadaEm": "2026-09-03T18:22:40-03:00"
}

Response samples

Content type
application/json
{
  • "success": true,
  • "id": "temp-12345:cesta",
  • "documentType": "cesta",
  • "tempQuoteId": "temp-12345",
  • "quoteId": null,
  • "itemCount": 3,
  • "total": 78.73,
  • "closedCount": 12,
  • "createdAt": "2026-09-14T11:46:27.592Z",
  • "updatedAt": "2026-10-13T21:44:22.335Z",
  • "extra": {
    }
}

O ERP informa a venda concluída

Quando a venda é concluída no ERP, o ERP informa à Sipru com o número do pedido (orderCode, o mesmo order_code das vendas que o ERP envia para a Sipru). É o que confirma os Sipru Points do vendedor contra as vendas do ERP.

Vale depois do atendimento: com o canal expirado ou encerrado, a venda é aceita se aquele canal já teve uma cesta fechada pelo mesmo parceiro. Chamar de novo sobrescreve a venda do atendimento.

Campos além de items e orderCode, no corpo ou em cada linha, são guardados como vieram e nunca recusam a venda. Um orderCode fora do formato é ignorado.

Authorizations:
(partnerIdpartnerSecret)
path Parameters
channelId
required
string <uuid>

O channelId do atendimento que gerou a cesta.

Request Body schema: application/json
required
required
Array of objects [ 1 .. 200 ] items

Os itens finais da cesta no ERP.

cestaId
string

Identificador da cesta no ERP.

cestaCodigoExterno
string

Código externo da cesta no ERP.

cestaAbertaEm
string <date-time>

Quando a cesta foi aberta no ERP.

cestaFechadaEm
string <date-time>

Quando a cesta foi fechada no ERP.

orderCode
string <= 64 characters ^[A-Za-z0-9._\-/]{1,64}$

Número do pedido no ERP: o mesmo order_code das vendas que o ERP envia para a Sipru. Recomendado; fora do formato é ignorado.

property name*
additional property
any

Responses

Request samples

Content type
application/json
{
  • "orderCode": "536215",
  • "items": [
    ]
}

Response samples

Content type
application/json
{
  • "success": true,
  • "id": "temp-12345:venda",
  • "documentType": "venda",
  • "tempQuoteId": "temp-12345",
  • "quoteId": null,
  • "itemCount": 1,
  • "total": 139.6,
  • "closedCount": 1,
  • "createdAt": "2026-10-16T14:02:10.000Z",
  • "updatedAt": "2026-10-16T14:02:10.000Z",
  • "extra": {
    }
}

Fecha o atendimento como cesta

Registra no Sipru que o atendimento do canal virou cesta.

Não é uma rota de parceiro. Quem chama é a tela do Sipru, com a sessão do vendedor dono do canal — sem Authorization: Bearer e sem X-Sipru-Partner-Id. O ERP fecha a cesta por POST /api/erp/channel/{channelId}/cesta.

O caminho guardou o nome antigo do documento (orcamento), que passou a se chamar cesta em 11/09/2026: a rota é a mesma e o documentType da resposta volta como cesta.

Authorizations:
sessaoVendedor
Request Body schema: application/json
required
channelId
required
string

O channelId devolvido por POST /api/erp/channel.

required
Array of objects (CartCloseItem) [ 1 .. 200 ] items

Os itens do atendimento. Quem fecha é quem sabe o que foi montado na tela, por isso a lista vem no corpo.

Responses

Request samples

Content type
application/json
{
  • "channelId": "8f2b0f1c-4d3a-4d1e-9f77-2b1c9a5e7d10",
  • "items": [
    ]
}

Response samples

Content type
application/json
{
  • "success": true,
  • "id": "temp-12345:cesta",
  • "documentType": "cesta",
  • "tempQuoteId": "temp-12345",
  • "quoteId": null,
  • "itemCount": 3,
  • "total": 78.73,
  • "closedCount": 12,
  • "createdAt": "2026-09-14T11:46:27.592Z",
  • "updatedAt": "2026-10-13T21:44:22.335Z",
  • "extra": {
    }
}

(parceiro implementa) Item adicionado ao carrinho Webhook

Quando o vendedor clica em "adicionar" na tela do Sipru, o Sipru faz POST no endpoint do parceiro. O destino é resolvido nesta ordem:

  1. cartCallbackUrl do canal, quando enviado na criação;
  2. cartCallbackUrl cadastrada para a loja em /integracoes/cart-erp;
  3. erpBaseUrl + orderEndpointPath da mesma configuração.

Sem nenhum dos três, o clique não vira POST.

Contrato esperado do parceiro: responder 2xx. O Sipru tenta 2 vezes, com timeout de 8 s, e só repete em status transitório (408, 429, 5xx) — um 4xx não transitório recusa o item de vez. Deduplique por Idempotency-Key, que também vem no corpo como idempotencyKey.

Authorizations:
(partnerIdpartnerSecret)
header Parameters
Idempotency-Key
required
string

Igual ao idempotencyKey do corpo. Formato erp_cart_<uuid>.

Request Body schema: application/json
required
idempotencyKey
required
string
event
required
string
Value: "cart_item_added"
channelId
required
string <uuid>
tempQuoteId
required
string
quoteId
string or null
object (Cliente)

Cliente da cotação. Todos os campos são opcionais.

required
object
required
object
required
object
timestamp
required
string <date-time>

Responses

Request samples

Content type
application/json
{
  • "idempotencyKey": "erp_cart_4f1c…",
  • "event": "cart_item_added",
  • "channelId": "5f6d08bc-455a-4532-98b8-19e2cee51160",
  • "tempQuoteId": "string",
  • "quoteId": "string",
  • "cliente": {
    },
  • "vendedor": {
    },
  • "tenant": {
    },
  • "item": {
    },
  • "timestamp": "2019-08-24T14:15:22Z"
}