AI Connect/How to create/Automatisations et comptes de service

Automatisations et comptes de service

Demandez à l’IA à propos de Vinkius

Faites en sorte que les cron jobs, les webhooks, les pipelines CI et les traitements nocturnes par lots possèdent leur propre utilisateur dans l'AI Connect SDK, connectent des systèmes sans interface avec des identifiants statiques et agissent avec un isolement et un budget par job, sans aucun navigateur. Les processus deviennent des acteurs gouvernés, pas des clés muettes dans un fichier de configuration.

L'IA la plus précieuse de votre entreprise s'exécute sans intervention humaine. Un job nocturne qui rapproche le grand livre, un webhook qui qualifie un nouveau lead dans le CRM, une étape de CI qui ouvre une issue après un déploiement raté. Jusqu'ici, l'infrastructure n'avait qu'une seule forme pour ce travail : une god-key dans un fichier d'environnement, sans identité, sans mesure, sans révocation, et une revue de sécurité qui se termine en haussement d'épaules.

L'AI Connect SDK remplace cette forme par ce qu'aucune plateforme de connectivité n'a offert avant : un processus est un utilisateur, avec son propre external_id, ses propres connecteurs, ses propres identifiants statiques, adossé à des milliers de connexions IA dès le premier jour. Donnez à chaque automatisation sa propre identité, connectez-la une fois et laissez-la appeler des capacités indéfiniment, isolée, mesurée et auditable. Chaque cron job devient un employé redevable, avec son badge, son budget et son propre départ. C'est une phrase que vous ne pouviez écrire à propos d'aucune plateforme d'intégration du marché jusqu'à celle-ci.

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.

Ce qu'un processus-utilisateur vous apporte

PropriétéPourquoi c'est important pour les jobs sans intervention
Headless par conceptionLes connecteurs à jeton statique (api_key, token) n'exigent aucun consentement interactif, credentials.set() constitue tout le flux.
Aucun navigateurUn conteneur cron avec seulement fetch et votre Application Key exécute tout le SDK.
Rayon d'impact par jobLa connexion propre à chaque automatisation fait qu'une clé exposée ne compromet qu'un job, pas tout le parc.
Métrage indépendantLes jetons par connexion montrent exactement ce que coûte le job nocturne de rapprochement.
Réessais déterministesUne clé d'idempotence garantit qu'un appel répété est appliqué exactement une fois.

Ces jobs détiennent de vrais identifiants sans humain dans la boucle. Gardez chaque extrait côté serveur, sourcez les jetons statiques depuis votre secret manager (jamais depuis un dépôt ni une invite du modèle) et donnez à chaque automatisation le plus petit ensemble de connecteurs avec lequel elle peut fonctionner.

1. Une automatisation, un id, un ensemble de connecteurs

Nommez le job d'après ce qu'il fait et traitez ce nom comme l'acteur propriétaire des connexions.

typescript
const JOB = 'svc-nightly-reconcile'; // stable, compatible URL, moins de 255 caractères, sans / \ ni espaces
const JOB_CONNECTORS = ['netsuite', 'stripe', 'sheets'];

2. Provisionnez un compte de service (une fois, à la configuration)

