Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ description: "New features, improvements, and fixes across the Sendmux platform.
rss: true
---

<Update label="3 November 2026" tags={["Bug fixes"]} rss={{ title: "Calendar versions in JSON responses" }}>
## Calendar versions in JSON responses

Calendar GET now returns `etag` in the JSON body, matching the `ETag` response header. CLI and MCP callers can use this value in `If-Match` to delete an empty calendar.

</Update>

<Update label="3 November 2026" tags={["New features"]} rss={{ title: "Delete empty mailbox calendars" }}>
## Delete empty mailbox calendars

Expand Down
19 changes: 19 additions & 0 deletions evidence/sdk-current-contract-897-20261103.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Webhook discriminator snapshot prerequisite

Part of qubrics/qubrics#897. Companion API branch: `agent/sdk-current-contract-897`.

The API producer names its two existing webhook payload variants so the generated snapshot carries explicit `type` discriminator mappings. Previously the inline variants had identical property names and ogen could not infer a discriminator from their enum values. This snapshot is emitted from the producer; no payload fields, event values, prose, hosts or runtime behaviour change.

Validation: API contract regression seen red (exit 1), then green (36/36 including closed-object composition); API `pnpm check:fast` exit 0; `pnpm spec:emit --output <this checkout>/openapi-app.json` exit 0; `npm run postman:emit` exit 0 with no collection diff; `npm run confidentiality:check` exit 0. Normal SDK Go generation against this snapshot exits 0 without generator or current-input binding changes. Remaining generation and CI must pass before delegated merge.

Initial Postman invocation found a missing local dependency (`ERR_MODULE_NOT_FOUND: openapi-to-postmanv2`); `npm ci` installed the committed lockfile, then the same command passed. No dependency changes.

Logs: `/Users/rj/Desktop/GIT-REPOS/sendmux/.claude/artifacts/rebrand-qubrics-897/current-contract-{postman,confidentiality,go}.log`. Source-only merge authorised; no publish or deploy invoked. Placement/polish: generated schema metadata only, no prose or navigation change.

## Approved Calendar GET companion

Ops approved exposing the existing Calendar GET version as JSON `data.etag` and a closed GET-only response component. List/create retain their original schemas. The API producer matches the existing event GET pattern and adds no reads or provider calls. Runtime/schema regressions each failed before the fix, then 80 focused tests, `check:fast` and `spec:check` passed (all exit 0).

Placement: existing generated Mailbox API GET reference and current-day changelog entry. Disposition: keep. Fresh-reader critique corrected the missing empty-calendar qualifier; humanize ran once and protected `etag`, `ETag`, `If-Match`, Calendar GET, CLI and MCP plus body/header equivalence. Final copy passed every applicable rubric line; no navigation changes. Generated snapshot uses the producer output; no hand edits or Postman bypass.

