@terminal49/cli is a command-line interface for Terminal49 APIs, designed for humans and automation agents.
Use npm:
npm install -g @terminal49/cliOr run from source:
cd sdks/typescript-sdk-cli
npm run dev -- --helpUse one of:
T49_API_TOKENenvironment variablet49 config set token <token>--token <token>
Prefer the environment variable or config file for tokens. --token is useful for one-off calls, but command-line arguments can be visible to other local processes through tools such as ps.
Token handling follows the SDK:
- A raw token is sent as
Authorization: Token <token>. - A value already prefixed with
TokenorBeareris sent unchanged. - For user-scoped bearer tokens, pass
--account-id <id>, setT49_ACCOUNT_ID, or setaccountIdin config. The SDK sends this asx-account-id.
Config lives at the path printed by:
t49 config pathConfig subcommands:
t49 config patht49 config get <key> [--reveal]t49 config set <key> <value>t49 config list [--reveal]t49 config cleart49 config auth-statust49 config client-check
Allowed config keys are: token, baseUrl, defaultFormat, maxRetries, accountId, timeoutMs.
Environment variables:
T49_API_TOKENT49_API_BASE_URLT49_ACCOUNT_ID
Resolution precedence:
- Token:
--token>T49_API_TOKEN> configtoken - Base URL:
--base-url>T49_API_BASE_URL> configbaseUrl - Account ID:
--account-id>T49_ACCOUNT_ID> configaccountId - Response format:
--format> configdefaultFormat>mapped - Max retries:
--max-retries> configmaxRetries> SDK default - Timeout:
--timeout> configtimeoutMs> SDK default
--jsonemits JSON envelopes.--tableforces table output.--compactminifies JSON output.--fields <fields>projects comma-separated fields. JSON output keeps only those fields, except that when the output has anitemsarray, ascommandsdoes, it projects each entry initemsand keeps the other top-level keys. A table shows one column per field path, named by the path as you typed it. When the paths share an array, such asports.nameoncontainers routeordata.idon a--format rawlist, the table prints one row per element.--format <raw|mapped|both>selects SDK response format.--token <token>overrides auth config.--base-url <url>overrides the API base URL.--account-id <id>sends an account id for user-scoped bearer tokens.--timeout <ms>sets request timeout in milliseconds.--max-retries <n>sets retry attempts for 429/5xx responses.0disables retries.
--json and --table are mutually exclusive. Without either flag, the CLI prints tables to a TTY and JSON to pipes.
Every command, including config and commands, honors --json, --table, --compact, and --fields. The one exception is --all, which always prints whole rows as newline-delimited JSON and ignores --table, --compact, --fields, and --format.
--quiet, --verbose, and --no-color were removed.
Paginated list commands (containers, shipments, tracking-requests, parties, webhooks, and webhook-notifications) support:
--page <n>selects a page for normal list output.--page-size <n>selects page size for normal list output and--all.
containers list, shipments list, tracking-requests list, and parties list also support:
--allemits all pages as newline-delimited JSON, one item per line, without the normal JSON envelope.--max-pages <n>limits pages fetched by--all.--max-rows <n>limits rows emitted by--all.
The API returns shipping-lines, custom-fields, custom-field-definitions, and custom-field-options lists whole, so those commands have no paging flags.
Success output in JSON mode:
{
"ok": true,
"command": "shipments.get",
"data": {},
"pagination": {},
"meta": {}
}pagination and meta are omitted when unavailable.
data holds the SDK result in the selected --format. With the default mapped format, containers get, vessels get, vessels get-by-imo, vessels future-positions, and vessels future-positions-coords print the SDK's mapped model with camelCase fields, such as data.number or data.positions. Earlier releases printed the JSON:API document, such as data.data.attributes.number. Pass --format raw to get the JSON:API document. containers route prints a ContainerRoute (ports, currentVessel, totalPorts) instead of a Route, and --format raw prints the GeoJSON FeatureCollection from GET /containers/{id}/map_geojson.
Every list command shapes data the same way:
--format mappedprints the array of mapped rows.--format rawprints the whole JSON:API document, includingincluded,links, andmeta. Project its rows with paths such as--fields data.id.--format bothprints{ "raw": <document>, "mapped": <rows> }.
List commands also copy the document's links and meta into pagination.
Error output:
{
"ok": false,
"error": {
"code": "AUTH_ERROR",
"message": "Missing authentication token.",
"details": {},
"retryable": true,
"retryAfterMs": 1000
}
}details, retryable, and retryAfterMs are omitted when unavailable. With --json, or when stderr is not a terminal, stderr carries only this JSON. Otherwise a terminal gets one CODE: message line, and usage errors add a (run --help for usage) line. A bad value for a global option (--format, --max-retries, --timeout, and so on) that comes before --json is reported before --json is read, so a terminal gets the line form. Rate-limit errors use retryable: true and include retryAfterMs when exposed by the SDK.
0success1general or unknown error2usage or argument error3authentication or authorization error4rate limited5not found6validation error7reserved8upstream/server error9network/connection error
Stable error codes include USAGE_ERROR, NETWORK_ERROR, AUTH_ERROR, RATE_LIMITED, VALIDATION_ERROR, NOT_FOUND, UPSTREAM_ERROR, and INTERNAL_ERROR.
t49 shipments get|list|update|stop-tracking|resume-tracking|custom-fields|set-custom-fieldshipments update <id> --attr key=value...splits each pair on its first=. A value that starts with[,{, or"is parsed as JSON, and so aretrue,false, andnullon keys other thanref_numbersandtags. Other values stay strings.ref_numbersandtagsare arrays of strings: a plain value, includingtrue,false, ornull, adds one string element, and repeated pairs add more, as in--attr tags=urgent --attr tags=hazmator--attr 'ref_numbers=["PO-1","PO-2"]'. A JSON value for them must be a string or an array of strings, and--attr tags=[]sends an empty array. An--attrkey replaces the same key from--payload.
t49 containers get|list|events|route|map|raw-events|refresh|demurrage|rail|custom-fields|set-custom-field(mapis deprecated: with--jsonit prints the raw GeoJSON FeatureCollection, the same asroute --format raw --json. In table mode both print only the collection type. In table mode,routeprints the ports and a Current vessel table.)t49 shipments set-custom-field <id> <api-slug> --value <json>andt49 containers set-custom-field <id> <api-slug> --value <json>take the custom field definition'sapi_slug, such aspriority. A definition id also works: the CLI looks up itsapi_slugfirst.t49 tracking-requests list|get|create|update|infer|create-from-infert49 track <number>t49 shipping-lines list [--search <text>]. The API has no search parameter, so--searchfetches every line and keeps those whose SCAC, alternative SCACs, name, or short name contain the text, ignoring case.t49 webhooks list|get|create|update|delete|ipst49 webhook-notifications list|get|examples.examplesrequires--event <event>, a webhook event name such ascontainer.transport.vessel_arrived.t49 vessels get|get-by-imo|future-positions|future-positions-coords.future-positionsrequires--port-idand--previous-port-id.future-positions-coordsalso requires--latitudeand--longitude. In table mode both print the vessel name and IMO, then one row per estimated position.t49 ports gett49 terminals gett49 parties list|gett49 metro-areas gett49 custom-fields list|get|create|update|deletet49 custom-field-definitions list|get|create|update|delete.listprints your account definitions, andlist --templatesprints the Terminal49 templates you can copy withtemplate_id.t49 custom-field-options list|get|create|update|deletet49 search <query>t49 config path|get|set|list|clear|auth-status|client-checkt49 commands
To validate output formatting against real read-only API payloads:
cd sdks/typescript-sdk-cli
export T49_API_TOKEN=Token YOUR_API_KEY
npm run fixtures:capture:live
npm testThis captures JSON and table fixtures under:
test/fixtures/api/live/test/fixtures/table/live/
Error fixtures are also captured for known read-only failure modes so output behavior remains test-covered.