---
title: "Register a Single Image"
description: "Register one image and its manifests in one request, read the response, and handle each refusal the registration surface answers."
published: 2026-09-21T23:43:34.145123+00:00
updated: 2026-09-21T23:43:34.145123+00:00
tags: ["parallax", "registration", "rest"]
url: https://xiobjects.com/docs/xio/parallax/register-single-image
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-21T23:42:07.1343920\u002B00:00",
  "encoding": "utf-8",
  "render_intent": "markdown",
  "title": "Register a Single Image",
  "slug": "xio/parallax/register-single-image",
  "copyright": "\u00A9 2026 XI Objects Inc"
}
-->

# Register a Single Image

`POST /registrations` registers one image synchronously. Send the image, get a registration id back. Use it when you have one image in hand. For many images at once, use [Register in batches](/docs/xio/parallax/register-in-batches).

## The request

The body is `multipart/form-data` with a boundary. It carries two kinds of part.

| Part | Count | Content-Type | Description |
|------|-------|--------------|-------------|
| `manifest[<kind>]` | 0 or more | `application/json`, `application/jumbf` or `application/c2pa` | A document to keep with the image. The kind is stated on the part name. The form comes from the part's own Content-Type. |
| `image` | Exactly 1 | An accepted image media type | The image itself. Must be a file part named `image`. |

Every manifest part must come before the image part. A manifest part after the image part refuses the request.

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

The image's content type must be one the deployment accepts. The beta deployment accepts `image/jpeg`, `image/png` and `image/webp`.

Manifests are covered in full on [Manifests](/docs/xio/parallax/manifests).

## The response

`200 OK`.

| Field | Type | Description |
|-------|------|-------------|
| `id` | `uuid` | Your registration id. Use it to take the registration down. |
| `imageHash` | `string` | The SHA-256 of the bytes you sent, lowercase hex, computed by the service. |
| `manifests` | `array` | The manifests as registered, in the order you sent them. Each is `{ type, form, payload }`. |
| `engineRecord` | `string` | The engine's own provenance document for this registration, carried as an opaque string. Store it if you want it. Do not parse it for behaviour. |

```json
{
  "id": "6f0d4d6e-9a0b-4c2e-9b1a-2d3c4e5f6a7b",
  "imageHash": "3b8f1a...9e4c",
  "manifests": [
    { "type": "xi-manifest", "form": "json", "payload": { "caption": "carried as given" } },
    { "type": "c2pa", "form": "jumbf", "payload": "AAAAHGp1bWL/AIB/" }
  ],
  "engineRecord": "{\"originalImageHash\":\"...\",\"outcome\":\"Registered\",\"engine\":\"...\",\"engineVersion\":\"...\"}"
}
```

A JSON manifest payload comes back inline, byte for byte as you posted it. A JUMBF payload comes back as base64 of the bytes you posted. The `form` field tells you which.

## The pre-check

Before anything is registered, the service checks your image against what is already registered. This happens once, inside the same request, and it is what decides whether the image is accepted at all.

The check is global. It asks whether any account holds a live registration whose original matches your image, not just your own account.

If nothing live matches, the image goes on to register. If something does, the request registers nothing, is charged no registration, and answers `409 Conflict`.

## Already registered

`409 Conflict`, title `Image not registered`. The `detail` is one of two sentences.

| `detail` | What it means |
|----------|---------------|
| `This image is already registered.` | These exact bytes already have a live registration. |
| `This image is a derivative of an image that is already registered.` | A different image that yours derives from, a crop or an edit, already has a live registration. |

When the registration that matched is **yours**, the body carries a `registrationId` extension naming it:

```json
{
  "title": "Image not registered",
  "status": 409,
  "detail": "This image is already registered.",
  "registrationId": "6f0d4d6e-9a0b-4c2e-9b1a-2d3c4e5f6a7b",
  "traceId": "00-..."
}
```

When the match belongs to **another account**, the body is the same shape with no `registrationId` at all. Nothing else about that account or its registration is disclosed.

**What to do.** If the `registrationId` is yours and you meant to change the manifests, you cannot. There is no manifest update on a registered image, ever. [Take the registration down](/docs/xio/parallax/take-down) and register the image again with the manifests you want. If there is no `registrationId`, the image is already spoken for by someone else, and this API will not register it for you.

The check is a point in time answer. It catches an exact duplicate and a derivative of a live original. It does not catch two crops of one another uploaded in the same batch before either is committed.

A registration that has been taken down no longer counts as a live original, so a taken-down image can be registered again.

## The other refusals

| Status | Title | When |
|--------|-------|------|
| `400` | `Malformed request` | The Content-Type is not `multipart/form-data` with a boundary. |
| `400` | `Malformed request` | The parts are not zero or more manifests followed by exactly one image part. |
| `400` | `Invalid manifest` | A manifest part states no kind, states a bad kind, declares an unsupported content type, is not a JSON object, or pushes the image past the manifests-per-image cap. |
| `413` | `Request too large` | The whole request exceeds the per-request size cap. |
| `413` | `Manifest too large` | One manifest part exceeds the per-manifest size cap. |
| `413` | `Image too large` | The image part exceeds the per-image size cap. |
| `415` | `Unsupported image content type` | The image part's content type is not accepted. |
| `422` | `Image could not be checked` | The engine would not answer the pre-check for this image. Nothing was registered and nothing was charged. Retry. |
| `409` | `Open-slot cap reached` | Your account already holds the maximum number of open slots. The body carries a `cap` extension. Commit or abandon a slot and retry. |
| `429` | `Quota exceeded` | Your registration grant, or your look-up grant, does not cover this request. The body carries `registrationRemaining` and `lookupRemaining`. |
| `503` | `Engine not configured` | The deployment has no engine wired. Nothing was registered or charged. |