This is a source prerequisite, not publication or deployment proof. Normal SDK generation and exact-head CI remain merge gates.
221 changes: 141 additions & 80 deletions openapi-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,43 @@
],
"type": "object"
},
"CalendarWebhookEventPayload": {
"additionalProperties": false,
"properties": {
"data": {
"$ref": "#/components/schemas/CalendarWebhookEventData"
},
"id": {
"description": "Stable per-event identifier. Also sent in the X-Sendmux-Event-Id header.",
"example": "evt_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"occurred_at": {
"description": "When the event occurred.",
"example": "2026-05-21T09:12:00Z",
"type": "string"
},
"team_public_id": {
"description": "Team that owns the event.",
"example": "team_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"type": {
"enum": [
"calendar.changed"
],
"type": "string"
}
},
"required": [
"id",
"type",
"team_public_id",
"occurred_at",
"data"
],
"type": "object"
},
"Connection": {
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -3214,7 +3251,7 @@
{
"properties": {
"data": {
"$ref": "#/components/schemas/MailboxCalendar"
"$ref": "#/components/schemas/MailboxCalendarWithEtag"
},
"meta": {
"$ref": "#/components/schemas/ResponseMeta"
Expand All @@ -3228,6 +3265,47 @@
],
"unevaluatedProperties": false
},
"MailboxCalendarWithEtag": {
"additionalProperties": false,
"properties": {
"etag": {
"description": "Current calendar version, matching the ETag response header. Use this value in If-Match to delete an empty calendar.",
"type": "string"
},
"id": {
"description": "Opaque identifier scoped to the selected mailbox.",
"maxLength": 4096,
"minLength": 1,
"pattern": "^[A-Za-z0-9_-]+$",
"type": "string"
},
"is_default": {
"description": "Whether this is the mailbox's current default calendar.",
"type": "boolean"
},
"mailbox_id": {
"type": "string"
},
"name": {
"type": "string"
},
"time_zone": {
"type": [
"string",
"null"
]
}
},
"required": [
"id",
"mailbox_id",
"name",
"time_zone",
"is_default",
"etag"
],
"type": "object"
},
"MailboxChanges": {
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -7934,6 +8012,52 @@
],
"unevaluatedProperties": false
},
"MailWebhookEventPayload": {
"additionalProperties": false,
"properties": {
"data": {
"$ref": "#/components/schemas/WebhookEventData"
},
"id": {
"description": "Stable per-event identifier. Also sent in the X-Sendmux-Event-Id header.",
"example": "evt_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"occurred_at": {
"description": "When the event occurred.",
"example": "2026-05-21T09:12:00Z",
"type": "string"
},
"team_public_id": {
"description": "Team that owns the event.",
"example": "team_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"type": {
"description": "Dotted event type. Same value as the X-Sendmux-Event-Type header.",
"enum": [
"message.delivered",
"message.bounced",
"message.complained",
"message.rejected",
"message.delivery_delayed",
"message.received",
"message.received.spam",
"sendmux.test"
],
"example": "message.received",
"type": "string"
}
},
"required": [
"id",
"type",
"team_public_id",
"occurred_at",
"data"
],
"type": "object"
},
"PatchCalendarEventBody": {
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -10819,6 +10943,20 @@
},
"WebhookEventPayload": {
"description": "The JSON request body delivered to the webhook endpoint.",
"discriminator": {
"mapping": {
"calendar.changed": "#/components/schemas/CalendarWebhookEventPayload",
"message.bounced": "#/components/schemas/MailWebhookEventPayload",
"message.complained": "#/components/schemas/MailWebhookEventPayload",
"message.delivered": "#/components/schemas/MailWebhookEventPayload",
"message.delivery_delayed": "#/components/schemas/MailWebhookEventPayload",
"message.received": "#/components/schemas/MailWebhookEventPayload",
"message.received.spam": "#/components/schemas/MailWebhookEventPayload",
"message.rejected": "#/components/schemas/MailWebhookEventPayload",
"sendmux.test": "#/components/schemas/MailWebhookEventPayload"
},
"propertyName": "type"
},
"example": {
"data": {
"bounce_subtype": null,
Expand Down Expand Up @@ -10846,87 +10984,10 @@
},
"oneOf": [
{
"additionalProperties": false,
"properties": {
"data": {
"$ref": "#/components/schemas/WebhookEventData"
},
"id": {
"description": "Stable per-event identifier. Also sent in the X-Sendmux-Event-Id header.",
"example": "evt_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"occurred_at": {
"description": "When the event occurred.",
"example": "2026-05-21T09:12:00Z",
"type": "string"
},
"team_public_id": {
"description": "Team that owns the event.",
"example": "team_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"type": {
"description": "Dotted event type. Same value as the X-Sendmux-Event-Type header.",
"enum": [
"message.delivered",
"message.bounced",
"message.complained",
"message.rejected",
"message.delivery_delayed",
"message.received",
"message.received.spam",
"sendmux.test"
],
"example": "message.received",
"type": "string"
}
},
"required": [
"id",
"type",
"team_public_id",
"occurred_at",
"data"
],
"type": "object"
"$ref": "#/components/schemas/MailWebhookEventPayload"
},
{
"additionalProperties": false,
"properties": {
"data": {
"$ref": "#/components/schemas/CalendarWebhookEventData"
},
"id": {
"description": "Stable per-event identifier. Also sent in the X-Sendmux-Event-Id header.",
"example": "evt_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"occurred_at": {
"description": "When the event occurred.",
"example": "2026-05-21T09:12:00Z",
"type": "string"
},
"team_public_id": {
"description": "Team that owns the event.",
"example": "team_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"type": {
"enum": [
"calendar.changed"
],
"type": "string"
}
},
"required": [
"id",
"type",
"team_public_id",
"occurred_at",
"data"
],
"type": "object"
"$ref": "#/components/schemas/CalendarWebhookEventPayload"
}
]
},
Expand Down
Loading