Configuration and scripting
Global flags
These work with every command, before or after the command name.
| Flag | Description |
|---|---|
--profile <name> | Credential profile to use. Default: the current profile |
--url <url> | Override the install URL for this command. Default https://dashboard.skry.be |
--output <format> | table (default), json or text |
--json | Shorthand for --output json |
-v, --version | Print the CLI version |
-h, --help | Help for any command, e.g. skrybe campaigns send --help |
There is deliberately no --api-key flag. See
Authentication for how the key is supplied.
Config file
Profiles live in ~/.skrybe/config.json, or $XDG_CONFIG_HOME/skrybe/config.json,
or wherever SKRYBE_CONFIG points. The file and its format are described under
Where credentials are stored.
It holds only profiles; there are no other settings in it.
Environment variables
| Variable | Description |
|---|---|
SKRYBE_API_KEY | API key; overrides any profile |
SKRYBE_API_URL | Install URL; overrides any profile |
SKRYBE_PROFILE | Profile to use when --profile is not given |
SKRYBE_CONFIG | Path of the config file |
SKRYBE_OUTPUT | Default output format: table, json or text |
XDG_CONFIG_HOME | Config file goes in $XDG_CONFIG_HOME/skrybe/ |
NO_COLOR | Any value turns colour off |
Colour is also off when stdout is not a terminal or TERM=dumb.
Output formats
--output picks the shape. Data goes to stdout and messages go to
stderr, so pipes and redirects stay clean.
| Format | For |
|---|---|
table | Reading. Aligned columns with a header. The default |
json | jq. The full record, with numbers and nulls intact |
text | cut, awk, while read. Tab-separated, no header, no padding |
The format is chosen in this order: --json, then --output, then
SKRYBE_OUTPUT, then table. An unknown value is an error (exit code 2)
rather than a silent fallback.
skrybe lists --output json | jq -r '.[].id'
skrybe lists --output text | cut -f2
How each kind of command uses the format:
- Collections (
skrybe lists,skrybe campaigns):jsonprints an array;textprints one tab-separated line per row, in the same columns as the table. - Single records (
campaigns get,lists get):jsonprints the object;textprintskey<TAB>valuelines, with nested keys dotted (subscribers.active) and arrays of objects indexed (links.0.url). - Single values (
lists count,subscribers status): undertableandtextthe bare value, so$(skrybe lists count ID)captures a number; underjsonan object. - Actions (
subscribers add,campaigns send): undertablea sentence goes to stderr and stdout stays empty. Underjsonandtextthe result record goes to stdout.
$ skrybe subscribers add ada@example.com --list l7763lkC1Ld892Ty4p3pvxw --output json
{
"email": "ada@example.com",
"list_id": "l7763lkC1Ld892Ty4p3pvxw",
"outcome": "subscribed"
}
A few commands that create something (lists create, campaigns duplicate,
templates create) print the new ID to stdout even under table, so a script
can capture it:
LIST=$(skrybe lists create 'Beta testers')
Empty results
An empty collection is a success: the command exits 0, --output json
prints [], text prints nothing, and table prints a note to stderr.
Errors
Errors always go to stderr. Under table and text they read:
Error [list_not_found]: List does not exist
Run `skrybe lists` to see the IDs available to this API key.
The code in brackets is a stable slug a script can match on. The second line, when present, is a hint.
Under --output json the error is JSON too, so a script parses one shape on
success and failure:
$ skrybe subscribers status nobody@example.com --list bogus --output json
{
"error": {
"code": "list_id_invalid",
"message": "List ID not passed",
"hint": "A list ID is the encrypted value `skrybe lists` prints, not the integer in the UI URL."
}
}
hint is null when there is none.
Exit codes
| Code | Meaning |
|---|---|
0 | Success, including an empty result |
1 | API error: the request reached Skrybe and failed, or the server could not be reached |
2 | Usage error: bad flag or argument, missing configuration, refused without --yes, or a command that is not available yet |
3 | Authentication failure: missing or invalid API key |
4 | Quota exceeded or rate limited |
--help and --version exit 0.
skrybe campaigns send 42 --list "$LIST" --yes
case $? in
0) echo sent ;;
4) echo "over quota" ;;
*) echo failed ;;
esac
Confirmation prompts
Commands that send, schedule or delete ask first, defaulting to no:
Send campaign 42 to 1,204 recipients? [y/N]
The prompt is written to stderr. When stdin is not a terminal (a script, CI,
a pipe) there is no one to answer, so these commands refuse with exit code 2
unless you pass --yes (-y):
| Command | Asks before |
|---|---|
lists delete | deleting the list and its subscribers |
lists fields delete | deleting the field and its values |
campaigns send | sending |
campaigns schedule | scheduling |
campaigns delete | deleting |
templates delete | deleting |
campaigns send and campaigns schedule also take --dry-run, which checks
everything and counts the recipients without changing anything.
Passing values from files
Any flag that takes a message body accepts file:// followed by a path,
the same convention as the AWS CLI:
skrybe templates create 'Welcome' --html-text file://welcome.html
skrybe campaigns update 42 --html-text file:///home/ada/emails/q4.html
The path after file:// is resolved against the current directory, so both
file://body.html and file:///absolute/path.html work. A value without the
prefix is used as it is. A missing or unreadable file is a usage error (exit
code 2).
The flags that accept file://:
| Command | Flags |
|---|---|
campaigns create, campaigns update | --html-text, --plain-text |
templates create, templates update | --html-text, --plain-text |
emails send | --html-text, --plain-text, --recipient-variables |
emails send-transactional | --html-body, --plain-body |
Reading from stdin
Two commands read secrets from stdin, so they never appear in ps or shell
history:
skrybe auth loginreads the API key (prompted at a terminal, or piped in).skrybe brands transport ... --password-stdinreads the relay password.
No other command reads stdin, apart from the yes/no confirmation prompts.
Pagination
Commands that page through large results share three flags:
| Flag | Description |
|---|---|
--page <n> | Page to show. Default 1 |
--limit <n> | Rows per page |
--all | Fetch every page and print them together |
--all cannot be combined with --page or --limit.
| Command | Default --limit | Maximum |
|---|---|---|
campaigns / campaigns ls | 10 | 100 |
subscribers ls | 100 | 1000 |
campaigns activity | 100 | 1000 |
Under table, when there are more rows than shown, a line such as
Showing 1-100 of 4,210. Use --page, or --all. goes to stderr
(subscribers ls and campaigns activity).
Retries and timeouts
Each request times out after 30 seconds. Read-only requests are retried up to
3 times on HTTP 429, 502, 503 and 504 or a dropped connection, honouring
Retry-After. Requests that create, send or change something are never
retried, because the API has no idempotency keys and a retry could send twice.
Pipes
skrybe lists | head -3 stops quietly with exit code 0 when head closes
the pipe, so set -o pipefail scripts are not tripped by it.
Shell completion
eval "$(skrybe completion bash)" # add to ~/.bashrc
eval "$(skrybe completion zsh)" # add to ~/.zshrc
skrybe completion fish > ~/.config/fish/completions/skrybe.fish
See completion.