# 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`. |