# useDocyrusEmailComposer URL: /docs/web/hooks/use-docyrus-email-composer Wire an `` to the Docyrus messaging API — loads sender accounts, lets the user pick a From address, and ships emails through `/v1/messaging/email/accounts/{id}/send`. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/hooks-use-docyrus-email-composer ``` **Dependencies:** - [@docyrus/api-client](https://www.npmjs.com/package/@docyrus/api-client) - [@tanstack/react-query](https://tanstack.com/query/latest) The hook needs an authenticated `RestApiClient` from `@docyrus/api-client` and a `QueryClientProvider` from `@tanstack/react-query` somewhere above your component tree. The access token must carry the `Messaging.Email.Send` scope — otherwise both the list-accounts and send calls return `403 insufficient_scope`. ## Overview `useDocyrusEmailComposer` is the Docyrus-backed companion to [`EmailComposer`](/docs/web/components/email-composer). It does three things: - **Loads sender accounts** — `GET /v1/messaging/email/accounts` returns both shared **tenant** accounts (`tenant_email_configuration`) and the caller's own connected OAuth mailboxes (Microsoft / Google). - **Manages composer state** — `to`, `cc`, `bcc`, `subject`, `body`, plus the currently selected account id. State persists across re-renders and resets after a successful send (toggle via `resetOnSendSuccess`). - **Sends through the picked account** — `POST /v1/messaging/email/accounts/{accountId}/send` returns `{ messageId, provider, accepted, rejected }`. Per-call overrides let you set `sendAsUser`, storage-backed `attachments`, or `replyTo` without touching the composer state. The first user-accessible account is auto-selected once the list resolves, so consumers don't have to wait for the user to pick before the composer becomes interactive. ## Usage ### Default integration ```tsx 'use client'; import { useDocyrusAuth } from '@docyrus/signin'; import { EmailComposer } from '@docyrus/ui/components/email-composer'; import { useDocyrusEmailComposer } from '@docyrus/ui/library/hooks/use-docyrus-email-composer'; export function SendEmailDialog({ onClose }: { onClose: () => void }) { const { client } = useDocyrusAuth(); if (!client) return null; const composer = useDocyrusEmailComposer({ client, initialTo: ['customer@example.com'], initialSubject: 'Following up on your order' }); return ( ); } ``` `composerProps` spreads the wiring for the From dropdown, recipient fields, subject, body, and the `onSend` handler. The composer's built-in Send button fires the hook's mutation; no glue required. ### Sending with `sendAsUser` For tenant accounts that allow it, `sendAsUser: true` overrides the From name/address with the authenticated user's identity at the API layer (subject to each account's `allowOverrideName` / `allowOverrideEmail` flags). ```tsx const composer = useDocyrusEmailComposer({ client, sendAsUser: true // forwarded with every send call }); ``` Or override it per call: ```tsx await composer.send({ sendAsUser: true }); ``` ### Storage-backed attachments The composer's local `EmailAttachment` carries `{ name, size }` only — it cannot be transmitted as-is, because the send endpoint expects files that already live in Docyrus storage. Upload first, then hand the storage path(s) to `send()`: ```tsx await composer.send({ attachments: [ { filePath: 'tenant-files/invoices/inv-1234.pdf', fileName: 'invoice.pdf', mimeType: 'application/pdf' } ] }); ``` ### Reading the response `lastSendResult` carries the latest server response so you can surface rejected recipients: ```tsx {composer.lastSendResult?.rejected.length > 0 && (

