# Create a sealed record

Creates a record under a random id the client chose, at revision 1. The server stores the sealed content as sent and never opens it.

**Idempotent on the id.** When the id already names a record of the same kind, that record's envelope is returned with `200 OK` and nothing is written, whatever the body carries; a create replayed from an offline queue never makes a duplicate. If that record is deleted, the answer is its tombstone's envelope (`deleted: true`): [undelete it](/docs/api-reference/sealed-stores/undelete-record) to bring it back. An id that names a record of another kind is `409 SEALED_RECORD_ID_CONFLICT`. The id is checked before the cap, so a replayed create is answered even when the cap has filled since.

**Caps.** A create is refused when the person already holds the kind's most live records: `422 SEALED_RECORD_LIMIT_REACHED` for `session` (500). For `favourites`, which is one per person, the refusal is `409 SEALED_RECORD_SINGLETON_EXISTS` carrying the existing record in `error.details.current`, so the client can adopt it and [update](/docs/api-reference/sealed-stores/update-record) it instead. Deleted records do not count.

A payload over 512 KiB decoded is `413 SEALED_PAYLOAD_TOO_LARGE`; the ceiling counts `payload` alone, not `wrapped_dek`.

An accepted create answers the record's envelope, without a copy of the sealed content. Requires an `Idempotency-Key` header: re-sending the same key answers the first call's exact response (header `Idempotent-Replayed: true`) and changes nothing; a refused call is not remembered, so it may be retried with the same key.

#### Path Parameters

**`store`** string required

The store: `aitronos-browser`. Up to 64 characters; an unknown store is `404 SEALED_STORE_NOT_FOUND`.

#### Headers

**`Authorization`** string required

`Bearer <access token>` from the person's own sign-in. API keys and service accounts are refused with `403 SEALED_STORE_PERSONAL_SESSION_REQUIRED`.

**`Idempotency-Key`** string required

