Resend Email Service And Webhooks ================================= This project uses Resend for outbound sales-invoice email delivery and for delivery/open/click/failure lifecycle webhooks. Outbound Email Service ---------------------- Outbound sends happen in ``accounting/invoice_email.py`` through Resend's ``/emails`` API and are called from ``SalesInvoiceEmailSendView``. Required environment variables: - ``RESEND_API_KEY`` - ``RESEND_FROM_EMAIL`` (or legacy alias ``RESEND_EMAIL_FROM``) Optional environment variables: - ``RESEND_REPLY_TO`` - ``RESEND_API_BASE_URL`` (default: ``https://api.resend.com``) - ``RESEND_REQUEST_TIMEOUT_SECONDS`` (default: ``20``) - ``RESEND_USER_AGENT`` (default: ``inkforje-accounting/1.0``) Webhook Endpoint ---------------- The application exposes a CSRF-exempt webhook endpoint: - ``/ledger/webhooks/resend/`` Register this URL in Resend as: - ``https:///ledger/webhooks/resend/`` Signature Verification ---------------------- Incoming webhooks are verified with Svix headers: - ``svix-id`` - ``svix-timestamp`` - ``svix-signature`` The app verifies signatures using ``RESEND_WEBHOOK_SECRET`` before processing events. Webhook Secret Configuration ---------------------------- Set this in your environment file: - ``RESEND_WEBHOOK_SECRET=whsec_xxxxx`` Local env file path: - ``/Users/danielmontecillo/Development/django/inkforje_accounting/.env`` Production env file path (from deployment workflow): - ``/.env`` Event Handling And Status Mapping --------------------------------- Supported Resend event types and mapped ``SalesInvoiceEmailLog.status``: - ``email.scheduled`` -> ``scheduled`` - ``email.sent`` -> ``sent`` - ``email.delivered`` -> ``delivered`` - ``email.delivery_delayed`` -> ``delivery_delayed`` - ``email.opened`` -> ``opened`` - ``email.clicked`` -> ``clicked`` - ``email.complained`` -> ``complained`` - ``email.bounced`` -> ``bounced`` - ``email.suppressed`` -> ``suppressed`` - ``email.failed`` -> ``failed`` Webhook events that do not match a known ``provider_message_id`` are safely acknowledged with HTTP ``204`` and ignored. Data Stored Per Event --------------------- For each matched event, the app updates the existing email log row with: - ``status`` - ``last_event_type`` - ``last_event_at`` - ``provider_response`` (raw event payload) - ``error_message`` (for failed/bounced/suppressed/delayed reasons when present) UI Surfaces Updated ------------------- Webhook-driven status appears in: - Sales Invoice detail status indicator - Sales Invoice update status indicator - Sales Correspondence list - Customer detail correspondence table Setup Checklist --------------- 1. Add Resend API and webhook environment variables. 2. Run migrations: .. code-block:: bash uv run python manage.py migrate 3. Restart the Django process. 4. In Resend, subscribe email event types listed above. 5. Send a test sales invoice email and verify status changes in the UI.