Cela s'exécute une seule fois lors de l'onboarding, un opérateur ou un script d'amorçage fournit les jetons statiques. Ensuite, le job utilise seulement la connexion.

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!, // depuis le 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();                 // obtenir-ou-créer, idempotent
    await connector.credentials.set(values);   // écriture seule ; p. ex. { API_KEY: … }
  }

  return Promise.all(
    Object.keys(tokens).map(async (slug) => ({
      slug,
      status: await user.connector(slug).status(), // attendez "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() ne renvoie jamais les valeurs stockées, et status() n'indique que les clés configurées. Un processus peut vérifier que ses connecteurs sont prêts sans jamais pouvoir exfiltrer ce qu'il a reçu, l'identifiant est utilisable, pas lisible.

3. Le job sans intervention lui-même

Le processus planifié n'a besoin d'aucun navigateur, d'aucun consentement, d'aucun utilisateur présent. Il charge ses capacités et agit.

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 : une capacité est indisponible, un identifiant a-t-il expiré ?');
    return;
  }

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

  const entry = await postEntry.execute(
    { lines: toJournalLines(invoices) },
    { idempotencyKey: `reconcile:${runDate()}` }, // une exécution logique = une écriture
  );
  if (entry.isError) await alertOps('écriture du reconcile rejetée : ' + entry.content[0]?.text);
}

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

idempotencyKey est la fonctionnalité d'automatisation. Un réessai, un déclenchement dupliqué du cron, un redéploiement en cours d'exécution, aucun ne publie deux fois l'écriture comptable, car le serveur déduplique les rejeux portant la même clé. Dérivez la clé de l'événement métier (la date d'exécution, l'id du ticket, l'id de livraison du webhook), jamais de Date.now().

4. Webhooks : un acteur, une livraison par clé

Pour les automatisations pilotées par événements, vous gardez généralement un seul utilisateur de compte de service, mais chaque appel mutatif s'appuie sur l'id de livraison afin qu'un webhook réessayé soit appliqué exactement une fois.

typescript
// POST /webhooks/lead  (vérifié)
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 de lead-intake non prêt');
    return;
  }

  const caps = await user.capabilities({ include: ['hubspot'] });
  await caps.findCapability('hubspot__create_contact')?.execute(
    { email: payload.email },
    { idempotencyKey: `lead:${payload.id}` }, // livraison réessayée -> pas de doublon
  );
}

5. Pipelines CI et exécuteurs éphémères

Un job CI s'authentifie exactement comme un cron : même Application Key, son propre external_id, identifiants statiques provisionnés dans l'environnement. La différence tient à la durée de vie, vous appelez disconnect() sur les exécuteurs éphémères quand le pipeline est détruit.

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} a échoué` },
    { idempotencyKey: `deploy:${runId}` },
  );
}

6. Faites tourner et retirez proprement

Comme tout est rattaché à un unique external_id, la mise hors service d'une automatisation est déterministe.

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

Pour faire tourner un identifiant, rappelez credentials.set() avec la nouvelle valeur, la connexion reste la même et aucune référence de capacité n'est rompue.

Fiabilité et sortie structurée pour les processus sans supervision

Un processus sans supervision est là où la robustesse intégrée du SDK compte le plus. Trois garanties sont héritées automatiquement :

Réessai automatique avec protection contre l'effet de troupeau. Les lectures idempotentes (et toute écriture portant un idempotencyKey) sont réessayées automatiquement : uniquement pour les erreurs transitoires 429/502/503/504 et réseau, avec un backoff à jitter complet qui respecte un Retry-After du serveur. Votre cron n'a pas besoin de sa propre boucle de réessai.

L'idempotence est le mécanisme de sécurité. Dans un job par lots, une panne et une réexécution sont banales. Un idempotencyKey stable supprime l'ambiguïté de savoir si la réexécution a dupliqué l'effet, car le serveur déduplique les rejeux sur cette clé. Fournissez la clé sur un appel mutatif et le SDK réessayera en toute sécurité même un POST non idempotent.

Consommez les résultats comme des objets, pas du texte. Quand un connecteur renvoie des données structurées, result.structuredContent les fournit déjà analysées, sans extraction de texte fragile dans le 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);

Associez les exécutions longues de lots à un timeoutMs par appel approprié et à une signal interrompue à l'arrêt du processus, et le job se termine de façon prévisible pendant un déploiement progressif.

Liste de contrôle de production

  • [ ] Donnez à chaque automatisation un external_id distinct et lisible (pas de compte partagé surdimensionné).
  • [ ] Sourcez chaque jeton statique depuis votre secret manager ; ne le versionnez jamais et ne le placez jamais dans une invite.
  • [ ] Provisionnez une fois à la configuration ; les runtimes lisent seulement status(), ils ne réenregistrent jamais de secrets.
  • [ ] Définissez un idempotencyKey stable dérivé de l'événement métier pour tout job mutatif.
  • [ ] Alertez quand status() !== 'ready' et sur isError, afin qu'une panne silencieuse ne soit pas une panne invisible.
  • [ ] Appelez disconnect() sur les comptes de service éphémères lors de la destruction.

Vous disposez désormais d'automatisations qui possèdent leur propre identité, connectent de vrais systèmes sans interface et agissent selon un agenda ou un événement, sans toute la mécanique OAuth et sans la dérive du « qui possède ce jeton ? » d'un compte partagé surdimensionné. Votre parc sans intervention est passé du bout du système dont les audits se plaignent à celui avec la meilleure histoire de gouvernance, parce que chacun de ses processus a enfin un nom.

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.

Prochaines étapes