---
title: "Registrations"
description: "Register images, singly with POST /registrations or in batches through a registration slot, and take a registration down."
published: 2026-09-23T10:54:48.88308+00:00
updated: 2026-09-23T10:54:48.88308+00:00
tags: ["api", "parallax", "reference", "rest"]
url: https://xiobjects.com/docs/xio/parallax/rest-api/reference/registrations
source: XI Objects
---

<!-- xion:doctype xion+markdown -->
<!-- xion:metadata
{
  "version": "1.0",
  "content_type": "application/xion\u002Bmarkdown",
  "source_type": "xi-content/doc",
  "generator": "xio-content-publisher/1.0.0",
  "generated": "2026-09-23T10:54:37.7246456\u002B00:00",
  "encoding": "utf-8",
  "render_intent": "markdown",
  "title": "Registrations",
  "slug": "xio/parallax/rest-api/reference/registrations",
  "copyright": "\u00A9 2026 XI Objects Inc"
}
-->

# Registrations

Registration comes in two forms: one image per request, or a slot that holds many images across as many uploads as you need and registers them all on commit. Both end in the same registration.

## POST /registrations

Registers one image synchronously. Accepts `multipart/form-data`.

**Parts:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `manifest[<kind>]` | Form part | No | Zero or more manifests. Content-Type must be `application/json`, `application/jumbf` or `application/c2pa`. All manifest parts must precede the image part. |
| `image` | File part | Yes | Exactly one image. Its content type must be accepted by the deployment. |

```bash
curl -sS -X POST "$BASE/registrations" \
  -H "Authorization: Bearer $TOKEN" \
  -F 'manifest[xi-manifest]={"caption":"carried as given"};type=application/json' \
  -F "image=@photo.png;type=image/png"
```

**Response: 200 OK**

| Field | Type | Description |
|-------|------|-------------|
| `id` | `uuid` | The registration id. |
| `imageHash` | `string` | SHA-256 of the posted bytes, lowercase hex. |
| `originalImageHash` | `string` | The engine's own hash of the original image. The key this registration's published record is read under. |
| `engineRecord` | `string` | The engine's own provenance document, opaque. |

```json
{
  "id": "6f0d4d6e-9a0b-4c2e-9b1a-2d3c4e5f6a7b",
  "imageHash": "3b8f1a...9e4c",
  "originalImageHash": "9f2c7a41e6b8d0532c1f4a9b7e0d8c6a53f1b2e4d7c0a9f8b6e3d1c4a7f0b2e5",
  "engineRecord": "{\"originalImageHash\":\"...\",\"outcome\":\"Registered\",\"engine\":\"...\",\"engineVersion\":\"...\"}"
}
```

No manifests are returned here. Read them back from `GET /records/{originalImageHash}`.

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 400 | `Malformed request` | Not multipart with a boundary, wrong parts, or a manifest part after the image part |
| 400 | `Invalid manifest` | A manifest part was refused |
| 409 | `Image not registered` | An original of this image is already registered. Carries `registrationId` when that registration is yours |
| 409 | `Open-slot cap reached` | Your open-slot cap is reached. Carries `cap` |
| 413 | `Request too large` | Past the per-request cap |
| 413 | `Manifest too large` | A manifest part is past its cap |
| 413 | `Image too large` | The image is past the per-image cap |
| 415 | `Unsupported image content type` | The image's content type is not accepted |
| 422 | `Image could not be checked` | The engine declined the pre-check |
| 429 | `Quota exceeded` | Carries `registrationRemaining` and `lookupRemaining` |
| 503 | `Image could not be checked yet` | The engine could not answer for this image yet. Nothing registered, answered or charged. `Retry-After` names the seconds to wait before sending the same request again |
| 503 | `Engine not configured` | No engine is wired |

See [Register a single image](/docs/xio/parallax/rest-api/register-single-image).

---

## DELETE /registrations/{registrationId}

Takes one of your registrations down. The image stops matching.

```bash
curl -sS -i -X DELETE "$BASE/registrations/$REGISTRATION_ID" \
  -H "Authorization: Bearer $TOKEN"
```

