Criar negócio
POST /deals — cria um negócio em uma etapa do pipeline, vinculando ou criando o contato, com idempotência por external_id.
/dealsBetaEscopo necessário: deals:write
Cria um negócio (deal) em uma etapa do pipeline. A organização é resolvida a partir da própria API key — não envie organization_id.
Vínculo de contato: informe contact.id (UUID existente) ou contact.email/contact.phone. Se o contato não existir, ele é criado e vinculado na mesma chamada (mesma lógica do POST /contacts).
Endpoint
POST https://crm.oficinamartech.com/api/v1/dealsCorpo da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
pipeline_id | string (UUID) | ✅ | Pipeline do negócio. |
stage_id | string (UUID) | ✅ | Etapa; deve pertencer ao pipeline_id. |
title | string (até 300) | △ | Título do negócio. Se omitido e houver contato, gera #N - Nome automaticamente. |
value | number | ❌ | Valor. Default: 0. |
currency | string (ISO 4217) | ❌ | Moeda. Default: BRL. |
status | open | won | lost | ❌ | Se omitido, é derivado da etapa (is_won/is_lost) ou open. |
expected_close_date | string (YYYY-MM-DD) | ❌ | Data prevista de fechamento. |
assigned_to | string (UUID) | ❌ | Operador responsável. Se omitido, segue a regra de atribuição do pipeline. |
source | string (até 120) | ❌ | Rótulo de origem. Default: api. |
external_id | string (até 200) | ❌ | Id externo para idempotência (veja abaixo). |
custom_fields | object (mapa chave→valor) | ❌ | Gravado em metadata do negócio. Veja Campos personalizados. |
contact | object | ❌ | Contato a vincular/criar. Veja Contato. |
△ = title é obrigatório a menos que você envie um contact (o título é gerado a partir dele).
Contato
O objeto contact exige um identificador (id, email ou phone):
| Campo | Tipo | Descrição |
|---|---|---|
id | string (UUID) | Vincula um contato existente. Se não existir → 404. |
email | string (email) | Resolve/cria o contato por email (primeira chave de match). |
phone | string | Resolve/cria por telefone (E.164, com variantes do 9º dígito). |
name | string | Nome (na criação). |
source | string | Origem do contato. |
utm | object | { source, medium, campaign, term, content }. |
fbclid/gclid | string | Click ids — gravam um toque de atribuição. |
custom_fields | object | Campos personalizados de contato (validados). |
Campos personalizados
As chaves de custom_fields são gravadas em metadata do negócio. Chaves cadastradas como campo personalizado de negócio (Configurações → Campos personalizados, entity deal) são validadas (incluindo opções de select/multiselect); chaves sem cadastro são aceitas livremente.
Idempotência
Envie external_id para tornar a criação idempotente: se já existir um negócio criado via API com o mesmo external_id na organização, ele é atualizado (resposta 200) em vez de duplicado. Sem external_id, cada chamada cria um novo negócio (201).
Resposta
201 Created (novo) ou 200 OK (idempotente atualizado):
{
"data": {
"id": "9b2e1c40-58cc-4372-a567-0e02b2c3d479",
"organization_id": "...",
"pipeline_id": "...",
"stage_id": "...",
"contact_id": "...",
"title": "Proposta plano Pro",
"value": 1990.0,
"currency": "BRL",
"status": "open",
"source": "api",
"expected_close_date": "2026-07-31",
"metadata": { "campanha": "black-friday" },
"created_via": "api",
"dedup_key": "lead-2026-000123",
"created_at": "2026-06-14T12:00:00.000Z",
"updated_at": "2026-06-14T12:00:00.000Z"
},
"created": true,
"contact": { "id": "...", "created": false }
}| Campo | Tipo | Descrição |
|---|---|---|
data | object | O negócio criado/atualizado. |
created | boolean | true se criou; false se atualizou um negócio idempotente. |
contact | object | null | Contato vinculado (id + created indicando se foi criado). |
Exemplos
curl -X POST "https://crm.oficinamartech.com/api/v1/deals" \
-H "Authorization: Bearer $OFM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pipeline_id": "1f0b...e3",
"stage_id": "2a7c...91",
"title": "Proposta plano Pro",
"value": 1990.0,
"external_id": "lead-2026-000123",
"custom_fields": { "campanha": "black-friday" },
"contact": {
"email": "maria@example.com",
"name": "Maria Silva",
"phone": "+55 11 99999-9999"
}
}'Códigos de erro específicos
| Status | code | Causa |
|---|---|---|
| 400 | — | JSON inválido, ou faltou title sem contact |
| 400 | pipeline_not_found | pipeline_id não existe nesta organização |
| 400 | stage_not_found | stage_id não pertence ao pipeline_id |
| 400 | invalid_custom_field_values | Valor fora das opções de um campo select/multiselect de negócio |
| 400 | invalid_phone | contact.phone não pôde ser normalizado |
| 400 | unknown_custom_fields | Chave de contact.custom_fields não cadastrada |
| 401 | missing_api_key | Header Authorization ausente |
| 403 | missing_scope | Chave sem o escopo deals:write |
| 404 | contact_not_found | contact.id não encontrado na organização |
| 409 | duplicate_deal | Corrida de idempotência com o mesmo external_id |
| 500 | — | Erro interno |
Veja Códigos de erro para o formato completo.
Notas
- A organização vem da API key — uma chave só cria negócios na própria organização.
- O
statusé derivado da etapa quando omitido; etapasis_won/is_lostdefinemwon_at/lost_at. - A atribuição do operador segue a regra do pipeline quando
assigned_tonão é enviado. - Quando um contato é criado/atualizado pelo objeto
contact, a chamada também aparece na aba Atividades do contato (mesmo registro do POST /contacts).