> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnifence.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Check a sign-up

> Check users against the Shared Registry, read the result, and act on a match

A check asks the registry whether any member reported a user. Send it when a user signs up, before
the account is active. You can also check at login and in a periodic re-check of existing users.

## Send a check

`POST /api/v1/registry/check` needs a key with the `registry:check` scope.

| Field | Type | Description |
| - | - | - |
| `identifiers` | array | One to five identifiers for **one** user. Each is `{ "type": "email", "sha256": "<digest>" }`. |
| `context` | string | Why you are checking: `signup`, `login` or `periodic`. |

`sha256` is the lowercase hexadecimal digest from [Hash an email](/registry/hashing). Send more than
one identifier when the user gave you more than one email, for example a login email and a recovery
email. The registry ignores repeated identifiers.

```bash theme={null}
curl -X POST https://api.omnifence.ai/api/v1/registry/check \
  -H "Authorization: Bearer $OMNIFENCE_REGISTRY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identifiers": [
      { "type": "email", "sha256": "d6117306485ed0e50afab3ac871e98f81699151f30281527d63ff5f233656c69" },
      { "type": "email", "sha256": "5a0b70300b36ef454b660f484ad2a9353e2b4e199f623fe55789406eb07055f5" }
    ],
    "context": "signup"
  }'
```

<Warning>
  Send identifiers for one user per request. Do not combine several users in one check: the result
  cannot tell you which of them matched.
</Warning>

## Read the result

```json theme={null}
{
  "match": true,
  "signals": [
    {
      "category": "payment_fraud",
      "reporter_count": 1,
      "first_reported_at": "2026-05-11T14:02:00.000Z",
      "last_reported_at": "2026-05-11T14:02:00.000Z",
      "automated_refusal_permitted": false
    },
    {
      "category": "prohibited_content",
      "reporter_count": 2,
      "first_reported_at": "2026-03-02T10:15:00.000Z",
      "last_reported_at": "2026-08-19T08:40:00.000Z",
      "automated_refusal_permitted": true
    }
  ],
  "normalisation_version": 1
}
```

| Field | Description |
| - | - |
| `match` | `true` when at least one live entry matches any of the identifiers. |
| `signals` | One object per matching category, sorted by category. Empty when `match` is `false`. |
| `normalisation_version` | The version of the [hashing rules](/registry/hashing#versioning) the registry expects. Currently `1`. |

Each signal combines every member's report in that category. See
[What a check returns](/registry/how-it-works#what-a-check-returns) for the fields. Your own reports
count too: if you reported the user, your check matches your report.

## Act on a match

A match is information for your own decision. Use the signals to choose an action:

| Signals | Recommended action |
| - | - |
| None | Continue the sign-up. |
| At least one with `automated_refusal_permitted: true` | You can refuse the sign-up. |
| Only signals with `automated_refusal_permitted: false` | Hold the account for review by your team, or apply extra verification. Refuse only after a person reviews it, or with a clear way to contest. |

You can use `reporter_count` and the dates to weigh a signal. For example, a recent report from
several members is stronger than one old report.

This example implements the check and the decision for a sign-up handler:

```javascript theme={null}
import { registryDigest } from './registry-hash.mjs';

const REGISTRY_CHECK_URL = 'https://api.omnifence.ai/api/v1/registry/check';

export async function checkRegistry(emails, context) {
  const identifiers = [...new Set(emails.filter(Boolean).map(registryDigest).filter(Boolean))]
    .slice(0, 5)
    .map((sha256) => ({ type: 'email', sha256 }));
  if (identifiers.length === 0) return { match: false, signals: [] };

  const response = await fetch(REGISTRY_CHECK_URL, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.OMNIFENCE_REGISTRY_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ identifiers, context }),
    signal: AbortSignal.timeout(3000),
  });
  if (!response.ok) {
    const body = await response.json().catch(() => ({}));
    throw new Error(`Registry check failed: ${response.status} ${body.error ?? ''}`.trim());
  }
  return response.json();
}

export function registryDecision(result) {
  if (!result.match) return 'allow';
  if (result.signals.some((signal) => signal.automated_refusal_permitted)) return 'refuse';
  return 'review';
}
```

```javascript theme={null}
// In your sign-up handler
let decision = 'allow';
try {
  decision = registryDecision(await checkRegistry([email, recoveryEmail], 'signup'));
} catch (err) {
  // Do not block sign-ups when the registry cannot answer. Record the user
  // and check again later with context "periodic".
  console.warn(err);
}
```

### What to tell the user

* Do not tell the user that they are in the registry, which category matched, or how many platforms
  reported them. Use your normal message for a refused or held account.
* If you refuse or hold an account, give the user your usual route to contest the decision. If the
  user wants to know what the registry holds about them, refer them to
  [Disputes](/registry/disputes#for-the-people-named).
* You do not know which member reported the user, so you cannot pass that on.

## When the registry cannot answer

The registry is one signal among your own controls. If a check fails, by a timeout, a `5xx` or a
`429`, continue the sign-up as if there was no match, and check the user again later with context
`periodic`. Do not retry a failed check in a tight loop: it counts against your quota and your rate
limit. See [Errors](/registry/errors).

## Check at login and periodically

* **`login`**: Check when an existing user logs in, if you want to find users that another member
  reported after they signed up with you.
* **`periodic`**: Re-check existing users on a schedule, for example accounts that signed up while
  the registry was unreachable.

The check request and the result are the same in every context. Choose the context that describes
why you check: Omnifence monitors the mix of contexts for each member.

## Daily check quota

Your membership has a daily check quota, sized to your sign-up volume. Each check request counts
once, whatever number of identifiers it carries. The count resets at 00:00 UTC.

A check over the quota returns `429 RATE_LIMITED` with the message
`Daily Shared Registry check quota of <quota> reached`, and does no lookup. If your volume grows,
contact [support@omnifence.ai](mailto:support@omnifence.ai) to raise the quota.

Registry keys also follow your account's [rate limits](/platform/rate-limiting): by default, 60
requests per minute and 6 per second. A `429` from the rate limit carries a `retry-after` header; the
quota `429` does not.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.