---
title: "Create confirmation"
method: POST
path: "/api/v1/confirmations"
tags: ["Confirmations"]
---

# Create confirmation

`POST /api/v1/confirmations`

## Request

- Base URL: `https://api.proof.holdings`
- URL: `https://api.proof.holdings/api/v1/confirmations`
- Auth: HTTP bearer

Create a new HITL confirmation request. Dispatches approval message via configured HITL channels.

## Request body

- CreateConfirmationBody: Request body for creating a confirmation
  - `hitl_id` string, required: HITL configuration to use for this confirmation
  - `message` string, required: Encrypted confirmation message (ciphertext envelope JSON)
  - `proof_expiry_days` integer: Per-request proof expiry override in days (1-365). Overrides default (confirmation window).

## Response `201`

Confirmation created and dispatched

- Confirmation: A HITL confirmation request
  - `id` string, required: MongoDB ObjectId
  - `hitl_id` string, required: HITL configuration used for this confirmation
  - `message` string, required: The stored ciphertext envelope, echoed back verbatim. The server never sees the plaintext, so this is never the human sentence — the approver decrypts it in their browser.
  - `status` 'pending' | 'approved' | 'denied', required: Status of a confirmation request. A confirmation that times out is `denied`, with `response.channel` `system` and `response.text` `timeout`.
  - `response` object, nullable: Approver response details (null while pending)
    - `action` 'approve' | 'deny', required
    - `text` string, nullable: The approver's optional comment, encrypted in the browser by the approval page: a ciphertext envelope (JSON), not plaintext. When `channel` is `system` no approver acted — the confirmation was denied by a timeout, a key rotation or an archived config — and this is that plaintext reason (`timeout`, `key_rotated`, `config_archived`). Null otherwise.
    - `channel` string, nullable
    - `at` string, date-time, required
    - `authorization_id` string, nullable: The enrolled approver's Authorization that resolved this confirmation, verified server-side; null when the decision is not attributable to one approver
  - `timeout_at` string, date-time, nullable: When this confirmation will time out
  - `proof_token` string, nullable: Signed proof of an approver's decision, approval or denial alike; null while pending and for a system denial (`response.channel` is `system`)
  - `resolved_at` string, date-time, nullable
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required

## Other responses

- `400`: Validation error
- `401`: Unauthorized
- `402`: card_required. Only once billing is live: a free account with no card on file that has used its included proofs and the 50-proof grace this month is refused new proof requests until a card is added (add_card_url). Proofs already issued keep working. The error carries add_card_url, used, included and hard_limit.
- `404`: HITL config not found

---

[API](https://skmtc.dev/proof/apis/proof-holdings-api.md) · [All operations](https://skmtc.dev/proof/apis/proof-holdings-api/llms.txt) · [OpenAPI document](https://skmtc.dev/proof/apis/proof-holdings-api/revisions/6b8d61ee354f?raw)
