Repository navigation
feat(inboxes): match the inbox commands to the stable Inboxes API - #420
Conversation
agent get reads actions, which replaced enabled_actions. agent update sends actions through the new --actions flag. --enabled-actions and --enabled_actions stay as hidden aliases. --tone accepts only the four presets and fails with invalid_tone for other text.
The API no longer returns highlights. The search table shows the matched email ID in place of the match snippet. matched_email_id is now the email ID that threads emails list returns.
Each command calls one thread label endpoint: POST, PUT and DELETE
/inboxes/{inbox_id}/threads/{thread_id}/labels. The API now strips
add_label_ids and remove_label_ids from update thread, so threads update
drops --add-label-ids and --remove-label-ids. The old flags stay hidden
and fail with invalid_options and the names of the new commands.
Every route under /inboxes/{inbox_id} now takes the inbox ID or its
email address. The commands pass a value with @ to the SDK as
emailAddress and any other value as inboxId, like the MCP servers.
- drafts update outputs only the object and the draft ID. - inboxes get prints created_at and updated_at; the help shows both fields on get and list. - A reply draft without a subject is sent with the thread subject. - Replies and forwards state only the 25 MB attachment rule. - create help says the address cannot contain a +. - reply, forward, drafts create and drafts send accept a sending-access key.
Document the threads labels commands, the --actions flag, the tone presets, the inbox email address, the sending-access commands, and the new draft and search output.
There was a problem hiding this comment.
2 issues found across 61 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/commands/inboxes/threads/search.test.ts">
<violation number="1" location="tests/commands/inboxes/threads/search.test.ts:251">
P3: This assertion can't fail as written: the mock no longer returns `highlights`, and JSON output passes the SDK result through unchanged. It doesn't guard against highlights reappearing. Either drop the assertion, or keep `highlights` in the mock and assert the CLI strips it, which needs the command to drop that field explicitly.</violation>
</file>
<file name="src/commands/inboxes/drafts/send.ts">
<violation number="1" location="src/commands/inboxes/drafts/send.ts:68">
P2: Interactive `drafts send` with a sending-access credential still fails when either ID is omitted: the pickers fetch before this permission is applied and require `full_access`. Use a sending-access-compatible picker path or require explicit IDs for these credentials.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| await runCreate( | ||
| { | ||
| loading: 'Sending draft...', | ||
| permission: 'sending_access', |
There was a problem hiding this comment.
P2: Interactive drafts send with a sending-access credential still fails when either ID is omitted: the pickers fetch before this permission is applied and require full_access. Use a sending-access-compatible picker path or require explicit IDs for these credentials.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/commands/inboxes/drafts/send.ts, line 68:
<comment>Interactive `drafts send` with a sending-access credential still fails when either ID is omitted: the pickers fetch before this permission is applied and require `full_access`. Use a sending-access-compatible picker path or require explicit IDs for these credentials.</comment>
<file context>
@@ -63,9 +65,10 @@ The output holds the ID of the sent email. Use it with "resend emails get".`,
await runCreate(
{
loading: 'Sending draft...',
+ permission: 'sending_access',
sdkCall: (resend) =>
resend.inboxes.drafts.send(
</file context>
| const parsed = JSON.parse(spies.logSpy.mock.calls[0][0] as string); | ||
| expect(parsed.data[0].matched_email_id).toBe(EMAIL_ID); | ||
| expect(parsed.data[0].highlights.subject).toEqual(['**Billing** question']); | ||
| expect(parsed.data[0]).not.toHaveProperty('highlights'); |
There was a problem hiding this comment.
P3: This assertion can't fail as written: the mock no longer returns highlights, and JSON output passes the SDK result through unchanged. It doesn't guard against highlights reappearing. Either drop the assertion, or keep highlights in the mock and assert the CLI strips it, which needs the command to drop that field explicitly.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/commands/inboxes/threads/search.test.ts, line 251:
<comment>This assertion can't fail as written: the mock no longer returns `highlights`, and JSON output passes the SDK result through unchanged. It doesn't guard against highlights reappearing. Either drop the assertion, or keep `highlights` in the mock and assert the CLI strips it, which needs the command to drop that field explicitly.</comment>
<file context>
@@ -249,7 +248,7 @@ describe('inboxes threads search command', () => {
const parsed = JSON.parse(spies.logSpy.mock.calls[0][0] as string);
expect(parsed.data[0].matched_email_id).toBe(EMAIL_ID);
- expect(parsed.data[0].highlights.subject).toEqual(['**Billing** question']);
+ expect(parsed.data[0]).not.toHaveProperty('highlights');
});
</file context>
- inboxes delete prints the API result, so an address prints the UUID. - threads labels add rejects an empty --labels with missing_labels. - agent update removes duplicate actions and checks renames with Object.hasOwn. - The threads update error names full commands. - The labels help lists missing_id and invalid_options.
There was a problem hiding this comment.
0 issues found across 13 files (changes from recent commits).
Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.
View guided diff | Turn on auto-fix | Re-trigger cubic
Remove the hidden --enabled-actions and --enabled_actions aliases of agent update --actions. Remove the custom error for the removed threads update label flags, so commander rejects them as unknown options. Remove the version-history paragraphs from the README and the skill reference.
The insufficient permissions message now lists the four inbox commands that a sending-access key can run. The inbox help, README and skill reference say that a sending-access key must pass every ID, because the pickers need full access.
There was a problem hiding this comment.
0 issues found across 10 files (changes from recent commits).
Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.
View guided diff | Turn on auto-fix | Re-trigger cubic
An old action value such as archive_thread now gets the same invalid_actions error as any other invalid value.
There was a problem hiding this comment.
0 issues found across 2 files (changes from recent commits).
Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.
View guided diff | Turn on auto-fix | Re-trigger cubic
dielduarte
left a comment
There was a problem hiding this comment.
I found no bugs, just two nits, already approving anyway!
- --labels doesn't split commas (threads/labels/utils.ts, requireLabelIds). --actions in the same PR does. --labels a,b gets a 422 from the API.
- The labels set missing-flag error doesn't mention that --labels "" is valid.
… set "threads labels add" and "threads labels set" now split --labels values on commas, like "agent update --actions". Before, "--labels a,b" sent one ID and the API returned a 422. The missing-flag error of "set" now names --labels "" to remove every label.
There was a problem hiding this comment.
0 issues found across 6 files (changes from recent commits).
Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.
View guided diff | Turn on auto-fix | Re-trigger cubic
What broke in 2.25.0
The Inboxes API is now stable. Three commands of the released 2.25.0 fail against the live API.
inboxes agent getreadsenabled_actions. The API now returnsactions, so the interactive output crashes on.join.inboxes agent update --enabled-actionssendsenabled_actions. The API ignores it and returns 422.inboxes threads update --add-label-idsand--remove-label-idssend fields that the API strips. The request returns 422.This PR moves the CLI to
resend@6.34.1and matches every inbox command to the merged OpenAPI spec (resend-openapi#137).Changes per command group
agent
agent getreads and printsactions.agent updatetakes--actionsand removes duplicate values. The values arearchiveandmove_to_trash, notarchive_threadanddelete_thread. An old value fails withinvalid_actionsand names the new value.--toneaccepts only the four presets fromINBOX_AGENT_TONES, or""to clear it. Other text fails withinvalid_tone(MES-2747).invalid_enabled_actionsis nowinvalid_actions. The sibling codes (invalid_tone,invalid_limit,invalid_header) take the name of the flag.mainhad no SDK bridge foractions. The bridge was only in the closed feat(inboxes): rename the agent enabled-actions flag and field to actions #417, so MES-2740 needs no code removal here.threads labels (new)
threads labels add <inbox> <thread> --labels <ids...>callsPOST /inboxes/{inbox_id}/threads/{thread_id}/labels.threads labels set <inbox> <thread> --labels <ids...>callsPUTon the same path.--labels ""removes every label.threads labels remove <inbox> <thread> <label>callsDELETE .../labels/{label_id}.resend.inboxes.threads.labels.add|set|remove) and outputs the label list of the thread.threads
threads updatetakes only--read,--unreadand--folder.threads searchdropshighlightsfrom help and from the table. The table showsmatched_email_id, which is now the email ID thatthreads emails listreturns.Inbox ID or email address
@goes to the SDK asemailAddress. Any other value goes asinboxId, like the MCP servers.--inbox-idflag says "Inbox UUID or email address".inboxes deleteprints the API result, so an address argument still prints the inbox UUID.inboxes, drafts, attachments, keys
inboxes getandlisthelp showcreated_atandupdated_at.getprints both in interactive mode.inboxes createhelp says the address cannot contain a+.drafts updatehelp shows the new output{"object":"inbox_draft","id":"<uuid>"}.drafts createanddrafts sendhelp say a reply draft without a subject is sent with the thread subject and aRe:prefix.threads emails replyandforwardhelp state only the 25 MB attachment rule. Thepathrule now matches the API.threads emails reply,threads emails forward,drafts createanddrafts sendaccept a sending-access key. The other inbox commands still need full access. The interactive pickers list inboxes, threads or drafts, so a sending-access key must pass every ID.Docs
skills/resend-cli/references/inboxes.mddescribe all of the above. The SKILL.md command table liststhreads labels.Compatibility
--enabled-actionsand--enabled_actionsstay as hidden aliases of--actions.--add-label-ids,--remove-label-idsand their snake_case spellings stay as hidden flags. They fail withinvalid_optionsand a message that namesthreads labels add,removeandset. The CLI does not drop them silently.invalid_enabled_actionsis nowinvalid_actions.drafts updateJSON output is now onlyobjectandid, because the API changed.Checks
pnpm install --frozen-lockfilepasses.resendis inminimumReleaseAgeExcludeinpnpm-workspace.yaml, so 6.34.1 installs.pnpm lint,pnpm typecheck,CI=true pnpm vitest run(1479 passed, 1 skipped) andpnpm buildpass.tests/commands/inboxes/email-address.test.tsruns every inbox command with an address.tests/commands/inboxes/sending-access.test.tschecks the key access.pnpm test:e2ewas not run.E2E
Built CLI,
.envkey, fixture inbox43cd94f5-63f2-4abe-93d4-58b9c26f353e.--helpfor all 36 inbox commands exits 0.agent getby ID and by address returnsactions. Interactive output printsActions: (none).agent updatewas not called on the API, so the fixture agent did not change. Its localinvalid_toneandinvalid_actionserrors were checked.threads labels add,set,removeandset --labels ""on thread772da839returned the expected lists. An unknown label returned 404Label not found.. The thread labels are[]again, as before. The labelsspec-137 aandspec-137 bdid not change.threads update --add-label-idsreturnedinvalid_optionsbefore any API call.inboxes get,labels list,threads list,threads search,threads get,threads emails list,threads emails get,drafts listanddrafts getpassed by ID, by address and by an uppercase address.matched_email_idequals theidfromthreads emails list.{object, id}), read and deleted.--forwardingand deleted by its address. The delete output held the inbox UUID.Release
Release 2.26.0.
package.jsonis 2.26.0 and theresend-cliskill is 2.23.0.