A random id minted once for this change and sent again on every retry of it: 16 to 128 printable ASCII characters, no spaces (a UUID works). The first accepted answer is remembered for 30 days; see [Idempotency-Key](/docs/api-reference/sealed-stores/introduction#idempotency-key).

#### Body Parameters

Fields the server owns (`revision`, `created_at`, `last_touched_at`, `deleted_at`) and any other unknown top-level field are ignored.

**`id`** string required

The record's id, minted randomly by the client: 16 to 128 characters of `A-Z a-z 0-9 _ -`. Never derive it from the content.

**`kind`** string required

The record kind: `session` or `favourites`. Any other kind is `422 SEALED_KIND_INVALID`.

**`schema_version`** integer required

The client's schema and sealing version for this value, 1 to 2,147,483,647. Stored and returned as sent; the server never interprets it.

**`device_id`** string required

The writing device's id: the device id this device signed in with. 16 to 255 characters of `A-Z a-z 0-9 . _ : @ -`.

**`wrapped_dek`** object required

The record's data key, wrapped to the account's identity public key with an ephemeral X25519 key agreement and an AEAD. Unknown fields inside it are refused; a wrapped key of the wrong shape is `422 SEALED_WRAPPED_KEY_INVALID`.

- **`v`** integer — the wrap format version, `1` (the string `"1"` is accepted too).
- **`aead`** string — `aes-256-gcm` or `chacha20-poly1305`.
- **`nonce`** string — the 12-byte nonce, base64.
- **`ct`** string — the 48-byte wrapped key (key plus tag), base64.
- **`epk`** string — the 32-byte ephemeral X25519 public key, base64.


**`payload`** string required

The sealed bytes, standard padded base64. At least 28 bytes decoded (a 12-byte nonce plus a 16-byte tag) and at most 512 KiB (524,288 bytes) decoded; the ceiling counts `payload` alone, not `wrapped_dek`.

## Returns

`201 Created` with the new record's envelope at revision 1, or `200 OK` with the envelope of the record the id already names, as it is now:

- **`object`** string — `sealed_record_envelope`.
- **`id`** string — the client's record id.
- **`kind`** string — `session` or `favourites`.
- **`schema_version`** integer — as the last accepted write sent it.
- **`revision`** integer — server-assigned; rises by 1 on every accepted write.
- **`created_at`** string — when the record was created (ISO 8601 UTC).
- **`last_touched_at`** string — set by the server on every accepted write. Sort by it, ties by `id`.
- **`deleted`** boolean — whether the record is a tombstone.
- **`deleted_at`** string or null — when it was deleted.
- **`device_id`** string — the device of the last accepted write.
- **`payload_bytes`** integer or null — the decoded payload size; `null` for a tombstone.


There is no copy of the sealed content: [get the record](/docs/api-reference/sealed-stores/get-record) or read the [change feed](/docs/api-reference/sealed-stores/list-changes) for it.

Every answer carries the header **`Idempotent-Replayed`**: `false` on the first answer, `true` when this is the remembered answer to an `Idempotency-Key` sent before.

## Errors

| Status | Code | When |
|  --- | --- | --- |
| 401 | `AUTHENTICATION_REQUIRED` | No access token, or one that is not valid. |
| 403 | `SEALED_STORE_PERSONAL_SESSION_REQUIRED` | The call used an API key or a service account. Only a person's own sign-in session reaches a sealed store. |
| 404 | `SEALED_STORE_NOT_FOUND` | No store with that name (`details.store`). |
| 409 | `SEALED_OPERATION_IN_PROGRESS` | An earlier call with this `Idempotency-Key` has not finished. Retry in a moment. |
| 409 | `SEALED_RECORD_ID_CONFLICT` | The id already names a record of another kind. `details.current` is that record. |
| 409 | `SEALED_RECORD_SINGLETON_EXISTS` | The kind is `favourites` and the person already holds its one live record. `details.current` is that record: adopt it. |
| 413 | `SEALED_PAYLOAD_TOO_LARGE` | `payload` is over 512 KiB decoded (`details.field`, `details.max_bytes`, `details.payload_bytes`). |
| 422 | `SEALED_IDEMPOTENCY_KEY_REQUIRED` | No `Idempotency-Key` header. |
| 422 | `SEALED_IDEMPOTENCY_KEY_INVALID` | The `Idempotency-Key` is not 16 to 128 printable ASCII characters without spaces. |
| 422 | `SEALED_IDEMPOTENCY_KEY_REUSED` | The `Idempotency-Key` was already used for a different call. Mint a new one for a new change. |
| 422 | `SEALED_PAYLOAD_INVALID` | `payload` is not standard padded base64, or decodes to fewer than 28 bytes (`details.field`). |
| 422 | `SEALED_WRAPPED_KEY_INVALID` | `wrapped_dek` is not a wrapped key of the documented shape (`details.field`). |
| 422 | `SEALED_KIND_INVALID` | The store does not hold that kind (`details.kind`, `details.allowed`). |
| 422 | `SEALED_RECORD_LIMIT_REACHED` | The person already holds the most live records of this kind (`details.kind`, `details.limit`). Delete one to add another. |
| 422 | `VALIDATION_ERROR` | A body field is missing, of the wrong type or out of range, for example an `id` or `device_id` that does not match its pattern, or an unknown field inside `wrapped_dek`. |



```bash cURL
curl -X POST "https://api.aitronos.com/v1/sealed-stores/aitronos-browser/records" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: 9c934eaf-90e5-4535-a127-8617cd36838f" \
  -H "Content-Type: application/json" \
  -d '{
  "id": "vSWmWbg9sDur336pnYgYzQ",
  "kind": "session",
  "schema_version": 1,
  "device_id": "1f783def-2aa1-4b44-9c73-3c6b9102c06b",
  "wrapped_dek": {
    "v": 1,
    "aead": "aes-256-gcm",
    "nonce": "gMFyLEjXkypUeolk",
    "ct": "XoS0w/WPA3TGVKgAD34pyWtRe5Cn1F6D96luS3Lb3FP/Ldhw6G7amge7Lsj25Z4r",
    "epk": "GwkxMcWIWVhWBXFK4FWOTDferBsgSPLB/LquF2r4/a0="
  },
  "payload": "8oejqfQSxiYbRt85f/f1olyTQ+FURmhC7j1Rrc62awq1jmIASqh7N38qLiXyF43bxLHtsAv2t+ChSkOSbPaL542Y8Z6T4bqGUfO1rlBP4RzizRSrcn8A1HDRmYeDgWMJ"
}'
```


```python Python
import os
import uuid

import requests

# Mint the key once for this change and send the same key on every retry of it.
idempotency_key = str(uuid.uuid4())
record = {
    "id": "vSWmWbg9sDur336pnYgYzQ",
    "kind": "session",
    "schema_version": 1,
    "device_id": "1f783def-2aa1-4b44-9c73-3c6b9102c06b",
    "wrapped_dek": {
        "v": 1,
        "aead": "aes-256-gcm",
        "nonce": "gMFyLEjXkypUeolk",
        "ct": "XoS0w/WPA3TGVKgAD34pyWtRe5Cn1F6D96luS3Lb3FP/Ldhw6G7amge7Lsj25Z4r",
        "epk": "GwkxMcWIWVhWBXFK4FWOTDferBsgSPLB/LquF2r4/a0="
    },
    "payload": "8oejqfQSxiYbRt85f/f1olyTQ+FURmhC7j1Rrc62awq1jmIASqh7N38qLiXyF43bxLHtsAv2t+ChSkOSbPaL542Y8Z6T4bqGUfO1rlBP4RzizRSrcn8A1HDRmYeDgWMJ"
}

response = requests.post(
    "https://api.aitronos.com/v1/sealed-stores/aitronos-browser/records",
    headers={
        "Authorization": f"Bearer {os.environ['AITRONOS_TOKEN']}",
        "Idempotency-Key": idempotency_key,
    },
    json=record,
    timeout=30,
)
body = response.json()
if response.status_code in (200, 201):
    # 201: created. 200: the id already named this record; nothing was written.
    print("revision", body["revision"], "replayed:", response.headers["Idempotent-Replayed"])
elif body["error"]["code"] == "SEALED_RECORD_SINGLETON_EXISTS":
    existing = body["error"]["details"]["current"]  # adopt it, then update it
    print("favourites already exist:", existing["id"], "at revision", existing["revision"])
else:
    print(response.status_code, body["error"]["code"])
```


```javascript JavaScript
// Mint the key once for this change and send the same key on every retry of it.
const idempotencyKey = crypto.randomUUID();
const record = {
  "id": "vSWmWbg9sDur336pnYgYzQ",
  "kind": "session",
  "schema_version": 1,
  "device_id": "1f783def-2aa1-4b44-9c73-3c6b9102c06b",
  "wrapped_dek": {
    "v": 1,
    "aead": "aes-256-gcm",
    "nonce": "gMFyLEjXkypUeolk",
    "ct": "XoS0w/WPA3TGVKgAD34pyWtRe5Cn1F6D96luS3Lb3FP/Ldhw6G7amge7Lsj25Z4r",
    "epk": "GwkxMcWIWVhWBXFK4FWOTDferBsgSPLB/LquF2r4/a0="
  },
  "payload": "8oejqfQSxiYbRt85f/f1olyTQ+FURmhC7j1Rrc62awq1jmIASqh7N38qLiXyF43bxLHtsAv2t+ChSkOSbPaL542Y8Z6T4bqGUfO1rlBP4RzizRSrcn8A1HDRmYeDgWMJ"
};

const response = await fetch("https://api.aitronos.com/v1/sealed-stores/aitronos-browser/records", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AITRONOS_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify(record),
});
const body = await response.json();
if (response.ok) {
  console.log("revision", body.revision, "replayed:", response.headers.get("Idempotent-Replayed"));
} else if (body.error.code === "SEALED_RECORD_SINGLETON_EXISTS") {
  const existing = body.error.details.current; // adopt it, then update it
  console.log("favourites already exist:", existing.id, existing.revision);
} else {
  console.log(response.status, body.error.code);
}
```

**Response:**

201 Created

```json
{
  "object": "sealed_record_envelope",
  "id": "vSWmWbg9sDur336pnYgYzQ",
  "kind": "session",
  "schema_version": 1,
  "revision": 1,
  "created_at": "2026-10-10T08:41:17.204913Z",
  "last_touched_at": "2026-10-10T08:41:17.204913Z",
  "deleted": false,
  "deleted_at": null,
  "device_id": "1f783def-2aa1-4b44-9c73-3c6b9102c06b",
  "payload_bytes": 96
}
```

409 Favourites already exist

```json
{
  "success": false,
  "error": {
    "code": "SEALED_RECORD_SINGLETON_EXISTS",
    "message": "Your favourites are already synced.",
    "system_message": "an account holds one live 'favourites' record and this is it",
    "type": "client_error",
    "status": 409,
    "details": {
      "current": {
        "object": "sealed_record",
        "id": "F1PMCmFG6rsW4-HU4afL_g",
        "kind": "favourites",
        "schema_version": 1,
        "revision": 7,
        "created_at": "2026-10-08T17:20:09.381146Z",
        "last_touched_at": "2026-10-10T08:55:12.774031Z",
        "deleted": false,
        "deleted_at": null,
        "device_id": "1aee2c02-ccac-4ede-b017-1334be895e2e",
        "wrapped_dek": {
          "v": 1,
          "aead": "aes-256-gcm",
          "nonce": "K4oMLrGJ4dOy3SSa",
          "ct": "KF2aNtgNDsrl44sR1t60za/U14/38uhVtZR5x1IwVuE1ZA9O40F7m+B3Z5c4F5ff",
          "epk": "YTA186lzj639ytab4sP9at47MIW6rLw1mPH+rLf5zYk="
        },
        "payload": "KwbjgOqLohvGjo8CWi61+eqKsSS+Hr8joqdGVDKWywAlQ10opPF9w3HUCpIUC+fHif6s4NscN7iuK7ewtpt27Hams2lMd2TDStW8nnG53gk=",
        "payload_bytes": 80
      }
    },
    "trace_id": "926dbbfa-2dd3-4f5c-a1e6-643fb4daeec0",
    "timestamp": "2026-10-10T08:57:03.412690Z"
  }
}
```

422 Limit reached

```json
{
  "success": false,
  "error": {
    "code": "SEALED_RECORD_LIMIT_REACHED",
    "message": "You've reached the most items you can sync. Delete one to add another.",
    "system_message": "the account holds 500 live 'session' records, the most allowed",
    "type": "validation_error",
    "status": 422,
    "details": {
      "kind": "session",
      "limit": 500
    },
    "trace_id": "fc613163-c5b4-4a9c-ae0e-1f1c40a10b69",
    "timestamp": "2026-10-10T08:42:09.770145Z"
  }
}
```

413 Payload too large

```json
{
  "success": false,
  "error": {
    "code": "SEALED_PAYLOAD_TOO_LARGE",
    "message": "This item is too large to sync.",
    "system_message": "payload is 524544 bytes decoded; the ceiling is 524288",
    "type": "client_error",
    "status": 413,
    "details": {
      "field": "payload",
      "max_bytes": 524288,
      "payload_bytes": 524544
    },
    "trace_id": "8a0dad07-485c-4283-9b02-2f45eb083bbf",
    "timestamp": "2026-10-10T08:43:51.006318Z"
  }
}
```

## Related Resources

- [Sealed stores](/docs/api-reference/sealed-stores/introduction)
- [Update a sealed record](/docs/api-reference/sealed-stores/update-record)
- [Get a sealed record](/docs/api-reference/sealed-stores/get-record)
- [Get a sealed store's limits](/docs/api-reference/sealed-stores/get-store)
- [List sealed store changes](/docs/api-reference/sealed-stores/list-changes)