AI Connect/Como criar/Automações & contas de serviço

Automações & contas de serviço

Pergunte à IA sobre a Vinkius

Faça jobs de cron, webhooks, pipelines de CI e processos noturnos de lote possuírem seu próprio usuário no AI Connect SDK, conectarem sistemas sem interface com credenciais estáticas e agirem com isolamento e orçamento por job, sem navegador em lugar nenhum. Processos viram atores governados, não chaves mudas num arquivo de configuração.

A IA mais valiosa da sua empresa roda sem intervenção humana. Um job noturno que reconcilia o livro-razão, um webhook que triagem um novo lead no CRM, um passo de CI que abre uma issue num deploy que falhou. Até hoje, a infraestrutura tinha exatamente um formato para esse trabalho: uma god-key num arquivo de ambiente, sem identidade, sem medição, sem revogação, e uma revisão de segurança que termina num ganido de ombros.

O AI Connect SDK substitui esse formato pelo que nenhuma plataforma de conectividade ofereceu antes: um processo é um usuário, com o próprio external_id, conectores próprios, credenciais estáticas próprias, apoiado em milhares de conexões de IA desde o primeiro dia. Dê a cada automação a própria identidade, conecte-a uma vez e deixe-a chamar capacidades de forma contínua, isolada, medida e auditável. Cada cron job se torna um funcionário responsável, com crachá, orçamento e desligamento próprios. Essa é uma frase que você não podia escrever sobre nenhuma plataforma de integração do mercado até esta.

Agent loop · one turn
One user turn of the quickstart, exactly as the console serves it. Click a step or press Run.
vinkius.user('alice_123').capabilities({ include: ['github'] })
HTTPGET /apps/vk_app_xxx/users/alice_123/tools?connector=github
const capabilities = await vinkius
  .user('alice_123')
  .capabilities({ include: ['github'] });
CapabilitySet (6)
  github__list_issues        read-only
  github__create_issue       POST /repos/{owner}/{repo}/issues
  github__list_pull_requests read-only
  github__search_code        read-only
  ...
Step 1 of 6
The agent loop, step by step. Press Run and follow one user turn: capabilities load, convert to tools, the model calls github__create_issue, the SDK executes on that user connection and the result feeds back. Every step shows the real SDK call and its HTTP request.
Real systemsledger · CRM · issuescron · nightlyown external_idwebhookown external_idCI pipelineown external_idstatic credentialsfrom your secret managerbrowsernever neededidempotent actions
Processes as users: headless jobs with their own static credentials act on real systems, idempotent and metered, with no browser anywhere.

O que o modelo "processo-como-usuário" proporciona

PropriedadePor que importa para jobs sem intervenção
Headless por designConectores de token estático (api_key, token) não exigem consentimento interativo, credentials.set() é o fluxo inteiro.
Nenhum navegadorUm contêiner de cron com apenas fetch e a sua Application Key executa o SDK por completo.
Raio de impacto por jobA conexão própria de cada automação significa que uma chave exposta compromete um job, não o parque todo.
Medição independenteTokens por conexão mostram exatamente quanto custa o job noturno de reconciliação.
Repetições determinísticasUma chave de idempotência garante que uma chamada repetida seja aplicada exatamente uma vez.

Estes jobs guardam credenciais reais sem humano no loop. Mantenha todo trecho no servidor, obtenha tokens estáticos do seu secret manager (nunca de um repositório ou prompt de modelo) e dê a cada automação o menor conjunto de conectores com que ela consiga funcionar.

1. Uma automação, um id, um conjunto de conectores

Nomeie o job pelo que ele faz e trate esse nome como o ator dono das conexões.

typescript
const JOB = 'svc-nightly-reconcile'; // estável, seguro para URL, menos de 255 chars, sem / \ nem espaços
const JOB_CONNECTORS = ['netsuite', 'stripe', 'sheets'];

2. Provisone uma conta de serviço (uma vez, na configuração)

Isto roda uma única vez no onboarding, um operador ou um script de bootstrap fornece os tokens estáticos. Depois, o job apenas usa a conexão.

typescript
// scripts/bootstrap-reconcile.ts
import { Vinkius } from '@vinkius/connect';

const vinkius = new Vinkius({
  appId: process.env.VINKIUS_APP_ID!,
  apiKey: process.env.VINKIUS_APP_KEY!, // do secret manager
});

