{
    "openapi": "3.0.3",
    "info": {
        "title": "Bodhveda Notifications API",
        "version": "1.0",
        "description": "API for sending notifications to recipients or broadcasting to all."
    },
    "paths": {
        "/notifications/send": {
            "post": {
                "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": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/SendNotificationPayload"
                            }
                        }
                    }
                },
                "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\": \"<h1>Your daily digest</h1><p>3 new follow-ups today.</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, &bodhveda.SendNotificationRequest{\n    RecipientID: &recipientID,\n    Target:      &bodhveda.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": {
                        "description": "Notification response",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Direct notification": {
                                        "summary": "Direct Notification Response",
                                        "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": {
                                        "summary": "Broadcast Notification Response",
                                        "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": {
                                        "summary": "Rejected Notification Based on Preferences",
                                        "value": {
                                            "message": "No notification was sent. Recipient's preferences do not allow delivery.",
                                            "data": {
                                                "notification": null,
                                                "broadcast": null
                                            }
                                        }
                                    },
                                    "Direct notification with email": {
                                        "summary": "Direct Notification Fanned Out to Inbox + 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"
                                                    }
                                                ]
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "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": {
                                        "summary": "Target not in the project's catalog (strict targets on)",
                                        "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."
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ]
            }
        },
        "/notifications/{notification_id}": {
            "get": {
                "summary": "Retrieve a notification.",
                "operationId": "getNotification",
                "tags": [
                    "Notifications"
                ],
                "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`, …).",
                "parameters": [
                    {
                        "name": "notification_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "The unique identifier of the notification (the `id` returned by the send endpoint)."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Notification found",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "In-app only": {
                                        "summary": "Direct send, in-app only (delivered)",
                                        "value": {
                                            "data": {
                                                "id": 42069,
                                                "recipient_id": "recipient_123",
                                                "payload": {
                                                    "title": "Elon, Zuck and others commented on your post."
                                                },
                                                "broadcast_id": null,
                                                "target": {
                                                    "channel": "posts",
                                                    "topic": "post_id_123",
                                                    "event": "new_comment"
                                                },
                                                "read": false,
                                                "opened": false,
                                                "status": "delivered",
                                                "completed_at": "2025-11-07T05:31:57Z",
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T05:31:57Z"
                                            }
                                        }
                                    },
                                    "With email": {
                                        "summary": "Direct send with email (delivered)",
                                        "value": {
                                            "data": {
                                                "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,
                                                "status": "delivered",
                                                "completed_at": "2025-11-07T05:31:57Z",
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T05:31:57Z",
                                                "email": {
                                                    "status": "delivered",
                                                    "attempt": 1,
                                                    "provider": "resend",
                                                    "provider_message_id": "re_abc123",
                                                    "address_snapshot": "john@example.com",
                                                    "sent_at": "2025-11-07T05:31:57Z",
                                                    "delivered_at": "2025-11-07T05:32:03Z"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Notification not found",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Not found": {
                                        "summary": "Notification Not Found",
                                        "value": {
                                            "message": "notification not found"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl https://api.bodhveda.com/notifications/42069 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst notification = await bodhveda.notifications.get(42069);\n\n// notification.status      -> \"delivered\" | \"muted\" | \"quota_exceeded\" | ...\n// notification.email?.status -> \"sent\" | \"delivered\" | \"bounced\" | ..."
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nnotification, _ := client.Notifications.Get(ctx, 42069)\n\n// notification.Status -> \"delivered\" | \"muted\" | ...\n// notification.Email  -> nil unless the send included an email block"
                    }
                ]
            }
        },
        "/recipients": {
            "post": {
                "summary": "Create a recipient",
                "operationId": "createRecipient",
                "tags": [
                    "Recipients"
                ],
                "description": "Create a new recipient.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateRecipientPayload"
                            }
                        }
                    }
                },
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X POST https://api.bodhveda.com/recipients \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"id\": \"recipient_123\",\n    \"name\": \"Alice Johnson\"\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst recipient = await bodhveda.recipients.create({\n    id: \"recipient_123\",\n    name: \"Alice Johnson\",\n});"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nname := \"Alice Johnson\"\nrecipient, _ := client.Recipients.Create(ctx, &bodhveda.CreateRecipientRequest{\n    ID:   \"recipient_123\",\n    Name: &name,\n})"
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Recipient created successfully",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Recipient created": {
                                        "summary": "Recipient Created Response",
                                        "value": {
                                            "message": "Recipient created",
                                            "data": {
                                                "id": "recipient_123",
                                                "name": "John Doe",
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T05:31:56Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Recipient already exists",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Conflict": {
                                        "summary": "Recipient Already Exists",
                                        "value": {
                                            "message": "Recipient already exists"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ]
            }
        },
        "/recipients/batch": {
            "post": {
                "summary": "Create up to 100 recipients at once.",
                "operationId": "batchCreateRecipients",
                "tags": [
                    "Recipients"
                ],
                "description": "Create up to 100 recipients at once.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "recipients": {
                                        "type": "array",
                                        "items": {
                                            "$ref": "#/components/schemas/CreateRecipientPayload"
                                        }
                                    }
                                },
                                "required": [
                                    "recipients"
                                ]
                            }
                        }
                    }
                },
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X POST https://api.bodhveda.com/recipients/batch \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"recipients\": [\n      { \"id\": \"recipient_123\", \"name\": \"Alice Johnson\" },\n      { \"id\": \"recipient_456\", \"name\": \"Bob Smith\" }\n    ]\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst res = await bodhveda.recipients.createBatch({\n    recipients: [\n        { id: \"recipient_123\", name: \"Alice Johnson\" },\n        { id: \"recipient_456\", name: \"Bob Smith\" },\n    ],\n});\n// res.created, res.updated, res.failed"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nalice, bob := \"Alice Johnson\", \"Bob Smith\"\nres, _ := client.Recipients.CreateBatch(ctx, &bodhveda.CreateRecipientsBatchRequest{\n    Recipients: []bodhveda.CreateRecipientRequest{\n        {ID: \"recipient_123\", Name: &alice},\n        {ID: \"recipient_456\", Name: &bob},\n    },\n})\n// res.Created, res.Updated, res.Failed"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Batch create recipients result",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Batch create result": {
                                        "summary": "Batch Create Recipients Response",
                                        "value": {
                                            "data": {
                                                "created": [
                                                    {
                                                        "id": "recipient_123"
                                                    }
                                                ],
                                                "updated": [
                                                    {
                                                        "id": "recipient_456"
                                                    }
                                                ],
                                                "failed": [
                                                    {
                                                        "id": "",
                                                        "batch_index": 2,
                                                        "errors": [
                                                            {
                                                                "message": "ID is required",
                                                                "description": "ID cannot be empty",
                                                                "property_path": "id",
                                                                "invalid_value": ""
                                                            }
                                                        ]
                                                    }
                                                ]
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ]
            }
        },
        "/recipients/{recipient_id}": {
            "get": {
                "summary": "Retrieve a recipient.",
                "operationId": "getRecipient",
                "tags": [
                    "Recipients"
                ],
                "description": "Retrieve a recipient.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Recipient found",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Recipient found": {
                                        "summary": "Recipient Found",
                                        "value": {
                                            "data": {
                                                "id": "recipient_123",
                                                "name": "John Doe",
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T05:31:56Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Recipient not found",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Not found": {
                                        "summary": "Recipient Not Found",
                                        "value": {
                                            "message": "Recipient not found"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl https://api.bodhveda.com/recipients/recipient_123 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst recipient = await bodhveda.recipients.get(\"recipient_123\");"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nrecipient, _ := client.Recipients.Get(ctx, \"recipient_123\")"
                    }
                ]
            },
            "patch": {
                "summary": "Update a recipient",
                "operationId": "updateRecipient",
                "tags": [
                    "Recipients"
                ],
                "description": "Update the name of a recipient.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X PATCH https://api.bodhveda.com/recipients/recipient_123 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Elon Musk\"\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst recipient = await bodhveda.recipients.update(\"recipient_123\", {\n    name: \"Elon Musk\",\n});"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nname := \"Elon Musk\"\nrecipient, _ := client.Recipients.Update(ctx, \"recipient_123\", &bodhveda.UpdateRecipientRequest{\n    Name: &name,\n})"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "Name of the recipient."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Recipient updated",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Recipient updated": {
                                        "summary": "Recipient Updated",
                                        "value": {
                                            "message": "Recipient updated",
                                            "data": {
                                                "id": "recipient_123",
                                                "name": "Elon Musk",
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T06:00:00Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Recipient not found",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Not found": {
                                        "summary": "Recipient Not Found",
                                        "value": {
                                            "message": "Recipient not found"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ]
            },
            "delete": {
                "summary": "Delete a recipient",
                "operationId": "deleteRecipient",
                "tags": [
                    "Recipients"
                ],
                "description": "Remove an existing recipient.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique external ID of the recipient."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Recipient deleted",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Recipient deleted": {
                                        "summary": "Recipient Deleted",
                                        "value": {
                                            "message": "Recipient deleted"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Recipient not found",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Not found": {
                                        "summary": "Recipient Not Found",
                                        "value": {
                                            "message": "Recipient not found"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X DELETE https://api.bodhveda.com/recipients/recipient_123 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nawait bodhveda.recipients.delete(\"recipient_123\");"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nerr := client.Recipients.Delete(ctx, \"recipient_123\")"
                    }
                ]
            }
        },
        "/recipients/{recipient_id}/contacts": {
            "get": {
                "summary": "List a recipient's contacts",
                "operationId": "listRecipientContacts",
                "tags": [
                    "Contacts"
                ],
                "description": "List all of a recipient's per-medium contact addresses.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "List of the recipient's contacts.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Contacts": {
                                        "summary": "Contacts Response",
                                        "value": {
                                            "data": {
                                                "contacts": [
                                                    {
                                                        "id": 1,
                                                        "medium": "email",
                                                        "address": "alice@example.com",
                                                        "is_primary": true,
                                                        "verified_at": null,
                                                        "created_at": "2025-11-07T05:31:56Z",
                                                        "updated_at": "2025-11-07T05:31:56Z"
                                                    }
                                                ]
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl https://api.bodhveda.com/recipients/recipient_123/contacts \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst { contacts } = await bodhveda.recipients.contacts.list(\"recipient_123\");"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nres, _ := client.Recipients.Contacts.List(ctx, \"recipient_123\")\n// res.Contacts"
                    }
                ]
            },
            "post": {
                "summary": "Add a contact to a recipient",
                "operationId": "createRecipientContact",
                "tags": [
                    "Contacts"
                ],
                "description": "Register a per-medium contact address for a recipient. Email needs a **primary** email contact to receive email. A duplicate `(medium, address)`, or a second primary for the same `(recipient, medium)`, returns `409`.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateRecipientContactPayload"
                            }
                        }
                    }
                },
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X POST https://api.bodhveda.com/recipients/recipient_123/contacts \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"medium\": \"email\",\n    \"address\": \"alice@example.com\",\n    \"is_primary\": true\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst contact = await bodhveda.recipients.contacts.create(\"recipient_123\", {\n    medium: \"email\",\n    address: \"alice@example.com\",\n    is_primary: true,\n});"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\ncontact, _ := client.Recipients.Contacts.Create(ctx, \"recipient_123\", &bodhveda.CreateRecipientContactRequest{\n    Medium:    bodhveda.MediumEmail,\n    Address:   \"alice@example.com\",\n    IsPrimary: true,\n})"
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Contact created",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Contact created": {
                                        "summary": "Contact Created Response",
                                        "value": {
                                            "message": "Contact created",
                                            "data": {
                                                "id": 1,
                                                "medium": "email",
                                                "address": "alice@example.com",
                                                "is_primary": true,
                                                "verified_at": null,
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T05:31:56Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "A contact with this address, or a primary contact for this medium, already exists.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Conflict": {
                                        "summary": "Contact Conflict",
                                        "value": {
                                            "message": "Contact already exists"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ]
            },
            "put": {
                "summary": "Set the primary contact",
                "operationId": "setPrimaryRecipientContact",
                "tags": [
                    "Contacts"
                ],
                "description": "Idempotently ensure an address is the recipient's **primary** contact for a medium — create it if absent, update the existing primary in place if the address differs (which resets `verified_at`), or no-op if it already matches. Returns the resulting primary contact (`200`) either way, so a \"keep the primary email current\" sync is a single call.\n\n💡 Sync this **server-side** so the address never rides a browser request.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/SetPrimaryContactPayload"
                            }
                        }
                    }
                },
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X PUT https://api.bodhveda.com/recipients/recipient_123/contacts \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"medium\": \"email\",\n    \"address\": \"alice@example.com\"\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst contact = await bodhveda.recipients.contacts.setPrimary(\"recipient_123\", {\n    medium: \"email\",\n    address: \"alice@example.com\",\n});"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\ncontact, _ := client.Recipients.Contacts.SetPrimary(ctx, \"recipient_123\", &bodhveda.SetPrimaryContactRequest{\n    Medium:  bodhveda.MediumEmail,\n    Address: \"alice@example.com\",\n})"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The resulting primary contact.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Primary set": {
                                        "summary": "Primary Contact Response",
                                        "value": {
                                            "message": "Primary contact set",
                                            "data": {
                                                "id": 1,
                                                "medium": "email",
                                                "address": "alice@example.com",
                                                "is_primary": true,
                                                "verified_at": null,
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T05:31:56Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "The target address is already a different contact for this recipient and medium.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ]
            }
        },
        "/recipients/{recipient_id}/contacts/{contact_id}": {
            "patch": {
                "summary": "Update a recipient's contact",
                "operationId": "updateRecipientContact",
                "tags": [
                    "Contacts"
                ],
                "description": "Update a contact's address and/or primary flag. Provide at least one field. Changing the address resets the contact's verification.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    },
                    {
                        "name": "contact_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "The unique identifier of the contact."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/UpdateRecipientContactPayload"
                            }
                        }
                    }
                },
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X PATCH https://api.bodhveda.com/recipients/recipient_123/contacts/1 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"address\": \"alice.new@example.com\"\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst contact = await bodhveda.recipients.contacts.update(\"recipient_123\", 1, {\n    address: \"alice.new@example.com\",\n});"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\naddress := \"alice.new@example.com\"\ncontact, _ := client.Recipients.Contacts.Update(ctx, \"recipient_123\", 1, &bodhveda.UpdateRecipientContactRequest{\n    Address: &address,\n})"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Contact updated",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Contact updated": {
                                        "summary": "Contact Updated Response",
                                        "value": {
                                            "message": "Contact updated",
                                            "data": {
                                                "id": 1,
                                                "medium": "email",
                                                "address": "alice.new@example.com",
                                                "is_primary": true,
                                                "verified_at": null,
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T06:00:00Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ]
            },
            "delete": {
                "summary": "Delete a recipient's contact",
                "operationId": "deleteRecipientContact",
                "tags": [
                    "Contacts"
                ],
                "description": "Delete a recipient's contact. Requires an API key with **Full access** scope.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    },
                    {
                        "name": "contact_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "The unique identifier of the contact."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Contact deleted",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Contact deleted": {
                                        "summary": "Contact Deleted Response",
                                        "value": {
                                            "message": "Contact deleted"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X DELETE https://api.bodhveda.com/recipients/recipient_123/contacts/1 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nawait bodhveda.recipients.contacts.delete(\"recipient_123\", 1);"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nerr := client.Recipients.Contacts.Delete(ctx, \"recipient_123\", 1)"
                    }
                ]
            }
        },
        "/recipients/{recipient_id}/preferences": {
            "get": {
                "summary": "List preferences for a recipient",
                "operationId": "listRecipientPreferences",
                "tags": [
                    "Preferences"
                ],
                "description": "Every `(target, medium)` known for this recipient, each with the state a send would **actually** act on.\n\n`state.enabled` is the **resolved** decision — the same answer the delivery path computes — not a raw stored row. Resolution walks, in order:\n\n1. the recipient's rule for the exact target,\n2. the recipient's `topic: any` rule for the channel/event,\n3. the project's rule for the exact target,\n4. the project's `topic: any` rule,\n5. the medium's **default**.\n\n**The default is per medium:** `in_app` delivers, every other medium does not. A `topic: none` target never takes an `any` rule.\n\nThe response covers every target in the project catalog **plus** any target this recipient has a rule of their own for, across the active mediums (`in_app` and `email`). So a recipient can have an entry for a `(target, medium)` you never cataloged — if they have an explicit rule for it, it is live, and this reports it.\n\nEach `state` carries:\n\n-   `enabled` — whether a send to this `(target, medium)` would deliver.\n-   `inherited` — `true` when the recipient has no rule of their own for this exact `(target, medium)`; the value came from elsewhere in the cascade. `false` means they set it explicitly.\n-   `cataloged` — whether a project-level rule exists for this exact `(target, medium)`. This is **context, not a gate**: an explicit recipient rule on an uncataloged pair still delivers, and `in_app` delivers by default with no catalog entry at all. Use it to decide what to render, never to predict delivery — `enabled` is the answer.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "List of recipient's resolved preferences.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Preferences": {
                                        "summary": "Preferences Response",
                                        "value": {
                                            "data": {
                                                "preferences": [
                                                    {
                                                        "target": {
                                                            "channel": "posts",
                                                            "topic": "any",
                                                            "event": "new_comment",
                                                            "medium": "in_app",
                                                            "name": "Comments on your posts"
                                                        },
                                                        "state": {
                                                            "enabled": true,
                                                            "inherited": true,
                                                            "cataloged": true,
                                                            "mandatory": false
                                                        }
                                                    },
                                                    {
                                                        "target": {
                                                            "channel": "announcements",
                                                            "topic": "none",
                                                            "event": "new_feature",
                                                            "medium": "email",
                                                            "name": "Product Announcements"
                                                        },
                                                        "state": {
                                                            "enabled": false,
                                                            "inherited": false,
                                                            "cataloged": true,
                                                            "mandatory": false
                                                        }
                                                    },
                                                    {
                                                        "target": {
                                                            "channel": "announcements",
                                                            "topic": "none",
                                                            "event": "new_feature",
                                                            "medium": "in_app"
                                                        },
                                                        "state": {
                                                            "enabled": true,
                                                            "inherited": true,
                                                            "cataloged": false,
                                                            "mandatory": false
                                                        }
                                                    }
                                                ]
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl https://api.bodhveda.com/recipients/recipient_123/preferences \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst { preferences } = await bodhveda.recipients.preferences.list(\"recipient_123\");"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nres, _ := client.Recipients.Preferences.List(ctx, \"recipient_123\")\n// res.Preferences"
                    },
                    {
                        "lang": "jsx",
                        "label": "React",
                        "source": "import { usePreferences } from \"@bodhveda/react\";\n\n// Requires a <BodhvedaProvider recipientID=\"recipient_123\"> higher in the tree.\nfunction Preferences() {\n    const { data, isLoading } = usePreferences();\n    if (isLoading) return null;\n    return <PreferenceList items={data.preferences} />;\n}"
                    }
                ]
            },
            "patch": {
                "summary": "Set recipient's subscription state for a target",
                "operationId": "setRecipientPreference",
                "tags": [
                    "Preferences"
                ],
                "description": "Set a recipient's subscription state for a specific target.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "target": {
                                        "$ref": "#/components/schemas/Target"
                                    },
                                    "medium": {
                                        "type": "string",
                                        "description": "The medium this preference applies to. In-app and email are toggled independently for the same target. Defaults to `in_app` when omitted.",
                                        "enum": [
                                            "in_app",
                                            "email"
                                        ],
                                        "default": "in_app"
                                    },
                                    "state": {
                                        "type": "object",
                                        "properties": {
                                            "enabled": {
                                                "type": "boolean",
                                                "description": "Whether the recipient is subscribed to this target for this medium."
                                            }
                                        },
                                        "required": [
                                            "enabled"
                                        ]
                                    }
                                },
                                "required": [
                                    "target",
                                    "state"
                                ]
                            }
                        }
                    }
                },
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X PATCH https://api.bodhveda.com/recipients/recipient_123/preferences \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"target\": {\n      \"channel\": \"posts\",\n      \"topic\": \"post_id_123\",\n      \"event\": \"new_comment\"\n    },\n    \"medium\": \"email\",\n    \"state\": {\n      \"enabled\": false\n    }\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst pref = await bodhveda.recipients.preferences.set(\"recipient_123\", {\n    target: { channel: \"posts\", topic: \"post_id_123\", event: \"new_comment\" },\n    medium: \"email\",\n    state: { enabled: false },\n});"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nreq := &bodhveda.SetPreferenceRequest{\n    Target: bodhveda.Target{Channel: \"posts\", Topic: \"post_id_123\", Event: \"new_comment\"},\n    Medium: bodhveda.MediumEmail,\n}\nreq.State.Enabled = false\npref, _ := client.Recipients.Preferences.Set(ctx, \"recipient_123\", req)"
                    },
                    {
                        "lang": "jsx",
                        "label": "React",
                        "source": "import { useUpdatePreference } from \"@bodhveda/react\";\n\n// Requires a <BodhvedaProvider recipientID=\"recipient_123\"> higher in the tree.\nfunction Toggle() {\n    const { mutate } = useUpdatePreference();\n    return (\n        <button\n            onClick={() =>\n                mutate({\n                    target: { channel: \"posts\", topic: \"post_id_123\", event: \"new_comment\" },\n                    medium: \"email\",\n                    state: { enabled: false },\n                })\n            }\n        >\n            Unsubscribe\n        </button>\n    );\n}"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Updated subscription state for the target",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Updated Target Subscription State": {
                                        "summary": "Updated Target Subscription State Response",
                                        "value": {
                                            "data": {
                                                "target": {
                                                    "channel": "posts",
                                                    "topic": "post_id_123",
                                                    "event": "new_comment",
                                                    "medium": "email"
                                                },
                                                "state": {
                                                    "enabled": false,
                                                    "inherited": false
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ]
            }
        },
        "/recipients/{recipient_id}/preferences/check": {
            "get": {
                "summary": "Check recipient's subscription state for a target",
                "operationId": "checkRecipientTargetSubscription",
                "tags": [
                    "Preferences"
                ],
                "description": "Resolve a single `(target, medium)` for a recipient: whether a send would **actually** deliver.\n\n`state.enabled` is the resolved decision, computed by the same cascade the delivery path uses — the recipient's exact rule → the recipient's `topic: any` rule → the project's exact rule → the project's `topic: any` rule → the medium's default (`in_app` delivers, every other medium does not). A `topic: none` target never takes an `any` rule.\n\nThe target does **not** need to be in your catalog; any `(channel, topic, event)` resolves. `state.cataloged` reports whether a project-level rule exists for this exact `(target, medium)` — context, not a gate.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    },
                    {
                        "name": "channel",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Channel to check."
                    },
                    {
                        "name": "topic",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Topic to check."
                    },
                    {
                        "name": "event",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Event to check."
                    },
                    {
                        "name": "medium",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "in_app",
                                "email"
                            ],
                            "default": "in_app"
                        },
                        "description": "Which medium to check the subscription state for. Defaults to `in_app`."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Subscription state for the target",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Target Subscription State": {
                                        "summary": "Target Subscription State Response",
                                        "value": {
                                            "data": {
                                                "target": {
                                                    "channel": "posts",
                                                    "topic": "post_id_123",
                                                    "event": "new_comment",
                                                    "medium": "email"
                                                },
                                                "state": {
                                                    "enabled": true,
                                                    "inherited": false,
                                                    "cataloged": false,
                                                    "mandatory": false
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -G https://api.bodhveda.com/recipients/recipient_123/preferences/check \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  --data-urlencode \"channel=posts\" \\\n  --data-urlencode \"topic=post_id_123\" \\\n  --data-urlencode \"event=new_comment\" \\\n  --data-urlencode \"medium=email\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst res = await bodhveda.recipients.preferences.check(\"recipient_123\", {\n    target: { channel: \"posts\", topic: \"post_id_123\", event: \"new_comment\" },\n    medium: \"email\",\n});\n// res.state.enabled"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nres, _ := client.Recipients.Preferences.Check(ctx, \"recipient_123\", &bodhveda.CheckPreferenceRequest{\n    Target: bodhveda.Target{Channel: \"posts\", Topic: \"post_id_123\", Event: \"new_comment\"},\n    Medium: bodhveda.MediumEmail,\n})\n// res.State.Enabled"
                    },
                    {
                        "lang": "jsx",
                        "label": "React",
                        "source": "import { useCheckPreference } from \"@bodhveda/react\";\n\n// Requires a <BodhvedaProvider recipientID=\"recipient_123\"> higher in the tree.\nfunction Subscribed() {\n    const { data } = useCheckPreference({\n        channel: \"posts\",\n        topic: \"post_id_123\",\n        event: \"new_comment\",\n    });\n    return <span>{data?.state.enabled ? \"Subscribed\" : \"Muted\"}</span>;\n}"
                    }
                ]
            }
        },
        "/recipients/{recipient_id}/notifications": {
            "get": {
                "summary": "List notifications for a recipient",
                "operationId": "listRecipientNotifications",
                "tags": [
                    "Notifications"
                ],
                "description": "Retrieve a paginated list of notifications for a recipient, ordered by most recent first.\n\nThis is the **recipient-facing** feed, so it excludes notifications the recipient was never shown: `muted` (their preferences disallowed it), `quota_exceeded`, and `not_requested` (an *email-only* send, which created no in-app notification). To see those, use the Console — they are exactly the rows you need when asking “why didn't they get it?”.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    },
                    {
                        "name": "before",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Returns results before the specified cursor. Use this to paginate backwards or fetch earlier items in the list."
                    },
                    {
                        "name": "after",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Returns results after the specified cursor. Use this to paginate forwards or fetch newer items in the list."
                    },
                    {
                        "name": "limit",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "default": 10,
                            "maximum": 100
                        },
                        "description": "Maximum number of notifications to return."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "List of notifications for the recipient.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Notifications list": {
                                        "summary": "Notifications List Response",
                                        "value": {
                                            "data": {
                                                "notifications": [
                                                    {
                                                        "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"
                                                    }
                                                ],
                                                "cursor": {
                                                    "before": "42069",
                                                    "after": "42069"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -G https://api.bodhveda.com/recipients/recipient_123/notifications \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  --data-urlencode \"limit=10\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst res = await bodhveda.recipients.notifications.list(\"recipient_123\", {\n    limit: 10,\n});\n// res.notifications, res.cursor"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nlimit := 10\nres, _ := client.Recipients.Notifications.List(ctx, \"recipient_123\", &bodhveda.ListNotificationsRequest{\n    Limit: &limit,\n})\n// res.Notifications, res.Cursor"
                    },
                    {
                        "lang": "jsx",
                        "label": "React",
                        "source": "import { useNotifications } from \"@bodhveda/react\";\n\n// Requires a <BodhvedaProvider recipientID=\"recipient_123\"> higher in the tree.\nfunction Inbox() {\n    const { data, fetchNextPage, hasNextPage } = useNotifications({ limit: 10 });\n    return (\n        <ul>\n            {data?.notifications.map((n) => (\n                <li key={n.id}>{JSON.stringify(n.payload)}</li>\n            ))}\n        </ul>\n    );\n}"
                    }
                ]
            },
            "patch": {
                "summary": "Update notifications state",
                "description": "Updates the state of one or more notifications for the specified recipient.\n\nOnly notifications visible in the recipient's feed are updated — `muted`, `quota_exceeded`, and `not_requested` notifications are skipped, and the returned count reflects only the rows actually changed.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Unique external identifier for the recipient."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "ids": {
                                        "type": "array",
                                        "description": "Array of notification IDs to update. If omitted, applies to all.",
                                        "items": {
                                            "type": "integer"
                                        }
                                    },
                                    "state": {
                                        "type": "object",
                                        "description": "The notification state to update. \n- You can set one or multiple state fields in the same request. \n- Fields left out of `state` will remain unchanged.",
                                        "properties": {
                                            "read": {
                                                "type": "boolean",
                                                "description": "Whether the notification is marked as read."
                                            },
                                            "opened": {
                                                "type": "boolean",
                                                "description": "Whether the notification is marked as opened."
                                            }
                                        },
                                        "example": {
                                            "read": true
                                        }
                                    }
                                },
                                "required": [
                                    "state"
                                ]
                            },
                            "example": {
                                "ids": [
                                    123,
                                    456
                                ],
                                "state": {
                                    "read": true
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Successfully updated notifications",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "updated_count": {
                                            "type": "integer",
                                            "description": "Number of notifications updated."
                                        }
                                    },
                                    "example": {
                                        "updated_count": 2
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X PATCH https://api.bodhveda.com/recipients/recipient_123/notifications \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"ids\": [123, 456],\n    \"state\": { \"read\": true }\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst res = await bodhveda.recipients.notifications.updateState(\"recipient_123\", {\n    ids: [123, 456],\n    state: { read: true },\n});\n// res.updated_count"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nread := true\nres, _ := client.Recipients.Notifications.UpdateState(ctx, \"recipient_123\", &bodhveda.UpdateNotificationsStateRequest{\n    IDs:   []int{123, 456},\n    State: bodhveda.NotificationStateOptional{Read: &read},\n})\n// res.UpdatedCount"
                    },
                    {
                        "lang": "jsx",
                        "label": "React",
                        "source": "import { useUpdateNotificationsState } from \"@bodhveda/react\";\n\n// Requires a <BodhvedaProvider recipientID=\"recipient_123\"> higher in the tree.\nfunction MarkRead({ ids }: { ids: number[] }) {\n    const { mutate } = useUpdateNotificationsState();\n    return <button onClick={() => mutate({ ids, state: { read: true } })}>Mark read</button>;\n}"
                    }
                ]
            },
            "delete": {
                "summary": "Delete notifications",
                "description": "Deletes one or more notifications for the specified recipient.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Unique external identifier for the recipient."
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "ids": {
                                        "type": "array",
                                        "description": "Array of notification IDs to delete. If omitted, all will be deleted.",
                                        "items": {
                                            "type": "integer"
                                        }
                                    }
                                }
                            },
                            "example": {
                                "ids": [
                                    123,
                                    456
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Successfully deleted notifications",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "deleted_count": {
                                            "type": "integer",
                                            "description": "Number of notifications deleted."
                                        }
                                    },
                                    "example": {
                                        "deleted_count": 2
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X DELETE https://api.bodhveda.com/recipients/recipient_123/notifications \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"ids\": [123, 456]\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst res = await bodhveda.recipients.notifications.delete(\"recipient_123\", {\n    ids: [123, 456],\n});\n// res.deleted_count"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nres, _ := client.Recipients.Notifications.Delete(ctx, \"recipient_123\", &bodhveda.DeleteNotificationsRequest{\n    IDs: []int{123, 456},\n})\n// res.DeletedCount"
                    },
                    {
                        "lang": "jsx",
                        "label": "React",
                        "source": "import { useDeleteNotifications } from \"@bodhveda/react\";\n\n// Requires a <BodhvedaProvider recipientID=\"recipient_123\"> higher in the tree.\nfunction Delete({ ids }: { ids: number[] }) {\n    const { mutate } = useDeleteNotifications();\n    return <button onClick={() => mutate({ ids })}>Delete</button>;\n}"
                    }
                ]
            }
        },
        "/recipients/{recipient_id}/notifications/unread-count": {
            "get": {
                "summary": "Get unread notification count for a recipient",
                "operationId": "getUnreadNotificationCount",
                "tags": [
                    "Notifications"
                ],
                "description": "Returns the number of unread notifications for a recipient.\n\nThis is the **recipient-facing** feed, so it excludes notifications the recipient was never shown: `muted` (their preferences disallowed it), `quota_exceeded`, and `not_requested` (an *email-only* send, which created no in-app notification). To see those, use the Console — they are exactly the rows you need when asking “why didn't they get it?”.",
                "parameters": [
                    {
                        "name": "recipient_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The unique identifier of the recipient."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Unread notification count for the recipient.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Unread count": {
                                        "summary": "Unread Notification Count",
                                        "value": {
                                            "data": {
                                                "unread_count": 5
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Recipient not found",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Not found": {
                                        "summary": "Recipient Not Found",
                                        "value": {
                                            "message": "Recipient not found"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithEitherScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl https://api.bodhveda.com/recipients/recipient_123/notifications/unread-count \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst res = await bodhveda.recipients.notifications.unreadCount(\"recipient_123\");\n// res.unread_count"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nres, _ := client.Recipients.Notifications.UnreadCount(ctx, \"recipient_123\")\n// res.UnreadCount"
                    },
                    {
                        "lang": "jsx",
                        "label": "React",
                        "source": "import { useNotificationsUnreadCount } from \"@bodhveda/react\";\n\n// Requires a <BodhvedaProvider recipientID=\"recipient_123\"> higher in the tree.\nfunction Badge() {\n    const { data } = useNotificationsUnreadCount();\n    return <span>{data?.unread_count ?? 0}</span>;\n}"
                    }
                ]
            }
        },
        "/preferences": {
            "get": {
                "summary": "List the project preference catalog",
                "operationId": "listProjectPreferences",
                "tags": [
                    "Project Preferences"
                ],
                "description": "List every entry in the project's preference **catalog**. The project is taken from the API key. Requires a `Full access` key.",
                "responses": {
                    "200": {
                        "description": "The project's catalog.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Catalog": {
                                        "summary": "Catalog Response",
                                        "value": {
                                            "data": [
                                                {
                                                    "id": 12,
                                                    "project_id": 1,
                                                    "target": {
                                                        "channel": "digest",
                                                        "topic": "none",
                                                        "event": "sent"
                                                    },
                                                    "medium": "email",
                                                    "default_enabled": true,
                                                    "label": "Daily digest",
                                                    "created_at": "2025-11-07T05:31:56Z",
                                                    "updated_at": "2025-11-07T05:31:56Z"
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl https://api.bodhveda.com/preferences \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst preferences = await bodhveda.preferences.list();"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\npreferences, _ := client.Preferences.List(ctx)"
                    }
                ]
            },
            "post": {
                "summary": "Create a catalog entry",
                "operationId": "createProjectPreference",
                "tags": [
                    "Project Preferences"
                ],
                "description": "Create a single catalog entry. **Strict**: an entry for the same `(channel, topic, event, medium)` returns `409`. Use `PATCH /preferences/{preference_id}` to change an existing entry, or `PUT /preferences` to set a whole catalog.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateProjectPreferencePayload"
                            }
                        }
                    }
                },
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X POST https://api.bodhveda.com/preferences \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"channel\": \"digest\",\n    \"topic\": \"none\",\n    \"event\": \"sent\",\n    \"medium\": \"email\",\n    \"name\": \"Daily digest\",\n    \"description\": \"Receive a daily summary of activity.\",\n    \"default_enabled\": true\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst pref = await bodhveda.preferences.create({\n    channel: \"digest\",\n    topic: \"none\",\n    event: \"sent\",\n    medium: \"email\",\n    name: \"Daily digest\",\n    description: \"Receive a daily summary of activity.\",\n    default_enabled: true,\n});"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\npref, _ := client.Preferences.Create(ctx, &bodhveda.CreateProjectPreferenceRequest{\n    Channel:        \"digest\",\n    Topic:          \"none\",\n    Event:          \"sent\",\n    Medium:         bodhveda.MediumEmail,\n    Name:           \"Daily digest\",\n    Description:    \"Receive a daily summary of activity.\",\n    DefaultEnabled: true,\n})"
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Catalog entry created.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Created": {
                                        "summary": "Created Response",
                                        "value": {
                                            "message": "Project preference created",
                                            "data": {
                                                "id": 12,
                                                "project_id": 1,
                                                "target": {
                                                    "channel": "digest",
                                                    "topic": "none",
                                                    "event": "sent"
                                                },
                                                "medium": "email",
                                                "default_enabled": true,
                                                "label": "Daily digest",
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T05:31:56Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "An entry for this `(channel, topic, event, medium)` already exists.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ]
            },
            "put": {
                "summary": "Bulk upsert the catalog",
                "operationId": "upsertProjectPreferences",
                "tags": [
                    "Project Preferences"
                ],
                "description": "Declaratively merge a whole catalog in one call — the primitive for a one-off \"set up my project's preferences\" script. Each item is upserted by its natural key (`channel, topic, event, medium`): new entries are inserted, existing ones have their label and default updated. Entries **not** in the array are left untouched, unless `prune=true`.\n\nReturns the full resulting catalog.",
                "parameters": [
                    {
                        "name": "prune",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "boolean",
                            "default": false
                        },
                        "description": "When `true`, catalog entries absent from the array are **deleted**, making the array the entire desired catalog. Pruning un-catalogs a `(target, medium)`, which turns a non-`in_app` medium off for recipients relying on the default — so it is opt-in."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "array",
                                "items": {
                                    "$ref": "#/components/schemas/CreateProjectPreferencePayload"
                                }
                            }
                        }
                    }
                },
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X PUT https://api.bodhveda.com/preferences \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '[\n    { \"channel\": \"digest\", \"topic\": \"none\", \"event\": \"sent\", \"medium\": \"email\", \"name\": \"Daily digest\", \"default_enabled\": true }\n  ]' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst catalog = await bodhveda.preferences.upsertMany([\n    {\n        channel: \"digest\",\n        topic: \"none\",\n        event: \"sent\",\n        medium: \"email\",\n        name: \"Daily digest\",\n        default_enabled: true,\n    },\n]);\n// Pass { prune: true } to delete entries absent from the array."
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\ncatalog, _ := client.Preferences.UpsertMany(ctx, []bodhveda.UpsertProjectPreferenceItem{\n    {\n        Channel:        \"digest\",\n        Topic:          \"none\",\n        Event:          \"sent\",\n        Medium:         bodhveda.MediumEmail,\n        Name:           \"Daily digest\",\n        DefaultEnabled: true,\n    },\n}, nil)\n// Pass &bodhveda.UpsertProjectPreferencesOptions{Prune: true} to delete absent entries."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The full resulting catalog.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Upserted": {
                                        "summary": "Upserted Response",
                                        "value": {
                                            "message": "Project preferences upserted",
                                            "data": [
                                                {
                                                    "id": 12,
                                                    "project_id": 1,
                                                    "target": {
                                                        "channel": "digest",
                                                        "topic": "none",
                                                        "event": "sent"
                                                    },
                                                    "medium": "email",
                                                    "default_enabled": true,
                                                    "label": "Daily digest",
                                                    "created_at": "2025-11-07T05:31:56Z",
                                                    "updated_at": "2025-11-07T05:31:56Z"
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ]
            }
        },
        "/preferences/{preference_id}": {
            "get": {
                "summary": "Get a catalog entry",
                "operationId": "getProjectPreference",
                "tags": [
                    "Project Preferences"
                ],
                "description": "Retrieve a single catalog entry by ID.",
                "parameters": [
                    {
                        "name": "preference_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "The catalog entry's ID."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The catalog entry.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Entry": {
                                        "summary": "Entry Response",
                                        "value": {
                                            "data": {
                                                "id": 12,
                                                "project_id": 1,
                                                "target": {
                                                    "channel": "digest",
                                                    "topic": "none",
                                                    "event": "sent"
                                                },
                                                "medium": "email",
                                                "default_enabled": true,
                                                "label": "Daily digest",
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T05:31:56Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "No catalog entry with this ID.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl https://api.bodhveda.com/preferences/42 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst pref = await bodhveda.preferences.get(42);"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\npref, _ := client.Preferences.Get(ctx, 42)"
                    }
                ]
            },
            "patch": {
                "summary": "Update a catalog entry",
                "operationId": "updateProjectPreference",
                "tags": [
                    "Project Preferences"
                ],
                "description": "Update a catalog entry's label and default. The natural key (`channel`/`topic`/`event`/`medium`) is immutable.",
                "parameters": [
                    {
                        "name": "preference_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "The catalog entry's ID."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/UpdateProjectPreferencePayload"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "The updated catalog entry.",
                        "content": {
                            "application/json": {
                                "examples": {
                                    "Updated": {
                                        "summary": "Updated Response",
                                        "value": {
                                            "message": "Project preference updated",
                                            "data": {
                                                "id": 12,
                                                "project_id": 1,
                                                "target": {
                                                    "channel": "digest",
                                                    "topic": "none",
                                                    "event": "sent"
                                                },
                                                "medium": "email",
                                                "default_enabled": false,
                                                "label": "Weekly digest",
                                                "created_at": "2025-11-07T05:31:56Z",
                                                "updated_at": "2025-11-07T06:00:00Z"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "No catalog entry with this ID.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X PATCH https://api.bodhveda.com/preferences/42 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Daily digest\",\n    \"description\": \"Receive a daily summary of activity.\",\n    \"default_enabled\": false\n  }' "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nconst pref = await bodhveda.preferences.update(42, {\n    name: \"Daily digest\",\n    description: \"Receive a daily summary of activity.\",\n    default_enabled: false,\n});"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\npref, _ := client.Preferences.Update(ctx, 42, &bodhveda.UpdateProjectPreferenceRequest{\n    Name:           \"Daily digest\",\n    Description:    \"Receive a daily summary of activity.\",\n    DefaultEnabled: false,\n})"
                    }
                ]
            },
            "delete": {
                "summary": "Delete a catalog entry",
                "operationId": "deleteProjectPreference",
                "tags": [
                    "Project Preferences"
                ],
                "description": "Delete a catalog entry, un-cataloging the `(target, medium)`.",
                "parameters": [
                    {
                        "name": "preference_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "The catalog entry's ID."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Catalog entry deleted."
                    },
                    "404": {
                        "description": "No catalog entry with this ID.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "BearerAuthWithAPIKeyWithFullAcessScope": []
                    }
                ],
                "x-codeSamples": [
                    {
                        "lang": "curl",
                        "label": "cURL",
                        "source": "curl -X DELETE https://api.bodhveda.com/preferences/42 \\\n  -H \"Authorization: Bearer bv_xxxxxxxxx\" "
                    },
                    {
                        "lang": "javascript",
                        "label": "JavaScript",
                        "source": "import { Bodhveda } from \"@bodhveda/js\";\n\nconst bodhveda = new Bodhveda(\"bv_xxxxxxxxx\");\n\nawait bodhveda.preferences.delete(42);"
                    },
                    {
                        "lang": "go",
                        "label": "Go",
                        "source": "import (\n    \"context\"\n\n    bodhveda \"github.com/MudgalLabs/bodhveda/sdk/go\"\n)\n\nctx := context.Background()\nclient := bodhveda.NewClient(\"bv_xxxxxxxxx\", nil)\n\nerr := client.Preferences.Delete(ctx, 42)"
                    }
                ]
            }
        }
    },
    "components": {
        "securitySchemes": {
            "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."
            },
            "BearerAuthWithAPIKeyWithEitherScope": {
                "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` or `Recipient acess` scope."
            }
        },
        "schemas": {
            "SendNotificationPayload": {
                "type": "object",
                "properties": {
                    "recipient_id": {
                        "type": "string",
                        "nullable": true,
                        "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`."
                    },
                    "target": {
                        "$ref": "#/components/schemas/Target"
                    },
                    "payload": {
                        "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.",
                        "nullable": true
                    },
                    "email": {
                        "$ref": "#/components/schemas/EmailContent"
                    }
                }
            },
            "EmailContent": {
                "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."
                    },
                    "html": {
                        "type": "string",
                        "description": "Rendered HTML body. At least one of `html` or `text` must be provided."
                    },
                    "text": {
                        "type": "string",
                        "description": "Plain-text body. Recommended for deliverability. If omitted, it is derived from `html`."
                    }
                },
                "required": [
                    "subject"
                ]
            },
            "NotificationDelivery": {
                "type": "object",
                "description": "A per-medium delivery outcome for a **direct** send (email in v1). A failed medium never rejects the send — the outcome is reported here instead.",
                "properties": {
                    "medium": {
                        "type": "string",
                        "description": "The delivery transport, e.g. `email`.",
                        "example": "email"
                    },
                    "status": {
                        "type": "string",
                        "description": "Delivery status. At send time one of `pending` (queued to the provider), `muted` (catalog/preference disallows email), `no_contact` (recipient has no primary email contact), or `failed` (e.g. email provider not configured). Later transitions to `sent`/`delivered`/`bounced`/`complained` via provider webhooks.",
                        "enum": [
                            "pending",
                            "sent",
                            "delivered",
                            "bounced",
                            "complained",
                            "failed",
                            "muted",
                            "no_contact"
                        ]
                    },
                    "address": {
                        "type": "string",
                        "description": "The address the delivery was sent to (captured at send time), when known."
                    },
                    "failure_reason": {
                        "type": "string",
                        "description": "Set when the medium did not send. E.g. `not_cataloged`, `preference_disabled`, `provider_not_configured`."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "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\")."
                    },
                    "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`."
                    },
                    "event": {
                        "type": "string",
                        "description": "Event that triggered this notification (e.g., \"new_comment\")."
                    }
                },
                "required": [
                    "channel",
                    "topic",
                    "event"
                ]
            },
            "SendNotificationResponse": {
                "type": "object",
                "description": "Response for send notification API.",
                "properties": {
                    "message": {
                        "type": "string"
                    },
                    "notification": {
                        "$ref": "#/components/schemas/Notification"
                    },
                    "broadcast": {
                        "$ref": "#/components/schemas/Broadcast"
                    },
                    "deliveries": {
                        "type": "array",
                        "description": "**Deprecated and no longer populated.** The send is fully asynchronous — the notification is accepted (`status: \"enqueued\"`) and every medium, including email, is resolved later by the worker. Read the resolved in-app status and the email outcome back with the [retrieve-a-notification](/api-reference/endpoint/notifications/get-notification) endpoint (see `notification.email`).",
                        "items": {
                            "$ref": "#/components/schemas/NotificationDelivery"
                        },
                        "deprecated": true
                    }
                }
            },
            "Notification": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "Unique identifier for the notification."
                    },
                    "recipient_id": {
                        "type": "string",
                        "description": "Unique identifier for the recipient who received this notification."
                    },
                    "payload": {
                        "type": "object",
                        "description": "JSON payload sent with the notification. `null` for an *email-only* send — one made with no `payload` block, which creates no in-app notification.",
                        "nullable": true
                    },
                    "broadcast_id": {
                        "type": "integer",
                        "nullable": true,
                        "description": "Unique identifier for the broadcast that created this notification. \n It is `null`, if this is a **direct** notification."
                    },
                    "target": {
                        "$ref": "#/components/schemas/Target"
                    },
                    "read": {
                        "type": "boolean",
                        "description": "Whether the notification has been read by the recipient. \n\n 💡 You can update this to `true` or `false` via [mark notifications as read](/api-reference/endpoint/mark-read)."
                    },
                    "opened": {
                        "type": "boolean",
                        "description": "Whether the notification has been opened by the recipient. \n\n 💡 You can update this to `true` or `false` via [mark notifications as opened](/api-reference/endpoint/mark-opened)."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "status": {
                        "type": "string",
                        "description": "The in-app delivery outcome, resolved **asynchronously** after the send is accepted. `enqueued` immediately on send; then one of `delivered`, `muted` (preferences disallow), `quota_exceeded`, or `failed`.\n\n`not_requested` is the exception: it is set **at send time** and never changes. It means the send carried no `payload`, so no in-app delivery was ever requested (an *email-only* send). Such a notification is hidden from the recipient's feed and unread count, but still carries the email delivery outcome.",
                        "enum": [
                            "enqueued",
                            "delivered",
                            "muted",
                            "quota_exceeded",
                            "failed",
                            "not_requested"
                        ]
                    },
                    "completed_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When the worker finished processing this notification across all mediums. `null` until then."
                    },
                    "email": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/NotificationEmailDelivery"
                            }
                        ],
                        "nullable": true,
                        "description": "The email-medium delivery outcome, present only when the send included an `email` block. Absent on the recipient inbox feed; populated by the retrieve-a-notification endpoint."
                    }
                }
            },
            "Broadcast": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "project_id": {
                        "type": "integer"
                    },
                    "payload": {
                        "type": "object"
                    },
                    "channel": {
                        "type": "string"
                    },
                    "topic": {
                        "type": "string"
                    },
                    "event": {
                        "type": "string"
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "ErrorResponse": {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "string"
                    },
                    "details": {
                        "type": "string"
                    }
                }
            },
            "CreateRecipientPayload": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "Unique identifier from your system. \n\n This cannot be changed after creation. So use a stable identifier that will not change over time."
                    },
                    "name": {
                        "type": "string",
                        "description": "Name of the recipient."
                    }
                },
                "required": [
                    "id"
                ]
            },
            "RecipientContact": {
                "type": "object",
                "description": "A per-medium contact address for a recipient.",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "Unique identifier for the contact."
                    },
                    "medium": {
                        "type": "string",
                        "description": "The delivery transport this address is for. Only `email` is exercised today; `sms`, `web_push`, and `mobile_push` are reserved for future transports.",
                        "enum": [
                            "email",
                            "sms",
                            "web_push",
                            "mobile_push"
                        ]
                    },
                    "address": {
                        "type": "string",
                        "description": "The contact address (e.g. an email address). Email addresses are stored lowercased."
                    },
                    "is_primary": {
                        "type": "boolean",
                        "description": "Whether this is the recipient's primary contact for its medium. Email is sent to the primary email contact."
                    },
                    "verified_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When the contact was verified, or `null` if unverified."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "CreateRecipientContactPayload": {
                "type": "object",
                "properties": {
                    "medium": {
                        "type": "string",
                        "description": "The delivery transport. Only `email` is exercised today.",
                        "enum": [
                            "email",
                            "sms",
                            "web_push",
                            "mobile_push"
                        ]
                    },
                    "address": {
                        "type": "string",
                        "description": "The contact address. For email it must contain `@` and is stored lowercased."
                    },
                    "is_primary": {
                        "type": "boolean",
                        "description": "Mark this as the primary contact for its medium. Only one primary is allowed per `(recipient, medium)`.",
                        "default": false
                    }
                },
                "required": [
                    "medium",
                    "address"
                ]
            },
            "UpdateRecipientContactPayload": {
                "type": "object",
                "description": "Provide at least one field. Changing the address resets `verified_at`.",
                "properties": {
                    "address": {
                        "type": "string",
                        "description": "New contact address."
                    },
                    "is_primary": {
                        "type": "boolean",
                        "description": "Set or unset this contact as the primary for its medium."
                    }
                }
            },
            "SetPrimaryContactPayload": {
                "type": "object",
                "properties": {
                    "medium": {
                        "type": "string",
                        "description": "The delivery transport. Only `email` is exercised today.",
                        "enum": [
                            "email",
                            "sms",
                            "web_push",
                            "mobile_push"
                        ]
                    },
                    "address": {
                        "type": "string",
                        "description": "The address to make primary. For email it must contain `@` and is stored lowercased."
                    }
                },
                "required": [
                    "medium",
                    "address"
                ]
            },
            "ProjectPreference": {
                "type": "object",
                "description": "A project-level preference — one entry in the project's **catalog**. The catalog is a GATE: a send whose `(target, medium)` has no entry here is rejected with `400`. Each entry also carries the default a recipient inherits until they set a preference of their own.",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "Unique identifier for the catalog entry."
                    },
                    "project_id": {
                        "type": "integer"
                    },
                    "target": {
                        "$ref": "#/components/schemas/Target"
                    },
                    "medium": {
                        "type": "string",
                        "description": "The medium this catalog entry gates.",
                        "enum": [
                            "in_app",
                            "email"
                        ]
                    },
                    "default_enabled": {
                        "type": "boolean",
                        "description": "The project-level default for this `(target, medium)`: whether a recipient who has set no preference of their own is delivered to."
                    },
                    "name": {
                        "type": "string",
                        "description": "Human-readable name for the catalog entry (e.g. \"Weekly digest\")."
                    },
                    "description": {
                        "type": "string",
                        "description": "Optional longer blurb shown alongside the name on a settings screen. Omitted or blank stores none.",
                        "nullable": true
                    },
                    "mandatory": {
                        "type": "boolean",
                        "description": "Whether recipients may opt out. A mandatory entry is cataloged (so sends pass the target gate) but its recipient toggle is refused with a `400`, and any rule a recipient already had is ignored. For transactional notifications — password resets, security alerts, a one-shot welcome. `default_enabled` still applies, so setting it `false` stops the notification: mandatory removes the recipient's choice, not yours. Defaults to `false`.",
                        "default": false
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "CreateProjectPreferencePayload": {
                "type": "object",
                "properties": {
                    "channel": {
                        "type": "string",
                        "description": "Broad category (e.g. \"digest\")."
                    },
                    "topic": {
                        "type": "string",
                        "description": "Subcategory. `any` matches all topics under the channel/event; `none` is the base channel/event."
                    },
                    "event": {
                        "type": "string",
                        "description": "Event that triggers the notification (e.g. \"sent\")."
                    },
                    "medium": {
                        "type": "string",
                        "description": "The medium this catalog entry gates. Defaults to `in_app`.",
                        "enum": [
                            "in_app",
                            "email"
                        ],
                        "default": "in_app"
                    },
                    "name": {
                        "type": "string",
                        "description": "Human-readable name for the catalog entry (e.g. \"Weekly digest\")."
                    },
                    "default_enabled": {
                        "type": "boolean",
                        "description": "The project-level default for recipients who have set no preference of their own."
                    },
                    "description": {
                        "type": "string",
                        "description": "Optional longer blurb shown alongside the name on a settings screen. Omitted or blank stores none."
                    },
                    "mandatory": {
                        "type": "boolean",
                        "description": "Whether recipients may opt out. A mandatory entry is cataloged (so sends pass the target gate) but its recipient toggle is refused with a `400`, and any rule a recipient already had is ignored. For transactional notifications — password resets, security alerts, a one-shot welcome. `default_enabled` still applies, so setting it `false` stops the notification: mandatory removes the recipient's choice, not yours. Defaults to `false`.",
                        "default": false
                    }
                },
                "required": [
                    "channel",
                    "topic",
                    "event",
                    "name",
                    "default_enabled"
                ]
            },
            "UpdateProjectPreferencePayload": {
                "type": "object",
                "description": "Updates a catalog entry's name, description, default and mandatory flag. The natural key (`channel`/`topic`/`event`/`medium`) is immutable.",
                "properties": {
                    "name": {
                        "type": "string",
                        "description": "Human-readable name for the catalog entry (e.g. \"Weekly digest\")."
                    },
                    "default_enabled": {
                        "type": "boolean"
                    },
                    "description": {
                        "type": "string",
                        "description": "Optional longer blurb shown alongside the name on a settings screen. Omitted or blank stores none."
                    },
                    "mandatory": {
                        "type": "boolean",
                        "description": "Whether recipients may opt out. A mandatory entry is cataloged (so sends pass the target gate) but its recipient toggle is refused with a `400`, and any rule a recipient already had is ignored. For transactional notifications — password resets, security alerts, a one-shot welcome. `default_enabled` still applies, so setting it `false` stops the notification: mandatory removes the recipient's choice, not yours. Defaults to `false`.",
                        "default": false
                    }
                },
                "required": [
                    "name",
                    "default_enabled"
                ]
            },
            "NotificationEmailDelivery": {
                "type": "object",
                "description": "The email-medium delivery outcome attached to a notification. Its lifecycle is independent of the in-app `status`.",
                "properties": {
                    "status": {
                        "type": "string",
                        "enum": [
                            "pending",
                            "sent",
                            "delivered",
                            "bounced",
                            "complained",
                            "failed",
                            "muted",
                            "no_contact"
                        ],
                        "description": "`pending` (queued to the provider) → `sent` (accepted) → `delivered`/`bounced`/`complained` (from provider webhooks), or `failed`/`muted`/`no_contact`."
                    },
                    "failure_reason": {
                        "type": "string",
                        "description": "Set when the email did not send, e.g. `not_cataloged`, `preference_disabled`, `provider_not_configured`."
                    },
                    "attempt": {
                        "type": "integer",
                        "description": "Number of send attempts made by the worker."
                    },
                    "provider": {
                        "type": "string",
                        "description": "The email provider that handled the send, e.g. `resend`."
                    },
                    "provider_message_id": {
                        "type": "string",
                        "description": "The provider's message id, for correlating with provider dashboards/webhooks."
                    },
                    "address_snapshot": {
                        "type": "string",
                        "description": "The address the email was sent to, captured at send time."
                    },
                    "sent_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "delivered_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "bounced_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "complained_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "opened_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "Soft signal — provider open tracking is unreliable. The in-app `read` state is the trustworthy signal."
                    },
                    "clicked_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            }
        }
    }
}
