Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .surface
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ hey contact note set
hey contact note set --note
hey contact note set --note-html
hey contact note show
hey contact photo
hey contact photo remove
hey contact photo set
hey contact show
hey contact show-again
hey contact threads
Expand Down
8 changes: 5 additions & 3 deletions API-COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ forward answers. The drafts index (`/entries/drafts.json`) pages the same way, w
| `/my/clearances.json` | GET | SDK `Clearances().Screened`, `ScreenedPage` | `hey screener history`, TUI Screener History | covered |
| `/my/clearances/{id}` | PATCH | SDK `Clearances().Rescreen` | — | unused by the CLI |
| `/contacts.json` | GET | SDK `Contacts().List` | `hey contact list`, Contacts TUI | covered |
| `/contacts/{id}.json` | GET | SDK `Contacts().Get` | `hey contact show`, `hey contact deliver --to screened-out` (external-contact check), Contacts TUI | covered |
| `/contacts/{id}.json` | GET | SDK `Contacts().Get` | `hey contact show`, `hey contact deliver --to screened-out` (external-contact check), `hey contact photo set` (existence check before uploading), Contacts TUI | covered |
| `/contacts.json` | POST | SDK `Contacts().Create` | `hey contact add`, Contacts TUI | covered |
| `/contacts/{id}.json` | PATCH | SDK `Contacts().Update` | `hey contact update`, Contacts TUI | covered |
| `/contacts/{id}.json` | DELETE | SDK `Contacts().Hide` | `hey contact hide`, Contacts TUI | covered |
Expand All @@ -56,6 +56,8 @@ forward answers. The drafts index (`/entries/drafts.json`) pages the same way, w
| `/contacts/{id}/note.json` | GET | SDK `Contacts().Note` | `hey contact note show`, Contacts TUI | covered |
| `/contacts/{id}/note.json` | PATCH | SDK `Contacts().SetNote` | `hey contact note set`, Contacts TUI | covered |
| `/contacts/{id}/note.json` | DELETE | SDK `Contacts().DeleteNote` | `hey contact note delete`, Contacts TUI | covered |
| `/contacts/{id}/uploaded_avatar.json` | PUT | SDK `Contacts().UpdateAvatar` | `hey contact photo set` | covered |
| `/contacts/{id}/uploaded_avatar.json` | DELETE | SDK `Contacts().DeleteAvatar` | `hey contact photo remove` | covered |
| `/autocompletable/contacts/addressable.json` | GET | SDK `Contacts().Addressable` | TUI composer's To, Cc and Bcc suggestions (new message, reply, forward) | covered |
| `/calendars.json` | GET | SDK `Calendars().List` | `hey calendar list` | covered |
| `/calendars/{id}/recordings.json` | GET | SDK `Calendars().GetRecordings` | `hey event list`, `hey event edit <id>` (reading the event back; with `--occurrence`, the occurrence's day and then the series' first day for its `Calendar::Countdown`), `hey event delete <id>` (checking the id is not a day of a series HEY has written out; with `--occurrence --apply-to future`, the occurrence's day), `hey todo list`, `hey journal list` | covered |
Expand All @@ -69,8 +71,8 @@ forward answers. The drafts index (`/entries/drafts.json`) pages the same way, w
| `/entries/{id}/replies/new.json` | GET | SDK `Entries().NewReply` | `hey reply <topic-id>` and `hey compose --thread-id` (recipients, with the acting user excluded) | covered |
| `/entries/drafts.json` | GET | SDK `Entries().ListDraftsPage` | `hey draft list` (`--all`/`--page` follow its cursor) | covered |
| `/entries/drafts/{id}` | DELETE | SDK `Entries().DeleteDraft` | `hey draft delete <id>...` | covered |
| `/rails/active_storage/direct_uploads.json` | POST | SDK `Attachments().Upload` | `hey compose --attach`, `hey reply --attach`, `hey bulk-reply send --attach` | covered |
| signed Active Storage upload URL | PUT | SDK `Attachments().Upload` | `hey compose --attach`, `hey reply --attach`, `hey bulk-reply send --attach` | covered |
| `/rails/active_storage/direct_uploads.json` | POST | SDK `Attachments().Upload` | `hey compose --attach`, `hey reply --attach`, `hey bulk-reply send --attach`, `hey contact photo set` | covered |
| signed Active Storage upload URL | PUT | SDK `Attachments().Upload` | `hey compose --attach`, `hey reply --attach`, `hey bulk-reply send --attach`, `hey contact photo set` | covered |
| signed Active Storage blob URL | GET | SDK `DownloadBlob` | `hey attachment save <id>` | covered |
| `/messages.json` | POST | SDK `Messages().Create`, `Messages().CreateDraft` | `hey compose`, `hey compose --draft`, `hey forward <topic-id>` | covered |
| `/entries/{id}/replies` | POST | SDK `Entries().CreateReply`, `Entries().CreateReplyDraft` | `hey reply <topic-id>`, `hey reply --draft`, `hey compose --thread-id [--draft]` | covered |
Expand Down
4 changes: 3 additions & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ hey contact note set 12345 "Prefers email"
hey contact note show 12345 # read the private note
hey contact note show 12345 --jq '.data.note_markdown' # the note as Markdown; set it back only if note_markdown_lossless is true
hey contact note delete 12345
hey contact photo set 12345 ~/Pictures/jane-doe.jpg # upload a PNG or JPEG as the contact's photo
hey contact photo remove 12345 # back to HEY's default avatar
hey screener list # who is waiting to be screened
hey screener list --count # just the number waiting
hey screener approve 91 # let a sender through
Expand Down Expand Up @@ -286,7 +288,7 @@ Drafts are the review-before-send lane: `hey compose --draft` (and `hey reply --

Search accepts free text plus `--required`, `--any`, `--none`, `--exact`, `--from`, `--to`, `--subject`, `--date`, `--in`, `--label`, and `--attachment`. `--in`, `--date`, `--label` and `--attachment` take one of the values `hey search filters` lists — the attachment kinds are `any`, `images`, `pdfs`, `calendar_invites`, `documents`, `spreadsheets`, `presentations`, `media` and `zip_files`, so it is `--attachment pdfs` rather than `pdf`, and an unrecognized `--in`, `--date` or `--attachment` is refused as a usage error, with the values it accepts, before any search is sent. `--in` also takes a box's kind or display name (`feedbox`, `"Paper Trail"`), the spellings `hey box view` takes, including a name you gave one of those boxes in HEY; a name it does not recognize costs one read of the box list to find out which box it is. Search can narrow only to the Imbox, The Feed, the Paper Trail and Trash: any other box, and a name no box has, is a usage error rather than `not_found`. Use `--page` for one page or `--all` to fetch up to 100 pages; capped searches report the next page for continuation. Search results include `topic_id` for reading the thread and the matching message summaries. Results also include `id` for organization actions, except when you have no box item for the thread.

Contact updates preserve omitted name, email, and alias fields. Supplying `--alias` replaces the complete alias list; `--alias=` clears it. Contact notes accept positional content, `--note`, stdin, or `$EDITOR`. `hey contact note set` replaces the whole note — HEY has no append — so adding a detail means reading the note, changing it and writing all of it back. `hey contact note show --json` (and `hey contact show --json`) answers the note three ways, each an empty string for a contact with no note: `note`, HEY's plain text, which drops formatting (bold is lost, list items become `•`); `note_html`, as HEY serves it; and `note_markdown`, the note as Markdown. `note_markdown_lossless` says whether that Markdown holds everything in the note. When it is `true`, `note set` writes the Markdown back as the same note — HEY's editor markup becomes plain paragraphs on the first write, and from then on a read and a write give the same Markdown and the same HTML. When it is `false`, the note holds an attachment, an image, a table or other markup Markdown has no syntax for, and setting Markdown would drop it: change `note_html` and write it back with `--note-html` instead. HTML passed to `--note-html` has the `<div class="trix-content">` HEY serves a note in taken off, so writing back `note_html` does not nest the note one level deeper each time. Nothing guards a read and a write against someone else saving in between: the last save wins, as it does in the web app. HEY hides contacts rather than permanently deleting them; hidden contacts leave lists, autocomplete, and search, and can be shown again by ID. `hey contact deliver <contact-id> --to imbox|feed|papertrail|screened-out` changes where a contact's future email arrives. It takes a contact ID — not an email address, clearance ID, box item ID, or box ID — and is write-only because HEY does not expose the current setting as structured data. Imbox removes a custom box preference; choosing a box does not screen a denied contact back in. Screened Out is the blocking choice and applies only to external email contacts. Selecting The Feed removes an existing bundle because HEY cannot keep Feed-designated contacts bundled; Imbox and Paper Trail preserve eligible bundles. Existing mail can move asynchronously, so success means HEY accepted the change, not that every affected thread has already moved. Bundling groups a contact's mail into one row without merging or deleting the underlying threads; unbundling lists those threads separately again. HEY bundles only a contact with no box preference or one sent to the Paper Trail; for any other, bundling answers success and changes nothing. A contact that is hidden, an alias, or another HEY user cannot be edited: updating it or setting or deleting its note answers `not_found`. `hey contact list` holds only non-HEY senders you have screened in — other HEY users, senders still in or denied by The Screener, aliases and hidden contacts are left out. It has no search: HEY ignores a query on that list, so finding a contact by email means reading every page (`--all`) and filtering, which matches primary addresses only.
Contact updates preserve omitted name, email, and alias fields. Supplying `--alias` replaces the complete alias list; `--alias=` clears it. Contact notes accept positional content, `--note`, stdin, or `$EDITOR`. `hey contact note set` replaces the whole note — HEY has no append — so adding a detail means reading the note, changing it and writing all of it back. `hey contact note show --json` (and `hey contact show --json`) answers the note three ways, each an empty string for a contact with no note: `note`, HEY's plain text, which drops formatting (bold is lost, list items become `•`); `note_html`, as HEY serves it; and `note_markdown`, the note as Markdown. `note_markdown_lossless` says whether that Markdown holds everything in the note. When it is `true`, `note set` writes the Markdown back as the same note — HEY's editor markup becomes plain paragraphs on the first write, and from then on a read and a write give the same Markdown and the same HTML. When it is `false`, the note holds an attachment, an image, a table or other markup Markdown has no syntax for, and setting Markdown would drop it: change `note_html` and write it back with `--note-html` instead. HTML passed to `--note-html` has the `<div class="trix-content">` HEY serves a note in taken off, so writing back `note_html` does not nest the note one level deeper each time. Nothing guards a read and a write against someone else saving in between: the last save wins, as it does in the web app. HEY hides contacts rather than permanently deleting them; hidden contacts leave lists, autocomplete, and search, and can be shown again by ID. `hey contact deliver <contact-id> --to imbox|feed|papertrail|screened-out` changes where a contact's future email arrives. It takes a contact ID — not an email address, clearance ID, box item ID, or box ID — and is write-only because HEY does not expose the current setting as structured data. Imbox removes a custom box preference; choosing a box does not screen a denied contact back in. Screened Out is the blocking choice and applies only to external email contacts. Selecting The Feed removes an existing bundle because HEY cannot keep Feed-designated contacts bundled; Imbox and Paper Trail preserve eligible bundles. Existing mail can move asynchronously, so success means HEY accepted the change, not that every affected thread has already moved. Bundling groups a contact's mail into one row without merging or deleting the underlying threads; unbundling lists those threads separately again. HEY bundles only a contact with no box preference or one sent to the Paper Trail; for any other, bundling answers success and changes nothing. `hey contact photo set <contact-id> <file>` uploads a photo for a contact, replacing any it had, and answers the contact with its new `avatar_url`; `hey contact photo remove <contact-id>` takes it off again, and removing from a contact with no photo succeeds. The file must be a PNG or JPEG — the formats HEY's own photo picker takes — of at most 40 MiB, the most HEY's web form can send, and at most 58 megapixels and 16000 pixels a side. That admits a full-resolution photo from a modern phone (an iPhone's 48 megapixels, or 50 on Android) and stays inside what HEY can resize, which for a JPEG it has to rotate upright is about 58.7 megapixels; the one exception is a CMYK JPEG that has to be rotated, which HEY can resize only up to about 47 megapixels, and which cameras do not produce. HEY shows a contact photo at 300×300 at most anyway. It is judged by its bytes rather than its name, and its header must decode, because HEY stores any file it is handed and fails only later, when it resizes the photo to show it: a HEIC from a phone, a photo too large to resize, or a file that is not an image would be saved and never shown, the contact keeping their initials. HEY crops the photo to a square, so a square image without transparency works best, and it caches avatars, so after a set or a remove the old photo can still show in places for up to 30 minutes. Your own contact takes a photo too. A contact that is hidden, an alias, or another HEY user cannot be edited: updating it, setting or deleting its note, or setting or removing its photo answers `not_found`. `hey contact list` holds only non-HEY senders you have screened in — other HEY users, senders still in or denied by The Screener, aliases and hidden contacts are left out. It has no search: HEY ignores a query on that list, so finding a contact by email means reading every page (`--all`) and filtering, which matches primary addresses only.

The Screener is where first-time senders wait. `hey screener list` returns clearance IDs — not contact IDs — with the sender and the subject of what they sent, plus `topic_id` for reading the thread before deciding. `--count` asks for the number alone, which is a far cheaper request than the queue, and prints it as a bare number like every other command's `--count`, so `n=$(hey screener list --count)` reads it directly. Approving delivers everything the sender has waiting; denying hides it. Either is reversible with the opposite command, and `hey screener history` shows what was already decided. Both listings page the way `hey box view` does: `--all` follows HEY's cursor to the end of the queue (up to 100 pages), and a single-page or capped read reports `next_page` in its JSON meta, which `--page <next_page>` continues from — the cursor is opaque, so a page *number* does not name a position. `hey screener list` also reports `total_count`, the whole queue's size, next to what the read returned. `--box` and `--seen` approve one sender at a time; several IDs go through HEY's bulk endpoint, which takes neither. `--seen` reliably marks the mail read only for a sender with one waiting thread. `--spam` marks what they sent as spam and, for a single ID, trains HEY's filter, which is harder to undo than denying. `hey screener clear` moves everything waiting to Trash (a shared thread loses your access instead) without approving or denying anyone — those senders reappear on their next email.

Expand Down
Loading
Loading