---
name: access-x401-resource
description: Access an x401-protected HTTP resource on behalf of the user. Hand the PROOF-REQUEST to Proof, give the user the link to verify their identity, poll for the result, and retry with PROOF-RESPONSE.
representative_queries:
  - A URL fetch returned HTTP 401 with a PROOF-REQUEST header
  - access an x401 protected endpoint
  - prove my identity to a website with Proof
  - get a Proof verifiable credential presentation for my user
---

# Present a Verifiable Identity with x401

Use this skill when an HTTP response carries a `PROOF-REQUEST` header: the site (the *Verifier*) wants the user to
prove their identity with [Proof](https://www.proof.com/) before it serves the resource. The protocol is
[x401](https://x401.proof.com/spec/latest/).

Do not try to verify, sign in, or approve on the user's behalf. Only they can.

## Before you start

- Agents trust list: `GET https://api.proof.com/.well-known/agents-trust-list` returns the origins of the agent families that
  can use this flow, one per line. If you do not recognize yourself as belonging to one of them, tell the user you
  cannot support the x401 handoff and stop.
- Access token: follow the `create-proof-oauth-client` skill
  (`https://api.proof.com/.well-known/agent-skills/create-proof-oauth-client/SKILL.md`). Never send this token to the Verifier.

## 1. Fetch the URL

Request the resource. Keep the request (method, URL, headers, body): you send it again in step 6. The response
carries the header:

```http
PROOF-REQUEST: <base64url x401 payload>
```

## 2. Decode the PROOF-REQUEST header

The value is base64url (RFC 4648 §5, no padding) of a UTF-8 JSON object, the *x401 payload*:

```json
{
  "scheme": "x401",
  "version": "0.2.0",
  "credential_requirements": {
    "digital": {
      "requests": [
        { "protocol": "openid4vp-v1-signed", "data": { "request": "<signed OpenID4VP request>" } }
      ]
    }
  },
  "request_id": "proof-template-basic-v1"
}
```

Do not change it.

## 3. Hand the x401 payload to Proof

```sh
curl -X POST "https://api.proof.com/verifiable-credentials/v1/x401-handoff" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '<decoded x401 payload>'
```

A `201` response:

```json
{
  "request_uri": "https://<short link>",
  "result_polling_uri": "https://api.proof.com/verifiable-credentials/v1/x401-result/<id>"
}
```

- `400`: tell the user Proof rejected the site's request, with the `error_description`, and stop.
- `401`: refresh the access token and retry once.

Give the user `request_uri` and name the site that asked. They open the link, sign in, verify their identity if Proof
asks (this can take several minutes), and approve sharing it with the site. Start step 4 right away.

## 4. Poll for the result

```sh
curl "<result_polling_uri>" -H "Authorization: Bearer <access_token>"
```

| Status | Meaning | What to do |
| --- | --- | --- |
| `202` `{"status": "pending"}` | The user has not finished. | Wait `Retry-After` seconds, poll again. |
| `200` `{"status": "completed", ...}` | Done. | Go to step 5. |
| `401` | Access token expired. | Refresh it and keep polling. |
| `404` | The result expired (after one hour) or does not exist. | Tell the user and start over from step 1. |

## 5. Read the completed result

```json
{
  "status": "completed",
  "credential_result": {
    "protocol": "openid4vp-v1-signed",
    "data": { "vp_token": "<vp_token>" }
  }
}
```

The result is kept for five minutes: go to step 6 right away. It holds the user's personal data: send it only to the
Verifier that asked for it, and do not log or store it.

## 6. Retry the URL with PROOF-RESPONSE

Build the *Result Artifact*: `credential_result` copied as is, plus `request_id` when the x401 payload had one.

```json
{
  "credential_result": { "protocol": "openid4vp-v1-signed", "data": { "vp_token": "<vp_token>" } },
  "request_id": "proof-template-basic-v1"
}
```

Encode it as base64url (no padding) of its UTF-8 JSON, and send the request from step 1 again with the header:

```http
PROOF-RESPONSE: <base64url Result Artifact>
```

## 7. Read the outcome

- The Verifier serves the resource: show it to the user. It is what they asked for, so they must see it.
- The response carries `PROOF-RESULT`: the proof failed. Decode it (base64url JSON with `error` and
  `error_description`), tell the user, and do not retry.
- The response carries a new `PROOF-REQUEST`: the Verifier did not accept the result. Ask the user before starting
  over.

## Related resources

- [x401 specification](https://x401.proof.com/spec/latest/)
- `setup-x401-verifier` skill on this host: the Verifier side of x401
- [Proof Verifiable Credentials API (OpenAPI)](https://dev.proof.com/openapi/verifiable-credentials-api.json)
