Hooks

useDocyrusEmailComposer

Wire an `<EmailComposer />` 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`.

Client Only

Installation

pnpm dlx @docyrus/cli add @docyrus/hooks-use-docyrus-email-composer
Required Packages(2 packages)
pnpm add @docyrus/api-client @tanstack/react-query

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

'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 (
    <EmailComposer
      {...composer.composerProps}
      onDiscard={onClose} />
  );
}

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).

const composer = useDocyrusEmailComposer({
  client,
  sendAsUser: true // forwarded with every send call
});

Or override it per call:

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():

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:

{composer.lastSendResult?.rejected.length > 0 && (
  <p>The provider rejected: {composer.lastSendResult.rejected.join(', ')}</p>
)}

Account selection

The dropdown surfaces every account returned by the API:

Account kindSourceBehavior
tenantShared tenant configuration (tenant_email_configuration)Send From is the configured senderName / senderEmail. sendAsUser may override these if allowOverride* is set.
userThe 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)

OptionTypeDefaultDescription
clientRestApiClient—Authenticated Docyrus API client.
initialTostring[][]Initial To recipients.
initialCcstring[][]Initial Cc recipients.
initialBccstring[][]Initial Bcc recipients.
initialReplyTostring[][]Initial Reply-To addresses (forwarded with every send).
initialSubjectstring''Initial subject.
initialBodystring''Initial body HTML.
initialAccountIdstring—Preselect a specific account id. When omitted, the hook picks the first isUserAccessible: true account once the list resolves.
resetOnSendSuccessbooleantrueClear to / cc / bcc / subject / body / attachments after a successful send. Disable to keep the composer populated for a follow-up send.
enabledbooleantrueToggles the GET /v1/messaging/email/accounts query.
sendAsUserboolean—Default forwarded as sendAsUser on every send unless explicitly overridden.
listAccountsEndpointstring/v1/messaging/email/accountsOverride the list endpoint.
sendEndpointstring/v1/messaging/email/accounts/{accountId}/sendOverride the send endpoint. {accountId} is replaced with the selected id.
mentionsbooleantrueToggles 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.
mentionUsersEmailComposerMentionUser[]—Explicit mention users. When provided, the /v1/users fetch is skipped.
listUsersEndpointstring/v1/usersOverride the mention users list endpoint.
slashCommandsEmailComposerSlashCommand[]—Developer-defined / slash commands forwarded to the composer body editor. Pure passthrough — no fetching. See the composer's Slash Commands section for the command API.

Return value

PropertyTypeDescription
composerPropsPick<EmailComposerProps, ...>Ready-to-spread props for <EmailComposer />. Wires accounts, selected account, all recipient/subject/body fields, mentionUsers, slashCommands, onSend, and sending.
accountsDocyrusEmailAccount[]Raw account DTOs (includes provider / override metadata).
selectedAccountDocyrusEmailAccount | nullCurrently selected account.
setSelectedAccountId(accountId: string) => voidProgrammatically change the selection.
isLoadingAccountsbooleanFirst-fetch loading state for the accounts query.
accountsErrorError | nullLast error from the accounts query (e.g. insufficient_scope).
refetchAccounts() => Promise<unknown>Invalidate and refetch the accounts cache.
mentionUsersEmailComposerMentionUser[]Users offered by the body editor's @ mention autocomplete (from mentionUsers option or the /v1/users fetch; empty when mentions: false).
to / setTostring[] / (next) => voidDirect access to the To state.
cc / setCcstring[] / (next) => voidDirect access to the Cc state.
bcc / setBccstring[] / (next) => voidDirect access to the Bcc state.
replyTo / setReplyTostring[] / (next) => voidDirect access to the Reply-To state.
subject / setSubjectstring / (next) => voidDirect access to the subject.
body / setBodystring / (next) => voidDirect access to the HTML body.
attachments / setAttachmentsEmailAttachment[] / (next) => voidLocal attachment metadata. Not sent automatically — the send endpoint expects storage paths.
send(overrides?) => Promise<DocyrusEmailSendResult>Trigger a send. Resolves with { messageId, provider, accepted, rejected }.
isSendingbooleanMutation in-flight state.
sendErrorError | nullLast send error.
lastSendResultDocyrusEmailSendResult | nullLast successful send response — surface rejected recipients here.
reset() => voidClear all field state back to the initial* values.

send(overrides?)

Every field is optional — anything omitted falls back to current composer state.

FieldTypeDefaultDescription
tostring[]state.toOverride To recipients.
ccstring[]state.ccOverride Cc recipients.
bccstring[]state.bccOverride Bcc recipients.
replyTostring[]state.replyToOverride Reply-To addresses.
subjectstringstate.subjectOverride subject.
bodystringstate.bodyOverride HTML body.
sendAsUserbooleanoptions.sendAsUserOverride the sendAsUser flag. Honored only on tenant accounts with allowOverride* set.
attachmentsDocyrusEmailAttachment[]—Storage-backed attachments ({ filePath, fileName?, mimeType? }).
accountIdstringselectedAccountIdSend through a specific account, bypassing the dropdown selection.

Type Exports

TypeDescription
UseDocyrusEmailComposerOptionsHook option shape.
UseDocyrusEmailComposerResultHook return shape.
DocyrusEmailAccountAccount DTO returned by the list endpoint — extends EmailComposerAccount with provider / override metadata.
DocyrusEmailAttachment{ filePath; fileName?; mimeType? } — payload shape for the send endpoint.
DocyrusEmailSendOverridesOptional 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:

StatusWhenWhat to do
401Missing/invalid access token.Re-authenticate via @docyrus/signin.
403 insufficient_scopeThe 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).
404The chosen accountId doesn't exist.Re-fetch the account list.
400Validation failure (invalid email, missing subject/body, attachment limits).Surface from sendError.message.

On this page