**Response: 204 No Content**, no body.

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 404 | `Registration not found` | Unknown, already taken down, or not yours |
| 503 | `Take-down refused` | The engine record could not be taken down. The registration is untouched |
| 503 | `Engine not configured` | No engine is wired |

See [Take down a registration](/docs/xio/parallax/rest-api/take-down).

---

## POST /slots

Opens a registration slot.

```bash
curl -sS -X POST "$BASE/slots" -H "Authorization: Bearer $TOKEN"
```

**Response: 201 Created**, with a `Location` header pointing at the slot.

| Field | Type | Description |
|-------|------|-------------|
| `slotId` | `string` | The slot's opaque id. |
| `expiresAt` | `datetime` | When the slot dies, unless activity moves it. |

```json
{
  "slotId": "sl_8x2k9f3m1q7w5e0r4t6y8u",
  "expiresAt": "2026-09-21T14:32:05.118Z"
}
```

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 409 | `Open-slot cap reached` | Carries `cap` |

---

## GET /slots/{slotId}

Reports one slot. Does not refresh its idle window.

**Response: 200 OK**

| Field | Type | Description |
|-------|------|-------------|
| `slotId` | `string` | The slot's id. |
| `status` | `string` | `open` or `committed`. |
| `entryCount` | `int` | Entries on the slot. |
| `openedAt` | `datetime` | When the slot was opened. |
| `expiresAt` | `datetime` | When the slot dies. |

```json
{
  "slotId": "sl_8x2k9f3m1q7w5e0r4t6y8u",
  "status": "open",
  "entryCount": 3,
  "openedAt": "2026-09-21T14:12:05.118Z",
  "expiresAt": "2026-09-21T14:32:05.118Z"
}
```

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 404 | `Slot not found` | Unknown, expired, abandoned, or not yours |

---

## DELETE /slots/{slotId}

Abandons the slot and everything in it. Nothing was registered, so nothing is undone. Held grant comes back.

**Response: 204 No Content**, no body.

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 404 | `Slot not found` | Unknown, already gone, or not yours |
| 409 | `Slot is no longer open` | The slot is committed |

---

## POST /slots/{slotId}/uploads

Uploads images into the slot. Accepts `multipart/form-data` carrying several images. Refreshes the slot's idle window.

**Parts:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `manifest[<kind>]` | Form part | No | Zero or more manifests for the next `image` part. |
| `image` | File part | Yes, at least one | One image per part. The part must be named `image`. |

```bash
curl -sS -X POST "$BASE/slots/$SLOT_ID/uploads" \
  -H "Authorization: Bearer $TOKEN" \
  -F 'manifest[xi-manifest]={"note":"v1"};type=application/json' \
  -F "image=@first.png;type=image/png" \
  -F "image=@second.jpg;type=image/jpeg"
```

**Response: 200 OK**

