Add an unverified secondary email address to a user
POST /v1/users/{user_id}/emails · permission users:write
Adds a secondary, unverified email address to the user. The address is normalized before it is stored. If the user already has the same address, no row is written and the existing row is returned (it may be verified or primary), so repeating the request is safe. A new row is written to the management audit log as user.email_added with the API key as actor; the address itself is not written to the audit record. To verify the address, send a verification email with POST /v1/users/{user_id}/emails/{email_id}:send-verification or mark it verified with :verify.
Request
| Parameter | In | Required | Description |
|---|---|---|---|
user_id |
path | yes | User id, prefixed usr_. |
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | yes | The address, at most 320 characters. |
Response
201 Created with the email row. Without the users:read_pii permission the address is masked (a***@e***.com).
{
"id": "0191f0a0-3333-7444-8555-666666666666",
"email": "ada@example.com",
"verified": false,
"primary": false,
"created_at": "2026-01-15T09:30:00Z"
}
Errors
Every error is an application/problem+json body with a stable code.
| HTTP | code |
When |
|---|---|---|
| 400 | malformed_request, unknown_field |
The body is not valid JSON or has an unknown field. |
| 409 | conflict |
The address is verified on another user of this environment (errors[]: /email, email_taken). |
| 409 | email_limit_reached |
The user already has 10 addresses. |
| 415 | unsupported_media_type |
The body was sent without Content-Type: application/json. |
| 422 | validation_failed |
email is missing, empty, longer than 320 characters, not a valid address, or has the wrong type. |
| 404 | not_found |
The user does not exist in this environment, is being deleted, or the id is malformed. |
| 401 | unauthenticated |
The API key is missing, invalid or revoked. |
| 403 | insufficient_permissions |
The key lacks the users:write permission. |
| 429 | rate_limited |
The rate limit was exceeded; honor Retry-After. |
| 503 | service_unavailable |
A dependency is temporarily unavailable; retry later. |
Example
curl -X POST https://api.eu.keenauth.com/v1/users/usr_0a1b2c3d4e5f6g7h8j9k0m1n2p/emails \
-H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "ada@example.com"}'
Personal data
This endpoint carries personal data: end-user profile. Personal data in the KeenAuth APIs describes what is stored, how long it is kept and how it is deleted. Do not send special categories of personal data (health, biometric data, religion, political opinions, sexual orientation) in any field.