Command line
The rhino-relay command calls the API for you. It takes the same JSON bodies, prints the JSON response, and exits with a code that says what happened. Its commands are generated from the same OpenAPI document as the API reference, so every endpoint you call with an API key has one.
Install
Section titled “Install”You need Node.js 20 or later.
npm install -g rhino-relay-clirhino-relay --versionSave your API key
Section titled “Save your API key”Create a key on the console’s API keys page, or with the API (see Authentication). Then save it:
rhino-relay configure --api-key ek_...This writes config.json, readable only by you, to $XDG_CONFIG_HOME/rhino-relay/ (or ~/.config/rhino-relay/), or to %APPDATA%\rhino-relay\ on Windows. To keep the key out of your shell history, pass --api-key - and type or pipe the key on stdin.
In CI, set environment variables instead. They win over the file:
export RHINO_RELAY_API_KEY="ek_..."export RHINO_RELAY_BASE_URL="https://api.rhinorelay.com/v1"rhino-relay health getThe base URL defaults to https://api.rhinorelay.com/v1, for accounts in us-east-2. For another Region, run rhino-relay configure --region <region> or set RHINO_RELAY_REGION, which use that Region’s host from the API’s OpenAPI document (see Regions and limits). For your custom API domain, use rhino-relay configure --base-url or RHINO_RELAY_BASE_URL, which win over the Region. RHINO_RELAY_CONFIG points the command at another config file.
Check it worked: rhino-relay health get prints your account’s health as JSON and exits 0.
How commands work
Section titled “How commands work”Every command is rhino-relay <group> <action>, then the resource ID when the endpoint has one, then flags:
- Query parameters are flags:
--limit 50,--status subscribed,--by stage. --data(or-d) is the request body: JSON text,@file.jsonto read a file, or-to read stdin.--all, on list commands, followsnextCursorand prints every page as one{"data": [...]}. See Pagination.--idempotency-keysets theIdempotency-Keyheader. Commands whose endpoint needs one, such ascampaigns schedule, make a new key when you don’t give one. Give your own to retry safely; see Idempotency.--if-match <version>sendsIf-Match, so an update fails with412if someone changed the resource since you read it.--compactprints the JSON on one line.
rhino-relay help lists the groups, rhino-relay contacts help lists a group’s actions, and rhino-relay contacts list --help lists an action’s flags.
Examples
Section titled “Examples”Contacts
Section titled “Contacts”rhino-relay contacts create --data '{"email":"ada@example.com","consent":{"source":"signup-form","at":"2026-10-01T09:00:00Z","method":"single_opt_in"}}'rhino-relay contacts lookup --data '{"email":"ada@example.com"}'rhino-relay contacts list --status subscribed --limit 50rhino-relay contacts list --tag customer --allrhino-relay contacts get ct_...rhino-relay contacts update ct_... --if-match 3 --data '{"addTags":["vip"],"fields":{"firstName":"Ada"}}'rhino-relay contacts batch --data @contacts.jsonrhino-relay contacts delete ct_...Templates
Section titled “Templates”rhino-relay templates create --data @template.jsonrhino-relay templates listrhino-relay templates create-version tp_... --data @version.jsonrhino-relay templates render tp_... --data '{"version":2}'Campaigns
Section titled “Campaigns”rhino-relay campaigns create --data '{"name":"October news","templateId":"tp_...","templateVersion":1,"segment":{},"stagePlan":{"type":"preset","name":"single"}}'rhino-relay campaigns test cm_... --data '{"to":["me@example.com"]}'rhino-relay campaigns schedule cm_...rhino-relay campaigns schedule cm_... --idempotency-key october-news-1rhino-relay campaigns report cm_... --by stagerhino-relay campaigns recipients cm_... --state failed --allrhino-relay campaigns pause cm_...Imports and exports
Section titled “Imports and exports”rhino-relay imports create-csv --data '{}'rhino-relay imports list --state dry_run_readyrhino-relay imports get im_...rhino-relay imports commit im_... --idempotency-key csv-commit-1rhino-relay exports create --data '{"kind":"contacts"}'rhino-relay exports list --kind contactsrhino-relay exports get ex_...See CSV import for uploading the file, and Erasure and export for what each export holds.
Keys, settings and setup
Section titled “Keys, settings and setup”rhino-relay keys create --data '{"name":"ci","scopes":["contacts:write"]}'rhino-relay keys listrhino-relay keys revoke key_...rhino-relay settings getrhino-relay settings update --data '{"footerAddress":"1 Main St, Springfield"}'rhino-relay health getrhino-relay domains getrhino-relay domains set --data '{"trackingDomain":"track.example.com"}'keys create prints the new key’s secret once. Store it then.
Waiting for work to finish
Section titled “Waiting for work to finish”Imports, exports, campaigns and setup take a while. --wait <condition> polls until the condition holds, then prints the final response. On a command that starts the work, it follows the response to the resource and polls that. rhino-relay <group> <action> --help lists the conditions a command can wait for, and --timeout <seconds> stops sooner. Each change of state goes to stderr as it happens.
rhino-relay exports create --data '{"kind":"contacts"}' --wait readyrhino-relay imports commit im_... --wait completedrhino-relay campaigns schedule cm_... --wait completed --timeout 3600rhino-relay health get --wait healthyThe conditions come from the API’s OpenAPI document, the same ones a generated client library offers.
Output and exit codes
Section titled “Output and exit codes”A successful response goes to stdout as JSON, indented unless you pass --compact. A 204 response, such as a delete, prints nothing. An error response goes to stderr as the API’s problem JSON, with its code (see Errors). The command never prints your API key.
| Exit code | Meaning |
|---|---|
0 |
Success (2xx) |
1 |
The API refused the request (4xx): the problem JSON is on stderr |
2 |
The command was wrong: an unknown command or flag, a missing ID, --data that isn’t JSON, or no API key |
3 |
The API failed (5xx), or it couldn’t be reached |
4 |
--wait stopped without its condition: the resource reached a failure state (the response is on stdout), or the wait timed out |
In a script, stdout is safe to parse whenever the exit code is 0.
Every command
Section titled “Every command”| Command | What it does |
|---|---|
rhino-relay contacts list | List contacts (GET /contacts) |
rhino-relay contacts create | Create or update a contact (POST /contacts) |
rhino-relay contacts batch | Create or update up to 1,000 contacts (POST /contacts/batch) |
rhino-relay contacts lookup | Look up a contact by email (POST /contacts/lookup) |
rhino-relay contacts get <id> | Get a contact (GET /contacts/{id}) |
rhino-relay contacts update <id> | Update a contact's fields, tags or email (PATCH /contacts/{id}) |
rhino-relay contacts delete <id> | Erase a contact (DELETE /contacts/{id}) |
rhino-relay contacts unsubscribe <id> | Unsubscribe a contact (POST /contacts/{id}/unsubscribe) |
rhino-relay contacts resubscribe <id> | Resubscribe an unsubscribed contact with new consent (POST /contacts/{id}/resubscribe) |
rhino-relay segments list | List segments (GET /segments) |
rhino-relay segments create | Create a segment (POST /segments) |
rhino-relay segments get <id> | Get a segment (GET /segments/{id}) |
rhino-relay segments update <id> | Update a segment (PATCH /segments/{id}) |
rhino-relay segments delete <id> | Delete a segment (DELETE /segments/{id}) |
rhino-relay segments count <id> | Count a saved segment's sendable contacts (GET /segments/{id}/count) |
rhino-relay segments count-definition | Count sendable contacts matching a definition (POST /segments/count) |
rhino-relay templates list | List templates (GET /templates) |
rhino-relay templates create | Create a template with its first version (POST /templates) |
rhino-relay templates versions <id> | List a template's versions (GET /templates/{id}/versions) |
rhino-relay templates create-version <id> | Add a template version (POST /templates/{id}/versions) |
rhino-relay templates get <id> | Get a template and its newest 20 versions (GET /templates/{id}) |
rhino-relay templates render <id> | Render a preview (POST /templates/{id}/render) |
rhino-relay campaigns list | List campaigns (GET /campaigns) |
rhino-relay campaigns create | Create a draft campaign (POST /campaigns) |
rhino-relay campaigns get <id> | Get a campaign (GET /campaigns/{id}) |
rhino-relay campaigns update <id> | Update a draft campaign (PATCH /campaigns/{id}) |
rhino-relay campaigns delete <id> | Delete a draft campaign (DELETE /campaigns/{id}) |
rhino-relay campaigns test <id> | Send test emails (POST /campaigns/{id}/test) |
rhino-relay campaigns schedule <id> | Schedule a campaign (POST /campaigns/{id}/schedule) |
rhino-relay campaigns pause <id> | Pause a sending campaign (POST /campaigns/{id}/pause) |
rhino-relay campaigns resume <id> | Resume a paused campaign (POST /campaigns/{id}/resume) |
rhino-relay campaigns cancel <id> | Cancel a campaign (POST /campaigns/{id}/cancel) |
rhino-relay campaigns report <id> | Get a campaign report (GET /campaigns/{id}/report) |
rhino-relay campaigns recipients <id> | List a campaign's send records (GET /campaigns/{id}/recipients) |
rhino-relay imports create-csv | Start a CSV import (POST /imports/csv) |
rhino-relay imports create-mailchimp | Start a Mailchimp import (POST /imports/mailchimp) |
rhino-relay imports list | List imports (GET /imports) |
rhino-relay imports get <id> | Get an import and its dry-run summary (GET /imports/{id}) |
rhino-relay imports delete <id> | Cancel an uncommitted import (DELETE /imports/{id}) |
rhino-relay imports commit <id> | Commit a dry-run import (POST /imports/{id}/commit) |
rhino-relay exports list | List exports (GET /exports) |
rhino-relay exports create | Start an export (POST /exports) |
rhino-relay exports get <id> | Get an export and its download link (GET /exports/{id}) |
rhino-relay health get | Check setup and sending health (GET /health) |
rhino-relay setup verify | Connect the installed template (POST /setup/verify) |
rhino-relay domains get | Get domain and certificate status (GET /setup/domains) |
rhino-relay domains set | Set the tracking domain and an optional custom API domain (POST /setup/domains) |
rhino-relay settings get | Get settings (GET /settings) |
rhino-relay settings update | Update settings (PATCH /settings) |
rhino-relay keys list | List API keys (GET /keys) |
rhino-relay keys create | Create an API key (POST /keys) |
rhino-relay keys revoke <id> | Revoke an API key (DELETE /keys/{id}) |