SuperAPI AI Directory

netlify-ops

Deployment & operations

View repository

Updated 10-15 20:07

About this skill

netlify-ops

Overview

This skill lets an agent perform Netlify web-hosting operations CLI-first (the netlify CLI) with a REST fallback on Netlify's official API — no SDK. The CLI is the ergonomic common path (create a site, deploy a directory, read status); the REST API is the comprehensive long tail (custom domains, fine-grained reads, DNS), grounded on a bundled OpenAPI via an endpoint index + a $ref-resolver so coverage never loads the spec into context. The agent consumes the caller-injected token, picks the CLI command or REST operation, runs it, and parses the JSON. It drives Netlify's static/JAMstack + serverless surface — not full-stack container hosting.

When to activate

  • ✅ Creating a Netlify site, or deploying a build to one (draft/preview or --prod).
  • ✅ Setting + verifying a custom domain, or configuring a site's DNS.
  • ✅ Reading deploy status (the state field) or listing/inspecting sites/deploys.

Do NOT activate when:

  • You need full-stack container/app hosting (Render, Fly.io, Railway, Vercel SSR-as-PaaS) — Netlify here is a static/JAMstack + serverless-function host only.
  • You only need credential setup — credentials are provided by the caller; this skill does not provision or resolve them, and never runs netlify login (see references/credentials.md).

Workflow

Step 1 — Receive the injected credentials

The caller has already resolved the account and injected the token — consume it; do not look for a record or log in. Netlify has no non-secret record field (the host is fixed; no account id/email). You receive from context the capability (web-hosting) and the token's variable NAME. Resolve that name as the project-level .env value if that file exists and defines the var, else the environment variable of that name (project .env first). The token value is never in context prose — only its variable name. Perform no scope resolution or directory walk; it is project .env, not .envrc.

Bridge the token into NETLIFY_AUTH_TOKEN (the CLI reads it; the REST fallback sends Authorization: Bearer). Never netlify login/logout. Full contract: references/credentials.md.

Step 2 — Pick the common path (CLI) or the long tail (REST)

Step 3 — Run it

The four bundled scripts cover the priority ops:

  • create-site.sh <name> [account-slug] — netlify sites:create … --json (returns the site; its id is the id field — --json keys it as id, not site_id — pass that id as --site to deploy).
  • deploy.sh <site-id> <dir> [--prod] — netlify deploy --dir … --site … --json (draft by default; --prod publishes).
  • set-custom-domain.sh <site-id> <domain> — REST PATCH updateSite (custom_domain).
  • deploy-status.sh <site-id> <deploy-id> — getSiteDeploy (the state field).

For anything else: CLI command from cli-index.md, or a curl against the resolved REST op.

Step 4 — Handle the response

  • Deploys are async + multi-stage. Pull the deploy id from deploy.sh's --json output (the deploy_id / id field) to feed deploy-status.sh. A deploy moves building → uploading → processing → ready (or error). Poll deploy-status.sh (or getSiteDeploy) until state is terminal; a draft deploy has a unique preview URL, --prod publishes to the live site.
  • Custom domain ≠ DNS. Setting custom_domain does not make it resolve — follow with DNS config (configureDNSForSite / dns_zones records).
  • Errors / limits: non-2xx JSON; rate limits are 500 req/min general and deploys 3/min · 100/day — honor the X-RateLimit-* headers. A 401 means a missing/expired token.

Rules

Hard rules (never violate):

  • Never netlify login / netlify logout. Auth is the injected token via NETLIFY_AUTH_TOKEN (or --auth); the CLI runs headlessly. A missing token is a hard caller error, not a cue to log in.
  • Never read or print the token value. Reference it only as $NETLIFY_AUTH_TOKEN (CLI env) or Bearer $NETLIFY_AUTH_TOKEN (REST); the subprocess reads it from the environment. The token lives only in .env (gitignored) or the environment.
  • Resolve before constructing a REST call. Build a body/params from the $ref-resolved schema (endpoint.py) — never from a guessed field set.
  • Deploy to a known site_id with --site. A fresh checkout has no .netlify/state.json; never rely on link state, never use --allow-anonymous.
  • CLI-first, REST for the tail. Use the CLI for create/deploy/status; drop to REST for custom domain, DNS, and fine-grained reads.
  • This skill never writes credentials. Credentials are provided by the caller.

Preferences (override-able):

  • Pass --json (machine output) and --force (skip prompts) to the CLI for non-interactive use.
  • For body-carrying REST writes (updateSite, createSiteDeploy) prefer the pure curl over the netlify api escape hatch (the escape-hatch body-key convention is unverified); use the escape hatch for path-params-only ops.
  • Prefer the CLI netlify deploy --dir over the hand-rolled digest protocol — it does the SHA1 diff + upload for you.

