---
title: "Mail a one-time code to an address the caller wants to verify (Snowflake native app only)."
method: POST
path: "/v1/me/email-verification"
tags: ["auth"]
---

# Mail a one-time code to an address the caller wants to verify (Snowflake native app only).

`POST /v1/me/email-verification`

## Request

- Base URL: `http://localhost:8080`
- URL: `http://localhost:8080/v1/me/email-verification`
- Auth: one of:
  - API key in cookie `dvt_session`
  - HTTP bearer

DVT-5182. Starts verifying the address a signed-in user wants dvt to treat as theirs:
dvt mails a 6-digit code (valid 15 minutes) through the same transport as "Email me
this report now" (`SYSTEM$SEND_EMAIL` or the SMTP relay), but sent as the app's own identity
rather than the caller's session. Confirm it with
`POST /v1/me/email-verification/confirm`. A new request voids any pending code.
Snowflake-native only (404 elsewhere). Interactive human sessions only: API keys, MCP
and OAuth-client credentials get 403 `delegated-credential`. Requires `data:query`,
`data:export` and `sheet:read`. The request must be same-origin and send
`Content-Type: application/json` (415 / 403 otherwise). At most 5 codes per user per
rolling hour (429 `email-verification-rate-limited` with `Retry-After`). If the mail
cannot be sent the code is retired and the same Problem shapes as the dashboard email
send are returned. The address is stored on the pending code row for up to about 48h (a
daily sweep deletes rows older than 24h) and in `users.verified_email` once confirmed; it
is never logged.

## Request body

- object
  - `email` string, required: One bare addr-spec (no display name, no list), at most 254 bytes after trimming.

## Response `202`

The code was mailed.

- object
  - `data` object, required
    - `expiresAt` string, date-time, required: When the mailed code stops working.
  - `meta` Meta, required: Response metadata.
    - `requestId` string, required: Correlates with the X-Request-Id header.
    - `total` integer: Item count, present on collection responses.
    - `nextCursor` string, nullable: Opaque keyset-pagination cursor, present on paginated responses (e.g. GET /v1/dashboards) when more rows exist beyond this page. Pass it back as the `cursor` query param to fetch the next page; absent or null on the last page.
    - `roleResolveDegraded` boolean: Snowflake mode only (DVT-2260), on GET /v1/dashboards: present and true when the caller's live Snowflake role resolution failed or was unavailable — role-shared dashboards may be missing from this page. Omitted (not merely false) whenever resolution succeeded or the role-share arm does not apply (off snowflake mode, an org admin/owner, or off the ingress listener).

## Other responses

- `400`: Malformed body or an address that is not a single bare email address.
- `401`: Missing, invalid, or expired session.
- `403`: The caller's role does not permit this action.
- `404`: Resource does not exist.
- `415`: `Content-Type` is not `application/json`.
- `429`: Per-actor API rate limit exceeded (SEC-2). All authenticated `/v1` routes apply a per-actor fixed-window default tier; selected compute-heavy routes (connection tests, column describe, data export) apply an additional tighter tier. Auth endpoints apply a per-IP tier. Problem type: `https://docs.dvt.dev/errors/rate-limited`. Wait for the `Retry-After` response header value (seconds) before retrying.
- `502`: The mail transport refused or failed the send (same shapes as the dashboard email send).

---

[API](https://skmtc.dev/dvt/apis/dvt-rest-api.md) · [All operations](https://skmtc.dev/dvt/apis/dvt-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dvt/apis/dvt-rest-api/revisions/00016f504165?raw)
