# Publish a model reference

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

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

Publishes an immutable pointer to one public Hugging Face model revision in the Modular Arithmetic Challenge Contributor Network.

Publication is account-scoped. The competition must be active and the caller must have completed enrollment, but an active team, team-owner role, and open formal-submission window are not required. This operation does not create, replace, or delete a formal competition submission.

## Request fields

| Field | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| `competitionId` | string | Yes | Must be `modular-arithmetic-challenge`. |
| `kind` | string | Yes | Must be `model-reference`. |
| `source` | [Model-reference source](#model-reference-source) | Yes | Exact public Hugging Face revision to publish. |
| `parentContributorNetworkItemId` | string | No | Active model reference from this competition when publishing a derived item. Omit for an original item. |
| `remark` | string | No | Publication note of at most 5,000 characters. |
| `idempotencyKey` | string | Yes | Client-generated retry key, at most 200 UTF-8 bytes after trimming. |

`track`, `modelId`, Lean Kernel confirmation fields, and other source shapes are not accepted by this operation.

### Model-reference source

```ts
type ModelReferenceSource = {
  from: "model-reference";
  modelName: string;
  commitHash: string;
};
```

`modelName` must exactly match the Hugging Face `owner/name` form `[A-Za-z0-9._-]+/[A-Za-z0-9._-]+`; surrounding whitespace is not accepted. `commitHash` must be a full 40-character hexadecimal Git commit SHA. Branches, tags, and abbreviated SHAs are not accepted, and the stored value is normalized to lowercase.

Before creating the item, SAIR verifies that the repository is public and the exact revision exists. SAIR stores the normalized `modelName@commitHash` pointer rather than model files. A later repository rename, access change, or deletion does not rewrite the immutable publication.

When `parentContributorNetworkItemId` is present, the parent must still be an active Modular Arithmetic `model-reference`. The parent may belong to another account. It records provenance only: the new item's `source` still identifies its own exact revision, and the lineage edge cannot be changed after publication.

## Idempotency

Generate one `idempotencyKey` for the intended publication. Reuse it only when retrying the same normalized source, parent, remark, competition, and kind after a timeout or uncertain response.

The first successful publication returns `201 Created`. An exact replay returns the original item with `200 OK` and does not verify Hugging Face again, generate another item or public code, create another lineage edge, or consume quota again. Reusing the key with a different request returns `409 IDEMPOTENCY_CONFLICT`.

## Example request

```bash
SAIR_IDEMPOTENCY_KEY="mac-publish-$(openssl rand -hex 16)"

curl -X POST "https://api.sair.foundation/api/public/v1/contributor-network/items" \
  -H "Authorization: Bearer $SAIR_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "competitionId": "modular-arithmetic-challenge",
    "kind": "model-reference",
    "source": {
      "from": "model-reference",
      "modelName": "your-team/modular-solver",
      "commitHash": "0123456789abcdef0123456789abcdef01234567"
    },
    "remark": "Pinned revision used for public comparison.",
    "idempotencyKey": "'"$SAIR_IDEMPOTENCY_KEY"'"
  }'
```

## Response

Returns the complete item documented by [Get a shared model reference](./get-contributor-model.md).

```json
{
  "ok": true,
  "data": {
    "id": "cn_01JMODULAR0000000000000001",
    "publicCode": "MAC01-000042",
    "competitionId": "modular-arithmetic-challenge",
    "kind": "model-reference",
    "title": "your-team/modular-solver",
    "remark": "Pinned revision used for public comparison.",
    "author": {
      "sairId": "U-9f7e1c00",
      "displayName": "alice",
      "avatarUrl": null
    },
    "contentBytes": 65,
    "contentSha256": "bf18f581ed57f05b4aac5af249017bd371991bbca1d5002fac7fc32612b01604",
    "favoriteCount": 0,
    "viewerHasFavorited": false,
    "commentCount": 0,
    "directChildCount": 0,
    "publishedAt": "2026-06-08T09:00:00Z",
    "modelName": "your-team/modular-solver",
    "commitHash": "0123456789abcdef0123456789abcdef01234567",
    "lineage": {
      "depth": 0,
      "parent": null,
      "root": {
        "id": "cn_01JMODULAR0000000000000001",
        "publicCode": "MAC01-000042",
        "available": true
      }
    }
  }
}
```

Using a new idempotency key for an already active identical model revision returns `409 DUPLICATE_CONTENT`; it does not create another lineage entry. Read the current active-publication allowance from [Get my participation](./get-my-participation.md).

## Errors

| HTTP | Code | When |
| :---: | :--- | :--- |
| `400` | `MALFORMED_BODY` | The JSON body, content type, top-level shape, or `source` shape is invalid, or an unknown field is present. |
| `400` | `IDEMPOTENCY_KEY_REQUIRED` | `idempotencyKey` is missing or empty. |
| `400` | `IDEMPOTENCY_KEY_TOO_LONG` | `idempotencyKey` exceeds 200 UTF-8 bytes. |
| `403` | `ENROLL_REQUIRED` | The caller has not completed competition enrollment. |
| `403` | `PUBLISH_CAP_EXCEEDED` | The account has no remaining active-publication capacity for this competition. |
| `404` | `NOT_FOUND` | The competition is unavailable for publication, or the requested parent is missing, inactive, or does not match this competition and kind. |
| `409` | `DUPLICATE_CONTENT` | The account already has an active publication for the same normalized model revision in this competition. |
| `409` | `IDEMPOTENCY_CONFLICT` | The key was already used for a different publication request. |
| `422` | `RESOURCE_FIELD_INVALID` | A competition, kind, model name, commit hash, parent, or remark value is invalid; this also covers a missing, private, or unavailable Hugging Face revision. |
| `429` | `DAILY_RESOURCE_LIMIT_EXCEEDED` | The account exhausted the UTC-day Contributor Network creation quota for this competition. |
| `503` | `MODEL_REFERENCE_LOOKUP_UNAVAILABLE` | Hugging Face could not be reached or returned a transient error during verification. Retry the same request with the same `idempotencyKey`. |

Withdrawing an item releases active capacity but does not restore the daily creation quota. See [Errors](../../../errors.md) for shared authentication, scope, and rate-limit errors.
