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

# Mint a challenge

> Mint a ceremony from your backend and show it to the visitor.

<ParamField header="Authorization" type="string" required>
  `Bearer hrtc_sk_…`
</ParamField>

<ParamField body="mode" type="string" required>
  `phone`: the page runs the ceremony on the device that opens it. `desktop`: the page shows a QR code that hands off to a phone.
</ParamField>

<ParamField body="account_ref" type="string">
  Your account handle, up to 128 characters. A device already registered to another `account_ref` cannot register again.
</ParamField>

<ParamField body="ttl_s" type="integer" default="600">
  120 to 900.
</ParamField>

The [widget](/integration/challenge-widget) mints ceremonies on its own. This endpoint mints one from your backend, for a visitor your own rule flagged. Read the outcome at [`GET /v1/challenge/{challenge_id}`](/api/challenge-read).

## Request

```json theme={"dark"}
{ "mode": "phone", "account_ref": "acct-77", "ttl_s": 600 }
```

## Response

```json theme={"dark"}
{
  "api_version": "2",
  "challenge_id": "chg_0123456789abcdef0123456789abcdef",
  "url": "https://heretic.tech/challenge/<token>",
  "mode": "phone",
  "expires_at": "2026-09-01T12:10:00.000Z"
}
```

## Show the ceremony

**As a modal on your page.** With `guard.js` loaded:

```html theme={"dark"}
<script src="https://heretic.tech/guard.js"></script>
<script>
  heretic.challenge(minted.url, {
    callback: (result) => {
      // { challenge_id, status, step_reached }
      location.href = '/signup/finish?challenge=' + result.challenge_id;
    },
    'dismiss-callback': () => {},   // visitor closed the frame; the ceremony stays pending
    dismissible: true,              // false removes Close and ignores Escape
  });
</script>
```

The frame closes when the ceremony reaches an outcome, then `callback` runs. On a phone the frame fills the screen. On a desktop it shows the QR hand-off and closes when the phone finishes. `heretic.closeChallenge(id)` closes it as a dismissal.

**In your own frame.** Any origin may frame the page. Give the frame these permissions:

```html theme={"dark"}
<iframe src="<url>" title="heretic verification"
  allow="publickey-credentials-get; publickey-credentials-create; accelerometer; gyroscope; magnetometer"></iframe>
```

At an outcome the page posts `{ source: "heretic", type: "heretic-challenge", challenge_id, status, step_reached }` to its parent. Check `event.origin === "https://heretic.tech"`.

**As a redirect.** Send the visitor to `url` and bring them back yourself.

The message on the page is for the page. Your backend reads the outcome below before acting.

## Status codes

| Code  | Meaning                                                                                                                           |
| ----- | --------------------------------------------------------------------------------------------------------------------------------- |
| `400` | Malformed body.                                                                                                                   |
| `401` | Bearer token missing or malformed.                                                                                                |
| `402` | Sandbox stopped at its monthly ceremony quota. Body: `{"error": "quota-exceeded", "plan": "sandbox", "used": 100, "limit": 100}`. |
| `404` | Unknown key, or a challenge that is not yours.                                                                                    |
| `429` | More than 600 calls a minute from your address. `retry-after` is set.                                                             |
| `503` | Store failure. Never `passed`.                                                                                                    |


## Related topics

- [Signup rules](/integration/writing-rules.md)
- [Heretic documentation](/index.md)
- [Dashboard, sites, and keys](/dashboard/dashboard-and-keys.md)
- [Pricing](/reference/pricing.md)
- [Challenge widget](/integration/challenge-widget.md)
