Skip to content

CLI Commands

Every command below accepts the global options. Run nuelink-cli --help for the top-level list, or nuelink-cli help <command> for one command’s flags.

Terminal window
printf '%s' "$NUELINK_API_KEY" | nuelink-cli auth:login --stdin
nuelink-cli auth:status
nuelink-cli auth:validate
nuelink-cli auth:clear
CommandFlagsWhat it does
auth:login--api-key <apiKey>, --stdinSaves a key to the local config
auth:status—Shows where the key is resolved from, without calling the API
auth:validate—Checks the resolved key against the API with GET /auth
auth:clear—Removes the saved key

Prefer --stdin over --api-key for auth:login, it keeps the key out of your shell history and your process list.

auth:status tells you which key the CLI will use; auth:validate tells you whether that key works.

Terminal window
nuelink-cli me
Terminal window
nuelink-cli brands --per-page 5 --page 1
nuelink-cli --json brands --per-page 5 --page 1
FlagDefault
--per-page <number>25
--page <number>1

Pagination accepts --page of 1 or more and --per-page from 1 through 100.

Terminal window
nuelink-cli schedule --brand-id 13493

Requires --brand-id. Returns every weekly queue slot in the brand, from every collection, grouped by day, in the brand’s timezone. It isn’t paginated.

Terminal window
nuelink-cli channels --brand-id 13493 --per-page 5 --page 1

Requires --brand-id. Output highlights name, status, and platform type. A page can come back short while more pages remain, because the API counts total before it filters channels; see GET /channels.

List:

Terminal window
nuelink-cli collections --brand-id 13493 --per-page 5 --page 1

Each collection comes back with its status, evergreen setting, channels, queue slots, and post counts.

Create:

Terminal window
nuelink-cli collections:create \
--brand-id 13493 \
--title "API Brand" \
--description "Collection from CLI" \
--max-republish 2 \
--channels "300294,60112" \
--queues "Mon 10:10,Mon 12:12" \
--dry-run
FlagRequiredNotes
--brand-id <id>Yes
--title <title>YesMax 255 characters
--description <text>NoDefaults to empty
--max-republish <number>NoEvergreen: 0 turns it off, N re-adds published posts after N weeks
--channels <ids>NoComma-separated channel IDs
--queues <slots>NoComma-separated, format Mon 10:10

Verified users can create up to 10 slots per collection per day, unverified users up to 5. The API doesn’t reject duplicate slots, so check --queues for repeats before you send.

Passing --channels "" or --queues "" sends an empty array. A collection with no channels is valid, and it’s the safest target for a first integration, since posts created there can’t reach an audience:

Terminal window
nuelink-cli --dry-run collections:create \
--brand-id 13493 \
--title "Draft Review" \
--description "Draft-only integration collection" \
--max-republish 0 \
--channels "" \
--queues ""

List:

Terminal window
nuelink-cli automations --brand-id 13493 --per-page 5 --page 1

Create:

Terminal window
nuelink-cli automations:create \
--brand-id 13493 \
--collection-id 720 \
--feed-url "https://www.nasa.gov/rss/dyn/breaking_news.rss" \
--import-as-type IMAGE \
--name "NASA Breaking News" \
--type RSS \
--title "{{title}}" \
--caption "{{title}} {{link}}" \
--load-old-posts false \
--add-posts-as-draft true \
--dry-run
FlagRequiredAllowed values / default
--brand-id <id>Yes
--collection-id <id>Yes
--feed-url <url>Yes
--import-as-type <type>YesLINK, IMAGE, VIDEO, CAROUSEL
--name <name>YesThe automation’s name, max 255 characters
--type <type>YesThe feed source: RSS, YOUTUBE, SHOPIFY, and the rest of the 27 values under POST /automations
--title <text>NoTitle template for imported posts. Defaults to {{title}}
--caption <text>NoCaption template for imported posts, max 255 characters
--load-old-posts <boolean>NoThe API defaults to false
--add-posts-as-draft <boolean>NoThe API defaults to false
--refresh-rate <hours>No24, 12, 6, or 1. The API defaults to 24

The table view is useful for scanning automation status, type, and feed URL at a glance.

List:

Terminal window
nuelink-cli media --brand-id 13493 --type IMAGE --per-page 5 --page 1

