Skip to content

Receiving and inboxes (0.9.0) - #6

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

heliocosta-dev merged 5 commits into
mainfrom
feat/inboxes

Conversation

@heliocosta-dev

@heliocosta-dev heliocosta-dev commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Adds receiving and inboxes to the SDK, matching the API of MillionSend v0.14.0. 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 (with username, or domain_id of a Domains.list row), list, get, update and delete, plus safety settings, the latest verification code (with since), 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.
  • Errors: every error carries retry_after, the retry-after header in seconds. forbidden, inbox_send_policy, inbox_send_cap_reached, approval_required, email_quarantined, email_blocked, too_many_waits and name_review_unavailable get their own classes instead of ApplicationError.
  • Related resources:
    • domains document the team's read-only shared row (shared, sending_mode, sending_mode_reason) and take receiving_catch_all;
    • webhooks take inbox_ids, and the event list has email.received and the 14 inbox.* events;
    • API keys take inbox_id.
  • Ids: empty, . and .. ids are refused before any request is made.
  • README: newest-first pagination with incremental sync, the shared domain and what it may send, retries, and the server version each feature needs.

The SDK was checked against v0.14.0's OpenAPI document and route code: paths, query and body field names, nullability, and the 202 branch. This SDK's tests and linters pass on Ruby 3.0 and 4.0.

🤖 Generated with Claude Code

heliocosta-dev and others added 5 commits October 12, 2026 03:38
… codes, quarantine, labels, threads and drafts (0.9.0)

- Emails::Receiving: list (long-poll wait and filters, held ids), get,
  reply and forward (Idempotency-Key; { kind: "sent" | "draft", data }
  since a 202 means the inbox's rules held it for approval), remove, and
  Attachments, which also take resend-ruby's hash shapes.
- Inboxes: create/list/get/update/remove plus Safety, Codes.latest,
  Quarantine, Labels, Threads (search, Threads::Emails reply/forward) and
  Drafts (send, reject). List arrays go out comma-separated.
- receiving_catch_all, inbox_id and inbox_ids documented on domains,
  API keys and webhooks; README sections for both surfaces.

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}/. Request now checks every path once, and such a
call raises ArgumentError without sending anything.

Receiving.list documents the long-poll's held keys as the API sends
them, quarantine_reason included.

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

- Threads::Emails.list passes include_quarantined: (it dropped every
  option but limit/after/before), and Threads::Emails.get takes an
  options hash for it, as the API reads it on both.
- Document quarantined: on Emails::Receiving.list and quarantine_reason
  on the long-poll's held items.
- The held fixture used a quarantine reason as `reason`; it now has
  reason "quarantined" plus quarantine_reason, as the API answers.

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

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

- Errors carry retry_after, the retry-after header in seconds (nil
  without one). forbidden, inbox_send_policy, inbox_send_cap_reached,
  approval_required, email_quarantined, email_blocked, too_many_waits and
  name_review_unavailable raise their own classes, ForbiddenError through
  NameReviewUnavailableError, instead of ApplicationError.
- Inboxes.create documents username and domain_id; inboxes never expire
  unless expires_in says so, and names take 1-320 characters.
  Codes.latest documents since.
- Domains documents the team's shared row (shared, sending_mode,
  sending_mode_reason, records: [], read-only), and ApiKeys.create that
  domain_id must be a shared: false row.
- Emails.send documents sending from a shared-domain inbox; Batch.send and
  broadcasts never send from the shared domain.
- Receiving.list: an unknown inbox_id is not_found at once, and unread
  works alone on an inbox-scoped key. Thread items carry quarantined when
  asked.
- README: newest-first pagination with incremental sync and dead cursors,
  the shared domain, retries, refused recipients and the server version
  each feature needs.
- Fixtures: contacts-batch errors carry statusCode and name, a shared
  inbox has no expires_at, and a shared domain row; the e2e contact uses
  the SES mailbox simulator, since example.com is now refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@heliocosta-dev
heliocosta-dev merged commit fb8ac15 into main Oct 16, 2026
5 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