feat(inboxes): add the inboxes endpoints and inbox webhook events

Merged
FE
6d ago
+3561-11

Summary

This PR adds the Inboxes API to the Rust SDK.

  • resend.inboxes is a new InboxesSvc with the 29 /inboxes operations. The nested resources use flat, prefixed methods, the same as webhooks.list_events:
    • Inboxes: create, list, get, update, delete.
    • Agent: get_agent, update_agent.
    • Labels: list_labels, create_label, update_label, delete_label.
    • Threads: list_threads, search_threads, get_thread, update_thread, delete_thread.
    • Thread labels: add_thread_labels, set_thread_labels, remove_thread_label.
    • Thread emails: list_thread_emails, get_thread_email, reply_thread_email, forward_thread_email.
    • Drafts: list_drafts, create_draft, get_draft, update_draft, delete_draft, send_draft.
  • The list endpoints take the usual ListOptions<T>. list_threads and search_threads also take a filter struct. The struct sends folders, labels, from, to, cc and bcc as comma-separated values, as the API requires.
  • Create inbox, create label and create draft return { object, id }. Reply, forward and send draft return { id }, the ID of the sent email.
  • reply_thread_email and forward_thread_email accept attachments. They use CreateAttachment, the same type as emails.send.
  • Every method takes the inbox ID or the inbox email address. inbox_path percent-encodes it.
  • update_thread takes read and folder. The folder is an UpdateInboxThreadFolder, because sent cannot be set. Labels change through add_thread_labels (POST, 1 to 10 IDs), set_thread_labels (PUT, up to 100 IDs, an empty slice clears them) and remove_thread_label (DELETE). Each returns a ListInboxThreadLabelsResponse with every label on the thread.
  • The agent uses actions. InboxAgentAction has Archive and MoveToTrash, and tone is the InboxAgentTone enum of the four presets.
  • update_draft returns UpdateInboxDraftResponse, with only object and id.
  • Search results have no highlights. matched_email_id is the email id that list_thread_emails returns.
  • Inbox has updated_at, and InboxListItem has created_at and updated_at.
  • Drafts accept attachments (resend/monorepo#12481). CreateInboxDraftAttachment needs content, filename and content_type, because the API stores each file on the draft. On update, with_attachments replaces every attachment, and an empty list removes them all. Drafts, draft list items and the draft in inbox.draft.* events return attachments.
  • Create draft, send draft, reply and forward accept an Idempotency-Key header (resend/monorepo#11768). They expose it the same way as emails.send: with_idempotency_key on the options returns an Idempotent<T>. send_draft takes a SendInboxDraftOptions for this, because the request has no body.
  • Reply, forward and send draft accept custom headers (resend/monorepo#12509), through with_header as on emails.send. Resend sets In-Reply-To, References and Reply-To, so the API returns a 422 for them. send_draft sends a body only when headers are set.
  • UpdateInboxAgentOptions and UpdateInboxDraftOptions have clear_* methods that send null, because the API clears a field on null.
  • Event and EventType get the 14 inbox.* webhook events. InboxEvent is an enum tagged by type, because the data shape changes with the event type. events_up_to_date now counts the inbox events instead of skipping them.
  • The doc comments use the words of the OpenAPI spec descriptions, from resend/resend-openapi#125 and #137.
  • Version bump to 0.35.0, in its own commit. I left CHANGELOG.md for the release.

Depends on

Verification

  • cargo build
  • cargo fmt --check
  • cargo clippy --all-targets -- -Dwarnings
  • cargo clippy --all-targets --no-default-features --features rustls-tls -- -Dwarnings
  • cargo clippy --all-targets --features blocking -- -Dwarnings
  • cargo test --lib inbox: 29 offline tests pass, with and without --features blocking. They cover the request bodies, the query strings, the response shapes, and one payload per inbox webhook event.
  • cargo test --lib events_up_to_date: passes against the live event types page with the 14 inbox events counted.

The three clippy runs and cargo fmt --check pass.

A live run through the methods against https://api.resend.com, on a test inbox addressed by its email address. It restored every setting it changed and did not create or delete an inbox. All 33 checks passed:

  • get, update and list: OK. updated_at and the list item created_at and updated_at are set. The name was restored.
  • get_agent and update_agent with InboxAgentTone::ExpandedCasual and [Archive, MoveToTrash]: OK, and the old values were restored.
  • create_label, update_label, list_labels, delete_label: OK.
  • add_thread_labels, set_thread_labels with an empty slice, remove_thread_label: OK, each returned the thread labels. Removing a label that is not on the thread returned 200. An unknown label returned 404 Label not found.
  • get_thread, update_thread with read and a restore, list_threads, and delete_thread on a thread in trash, which returned deleted: false.
  • search_threads: OK, and matched_email_id is an email id from list_thread_emails.
  • list_thread_emails, get_thread_email, reply_thread_email, forward_thread_email: OK.
  • create_draft, update_draft (only object and id), get_draft, list_drafts, send_draft, delete_draft: OK.
  • An unknown inbox address returned 404 Inbox not found.

The crate tests that call the live API do not include inboxes, because the CI team may have no inbox to test with.

Not in this PR

  • Sender authentication fields. They are not on the API yet.