---
title: "Verify a receipt signature, no key needed"
method: POST
path: "/v1/receipts/verify"
---

# Verify a receipt signature, no key needed

`POST /v1/receipts/verify`

## Request

- Base URL: `https://api.recallsapi.com`
- URL: `https://api.recallsapi.com/v1/receipts/verify`
- Auth: none required

Recomputes the canonical JSON of the receipt without its signature field and verifies the Ed25519 signature with the public key named by its kid. Free and unmetered; body up to 100 KB. The same check can be done offline with the keys file.

## Request body

- object
  - `receipt` Receipt, required: A dated, verifiable record of what recallsapi.com held when a product was checked: the normalized input, match counts, the state of every source and a plain statement, signed with Ed25519. It is not a safety certification and absence of a match does not mean a product is safe. Stored for 400 days.
    - `id` string
    - `issued_at` string, date-time
    - `issuer` 'recallsapi.com (PrimeZen LLC)'
    - `input` object: The normalized input: the resolved block of the check
    - `result` object
      - `matches` object
        - `exact` integer
        - `strong` integer
        - `possible` integer
      - `signals` integer: Early-warning signals in the answer
      - `adverse_events` boolean, nullable: true when FDA MAUDE held reports for the brand and model; false when both were given and the live lookup answered nothing; null when the check did not look
    - `sources` object[]: Every recall source, as /v1/health reported it at issue time
      - `source` string
      - `status` 'ok' | 'stale' | 'pending' | 'coming_soon'
      - `last_success` string, date-time, nullable
      - `records` integer
      - `newest_record` string, nullable
    - `signal_sources` object[]: Every early-warning signal source at issue time
      - `source` string
      - `status` 'ok' | 'stale' | 'pending' | 'coming_soon'
      - `last_success` string, date-time, nullable
      - `records` integer
      - `newest_record` string, nullable
    - `statement` string: What was or was not found, as of issued_at, and what the receipt is not
    - `signature` object, nullable: null while signing is not enabled (then note says so)
      - `alg` 'Ed25519'
      - `kid` string: First 8 hex characters of the SHA-256 of the SPKI public key; look it up in /.well-known/recallsapi-keys.json
      - `value` string: base64url Ed25519 signature over the canonical JSON (object keys sorted recursively, arrays in order, no whitespace) of the receipt without the signature field
    - `note` string: Only when signature is null
    - `verify_url` string, uri: GET returns the stored receipt without a key

## Response `200`

valid true or false, with a reason when false

- Verification
  - `valid` boolean
  - `kid` string, nullable
  - `issued_at` string, nullable
  - `reason` string: Only when valid is false

## Other responses

- `400`: No receipt in the body, or the body is over 100 KB
- `413`: Body over 100 KB

---

[API](https://skmtc.dev/recallsapi/apis/recallsapi-com.md) · [All operations](https://skmtc.dev/recallsapi/apis/recallsapi-com/llms.txt) · [OpenAPI document](https://skmtc.dev/recallsapi/apis/recallsapi-com/revisions/2501df40ea54?raw)
