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

# Challenge

## The Challenge Object

A challenge is a single verification a user has to complete — an OTP, a magic link, a passkey prompt, etc. One challenge tracks one attempt at proving something: that the user owns this email, that they're really the one signing in, that they consent to a sensitive action.

### Attributes

<ResponseField name="id" type="string" required>
  Unique identifier.
</ResponseField>

<ResponseField name="app_id" type="string" required>
  The app this challenge belongs to.
</ResponseField>

<ResponseField name="app_user_id" type="string">
  The user being challenged. May be empty for anonymous flows (e.g. a sign-up before the user record exists).
</ResponseField>

<ResponseField name="purpose" type="string" required>
  Why this challenge exists. One of:

  * `authenticate` — primary login
  * `mfa` — second factor after primary auth
  * `step_up` — re-verify before a sensitive action
  * `verify_contact` — confirm an email or phone number
  * `verify_identity` — KYC / identity verification
  * `change_identifier` — confirm a new email or phone before swapping
  * `custom` — triggered by you, for your own flow
</ResponseField>

<ResponseField name="challenge_method" type="string" required>
  How the user will prove themselves. One of:

  `email_otp`, `sms_otp`, `magic_link`, `totp`, `backup_code`, `webauthn`, `oauth`, `push`, `plaid_idv`
</ResponseField>

<ResponseField name="status" type="string" required>
  Current state. One of:

  * `pending` — issued, waiting on the user
  * `completed` — user passed
  * `failed` — too many wrong attempts
  * `expired` — `expires_at` passed before completion
  * `cancelled` — voided by you or the user
  * `denied` — explicitly rejected (e.g. user said "this wasn't me")
</ResponseField>

<ResponseField name="identifier" type="string">
  The email or phone the challenge was sent to, when applicable.
</ResponseField>

<ResponseField name="intent" type="string">
  Free-form label for what action this challenge guards (e.g. `login`, `delete_account`, `wire_transfer`). Useful for filtering and audit.
</ResponseField>

<ResponseField name="intent_fields" type="object">
  Structured data describing the action. Echoed back when the challenge completes so you can act on it.
</ResponseField>

<ResponseField name="initiator_type" type="string">
  Who started the challenge: `user`, `admin`, `system`, or your own value.
</ResponseField>

<ResponseField name="initiator_id" type="string">
  ID of the initiator.
</ResponseField>

<ResponseField name="parent_challenge_id" type="string">
  If this challenge is a follow-up to another (e.g. MFA after primary auth), the parent's ID.
</ResponseField>

<ResponseField name="attempts" type="integer">
  Number of guesses made so far.
</ResponseField>

<ResponseField name="max_attempts" type="integer">
  How many guesses are allowed before the challenge fails. Default `3`.
</ResponseField>

<ResponseField name="remaining_attempts" type="integer">
  `max_attempts - attempts`. Convenience field.
</ResponseField>

<ResponseField name="timeout" type="integer">
  Lifetime in seconds from creation. Default `600`.
</ResponseField>

<ResponseField name="callback_url" type="string">
  Where the user is sent after completing a magic link or OAuth challenge.
</ResponseField>

<ResponseField name="short_url" type="string">
  Shareable short URL for magic-link and similar flows. Only present once a short token has been minted.
</ResponseField>

<ResponseField name="metadata" type="object">
  Arbitrary key/value data you attached when creating the challenge.
</ResponseField>

<ResponseField name="context" type="object">
  Additional context captured by Scute (request origin, flow hints, etc.).
</ResponseField>

<ResponseField name="device_info" type="object">
  User agent / device fingerprint of the requester, when available.
</ResponseField>

<ResponseField name="ip_address" type="string">
  IP that initiated the challenge.
</ResponseField>

<ResponseField name="created_at" type="string" required>
  ISO 8601 timestamp.
</ResponseField>

<ResponseField name="expires_at" type="string" required>
  When the challenge stops accepting answers.
</ResponseField>

<ResponseField name="delivered_at" type="string">
  When the OTP / magic link was successfully sent.
</ResponseField>

<ResponseField name="opened_at" type="string">
  When the user first opened the magic link or OTP message, if tracked.
</ResponseField>

<ResponseField name="verified_at" type="string">
  When the user passed the challenge.
</ResponseField>

<ResponseField name="completed_at" type="string">
  When the challenge reached its final state (verified, failed, expired, cancelled, or denied).
</ResponseField>

## Example

```json theme={null}
{
  "id": "ch_01H8X...",
  "app_id": "app_01H8X...",
  "app_user_id": "usr_01H8X...",
  "purpose": "authenticate",
  "challenge_method": "email_otp",
  "status": "pending",
  "identifier": "user@example.com",
  "intent": "login",
  "intent_fields": {},
  "initiator_type": "user",
  "initiator_id": "usr_01H8X...",
  "attempts": 0,
  "max_attempts": 3,
  "remaining_attempts": 3,
  "timeout": 600,
  "callback_url": null,
  "short_url": null,
  "metadata": {},
  "context": {},
  "ip_address": "203.0.113.42",
  "created_at": "2026-04-23T10:30:00Z",
  "expires_at": "2026-04-23T10:40:00Z",
  "delivered_at": "2026-04-23T10:30:01Z",
  "opened_at": null,
  "verified_at": null,
  "completed_at": null
}
```