async function provision(jobId: string, tokens: Record<string, Record<string, string>>) {
  const user = vinkius.user(jobId);
  await user.ensure({ kind: 'service-account', job: 'nightly-reconcile' });

  for (const [slug, values] of Object.entries(tokens)) {
    const connector = user.connector(slug);
    await connector.connect();                 // get-or-create, idempotente
    await connector.credentials.set(values);   // write-only; ex. { API_KEY: … }
  }

  return Promise.all(
    Object.keys(tokens).map(async (slug) => ({
      slug,
      status: await user.connector(slug).status(), // espere "ready"
    })),
  );
}

await provision('svc-nightly-reconcile', {
  netsuite: await secrets.read('netsuite.reconcile'),
  stripe: await secrets.read('stripe.reconcile'),
  sheets: await secrets.read('sheets.reconcile'),
});

credentials.set() jamais retorna os valores armazenados, e o status() só informa quais chaves estão configuradas. Um processo pode verificar que seus conectores estão prontos sem jamais conseguir exfiltrar o que recebeu, a credencial é usável, não legível.

3. O job sem intervenção em si

O processo agendado não precisa de navegador, de consentimento, nem de usuário presente. Ele carrega as próprias capacidades e age.

typescript
// jobs/nightly-reconcile.ts
import { Vinkius } from '@vinkius/connect';

const vinkius = new Vinkius({
  appId: process.env.VINKIUS_APP_ID!,
  apiKey: process.env.VINKIUS_APP_KEY!,
});

async function run() {
  const job = vinkius.user('svc-nightly-reconcile');
  const capabilities = await job.capabilities({ include: ['netsuite', 'stripe'] });

  const fetchOpen = capabilities.findCapability('stripe__list_invoices');
  const postEntry = capabilities.findCapability('netsuite__create_journal_entry');

  if (!fetchOpen || !postEntry) {
    await alertOps('reconcile: uma capacidade está indisponível, a credencial expirou?');
    return;
  }

  const invoices = await fetchOpen.execute({ status: 'open', limit: 200 });
  if (invoices.isError) throw new Error('list do stripe falhou: ' + invoices.content[0]?.text);

  const entry = await postEntry.execute(
    { lines: toJournalLines(invoices) },
    { idempotencyKey: `reconcile:${runDate()}` }, // uma execução lógica = um lançamento
  );
  if (entry.isError) await alertOps('lançamento do reconcile rejeitado: ' + entry.content[0]?.text);
}

run().catch(async (error) => {
  await alertOps(`reconcile quebrou: ${error.message}`);
});

O idempotencyKey é o recurso de automação. Uma repetição, um disparo duplicado de cron, um redeploy no meio da execução, nenhum deles registra duas vezes o lançamento contábil, porque o servidor deduplica replays com a mesma chave. Derive a chave do evento de negócio (a data da execução, o id do ticket, o id da entrega do webhook), jamais de Date.now().

4. Webhooks: um ator, uma chave por entrega

Para automações orientadas a evento, normalmente você mantém um único usuário de conta de serviço, mas toda chamada mutável usa o id da entrega como chave, para que um webhook repetido seja aplicado exatamente uma vez.

typescript
// POST /webhooks/lead  (verificado)
async function handleLeadWebhook(payload: { id: string; email: string }) {
  const user = vinkius.user('svc-lead-intake');

  if ((await user.connector('hubspot').status()) !== 'ready') {
    await alertOps('CRM do lead-intake não está pronto');
    return;
  }

  const caps = await user.capabilities({ include: ['hubspot'] });
  await caps.findCapability('hubspot__create_contact')?.execute(
    { email: payload.email },
    { idempotencyKey: `lead:${payload.id}` }, // entrega repetida -> sem duplicata
  );
}

5. Pipelines de CI e executores de uso único

Um job de CI se autentica exatamente como um cron: a mesma Application Key, o próprio external_id, credenciais estáticas provisionadas no ambiente. A diferença é o tempo de vida, você disconnect() executores efêmeros quando o pipeline é desmontado.

typescript
async function openIssueOnFailedDeploy(runId: string, repo: string) {
  const caps = await vinkius.user('ci-deploy-bot').capabilities({ include: ['github'] });

  await caps.findCapability('github__create_issue')?.execute(
    { owner: 'acme', repo, title: `Deploy ${runId} falhou` },
    { idempotencyKey: `deploy:${runId}` },
  );
}