Media type filters are IMAGE, VIDEO, GIF, APPLICATION, DOCUMENT, and CSV.

Upload:

Terminal window
nuelink-cli media:upload --brand-id 13493 --file ./assets/image.jpg --dry-run
FlagRequired
--brand-id <id>Yes
--file <path>Yes

Allowed upload extensions are jpg, jpeg, png, bmp, mp4, mov, and pdf, up to 100 MiB: the same types the upload endpoint accepts. NUELINK_MAX_MEDIA_SIZE_BYTES can lower that local limit but not raise it. A dry run checks the path, regular-file status, non-empty size, extension, and size without reading file bytes; it does not prove the server will accept the upload.

Terminal window
nuelink-cli posts --brand-id 13493 --collection-id 33273 --per-page 5 --page 1
nuelink-cli posts --brand-id 13493 --collection-id 33273 \
--status DRAFT --sort-by updated_at --sort-order desc

Output highlights post ID, type, status, and publish date.

FlagRequiredAllowed values / notes
--brand-id <id>Yes
--collection-id <id>Yes
--status <status>NoPENDING, DRAFT, PUBLISHED
--post-type <type>NoTEXT, IMAGE, VIDEO, MULTIMEDIA, SHORT, DOCUMENT, THREAD, SLIDES, REEL, TIKTOK, STORY, POLL, ENGAGE_COMMENT
--posting-type <type>NoNOW, SCHEDULE, QUEUE, DRAFT
--created-from <datetime>NoUTC, YYYY-MM-DD HH:mm:ss
--created-to <datetime>NoUTC, YYYY-MM-DD HH:mm:ss. Can’t be earlier than --created-from
--sort-by <field>Noid (the API’s default), title, post_date, created_at, updated_at
--sort-order <order>Nodesc (the API’s default), asc
--per-page <number>, --page <number>No25 and 1

posts has no --view flag. For the view shortcut on one collection, use brand-posts with --collection-id.

Terminal window
nuelink-cli brand-posts --brand-id 13493 --view SCHEDULED --sort-by post_date --sort-order asc
nuelink-cli brand-posts --brand-id 13493 --collection-id 33273 --view DRAFT

Takes the same flags as posts, except that --collection-id is optional, plus --view: QUEUE, SCHEDULED, DRAFT, or PUBLISHED.

Publish dates come back in UTC. Convert them to the brand’s timezone before you compare them with a --scheduled-at value, which is brand-local.

Best for anything complex, and for payloads you keep in version control:

Terminal window
nuelink-cli posts:add-json \
--brand-id 13493 \
--collection-id 33273 \
--payload ./post.json \
--dry-run

Pass --payload - to read the JSON from stdin instead.

{
"title": "CLI Post Title",
"caption": "Post body from CLI",
"publishMode": "DRAFT"
}

The payload accepts the same fields as POST .../posts, including platforms, poll, media, comment, and autoThreadText.

Best for one-liners and simple posts:

Terminal window
nuelink-cli posts:create \
--brand-id 13493 \
--collection-id 33273 \
--title "title here" \
--caption "body here as well" \
--alt "alt text" \
--link "https://nuelink.com" \
--publish-mode DRAFT \
--post-as-story true \
--auto-thread-text false \
--comment "this is a comment" \
--comment-delay 30 \
--media-ids "media-id-1" \
--media-urls "https://media.nuelink.com/media/abc" \
--poll-question "Which feature next?" \
--poll-options "Analytics,Approvals" \
--poll-period 3d \
--poll-correct-option-index 0 \
--poll-explanation "Analytics shipped first" \
--instagram-collab "harperandvine" \
--instagram-location-id "abc123" \
--instagram-location-name "Brooklyn" \
--instagram-trial-reel MANUAL \
--instagram-share-to-feed true \
--facebook-location-id "abc123" \
--facebook-location-name "Brooklyn" \
--tiktok-send-to-inbox true \
--tiktok-auto-add-music false \
--youtube-tags "tag1,tag2,tag3" \
--youtube-playlists '[{"channelId":1,"playlistIds":["1","2"]}]' \
--googlemybusiness-upload-to-photos-section true \
--dry-run

Core flags

