---
title: "Recover an Image's Attribution"
description: "Recover the manifests, and the material to verify them, for any image you have found. Any token holder can read any image's published record, whoever registered it."
published: 2026-09-22T02:49:03.515129+00:00
updated: 2026-09-22T02:49:03.515129+00:00
tags: ["manifests", "parallax", "records", "rest"]
url: https://xiobjects.com/docs/xio/parallax/records
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-22T02:47:25.9595079\u002B00:00",
  "encoding": "utf-8",
  "render_intent": "markdown",
  "title": "Recover an Image\u0027s Attribution",
  "slug": "xio/parallax/records",
  "copyright": "\u00A9 2026 XI Objects Inc"
}
-->

# Recover an Image's Attribution

You have found an image. You want to know who made it, what licence it carries, what its C2PA manifest says. This is the request that tells you.

`GET /records/{originalImageHash}` returns the published record for one image: the manifests it was registered with, and the material to verify that record. `POST /records` does the same for a list of hashes.

**Any token holder can read any image's record, whoever registered it.** You do not have to own the image. You do not have to have registered anything at all. The record is public to token holders, and it carries no account detail: it never names the account that registered the image, that account's registration, or anything else about them.

## The two calls that recover attribution

Recovering attribution is two requests, and it is always two.

1. **Look the image up.** [`POST /lookup`](/docs/xio/parallax/look-up-single-image) tells you whether the image is registered and gives you the `matchedOriginalImageHashes` it matched. A crop, a re-encode or a derivative matches the original it came from, so the hash you get back is the ORIGINAL image's, not your copy's.
2. **Ask for the record.** `GET /records/{originalImageHash}` returns the manifests registered with that original.

Look-up finds the original. The records request recovers the attribution.

If you already know the hash, skip step one. A registrant reads its own `originalImageHash` straight off the registration response.

## The request

```bash
curl -sS "$BASE/records/$HASH" \
  -H "Authorization: Bearer $TOKEN"
```

`{originalImageHash}` is a 64-character lowercase hex hash. Anything else is refused `400` before the engine is asked.

## The answer

`200 OK`. The `outcome` says which of three things is true of that hash.

| Field | Type | Description |
|-------|------|-------------|
| `originalImageHash` | `string` | The hash you asked for, echoed back. |
| `outcome` | `string` | `published`, `noRecordAnswered` or `takenDown`. |
| `manifests` | `array` | The manifests registered with the image. Present only when `published`. |
| `verification` | `object` | The material to verify the record. Present only when `published`. |

### A published record

```json
{
  "originalImageHash": "9f2c7a41e6b8d0532c1f4a9b7e0d8c6a53f1b2e4d7c0a9f8b6e3d1c4a7f0b2e5",
  "outcome": "published",
  "manifests": [
    {
      "type": "xi-manifest",
      "form": "json",
      "payload": { "creator": "Ada Lovelace", "licence": "CC-BY-4.0" }
    },
    {
      "type": "c2pa",
      "form": "jumbf",
      "payload": "AAAAHGp1bWL/AIB/"
    }
  ],
  "verification": {
    "contentHash": "3b1f...",
    "hashAlgorithm": "BLAKE3-256",
    "signedAtUtc": "2026-09-22T09:14:03.771Z",
    "signature": "MEUCIQ...",
    "signatureAlgorithm": "Ed25519",
    "publicKey": "MCowBQ...",
    "leafCertificate": "-----BEGIN CERTIFICATE-----\n...",
    "certificateChain": ["-----BEGIN CERTIFICATE-----\n..."],
    "leafCertificateThumbprint": "A1B2C3...",
    "trustContext": "xio",
    "trustVersion": 1,
    "canonicalVersion": 1
  }
}
```

The manifests come back exactly as they were registered. The kind is the one the registrant stated. A `json` payload is the JSON object itself, inline, byte for byte. A `jumbf` payload is the base64 of the bytes that were registered. Nothing is re-encoded, reordered or normalised on the way. See [Manifests](/docs/xio/parallax/manifests).

### No record answered

```json
{
  "originalImageHash": "0000...beef",
  "outcome": "noRecordAnswered",
  "manifests": null,
  "verification": null
}
```

This is one answer for two facts, and you cannot tell them apart. Either no record was ever published for that hash, or the record store would not answer for it right now. The API does not guess, so it does not answer `404` either: a `404` would claim a distinction nobody has. Treat it as "no attribution available", and ask again later if it matters.

### A taken-down record

```json
{
  "originalImageHash": "9f2c...b2e5",
  "outcome": "takenDown",
  "manifests": null,
  "verification": null
}
```

The registrant took the image down. Nothing else about the record is returned. See [Take down a registration](/docs/xio/parallax/take-down).

## What the verification material is for

It lets you check that the record you are reading is the one that was published, and that it was published by a key that chains to the XI trust root.

`signature` is an Ed25519 signature by the registrant's leaf key over the original image's content-hash bytes alone, which is `contentHash` hex-decoded. You verify it with `contentHash`, `signature` and `publicKey` and nothing else. You then chain `leafCertificate` and `certificateChain` to the trust root. `trustContext`, `trustVersion` and `canonicalVersion` are metadata about the record. They are not inputs to that signature.

Verifying it proves that this key attested this image.

**It does not cover the manifests.** The manifests are returned beside the signature, unsigned by that key. A C2PA manifest carries its own signatures, and you verify those with C2PA tooling. An `xi-manifest` carries none today.

The leaf certificate identifies nobody. Its subject is the service's own metadata and is identical for every registrant's certificate. Certificates differ only by key.