Gotchas

  • Don't log in. The single biggest contract violation: reaching for netlify login when the token "isn't working." A bad token is an error to surface, not a login to perform.
  • --no-build, not --build. The CLI builds by default; there is no --build flag — opt out with --no-build.
  • Draft vs prod. Omitting --prod gives a draft/preview deploy (unique URL, production untouched). Only --prod publishes to the live site.
  • site_id is the key. --name/--account-slug are human-facing; every API op keys on the opaque site_id (in .netlify/state.json after a link). Note: sites:create --json returns this id under id, not site_id (.site_id is null on create) — read .id from create output and pass it as --site.
  • The digest-deploy required-files step. A hand-rolled REST createSiteDeploy returns a required array of SHA1s you must then upload — skip it and the deploy never reaches ready. The CLI handles this.
  • Custom domain needs DNS. updateSite sets custom_domain; the domain only resolves once DNS is configured.
  • Rate limits bite deploys. 3 deploys/min, 100/day — back off on X-RateLimit-Remaining: 0.
  • state is a free string. The spec doesn't enumerate deploy states; treat the value list as from docs and confirm against a live deploy.

Anti-patterns

  • Running netlify login. Forbidden — the contract injects the token; never resolve your own auth or prompt the user to log in.
  • Echoing the token. Never echo $NETLIFY_AUTH_TOKEN, never paste it into a printed command, never write its value to a file. Reference the env var only.
  • Guessing a REST body. Resolve updateSite / createSiteDeploy with endpoint.py — don't hand-write a body from memory.
  • Deploying without --site. Relying on link state in a fresh checkout silently deploys to the wrong (or no) site. Always pass site_id.
  • Using --allow-anonymous. That creates a claimable site without auth — not this skill's path; always deploy to a known site with the injected token.
  • Loading the whole spec. Don't cat the ~690 KB OpenAPI — scan the index, resolve one op.

Output

This skill produces CLI/API side effects (the requested Netlify operation) and returns the parsed JSON to the calling agent. It writes no files of its own (it only consumes the caller-injected token). For a deploy it reports the deploy id + URL + terminal state; for a site it reports the site_id + URL; for reads it returns the requested JSON. The abstract consumer is the calling agent (or a sub-agent) that needs the web-hosting operation performed; secrets never enter that output.

Related

  • references/credentials.md — the credential contract (token-by-name → NETLIFY_AUTH_TOKEN, the ordered load rule, no-login).
  • references/cli-reference.md — the netlify CLI surface + the netlify api escape hatch.
  • references/rest-api.md — the REST long tail, the digest-deploy protocol, state + limits.
  • This is the CLI-first sibling of github-cli-ops and the OpenAPI-grounded sibling of atlassian-rest-ops, under the per-provider service-skill pattern.

Progressive disclosure

Heavy content lives in subfolders, loaded only on demand:

  • references/credentials.md — caller-injected token, the ordered load rule, the Bearer bridge, no-login. Load in Step 1.
  • references/cli-reference.md — the CLI commands + escape hatch + CLI gotchas. Load in Steps 2–3.
  • references/rest-api.md — the REST pattern, priority-op table, digest-deploy protocol, state + limits. Load for the REST tail.
  • references/sources.md — provenance (official OpenAPI + CLI/API docs + the two source skills) + the "confirm live" items.
  • assets/cli-index.md — the CLI quick command list (Step 2).
  • assets/endpoint-index.md — one line per REST operation (Step 2).
  • assets/netlify-openapi.json — the bundled spec (Swagger 2.0; queried via the resolver, never loaded wholesale).
  • scripts/endpoint.py + .validation.md — the python3 $ref-resolver (Step 2).
  • scripts/{create-site,deploy,set-custom-domain,deploy-status}.sh + .validation.md — four validated priority-op scripts.

Standalone usage (optional, not required)

This is a convenience for a human running the skill by hand outside an agent-flow — it is not a dependency of the skill. The skill's normative contract is caller-injection (Step 1); this appendix is only the manual-operator bridge.

To run by hand, create a Netlify PAT (User → Applications → Personal access tokens), store its value in a gitignored .env, install the CLI (npm i -g netlify-cli), then populate the var yourself:

set -a; source .env; set +a                 # loads $NETLIFY_PROD_TOKEN, never prints it
export NETLIFY_AUTH_TOKEN="$NETLIFY_PROD_TOKEN"
bash scripts/create-site.sh my-site
bash scripts/deploy.sh <site-id> ./dist --prod

The token value is referenced by name only, never printed. Prefer a revocable, short-lived PAT.

Body budget

  • description ≤ 1,024 chars (agentskills.io cap).
  • Body ≤ ~500 lines / 5,000 tokens — kept in context every turn; CLI/REST detail lives in references/.
  • assets/netlify-openapi.json is large (queried on disk, never loaded into context).