## Why a look-up grant can block a registration

A pre-check that finds a match gives you a global look-up verdict, so it spends one look-up. The service reserves that look-up before it calls the engine. If you have no look-up grant left, the request answers `429 Quota exceeded` before the engine is asked anything. An account with no look-ups left cannot register. See [Quotas and stats](/docs/xio/parallax/quotas-and-stats).

## What this call costs

| Outcome | Registrations spent | Look-ups spent |
|---------|--------------------|----------------|
| Registered | 1 | 0 |
| Refused as already registered or as a derivative | 0 | 1 |
| Refused for any other reason | 0 | 0 |
<!-- xion:trust
{
  "v": 1,
  "canon_v": 1,
  "ctx": "xiobjects.com/content",
  "hash_blake3_hex": "591648715dea06462a8663136df3b988f82d799ae87ce04ed32846f37b565287",
  "hash_sha256_hex": null,
  "sig_alg": "ed25519",
  "sig_b64": "f7Q56FN9IHZqdDtl_oec5JNtgRfvHW0eza5-CXvhsVmi7ckwVzIF-Drrjfwk5QvUe3WDhnoNpUt5vmucjmexAw",
  "pubkey_b64": "VrV0rqYGnIqutEwPTr11jfrjH9wmtkOlL68wL0W6ed0",
  "x509_chain_pem": [
    "-----BEGIN CERTIFICATE-----\nMIIB9DCCAaagAwIBAgIQbmGqXUijS3XuRUHDuVME8jAFBgMrZXAwLjEsMCoGA1UE\nAwwjWEkgT2JqZWN0cyBJbmMgQ29udHJvbCBJbnRlcm1lZGlhdGUwHhcNMjYwOTIx\nMjMzNjU1WhcNMjYxMDIxMjMzNjU1WjBLMR4wHAYDVQQDDBV4aW8tY29udGVudC1w\ndWJsaXNoZXIxFzAVBgNVBAoMDlhJIE9iamVjdHMgSW5jMRAwDgYDVQQLDAdDb250\nZW50MCowBQYDK2VwAyEAVrV0rqYGnIqutEwPTr11jfrjH9wmtkOlL68wL0W6ed2j\ngbwwgbkwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI\nKwYBBQUHAyQwZQYDVR0jBF4wXIAUOym3mFmw/qs1fgKrujCkxhrTk7KhLqQsMCox\nKDAmBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0GCFFJgN/ix\nQn72H6h3T5lEr9f8lJQFMB0GA1UdDgQWBBTnptRqwN8T\u002B5J0zUSRl65iscaPUzAF\nBgMrZXADQQBTzG1wSuUk70ymEN3Mj6XxsS1c5egjDoy\u002BW/V2kko5c2a1Cs7c/kiD\n6H2y9z1DNSH5qjzLZcm9JKPN0mjCy8MO\n-----END CERTIFICATE-----\n",
    "-----BEGIN CERTIFICATE-----\nMIIByDCCAXqgAwIBAgIUUmA3\u002BLFCfvYfqHdPmUSv1/yUlAUwBQYDK2VwMCoxKDAm\nBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0EwHhcNMjUxMTAy\nMDMxNzEyWhcNMzAxMTAxMDMxNzEyWjAuMSwwKgYDVQQDDCNYSSBPYmplY3RzIElu\nYyBDb250cm9sIEludGVybWVkaWF0ZTAqMAUGAytlcAMhAFSS/pggSRmTcAMko7uc\nATH8OHgxVymd5mBFlPXbJkgio4GtMIGqMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD\nVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQ7KbeYWbD\u002BqzV\u002BAqu6MKTGGtOTsjBlBgNV\nHSMEXjBcgBQAZRTDswSVORu\u002BkUOKX6WvrOvmQKEupCwwKjEoMCYGA1UEAwwfSW5z\ndGl0dXRlIG9mIFByb3ZlbmFuY2UgUm9vdCBDQYIUJqoJlpiSFg\u002B7W5IJLMrLttgR\nQp4wBQYDK2VwA0EA5FOht7YOsVRPp/FOKMQ\u002B3Mo9JxrvGR3ylKWAWNm6OUV7N3DB\nI9cD62wU5I0d0EKDBy0CX9DnoqUyxv5yguraAA==\n-----END CERTIFICATE-----\n",
    "-----BEGIN CERTIFICATE-----\nMIIBaTCCARugAwIBAgIUJqoJlpiSFg\u002B7W5IJLMrLttgRQp4wBQYDK2VwMCoxKDAm\nBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0EwHhcNMjUxMTAy\nMDMwNTEyWhcNMzUxMDMxMDMwNTEyWjAqMSgwJgYDVQQDDB9JbnN0aXR1dGUgb2Yg\nUHJvdmVuYW5jZSBSb290IENBMCowBQYDK2VwAyEAEWNZl\u002Br3IC7\u002BgBh90Yo1kWk1\npZCVzVuFdFT7qBBU8W2jUzBRMB0GA1UdDgQWBBQAZRTDswSVORu\u002BkUOKX6WvrOvm\nQDAfBgNVHSMEGDAWgBQAZRTDswSVORu\u002BkUOKX6WvrOvmQDAPBgNVHRMBAf8EBTAD\nAQH/MAUGAytlcANBAO6QeydOFNrN75qNyftggYudsxMyl4w9qWkSdZ6hlhrRcbSr\niG9Si0kbrIJOwYB/LTBU0RM4Rl\u002Bo9PM3Qp0mPwo=\n-----END CERTIFICATE-----\n"
  ],
  "key_id": "_A4EpsXvfBfVEGu5deK65yN4EjBJsUpgkdoS25zPEFk",
  "created_at": "2026-09-21T23:42:07Z"
}
-->