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.
printf '%s' "$NUELINK_API_KEY" | nuelink-cli auth:login --stdinnuelink-cli auth:statusnuelink-cli auth:validatenuelink-cli auth:clear| Command | Flags | What it does |
|---|---|---|
auth:login | --api-key <apiKey>, --stdin | Saves 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.
Profile
Section titled “Profile”nuelink-cli meBrands
Section titled “Brands”nuelink-cli brands --per-page 5 --page 1nuelink-cli --json brands --per-page 5 --page 1| Flag | Default |
|---|---|
--per-page <number> | 25 |
--page <number> | 1 |
Pagination accepts --page of 1 or more and --per-page from 1 through 100.
Schedule
Section titled “Schedule”nuelink-cli schedule --brand-id 13493Requires --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.
Channels
Section titled “Channels”nuelink-cli channels --brand-id 13493 --per-page 5 --page 1Requires --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.
Collections
Section titled “Collections”List:
nuelink-cli collections --brand-id 13493 --per-page 5 --page 1Each collection comes back with its status, evergreen setting, channels, queue slots, and post counts.
Create:
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| Flag | Required | Notes |
|---|---|---|
--brand-id <id> | Yes | |
--title <title> | Yes | Max 255 characters |
--description <text> | No | Defaults to empty |
--max-republish <number> | No | Evergreen: 0 turns it off, N re-adds published posts after N weeks |
--channels <ids> | No | Comma-separated channel IDs |
--queues <slots> | No | Comma-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:
nuelink-cli --dry-run collections:create \ --brand-id 13493 \ --title "Draft Review" \ --description "Draft-only integration collection" \ --max-republish 0 \ --channels "" \ --queues ""Automations
Section titled “Automations”List:
nuelink-cli automations --brand-id 13493 --per-page 5 --page 1Create:
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| Flag | Required | Allowed values / default |
|---|---|---|
--brand-id <id> | Yes | |
--collection-id <id> | Yes | |
--feed-url <url> | Yes | |
--import-as-type <type> | Yes | LINK, IMAGE, VIDEO, CAROUSEL |
--name <name> | Yes | The automation’s name, max 255 characters |
--type <type> | Yes | The feed source: RSS, YOUTUBE, SHOPIFY, and the rest of the 27 values under POST /automations |
--title <text> | No | Title template for imported posts. Defaults to {{title}} |
--caption <text> | No | Caption template for imported posts, max 255 characters |
--load-old-posts <boolean> | No | The API defaults to false |
--add-posts-as-draft <boolean> | No | The API defaults to false |
--refresh-rate <hours> | No | 24, 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:
nuelink-cli media --brand-id 13493 --type IMAGE --per-page 5 --page 1Media type filters are IMAGE, VIDEO, GIF, APPLICATION, DOCUMENT, and CSV.
Upload:
nuelink-cli media:upload --brand-id 13493 --file ./assets/image.jpg --dry-run| Flag | Required |
|---|---|
--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.
List a collection’s posts
Section titled “List a collection’s posts”nuelink-cli posts --brand-id 13493 --collection-id 33273 --per-page 5 --page 1nuelink-cli posts --brand-id 13493 --collection-id 33273 \ --status DRAFT --sort-by updated_at --sort-order descOutput highlights post ID, type, status, and publish date.
| Flag | Required | Allowed values / notes |
|---|---|---|
--brand-id <id> | Yes | |
--collection-id <id> | Yes | |
--status <status> | No | PENDING, DRAFT, PUBLISHED |
--post-type <type> | No | TEXT, IMAGE, VIDEO, MULTIMEDIA, SHORT, DOCUMENT, THREAD, SLIDES, REEL, TIKTOK, STORY, POLL, ENGAGE_COMMENT |
--posting-type <type> | No | NOW, SCHEDULE, QUEUE, DRAFT |
--created-from <datetime> | No | UTC, YYYY-MM-DD HH:mm:ss |
--created-to <datetime> | No | UTC, YYYY-MM-DD HH:mm:ss. Can’t be earlier than --created-from |
--sort-by <field> | No | id (the API’s default), title, post_date, created_at, updated_at |
--sort-order <order> | No | desc (the API’s default), asc |
--per-page <number>, --page <number> | No | 25 and 1 |
posts has no --view flag. For the view shortcut on one collection, use brand-posts with --collection-id.
List posts across a brand
Section titled “List posts across a brand”nuelink-cli brand-posts --brand-id 13493 --view SCHEDULED --sort-by post_date --sort-order ascnuelink-cli brand-posts --brand-id 13493 --collection-id 33273 --view DRAFTTakes 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.
Create from a JSON payload
Section titled “Create from a JSON payload”Best for anything complex, and for payloads you keep in version control:
nuelink-cli posts:add-json \ --brand-id 13493 \ --collection-id 33273 \ --payload ./post.json \ --dry-runPass --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.
Create from flags
Section titled “Create from flags”Best for one-liners and simple posts:
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-runCore flags
| Flag | Required | Notes |
|---|---|---|
--brand-id <id> | Yes | |
--collection-id <id> | Yes | |
--caption <text> | Yes, unless a poll is set | 1–3000 characters |
--publish-mode <mode> | No | DRAFT (default), QUEUE, SCHEDULE, IMMEDIATE |
--scheduled-at <value> | Only with SCHEDULE | YYYY-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> | No | Used by YouTube, Pinterest |
--alt <text> | No | Accessibility alt text |
--link <url> | No | Google Business Profile LEARN_MORE button and Pinterest destination link |
--post-as-story <boolean> | No | Instagram and Facebook only |
--auto-thread-text <boolean> | No | Nuelink’s automatic text splitting. See Threads |
--media-ids <ids> | No | Comma-separated, from media:upload |
--media-urls <urls> | No | Comma-separated public URLs |
--comment <text> | No | Auto-comment, 1–1000 characters |
--comment-delay <seconds> | With --comment | The 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.
Update a post
Section titled “Update a post”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.
nuelink-cli posts:update --brand-id 13493 --post-id 4364709 \ --publish-mode SCHEDULE --scheduled-at "2026-10-12 09:00:00" --dry-runnuelink-cli posts:update --brand-id 13493 --post-id 4364709 \ --queue-position FRONT --dry-runnuelink-cli posts:update --brand-id 13493 --post-id 4364709 \ --publish-mode DRAFT --dry-run| Flag | Required | Allowed values / notes |
|---|---|---|
--brand-id <id> | Yes | |
--post-id <id> | Yes | |
--publish-mode <mode> | Unless --queue-position is set | DRAFT, SCHEDULE, QUEUE. No IMMEDIATE |
--queue-position <position> | Unless --publish-mode is set | FRONT, BACK. Can be combined with --publish-mode QUEUE |
--scheduled-at <value> | Only with SCHEDULE | YYYY-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.
Delete a post
Section titled “Delete a post”nuelink-cli posts:delete --brand-id 13493 --post-id 4364709 --dry-run| Flag | Required |
|---|---|
--brand-id <id> | Yes |
--post-id <id> | Yes |
Published results
Section titled “Published results”nuelink-cli published-posts --brand-id 13493nuelink-cli published-posts --brand-id 13493 --status FAILEDnuelink-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:
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_deliverycheck_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.
| Flag | Default | Allowed values / notes |
|---|---|---|
--brand-id <id> | Required | |
--status <status> | PUBLISHED | PUBLISHED, 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> | id | id, created_at, likes, comments, shares |
--sort-order <order> | desc | asc, desc |
--per-page <number>, --page <number> | 25, 1 |
The safety model
Section titled “The safety model”The CLI is built to make the careful path the easy one. In order:
- Verify the connected account with
auth:status,auth:validate, andme. - Resolve brand, collection, and post IDs from list commands, never hardcode them from an example.
- Preview every API mutation with
--dry-run. - Default unclear publishing intent to
DRAFT. - Ask before creating a collection or automation, uploading, queueing, scheduling, publishing, or updating a post.
- Confirm
posts:deleteseparately, against the exact brand and post ID, immediately before running it. - Repeat the exact previewed command after confirmation, not an abbreviated one that could change the payload.
- 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.
Production patterns
Section titled “Production patterns”Shell script
Section titled “Shell script”#!/usr/bin/env bashset -euo pipefail
export NUELINK_API_KEY="${NUELINK_API_KEY:?NUELINK_API_KEY is required}"
nuelink-cli brands --per-page 10 --page 1nuelink-cli collections --brand-id 13493 --per-page 10 --page 1Piping JSON into jq
Section titled “Piping JSON into jq”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)"'Find failed deliveries
Section titled “Find failed deliveries”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-runTroubleshooting
Section titled “Troubleshooting”Invalid token
- Run
nuelink-cli auth:statusto confirm which source is active, thennuelink-cli auth:validateto 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_KEYisn’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
NUELINK_TIMEOUT_MS=120000 nuelink-cli media --brand-id 13493NUELINK_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
nuelink-cli --helpnuelink-cli help collectionsSecurity notes
Section titled “Security notes”- Treat API keys as secrets.
- Never commit a token to source control.
- Prefer environment-based secrets in CI/CD.
- Use
--stdinwithauth:loginso the key never enters your shell history.
The CLI is MIT licensed. For support, email support@nuelink.com.