Skip to content
14 changes: 14 additions & 0 deletions members.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions models_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

178 changes: 178 additions & 0 deletions openapi/openapi.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -28947,6 +28947,94 @@
"source",
"key"
]
},
"MemberNotifyRequest": {
"type": "object",
"description": "Notify members by email request",
"required": [
"subject",
"html"
],
"properties": {
"person_ids": {
"type": "array",
"items": {
"type": "integer",
"format": "int64"
},
"maxItems": 20,
"uniqueItems": true,
"description": "Recipient member IDs. Optional, up to 20, no duplicates. Omitted or empty sends to the caller only."
},
"subject": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Email subject, used as written. Required, 1–200 characters. Line breaks are replaced with a space; leading/trailing whitespace is trimmed."
},
"html": {
"type": "string",
"maxLength": 102400,
"description": "Email body as an HTML fragment (no `<html>`/`<head>`/`<body>` wrapper needed); recipients receive it as the whole email body. Required, up to 102,400 bytes of raw UTF-8 input (larger messages are clipped by common email clients), and must be non-empty after sanitization. Sanitized server-side: `<script>`, `<style>`, `<iframe>`, `<object>`, `<embed>`, `<form>`, `<input>`, `<button>`, `<svg>`, `<meta>`, `<link>`, and `<base>` tags and all `on*` event handlers are removed; images are kept only when their `src` is `https` — images with any other or no `src`, including `data:`, are removed; links are restricted to `http`, `https`, and `mailto`. Inline `style` attributes are kept as written."
},
"dry_run": {
"type": "boolean",
"default": false,
"description": "Check without sending. When `true`, every check runs and the response returns the exact email in `html`, but nothing is queued and neither the hourly limit nor the per-turn duplicate check is consumed. Defaults to `false`."
}
}
},
"MemberNotifyResponse": {
"type": "object",
"description": "Notify members by email response",
"properties": {
"recipients": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MemberNotifyResultItem"
},
"description": "One result per resolved recipient, in the same order as the resolved recipient list. With `dry_run`, each result is what a real send would return."
},
"html": {
"type": "string",
"description": "Only present when `dry_run` is `true`: the complete email HTML exactly as recipients would receive it, after sanitization."
}
}
},
"MemberNotifyResultItem": {
"type": "object",
"description": "Per-recipient notify result",
"required": [
"person_id",
"status"
],
"properties": {
"person_id": {
"type": "integer",
"format": "int64",
"description": "Recipient member ID."
},
"status": {
"type": "string",
"enum": [
"accepted",
"skipped"
],
"description": "Delivery status. `accepted` — the email was queued for asynchronous delivery; `skipped` — no email was queued, see `reason`."
},
"reason": {
"type": "string",
"enum": [
"not_member",
"no_email",
"email_disabled",
"duplicate",
"rate_limited",
"send_failed"
],
"description": "Why the recipient was skipped. Only present when `status` is `skipped`. `not_member` — not an active member of the caller's account; `no_email` — the member has no email address on file; `email_disabled` — the member's notification preferences for this kind of message exclude email; `duplicate` — this recipient already received a message from the same AI SRE session turn; `rate_limited` — this recipient has already been sent 20 emails through this endpoint within the last hour; `send_failed` — enqueueing the email failed."
}
}
}
},
"securitySchemes": {
Expand Down Expand Up @@ -60047,6 +60135,96 @@
}
}
}
},
"/member/notify": {
"post": {
"operationId": "memberNotify",
"summary": "Notify members",
"description": "Send an email to account members on behalf of the caller, with content the caller supplies. Only callable with a credential minted for an AI SRE session; any other credential is rejected with `AccessDenied`. Delivery is asynchronous — `accepted` means the email was queued, not that it was delivered. Call it with `dry_run` set to `true` before sending: `html` in the response is the email exactly as recipients will get it, so you can confirm the sanitizer kept everything the message depends on.",
"tags": [
"Platform/Members"
],
"x-mint": {
"content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **200 requests/minute**; **10 requests/second** per account |\n| Permissions | None — callable only with an AI SRE session credential; any other credential is rejected with `AccessDenied` |\n\n## Usage\n\n- Recipients that are not active members of the caller's account, or that have no email address on file, are skipped rather than failing the whole request.\n- Whether email is included follows each recipient's own notification preferences for this kind of message; a recipient with no preference set defaults to receiving it.\n- Recipients receive exactly the sanitized `html` as the email body, with nothing added around it. The sender name shows the caller's name followed by \"(via AI SRE)\".\n- Set `dry_run` to `true` to run every check and get the exact email back in `html` without sending: nothing is queued, and neither the hourly limit nor the per-turn duplicate check is consumed.\n- At most 20 emails are delivered to the same recipient through this endpoint per hour; further deliveries to that recipient in the same window are skipped with `rate_limited`.\n- Retrying the same call within the same AI SRE session turn does not send a duplicate email to a recipient who already received one; the repeat is skipped with `duplicate`.",
"href": "/en/api-reference/platform/members/member-notify",
"metadata": {
"sidebarTitle": "Notify members"
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/SuccessEnvelope"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/MemberNotifyResponse"
}
}
}
]
},
"example": {
"request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4",
"data": {
"recipients": [
{
"person_id": 5068740052131,
"status": "accepted"
},
{
"person_id": 5068740052132,
"status": "skipped",
"reason": "email_disabled"
}
]
}
}
}
}
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"429": {
"$ref": "#/components/responses/TooManyRequests"
},
"500": {
"$ref": "#/components/responses/ServerError"
}
},
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MemberNotifyRequest"
},
"example": {
"person_ids": [
5068740052131,
5068740052132
],
"subject": "Incident 20260914-1 needs your input",
"html": "<p>Can you confirm the rollback window?</p>"
}
}
}
}
}
}
},
"security": [
Expand Down
Loading