---
title: Native claims and the Errand
description: Choose a claim policy for native direct-issue and handle the
  browser Errand when required consent or profile data is missing.
editUrl: true
head: []
template: doc
sidebar:
  order: 2
  hidden: false
  attrs: {}
pagefind: true
draft: false
---

Steam ticket and AccessKey direct-issue authenticate without an application login page. That keeps the happy path short, but it also means the client cannot display a consent form or ask the user to complete missing profile data.

This page covers both sides of that constraint:

1. Choose claim policies that fit a non-interactive client.
2. Handle the **Errand** browser handoff when a required real claim cannot yet be issued.

The shared policy and consent model is documented in [Identity claims and sharing](/en-us/concepts/identity-claims/).

## Choose a native claim policy

| Policy | Native direct-issue behavior |
|---|---|
| **Off** | Never requested. |
| **Optional** | Shared only if the user already granted it. Never blocks, so a native-only user may never be prompted. |
| **Required** | Guaranteed present with real data. Missing consent or data returns `403` with an Errand. |
| **Synthetic** | Guaranteed present, using real data when granted and a stable placeholder otherwise. Never blocks and never creates an Errand. |

For most native integrations, prefer **`SYNTHETIC`** over **`REQUIRED`** unless you specifically need verified real data.

- Need a stable name or email-shaped value, and a placeholder is acceptable: use `SYNTHETIC`.
- Need a real verified email for delivery or reconciliation: use `REQUIRED` and implement the Errand flow.
- Want real data when already granted but can continue without it: use `OPTIONAL`.

If every requested claim is `OFF` or `SYNTHETIC`, claim policy can never force direct-issue into a browser handoff.

Synthetic names are generated placeholders. Synthetic emails use a stable `…@proxy.sudomimus.email` address. Proxy delivery is best-effort, not guaranteed, and OIDC exposes synthetic email with `email_verified: false`.

## The Errand

An **Errand** is short-lived account remediation, not token issuance. When direct-issue cannot satisfy a required claim, its `403` response gives the client a browser URL. The user completes consent or missing profile work there, then the client retries the original direct-issue.

Only two claim-gate reasons carry an Errand:

| `403` reason | Meaning | Browser work |
|---|---|---|
| `ClaimConsentRequired` | A required claim has not been granted. | Grant consent and, when necessary, first add the missing data. |
| `RequiredClaimDataMissing` | Consent exists, but the account lacks the real value. | Register an email or complete the missing name. |

Other `403` responses, such as rule denial or a disabled account, are terminal and do not include an Errand.

### Handoff response

```json
{
  "reason": "ClaimConsentRequired",
  "claims": {
    "email":     { "requirement": "REQUIRED", "state": "UNKNOWN" },
    "firstName": { "requirement": "OPTIONAL", "state": "UNKNOWN" },
    "lastName":  { "requirement": "OFF",      "state": "UNKNOWN" }
  },
  "errand": {
    "errandKey": "ernd_...",
    "url": "https://via.sudomimus.com/errand?key=ernd_...",
    "expiresAt": "2026-06-10T12:30:00Z"
  }
}
```

- Open `errand.url` in the user's **system browser**.
- Treat `errandKey` as a bearer secret. It is also used to poll status.
- The Errand is single-use and expires after **30 minutes**.

### Client loop

```text
native client ── direct-issue ──▶ 403 { reason, claims, errand }
   │
   ├── open errand.url in the system browser
   ├── poll GET /errand/{errandKey}/status
   └── on COMPLETED, retry direct-issue once ──▶ 200 { tokens, claims }
```

Polling is optional. A client may instead ask the user to confirm that they finished in the browser before retrying.

```bash
curl https://native-api.sudomimus.com/errand/ernd_.../status
# → { "status": "PENDING" }
# → { "status": "COMPLETED" }
# → { "status": "EXPIRED" }
```

Poll about every two seconds with a sensible overall timeout. `EXPIRED` deliberately covers unknown, malformed, consumed, and genuinely expired keys; rerun direct-issue to obtain a fresh handoff. The status endpoint never issues tokens.

Retries normally return the same live Errand when it has at least 15 minutes remaining and the required work has not changed. This prevents an eager retry loop from splitting user progress across multiple URLs.

## Security behavior

- **Consent only:** no additional sign-in is required because the credential holder already proved control of a token-minting credential.
- **Writing identity data:** the browser requires sign-in, and the signed-in account must match the account resolved from the Steam ticket or AccessKey.
- Optional and synthetic claims never create an Errand.
- Connect `/refresh` and OIDC `/token` do not embed Errand handoffs. A native session blocked during refresh recovers by running direct-issue again.

## Related

- [Native integration](/en-us/native/overview/) — browser polling, Steam ticket, and AccessKey flows.
- [Identity claims and sharing](/en-us/concepts/identity-claims/) — the shared policy, grant, and inclusion model.
- [Tokens and verification](/en-us/concepts/tokens-and-verification/) — the tokens issued after the claim gate is satisfied.