FlagRequiredNotes
--brand-id <id>Yes
--collection-id <id>Yes
--caption <text>Yes, unless a poll is set1–3000 characters
--publish-mode <mode>NoDRAFT (default), QUEUE, SCHEDULE, IMMEDIATE
--scheduled-at <value>Only with SCHEDULEYYYY-MM-DD HH:mm:ss, in the brand’s timezone. The CLI validates format and calendar validity; the API requires at least 10 minutes in the future, which --dry-run does not verify
--title <text>NoUsed by YouTube, Pinterest
--alt <text>NoAccessibility alt text
--link <url>NoGoogle Business Profile LEARN_MORE button and Pinterest destination link
--post-as-story <boolean>NoInstagram and Facebook only
--auto-thread-text <boolean>NoNuelink’s automatic text splitting. See Threads
--media-ids <ids>NoComma-separated, from media:upload
--media-urls <urls>NoComma-separated public URLs
--comment <text>NoAuto-comment, 1–1000 characters
--comment-delay <seconds>With --commentThe API needs both. The CLI doesn’t check the pair before sending, so a comment without a delay fails at the API

Poll flags: --poll-question, --poll-options (comma-separated, 2–4 items), --poll-period (1d, 3d, 7d), --poll-correct-option-index (zero-based), --poll-explanation.

Platform flags: --instagram-collab, --instagram-location-id, --instagram-location-name, --instagram-trial-reel, --instagram-share-to-feed, --facebook-location-id, --facebook-location-name, --tiktok-send-to-inbox, --tiktok-auto-add-music, --youtube-tags, --youtube-playlists, --googlemybusiness-upload-to-photos-section.

Reschedule a post, put it in its collection’s queue, move it to the front or back of that queue, or turn it into a draft. Content can’t be changed.

Terminal window
nuelink-cli posts:update --brand-id 13493 --post-id 4364709 \
--publish-mode SCHEDULE --scheduled-at "2026-10-12 09:00:00" --dry-run
nuelink-cli posts:update --brand-id 13493 --post-id 4364709 \
--queue-position FRONT --dry-run
nuelink-cli posts:update --brand-id 13493 --post-id 4364709 \
--publish-mode DRAFT --dry-run
FlagRequiredAllowed values / notes
--brand-id <id>Yes
--post-id <id>Yes
--publish-mode <mode>Unless --queue-position is setDRAFT, SCHEDULE, QUEUE. No IMMEDIATE
--queue-position <position>Unless --publish-mode is setFRONT, BACK. Can be combined with --publish-mode QUEUE
--scheduled-at <value>Only with SCHEDULEYYYY-MM-DD HH:mm:ss, in the brand’s timezone, at least 10 minutes ahead

The CLI rejects a command with neither --publish-mode nor --queue-position, a SCHEDULE without --scheduled-at, and a --scheduled-at without SCHEDULE, before anything is sent. A published post can’t be updated; the API returns 409.

Terminal window
nuelink-cli posts:delete --brand-id 13493 --post-id 4364709 --dry-run
FlagRequired
--brand-id <id>Yes
--post-id <id>Yes
Terminal window
nuelink-cli published-posts --brand-id 13493
nuelink-cli published-posts --brand-id 13493 --status FAILED
nuelink-cli published-posts --brand-id 13493 --sort-by likes \
--published-from "2026-09-01 00:00:00" --published-to "2026-09-30 23:59:59"

Lists what went out, one entry per channel, with the delivery status, the link to the post on the platform, and likes, comments, and shares.

--status defaults to PUBLISHED, and entries in other statuses are left out. To check one post on every channel, ask for each status and walk every page of each, since a post with many channels, or one that evergreen republished, can have more entries than fit on a page:

Terminal window
check_delivery() {
local state page response next
for state in PUBLISHED FAILED RETRYING SKIPPED SENT; do
page=1
while :; do
response=$(nuelink-cli --json published-posts --brand-id "$BRAND_ID" --post-id "$POST_ID" \
--status "$state" --per-page 100 --page "$page") || { echo "$response" >&2; return 1; }
jq -r --arg state "$state" '.data[] | "\($state)\t\(.channel.name)\t\(.url // "")"' <<<"$response" || return 1
next=$(jq -r '.pagination.nextPageUrl // empty' <<<"$response") || return 1
[ -n "$next" ] || break
page=$((page + 1))
done
done
}
check_delivery

