npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ffy-api-cli

v0.2.1

Published

Agent-friendly CLI for the Frontify GraphQL API

Readme

Frontify CLI

Agent-friendly CLI for the Frontify GraphQL API.

Run with npx

After publishing to npm:

npx ffy-api-cli --help
npx ffy-api-cli auth login your-company.frontify.com
npx ffy-api-cli status
npx ffy-api-cli brand list --json

The npm package is named ffy-api-cli; the executable inside it is named frontify. The shorter package name frontify is already taken on npm.

Install locally

npm install
npm link

During development you can also run:

node ./bin/frontify.mjs --help

Sign in

Create an OAuth app in Frontify with a redirect URL like:

http://localhost:5600/oauth

Then run:

frontify auth login --domain your-company.frontify.com

You can also pass the instance as a positional value:

frontify auth login your-company.frontify.com

frontify status is a shortcut for frontify auth status.

The login flow uses Frontify's /api/oauth/random PKCE challenge by default, matching the existing Frontify CLI OAuth behavior. Token exchange is sent as JSON, which is what Frontify's OAuth endpoint expects. If you are testing a fully standard local PKCE OAuth app instead, add --challenge local.

By default, OAuth login now matches the official Frontify CLI:

client_id=block-cli
redirect_uri=http://localhost:5600/oauth
scope=basic:read+blocks:read+blocks:write

The official block-cli client is not allowed to issue basic:write. For general GraphQL writes, use a dedicated OAuth client registered for http://localhost:5600/oauth, or store a developer/service token.

OAuth client IDs are bound to their registered redirect URI. For example, frontify-explorer is used by Frontify's hosted Authenticator flow with a callback like /connection/authenticator, so it cannot complete a localhost CLI login unless that client is also configured for the CLI redirect URL. For that case, copy the access token from the hosted flow and store it with frontify auth set-token.

For general GraphQL write access through OAuth:

frontify auth login \
  --domain your-company.frontify.com \
  --client-id YOUR_GRAPHQL_CLI_CLIENT_ID \
  --scopes basic:read,basic:write

If your OAuth app requires a client secret:

frontify auth login \
  --domain your-company.frontify.com \
  --client-id YOUR_CLIENT_ID \
  --client-secret YOUR_CLIENT_SECRET \
  --scopes basic:read,basic:write

The CLI stores tokens in macOS Keychain when available. If Keychain is not available, it falls back to a chmod 0600 file under the CLI config directory. The raw token is not printed by default.

Service or developer token

For automation, you can store an existing Frontify token without OAuth:

pbpaste | frontify auth set-token \
  --domain your-company.frontify.com \
  --token-stdin \
  --scopes basic:read,basic:write

Agent usage

Once signed in, agents can call the CLI on your behalf:

frontify whoami --json
frontify gql --query 'query { currentUser { id name } account { id } }'
frontify gql ./query.graphql --vars ./vars.json

The account GraphQL query requires account:read in addition to basic:read. If frontify api run account returns a missing-scope error, sign in with:

frontify auth login your-company.frontify.com \
  --scopes basic:read,account:read,blocks:read,blocks:write

If the OAuth client rejects account:read, use a Frontify OAuth client allowed to request that scope or store a developer/service token with auth set-token.

The generated API layer covers the documented GraphQL reference:

frontify api list
frontify api list --kind mutation
frontify api list --json
frontify api --list
frontify api help createAsset --json
frontify api help createAsset --json --related
frontify api type RemoveCollectionAssetsInput --json
frontify api query --list
frontify api query --list --verbose
frontify api query brandEssentialsFonts
frontify api query asset
frontify api query createAsset
frontify api query brandEssentialsFonts --json
frontify api run currentUser
frontify api run webhooks --all --limit 100
frontify api run updateAsset \
  --input '{"id":"asset-id","data":{"title":"New title"}}' \
  --confirm
frontify api run updateAsset \
  --input '{"id":"asset-id","data":{"title":"New title"}}' \
  --dry-run

Mutations through api run require --confirm unless --dry-run is used. Use api help <operation> --json to inspect operation arguments and defaults, api type <TypeName> --json to inspect a focused GraphQL object, interface, enum, or input object shape, and api help <operation> --json --related only when you explicitly want the recursive related-type payload. api run validates nested required input fields before sending the request. Use api query <operation> to print the generated GraphQL document for any documented query or mutation without making a network request. Interface and union selections include inline fragments, for example ... on Image for asset. Curated templates use lower camel case names such as brandEssentialsFonts. The old PascalCase template names remain aliases. api query --list prints a names-only index; add --verbose for descriptions or --json for structured metadata. All GraphQL requests include X-Frontify-Beta: enabled so beta/reference fields such as guideline pages and blocks are available when the tenant supports them.

