Skip to content
  • sdk
  • cli
  • api

Upgrading to samva 0.35 and the CLI 0.30

samva 0.31 to 0.35 reject undeclared fields, cap resource names, read threads and drafts across mailboxes, and route unknown addresses. Upgrade the CLI to 0.30.

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

  1. 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 events with eventTypes, 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 answers MalformedRequestError (400) when only top-level fields are undeclared and ValidationError (422) for nested fields or other faults; fields names each rejected path and the declared alternatives. samva commands that take --data apply the same rule to JSON input, fail before sending, and name each rejected field. Template data, metadata, and webhook test data stay free-form. See MalformedRequestError.

  2. Optional filters and headers are absent, not null (0.35). List filters such as cursor, channel, and direction, and headers such as idempotency-key, no longer accept null in their types. Omit the field instead. Replace imports of the Cursor, VersionCursor, and PublicationDomainCursor aliases with string.

  3. 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 switch over a channel must handle every member.

  4. 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) with fields naming 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 also ValidationError.

  5. 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 to null to 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
  1. Work across mailboxes (0.32). mailboxes.listOrganizationThreads lists threads in the mailboxes your credential can read, and it takes conversation to find the threads one conversation has across this organization. mailboxes.listOrganizationDrafts lists your own drafts in the mailboxes where you may write drafts, and mailboxes.getNeedsYou collects held sends you may approve and quarantined messages you may release, with counts over every page. Each row carries its mailbox's id, address, and kind, so one list replaces a walk over every mailbox you support. Effect callers import the same operations from samva/effect/mailboxes. The CLI adds samva mailboxes all-threads, samva mailboxes needs-you, and samva 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.

  1. Templates can answer 503 (0.32). Template operations report a temporary service failure as ServiceUnavailableError (503), with retryAfterSeconds matching the Retry-After header. Add the tag wherever you branch on _tag, wait the advertised time, and reuse the same idempotency key on a write. See ServiceUnavailableError.
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;
  }
}
  1. Conversations filter by mailbox (0.31). conversations.list takes mailboxId and 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.getLogs includes a terminal queue replay as an attempt with kind: "replay", classification: "skipped", and a queueMessageId. A replay record shows processing without another HTTP request and does not raise the attempt count, so filter on kind if 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: null to email.updateCatchAllConfig to remove the webhook while catch-all stays enabled. Unknown addresses then follow the saved unknownRecipientMailboxId, or are dropped without a bounce when none is set. Set unknownRecipientMailboxId in the same update to choose a mailbox, or pass null to 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 its errorCode and completedAt. A cloudflare_challenge code means your Cloudflare zone challenged the request before it reached the endpoint. See Cloudflare challenges.
  • Templates on every channel (CLI 0.30). samva templates list and samva templates get read 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 read accepts a message whose campaign and sender fields are null, including with --json.

Links

Related

All changelog entries