check_delivery stops at the first failed request, prints the CLI’s JSON error, and returns 1. In a script or a CI step, that fails the job instead of reporting an incomplete picture as a success.

FlagDefaultAllowed values / notes
--brand-id <id>Required
--status <status>PUBLISHEDPUBLISHED, FAILED, RETRYING, SKIPPED, SENT
--collection-id <id>
--channel-id <id>
--post-id <id>The Nuelink post the entries came from
--post-type <type>
--search <text>Case-insensitive match on the published text, max 255 characters
--published-from <datetime>UTC, YYYY-MM-DD HH:mm:ss
--published-to <datetime>UTC. Can’t be earlier than --published-from
--sort-by <field>idid, created_at, likes, comments, shares
--sort-order <order>descasc, desc
--per-page <number>, --page <number>25, 1

The CLI is built to make the careful path the easy one. In order:

  1. Verify the connected account with auth:status, auth:validate, and me.
  2. Resolve brand, collection, and post IDs from list commands, never hardcode them from an example.
  3. Preview every API mutation with --dry-run.
  4. Default unclear publishing intent to DRAFT.
  5. Ask before creating a collection or automation, uploading, queueing, scheduling, publishing, or updating a post.
  6. Confirm posts:delete separately, against the exact brand and post ID, immediately before running it.
  7. Repeat the exact previewed command after confirmation, not an abbreviated one that could change the payload.
  8. Read the resource back with the matching list command.

Step 7 is the one people skip. A shortened re-run is a different request, and the thing you reviewed is not the thing you sent.

#!/usr/bin/env bash
set -euo pipefail
export NUELINK_API_KEY="${NUELINK_API_KEY:?NUELINK_API_KEY is required}"
nuelink-cli brands --per-page 10 --page 1
nuelink-cli collections --brand-id 13493 --per-page 10 --page 1
Terminal window
BRAND_ID=$(nuelink-cli --json brands --per-page 100 \
| jq -r '.data[] | select(.title == "Fernwood Botanicals") | .id')
nuelink-cli --json collections --brand-id "$BRAND_ID" \
| jq -r '.data[] | "\(.id)\t\(.title)"'
Terminal window
nuelink-cli --json published-posts --brand-id "$BRAND_ID" --status FAILED --per-page 100 \
| jq -r '.data[] | "\(.postId)\t\(.channel.name)\t\(.message // "")"'

This reads the 100 most recent failures. Add --page 2 and onward to go further back.

Store NUELINK_API_KEY as a repository or organization secret. Keep validation jobs network-free with --dry-run. Put any real QUEUE, SCHEDULE, or IMMEDIATE command in a separately approved deployment job or protected environment.

- name: Preview release announcement
env:
NUELINK_API_KEY: ${{ secrets.NUELINK_API_KEY }}
run: |
nuelink-cli posts:create \
--brand-id "$BRAND_ID" \
--collection-id "$COLLECTION_ID" \
--caption "v${{ github.ref_name }} is out" \
--publish-mode DRAFT \
--dry-run

Invalid token

  • Run nuelink-cli auth:status to confirm which source is active, then nuelink-cli auth:validate to check the key against the API.
  • Re-save the key: printf '%s' "$NUELINK_API_KEY" | nuelink-cli auth:login --stdin
  • Check that a stale NUELINK_API_KEY isn’t overriding your saved config. The environment variable wins over the config file.

posts:delete returns 403

Sensitive actions aren’t enabled for the account. The account owner can turn on Allow AI to perform sensitive actions in Settings → API, or you can move the post to DRAFT with posts:update instead.

automations:create rejects --sub-type or --dynamic-title

Those flags were renamed in 1.4.5. See Automations.

Request timeout

Terminal window
NUELINK_TIMEOUT_MS=120000 nuelink-cli media --brand-id 13493

NUELINK_TIMEOUT_MS must be between 1 and 600000. NUELINK_GET_RETRIES must be 0 to 10. Retry delay variables must be non-negative integers no greater than 600000.

Command help

Terminal window
nuelink-cli --help
nuelink-cli help collections
  • Treat API keys as secrets.
  • Never commit a token to source control.
  • Prefer environment-based secrets in CI/CD.
  • Use --stdin with auth:login so the key never enters your shell history.

The CLI is MIT licensed. For support, email support@nuelink.com.