For the complete documentation index, see llms.txt. This page is also available as Markdown.

Force Password Reset

Bulk password reset flow. For users whose email addresses are listed in an uploaded Excel (.xlsx) file, this flow:

  • Deletes their auth tokens (logging them out of all active sessions)

  • Deletes their active sessions

  • Clears the UserProfile.old_passwords list

The operation runs asynchronously (Celery). The upload endpoint immediately returns a cache_key; you then poll a separate status endpoint for the result.

If the FORCE_PASSWORD_RESET_CHECK_ACTIVE dynamic setting is enabled, users whose old_passwords are cleared by this flow will not be able to log in until they reset their password. Users without a UserProfile are not affected.

Authorization

All endpoints require admin (staff) permissions — permissions.IsAdminUser.

Authorization: Token <ADMIN_TOKEN>
1

Start a Password Reset

POST /api/v1/users/force-password-reset/
Content-Type: multipart/form-data
Field
Type
Required
Description

file

file

Yes

An .xlsx file. The first column must contain email addresses. If the first row is the email header, it is skipped.

Excel Format

email

user1@example.com

user2@example.com

The header row is optional; if the first cell is email, it is skipped automatically.

curl

curl -X POST "https://<HOST>/api/v1/users/force-password-reset/" \
  -H "Authorization: Token <ADMIN_TOKEN>" \
  -F "file=@emails.xlsx"

Success Response — 200 OK

{
  "cache_key": "3f1c9e4a-2b7d-4d1e-9c3a-8f0b2a1d5e6c",
  "total": 2,
  "processed": 0,
  "is_ready": false,
  "result": null,
  "error_message": null
}

Use the returned cache_key to query the status.

Error Responses — 400 Bad Request

{ "detail": "An Excel file must be uploaded in the 'file' field." }
{ "detail": "Could not parse the uploaded file. Please upload a valid .xlsx file." }
{ "detail": "No emails found in the file." }
2

Check Status (poll)

GET /api/v1/users/force-password-reset-status/?cache_key=<CACHE_KEY>

curl

curl -X GET "https://<HOST>/api/v1/users/force-password-reset-status/?cache_key=3f1c9e4a-2b7d-4d1e-9c3a-8f0b2a1d5e6c" \
  -H "Authorization: Token <ADMIN_TOKEN>"

In Progress — 200 OK

{
  "cache_key": "3f1c9e4a-2b7d-4d1e-9c3a-8f0b2a1d5e6c",
  "total": 250,
  "processed": 100,
  "is_ready": false,
  "result": null,
  "error_message": null
}

Completed — 200 OK

{
  "cache_key": "3f1c9e4a-2b7d-4d1e-9c3a-8f0b2a1d5e6c",
  "total": 2,
  "processed": 2,
  "is_ready": true,
  "result": {
    "processed_email_count": 2,
    "found_user_count": 2,
    "deleted_token_count": 2,
    "deleted_session_count": 1,
    "updated_profile_count": 2,
    "missing_emails": []
  },
  "error_message": null
}

missing_emails: emails present in the file for which no matching user was found in the system.

Finished With an Error — 200 OK

{
  "cache_key": "3f1c9e4a-2b7d-4d1e-9c3a-8f0b2a1d5e6c",
  "total": 2,
  "processed": 0,
  "is_ready": true,
  "result": null,
  "error_message": "<error message>"
}

Not Found / Expired cache_key404 Not Found

{
  "cache_key": "3f1c9e4a-2b7d-4d1e-9c3a-8f0b2a1d5e6c",
  "is_ready": false,
  "error_message": "Not found or expired."
}

If the cache_key parameter is not sent at all, an empty body is returned with 404.

Response Fields (state)

Field
Type
Description

cache_key

string

The key identifying the operation.

total

int

Total number of emails read from the file.

processed

int

Number of emails processed so far.

is_ready

bool

Whether the operation has finished (including errors).

result

object|null

Populated once the operation completes (see fields below).

error_message

string|null

The error message, if any.

result contents

Field
Description

processed_email_count

Number of emails processed.

found_user_count

Number of matching users found.

deleted_token_count

Number of auth tokens deleted.

deleted_session_count

Number of active sessions deleted.

updated_profile_count

Number of profiles whose old_passwords were cleared.

missing_emails

List of emails for which no matching user was found.

Notes

  • Emails are normalized with strip().lower(); matching is done case-insensitively.

  • The operation proceeds in chunks of 100; processed is updated at each step.

  • Cache TTL: 2 hours. After this period, the status can no longer be queried using the cache_key.

Last updated

Was this helpful?