Upgrade samva to 0.35.0 and @samva/cli to 0.30.0. Start with the request bodies: the API now
rejects fields an operation does not declare, so a request that spreads a whole response object or
sends events to a webhook update fails until you correct it. Then handle the name limits and the
unknown-address setting, widen channel switches, add the ServiceUnavailableError branch if you
create templates, and read mailboxes across the organization where you used to walk them one at a
time. Each package's full notes are on its
GitHub release.
What you need to do
-
Undeclared fields are rejected (0.35, CLI 0.30). Remove properties that an operation does not declare from request bodies, including nested objects. For a webhook update, replace
eventswitheventTypes, and pass the mutation's input fields instead of spreading a whole response object into a request. The Effect SDK rejects undeclared body fields before it sends; a Promise client call sends the request and receives the API's error. The API answersMalformedRequestError(400) when only top-level fields are undeclared andValidationError(422) for nested fields or other faults;fieldsnames each rejected path and the declared alternatives.samvacommands that take--dataapply the same rule to JSON input, fail before sending, and name each rejected field. Template data, metadata, and webhook test data stay free-form. SeeMalformedRequestError. -
Optional filters and headers are absent, not null (0.35). List filters such as
cursor,channel, anddirection, and headers such asidempotency-key, no longer acceptnullin their types. Omit the field instead. Replace imports of theCursor,VersionCursor, andPublicationDomainCursoraliases withstring. -
Types keep every channel (0.35). Request and response types retain every channel's values and content, so template lists, mailboxes, messages, subscriptions, and other shared resources decode what the API returns. Unions widen: an exhaustive
switchover a channel must handle every member. -
Names have limits (0.32). Create and update requests reject an organization, mailbox, endpoint display, sender, contact, contact group, campaign, newsletter, topic, or OAuth client name, or a mailbox label display name, that exceeds its limit. A rejected request returns
ValidationError(422) withfieldsnaming the input. Existing longer names stay readable, so shorten one the next time you change it. The CLI applies the same limits. The name limits table lists each maximum; see alsoValidationError. -
Unknown addresses reach a mailbox (0.33). Catch-all receiving no longer needs a webhook endpoint or a forwarding rule to have somewhere to deliver. A domain carries
unknownRecipientMailboxId: name an active mailbox on the same domain and mail that matches no mailbox, forwarding rule, or catch-all webhook reaches that mailbox instead of being dropped. Set it tonullto drop unknown addresses again. They never bounce and send no delivery-status notification. A catch-all webhook takes precedence, so the mailbox receives unknown addresses only while the domain has no catch-all webhook. See receive unknown addresses.
import { createClient } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
await samva.email.updateCatchAllConfig({
domainId: "dom_abc123",
unknownRecipientMailboxId: "mbx_7q2xk9mvt4znw8rh",
});
// Drop unknown addresses again.
await samva.email.updateCatchAllConfig({ domainId: "dom_abc123", unknownRecipientMailboxId: null });
The CLI follows. samva email receiving enable --catch-all enables receiving with no --endpoint and
no forwarding rule, and samva email receiving status reports where mail lands. Neither command sets
this mailbox, so send that setting through samva api:
samva api email/catch-all/config -X PATCH \
-F domainId=dom_abc123 \
-F unknownRecipientMailboxId=mbx_7q2xk9mvt4znw8rh
- Work across mailboxes (0.32).
mailboxes.listOrganizationThreadslists threads in the mailboxes your credential can read, and it takesconversationto find the threads one conversation has across this organization.mailboxes.listOrganizationDraftslists your own drafts in the mailboxes where you may write drafts, andmailboxes.getNeedsYoucollects held sends you may approve and quarantined messages you may release, with counts over every page. Each row carries its mailbox'sid,address, andkind, so one list replaces a walk over every mailbox you support. Effect callers import the same operations fromsamva/effect/mailboxes. The CLI addssamva mailboxes all-threads,samva mailboxes needs-you, andsamva mailboxes thread-update. See work across mailboxes and find your drafts across mailboxes.
import { createClient } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const id = "mbx_7q2xk9mvt4znw8rh";
const threadId = "thread_7q2xk9mvt4znw8rh";
const page = await samva.mailboxes.listOrganizationThreads({ kind: "shared", state: "active" });
for (const thread of page.items) console.log(thread.mailbox.address, thread.subject);
await samva.mailboxes.updateThread({ id, threadId, done: true });
mailboxes.getNeedsYou lists only decisions the caller may take. An API key cannot approve a send or
release a quarantined message, so a call made with an API key returns an empty list. Read it as a
signed-in user, for example with the CLI:
samva mailboxes needs-you --limit 20 --json
A thread now takes done for your own view and closed for everyone in a shared mailbox. A new
live message clears Done for every reader and reopens a closed thread, and personal mailboxes refuse
Close and archive instead. Neither state emits a webhook or event-stream event, so read the thread or
refresh the list to observe it. See
Done and Close.
- Templates can answer 503 (0.32). Template operations report a temporary service failure as
ServiceUnavailableError(503), withretryAfterSecondsmatching theRetry-Afterheader. Add the tag wherever you branch on_tag, wait the advertised time, and reuse the same idempotency key on a write. SeeServiceUnavailableError.
import { createClient, ServiceUnavailableError } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
try {
await samva.templates.create({ name: "Receipt" });
} catch (error) {
if (error instanceof ServiceUnavailableError) {
console.error(`Templates are unavailable for ${error.retryAfterSeconds} seconds`);
} else {
throw error;
}
}
- Conversations filter by mailbox (0.31).
conversations.listtakesmailboxIdand returns only the conversations that reached that mailbox, beside the contact, channel, status, and date filters you already use. See list conversations.
import { createClient } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const page = await samva.conversations.list({ mailboxId: "mbx_7q2xk9mvt4znw8rh", limit: 20 });
for (const conversation of page.items) console.log(conversation.id, conversation.status);
Also new
- Skipped webhook replays (0.34).
webhooks.getLogsincludes a terminal queue replay as an attempt withkind: "replay",classification: "skipped", and aqueueMessageId. A replay record shows processing without another HTTP request and does not raise the attempt count, so filter onkindif you count attempts. See webhooks. - Mailbox Retry (0.33). Retry help and the SDK docs now state the boundary: an eligible queued mailbox action can be retried while cleanup from an ended send attempt waits. Retry keeps the existing delays and the original deadline. A send attempt that is still running, or whose status cannot be confirmed, stays ineligible. See Cancel or retry.
- Clear a catch-all webhook (0.35, CLI 0.30). Pass
endpointId: nulltoemail.updateCatchAllConfigto remove the webhook while catch-all stays enabled. Unknown addresses then follow the savedunknownRecipientMailboxId, or are dropped without a bounce when none is set. SetunknownRecipientMailboxIdin the same update to choose a mailbox, or passnullto drop unknown addresses. Omitted settings stay unchanged.samva email receiving update <domainId>sets or clears both in one request. See receive unknown addresses. - Latest webhook attempt (0.35). The stats for a webhook endpoint include
latestAttempt, the newest non-skipped delivery attempt with itserrorCodeandcompletedAt. Acloudflare_challengecode means your Cloudflare zone challenged the request before it reached the endpoint. See Cloudflare challenges. - Templates on every channel (CLI 0.30).
samva templates listandsamva templates getread templates on every channel, so an organization with templates on more than one channel no longer fails while decoding. - CLI 0.29.2.
samva mailboxes readaccepts a message whose campaign and sender fields are null, including with--json.