Human commands

For common workflows, use domain-specific commands:

frontify asset get ey123
frontify asset create library123 ./logo.png
frontify asset create library123 ./hero.jpg --title "Campaign hero" --tags "campaign,approved"
frontify asset create library123 ./hero.jpg --directory "Campaign/Launch"
frontify library upload library123 ./logo.png ./banner.jpg
frontify asset comments ey123 --status open --all
frontify asset comment ey123 --content "Looks good"
frontify brand list
frontify brand find sbb
frontify brand libraries brand-check --search sbb
frontify brand essentials brand-check
frontify brand colors brand-check
frontify brand fonts brand-check
frontify brand templates dynamo --search business --json
frontify brand templates pizzapie --all --output templates.json
frontify brand guidelines brand-check --language EN --search logo
frontify brand guideline-search brand-check "logo usage" --limit 20
frontify guideline pages guideline123 --search logo --all
frontify guideline search guideline123 "logo usage" --format jsonl --output guideline-search.jsonl
frontify guideline page page123 --json
frontify guideline block block123 --json
frontify download asset asset123 --output downloads/assets
frontify download library library123 --output downloads/my-library
frontify download folder folder123 --output downloads/my-folder
frontify download guideline guideline123 --output downloads/my-guideline
frontify library assets library123 --search logo --all
frontify library comments library123 --status all --latest 10 --all-comment-pages
frontify library comments library123 --latest 100 --format jsonl --output comments.jsonl
frontify comment reply c123 --content "Updated"
frontify comment resolve c123
frontify folder create parent123 --name "Campaign"
frontify collection add-assets collection123 asset1 asset2
frontify metadata set asset1 --property property1 --value '"Approved"' --confirm
frontify webhook list
frontify webhook list --all

Human commands print readable tables in a terminal. Add --json for machine-readable output.

Asset reads:

  • asset get and library assets query concrete Frontify asset types by default: Image, Video, Document, File, and Audio.
  • Type-specific fields are selected with GraphQL inline fragments, including filename, size, extension, fileCreatedAt, previewUrl, thumbnailUrl, and downloadUrl where available.
  • Media-specific stable fields are included when applicable, such as image/video dimensions, video duration/bitrate, document page count, and focal point.
  • Asset reads also include stable shared fields such as tags, current user permissions, and custom metadata values.
  • Comment reads include markings, mentioned users, current user permissions, modifiers, and replies.
  • Signed downloadUrl values can be verbose and temporary; use --fields or --output when collecting lots of asset records.
  • Workflow task/status fields are beta and may be unavailable for a tenant/client; use explicit gql or api run checks before depending on them.

Guideline reads:

  • brand guidelines <brand> lists guideline projects for a brand, including default/translation languages and page counts.
  • brand templates <brand> lists creative templates for a brand through scoped brand(id:) queries, avoiding the over-broad nested brands { creativeTemplates } shape.
  • brand essentials <brand>, brand colors <brand>, and brand fonts <brand> read Brand Essentials color palettes and font families.
  • Add --search term to brand guidelines, guideline pages, and guideline library-pages to filter the returned listing client-side.
  • guideline pages <guidelineId> lists beta Guideline Pages for a guideline.
  • guideline library-pages <guidelineId> lists legacy/current library pages exposed by the guideline.
  • guideline page <pageId> reads page sections and content elements. It includes headings, blocks, block references, block type, searchable block content, settings, and referenced block asset previews.
  • guideline block <blockId> reads a block directly, including content, settings, type schema, and asset settings.
  • guideline search <guidelineId> <term> scans guideline pages, headings, block content, block type names, URLs, and settings. Use --limit, --page-limit, --max-pages, --section-limit, --section-max-pages, --element-limit, --titles-only, and --context to bound the scan.
  • brand guideline-search <brand> <term> searches across a brand's guidelines with the same output and adds --guideline-limit / --guideline-max-pages.
  • download asset <assetId> saves one binary asset file. download library <libraryId> and download folder <folderId> save binary asset files in bulk; folders recurse by default and support --no-recursive.
  • download guideline <guidelineId> creates a resumable local guideline export with pages.json, page-files.json, failures.json, and one JSON file per guideline page.
  • Download commands skip existing files unless --overwrite is passed and support --dry-run for previews. They print concise progress by default, such as Found library "Media"; starting download... and Downloaded 10 assets to ...; add --json for the full receipt. For bulk downloads, --limit 10 caps the total downloaded assets/pages and --page-size n controls the API request chunk size.
  • Search is client-side because the documented guideline GraphQL fields do not expose native text query arguments.
  • Guideline page/block queries are beta and can be tenant- or token-gated; when Frontify rejects them, the CLI prints the GraphQL response unchanged and exits 2.

