SDKs
As bibliotecas oficiais de TypeScript e Python: um método tipado por operação, com novas tentativas, idempotência, paginação e prévia incluídas. Para outras linguagens, o contrato OpenAPI.
| Linguagem | Pacote | Status |
|---|---|---|
| TypeScript / JavaScript | @mb-suite/sdk | Versão preliminar sobre o contrato 2026-11-01. Ainda não publicado no npm. |
| Python | mb-suite | Versão preliminar sobre o contrato 2026-11-01. Ainda não publicado no PyPI. Veja SDK de Python. |
Instalação
npm install @mb-suite/sdkimport { MBSuite } from "@mb-suite/sdk";
// Sem apiKey, lê a variável de ambiente MB_API_KEY
const mb = new MBSuite({ apiKey: process.env.MB_API_KEY });- Node.js 20 ou posterior, ou qualquer ambiente com
fetchglobal: Deno, Bun, Cloudflare Workers, funções da Vercel. Sem dependências. - Só no seu servidor: uma API key é um segredo e a API não aceita pedidos de um navegador, então o cliente não inicia em um.
- Uma API key (
mbs_live_…) ou um token OAuth (mbo_…): veja Autenticação.
Seu primeiro pedido
const me = await mb.platform.me(); // GET /v1/me
console.log(me.actor.uid, me.org.id);
console.log(me.lastResponse.requestId); // req_…: inclua-o se escrever para nósCada operação tem um id estável e o método o segue: crm.deals.create é mb.crm.deals.create(). Cada trecho vai em camelCase: crm.custom_fields.list é mb.crm.customFields.list() e task_management.tasks.complete é mb.taskManagement.tasks.complete(). A Referência mostra o id de cada uma.
- 1Primeiro, os parâmetros de rota, como texto e na ordem da rota:
mb.crm.deals.get("deal_123"). - 2Depois, os parâmetros: um objeto em snake_case, igual à API (a query de um
GET, o corpo do resto). - 3No fim, as opções do pedido:
organization,idempotencyKey,confirmToken,timeout,maxRetries,signaleheaders.
Listar, criar e editar
const { data, has_more } = await mb.crm.deals.list({ status: "open", limit: 50 });
const { deal_id } = await mb.crm.deals.create({
title: "Plan anual — Acme",
value: 4800,
currency: "USD",
pipeline_id: "PIPELINE_ID",
stage_id: "STAGE_ID",
});
await mb.crm.deals.move(deal_id, { stage_id: "NEXT_STAGE_ID" });
await mb.crm.deals.update(deal_id, { updates: { value: 5200 } });
const deal = await mb.crm.deals.get(deal_id);Os parâmetros e os resultados são tipados a partir do contrato, e cada método traz a descrição da sua operação: o seu editor a mostra ao passar o cursor.
Paginação
Cada lista paginada tem um gêmeo …All() que percorre todas as páginas e entrega os itens um por um. Uma página pode vir vazia com has_more em true: o percurso continua. Saia do laço quando tiver o que precisa. Veja Paginação.
// Segue next_cursor até has_more ser false; limit é o tamanho de cada página
for await (const deal of mb.crm.deals.listAll({ status: "open", limit: 200 })) {
console.log(deal.id, deal.title);
}Prévia e confirmação
As operações que pedem confirmação —as em massa, as irreversíveis, as que enviam ou mudam algo fora do MB Suite ou têm um custo à parte, e as que sempre pedem— têm .dryRun(): não escreve nada e devolve a prévia e confirm(), que executa a mesma operação com os mesmos parâmetros e o confirm_token da prévia (vale 10 minutos). A sua idempotencyKey, passe-a para .dryRun() ou para confirm(): ela vai com a confirmação, nunca com a prévia, que não escreve nada. Veja Prévia e confirmação.
const { preview, confirm } = await mb.crm.deals.bulk.delete.dryRun({
deal_ids: ["DEAL_A", "DEAL_B"],
});
console.log(preview.summary, preview.counts, preview.warnings); // nada foi excluído ainda
const { deleted } = await confirm(); // dentro de 10 minutos- Chamá-la sem token lança
ConfirmationRequiredError. - Se o
confirm_tokenchegar por outro caminho (uma aprovação, um agente), passe-o como opção:mb.crm.deals.bulk.delete(params, { confirmToken }). - Com uma API key, o
DELETEsimples de um registro que vai para a lixeira executa direto:await mb.crm.deals.delete("deal_123"). Com um token OAuth também passa por.dryRun().
Novas tentativas e idempotência
- Cada mutação sai com uma
Idempotency-Key(um UUID que o SDK gera, ou a sua) e a mantém nas novas tentativas: se uma resposta se perde, a nova tentativa devolve a guardada em vez de executar duas vezes. - São repetidos sozinhos, até 3 vezes com espera exponencial (0,5 s, 1 s, 2 s): os erros de rede, os timeouts, um 429 (
rate_limited,external_rate_limited, umcooldown_activecurto), um 409idempotency_in_progresse os 5xx (os 503external_unavailableepdf_unavailableincluídos). Se a resposta trazRetry-After, espera exatamente isso. - Não são repetidos: o resto dos 4xx (um 409
external_rejected, por exemplo: a plataforma já disse que não; ou um 409external_outcome_unknown: um envio que pode ter saído, verifique antes de repeti-lo), um 429quota_exceeded(a cota diária volta às 00:00 UTC), um 502ai_generation_failed(repetir chama a IA de novo e gasta créditos se ela responder: decida você) nem umRetry-Afterde mais de 60 segundos (umdaily_limit_reached). O erro volta comretryAfterpara você reprogramar.
Para que uma operação lógica seja segura entre as suas próprias novas tentativas, dê a ela a sua própria chave (vale 24 horas). A mesma chave com outro corpo lança IdempotencyError. Veja Idempotência.
// A mesma chave numa nova tentativa sua (outro processo, outro deploy) devolve a resposta guardada
await mb.crm.deals.create(newDeal, { idempotencyKey: `import-${row.id}` });Erros
Todo erro herda de MBSuiteError e traz o formato único da API: type, code (estável: decida com ele), message, status, param, requestId, docUrl e details. A classe depende do type:
| Classe | type | HTTP |
|---|---|---|
InvalidRequestError | invalid_request | 400 |
AuthenticationError | authentication | 401 |
PaymentRequiredError | payment_required | 402 |
PermissionError | permission | 403 |
NotFoundError | not_found | 404 |
ConflictError | conflict | 409 |
ConfirmationRequiredError | confirmation_required | 409 |
IdempotencyError | idempotency | 422 |
RateLimitError | rate_limit | 429 |
QuotaExceededError | quota | 429 |
UpstreamError | upstream | 502 |
ServiceUnavailableError | unavailable | 503 |
MaintenanceError | maintenance | 503 |
InternalServerError | internal | 5xx |
ConnectionError, TimeoutError | connection | — |
import { MBSuiteError, NotFoundError, RateLimitError } from "@mb-suite/sdk";
try {
await mb.crm.deals.get(dealId);
} catch (err) {
if (err instanceof NotFoundError) return null;
if (err instanceof RateLimitError) console.log(`Tente de novo em ${err.retryAfter} s`);
if (err instanceof MBSuiteError) console.error(err.code, err.requestId, err.docUrl);
throw err;
}InvalidRequestError soma issues (qual campo e por quê), ConfirmationRequiredError soma reason (expired, mismatch), RateLimitError e ServiceUnavailableError somam retryAfter, QuotaExceededError é um RateLimitError com quota e MaintenanceError é um ServiceUnavailableError. Todos os códigos estão em Erros.
Contas
Uma API key de workspace trabalha nas Contas do seu alcance com MB-Organization. Escolha a Conta para todo o cliente, para um pedido ou com um cliente preso a ela. mb.platform.orgs.list() devolve as que a API key alcança. Veja Workspaces e Contas.
// Para todos os pedidos do cliente
const mb = new MBSuite({ apiKey: process.env.MB_API_KEY, organization: "ACCOUNT_ID" });
// Para um pedido (null = a organização da API key)
await mb.crm.deals.list({}, { organization: "OTHER_ACCOUNT_ID" });
// Um cliente para essa Conta; o original não muda
const account = mb.withOptions({ organization: "ACCOUNT_ID" });O que cada resposta traz
Cada resultado leva lastResponse, que não aparece em JSON.stringify nem ao copiar o objeto: o request_id, a versão, a rajada e a cota do dia (para frear antes de um 429, veja Limites) e se foi a resposta guardada de um pedido anterior.
const page = await mb.crm.deals.list();
page.lastResponse.requestId; // "req_…"
page.lastResponse.rateLimit; // { limit, remaining, reset }: a rajada da API key (10 s)
page.lastResponse.quota; // { limit, remaining, reset }: as chamadas de hoje
page.lastResponse.idempotentReplayed; // true se é a resposta guardada de um pedido anteriorVersões
O SDK envia MB-Version: 2026-11-01 em cada pedido: a versão que os seus tipos descrevem, não importa em qual a sua organização ficou fixada. Quando sai uma versão nova da API sai uma do SDK; a anterior continua ativa por 12 meses, e se uma resposta vier de uma versão em retirada, o SDK avisa uma vez com a data de Sunset. Veja Versões e o Registro de mudanças.
Tipos e pedidos manuais
import type { Item, Params } from "@mb-suite/sdk";
type NewDeal = Params<"crm.deals.create">; // o corpo de POST /v1/crm/deals
type Deal = Item<"crm.deals.list">; // um elemento de data[]
// Qualquer endpoint por método e rota, com as mesmas novas tentativas e erros
await mb.request({ method: "POST", path: "/crm/deals/DEAL_ID/archive", body: { archived: true } });Por padrão o cliente fala com https://api.mb-suite.com/v1; baseUrl o aponta para outro endereço (a mesma API responde em https://mb-suite.com/api/platform/v1). Os tipos crus do contrato são exportados como paths, operations e components.
Outras linguagens: o contrato OpenAPI
O contrato completo, em OpenAPI 3.1, está em GET /v1/openapi.json (com a sua API key ou o seu token, como qualquer pedido). É dele que saem os SDKs de TypeScript e Python, e serve para gerar um cliente em outra linguagem.
curl https://api.mb-suite.com/v1/openapi.json \
-H "Authorization: Bearer $MB_API_KEY" \
-o mb-suite-openapi.jsonCada operação do contrato traz extensões x-mb-*: a classe (x-mb-kind), se é idempotente, o lote, os eventos que emite, a tool do MCP e a permissão. Dentro de uma versão, o contrato só soma: veja Versões.