> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Preview Decision Email

> See the Approved or Declined email a person receives, with its recipient, sender and language, before you change the session status.



Preview Decision Email Prompt
        Copy prompt to clipboard



      
      

\<pre>

\`Preview the Approved or Declined email a Didit session sends the person, then confirm the decision so exactly that email goes out.

Endpoint:
POST https://verification.didit.me/v3/session/{session_id}/decision-email-preview/

Authentication:
x-api-key (client API key) or a user-auth token. Privilege: write:sessions. A scoped API key needs write access to decisions.

Path parameter:
\- session_id (UUID): KYC or KYB; Didit resolves it against both session types.

Request body:
\- new_status (string, required): "Approved" or "Declined".
\- email_language (string, optional, default "en"): language of the email. A language that is not offered falls back to English.

The session's current status must be one of: Approved, Declined, In Review, Kyc Expired, Abandoned, Resubmitted.

Previewing changes nothing and sends nothing.

Response (200, Cache-Control: no-store):
\- recipient: the person's own address (the one they verified in the flow, else the one in contact_details). Empty string when none is known.
\- recipient_source: "email_verification", "contact_details", or "" when there is no address.
\- sender, sender_name, sender_email: the From header and its parts.
\- sender_kind: "customer" (your verified sender domain) or "didit" (Didit's sender).
\- subject, html: the email exactly as sent. Render html in a sandboxed frame.
\- language: the language actually used.
\- will_be_sent (boolean) and not_sent_reason: null, "status_unchanged", "notification_emails_disabled", "no_recipient" or "sandbox".
\- preview_token: receipt for this exact email, valid 15 minutes. Empty when there is no recipient.

Confirm with PATCH https://verification.didit.me/v3/session/{session_id}/update-status/:
\{
"new_status": "<same new_status>",
"send_email": true,
"email_address": "<recipient>",
"email_language": "<language>",
"email_preview_token": "<preview_token>"
}
If the recipient, language, session status, sender or email changed since the preview, or the receipt is older than 15 minutes, update-status returns 409 { "detail": "This email preview changed or expired. Preview the message again before confirming." } and changes nothing. Preview again and confirm with the new receipt.

curl example:
curl -X POST https://verification.didit.me/v3/session/<SESSION_ID>/decision-email-preview/ \
-H "x-api-key: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{ "new_status": "Approved", "email_language": "en" }'

Failure modes:
\- 400: the session is not in an accepted status ({ "detail": "Only sessions with status ... can be updated. Current status: ..." }), or new_status is missing or invalid.
\- 401: the scoped API key has expired.
\- 403: invalid credentials, the caller lacks write:sessions, a scoped key lacks decisions write, or the session belongs to another application ({ "detail": "You do not have permission to perform this action." }).
\- 404: { "detail": "Not found." } when no session with this session_id exists.

For end-to-end Didit integration, paste in the full prompt at /integration/integration-prompt.`

\</pre>



      
      

Paste into

