The three partner-facing endpoints your device exercises at feed time, with copy-pasteable curl, exact response shapes, and the documented error envelopes. Pair this page with the live /demo reference client.
Enroll a new biometric signature against an existing pet. Auth is owner-session: the request body’s owner_email must match the owner_email stored on the pets row.
curl -X POST http://localhost:3000/api/pets/<PET_ID>/biometric-signatures \ -F "image=@/path/to/nose.jpg" \ -F "owner_email=<OWNER_EMAIL>" \ -F "signature_type=nose_print" \ -F "confidence_score=0.92" \ -F "device_id=feeder-A4-21" \ -F "model_version=critterkey-v1" \ -F "notes=Initial nose-print enrollment"
Replace <PET_ID> with the pet's id from /portal and <OWNER_EMAIL> with the value used at /register. The file mimetype must be image/* and the file must be ≤ 10 MB.
| Field | Type | Required | Notes |
|---|---|---|---|
image | multipart file | required | Raw signature image bytes; image/* mimetype; ≤ 10 MB. |
owner_email | string | required | Must match pets.owner_email exactly (case-insensitive). Otherwise 403. |
signature_type | string (≤ 50 chars) | required | One of nose_print, face, iris, or your own modality label. |
confidence_score | number 0..1 | optional | Your device’s read-time confidence; must be a finite number ≥ 0 and ≤ 1. |
signature_timestamp | ISO 8601 string | optional | When the sample was captured; falls back to server time. |
device_id | string | optional | Free-form identifier your hardware echoes back on the read path. |
model_version | string (≤ 50 chars) | optional | Enrollment-pipeline version, surfaced on the read path’s metadata list. |
notes | string (≤ 2000 chars) | optional | Operator notes; trimmed and capped server-side. |
Body
{
"signature": {
"id": 17,
"pet_id": 42,
"signature_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"signature_type": "nose_print",
"confidence_score": 0.92,
"captured_at": "2026-08-11T14:32:01.000Z",
"device_id": "feeder-A4-21",
"model_version": "critterkey-v1",
"notes": "Initial nose-print enrollment",
"created_at": "2026-08-11T14:32:02.418Z"
}
}List the metadata for every signature enrolled against a pet. Auth is owner-session via X-Owner-Email header or ?owner_email= query parameter — the same gating the owner’s dashboard uses.
Raw payload is never returned. Your device never receives the raw biometric — only the modality, model version, notes, and enrollment timestamp for each signature this pet has stored. The server deliberately strips signature_hash and the underlying image bytes from the response. Verify this on your own integration: the response shape below contains neither key.
curl -X GET http://localhost:3000/api/pets/<PET_ID>/biometric-signatures \ -H "X-Owner-Email: <OWNER_EMAIL>"
Equivalent to curl ... &owner_email=<OWNER_EMAIL> as a query parameter if you cannot set headers in your device SDK.
Body
{
"pet_id": 42,
"signatures": [
{
"id": 17,
"modality": "nose_print",
"model_version": "critterkey-v1",
"notes": "Initial nose-print enrollment",
"enrolled_at": "2026-08-11T14:32:01.000Z"
}
]
}{ "pet_id": 42, "signatures": [] } — an HTTP 200 with an empty array, not a 404. This is the expected shape after a fresh registration.
Partner-device read flow. Send a fresh signature image; the server hashes it and returns ranked matches against every enrolled pet, scoped to your integration key.
Matches are hash-only. The server computes a SHA-256 fingerprint of the uploaded bytes and compares it against the per-pet hashes stored in biometric_signatures. The raw image is buffered in memory only (multer.memoryStorage()) and discarded as soon as the hash check completes — it is never persisted to disk or echoed back in any response.
Send your partner integration key in the X-API-Key request header. The server stores only the SHA-256 digest of the raw key — if your real key ever leaks, rotation is one DELETE + re-seed against partner_api_keys.
X-API-Key: "critterkey-test-key-demo"
The value above is the dev-mode seeded token — the same one shown on the live /demo reference client. Paste the curl below as-is against a running server.
curl -X POST http://localhost:3000/api/identify \ -H "X-API-Key: "critterkey-test-key-demo"" \ -F "image=@/path/to/nose.jpg" \ -F "device_id=feeder-A4-21"
Image must be image/* mimetype, ≤ 10 MB. device_id is optional and surfaces in dev-mode logs for debugging call provenance.
| Field | Type | Required | Notes |
|---|---|---|---|
image | multipart file | required | Raw signature image bytes; image/* mimetype; ≤ 10 MB. Buffered in memory only — never persisted. |
device_id | string | optional | Echoed on the dev demo page; opaque to the server. |
Body
{
"matches": [
{ "pet_id": 42, "confidence_score": 1.0 }
],
"count": 1,
"partner": "Test Partner"
}{ "matches": [], "count": 0, "partner": "Test Partner" } — an HTTP 200 not a 404. Matches are sorted by confidence_score descending and any score below 0.5 is filtered out server-side.