The provider rejected: {composer.lastSendResult.rejected.join(', ')}

)} ``` ## Account selection The dropdown surfaces every account returned by the API: | Account kind | Source | Behavior | |--------------|--------|----------| | `tenant` | Shared tenant configuration (`tenant_email_configuration`) | Send From is the configured `senderName` / `senderEmail`. `sendAsUser` may override these if `allowOverride*` is set. | | `user` | The caller's connected OAuth mailbox (`tenant_connection_user`) | Always sends as the user; `sendAsUser` is ignored by the server. | Entries with `isUserAccessible: false` are rendered disabled in the dropdown — they exist in the tenant configuration but the current user can't send through them. The hook auto-selects the first `isUserAccessible: true` account on first load. Override via `initialAccountId` or by calling `setSelectedAccountId(id)`. ## API Reference ### `useDocyrusEmailComposer(options)` | Option | Type | Default | Description | |--------|------|---------|-------------| | `client` | `RestApiClient` | — | Authenticated Docyrus API client. | | `initialTo` | `string[]` | `[]` | Initial **To** recipients. | | `initialCc` | `string[]` | `[]` | Initial **Cc** recipients. | | `initialBcc` | `string[]` | `[]` | Initial **Bcc** recipients. | | `initialReplyTo` | `string[]` | `[]` | Initial **Reply-To** addresses (forwarded with every send). | | `initialSubject` | `string` | `''` | Initial subject. | | `initialBody` | `string` | `''` | Initial body HTML. | | `initialAccountId` | `string` | — | Preselect a specific account id. When omitted, the hook picks the first `isUserAccessible: true` account once the list resolves. | | `resetOnSendSuccess` | `boolean` | `true` | Clear `to` / `cc` / `bcc` / `subject` / `body` / `attachments` after a successful send. Disable to keep the composer populated for a follow-up send. | | `enabled` | `boolean` | `true` | Toggles the `GET /v1/messaging/email/accounts` query. | | `sendAsUser` | `boolean` | — | Default forwarded as `sendAsUser` on every send unless explicitly overridden. | | `listAccountsEndpoint` | `string` | `/v1/messaging/email/accounts` | Override the list endpoint. | | `sendEndpoint` | `string` | `/v1/messaging/email/accounts/{accountId}/send` | Override the send endpoint. `{accountId}` is replaced with the selected id. | | `mentions` | `boolean` | `true` | Toggles the `GET /v1/users` query that powers the body editor's `@` mention autocomplete. `/v1/users` has no server-side search, so the list is fetched once (limit 1000, 5-minute stale time) and filtered client-side. | | `mentionUsers` | `EmailComposerMentionUser[]` | — | Explicit mention users. When provided, the `/v1/users` fetch is skipped. | | `listUsersEndpoint` | `string` | `/v1/users` | Override the mention users list endpoint. | | `slashCommands` | `EmailComposerSlashCommand[]` | — | Developer-defined `/` slash commands forwarded to the composer body editor. Pure passthrough — no fetching. See the [composer's Slash Commands section](/docs/web/components/email-composer#-slash-commands) for the command API. | ### Return value | Property | Type | Description | |----------|------|-------------| | `composerProps` | `Pick` | Ready-to-spread props for ``. Wires accounts, selected account, all recipient/subject/body fields, `mentionUsers`, `slashCommands`, `onSend`, and `sending`. | | `accounts` | `DocyrusEmailAccount[]` | Raw account DTOs (includes provider / override metadata). | | `selectedAccount` | `DocyrusEmailAccount \| null` | Currently selected account. | | `setSelectedAccountId` | `(accountId: string) => void` | Programmatically change the selection. | | `isLoadingAccounts` | `boolean` | First-fetch loading state for the accounts query. | | `accountsError` | `Error \| null` | Last error from the accounts query (e.g. `insufficient_scope`). | | `refetchAccounts` | `() => Promise` | Invalidate and refetch the accounts cache. | | `mentionUsers` | `EmailComposerMentionUser[]` | Users offered by the body editor's `@` mention autocomplete (from `mentionUsers` option or the `/v1/users` fetch; empty when `mentions: false`). | | `to` / `setTo` | `string[]` / `(next) => void` | Direct access to the **To** state. | | `cc` / `setCc` | `string[]` / `(next) => void` | Direct access to the **Cc** state. | | `bcc` / `setBcc` | `string[]` / `(next) => void` | Direct access to the **Bcc** state. | | `replyTo` / `setReplyTo` | `string[]` / `(next) => void` | Direct access to the **Reply-To** state. | | `subject` / `setSubject` | `string` / `(next) => void` | Direct access to the subject. | | `body` / `setBody` | `string` / `(next) => void` | Direct access to the HTML body. | | `attachments` / `setAttachments` | `EmailAttachment[]` / `(next) => void` | Local attachment metadata. **Not** sent automatically — the send endpoint expects storage paths. | | `send` | `(overrides?) => Promise` | Trigger a send. Resolves with `{ messageId, provider, accepted, rejected }`. | | `isSending` | `boolean` | Mutation in-flight state. | | `sendError` | `Error \| null` | Last send error. | | `lastSendResult` | `DocyrusEmailSendResult \| null` | Last successful send response — surface rejected recipients here. | | `reset` | `() => void` | Clear all field state back to the `initial*` values. | ### `send(overrides?)` Every field is optional — anything omitted falls back to current composer state. | Field | Type | Default | Description | |-------|------|---------|-------------| | `to` | `string[]` | `state.to` | Override **To** recipients. | | `cc` | `string[]` | `state.cc` | Override **Cc** recipients. | | `bcc` | `string[]` | `state.bcc` | Override **Bcc** recipients. | | `replyTo` | `string[]` | `state.replyTo` | Override **Reply-To** addresses. | | `subject` | `string` | `state.subject` | Override subject. | | `body` | `string` | `state.body` | Override HTML body. | | `sendAsUser` | `boolean` | `options.sendAsUser` | Override the `sendAsUser` flag. Honored only on tenant accounts with `allowOverride*` set. | | `attachments` | `DocyrusEmailAttachment[]` | — | Storage-backed attachments (`{ filePath, fileName?, mimeType? }`). | | `accountId` | `string` | `selectedAccountId` | Send through a specific account, bypassing the dropdown selection. | ## Type Exports | Type | Description | |------|-------------| | `UseDocyrusEmailComposerOptions` | Hook option shape. | | `UseDocyrusEmailComposerResult` | Hook return shape. | | `DocyrusEmailAccount` | Account DTO returned by the list endpoint — extends `EmailComposerAccount` with provider / override metadata. | | `DocyrusEmailAttachment` | `{ filePath; fileName?; mimeType? }` — payload shape for the send endpoint. | | `DocyrusEmailSendOverrides` | Optional overrides accepted by `send(...)`. | | `DocyrusEmailSendResult` | `{ messageId; provider; accepted[]; rejected[] }`. | | `DocyrusEmailProvider` | `'aws' \| 'smtp' \| 'mailgun' \| 'resend' \| 'microsoft-graph' \| 'gmail'`. | ## Error handling `accountsError` and `sendError` surface the underlying API failures verbatim. Common ones: | Status | When | What to do | |--------|------|------------| | `401` | Missing/invalid access token. | Re-authenticate via `@docyrus/signin`. | | `403 insufficient_scope` | The access token doesn't include `Messaging.Email.Send`. | Add the scope to the OAuth2 client config, re-authorize. | | `403` (accessible flag) | Selected account has `isUserAccessible: false`. | Disable the option in the dropdown (already done by default). | | `404` | The chosen `accountId` doesn't exist. | Re-fetch the account list. | | `400` | Validation failure (invalid email, missing subject/body, attachment limits). | Surface from `sendError.message`. |