---
name: vinkius-service-account-automation
description: 'Teaches the unattended-automation pattern on @vinkius/connect: cron jobs, webhooks, CI steps and nightly batch processes each become their own user with a stable external_id, connect headlessly with static credentials, act idempotently on business events, and are metered, observable and revocable per job. Explains naming, provisioning once at setup, the unattended loop, webhooks keyed by actor, CI boundaries and rotation. Use when building scheduled jobs, event handlers or CI flows with @vinkius/connect. Prerequisite: vinkius-connect-core.'
license: Apache-2.0
metadata:
  author: Vinkius
  package: "@vinkius/connect"
  pattern: "user = process / service account"
  docs: "https://vinkius.com/learn/connect-sdk/build-service-account-automation"
---

# Unattended automations and service accounts

Applies vinkius-connect-core. The principle: a process that needs to act deserves an identity, not a borrowed one. One shared god-key across your whole automation estate turns any leak into a full estate breach; per-process ids turn it into one job's problem.

## Identity rules

- Name each automation after what it does (`svc-nightly-reconcile`, `ci-issue-on-failed-deploy`, `hook-lead-triage`) and treat that name as the actor owning the connections. One job, one id, one connector set.
- The id is durable (it survives redeploys and refactors) and non-secret. `user.ensure({ kind: 'service', owner })` tags it for your dashboards.

## Provisioning once, then headless

Provision at setup, never inside the job: a bootstrap script or operator run calls `connect()` and `credentials.set()` with the static token from your secret manager, then the runtime only ever reads `status()`. For OAuth connectors there is nothing to set (consent happened once in `connect()`), which is why static-credential connectors are the natural fit for unattended work: the whole flow is headless.

## The unattended loop

- The job resolves its own id (from configuration), loads its capabilities, calls the model if the step is agentic, and executes each chosen capability with controls.
- Idempotency is the load-bearing rule: a nightly reconciliation that retried, a cron that double-fired, a CI rerun mid-deploy, none may double-post. Derive `idempotencyKey` from the business event (run date plus job, ticket id, webhook delivery id), never from `Date.now()`; the server dedupes replays of the same key, so a re-run is a non-event.
- A failed connector or an `isError` result is data to log and alert on, not an exception that kills the process; thrown SDK errors (auth, quota) are a human-attention event. Alert when `user.connectors()` shows anything not `ready`, a silent failure must never become an invisible one.
- Webhooks: one delivery, one actor. Route by the event type to the automation whose id owns the connector for it; the key makes redelivery safe.

## Rotation and retirement

Rotate a credential by `set()`-ing the new value, the connection and its capabilities survive unchanged. Retire an automation with `disconnect()` as part of teardown so a decommissioned job never leaves live tokens behind.

## Rules that keep this safe

- Server-side only: static tokens come from your secret manager, never from a repository, an image layer or a model prompt.
- Give each automation the smallest connector set it can function with; per-job metering makes a runaway loop cheap to spot.
- No browser exists in this flow at runtime: a container with only fetch and the Application key is the entire surface.

## Verify it works before shipping

Run the same business event twice through the job and assert exactly one side effect; revoke one job's token and assert every other job still runs; assert the logs prove which automation made each call.

Docs with the full walkthrough: https://vinkius.com/learn/connect-sdk/build-service-account-automation
