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

# Two-factor & challenges

> Resolve SMS, authenticator, email, and selfie challenges during connect, and reauthenticate an account from stored credentials.

When an account has two-factor enabled — or OnlyFans raises a device-trust
gate for an unfamiliar login — the attempt parks on a challenge state and
`next_actions` tells you what to send.

## Two-factor channels

OnlyFans offers three two-factor channels:

| Poll `state`     | Channel                  | How to resolve                                                                       | Status    |
| ---------------- | ------------------------ | ------------------------------------------------------------------------------------ | --------- |
| `needs_otp`      | SMS code                 | `PUT /v1/connect/login/{id}` `{ "code": "123456" }`                                  | Supported |
| `needs_app_otp`  | Authenticator app (TOTP) | `PUT /v1/connect/login/{id}` `{ "code": "123456" }`                                  | Supported |
| `needs_face_otp` | Selfie / Face ID         | Open `face_verification_url`, then `PUT … { "selfie_verification_completed": true }` | Upcoming  |

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X PUT https://api.onlyfanskit.dev/v1/connect/login/$ATTEMPT_ID \
  -H "Authorization: Bearer $OFK_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "code": "123456" }'
```

A wrong code parks the attempt on `wrong_2fa_code_retry` so the creator can
try again. The poll includes `otp_channel` and (for email) `email_mask` to
drive your UI.

## Email device verification

Separately from 2FA, OnlyFans may require a one-time **email verification
code** to establish device trust for an unfamiliar login context (new
device/region) or before sensitive actions. This is not a channel you
configure — it's a gate the API raises. When it triggers, the attempt parks on
`needs_email`:

<Steps>
  <Step title="Trigger the email">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    curl -X POST \
      https://api.onlyfanskit.dev/v1/connect/login/$ATTEMPT_ID/send-email-to-creator \
      -H "Authorization: Bearer $OFK_KEY"
    ```
  </Step>

  <Step title="Submit the code">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    curl -X PUT https://api.onlyfanskit.dev/v1/connect/login/$ATTEMPT_ID \
      -H "Authorization: Bearer $OFK_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "code": "123456" }'
    ```
  </Step>
</Steps>

## Reauthentication

If an account was connected with `remember_credentials: true`, you can
re-acquire a fresh session from the stored credentials without a full login:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.onlyfanskit.dev/v1/accounts/$ACCOUNT_ID/reauthenticate \
  -H "Authorization: Bearer $OFK_KEY"
```

This returns an attempt. It completes in one trip when no 2FA is required, or
parks on `needs_app_otp` so you can submit a code via the same `PUT`. Without
stored credentials you get `412` and must re-link.