Large outputs:

  • --output <file> writes the payload to disk and prints only a small receipt to stdout.
  • --format jsonl writes list-like results as one JSON record per line, useful with head, rg, wc -l, and jq.
  • --brief projects common records to compact fields; for comments this includes id, created time, asset title, resolved state, creator, and content.
  • --fields a,b,c selects exact record fields. Dot paths like creator.name are supported.
  • --truncate n shortens long string fields after whitespace is collapsed.

Example:

frontify library comments library123 \
  --status all \
  --latest 100 \
  --format jsonl \
  --fields createdAt,assetTitle,isResolved,creatorName,content \
  --truncate 240 \
  --output comments.jsonl

head -n 10 comments.jsonl
rg "red window" comments.jsonl
jq -r '.assetTitle + "\t" + .content' comments.jsonl | head

Asset creation:

  • asset create <parentId> <file...> accepts a library, workspace project, or folder API id as parentId.
  • library upload <libraryId> <file...> is a shortcut for the common library upload case.
  • The CLI initializes uploadFile, streams each local file chunk to the returned S3 PUT URLs, then calls createAsset.
  • Titles default to the filename without extension. Use --title for a single file.
  • Useful options: --description, --alt, --author, --external-id, --tags "a,b", --directory "Folder/Subfolder", --skip-file-metadata, --collection <collectionId>, and --dry-run.
  • Frontify requires basic:write for uploadFile and createAsset; the default block-cli OAuth client cannot request that scope, so use a dedicated OAuth client or stored developer/service token for uploads.

Pagination:

  • Use --page n --limit n for a single page.
  • Use --all on supported paginated commands to fetch every page and merge items.
  • Use --max-pages n as a safety cap for long scans.
  • library comments always scans asset pages and supports --all-comment-pages when an individual asset has more comment pages.

Rate limits

Frontify currently documents a GraphQL limit of 2500 requests per 5 minute rolling window. The CLI protects paginated and repeated calls by keeping a shared rolling-window request ledger and handling HTTP 429 responses centrally:

  • By default, the CLI reserves at most 2400 GraphQL requests per Frontify host in any 5 minute rolling window, leaving headroom below the published ceiling.
  • The rolling-window state is shared across repeated CLI invocations for the same Frontify host, so shell loops and agent scripts do not accidentally bypass the budget.
  • Optional request spacing is available with FRONTIFY_RATE_LIMIT_MIN_INTERVAL_MS, but the safety mechanism is the rolling-window count, not the delay between requests.
  • HTTP 429 responses retry with Retry-After when present, otherwise exponential backoff.
  • HTTP 422 GraphQL errors are not retried. Repeated GraphQL errors trigger a short local cooldown to avoid suspicious error bursts.
  • HTTP 403 responses trigger a local cooldown as a conservative stop signal; retry later instead of probing.
  • If the requested wait is too long, the CLI stops and returns structured rateLimit metadata instead of continuing to send requests.

Environment overrides:

FRONTIFY_RATE_LIMIT_REQUESTS=2400
FRONTIFY_RATE_LIMIT_WINDOW_MS=300000
FRONTIFY_RATE_LIMIT_MIN_INTERVAL_MS=0
FRONTIFY_RATE_LIMIT_SHARED=true
FRONTIFY_RATE_LIMIT_RETRIES=3
FRONTIFY_RATE_LIMIT_BASE_DELAY_MS=1000
FRONTIFY_RATE_LIMIT_MAX_WAIT_MS=60000
FRONTIFY_RATE_LIMIT_422_THRESHOLD=3
FRONTIFY_RATE_LIMIT_422_COOLDOWN_MS=60000
FRONTIFY_RATE_LIMIT_403_COOLDOWN_MS=300000

Authenticated profiles use the instance GraphQL endpoint by default:

https://your-company.frontify.com/graphql

The public developer endpoint is still useful for unauthenticated schema checks:

https://developer.frontify.com/graphql

Override it per profile with --graphql-endpoint during login or token setup.

Commands

frontify auth login
frontify auth set-token
frontify auth status
frontify status
frontify auth set-endpoint
frontify auth logout
frontify whoami
frontify gql
frontify introspect
frontify api list
frontify api help
frontify api run
frontify asset
frontify brand
frontify library
frontify comment
frontify folder
frontify collection
frontify guideline
frontify metadata
frontify webhook