Upgrade samva to 0.37.0, @samva/webhooks to 0.37.0, and @samva/cli to 0.31.0. Start with decision
rules: send modes, per-grant approval flags, and mailbox policies are gone, and a policy write now
names the version it replaces. Then move template deletion calls and commands, pass the review round
when you approve or deny a held action, upgrade a template project's own authoring packages, and
check for an import of the webhook protocol entrypoint.
Each package's full notes are on its
GitHub release.
What you need to do
- Decision rules replace send modes (0.36, CLI 0.31). A mailbox action now records a decision:
allow,hold, orrefuse, with its decider, reason, and the scopes that produced it. Rules live on one scope, read and written atGET,PUT, andDELETE /v1/policies/{kind}/{id}, wherekindisorganization,mailbox,apiKey, oragent. Each rule sets either adecidedefault or anatLeastfloor; a narrower scope replaces a broader default, and every matching floor still applies. A read returnsversion, and every replacement or clear must pass the observedifVersion. A stale one returnsConflictError(409), so reload the snapshot, review it, and write again instead of retrying an old configuration. Clearing a scope advances the version too, so an earlier replacement cannot restore rules you removed. Policy changes need a signed-in person or an OAuth session; an API key cannot write them. Removed with the old model: mailbox grant and API keysendMode, the mailbox policy operations under/v1/mailboxes/{id}/policies, andsendModeon an extension installation, which is nowpolicy. See configure decision rules.
import { createClient } from "samva";
const samva = createClient({
authToken: process.env.SAMVA_OAUTH_TOKEN!,
orgSlug: process.env.SAMVA_ORG_SLUG!,
});
const current = await samva.policies.getPolicy({ kind: "mailbox", id: "mbx_..." });
await samva.policies.setPolicy({
kind: "mailbox",
id: "mbx_...",
ifVersion: current.version,
presets: [],
rules: [{ match: { effect: "write" }, atLeast: "person" }],
});
The CLI adds samva policies get, samva policies set, and samva policies clear, each taking a
scope kind and id. Pass --if-version with a version you already read; without it, the command reads
the current version first and writes that.
- Approvals need the review round (0.36, CLI 0.31). An approval or denial must carry
decision.reviewRoundfrom the action you reviewed. Read the action first and pass that number: a stale round returnsConflictError(409), because the action changed since you read it. Approval also takesedits.textoredits.htmlto send reviewed copy, andalways: trueto allow matching future actions from the same actor while policy floors and guards still apply. Naming only one email body clears the other. The CLI requires--review-roundonsamva mailboxes actions approveanddeny, adds--edit-textto approval, and no longer accepts--send-modeonsamva api-keys createorsamva api-keys update. Needs you returnsheldActionitems, tool calls included, and counts them asheldActionswhere it reportedheldSends; each item carries itsdecision, and a held tool call also carries itstool. Pending-approval webhook events carry the same decision.
import { createClient } from "samva";
const samva = createClient({
authToken: process.env.SAMVA_OAUTH_TOKEN!,
orgSlug: process.env.SAMVA_ORG_SLUG!,
});
const mailboxId = "mbx_7q2xk9mvt4znw8rh";
const actionId = "mbxact_7q2xk9mvt4znw8rh";
const action = await samva.mailboxes.getAction({ id: mailboxId, actionId });
if (action.decision === null) throw new Error("This action has no decision to review");
await samva.mailboxes.approveAction({
id: mailboxId,
actionId,
reviewRound: action.decision.reviewRound,
});
-
Template deletion is permanent (0.36, CLI 0.31).
templates.deletereplacestemplates.removeand removes a template with its publications, andtemplates.deleteProjectremoves a project with every template in it. Both keep sent-message history and its snapshots, and both refuse a template that something still uses withTemplateInUseError(409) naming each reference bykindandid.isActiveis gone from template responses and from create and update payloads, so a template either exists or it does not. The CLI renamessamva templates removetosamva templates delete <id>, previews with--dry-run, and confirms with--yes;samva templates projects delete <id>removes a project and every template in it. See delete a template. -
Namespace keys reach scheduled sends and reports (0.36, CLI 0.31). A namespace key can schedule a message, read analytics, and read conversations in its own namespace, and a scheduled send keeps that namespace when it fires. With an organization credential, pass
namespaceIdto select one; scheduled messages and conversations return theirs. A namespace key also reads the organization's brands. On the CLI,--namespacereplaces--namespace-idon topics and newsletters commands, and the scheduled-message, analytics, conversation, mailbox, webhook endpoint, and API key commands take--namespace. See namespaces. -
A template project upgrades its authoring packages (CLI 0.31).
samva templates dev,check,render,snapshot,build, andexportload@samva/markupand@samva/vitefrom the project rather than from the CLI, and 0.31 drives the 0.23 release line. A project still on 0.22.x stops with the version it found and the command that fixes it, so upgrade both packages in the project:
npm install --save-dev @samva/markup@~0.23.0 @samva/vite@~0.23.0
- Webhook verification is HMAC only (0.37).
@samva/webhooks0.37.0 removesverifyWebhookAtWithPublicKeys,WebhookSignatureVersion,webhookSignedContent, andencodeWebhookSignaturefrom the@samva/webhooks/protocolentrypoint. Samva signs deliveries with HMACv1and never sent the RSAv2scheme those exports verified, so a handler that used them has nothing to check. Verify withverifyorverifyRequestand your endpoint's signing secret; replay checks, payload decoding, and the Promise and Effect adapters are unchanged. See verify each request.
import { verifyRequest } from "@samva/webhooks";
export async function handle(request: Request): Promise<{ id: string; type: string }> {
const { id, event } = await verifyRequest(request, process.env.SAMVA_WEBHOOK_SECRET!);
return { id, type: event.type };
}
Preview · Available to selected organizations.
Agents. An agent definition no longer takes mode, and an integration grant no longer takes
requireApproval. Both are part of definition.policy, the same presets, rules, and
instructions a policy scope holds, and the published policy decides every proposed action. A
model rule has Samva judge the action and holds it for a person when judging is unavailable. A
reply is a write action, so mode: "supervised" becomes a write rule at person; a final rule
alone leaves replies to the model default and they can send without a person. Knowledge sources
must belong to the agent's namespace: attach organization-level sources only to an
organization-level agent, or the draft is refused with ValidationError (422) and
definition.knowledgeSourceIds naming each source. An agent can call hand_to_team to hand a
conversation to staff with a held handoff decision. Approving with always writes an agent policy
scope that you can read and clear, and a saved test now expects the decisions for its actions in
expectedDecisions instead of approvedReply. See
what needs you.
// Before: the whole definition carried its supervision mode.
await samva.agents.replaceDraft({ id: agentId, revision, definition: { ...definition, mode: "supervised" } });
// After: the same definition carries its decision rules. A reply is a write action, and a final
// rule alone would leave replies to the model default.
await samva.agents.replaceDraft({
id: agentId,
revision,
definition: {
...definition,
policy: {
presets: [],
rules: [
{ match: { effect: "write" }, atLeast: "person" },
{ match: { effect: "final" }, atLeast: "person" },
],
},
},
});
Also new
- Search conversations by meaning (0.36).
mailboxes.listThreadsandlistOrganizationThreadstakeqand rank the best 100 matches across subjects, participants, message text, and attachment filenames. Filters narrow that set without changing its order, and new mail takes a few seconds to become searchable. See search conversations. - Per-namespace sending health (0.36). Each namespace sends through its own email sending setup,
so a pause in one namespace leaves the others sending.
email.sending.pausedandemail.sending.resumedcarrynamespaceId, ornullfor organization-level sending. See email sending events. - Mailbox sending readiness (0.36). An email endpoint reports
sendingStatusaspending,ready, orfailed. Creation returns while the address is still being set up, so pass an Idempotency-Key to retry safely, including withsamva mailboxes create --idempotency-key. See sending readiness. - A complete sign-in link (CLI 0.31).
samva loginprints the link that carries the code and asks you to confirm the page shows the same code. A dropped connection or a temporary server error while waiting for approval keeps waiting until the code expires. - Template editor keys (CLI 0.31). In
samva templates dev, ⌘S or Ctrl+S saves a version, ⌘ or Ctrl with +, -, or 0 zooms the canvas, and Escape closes an open panel. Keys match the physical key, and on macOS only ⌘ saves or zooms.