---
title: "Send a CRM test/preview email"
method: POST
path: "/api/campaigns/send-test-email"
tags: ["crm-email"]
---

# Send a CRM test/preview email

`POST /api/campaigns/send-test-email`

Admin-only. Sends a single test/preview email to to_email so an admin can verify the content and personalization before scheduling the real send.

## Request body

- CRMSendEmailRequest — Request model for sending a CRM patient email. The audience is resolved from either an explicit list of ``recipient_emails`` (e.g. an uploaded CSV) or, when none are provided, from the filters
  - `to_email` string — Recipient address for the test/preview send.
  - `recipient_emails` string[] — Explicit recipient email addresses (e.g. from an uploaded CSV). Take priority over the filters. Emails with no matching user are still emailed, with an empty first name. Capped at 50000 addresses per request.
  - `patient_states` string[] — Stato paziente filters. OR-combined with registration_reasons.
  - `patient_filters` string[] — Deprecated alias of patient_states (kept for the current frontend payload).
  - `registration_reasons` string[] — Motivo iscrizione filters (users.registration_reason). OR-combined with patient_states.
  - `email_subject` string, nullable — Subject for the email. Falls back to a generated test subject when empty.
  - `email_html` string, nullable — Composed HTML body (template with image placeholders already replaced by URLs). Used as the test send body when provided.
  - `template_path` string, nullable — Storage path of the uploaded template under public/templates/ (audit/reuse).
  - `placeholder_images` object — Map of placeholder name -> image public URL used to compose email_html (audit/reuse).

## Response `202`

Successful Response

- CRMSendEmailResponse — Response model for sending a CRM patient email.
  - `status` string, required — Whether the task was enqueued.
  - `to_email` string, required — Test/preview recipient address.
  - `subject` string, required — Subject used for the email.
  - `campaign_key` string, required — Unique campaign key used for dedupe/audit.
  - `task_id` string, nullable — Celery task id, if enqueue succeeded.
  - `resolved_from` string — 'selection' when an explicit recipient_emails list was used, otherwise 'filters'.
  - `recipient_count` integer — Number of patients matched by the selection/filters.
  - `sent_count` integer — Number of addresses actually enqueued (1 for a test send).
  - `recipients` CRMEmailRecipientPreview[] — Resolved audience. On a test send this is a preview only.
    - `user_id` string, nullable — users.id of the matched patient.
    - `patient_id` string, nullable — patients.id of the matched patient.
    - `email` string, nullable — Patient email address.
    - `first_name` string, nullable — Patient first name.
    - `last_name` string, nullable — Patient last name.

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.dev/corabea/apis/corabea-api.md) · [All operations](https://skmtc.dev/corabea/apis/corabea-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/corabea/corabea-api/revisions/13c1bce1bdc4/schema)
