Skip to main content Skip to guide navigation

Integrations

CLI command and output reference

Last reviewed

This reference describes the command contract and schemaVersion: 1 output of Portrait CLI 0.1.9. Check portrait --version and portrait --help against your installed package before building an integration. Follow Install and use the CLI for setup and account requirements.

Commands and effects

Command Effect
auth login, auth status, auth logout Connect, check sign-in state, or remove this installation's connection
sessions list, sessions history Read accessible conversations or a retained transcript
sessions create Create an empty session; --resume resumes the specified conversation
ask Send a new research turn; it can propose or execute supported work subject to its confirmation and usage rules
files upload Save one explicitly selected regular file to My Library
files status, files read Check processing or read extracted LLM text
files delete Permanently remove the specified known document, without an interactive confirmation
monitors list, monitors get Read one page; get --mark-read also changes the read state of that returned content
install-skill Write the bundled agent skill locally; --force replaces its existing portrait directory recursively

Authentication alone does not grant Easel access. ask and sessions require both full Easel access and the provisioned CLI bridge. File and direct Monitor operations have their own access checks. A read-like command name or a successful login does not authorize a later write.

ask accepts exactly one non-empty prompt argument. Place --session, --document, and --stream before it. Use shell-safe quoting; currency written as USD 10 billion avoids shell expansion of a dollar-prefixed number.

portrait ask --stream 'Compare the latest earnings commentary for AAPL and MSFT.'

JSON envelopes

With non-interactive stdout, a non-streaming operational command writes one JSON envelope. A successful command exits with status 0; a reported CLI failure exits with status 1. Help and version remain plain text. Interactive authentication and non-streaming interactive errors use concise text; login instructions and diagnostics go to stderr. Keep stderr separate from a JSON parser.

Illustrative ask success; the UUID and answer below are placeholders:

{"schemaVersion":1,"ok":true,"command":"ask","data":{"session_id":"00000000-0000-4000-8000-000000000001","finalText":"Illustrative answer."}}

Illustrative failure before a selected document is ready:

{"schemaVersion":1,"ok":false,"command":"ask","error":{"code":"DOCUMENT_NOT_READY","message":"Document is not ready.","httpStatus":null,"retryable":false}}

command identifies the operation, for example ask, files.status, or sessions.list. Require a supported schemaVersion, inspect ok, and read the command-specific data. Do not assume all data uses the same identifier casing. Error fields are code, message, nullable httpStatus, and boolean retryable. This schema does not include a serialized retry-delay field.

Authentication errors

Authentication uses the operating system keyring. Keep stdout and stderr separate: the browser verification instructions are not JSON response data.

Error Meaning and recovery
DEVICE_CODE_EXPIRED Restart auth login and use the newly printed code
AUTHORIZATION_DENIED Authorization was declined; do not retry automatically
AUTH_NETWORK_ERROR The authorization service could not be reached or its response could not be read; check connectivity before retrying login
AUTH_LOGIN_IN_PROGRESS, AUTH_MUTATION_IN_PROGRESS Another authentication change holds the local lock; finish that operation first
KEYRING_TIMEOUT, KEYRING_BACKEND_ERROR, KEYRING_HELPER_ERROR Credential-store access failed; repair the OS keyring or execution environment
KEYRING_WRITE_UNCONFIRMED, KEYRING_STATE_UNKNOWN Stored sign-in state is uncertain; restore keyring access and sign in again
KEYRING_DELETE_UNCONFIRMED Local credential removal was not confirmed; do not report successful local logout
LOGOUT_INCOMPLETE Read the message's separate remote-revocation and local-removal results; neither implies the other succeeded

Successful logout data includes environment, revocationAttempted, revocationSucceeded, and localRemovalSucceeded. With no stored credential, no revocation may be needed. On failure, these outcomes are described in the error message rather than a success data object.

If local removal succeeded but remote revocation failed, running logout again cannot revoke a token that is no longer stored. Contact support if remote revocation must be confirmed. Do not collect or transmit the token to diagnose this condition.

Streaming completion

ask --stream writes NDJSON on stdout. Each line is one independent JSON object; there is no enclosing array and no ok field on these stream records.

{"schemaVersion":1,"type":"answer.delta","command":"ask","data":{"session_id":"00000000-0000-4000-8000-000000000001","delta":"Illustrative "}}
{"schemaVersion":1,"type":"answer.final","command":"ask","data":{"session_id":"00000000-0000-4000-8000-000000000001","finalText":"Illustrative answer."}}
{"schemaVersion":1,"type":"operation.complete","command":"ask","data":{"session_id":"00000000-0000-4000-8000-000000000001"}}

Use deltas for progress display only. The authoritative complete answer is answer.final.data.finalText; do not reconstruct it by joining deltas. Retain the returned session_id and require operation.complete plus a successful process exit before reporting transport completion. A completed transport still does not establish that the research answered the question correctly or that every proposed action ran.

