Versionamento
Leia X-DestrIA-SDK-Version ao baixar o arquivo e valide no runtime com SDK_VERSION, OPENAPI_VERSION e client.sdkVersion antes de iniciar workers críticos.
Cliente TypeScript gerado pelo próprio ambiente, sem dependências, compatível com Node 18+, Bun, Deno, browsers controlados e edge runtimes. Inclui retries para 429/5xx, parsing de CSV e erros padronizados via DestriaApiError.
DESTRIA_API_KEY em bundles públicos; se precisar chamar pelo browser, faça proxy por uma rota server-side com autenticação própria.O endpoint /api/v1/sdk.ts retorna X-DestrIA-SDK-Version para pinning em pipelines e também exporta SDK_VERSION, OPENAPI_VERSION e client.sdkVersion para leitura em runtime. O gate sdk:surface no CI valida essa superfície antes de cada release.
createDestriaClientDestriaClientDestriaClientOptionsDestriaApiErrorSDK_VERSIONOPENAPI_VERSIONRateLimitInfoparseRateLimit(response)request<T>()listContacts() / upsertContact() / getContact() / deleteContact()listCampaigns() / createCampaign() / enrollContactInCampaign()listCampaignEvents() / getCampaignMetrics()listWebhooks() / createWebhook() / rotateWebhookSecret()listWebhookDeliveries() / retryWebhookDelivery() / exportWebhookDeliveriesCsv()cronCampaignsProcess() / cronCampaignsDispatch() / cronIdempotencyCleanup()onRateLimit(info, ctx)client.lastRateLimitclient.sdkVersionLeia X-DestrIA-SDK-Version ao baixar o arquivo e valide no runtime com SDK_VERSION, OPENAPI_VERSION e client.sdkVersion antes de iniciar workers críticos.
Novos métodos podem ser adicionados em releases 1.x; remoções ou mudanças incompatíveis só entram em major version documentada.
O teste sdk:surface falha se SDK_VERSION, OPENAPI_VERSION, client.sdkVersion, RateLimitInfo, parseRateLimit, DestriaApiError.rateLimit, onRateLimit, lastRateLimit ou helpers cron desaparecerem.
O endpoint https://seu-projeto.lovable.app/api/v1/sdk.ts sempre aponta para o ambiente atual, evitando base URLs hardcoded entre preview, staging e produção.
Baixe o arquivo gerado pelo ambiente e importe em jobs, route handlers, server actions ou filas.
curl -L https://seu-projeto.lovable.app/api/v1/sdk.ts -o src/lib/destria-mailer-sdk.ts
import { createDestriaClient, DestriaApiError } from "./lib/destria-mailer-sdk";
const destria = createDestriaClient({
apiKey: process.env.DESTRIA_API_KEY!,
baseUrl: "https://seu-projeto.lovable.app/api/v1",
maxRetries: 2,
});
export async function syncLead(lead: { email: string; firstName?: string; company?: string }) {
try {
const { data: contact } = await destria.upsertContact({
email: lead.email,
first_name: lead.firstName,
company: lead.company,
add_tags: ["node-sync"],
metadata: { source: "app" },
});
await destria.enrollContactInCampaign("cmp_outbound_q3", {
email: contact.email,
first_name: contact.first_name ?? undefined,
company: contact.company ?? undefined,
});
} catch (err) {
if (err instanceof DestriaApiError && err.status === 409) {
console.info("Contato bloqueado por supressão/status protegido", err.code);
return;
}
throw err;
}
}Use este teste no CI ou na primeira configuração de um cliente para validar chave, CORS server-side, rate-limit e parsing de resposta.
import { createDestriaClient, DestriaApiError, SDK_VERSION, OPENAPI_VERSION } from "./destria-mailer-sdk";
const destria = createDestriaClient({
apiKey: process.env.DESTRIA_API_KEY!,
baseUrl: "https://seu-projeto.lovable.app/api/v1",
maxRetries: 2,
});
try {
const contacts = await destria.listContacts({ limit: 1 });
console.log("SDK OK", { sdkVersion: SDK_VERSION, openapiVersion: OPENAPI_VERSION, clientVersion: destria.sdkVersion, pagination: contacts.pagination });
} catch (err) {
if (err instanceof DestriaApiError) {
console.error("DestrIA API", err.status, err.code, err.retryAfterSeconds, err.body);
process.exitCode = 1;
} else {
throw err;
}
}Referência operacional dos métodos exportados por DestriaClient.
| Categoria | Método | Endpoint | Uso real |
|---|---|---|---|
Baixo nível | request<T>() request<T>(path, { method, query, body, headers, signal, accept }) | qualquer /api/v1/* | Executa chamadas autenticadas com headers padrão, parsing JSON/texto, retry e DestriaApiError.
|
Contatos | listContacts() listContacts({ limit, cursor, status, q, tags, tag, tag_match }) | GET /contacts | Lista contatos paginados do workspace com filtros por status, busca e tags all/any.
|
Contatos | upsertContact() upsertContact({ email, local_id, first_name, add_tags, metadata, custom_fields, ... }) | POST /contacts | Cria ou atualiza contato por e-mail com merge seguro de tags, metadata e custom_fields.
|
Contatos | getContact() / deleteContact() getContact(id) · deleteContact(id) | GET/DELETE /contacts/{id} | Consulta por UUID/local_id/email ou executa soft-delete com supressão manual.
|
Campanhas | listCampaigns() listCampaigns({ status, q, list_id, account_id, since, until, include_sequence }) | GET /campaigns | Lista campanhas com métricas agregadas e sequência sanitizada opcional.
|
Campanhas | createCampaign() createCampaign({ local_id, name, status, sequence, schedule, ... }) | POST /campaigns | Cria ou atualiza campanha por local_id, útil para seeds de CI e integrações que preparam campanhas antes do enrollment.
|
Campanhas | enrollContactInCampaign() enrollContactInCampaign(campaignId, input, { idempotencyKey }) | POST /campaigns/{id}/enroll | Inscreve um contato em campanha por UUID ou local_id, cria o contato se necessário e retorna created/queued/enrollment_id.
|
Campanhas | listCampaignEvents() / getCampaignMetrics() listCampaignEvents(id, params) · getCampaignMetrics(id, { since, until, max_events }) | GET /campaigns/{id}/events · GET /campaigns/{id}/metrics | Consulta eventos cursor-paginados e métricas detalhadas por etapa, variante A/B e links.
|
Webhooks | listWebhooks() / createWebhook() listWebhooks(params) · createWebhook({ url, events, active, local_id }) | GET/POST /webhooks | Lista e provisiona webhooks HTTPS assinados por HMAC-SHA256.
|
Webhooks | getWebhook() / updateWebhook() / deleteWebhook() getWebhook(id) · updateWebhook(id, patch) · deleteWebhook(id) | GET/PATCH/DELETE /webhooks/{id} | Gerencia URL, eventos, ativação e rotação de secret com janela de graça.
|
Entregas | listWebhookDeliveries() / exportWebhookDeliveriesCsv() listWebhookDeliveries(filters) · exportWebhookDeliveriesCsv(filters) | GET /webhook-deliveries | Consulta ou exporta histórico de entregas com filtros por evento, status, origem e período.
|
Entregas | retryWebhookDelivery() / listWebhookDeliveriesForWebhook() retryWebhookDelivery(deliveryId) · listWebhookDeliveriesForWebhook(webhookId, filters) | POST /webhook-deliveries/{id}/retry · GET /webhooks/{id}/deliveries | Reenvia entregas elegíveis e lista histórico escopado por webhook.
|
Cron | cronCampaignsProcess() cronCampaignsProcess({ limit, per_campaign_limit, dry_run }) | POST /api/public/cron/campaigns-process | Executa o avanço de inscrições e prepara próximos envios a partir de jobs externos ou schedulers gerenciados.
|
Cron | cronCampaignsDispatch() cronCampaignsDispatch({ limit, dry_run }) | POST /api/public/cron/campaigns-dispatch | Dispara a fila pronta, respeitando autenticação cron, rate limit curto e respostas tipadas para observabilidade.
|
Cron | cronIdempotencyCleanup() cronIdempotencyCleanup({ older_than, limit, dry_run }) | POST /api/public/cron/idempotency-cleanup | Remove chaves de idempotência antigas com limite de varredura para manter o store operacional.
|
Utilitários | parseRateLimit() parseRateLimit(response) → { limit, remaining, resetAt, retryAfter } | Client-side helper (não faz requisição) | Lê os headers RateLimit-Limit/Remaining/Reset (com fallback para X-RateLimit-*) e Retry-After de qualquer Response, retornando limites tipados para observabilidade e backoff manual.
|
Utilitários | onRateLimit / lastRateLimit createDestriaClient({ onRateLimit(info, { path, method, status, ok }) }) • client.lastRateLimit | Client-side (sem requisição extra) | Hook opcional invocado em toda resposta com headers de rate-limit e cache do último snapshot no client, para telemetria e autotuning sem envolver cada chamada.
|
Todo erro HTTP final vira DestriaApiError com status, code, body, headers, retryAfter, retryAfterSeconds e rateLimit.
Corrija payload, schema, e-mail, campos JSONB ou filtros antes de reenviar.
Revogue a chave exposta, gere uma nova em /api e valide o header Authorization.
Confirme workspace, UUID/local_id e se a chave pertence ao tenant correto.
Trate como resultado esperado para contato suprimido, status protegido, campanha arquivada ou retry ainda em backoff.
Reduza o corpo para o limite do endpoint. Nos crons públicos o máximo é 8 KB e o erro não deve ser repetido.
Envie application/json nos helpers cron; ajuste o scheduler antes de reenviar.
O SDK respeita Retry-After antes de falhar; reduza concorrência e monitore X-RateLimit-Plan.
O SDK tenta novamente com backoff exponencial; persista o evento externo para novo processamento se exceder maxRetries.
import { createDestriaClient, DestriaApiError } from "./destria-mailer-sdk";
const destria = createDestriaClient({ apiKey: process.env.DESTRIA_API_KEY!, baseUrl: "https://seu-projeto.lovable.app/api/v1" });
try {
await destria.enrollContactInCampaign("cmp_outbound_q3", {
email: "[email protected]",
first_name: "Ana",
custom_fields: { crm_id: "deal_123" },
});
} catch (err) {
if (!(err instanceof DestriaApiError)) throw err;
switch (err.status) {
case 409:
// contato suprimido/status protegido: marque opt-out no CRM e não tente novamente
console.info("Enrollment bloqueado", err.code, err.body);
return;
case 429:
// o SDK já tentou novamente; use retryAfterSeconds para reagendar job externo
console.warn("Rate limit", { retryAfter: err.retryAfterSeconds });
return;
case 401:
// chave inválida/exposta: alerte operação e rotacione credencial
throw new Error("DESTRIA_API_KEY inválida ou expirada");
default:
throw err;
}
}Os métodos cronCampaignsProcess, cronCampaignsDispatch e cronIdempotencyCleanup chamam rotas públicas com apikey, rate limit por IP e JSON estrito.
Defina cronBaseUrl como a origem do app e cronApiKey como a chave pública/anon do backend usada pelo scheduler no header apikey.
400, 413 e 415 indicam contrato inválido. Ajuste payload, tamanho ou headers antes de tentar novamente.
429 e 5xx seguem o mesmo backoff do SDK e expõem retryAfterSeconds para reagendar jobs externos.
import { createDestriaClient, DestriaApiError } from "./destria-mailer-sdk";
const destria = createDestriaClient({
apiKey: process.env.DESTRIA_API_KEY!,
baseUrl: "https://seu-projeto.lovable.app/api/v1",
cronBaseUrl: "https://seu-projeto.lovable.app",
cronApiKey: process.env.BACKEND_PUBLIC_API_KEY!,
maxRetries: 2,
});
export async function runDispatchCron() {
try {
const result = await destria.cronCampaignsDispatch({ limit: 100, dry_run: false });
console.log("dispatch ok", {
dispatched: result.dispatched,
campaigns: result.campaigns_touched,
});
return result;
} catch (err) {
if (!(err instanceof DestriaApiError)) throw err;
if (err.status === 413) {
// Payload acima de 8 KB: erro de configuração, não tente novamente.
throw new Error("Payload do cron excede 8 KB");
}
if (err.status === 415) {
// O SDK envia application/json; se isto aparecer, há proxy/scheduler alterando headers.
throw new Error("Scheduler enviou Content-Type inválido");
}
if (err.status === 429) {
// O SDK já respeitou Retry-After durante os retries; reprograme a próxima janela.
console.warn("Cron limitado", { retryAfter: err.retryAfterSeconds });
return null;
}
if (err.status === 400) {
console.error("JSON inválido ou parâmetro fora do contrato", err.body);
return null;
}
throw err;
}
}onRateLimit e lastRateLimit funcionam nas chamadas REST e nos helpers cron, permitindo reduzir concorrência, reagendar jobs e alimentar métricas externas com o mesmo formato.
O hook roda em respostas de sucesso, 429 e 5xx quando houver headers RateLimit-*, inclusive em cronCampaignsDispatch.
Use error.rateLimit no catch e client.lastRateLimit fora dele para pausar filas sem parse manual de headers.
import { createDestriaClient, DestriaApiError } from "./destria-mailer-sdk";
const destria = createDestriaClient({
apiKey: process.env.DESTRIA_API_KEY!,
baseUrl: "https://seu-projeto.lovable.app/api/v1",
cronBaseUrl: "https://seu-projeto.lovable.app",
cronApiKey: process.env.BACKEND_PUBLIC_API_KEY!,
onRateLimit: (rateLimit, ctx) => {
console.info("destria rate limit", {
path: ctx.path,
status: ctx.status,
remaining: rateLimit.remaining,
resetAt: rateLimit.resetAt?.toISOString(),
});
},
});
try {
await destria.cronCampaignsDispatch({ limit: 50 });
} catch (err) {
if (err instanceof DestriaApiError && err.rateLimit.remaining === 0) {
console.warn("aguarde nova janela", {
retryAfter: err.rateLimit.retryAfter,
resetAt: err.rateLimit.resetAt?.toISOString(),
lastSeen: destria.lastRateLimit,
});
return;
}
throw err;
}O SDK tenta novamente 408, 409, 425, 429 e 5xx até maxRetries. Para 429, respeita Retry-After; caso contrário usa backoff exponencial até 8s.
Para Zaps, Makes e filas, persista o ID do evento externo. Se um job falhar depois da resposta da API, reexecutar upsert/enroll é seguro dentro das regras do backend.
Sempre processe a página antes de salvar next_cursor. Em BI incremental, salve cursor por campanha e por workspace para evitar perda ou duplicidade.
Combine este SDK com OpenAPI, Postman, receitas no-code e guia de webhooks para validar o fluxo ponta a ponta antes de liberar automações em produção.