# Create a Contributor Network comment

```http
POST /api/public/v1/contributor-network/items/{itemId}/comments
Content-Type: application/json
```

**Scope**: `contributor-network.write`.

Creates one top-level plain-text comment on an active public item. Replies, rich text, and HTML are not supported. This is a kind-neutral community action, so clients do not send Competition or kind assertions.

Competition enrollment is not required, but the API key owner must have completed account setup.

## Path parameter

| Parameter | Requirement |
| :--- | :--- |
| `itemId` | **Required string.** Opaque Contributor Network item ID. |

## Request body

| Field | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| `body` | string | Yes | Plain text; non-empty and at most 2,000 Unicode code points after trimming. |
| `idempotencyKey` | string | Yes | Client-generated retry key; non-empty and at most 200 UTF-8 bytes after trimming. |

The server stores the trimmed `body` unchanged and applies the platform content policy before creation. Unknown fields are rejected.

## Example request

```bash
export SAIR_API_BASE="https://api.sair.foundation/api/public/v1"
export SAIR_COMMENT_KEY="comment-$(openssl rand -hex 16)"

curl -X POST \
  "$SAIR_API_BASE/contributor-network/items/$ITEM_ID/comments" \
  -H "Authorization: Bearer $SAIR_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "body": "Reproduced from the pinned revision.",
    "idempotencyKey": "'"$SAIR_COMMENT_KEY"'"
  }'
```

## Idempotency

Generate one `idempotencyKey` for the intended comment and reuse it only when retrying the same normalized body after a timeout or uncertain response.

The key namespace is the API key owner plus the comment-creation operation, not one individual API key or item. The request fingerprint includes `itemId` and the trimmed `body`; different API keys owned by the same account share this namespace.

The first successful request creates the comment and returns `201 Created`. Concurrent or later exact replays return the same comment ID with `200 OK` and never create another thread entry. If the comment has since become a tombstone, an exact replay returns that current tombstone while the item remains readable.

Reusing the key with another item or normalized body returns `409 IDEMPOTENCY_CONFLICT`. A rejected request that creates no comment does not consume the key. After a successful creation, the key remains bound even if the comment or item is later deleted, hidden, or withdrawn; it can never create a replacement comment.

## Response

Returns the `Comment` shape documented by [List comments](./list-comments.md). On the first successful creation, `author` and `body` are non-null, `isDeleted` is `false`, and `viewerCanDelete` is `true`.

Successful responses use `Content-Type: application/json` and `Cache-Control: private, no-store`.

## Errors

| HTTP | Code | When |
| :---: | :--- | :--- |
| `400` | `IDEMPOTENCY_KEY_REQUIRED` | `idempotencyKey` is missing or empty. |
| `400` | `IDEMPOTENCY_KEY_TOO_LONG` | `idempotencyKey` exceeds 200 UTF-8 bytes. |
| `400` | `MALFORMED_BODY` | The content type or JSON is invalid, a required field has the wrong type, or the request contains an unknown field. |
| `403` | `ACCOUNT_INITIALIZATION_REQUIRED` | The API key owner has not completed account setup. |
| `404` | `NOT_FOUND` | The item or its owning Competition does not exist, is not active and publicly visible, or does not expose Contributor Network. |
| `409` | `IDEMPOTENCY_CONFLICT` | The key was already used for another comment operation. |
| `422` | `COMMENT_BODY_INVALID` | `body` is empty after trimming or exceeds 2,000 Unicode code points. |
| `422` | `CONTENT_VIOLATION` | The normalized comment violates the platform content policy. |

See [Errors](../../errors.md) for shared errors.
