Skip to content

feat(inbound): custom receiving domains + catch-all addresses - #14

Merged
tuyakhov merged 4 commits into
claude/gallant-einstein-czq95mfrom
claude/gallant-einstein-ca80ql
Jul 29, 2026
Merged

feat(inbound): custom receiving domains + catch-all addresses#14
tuyakhov merged 4 commits into
claude/gallant-einstein-czq95mfrom
claude/gallant-einstein-ca80ql

Conversation

@tuyakhov

Copy link
Copy Markdown
Contributor

Summary

Follows up the base inbound support with the phase-2 capabilities now available
on the API, on both the sync and async clients:

  • Custom receiving domains — receive mail on your own domain (e.g.
    inbound.acme.com) in addition to the shared receiving domain. Claiming a
    domain returns the DNS records to publish; it verifies once they're live.
  • Catch-all addresses — a "*" local part receives every address no exact
    address claims.
  • Address creation gains an explicit livemode and a domain_id to target a
    custom domain.

Stacked on the base inbound branch so the diff here is only the phase-2 delta.
Retargets to main automatically once the base branch merges.

Changes

  • client.inbound.domainslist(), create(domain, ...), delete(id) on
    both Inbound and AsyncInbound. create() returns the DNS records to
    publish; a domain that already routes mail elsewhere raises a 409
    SenderKitAPIError (existing_mx) so a caller can confirm before redirecting.
  • inbound.addresses.create() gains domain_id and livemode; local_part="*"
    provisions a catch-all.
  • New models: InboundDomain, InboundDnsRecord.
  • README gains a custom-domains / catch-all example.

Checklist

  • ruff check . passes
  • ruff format . applied
  • mypy src passes
  • pytest passes (new behavior is covered by tests)
  • PR title / commits follow Conventional Commits
  • Docs / docstrings updated

This mirrors the matching change in the TypeScript and PHP SDKs so all three
stay in sync.


Generated by Claude Code

claude added 2 commits July 28, 2026 21:16
Extends inbound support with the phase-2 capabilities now on the API: claim a
custom domain to receive on (alongside the shared receiving domain), and
provision catch-all addresses. Covers both the sync and async clients.

- client.inbound.domains — list(), create(domain, ...), delete(id) on both
  Inbound and AsyncInbound. create() returns the DNS records to publish; an
  existing-MX conflict surfaces as a 409 SenderKitAPIError so callers can
  confirm before redirecting mail.
- inbound.addresses.create() gains domain_id and livemode; local_part "*"
  provisions a catch-all.
- New models: InboundDomain, InboundDnsRecord.

Tests: ruff, mypy, and pytest all pass.
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.43750% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/senderkit/resources/inbound.py 97.56% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor Author

CI status: the red checks are ruff format --check (the Lint & type-check job) and the aggregate CI gate that depends on it. The reformat it wants is in pre-existing docstring/example code (the integrations/ framework examples) — files untouched by this change.

It's pre-existing on the base branch: this PR is stacked on the inbound base branch, whose CI fails identically, so it needs to clear there. This phase-2 diff itself passes ruff check, mypy src, and pytest locally — so I'm not reformatting unrelated files in this feature PR.


Generated by Claude Code

CI installs the dev extra with a floating `ruff>=0.5`, so it picks up ruff
0.16, which formats Python code blocks inside Markdown by default. That makes
`ruff format --check .` reformat README.md and fail, even though the pinned
ruff-pre-commit hook (0.15.17) and local runs are clean.

Pin `ruff==0.15.17` in the dev extra so CI matches the pre-commit hook and the
lint gate is deterministic. No source changes.

Copy link
Copy Markdown
Contributor Author

Update — root-caused and fixed. The red ruff format gate was ruff 0.16 (which CI's floating ruff>=0.5 now resolves to) formatting Python code blocks inside Markdown by default, so README.md got reformatted. Local runs and the pinned pre-commit hook (0.15.17) don't do this.

Fix: pinned ruff==0.15.17 in the dev extra to match the pre-commit hook (verified: clean under 0.15.17, reformats under 0.16). Pushed here so this PR's lint gate goes green, and opened the same one-line fix against the base branch in #15 so it's fixed at the source too — the pins are identical, so they merge cleanly.


Generated by Claude Code

…essages)

Adds async-client tests for the new inbound domains methods and the async
address-create params, plus the pre-existing async address/message paths that
had no coverage. Raises patch coverage above the project target.
@tuyakhov
tuyakhov merged commit 6ea75c4 into claude/gallant-einstein-czq95m Jul 29, 2026
7 checks passed
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.

2 participants