Previewing changes nothing and sends nothing.
The response shows the email exactly as the person receives it when you set the session to `Approved` or `Declined` with `send_email: true`.
It also tells you who receives it, who it comes from, and whether confirming sends it at all.## Who receives the emailThe email goes to the person's own address: the one they verified in an email verification step of the flow, else the one given in `contact_details` when the session was created.
`recipient_source` says which one applies (`email_verification` or `contact_details`).
When the session has no address for the person, `recipient`, `recipient_source` and `preview_token` are empty strings and no email can be sent.The email goes out from your application's verified sender domain when your [white-label configuration](/console/white-label.md) applies (`sender_kind: customer`), else from Didit's sender (`sender_kind: didit`).## Whether the email goes out`will_be_sent` is `true` when confirming the change with `send_email: true` sends the email.
When it is `false`, `not_sent_reason` says why:| `not_sent_reason` | What it means |
| - | - |
| `status_unchanged` | The session already has this status, so the update changes nothing and sends nothing. |
| `notification_emails_disabled` | The session was created without `contact_details.send_notification_emails: true`, so no decision email goes to the address given in `contact_details`. |
| `no_recipient` | No address is known for the person. |
| `sandbox` | The session is a sandbox session and the recipient has not opted into sandbox email delivery, so the email is only simulated. See [email opt-in](/integration/sandbox-testing.md#otp-codes-and-email-opt-in). |## Confirm the decisionCall [Update Session Status](/sessions-api/update-status.md) with these fields:| Update Session Status field | Value |
| - | - |
| `new_status` | The same `new_status` you previewed |
| `send_email` | `true` |
| `email_address` | `recipient` from the preview |
| `email_language` | `language` from the preview |
| `email_preview_token` | `preview_token` from the preview |The receipt expires after 15 minutes.
If the recipient, language, session status, sender or email changed since the preview, the update returns `409` before anything changes.
Preview again and confirm with the new receipt.## Related- [Update Session Status](/sessions-api/update-status.md): approve or decline the session and send the email.
- [Preview Resubmission Email](/sessions-api/preview-resubmission.md): preview the email for a resubmission instead.
- [White label](/console/white-label.md): the sender and branding the email uses.


## OpenAPI

````yaml POST /v3/session/{sessionId}/decision-email-preview/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/session/{sessionId}/decision-email-preview/:
    post:
      tags:
        - Sessions
      summary: Preview a session's decision email
      description: >-
        Render the email the person receives when you set a session to
        `Approved` or `Declined` with `send_email: true`, without changing the
        session or sending anything. The response shows the subject and HTML
        exactly as they are sent, the recipient, the sender, the language, and
        whether confirming the change sends the email at all (`will_be_sent`,
        `not_sent_reason`).


        The recipient is the person's own address: the one they verified in an
        email verification step of the flow, else the one given in
        `contact_details` when the session was created. The sender is your
        application's verified sender domain when white label applies
        (`sender_kind: customer`), else Didit's sender (`sender_kind: didit`).


        To send exactly what you previewed, call [Update Session
        Status](/sessions-api/update-status) with the same `new_status`,
        `send_email: true`, `email_address` set to `recipient`, `email_language`
        set to `language` and `email_preview_token` set to `preview_token`. The
        receipt expires after 15 minutes. When the recipient, language, session
        status, sender or email changed since the preview, the update returns
        `409` before anything changes; preview again and confirm with the new
        receipt.


        Works for User Verification (KYC) and Business Verification (KYB)
        sessions in `Approved`, `Declined`, `In Review`, `Kyc Expired`,
        `Abandoned` or `Resubmitted` status. The status check runs before body
        validation. Requires the `write:sessions` privilege; a scoped API key
        needs write access to decisions. Responses use `Cache-Control:
        no-store`: treat them as sensitive verification content.
      operationId: post_v3_session_decision_email_preview
      parameters:
        - in: path
          name: sessionId
          required: true
          description: >-
            UUID of the verification session. Accepts both user (KYC) and
            business (KYB) session IDs.
          schema:
            type: string
            format: uuid
            example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - new_status
              properties:
                new_status:
                  type: string
                  enum:
                    - Approved
                    - Declined
                  description: The decision whose email you want to preview.
                  example: Approved
                email_language:
                  type: string
                  default: en
                  description: >-
                    Language of the email, the same default as Update Session
                    Status. A language that is not offered falls back to
                    English; `language` in the response says which one is used.
                  example: en
            examples:
              Approved:
                summary: Preview the Approved email in English
                value:
                  new_status: Approved
              Declined in Spanish:
                summary: Preview the Declined email in Spanish
                value:
                  new_status: Declined
                  email_language: es
      responses:
        '200':
          description: Preview only. The session is unchanged and no email is sent.
          headers:
            Cache-Control:
              description: >-
                Always `no-store`: the preview carries verification content and
                must not be cached.
              schema:
                type: string
                example: no-store
          content:
            application/json:
              schema:
                type: object
                required:
                  - recipient
                  - recipient_source
                  - sender
                  - sender_name
                  - sender_email
                  - sender_kind
                  - subject
                  - html
                  - language
                  - will_be_sent
                  - not_sent_reason
                  - preview_token
                properties:
                  recipient:
                    type: string
                    description: >-
                      The address the email goes to. An empty string when no
                      address is known for the person: then no email can be sent
                      and `preview_token` is empty too. Pass it as
                      `email_address` when you confirm.
                    example: person@example.com
                  recipient_source:
                    type: string
                    enum:
                      - email_verification
                      - contact_details
                      - ''
                    description: >-
                      Where `recipient` comes from. `email_verification`: the
                      address the person verified in an email verification step
                      of the flow. `contact_details`: the address given in
                      `contact_details` when the session was created. An empty
                      string when there is no address.
                    example: email_verification
                  sender:
                    type: string
                    description: The `From` header the email goes out with.
                    example: Example App <no-reply@mail.example.com>
                  sender_name:
                    type: string
                    description: The display name part of `sender`.
                    example: Example App
                  sender_email:
                    type: string
                    format: email
                    description: The address part of `sender`.
                    example: no-reply@mail.example.com
                  sender_kind:
                    type: string
                    enum:
                      - customer
                      - didit
                    description: >-
                      `customer`: the email goes out from your application's
                      verified sender domain (white label). `didit`: it goes out
                      from Didit's own sender.
                    example: customer
                  subject:
                    type: string
                    description: The subject line exactly as it is sent.
                    example: Your verification has been approved in Example App
                  html:
                    type: string
                    description: >-
                      The email's HTML exactly as it is sent, with its logo
                      embedded as a data URL. Render it in a sandboxed frame,
                      never directly in your application DOM.
                    example: <!DOCTYPE html><html><body>...</body></html>
                  language:
                    type: string
                    description: >-
                      The language the email is written in. English (`en`) when
                      the `email_language` you asked for is not offered.
                    example: en
                  will_be_sent:
                    type: boolean
                    description: >-
                      Whether confirming the change with `send_email: true`
                      sends this email. `false` when `not_sent_reason` is set.
                    example: true
                  not_sent_reason:
                    type: string
                    nullable: true
                    enum:
                      - status_unchanged
                      - notification_emails_disabled
                      - no_recipient
                      - sandbox
                      - null
                    description: >-
                      Why confirming would send no email, or `null` when it will
                      be sent. `status_unchanged`: the session already has this
                      status, so the update changes nothing and sends nothing.
                      `notification_emails_disabled`: the session was created
                      without `contact_details.send_notification_emails: true`,
                      so no decision email goes to the address given in
                      `contact_details`. `no_recipient`: no address is known for
                      the person. `sandbox`: a sandbox session whose recipient
                      has not opted into sandbox email delivery, so the send is
                      only simulated.
                    example: null
                  preview_token:
                    type: string
                    description: >-
                      Signed receipt for this exact email, valid for 15 minutes.
                      Pass it as `email_preview_token` to [Update Session
                      Status](/sessions-api/update-status). An empty string when
                      there is no recipient.
                    example: >-
                      eyJkaWdlc3QiOiI0ZjJhIn0:1uExAm:Pl3x4mpleS1gnatureValue000000000000000
              examples:
                Will Be Sent:
                  summary: The Approved email goes to the address verified in the flow
                  value:
                    recipient: person@example.com
                    recipient_source: email_verification
                    sender: Example App <no-reply@mail.example.com>
                    sender_name: Example App
                    sender_email: no-reply@mail.example.com
                    sender_kind: customer
                    subject: Your verification has been approved in Example App
                    html: <!DOCTYPE html><html><body>...</body></html>
                    language: en
                    will_be_sent: true
                    not_sent_reason: null
                    preview_token: >-
                      eyJkaWdlc3QiOiI0ZjJhIn0:1uExAm:Pl3x4mpleS1gnatureValue000000000000000
                No Recipient:
                  summary: No address is known for the person
                  value:
                    recipient: ''
                    recipient_source: ''
                    sender: Example App <no-reply@mail.example.com>
                    sender_name: Example App
                    sender_email: no-reply@mail.example.com
                    sender_kind: customer
                    subject: Your verification has been declined in Example App
                    html: <!DOCTYPE html><html><body>...</body></html>
                    language: en
                    will_be_sent: false
                    not_sent_reason: no_recipient
                    preview_token: ''
        '400':
          description: >-
            The session is not in a status this endpoint accepts (`{"detail":
            ...}`), or the body is invalid (`{"<field>": ["<message>"]}`).
          content:
            application/json:
              examples:
                Wrong Current Status:
                  summary: Session is not in a status this endpoint accepts
                  value:
                    detail: >-
                      Only sessions with status Approved, Declined, In Review,
                      Kyc Expired, Abandoned, Resubmitted can be updated.
                      Current status: In Progress
                Invalid new_status:
                  summary: new_status is not Approved or Declined
                  value:
                    new_status:
                      - '"Resubmitted" is not a valid choice.'
                Missing new_status:
                  summary: Required field omitted
                  value:
                    new_status:
                      - This field is required.
        '401':
          description: The scoped API key has expired.
          content:
            application/json:
              examples:
                Expired API Key:
                  summary: Scoped API key past its expiry date
                  value:
                    detail: This API key has expired.
        '403':
          description: >-
            Missing or invalid credentials, a caller without the
            `write:sessions` privilege, a scoped API key without the access this
            endpoint needs, or a `session_id` that belongs to another
            application.
          content:
            application/json:
              examples:
                Invalid Credentials:
                  summary: Missing or invalid x-api-key
                  value:
                    detail: >-
                      Authentication credentials were not provided or are
                      invalid.
                Missing Privilege:
                  summary: Caller lacks write:sessions
                  value:
                    detail: You do not have permission to perform this action.
                Scoped Key Without Access:
                  summary: Scoped API key without this access
                  value:
                    detail: This API key has no write access to decisions.
        '404':
          description: No KYC or KYB session with this `session_id` exists.
          content:
            application/json:
              examples:
                Not Found:
                  summary: Unknown session_id
                  value:
                    detail: Not found.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: curl
          source: |-
            curl -X POST \
              https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/decision-email-preview/ \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{"new_status": "Approved", "email_language": "en"}'
        - lang: python
          label: Python
          source: |-
            import requests

            response = requests.post(
                "https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/decision-email-preview/",
                headers={"x-api-key": "YOUR_API_KEY"},
                json={"new_status": "Approved", "email_language": "en"},
            )
            response.raise_for_status()
            print(response.json())
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Your application's API key, from Developers -> API keys in the Business
        Console. The primary key has full access. A named key can be scoped:
        none, read or write per resource, limited to some workflows or to
        approved sessions, to a list of IP addresses, and to an expiry date. 401
        means the key is missing, wrong, revoked or expired; 403 means the key
        has no access to this resource or action, or the request came from an
        address outside its IP list; 404 on a session route means the session is
        outside the key's workflows or statuses. A key without media access
        receives image, video and PDF URLs as null, and a key without sessions
        write receives session links and tokens as null. See
        https://docs.didit.me/console/api-keys.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.