6. Rotacione e aposente de forma limpa

Como tudo depende de um único external_id, desativar uma automação é determinístico.

typescript
async function decommission(jobId: string) {
  const user = vinkius.user(jobId);
  for (const conn of await user.connectors()) {
    await user.connector(conn.slug).disconnect();
  }
}

Para rotacionar uma credencial, chame credentials.set() novamente com o novo valor, a conexão permanece a mesma e nenhuma referência de capacidade quebra.

Confiabilidade e saída estruturada, para processos sem supervisão

Um processo sem intervenção é exatamente onde a robustez embutida no SDK se destaca. Três garantias são herdadas automaticamente:

Repetição automática com proteção contra avalanche. Leituras idempotentes (e qualquer escrita que transporte um idempotencyKey) são repetidas automaticamente: somente para os transitórios 429/502/503/504 e erros de rede, com backoff de jitter completo que respeita o cabeçalho Retry-After do servidor. Seu cron não precisa de um loop de repetição próprio.

Idempotência é o mecanismo de segurança. Para um job de lote, falhar e ser reexecutado é normal. Um idempotencyKey estável elimina a dúvida "a reexecução duplicou o efeito?", porque o servidor deduplica as replays daquela chave. Ao fornecer a chave a uma chamada mutável, o SDK passa a repetir inclusive um POST não-idempotente com segurança.

Consuma resultados como objetos, não como texto. Quando o conector devolve dados estruturados, o result.structuredContent os entrega já interpretados, sem extração frágil de texto no pipeline:

typescript
const invoices = await fetchOpen.execute({ status: 'open', limit: 200 });
const list = invoices.structuredContent as { invoices: Array<{ id: string; amount: number }> };

const total = list.invoices.reduce((sum, i) => sum + i.amount, 0);

Associe a execução longa de um lote a um timeoutMs por chamada adequado e a um signal abortado no encerramento do processo, e o job conclui de forma previsível durante um deploy progressivo.

Checklist de produção

  • [ ] Dê a cada automação um external_id distinto e legível (sem uma única conta ampla compartilhada).
  • [ ] Obtenha todo token estático do seu secret manager; nunca o versione nem o coloque em prompt.
  • [ ] Provisone uma vez na configuração; os runtimes só leem status(), nunca rearmazenam segredos.
  • [ ] Defina um idempotencyKey estável derivado do evento de negócio em todo job mutável.
  • [ ] Alerte quando status() !== 'ready' e em isError, para que uma falha silenciosa não se torne invisível.
  • [ ] disconnect() contas de serviço efêmeras como parte do teardown.

Você agora tem automações que possuem a própria identidade, conectam sistemas reais sem interface e agem em agenda ou por evento, sem a mecânica de OAuth e sem a divergência de "quem é o dono deste token?" causada por uma conta ampla compartilhada. O seu parque sem supervisão saiu de ser a parte do sistema que as auditorias reclamam para ser a parte com a melhor história de governança, porque cada processo dele finalmente tem um nome.

What you just got

Not a pitch: the properties this build inherits automatically.

Isolation by construction

Connections and capabilities resolve only inside one external_id. No cross-actor leakage is possible, and you wrote none of that enforcement.

Write-only credentials

Your server stores secrets and can read back which fields are configured, never the values. Not your code, the model, or a dashboard can exfiltrate them.

Metered, revocable spend

Every connection owns a vk_live_* token, so cost and revocation are per connection. One call to disconnect() is a complete, auditable stop.

Any model runtime

One CapabilitySet converts to OpenAI, Anthropic, Gemini, Vercel AI SDK, LangChain, LlamaIndex, Workers AI or neutral JSON Schema. Only the last line changes.

Production safety built in

idempotencyKey, timeoutMs and AbortSignal per call; automatic full-jitter retries on transient failures; typed VinkiusError branches. No bespoke harness.

Headless and deterministic

A cron container with only fetch and your key runs it. An idempotencyKey from the business event makes a re-run a non-event.

Give it to your AI agent

An Agent Skill (SKILL.md) for this build. Preview the first lines below, then copy or download it into your repo under .claude/skills/: Claude Code, Cursor or any Agent-Skills-compatible agent follows it to implement this pattern correctly.

Download SKILL.md6 · Available in your language
What unattended automations inherit, plus the SKILL.md, in your language, for your coding agent.

Próximos passos