| Field | Type | Description |
|-------|------|-------------|
| `outcomes` | `array` | One upload outcome per image part, in request order. See [Upload outcome](#upload-outcome). |

```json
{
  "outcomes": [
    { "partIndex": 1, "fileName": "first.png", "accepted": true, "imageHash": "3b8f1a...9e4c", "rejectionReason": null, "registrationRemaining": null, "lookupRemaining": null, "registrationId": null },
    { "partIndex": 2, "fileName": "second.jpg", "accepted": false, "imageHash": null, "rejectionReason": "The account's remaining registration grant does not cover this image.", "registrationRemaining": 0, "lookupRemaining": 12, "registrationId": null }
  ]
}
```

An image already held under the same hash is replaced whole, taking the new request's manifests. It adds no second entry and no second reservation.

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 400 | `Malformed request` | Not multipart with a boundary, or a part this conversation does not accept |
| 404 | `Slot not found` | Unknown, expired, or not yours |
| 409 | `Slot is no longer open` | The slot is committed |
| 413 | `Request too large` | Past the per-request cap |
| 429 | `Quota exceeded` | No image in the request fit. Carries both remaining counts |
| 503 | `Engine not configured` | No engine is wired |

See [Register in batches](/docs/xio/parallax/rest-api/register-in-batches).

---

## POST /slots/{slotId}/uploads/missing

Answers which of your declared hashes the slot does not already hold. Refreshes the slot's idle window.

**Request body:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `hashes` | `string[]` | No | Image hashes, 64 lowercase hex characters each, no duplicates. Omitted or null reads as an empty list. |

```bash
curl -sS -X POST "$BASE/slots/$SLOT_ID/uploads/missing" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"hashes":["3b8f1a...9e4c","ffff...ffff"]}'
```

**Response: 200 OK**

| Field | Type | Description |
|-------|------|-------------|
| `missing` | `string[]` | The declared hashes the slot does not hold, in declared order. |

```json
{ "missing": ["ffff...ffff"] }
```

An entry the slot remembers as an errata is never reported missing.

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 400 | `Too many declared hashes` | Past the declared-list cap |
| 400 | `Invalid declared hash` | A value is not a unique 64-character lowercase hex hash |
| 404 | `Slot not found` | Unknown, expired, or not yours |
| 409 | `Slot is no longer open` | The slot is committed |

---

## GET /slots/{slotId}/manifest

Reports the slot's entries. Does not refresh the idle window. Never returns a manifest body or an image byte.

**Response: 200 OK**

| Field | Type | Description |
|-------|------|-------------|
| `entries` | `array` | One [slot entry](#slot-entry) per entry, ordered by image hash. |

```json
{
  "entries": [
    {
      "imageHash": "3b8f1a...9e4c",
      "manifestsHash": "c41d7b...80ae",
      "byteLength": 184320,
      "contentType": "image/png",
      "createdAt": "2026-09-21T14:12:41.882Z",
      "state": "held",
      "notRegisteredReason": null
    }
  ]
}
```

This route names no registration id, on any entry.

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 404 | `Slot not found` | Unknown, expired, or not yours |

---

## PUT /slots/{slotId}/entries/{imageHash}/manifests

Replaces one entry's whole manifest list. Accepts the same multipart idiom the upload does. Refreshes the slot's idle window.

**Parts:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `manifest[<kind>]` | Form part | No | Zero or more manifests. A well formed body carrying none clears the list. No other part is accepted. |

```bash
curl -sS -X PUT "$BASE/slots/$SLOT_ID/entries/$IMAGE_HASH/manifests" \
  -H "Authorization: Bearer $TOKEN" \
  -F 'manifest[xi-manifest]={"note":"replaced"};type=application/json'
```

**Response: 200 OK**, the updated [slot entry](#slot-entry).

```json
{
  "imageHash": "3b8f1a...9e4c",
  "manifestsHash": "9ac2f0...1b77",
  "byteLength": 184320,
  "contentType": "image/png",
  "createdAt": "2026-09-21T14:12:41.882Z",
  "state": "held",
  "notRegisteredReason": null
}
```

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 400 | `Malformed request` | Not multipart with a boundary |
| 400 | `Invalid manifest` | A part was refused, or a part is not a manifest part |
| 404 | `Slot not found` | Unknown, expired, or not yours |
| 404 | `Entry not found` | The slot holds no entry under that hash |
| 409 | `Slot is no longer open` | The slot is committed |
| 413 | `Manifest too large` | A part is past the per-manifest cap |

---

## DELETE /slots/{slotId}/entries/{imageHash}

Removes one entry and releases whatever it was holding. Idempotent: removing an entry that is already gone still answers `204`. Refreshes the slot's idle window.

**Response: 204 No Content**, no body.

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 404 | `Slot not found` | Unknown, expired, or not yours |
| 404 | `Entry not found` | The hash is not a valid image hash |
| 409 | `Slot is no longer open` | The slot is committed |

---

## POST /slots/{slotId}/commit

Registers every held entry. This is the only call that registers anything, and it is terminal.

```bash
curl -sS -X POST "$BASE/slots/$SLOT_ID/commit" -H "Authorization: Bearer $TOKEN"
```

**Response: 200 OK**

| Field | Type | Description |
|-------|------|-------------|
| `slotId` | `string` | The slot's id. |
| `status` | `string` | Always `committed`. |
| `entries[].imageHash` | `string` | The entry's hash. |
| `entries[].state` | `string` | `registered`, `errata`, `failed` or `retry`. |
| `entries[].registrationId` | `uuid?` | The registration this entry points at, when it has one. |
| `entries[].failureReason` | `string?` | Why the entry failed, or why an errata will not register. |

```json
{
  "slotId": "sl_8x2k9f3m1q7w5e0r4t6y8u",
  "status": "committed",
  "entries": [
    { "imageHash": "3b8f1a...9e4c", "state": "registered", "registrationId": "6f0d4d6e-9a0b-4c2e-9b1a-2d3c4e5f6a7b", "failureReason": null },
    { "imageHash": "a17c05...2f91", "state": "errata", "registrationId": null, "failureReason": "This image is a derivative of an image that is already registered." }
  ]
}
```

One entry's outcome never affects its siblings. An `errata`, `failed` or `retry` entry is charged nothing. A `retry` entry is one the engine could not answer yet: send that image again, in a new slot or through `POST /registrations`.

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 404 | `Slot not found` | Unknown, expired, or not yours |
| 409 | `Slot is no longer open` | Already committed or abandoned, or a concurrent commit won |
| 429 | `Quota exceeded` | The billable count exceeds your remaining registration grant. Nothing is registered and the slot stays open |
| 503 | `Engine not configured` | No engine is wired |

---

## GET /slots/{slotId}/progress

Reports the slot's state and every entry's own. Polling only. Does not refresh the idle window.

**Response: 200 OK**, the [progress](#progress) shape.

```json
{
  "slotId": "sl_8x2k9f3m1q7w5e0r4t6y8u",
  "status": "committed",
  "counts": { "total": 2, "held": 0, "registered": 1, "answered": 0, "failed": 0, "errata": 1 },
  "entries": [
    { "imageHash": "3b8f1a...9e4c", "state": "registered", "registrationId": "6f0d4d6e-9a0b-4c2e-9b1a-2d3c4e5f6a7b", "failureReason": null },
    { "imageHash": "a17c05...2f91", "state": "errata", "registrationId": "2c1e7f80-5d3b-4a92-8e11-0f4c6b7d8a90", "failureReason": "This image is already registered." }
  ]
}
```

**Statuses:**

| Status | Title | Meaning |
|--------|-------|---------|
| 404 | `Slot not found` | Unknown, expired, purged, or not yours |

---
<!-- xion:trust
{
  "v": 1,
  "canon_v": 1,
  "ctx": "xiobjects.com/content",
  "hash_blake3_hex": "ed6ca9cfc8dcc4e5ce18448ba09ad7c09cb1f44b31ec8c25735440f3708ed544",
  "hash_sha256_hex": null,
  "sig_alg": "ed25519",
  "sig_b64": "hPPRz43RFzjxGCyrSLRknOx28eoGWFjGSQO8cM3XEAmxEPrfAu8h0uFHGpZhmp4HaCehZZayjmLGh9XRF1SpBw",
  "pubkey_b64": "qvdEfSxeAWQCSmFhYZ6YX5kI935su0PASlrB7Yi2nJ8",
  "x509_chain_pem": [
    "-----BEGIN CERTIFICATE-----\r\nMIIB9TCCAaegAwIBAgIRAMAcad\u002BzF5t\u002B/s4nONSc6aAwBQYDK2VwMC4xLDAqBgNV\r\nBAMMI1hJIE9iamVjdHMgSW5jIENvbnRyb2wgSW50ZXJtZWRpYXRlMB4XDTI2MDky\r\nMzA0MTAzMFoXDTI2MTAyMzA0MTAzMFowSzEeMBwGA1UEAwwVeGlvLWNvbnRlbnQt\r\ncHVibGlzaGVyMRcwFQYDVQQKDA5YSSBPYmplY3RzIEluYzEQMA4GA1UECwwHQ29u\r\ndGVudDAqMAUGAytlcAMhAKr3RH0sXgFkAkphYWGemF\u002BZCPd\u002BbLtDwEpawe2Itpyf\r\no4G8MIG5MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG\r\nCCsGAQUFBwMkMGUGA1UdIwReMFyAFDspt5hZsP6rNX4Cq7owpMYa05OyoS6kLDAq\r\nMSgwJgYDVQQDDB9JbnN0aXR1dGUgb2YgUHJvdmVuYW5jZSBSb290IENBghRSYDf4\r\nsUJ\u002B9h\u002Bod0\u002BZRK/X/JSUBTAdBgNVHQ4EFgQUg7Gut2vWupuiLVcKgZt1GwdYmggw\r\nBQYDK2VwA0EA1cI0DTLhDQyTflrGrMlnMT/3Iw2c1OXVYphjr0uXnCmX1Dt5sNYT\r\niTgydyG3BPQqqiZ253V1ltTxT68ZA2gNAg==\r\n-----END CERTIFICATE-----\r\n",
    "-----BEGIN CERTIFICATE-----\r\nMIIByDCCAXqgAwIBAgIUUmA3\u002BLFCfvYfqHdPmUSv1/yUlAUwBQYDK2VwMCoxKDAm\r\nBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0EwHhcNMjUxMTAy\r\nMDMxNzEyWhcNMzAxMTAxMDMxNzEyWjAuMSwwKgYDVQQDDCNYSSBPYmplY3RzIElu\r\nYyBDb250cm9sIEludGVybWVkaWF0ZTAqMAUGAytlcAMhAFSS/pggSRmTcAMko7uc\r\nATH8OHgxVymd5mBFlPXbJkgio4GtMIGqMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD\r\nVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQ7KbeYWbD\u002BqzV\u002BAqu6MKTGGtOTsjBlBgNV\r\nHSMEXjBcgBQAZRTDswSVORu\u002BkUOKX6WvrOvmQKEupCwwKjEoMCYGA1UEAwwfSW5z\r\ndGl0dXRlIG9mIFByb3ZlbmFuY2UgUm9vdCBDQYIUJqoJlpiSFg\u002B7W5IJLMrLttgR\r\nQp4wBQYDK2VwA0EA5FOht7YOsVRPp/FOKMQ\u002B3Mo9JxrvGR3ylKWAWNm6OUV7N3DB\r\nI9cD62wU5I0d0EKDBy0CX9DnoqUyxv5yguraAA==\r\n-----END CERTIFICATE-----\r\n",
    "-----BEGIN CERTIFICATE-----\r\nMIIBaTCCARugAwIBAgIUJqoJlpiSFg\u002B7W5IJLMrLttgRQp4wBQYDK2VwMCoxKDAm\r\nBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0EwHhcNMjUxMTAy\r\nMDMwNTEyWhcNMzUxMDMxMDMwNTEyWjAqMSgwJgYDVQQDDB9JbnN0aXR1dGUgb2Yg\r\nUHJvdmVuYW5jZSBSb290IENBMCowBQYDK2VwAyEAEWNZl\u002Br3IC7\u002BgBh90Yo1kWk1\r\npZCVzVuFdFT7qBBU8W2jUzBRMB0GA1UdDgQWBBQAZRTDswSVORu\u002BkUOKX6WvrOvm\r\nQDAfBgNVHSMEGDAWgBQAZRTDswSVORu\u002BkUOKX6WvrOvmQDAPBgNVHRMBAf8EBTAD\r\nAQH/MAUGAytlcANBAO6QeydOFNrN75qNyftggYudsxMyl4w9qWkSdZ6hlhrRcbSr\r\niG9Si0kbrIJOwYB/LTBU0RM4Rl\u002Bo9PM3Qp0mPwo=\r\n-----END CERTIFICATE-----\r\n"
  ],
  "key_id": "i1xUjBgnfprOkR49BjDnH_u3g5aYtfteENcAGMjJGlA",
  "created_at": "2026-09-23T10:54:37Z"
}
-->