Skip to content

Receiving and inboxes (0.9.0) - #19

Merged
heliocosta-dev merged 6 commits into
mainfrom
feat/inboxes
Oct 16, 2026
Merged

heliocosta-dev merged 6 commits into
mainfrom
feat/inboxes

Conversation

@heliocosta-dev

Copy link
Copy Markdown
Member

Adds receiving and inboxes to the SDK, matching the API from MillionSend/millionsend v0.8.0 (receiving) and MillionSend/millionsend#112 (inboxes). The SDK version becomes 0.9.0.

  • Received mail: list (with the wait long-poll and the inbox, recipient, sender, subject, unread and quarantine filters), get, reply, forward, delete and attachments.
  • Inboxes: create, list, get, update and delete, plus safety settings, the latest verification code, quarantine and release, labels, threads with their emails, and drafts with send and reject.
  • Send results: replies, forwards and draft sends return the sent email, or on 202 the draft waiting for approval, kept apart from a sent result.
  • Related resources:
    • webhooks take inbox_ids, and the event list has email.received and the 14 inbox.* events;
    • API keys take inbox_id;
    • domains take receiving_catch_all.
  • Ids: empty, . and .. ids are refused before any request is made.

The SDK was checked against the API's OpenAPI document and route code on the product branch: paths, query and body field names, nullability, and the 202 branch. This SDK's tests and linters pass locally.

Merge and release once the product release with inboxes is deployed.

🤖 Generated with Claude Code

heliocosta-dev and others added 6 commits October 12, 2026 03:19
- emails.receiving: list (wait long-poll and the inbox, address, subject,
  unread and quarantine filters), get, reply, forward, remove, and
  attachments.list / attachments.get.
- inboxes: create, list, get, update, remove, plus safety, codes.latest,
  quarantine, labels, threads (with threads.emails) and drafts (with send
  and reject).
- Replies, forwards and drafts.send resolve to { kind: "sent", email } or,
  on an HTTP 202, { kind: "draft", draft }.
- A request with a wait query gets a deadline that outlasts it.
- WebhookEvent names email.received and the 14 inbox.* events; webhooks
  take inboxIds, apiKeys.create takes inboxId, domains.update takes
  receivingCatchAll.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
URL normalization resolves an empty, "." or ".." path segment away, so an
id such as ".." turned DELETE /inboxes/{id}/labels/.. into
DELETE /inboxes/{id}/. The HTTP client now checks every path once, and
such a call resolves to { error: { name: "validation_error",
statusCode: null } } without sending anything.

The long-poll's held items are typed as the API sends them: reason is
pending or quarantined, quarantine_reason says why, and inbox_id is null
for a catch-all copy held for a virus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ails

- SentEmail.thread_id/inbox_id, SentDraft.thread_id and
  QuarantinedEmail.thread_id/subject are nullable, as the API answers them.
- LabelAck is the whole label: create and update answer every field.
- threads.emails.list and get take includeQuarantined, as the threads
  list already does.
- README: held items carry quarantine_reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every error now has retryAfter: the seconds the response's retry-after
header asks to wait, or null without one. Client-side and transport
failures carry null. MillionSend sends the header on its 429s (rate
limits, the shared domain's daily cap, the name-review budget) and on
503 name_review_unavailable.

Tests pin the header parsing and the 404 of an unknown route: JSON
not_found from current servers, application_error from older ones
that answer in plain text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- inboxes.create takes username (the part before the @) and domainId (a
  domains.list row instead of emailAddress); codes.latest takes since.
- Domains carry shared; the team's shared row, listed last, adds
  sending_mode (DomainSendingMode, an open set) and sending_mode_reason.
- Thread list and search items carry an optional quarantined count.
- Contacts batch errors are BatchContactError, with the statusCode and
  name strict mode would answer.
- Docs: inboxes never expire by default, the shared domain's sending
  rules and refusals, name review (503 name_review_unavailable, 429),
  newest-first lists with incremental sync and dead cursors, retries,
  recipients that can never receive mail, the 998-character subject,
  the read-only shared row and the API key domainId rule.
- The CHANGELOG lists the MillionSend version each feature needs.
- The e2e contact uses the SES simulator: the API refuses example.com.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@heliocosta-dev
heliocosta-dev merged commit a113dad into main Oct 16, 2026
4 checks passed
@heliocosta-dev
heliocosta-dev deleted the feat/inboxes branch October 16, 2026 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant