A send names an optional target (channel/topic/event) and carries a payload (in-app), an email block, or both. The mediums it asks for are the mediums it carries.
Targets and your catalog
By default you can send any target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients’ preference screen and lets email resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it.
If the project has strict targets turned on (a per-project setting, off by default), this endpoint instead rejects a send whose (target, medium) has no matching catalog entry — see the 400 below. A topic: any catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no target at all is never checked.
Send a direct notification to a specific recipient or send a broadcast to all recipients subscribed to a target.
Direct: If recipient_id is set, target is optional.
Broadcast: If recipient_id is null, target is required.
A send must carry at least one content block — payload for in-app, email for email. Omitting both returns a 400, since such a send names no medium.
The target must be cataloged. If your project has no project-level preference for the (target, medium) a send is asking for, the request is rejected with 400 and nothing is written — for direct and broadcast alike. A topic: any catalog entry satisfies the gate for every concrete topic beneath it. See the catalog is a gate.
Sending is asynchronous. A direct send returns as soon as the notification is accepted — the response carries the notification id with status: "enqueued". Preference gating, quota, and email are all resolved by a background worker. Read the resolved outcome back with retrieve a notification.
Include the optional typed email block (subject, html, text) to also send an email. Its presence is your “email is eligible” signal — omit it and no email is sent (there is no fallback that derives an email from payload). See mediums for the full model.
Broadcast email is capped. A broadcast carrying an email block fans out to eligible recipients up to the project’s max_broadcast_recipients_for_email. Exceeding the cap blocks email entirely rather than mailing an arbitrary subset — a truncated blast looks like it worked.
No templating. You render the subject/HTML/text yourself (e.g. with @react-email) and pass the result. text is optional and derived from html when omitted.
Past the gate, email fires only when the recipient’s email preference is enabled and they have a primary emailcontact. Otherwise the send still succeeds and the email is skipped.
The email is delivered by a background worker, so its outcome is not on the send response. Read it back on the notification’s email field via retrieve a notification.
On a direct send, payload is optional. Omit it and Bodhveda sends the email without creating an in-app notification — useful when the two mediums want different timing (an instant in-app row per event, but one debounced email covering several).
{ "recipient_id": "recipient_123", "target": { "channel": "conversation", "topic": "thread_7", "event": "reply" }, "email": { "subject": "3 new messages on your thread", "html": "<p>Vikram and 2 others replied.</p>" }}
The notification is still created and still returns an id — you need one to read the email outcome back — but its status is not_requested and it is excluded from the recipient’s feed, their unread count, and mark-all-read. The recipient’s in-app preference does not apply to it; the email gates above still do.An explicit "payload": null is treated the same as omitting it. payload remains required on a broadcast.
Arbitrary JSON payload for the notification. This is the in-app content block — its presence is what makes the notification eligible for in-app delivery.
Optional on a direct send. Omit it to deliver on email without creating an in-app notification (an email-only send) — the email block then stands alone. An explicit null counts as omitted.
Required on a broadcast, which is in-app only.
⚠️ A send must carry at least one content block. Omitting both payload and email is a 400 — such a send names no medium.
Optional typed email content block. Its presence is your signal that email is eligible for this send (content-block-implies-intent). Absence ⇒ no email — there is no fallback that derives email from payload.
Direct sends only. Including an email block on a broadcast returns 400 (email is never sent on broadcasts).
Bodhveda does no templating in v1 — you render your own subject/HTML/text (e.g. with @react-email) and pass the result.
Email still fires only if the (target, email) pair is in the project catalog, the recipient's email preference is enabled, and the recipient has a primary email contact.
Show child attributes
Response
Notification response
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Bodhveda","colors":{"primary":"#5A86C3","light":"#7CA6D8","dark":"#3E5D94"},"logo":{"light":"https://mintcdn.com/mudgallabs/uB9oPsFjNmYjQlol/logo/logo.svg?fit=max\u0026auto=format\u0026n=uB9oPsFjNmYjQlol\u0026q=85\u0026s=007119f0163f4b2f5617af39d7ccc782","dark":"https://mintcdn.com/mudgallabs/uB9oPsFjNmYjQlol/logo/logo.svg?fit=max\u0026auto=format\u0026n=uB9oPsFjNmYjQlol\u0026q=85\u0026s=007119f0163f4b2f5617af39d7ccc782"},"favicon":"https://mintcdn.com/mudgallabs/uB9oPsFjNmYjQlol/favicon.ico?fit=max\u0026auto=format\u0026n=uB9oPsFjNmYjQlol\u0026q=85\u0026s=8797dee5c6d2cd0e45b262d0345d295a","navbar":{"links":[{"href":"mailto:hey@ceoshikhar.com","label":"Support"}],"primary":{"type":"button","label":"Console","href":"https://console.bodhveda.com"}},"footer":{"socials":{"discord":"https://discord.gg/Wg9ebJSAAG","github":"https://github.com/MudgalLabs/bodhveda","website":"https://ceoshikhar.com"}},"contextual":{"options":["copy","view","chatgpt","claude","perplexity","mcp","cursor","vscode"],"display":"header"},"styling":{"latex":false},"navigation":{"global":{"anchors":[]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"mudgallabs","hasAudioNarration":false,"actualSubdomain":"mudgallabs","customDomains":["docs.bodhveda.com"],"gitSource":{"type":"github","owner":"mudgallabs","repo":"bodhveda","deployBranch":"main","contentDirectory":"docs","isPrivate":false,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6a73b6373c1cab3bc3c3e3cb","collectionId":"71eee3ae-d08f-4eb0-9a8b-c469c4a80c9a","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6a73b6373c1cab3bc3c3e3cb"},"entitlements":{},"buildId":"6a73b6373c1cab3bc3c3e3cb:success","clientVersion":"0.0.4021","searchFilterCounts":[{"language":"default","tag":"API Reference","count":27},{"language":"default","tag":"Documentation","count":18},{"language":"default","tag":"Recipients","count":17},{"language":"default","tag":"Notifications","count":6},{"language":"default","tag":"Project Preferences","count":6},{"language":"default","tag":"Concepts","count":6},{"language":"default","tag":"Contacts","count":5},{"language":"default","tag":"Preferences","count":3}]},"assistantConfig":{},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"api-reference/endpoint/notifications/send-notification","slug":"/api-reference/endpoint/notifications/send-notification","pageMetadata":{"title":"Send notification","openapi":"POST /notifications/send","description":"Start sending notifications through Bodhveda API.\n\nA send names an optional `target` (`channel`/`topic`/`event`) and carries a `payload` (in-app), an `email` block, or both. The mediums it asks for are the mediums it carries.\n\n### Targets and your catalog\n\nBy default you can send **any** target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients' preference screen and lets `email` resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it.\n\nIf the project has **strict targets** turned on (a per-project setting, **off by default**), this endpoint instead rejects a send whose `(target, medium)` has no matching catalog entry — see the `400` below. A `topic: any` catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no `target` at all is never checked.","href":"/api-reference/endpoint/notifications/send-notification"},"description":{"compiledSource":"\"use strict\";\nconst {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n h3: \"h3\",\n p: \"p\",\n strong: \"strong\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsx(_components.p, {\n children: \"Start sending notifications through Bodhveda API.\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"A send names an optional \", _jsx(_components.code, {\n children: \"target\"\n }), \" (\", _jsx(_components.code, {\n children: \"channel\"\n }), \"/\", _jsx(_components.code, {\n children: \"topic\"\n }), \"/\", _jsx(_components.code, {\n children: \"event\"\n }), \") and carries a \", _jsx(_components.code, {\n children: \"payload\"\n }), \" (in-app), an \", _jsx(_components.code, {\n children: \"email\"\n }), \" block, or both. The mediums it asks for are the mediums it carries.\"]\n }), \"\\n\", _jsx(_components.h3, {\n children: \"Targets and your catalog\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"By default you can send \", _jsx(_components.strong, {\n children: \"any\"\n }), \" target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients’ preference screen and lets \", _jsx(_components.code, {\n children: \"email\"\n }), \" resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"If the project has \", _jsx(_components.strong, {\n children: \"strict targets\"\n }), \" turned on (a per-project setting, \", _jsx(_components.strong, {\n children: \"off by default\"\n }), \"), this endpoint instead rejects a send whose \", _jsx(_components.code, {\n children: \"(target, medium)\"\n }), \" has no matching catalog entry — see the \", _jsx(_components.code, {\n children: \"400\"\n }), \" below. A \", _jsx(_components.code, {\n children: \"topic: any\"\n }), \" catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no \", _jsx(_components.code, {\n children: \"target\"\n }), \" at all is never checked.\"]\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Note,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Note)_missingMdxReference(`Note`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[`Send a `,_jsx(_components.strong,{children:`direct`}),` notification to a specific recipient or send a `,_jsx(_components.strong,{children:`broadcast`}),` to all recipients subscribed to a `,_jsx(_components.code,{children:`target`}),`.`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Direct`}),`: If `,_jsx(_components.code,{children:`recipient_id`}),` is set, `,_jsx(_components.code,{children:`target`}),` is optional.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Broadcast`}),`: If `,_jsx(_components.code,{children:`recipient_id`}),` is null, `,_jsx(_components.code,{children:`target`}),` is required.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`A send must carry `,_jsx(_components.strong,{children:`at least one content block`}),` — `,_jsx(_components.code,{children:`payload`}),` for in-app, `,_jsx(_components.code,{children:`email`}),` for email. Omitting both returns a `,_jsx(_components.code,{children:`400`}),`, since such a send names no medium.`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`The target must be cataloged.`}),` If your project has no project-level preference for the `,_jsx(_components.code,{children:`(target, medium)`}),` a send is asking for, the request is rejected with `,_jsx(_components.code,{children:`400`}),` and nothing is written — for direct and broadcast alike. A `,_jsx(_components.code,{children:`topic: any`}),` catalog entry satisfies the gate for every concrete topic beneath it. See `,_jsx(_components.a,{href:`/docs/concepts/preferences#the-catalog-is-a-gate`,children:`the catalog is a gate`}),`.`]})}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[`Sending is `,_jsx(_components.strong,{children:`asynchronous`}),`. A direct send returns as soon as the notification is accepted — the response carries the notification `,_jsx(_components.code,{children:`id`}),` with `,_jsx(_components.code,{children:`status: \"enqueued\"`}),`. Preference gating, quota, and email are all resolved by a background worker. Read the resolved outcome back with `,_jsx(_components.a,{href:`/api-reference/endpoint/notifications/get-notification`,children:`retrieve a notification`}),`.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`delivering-email`,children:`Delivering email`}),`\n`,_jsxs(_components.p,{children:[`Include the optional typed `,_jsx(_components.code,{children:`email`}),` block (`,_jsx(_components.code,{children:`subject`}),`, `,_jsx(_components.code,{children:`html`}),`, `,_jsx(_components.code,{children:`text`}),`) to also send an `,_jsx(_components.strong,{children:`email`}),`. Its presence is your “email is eligible” signal — omit it and no email is sent (there is no fallback that derives an email from `,_jsx(_components.code,{children:`payload`}),`). See `,_jsx(_components.a,{href:`/docs/concepts/mediums`,children:`mediums`}),` for the full model.`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Broadcast email is capped.`}),` A broadcast carrying an `,_jsx(_components.code,{children:`email`}),` block fans out to eligible recipients up to the project’s `,_jsx(_components.code,{children:`max_broadcast_recipients_for_email`}),`. Exceeding the cap `,_jsx(_components.strong,{children:`blocks email entirely`}),` rather than mailing an arbitrary subset — a truncated blast looks like it worked.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`No templating.`}),` You render the subject/HTML/text yourself (e.g. with `,_jsx(_components.code,{children:`@react-email`}),`) and pass the result. `,_jsx(_components.code,{children:`text`}),` is optional and derived from `,_jsx(_components.code,{children:`html`}),` when omitted.`]}),`\n`,_jsxs(_components.li,{children:[`Past the gate, email fires only when the recipient’s email preference is enabled and they have a `,_jsx(_components.strong,{children:`primary email`}),` `,_jsx(_components.a,{href:`/api-reference/endpoint/recipients/contacts/create-contact`,children:`contact`}),`. Otherwise the send still succeeds and the email is skipped.`]}),`\n`,_jsxs(_components.li,{children:[`The email is delivered by a background worker, so its outcome is `,_jsx(_components.strong,{children:`not`}),` on the send response. Read it back on the notification’s `,_jsx(_components.code,{children:`email`}),` field via `,_jsx(_components.a,{href:`/api-reference/endpoint/notifications/get-notification`,children:`retrieve a notification`}),`.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`email-without-an-in-app-notification`,children:`Email without an in-app notification`}),`\n`,_jsxs(_components.p,{children:[`On a `,_jsx(_components.strong,{children:`direct`}),` send, `,_jsx(_components.code,{children:`payload`}),` is optional. Omit it and Bodhveda sends the email `,_jsx(_components.strong,{children:`without`}),` creating an in-app notification — useful when the two mediums want different timing (an instant in-app row per event, but one debounced email covering several).`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`8`,language:`json`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`json`,children:_jsxs(_components.code,{language:`json`,numberOfLines:`8`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"recipient_id\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"recipient_123\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"target\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: { `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:`\"channel\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"conversation\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:`\"topic\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"thread_7\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:`\"event\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"reply\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` },`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"email\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"subject\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"3 new messages on your thread\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"html\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"\u003cp>Vikram and 2 others replied.\u003c/p>\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`The notification is still created and still returns an `,_jsx(_components.code,{children:`id`}),` — you need one to read the email outcome back — but its `,_jsx(_components.code,{children:`status`}),` is `,_jsx(_components.code,{children:`not_requested`}),` and it is excluded from the recipient’s `,_jsx(_components.a,{href:`/api-reference/endpoint/recipients/notifications/list-notifications`,children:`feed`}),`, their `,_jsx(_components.a,{href:`/api-reference/endpoint/recipients/notifications/unread-count`,children:`unread count`}),`, and mark-all-read. The recipient’s `,_jsx(_components.strong,{children:`in-app`}),` preference does not apply to it; the email gates above still do.`]}),`\n`,_jsxs(_components.p,{children:[`An explicit `,_jsx(_components.code,{children:`\"payload\": null`}),` is treated the same as omitting it. `,_jsx(_components.code,{children:`payload`}),` remains `,_jsx(_components.strong,{children:`required on a broadcast`}),`.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Send notification","openapi":"POST /notifications/send","description":"Start sending notifications through Bodhveda API.\n\nA send names an optional `target` (`channel`/`topic`/`event`) and carries a `payload` (in-app), an `email` block, or both. The mediums it asks for are the mediums it carries.\n\n### Targets and your catalog\n\nBy default you can send **any** target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients' preference screen and lets `email` resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it.\n\nIf the project has **strict targets** turned on (a per-project setting, **off by default**), this endpoint instead rejects a send whose `(target, medium)` has no matching catalog entry — see the `400` below. A `topic: any` catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no `target` at all is never checked.","href":"/api-reference/endpoint/notifications/send-notification"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Delivering email","slug":"delivering-email","depth":2,"children":[]},{"title":"Email without an in-app notification","slug":"email-without-an-in-app-notification","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"summary":"Send a notification (direct or broadcast)","operationId":"sendNotification","tags":["Notifications"],"description":"Start sending notifications through Bodhveda API.\n\nA send names an optional `target` (`channel`/`topic`/`event`) and carries a `payload` (in-app), an `email` block, or both. The mediums it asks for are the mediums it carries.\n\n### Targets and your catalog\n\nBy default you can send **any** target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients' preference screen and lets `email` resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it.\n\nIf the project has **strict targets** turned on (a per-project setting, **off by default**), this endpoint instead rejects a send whose `(target, medium)` has no matching catalog entry — see the `400` below. A `topic: any` catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no `target` at all is never checked.","requestBody":"cd2d5a94-f50b-48c2-8e93-9c16d9459b22","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST https://api.bodhveda.com/notifications/send \\\n -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"recipient_id\": \"recipient_123\",\n \"target\": {\n \"channel\": \"posts\",\n \"topic\": \"post_id_123\",\n \"event\": \"new_comment\"\n },\n \"payload\": {\n \"title\": \"Elon, Zuck and others commented on your post.\",\n \"post_url\": \"/posts/post_id_123#comments\"\n }\n }' "},{"lang":"curl","label":"cURL — with email","source":"# Direct send fanned out to inbox + email.\n# The `email` block makes email eligible (direct sends only).\ncurl -X POST https://api.bodhveda.com/notifications/send \\\n -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"recipient_id\": \"recipient_123\",\n \"target\": {\n \"channel\": \"digest\",\n \"topic\": \"none\",\n \"event\": \"sent\"\n },\n \"payload\": {\n \"title\": \"Your daily digest is ready.\"\n },\n \"email\": {\n \"subject\": \"Your daily digest\",\n \"html\": \"\u003ch1>Your daily digest\u003c/h1>\u003cp>3 new follow-ups today.\u003c/p>\",\n \"text\": \"Your daily digest\\n3 new follow-ups today.\"\n }\n }' "},{"lang":"javascript","label":"JavaScript","source":"import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst res = await bodhveda.notifications.send({\n recipient_id: \"recipient_123\",\n target: { channel: \"posts\", topic: \"post_id_123\", event: \"new_comment\" },\n payload: {\n title: \"Elon, Zuck and others commented on your post.\",\n post_url: \"/posts/post_id_123#comments\",\n },\n});\n// res.notification (direct) or res.broadcast, plus res.deliveries for email."},{"lang":"go","label":"Go","source":"import (\n \"context\"\n \"encoding/json\"\n\n bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nrecipientID := \"recipient_123\"\npayload, _ := json.Marshal(map[string]any{\n \"title\": \"Elon, Zuck and others commented on your post.\",\n \"post_url\": \"/posts/post_id_123#comments\",\n})\nres, _ := client.Notifications.Send(ctx, \u0026bodhveda.SendNotificationRequest{\n RecipientID: \u0026recipientID,\n Target: \u0026bodhveda.Target{Channel: \"posts\", Topic: \"post_id_123\", Event: \"new_comment\"},\n Payload: payload,\n})\n// res.Notification (direct) or res.Broadcast, plus res.Deliveries for email."}],"responses":{"200":"08a9f562-0ad0-4097-b585-0b4a0bfd3ecd","400":"67fb64a3-f0b0-490e-8ed3-eb3ae7516009"},"security":["6b8f63a1-3b44-49cf-9a5d-059162e75315"],"method":"post","type":"path","servers":[],"path":"/notifications/send","baseUrlOptions":[],"title":"Send a notification (direct or broadcast)","codeSamples":[{"label":"cURL","lang":"curl","source":"curl -X POST https://api.bodhveda.com/notifications/send \\\n -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"recipient_id\": \"recipient_123\",\n \"target\": {\n \"channel\": \"posts\",\n \"topic\": \"post_id_123\",\n \"event\": \"new_comment\"\n },\n \"payload\": {\n \"title\": \"Elon, Zuck and others commented on your post.\",\n \"post_url\": \"/posts/post_id_123#comments\"\n }\n }' "},{"label":"cURL — with email","lang":"curl","source":"# Direct send fanned out to inbox + email.\n# The `email` block makes email eligible (direct sends only).\ncurl -X POST https://api.bodhveda.com/notifications/send \\\n -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"recipient_id\": \"recipient_123\",\n \"target\": {\n \"channel\": \"digest\",\n \"topic\": \"none\",\n \"event\": \"sent\"\n },\n \"payload\": {\n \"title\": \"Your daily digest is ready.\"\n },\n \"email\": {\n \"subject\": \"Your daily digest\",\n \"html\": \"\u003ch1>Your daily digest\u003c/h1>\u003cp>3 new follow-ups today.\u003c/p>\",\n \"text\": \"Your daily digest\\n3 new follow-ups today.\"\n }\n }' "},{"label":"JavaScript","lang":"javascript","source":"import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst res = await bodhveda.notifications.send({\n recipient_id: \"recipient_123\",\n target: { channel: \"posts\", topic: \"post_id_123\", event: \"new_comment\" },\n payload: {\n title: \"Elon, Zuck and others commented on your post.\",\n post_url: \"/posts/post_id_123#comments\",\n },\n});\n// res.notification (direct) or res.broadcast, plus res.deliveries for email."},{"label":"Go","lang":"go","source":"import (\n \"context\"\n \"encoding/json\"\n\n bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nrecipientID := \"recipient_123\"\npayload, _ := json.Marshal(map[string]any{\n \"title\": \"Elon, Zuck and others commented on your post.\",\n \"post_url\": \"/posts/post_id_123#comments\",\n})\nres, _ := client.Notifications.Send(ctx, \u0026bodhveda.SendNotificationRequest{\n RecipientID: \u0026recipientID,\n Target: \u0026bodhveda.Target{Channel: \"posts\", Topic: \"post_id_123\", Event: \"new_comment\"},\n Payload: payload,\n})\n// res.Notification (direct) or res.Broadcast, plus res.Deliveries for email."}],"requiredOnlyExamples":false,"requestExampleType":"codeSamples","responseExampleType":"generated"},"dependencies":{"parameters":{},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"recipient_id":{"oneOf":[{"type":"string","description":"The `id` of a recipient.\n- If null → this is treated as a **broadcast** notification.\n- If set → this is a **direct** notification to that recipient. \n\n 💡 If no recipient exists with the `recipient_id`, Bodhveda will create a new recipient with that `recipient_id`.","uniqueKey":"recipient_id.oneOf.0","typeLabel":"string"},{"type":"null","description":"The `id` of a recipient.\n- If null → this is treated as a **broadcast** notification.\n- If set → this is a **direct** notification to that recipient. \n\n 💡 If no recipient exists with the `recipient_id`, Bodhveda will create a new recipient with that `recipient_id`.","uniqueKey":"recipient_id.oneOf.1","typeLabel":"null"}],"uniqueKey":"recipient_id","typeLabel":"string | null"},"target":{"type":"object","description":"A **target** is the structure used to describe _who should get a notification_ and _how it should be categorized_.","properties":{"channel":{"type":"string","description":"Broad category for a notification (e.g., \"posts\").","isRequired":true,"uniqueKey":"target.channel","typeLabel":"string"},"topic":{"type":"string","description":"Subcategory inside the channel (e.g., \"post_id_123\"). \n- `any` and `none` have special meaning. \n- `any` means **all topics under this channel/event**. \n- `none` means **only the base channel/event**. \n- `any` is **not** a valid `topic` when sending a notification, but both are valid for preferences. \n\n A preference with the target `posts:any:new_comment` will match a notification with target `posts:post_id_123:new_comment` but a preference with the target `posts:none:new_comment` will **only** match a notification with target `posts:none:new_comment`.","isRequired":true,"uniqueKey":"target.topic","typeLabel":"string"},"event":{"type":"string","description":"Event that triggered this notification (e.g., \"new_comment\").","isRequired":true,"uniqueKey":"target.event","typeLabel":"string"}},"required":["channel","topic","event"],"uniqueKey":"target","typeLabel":"object"},"payload":{"oneOf":[{"type":"object","description":"Arbitrary JSON payload for the notification. This is the **in-app** content block — its presence is what makes the notification eligible for in-app delivery.\n\n**Optional on a direct send.** Omit it to deliver on email *without* creating an in-app notification (an *email-only* send) — the `email` block then stands alone. An explicit `null` counts as omitted.\n\n**Required on a broadcast**, which is in-app only.\n\n⚠️ A send must carry at least one content block. Omitting both `payload` and `email` is a `400` — such a send names no medium.","uniqueKey":"payload.oneOf.0","typeLabel":"object"},{"type":"null","description":"Arbitrary JSON payload for the notification. This is the **in-app** content block — its presence is what makes the notification eligible for in-app delivery.\n\n**Optional on a direct send.** Omit it to deliver on email *without* creating an in-app notification (an *email-only* send) — the `email` block then stands alone. An explicit `null` counts as omitted.\n\n**Required on a broadcast**, which is in-app only.\n\n⚠️ A send must carry at least one content block. Omitting both `payload` and `email` is a `400` — such a send names no medium.","uniqueKey":"payload.oneOf.1","typeLabel":"null"}],"uniqueKey":"payload","typeLabel":"object | null"},"email":{"type":"object","description":"Optional typed **email** content block. Its **presence** is your signal that email is eligible for this send (content-block-implies-intent). Absence ⇒ no email — there is **no** fallback that derives email from `payload`.\n\n- **Direct sends only.** Including an `email` block on a broadcast returns `400` (email is never sent on broadcasts).\n- Bodhveda does **no** templating in v1 — you render your own subject/HTML/text (e.g. with `@react-email`) and pass the result.\n- Email still fires only if the `(target, email)` pair is in the project catalog, the recipient's email preference is enabled, and the recipient has a primary email contact.","properties":{"subject":{"type":"string","description":"The email subject line. Required when an `email` block is present.","isRequired":true,"uniqueKey":"email.subject","typeLabel":"string"},"html":{"type":"string","description":"Rendered HTML body. At least one of `html` or `text` must be provided.","uniqueKey":"email.html","typeLabel":"string"},"text":{"type":"string","description":"Plain-text body. Recommended for deliverability. If omitted, it is derived from `html`.","uniqueKey":"email.text","typeLabel":"string"}},"required":["subject"],"uniqueKey":"email","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"recipient_id":"\u003cstring>","payload":{}}}},"exampleType":"generated"}},"exampleType":"generated"},"responses":{"08a9f562-0ad0-4097-b585-0b4a0bfd3ecd":{"description":"Notification response","content":{"application/json":{"examples":{"Direct notification":{"title":"Direct notification","value":{"message":"Direct notification queued for delivery to recipient recipient_123.","data":{"notification":{"id":42069,"recipient_id":"recipient_123","payload":{"title":"Elon, Zuck and others commented on your post.","post_url":"/posts/post_id_123#comments"},"broadcast_id":null,"target":{"channel":"posts","topic":"post_id_123","event":"new_comment"},"read":false,"opened":false,"created_at":"2025-11-07T05:31:56Z","updated_at":"2025-11-07T05:31:56Z","status":"enqueued"},"broadcast":null}}},"Broadcast notification":{"title":"Broadcast notification","value":{"message":"Broadcast notification sent successfully. It will be delivered to all elligible recipients.","data":{"notification":null,"broadcast":{"id":42069,"target":{"channel":"announcements","topic":"product","event":"new_feature"},"payload":{"title":"Big News!","message":"We just launched a new feature."},"created_at":"2025-08-11T20:54:25.039856+05:30","updated_at":"2025-08-11T20:54:25.039856+05:30"}}}},"Direct notification rejected":{"title":"Direct notification rejected","value":{"message":"No notification was sent. Recipient's preferences do not allow delivery.","data":{"notification":null,"broadcast":null}}},"Direct notification with email":{"title":"Direct notification with email","value":{"message":"Direct notification sent successfully to recipient recipient_123.","data":{"notification":{"id":42070,"recipient_id":"recipient_123","payload":{"title":"Your daily digest is ready."},"broadcast_id":null,"target":{"channel":"digest","topic":"none","event":"sent"},"read":false,"opened":false,"created_at":"2025-11-07T05:31:56Z","updated_at":"2025-11-07T05:31:56Z"},"broadcast":null,"deliveries":[{"medium":"email","status":"pending","address":"alice@example.com","created_at":"2025-11-07T05:31:56Z","updated_at":"2025-11-07T05:31:56Z"}]}}}},"exampleType":"examples"}},"exampleType":"examples"},"67fb64a3-f0b0-490e-8ed3-eb3ae7516009":{"description":"Invalid send. **Only projects with strict targets enabled** return the uncataloged-target error — with the setting off (the default) an uncataloged target is accepted. The check runs per medium, against the mediums the send actually asks for, and applies to direct sends and broadcasts alike. It tests for the **existence** of a catalog entry, not whether it is enabled: an entry with `default_enabled: false` is a real state (defined, switched off) and a send to it is accepted.","content":{"application/json":{"examples":{"TargetNotCataloged":{"title":"TargetNotCataloged","value":{"message":"Target billing/none/invoice_paid is not in this project's catalog for the in_app medium. Create a project preference for it before sending, or turn off strict targets in your project settings."}}},"exampleType":"examples"}},"exampleType":"examples"}},"security":{"6b8f63a1-3b44-49cf-9a5d-059162e75315":{"BearerAuthWithAPIKeyWithFullAcessScope":{"type":"http","scheme":"bearer","bearerFormat":"APIKey","description":"Bearer authentication header of the form `Bearer bv_xxxxxxxxx`, where `bv_xxxxxxxxx` is your **API Key** with `Full access` scope.","scopes":[]}}},"servers":{},"processedSecurityOptions":[{"schemes":[{"scheme":{"type":"http","scheme":"bearer","bearerFormat":"APIKey","description":"Bearer authentication header of the form `Bearer bv_xxxxxxxxx`, where `bv_xxxxxxxxx` is your **API Key** with `Full access` scope.","scopes":[]},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"Bearer authentication header of the form `Bearer bv_xxxxxxxxx`, where `bv_xxxxxxxxx` is your **API Key** with `Full access` scope.","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"BearerAuthWithAPIKeyWithFullAcessScope","name":"Authorization"}],"title":"BearerAuthWithAPIKeyWithFullAcessScope"}]},"originalFileLocation":"api-reference/openapi.json"},"subdomain":"mudgallabs","siteName":"Bodhveda","title":"Send notification - Bodhveda","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-08-01T19:24:01.707Z","originalFileLocation":"api-reference/endpoint/notifications/send-notification.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Start sending notifications through Bodhveda API. A send names an optional target (channel/topic/event) and carries a payload (in-app), an email block, or both. The mediums it asks for are the mediums it carries. ### Targets and your catalog By default you can send any target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients\u0026#x27; preference screen and lets email resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it. If the project has strict targets turned on (a per-project setting, off by default), this endpoint instead rejects a send whose (target, medium) has no matching catalog entry — see the 400 below. A topic: any catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no target at all is never checked.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Bodhveda\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Send notification - Bodhveda\">\u003cmeta data-mint-head property=\"og:description\" content=\"Start sending notifications through Bodhveda API. A send names an optional target (channel/topic/event) and carries a payload (in-app), an email block, or both. The mediums it asks for are the mediums it carries. ### Targets and your catalog By default you can send any target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients\u0026#x27; preference screen and lets email resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it. If the project has strict targets turned on (a per-project setting, off by default), this endpoint instead rejects a send whose (target, medium) has no matching catalog entry — see the 400 below. A topic: any catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no target at all is never checked.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Bodhveda\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://mudgallabs.mintlify.app/_mintlify/api/og/v2?division=Notifications\u0026amp;title=Send+notification\u0026amp;description=Start+sending+notifications+through+Bodhveda+API.%0A%0AA+send+names+an+optional+%60target%60+%28%60channel%60%2F%60topic%60%2F%60event%60%29+and+carries+a+%60payload%60+%28in-app%29%2C+an+%60email%60+bl\u0026amp;theme=615ce636f5c179f532b0b0a2\u0026amp;signature=e0629eae0822e50b664834827ce8dd1abec8bc5c329c0f7543ce2d0945a7d132\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Send notification - Bodhveda\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Start sending notifications through Bodhveda API. A send names an optional target (channel/topic/event) and carries a payload (in-app), an email block, or both. The mediums it asks for are the mediums it carries. ### Targets and your catalog By default you can send any target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients\u0026#x27; preference screen and lets email resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it. If the project has strict targets turned on (a per-project setting, off by default), this endpoint instead rejects a send whose (target, medium) has no matching catalog entry — see the 400 below. A topic: any catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no target at all is never checked.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://mudgallabs.mintlify.app/_mintlify/api/og/v2?division=Notifications\u0026amp;title=Send+notification\u0026amp;description=Start+sending+notifications+through+Bodhveda+API.%0A%0AA+send+names+an+optional+%60target%60+%28%60channel%60%2F%60topic%60%2F%60event%60%29+and+carries+a+%60payload%60+%28in-app%29%2C+an+%60email%60+bl\u0026amp;theme=615ce636f5c179f532b0b0a2\u0026amp;signature=e0629eae0822e50b664834827ce8dd1abec8bc5c329c0f7543ce2d0945a7d132\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/mudgallabs/5v1jVLXdcr6kuLJI/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Bodhveda\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#5A86C3\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/endpoint/notifications/send-notification.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.bodhveda.com/#organization\",\"name\":\"Bodhveda\",\"url\":\"https://docs.bodhveda.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/mudgallabs/uB9oPsFjNmYjQlol/logo/logo.svg?fit=max\u0026auto=format\u0026n=uB9oPsFjNmYjQlol\u0026q=85\u0026s=007119f0163f4b2f5617af39d7ccc782\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.bodhveda.com#website\",\"name\":\"Bodhveda\",\"url\":\"https://docs.bodhveda.com\",\"publisher\":{\"@id\":\"https://docs.bodhveda.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification#webpage\",\"url\":\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification\",\"name\":\"Send notification\",\"description\":\"Start sending notifications through Bodhveda API. A send names an optional target (channel/topic/event) and carries a payload (in-app), an email block, or both. The mediums it asks for are the mediums it carries. ### Targets and your catalog By default you can send any target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients' preference screen and lets email resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it. If the project has strict targets turned on (a per-project setting, off by default), this endpoint instead rejects a send whose (target, medium) has no matching catalog entry — see the 400 below. A topic: any catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no target at all is never checked.\",\"dateModified\":\"2026-08-01T19:24:01.707Z\",\"isPartOf\":{\"@id\":\"https://docs.bodhveda.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Bodhveda\",\"item\":\"https://docs.bodhveda.com\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Send notification\",\"item\":\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification#apireference\",\"headline\":\"Send notification\",\"name\":\"Send notification\",\"description\":\"Start sending notifications through Bodhveda API. A send names an optional target (channel/topic/event) and carries a payload (in-app), an email block, or both. The mediums it asks for are the mediums it carries. ### Targets and your catalog By default you can send any target, cataloged or not. Cataloging a target — creating a project preference for it — is what puts it on your recipients' preference screen and lets email resolve to deliverable; an uncataloged target still sends in-app, but no recipient can mute it. If the project has strict targets turned on (a per-project setting, off by default), this endpoint instead rejects a send whose (target, medium) has no matching catalog entry — see the 400 below. A topic: any catalog entry satisfies the check for every concrete topic beneath it, so one entry covers an unbounded set of runtime-generated topics; you never need one entry per resource id. A send carrying no target at all is never checked.\",\"url\":\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification\",\"mainEntityOfPage\":{\"@id\":\"https://docs.bodhveda.com/api-reference/endpoint/notifications/send-notification#webpage\"},\"dateModified\":\"2026-08-01T19:24:01.707Z\",\"publisher\":{\"@id\":\"https://docs.bodhveda.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.bodhveda.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Bodhveda\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Bodhveda","nav":{"global":{"anchors":[]},"tabs":[{"tab":"Documentation","pages":[{"title":"Documentation","href":"/docs/introduction"}]},{"tab":"API Reference","groups":[{"group":"API Reference","pages":[{"title":"Introduction","description":"Send notifications, manage recipients, their notifications and preferences.","href":"/api-reference/introduction"}]},{"group":"Notifications","pages":[{"title":"Send notification","openapi":"POST /notifications/send","href":"/api-reference/endpoint/notifications/send-notification"},{"title":"Retrieve notification","openapi":"GET /notifications/{notification_id}","description":"Retrieve a single notification by its id.\n\nSending is **asynchronous**: the [send endpoint](/api-reference/endpoint/notifications/send-notification) accepts the notification and returns its id with `status: \"enqueued\"`, then a worker resolves in-app delivery and — when the send included an `email` block — the email. Poll this endpoint to read the resolved in-app `status` and the email delivery outcome on `email` (whether it was `sent`, `delivered`, `bounced`, …).","href":"/api-reference/endpoint/notifications/get-notification"}]},{"group":"Recipients","pages":[{"title":"Create recipient","openapi":"POST /recipients","href":"/api-reference/endpoint/recipients/create-recipient"},{"title":"Batch create recipients","openapi":"POST /recipients/batch","href":"/api-reference/endpoint/recipients/batch-create-recipients"},{"title":"Retrieve recipient","openapi":"GET /recipients/{recipient_id}","href":"/api-reference/endpoint/recipients/retrieve-recipient"},{"title":"Update recipient","openapi":"PATCH /recipients/{recipient_id}","href":"/api-reference/endpoint/recipients/update-recipient"},{"title":"Delete recipient","openapi":"DELETE /recipients/{recipient_id}","href":"/api-reference/endpoint/recipients/delete-recipient"},{"group":"Notifications","pages":[{"title":"List notifications","openapi":"GET /recipients/{recipient_id}/notifications","href":"/api-reference/endpoint/recipients/notifications/list-notifications"},{"title":"Unread count","openapi":"GET /recipients/{recipient_id}/notifications/unread-count","href":"/api-reference/endpoint/recipients/notifications/unread-count"},{"title":"Update notifications state","openapi":"PATCH /recipients/{recipient_id}/notifications","href":"/api-reference/endpoint/recipients/notifications/update-state"},{"title":"Delete notifications","openapi":"DELETE /recipients/{recipient_id}/notifications","href":"/api-reference/endpoint/recipients/notifications/delete"}]},{"group":"Preferences","pages":[{"title":"List preferences","openapi":"GET /recipients/{recipient_id}/preferences","href":"/api-reference/endpoint/recipients/preferences/list-preferences"},{"title":"Set preference","openapi":"PATCH /recipients/{recipient_id}/preferences","href":"/api-reference/endpoint/recipients/preferences/set-preference"},{"title":"Check preference","openapi":"GET /recipients/{recipient_id}/preferences/check","href":"/api-reference/endpoint/recipients/preferences/check-preference"}]},{"group":"Contacts","pages":[{"title":"List contacts","openapi":"GET /recipients/{recipient_id}/contacts","href":"/api-reference/endpoint/recipients/contacts/list-contacts"},{"title":"Add contact","openapi":"POST /recipients/{recipient_id}/contacts","href":"/api-reference/endpoint/recipients/contacts/create-contact"},{"title":"Set the primary contact","openapi":"PUT /recipients/{recipient_id}/contacts","href":"/api-reference/endpoint/recipients/contacts/set-primary-contact"},{"title":"Update contact","openapi":"PATCH /recipients/{recipient_id}/contacts/{contact_id}","href":"/api-reference/endpoint/recipients/contacts/update-contact"},{"title":"Delete contact","openapi":"DELETE /recipients/{recipient_id}/contacts/{contact_id}","href":"/api-reference/endpoint/recipients/contacts/delete-contact"}]}]},{"group":"Project Preferences","pages":[{"title":"List the catalog","openapi":"GET /preferences","href":"/api-reference/endpoint/preferences/list-project-preferences"},{"title":"Create a catalog entry","openapi":"POST /preferences","href":"/api-reference/endpoint/preferences/create-project-preference"},{"title":"Set up the catalog (bulk)","openapi":"PUT /preferences","href":"/api-reference/endpoint/preferences/upsert-project-preferences"},{"title":"Get a catalog entry","openapi":"GET /preferences/{preference_id}","href":"/api-reference/endpoint/preferences/get-project-preference"},{"title":"Update a catalog entry","openapi":"PATCH /preferences/{preference_id}","href":"/api-reference/endpoint/preferences/update-project-preference"},{"title":"Delete a catalog entry","openapi":"DELETE /preferences/{preference_id}","href":"/api-reference/endpoint/preferences/delete-project-preference"}]}]}]},"title":"Send notification - Bodhveda","buildId":"dpl_EnxDqhjaNc9a8MVa833TGULPd9uJ","deploymentId":"dpl_EnxDqhjaNc9a8MVa833TGULPd9uJ","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-26-lDe7V.js","utils-CWQmgYtH.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-4-Aw3rSD.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-C2zjXYA5.js","snippetPresets-DFmpFA7C.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-FJpQavWq.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-D_GvPF7k.js","shallow-Bs2ZB0NO.js","getApiPlaygroundDisplay-DCcqJJk-.js","CodeSnippet-DzDxawVe.js","CodeSnippets-aUr7fU-7.js","Page-CT9uE8Ip.js","getPastedJson-Y1fKsj_I.js","icon-DI4zTsDr.js","MethodPill-C0nDlmSQ.js","DuplicateIcon-NUXbx2nQ.js","createIcon-BdTHrPAi.js","DynamicMarkdown-CtpgKJwj.js","SectionHeading-B3x35tOt.js","axios-vUo_bRtp.js","Markdown-3OmurJMP.js","clsx-DMKzROTp.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-1ms6tsT-.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-Ke9xIMFx.js"]}