XI Parallax verifies nothing on your behalf. It hands you what was published and you check it yourself.

## Many hashes at once

`POST /records` takes a JSON list and answers one result per hash, in the order you asked.

```bash
curl -sS -X POST "$BASE/records" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"originalImageHashes":["9f2c...b2e5","0000...beef"]}'
```

```json
{
  "records": [
    { "originalImageHash": "9f2c...b2e5", "outcome": "published", "manifests": [], "verification": {} },
    { "originalImageHash": "0000...beef", "outcome": "noRecordAnswered", "manifests": null, "verification": null }
  ]
}
```

Each result carries the same fields the single route answers, plus a `failureReason` when the engine refused that one hash. One hash's refusal never costs its siblings their answers.

Two more outcomes can appear here, per hash, that the single route answers as a status instead:

| `outcome` | Meaning | What to do |
|-----------|---------|------------|
| `refused` | The engine would not answer for this hash. `failureReason` says why. Nothing was charged for it. | Do not repeat it. The same request answers the same thing. |
| `retry` | The engine could not answer for this hash yet. Nothing was charged for it. | Ask again for that hash shortly. |

A hash that is not 64 lowercase hex characters is answered `refused` in its own position, and is never sent to the engine.

The list is bounded by the deployment's own cap. A longer list is refused `400 Too many declared hashes` and nothing is read or charged.

## What it costs

One look-up from your look-up grant, per hash that answers something.

| `outcome` | Charged |
|-----------|---------|
| `published` | Yes |
| `takenDown` | Yes |
| `noRecordAnswered` | No |
| `refused` | No |
| `retry` | No |

A batch reserves one look-up for every hash it will send, all or nothing. If your remaining grant does not cover the whole list, the call is refused `429` and no hash is read. The refusal carries both remaining counts.

Reading a record shows up in [your stats](/docs/xio/parallax/quotas-and-stats) as a look-up spent. The registrant is not charged for anyone else's reads.

## Refusals

| Status | Title | `detail` |
|--------|-------|----------|
| `400` | `Invalid declared hash` | The path hash is not a valid 64-character lowercase hex hash. `GET` only. |
| `400` | `Malformed request` | `POST /records requires at least one original image hash.` |
| `400` | `Too many declared hashes` | The list is longer than the configured cap. `POST` only. |
| `422` | `Image could not be checked` | The engine refused this hash. Nothing was charged. `GET` only. |
| `503` | `Image could not be checked yet` | The engine could not answer this hash yet. Carries `Retry-After`. Nothing was charged. `GET` only. |
| `429` | `Quota exceeded` | Your look-up grant does not cover this call. Carries `registrationRemaining` and `lookupRemaining`. |
| `503` | `Engine not configured` | The deployment has no engine wired. Nothing was charged. |

See [Errors and refusals](/docs/xio/parallax/errors-and-refusals).

## What this request does not tell you

It tells you nothing about who registered the image. No account, no registration id, no slot, no timestamp of registration beyond the record's own `signedAtUtc`. The attribution you get back is whatever the registrant chose to put in the manifests. See [Custody and privacy](/docs/xio/parallax/custody-and-privacy).
<!-- xion:trust
{
  "v": 1,
  "canon_v": 1,
  "ctx": "xiobjects.com/content",
  "hash_blake3_hex": "fc117cdc9faa19de58d22d1e8372365f7e0bd6cddb2c62def514592b80781aa8",
  "hash_sha256_hex": null,
  "sig_alg": "ed25519",
  "sig_b64": "e4sBDDIWRqE0tARLmlmEtogzcRHoPpP1JVKB922pVDHuNRbCRduMkBDud2VR6p2XyKilZy-XQ0CGAfxQGELhCA",
  "pubkey_b64": "AtGqYUOR3nxAf7q59Sf55bZRGbBdbBAXg3UKXOKkzbQ",
  "x509_chain_pem": [
    "-----BEGIN CERTIFICATE-----\r\nMIIB9DCCAaagAwIBAgIQG\u002B0AH\u002Bf7UsGvRcIGvZRkQTAFBgMrZXAwLjEsMCoGA1UE\r\nAwwjWEkgT2JqZWN0cyBJbmMgQ29udHJvbCBJbnRlcm1lZGlhdGUwHhcNMjYwOTIy\r\nMDI0MTIyWhcNMjYxMDIyMDI0MTIyWjBLMR4wHAYDVQQDDBV4aW8tY29udGVudC1w\r\ndWJsaXNoZXIxFzAVBgNVBAoMDlhJIE9iamVjdHMgSW5jMRAwDgYDVQQLDAdDb250\r\nZW50MCowBQYDK2VwAyEAAtGqYUOR3nxAf7q59Sf55bZRGbBdbBAXg3UKXOKkzbSj\r\ngbwwgbkwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI\r\nKwYBBQUHAyQwZQYDVR0jBF4wXIAUOym3mFmw/qs1fgKrujCkxhrTk7KhLqQsMCox\r\nKDAmBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0GCFFJgN/ix\r\nQn72H6h3T5lEr9f8lJQFMB0GA1UdDgQWBBS2r6uQWP\u002BWcS10Hzd\u002BQG0NQZH\u002B/DAF\r\nBgMrZXADQQAZkyHx5hzQ3xt9dFWhhcyTd8yDMwhru\u002BcRZ0f2OgDCwY0ZzBeSuxEq\r\naoKfEGH53Mq9gKuM1CPazrwXVdXNYf0D\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": "v2f2bCoE-PZKXbGe0Vt92WLwSoVUtbmqCLYHVbCzr2Q",
  "created_at": "2026-09-22T02:47:25Z"
}
-->