---
title: "Prove you hold the key — Anis Developers"
description: "Sends the proof of possession. Once accepted, the key waits for Anis staff to record its fingerprint and confirm it."
url: https://developers.anis.ly/reference/submit-proof/
language: en
---

> Every page of this documentation: https://developers.anis.ly/llms.txt

[API reference](https://developers.anis.ly/reference.md) · Enrolment

# Prove you hold the key

`POST /v1/enrollments/{invitationId}/proof`

Sends the proof of possession. Once accepted, the key waits for Anis staff to record its fingerprint and confirm it.

- **Permission:** None
- **Authentication:** Enrolment token
- **Staff-set limits it counts toward:** None
- **.NET SDK:** `enrollment.ProveAsync(submitted, key)`

- A proof that does not verify, or comes more than about 30 minutes after the key submission, is not refused: the answer says `proofState: "failed"`. Check for `"accepted"`.
- After five failed proofs, further proofs are refused as `rate_limited` until Anis staff restart the enrolment.

## Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `invitationId` (required) | `path` | UUID | A UUID, lower-case with hyphens. |

## Request

Authorised with `Authorization: Enrollment <token>`; not signed.

Headers: `Accept-Language` (optional). The SDKs set all of them for you.

**Body:** [EnrollmentProofRequest](#type-enrollmentproofrequest), as JSON.

| Field | Type | Notes |
| --- | --- | --- |
| `keyId` (required) | UUID | The key id from the key submission. |
| `challengeGeneration` (required) | integer | The challenge generation you are answering. |
| `signature` (required) | string | ECDSA P-256/SHA-256 over the proof message, 64 bytes (not DER), base64url without padding. |

## Responses

| Status | Meaning | Body |
| --- | --- | --- |
| 200 | Success. | [EnrollmentStatus](#type-enrollmentstatus) |
| 401 | Refused: not authenticated. | [Problem](#refusals) |
| 404 | Refused: not found, or not yours. | [Problem](#refusals) |
| 409 | Refused: conflicts with the current state. | [Problem](#refusals) |
| 422 | Refused: the request breaks a rule. | [Problem](#refusals) |
| 429 | Refused: a limit was reached. | [Problem](#refusals) |
| 503 | No decision: a dependency was unavailable. | [Problem](#refusals) |

Every answer is signed by Anis; the SDKs check it before you see it.

## Refusals

Every refusal is a signed problem. Branch on its `code`; each links to what it means and what to do.

| Error | Code | Status |
| --- | --- | --- |
| [Invitation invalid](https://developers.anis.ly/errors/invitation-invalid.md) | `invitation_invalid` | 401 |
| [Not found](https://developers.anis.ly/errors/resource-not-found.md) | `resource_not_found` | 404 |
| [Validation failed](https://developers.anis.ly/errors/validation-failed.md) | `validation_failed` | 422 |
| [Challenge replaced](https://developers.anis.ly/errors/challenge-expired.md) | `challenge_expired` | 409 |
| [Rate limited](https://developers.anis.ly/errors/rate-limited.md) | `rate_limited` | 429 |
| [Service unavailable](https://developers.anis.ly/errors/dependency-unavailable.md) | `dependency_unavailable` | 503 |
| [Request timeout](https://developers.anis.ly/errors/request-timeout.md) | `request_timeout` | 504 |
| [Internal error](https://developers.anis.ly/errors/internal-error.md) | `internal_error` | 500 |

## Types

### EnrollmentProofRequest

| Field | Type | Notes |
| --- | --- | --- |
| `keyId` (required) | UUID | The key id from the key submission. |
| `challengeGeneration` (required) | integer | The challenge generation you are answering. |
| `signature` (required) | string | ECDSA P-256/SHA-256 over the proof message, 64 bytes (not DER), base64url without padding. |

### EnrollmentStatus

| Field | Type | Notes |
| --- | --- | --- |
| `keyId` | UUID | Your key id. |
| `challengeGeneration` | integer | The current challenge generation. |
| `proofState` | string | `pending`, `accepted` or `failed` — a proof that did not verify or came too late. After five failed proofs, ask Anis staff to restart the enrolment. |
| `approvalState` | string | `pending` while Anis staff have not confirmed the key, `approved` once it is active, `notApplicable` otherwise. |
| `state` | string | `pendingProof`, `pendingApproval`, `active` — the key signs requests — or `unavailable` (revoked, expired or replaced). |
| `expiresAt` | string (date-time) | When the current step expires. |
