Sandbox & Idempotência
Todo endpoint de escrita da API pública tem dois mecanismos de segurança operacional: o sandbox (para testar sem gravar) e a idempotência (para reenviar uma chamada sem duplicar o efeito). Os dois são independentes e podem ser usados juntos.
Sandbox
Seção intitulada “Sandbox”Uma chave test (prefixo maestron_pk_test_, ver Autenticação) executa a mesma validação e a mesma regra de negócio de uma chave live, mas a operação inteira roda dentro de uma transação de banco que é sempre desfeita ao final — nada é persistido, ganhe ou perca a requisição.
A resposta de uma chamada em sandbox é idêntica à de uma chamada real, com um campo a mais no nível raiz do envelope:
{ "message": "Negócio criado.", "data": { "id": "b7e8c1a2-3d4f-4a5b-8c9d-0e1f2a3b4c5d", "...": "..." }, "sandbox": true}O sandbox se aplica a toda rota de escrita: criação, atualização, remoção, mover de etapa, ganho/perdido/reabertura e concluir/reabrir atividade. Uma chave live nunca entra em sandbox — o middleware verifica o ambiente da chave e, se for live, executa a operação normalmente, sem custo adicional.
Idempotência
Seção intitulada “Idempotência”Chamadas de criação aceitam um campo opcional idempotency_key no corpo da requisição. Ele evita que uma falha de rede seguida de retry duplique o efeito da chamada.
Exemplo — criação com idempotency_key
idempotency_key é opcional. Reenviar a mesma chamada com a mesma chave devolve a resposta original, sem criar um segundo negócio.
Corpo (body)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
idempotency_key | string | — | Identificador único escolhido pelo seu sistema para esta operação (ex.: um UUID). |
curl -X POST https://api.maestron.com.br/api/v1/crm/deals \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"funnel_id": "b7e8c1a2-3d4f-4a5b-8c9d-0e1f2a3b4c5d",
"stage_id": "a1c2d3e4-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
"contact_name": "Maria Silva",
"contact_date": "2026-08-31",
"status": "sem_contato",
"idempotency_key": "3f7b1c9a-2d4e-4f6a-8b0c-5d9e1f3a7c2b"
}' <?php
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.maestron.com.br/api/v1/crm/deals', [
'headers' => [
'Authorization' => 'Bearer SEU_TOKEN',
'Accept' => 'application/json',
],
'json' => [
'funnel_id' => 'b7e8c1a2-3d4f-4a5b-8c9d-0e1f2a3b4c5d',
'stage_id' => 'a1c2d3e4-5f6a-4b7c-8d9e-0f1a2b3c4d5e',
'contact_name' => 'Maria Silva',
'contact_date' => '2026-08-31',
'status' => 'sem_contato',
'idempotency_key' => '3f7b1c9a-2d4e-4f6a-8b0c-5d9e1f3a7c2b',
],
]);
$data = json_decode($response->getBody(), true); import requests
headers = {"Authorization": "Bearer SEU_TOKEN"}
payload = {
"funnel_id": "b7e8c1a2-3d4f-4a5b-8c9d-0e1f2a3b4c5d",
"stage_id": "a1c2d3e4-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
"contact_name": "Maria Silva",
"contact_date": "2026-08-31",
"status": "sem_contato",
"idempotency_key": "3f7b1c9a-2d4e-4f6a-8b0c-5d9e1f3a7c2b",
}
response = requests.post(
"https://api.maestron.com.br/api/v1/crm/deals",
headers=headers,
json=payload,
)
data = response.json() const response = await fetch("https://api.maestron.com.br/api/v1/crm/deals", {
method: "POST",
headers: {
"Authorization": "Bearer SEU_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
"funnel_id": "b7e8c1a2-3d4f-4a5b-8c9d-0e1f2a3b4c5d",
"stage_id": "a1c2d3e4-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
"contact_name": "Maria Silva",
"contact_date": "2026-08-31",
"status": "sem_contato",
"idempotency_key": "3f7b1c9a-2d4e-4f6a-8b0c-5d9e1f3a7c2b"
}),
});
const data = await response.json(); using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "SEU_TOKEN");
var payload = new StringContent(
"{\"funnel_id\":\"b7e8c1a2-3d4f-4a5b-8c9d-0e1f2a3b4c5d\",\"stage_id\":\"a1c2d3e4-5f6a-4b7c-8d9e-0f1a2b3c4d5e\",\"contact_name\":\"Maria Silva\",\"contact_date\":\"2026-08-31\",\"status\":\"sem_contato\",\"idempotency_key\":\"3f7b1c9a-2d4e-4f6a-8b0c-5d9e1f3a7c2b\"}",
System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.maestron.com.br/api/v1/crm/deals", payload);
var data = await response.Content.ReadAsStringAsync(); POST /crm/deals HTTP/1.1
Host: api.maestron.com.br
Authorization: Bearer SEU_TOKEN
Content-Type: application/json
{
"funnel_id": "b7e8c1a2-3d4f-4a5b-8c9d-0e1f2a3b4c5d",
"stage_id": "a1c2d3e4-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
"contact_name": "Maria Silva",
"contact_date": "2026-08-31",
"status": "sem_contato",
"idempotency_key": "3f7b1c9a-2d4e-4f6a-8b0c-5d9e1f3a7c2b"
} Respostas
{
"message": "Negócio criado.",
"data": {
"id": "f1a2b3c4-..."
}
} {
"message": "Negócio criado.",
"data": {
"id": "f1a2b3c4-..."
}
} Como funciona
Seção intitulada “Como funciona”- A chave de idempotência é escopada por API Key — a mesma string
idempotency_keyusada por duas chaves diferentes não colide entre elas. - Na primeira chamada com uma dada
idempotency_key, a operação roda normalmente e a resposta (status e corpo) fica congelada. - Qualquer chamada seguinte com a mesma
idempotency_keydevolve a resposta congelada, com o headerIdempotency-Replayed: true, sem executar a operação de novo — nem abre o sandbox de uma chavetest. - Duas chamadas com a mesma
idempotency_keychegando quase ao mesmo tempo não duplicam o efeito: a segunda é bloqueada no nível do banco e recebe a mesma resposta computada pela primeira. - Omitir
idempotency_key(ou enviar string vazia) executa a operação normalmente, sem nenhuma garantia de deduplicação.
Em quais rotas está disponível
Seção intitulada “Em quais rotas está disponível”Rotas com idempotency_key | Rotas sem idempotência |
|---|---|
POST /crm/deals | Todo PATCH e DELETE — já são idempotentes por natureza (repetir com o mesmo id produz o mesmo resultado). |
POST /crm/pipelines | POST /crm/activities/{id}/complete |
POST /crm/activities | POST /crm/activities/{id}/reopen |
POST /crm/deals/{id}/win, /lose, /reopen | — |
Para o formato de erro e os códigos de status, veja Erros.