---
title: "Submit a public key — Anis Developers"
description: "Sends the PUBLIC half of a new P-256 key, and receives the challenge to prove you hold the private half."
url: https://developers.anis.ly/reference/submit-key/
language: en
---

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

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

# Submit a public key

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

Sends the PUBLIC half of a new P-256 key, and receives the challenge to prove you hold the private half.

- **Permission:** None
- **Authentication:** Enrolment token
- **Staff-set limits it counts toward:** None
- **.NET SDK:** `enrollment.SubmitKeyAsync(request)`

- Save the private key before you call this: an invitation takes exactly one key.

## 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:** [EnrollmentKeyRequest](#type-enrollmentkeyrequest), as JSON.

| Field | Type | Notes |
| --- | --- | --- |
| `publicJwk` (required) | [PublicJwk](#type-publicjwk) | The PUBLIC half of a new P-256 key only. A private member is refused. |
| `notBefore` (required) | string (date-time) | Only the length of the window (to `expiresAt`) is used — kept between 1 day and 2 years by default — and it starts when Anis staff activate the key. |
| `expiresAt` (required) | string (date-time) | The end of the validity you ask for. See `notBefore`. |
| `cidrs` | list of string | The networks you will call from, proposed for Anis staff to confirm. |

## Responses

| Status | Meaning | Body |
| --- | --- | --- |
| 200 | Success. | [EnrollmentKeyResult](#type-enrollmentkeyresult) |
| 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 |
| [Public key invalid](https://developers.anis.ly/errors/key-proof-invalid.md) | `key_proof_invalid` | 422 |
| [Invitation already used](https://developers.anis.ly/errors/key-duplicate.md) | `key_duplicate` | 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

### EnrollmentKeyRequest

| Field | Type | Notes |
| --- | --- | --- |
| `publicJwk` (required) | [PublicJwk](#type-publicjwk) | The PUBLIC half of a new P-256 key only. A private member is refused. |
| `notBefore` (required) | string (date-time) | Only the length of the window (to `expiresAt`) is used — kept between 1 day and 2 years by default — and it starts when Anis staff activate the key. |
| `expiresAt` (required) | string (date-time) | The end of the validity you ask for. See `notBefore`. |
| `cidrs` | list of string | The networks you will call from, proposed for Anis staff to confirm. |

### PublicJwk

| Field | Type | Notes |
| --- | --- | --- |
| `kty` (required) | string | Always `EC`. Values: `EC` |
| `crv` (required) | string | Always `P-256`. Values: `P-256` |
| `x` (required) | string | The key’s x coordinate: exactly 32 bytes, base64url without padding. |
| `y` (required) | string | The key’s y coordinate: exactly 32 bytes, base64url without padding. |
| `kid` | string | In Anis’s signing keys: the key an answer’s signature names. Treat it as an opaque name. Not sent when you submit your own key. |
| `use` | string | Intended use, when published. |
| `alg` | string | Algorithm, when published. |

### EnrollmentKeyResult

| Field | Type | Notes |
| --- | --- | --- |
| `keyId` (required) | UUID | Your key id: the `keyid` every signature will carry once the key is active. |
| `thumbprint` (required) | string | The key’s fingerprint. Give it to Anis staff through the channel you agreed; they confirm the key with it. |
| `challenge` (required) | string | The challenge your proof answers. It is returned only here: keep it until the proof is accepted. |
| `challengeGeneration` | integer | Which challenge this is; a restarted enrolment issues the next one. |