A streaming failure is an operation.error record with the error at the top level:

{"schemaVersion":1,"type":"operation.error","command":"ask","error":{"code":"DOCUMENT_NOT_READY","message":"Document is not ready.","httpStatus":null,"retryable":false}}

EOF, a process interruption, or some received deltas without a successful terminal sequence leave the outcome uncertain. Preserve partial text as partial; do not label it a completed answer. Handle unknown fields without discarding known data, and stop for review if the schema version is unsupported.

Identifiers, readiness, and pagination

All paths in this table are within the JSON envelope. Each read makes one page or chunk request; the CLI does not automatically exhaust the result.

Operation Identifier or state to retain Continue / finish
sessions list --limit N IDs in data.sessions; data.next_page_cursor N is 1–50. Pass a non-empty returned cursor unchanged to --cursor; null or absent means no next page
sessions create, ask data.session_id Reuse the returned ID. Resume preserves the durable identity; an already-active resume can return data.already_active=true
files upload data.document_id Upload accepts one regular file, at most 26,214,400 bytes (25 MiB); it does not wait for extracted text to finish
files status data.documentId, data.representations, data.llmReady Only llmReady=true establishes that CLI text reading and ask --document can use the LLM representation
files read data.content, data.offset, data.limit, data.total_size Pass returned data.nextOffset as the next --offset; its absence means the end was reached. Default offset is 0 and limit is 65,536; maximum limit is 4,194,304
monitors list data.total, data.limit, data.offset, and each datapoint_monitor_id Default limit 50, maximum 100. Advance --offset with the page's limit until the returned total is covered; preserve the filters
monitors get data.next_cursor Pass a non-empty cursor unchanged to --cursor; null or absent means no next page. Maximum content limit is 100

files read returns extracted LLM text, not original binary bytes. An uploaded Word document or workbook cannot be faithfully reconstructed from that text alone. A document still being prepared causes ask --document to refuse with DOCUMENT_NOT_READY; use a later status check instead of repeatedly submitting research turns.

For Monitor company filters, use full composite tickers returned by Portrait. Responses retain company UUID arrays and positionally matching tickers arrays; a ticker can be null. Do not shift the arrays or guess a ticker for an unresolved company. monitors get --project can pin a known Monitor to the Project ID returned by discovery; omit it when not needed.

--mark-read marks only the content retrieved in that page, after the read. Omit it for inspection that must preserve unread state. Paging through a changing unread feed while marking pages changes that feed; do not equate one marked page with marking the entire Monitor.

Recover from an uncertain request

  1. Keep the session ID, command, exit status, and safely redacted error or partial output.
  2. If the request may have reached Portrait, inspect sessions history for that session before repeating an instruction. A saved transcript can trail work in flight, so absence of a final answer is not proof that nothing ran.
  3. Reconnect authentication when required. Use a returned session ID to resume; do not substitute a new conversation merely to avoid an access or capacity refusal.
  4. Treat retryable=true as a transient-error classification, not permission to repeat effects. Reconfirm any changed scope or proposed costly operation with the user.

If a failed ask returned no session ID, use sessions list to find candidate conversations from that attempt and inspect their histories before choosing one. Match the request and time; do not guess from a similar title. If the prior outcome cannot be identified, report it as uncertain instead of starting replacement work.

Idempotency and message identity

Within one ask invocation, the CLI reuses its clientMessageId while performing supported transport recovery. A second invocation generates a new message identity, even with the same --session. The session ID selects conversation context; it is not an idempotency key. Without --session, the invocation also creates a new conversation. There is no CLI option for making separate invocations the same research turn.

HTTP 429 and retry delays

For Easel operations, HTTP 429 can produce RATE_LIMITED (retryable=true) or USAGE_REFUSED (retryable=false). Inspect the error code as well as the status: a usage refusal needs its usage or access condition resolved before another attempt.

For RATE_LIMITED, the CLI can retry once within the same request when the server provides a numeric Retry-After delay that fits the CLI's bounded recovery budget. A missing, unusable, or excessive delay does not trigger that automatic retry. The JSON error envelope does not expose the header or a machine-readable retry delay. If the CLI returns the error, retain the session and review its history before deciding whether another ask is safe; do not infer a delay from the HTTP status or repeat the turn solely because retryable is true.

Stopping the local process or losing its network connection does not confirm remote cancellation. This CLI version has no public cancel or end command. Use the supported conversation controls in Portrait and verify the state; do not relaunch a screen or other workflow solely because its answer stream ended unexpectedly.

Validate a consuming agent

Before using a parser for real research, check it with local fixtures for a complete stream, a stream ending before completion, an operation.error, an unready document, two-page results, and a null company ticker. Verify that it preserves exact IDs, detects incomplete output, and never changes read state or repeats a write merely to recover from a read failure.