Biometric Upload
Upload fingerprint (nose) and appearance images using the Petnow API.
Overview
This API allows you to upload pet biometric data (fingerprint/nose images, appearance images). You must create a capture session before uploading.
All calls on this page run on your app server with the x-petnow-api-key header. Do not call them directly from a client app (that would expose the API key). A mobile/RN client sends the captured file:// images to your app server, which then uploads them via the endpoints below.
Create Capture Session
Create a capture session before uploading biometric data.
Endpoint: POST /v2/capture-sessions
Request
curl -X POST "https://api.petify.petnow.io/v2/capture-sessions" \
-H "x-petnow-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"species": "DOG",
"purpose": "PET_PROFILE_REGISTRATION",
"petId": "pet-uuid-1234"
}'import requests
url = "https://api.petify.petnow.io/v2/capture-sessions"
headers = {
"x-petnow-api-key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
data = {
"species": "DOG",
"purpose": "PET_PROFILE_REGISTRATION",
"petId": "pet-uuid-1234"
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
session_id = result["data"]["id"]
print(f"Session ID: {session_id}")const response = await fetch("https://api.petify.petnow.io/v2/capture-sessions", {
method: "POST",
headers: {
"x-petnow-api-key": "YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
species: "DOG",
purpose: "PET_PROFILE_REGISTRATION",
petId: "pet-uuid-1234"
})
});
const result = await response.json();
const sessionId = result.data.id;
console.log(`Session ID: ${sessionId}`);Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
species | string | ✅ | Species: DOG or CAT |
purpose | string | ✅ | Purpose (see table below) |
petId | string | Conditional | Required for registration/verification |
Purpose Values
| Purpose | Description | Pet ID |
|---|---|---|
PET_PROFILE_REGISTRATION | Register new pet | Required |
PET_VERIFICATION | Verify specific pet (1:1) | Required |
PET_IDENTIFICATION | Identify pet (1:N) | Not required |
Response
{
"success": true,
"data": {
"id": "session-uuid-abcd"
}
}Upload Fingerprint (Nose) Image
Upload a pet's nose (fingerprint) image.
Endpoint: POST /v2/fingerprints:upload?sessionId={sessionId}
Required: The sessionId query parameter is always required. Uploaded files are automatically linked to the session.
Request
curl -X POST "https://api.petify.petnow.io/v2/fingerprints:upload?sessionId=session-uuid-abcd" \
-H "x-petnow-api-key: YOUR_API_KEY" \
-F "file=@/path/to/nose_image.jpg"import requests
session_id = "session-uuid-abcd"
url = f"https://api.petify.petnow.io/v2/fingerprints:upload?sessionId={session_id}"
headers = {
"x-petnow-api-key": "YOUR_API_KEY"
}
with open("/path/to/nose_image.jpg", "rb") as f:
files = {"file": ("nose_image.jpg", f, "image/jpeg")}
response = requests.post(url, headers=headers, files=files)
result = response.json()
fingerprint_id = result["data"]["id"]
print(f"Fingerprint ID: {fingerprint_id}")const sessionId = "session-uuid-abcd";
const formData = new FormData();
formData.append("file", fileBlob, "nose_image.jpg");
const response = await fetch(
`https://api.petify.petnow.io/v2/fingerprints:upload?sessionId=${sessionId}`,
{
method: "POST",
headers: {
"x-petnow-api-key": "YOUR_API_KEY"
},
body: formData
}
);
const result = await response.json();
const fingerprintId = result.data.id;
console.log(`Fingerprint ID: ${fingerprintId}`);Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string (query) | ✅ | Capture session ID |
file | file | ✅ | Fingerprint (nose) image file (JPEG) |
Response
{
"success": true,
"data": {
"id": "fingerprint-uuid-1234"
}
}Upload Count: Minimum 5 fingerprint images for registration, minimum 3 for verification/identification. More images improve recognition accuracy. An appearance image is also required for every purpose — upload the appearance images the UI module provides; you do not need to track the count.
Upload Appearance Image
Upload the pet's appearance image. This is required for every capture session — registration/fingerprint addition, verification, and identification. Upload the appearance images the UI module provides; omitting them causes the action to fail with CaptureSessionIsNotFinishedException (PETNOWB2B20012).
Endpoint: POST /v2/appearances:upload?sessionId={sessionId}
Request
curl -X POST "https://api.petify.petnow.io/v2/appearances:upload?sessionId=session-uuid-abcd" \
-H "x-petnow-api-key: YOUR_API_KEY" \
-F "file=@/path/to/pet_photo.jpg"import requests
session_id = "session-uuid-abcd"
url = f"https://api.petify.petnow.io/v2/appearances:upload?sessionId={session_id}"
headers = {
"x-petnow-api-key": "YOUR_API_KEY"
}
with open("/path/to/pet_photo.jpg", "rb") as f:
files = {"file": ("pet_photo.jpg", f, "image/jpeg")}
response = requests.post(url, headers=headers, files=files)
result = response.json()
appearance_id = result["data"]["id"]
print(f"Appearance ID: {appearance_id}")const sessionId = "session-uuid-abcd";
const formData = new FormData();
formData.append("file", fileBlob, "pet_photo.jpg");
const response = await fetch(
`https://api.petify.petnow.io/v2/appearances:upload?sessionId=${sessionId}`,
{
method: "POST",
headers: {
"x-petnow-api-key": "YOUR_API_KEY"
},
body: formData
}
);
const result = await response.json();
const appearanceId = result.data.id;
console.log(`Appearance ID: ${appearanceId}`);Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string (query) | ✅ | Capture session ID |
file | file | ✅ | Appearance image file (JPEG) |
Response
{
"success": true,
"data": {
"id": "appearance-uuid-5678"
}
}Complete Upload Flow Example
Automatic Session Linking: When uploading with the sessionId query parameter, files are automatically linked to the session. No separate session end API call is required.
Finish every upload before you start a job: start registration, verification, or identification only after every upload below has returned 201. The job may not use an image that arrives after the job call. If you upload in parallel, wait for all uploads to finish first. See Registration/Verification/Identification.
import requests
from pathlib import Path
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.petify.petnow.io"
HEADERS = {"x-petnow-api-key": API_KEY}
def upload_biometrics(pet_id: str, fingerprint_files: list, appearance_files: list):
# 1. Create capture session
session_response = requests.post(
f"{BASE_URL}/v2/capture-sessions",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"species": "DOG",
"purpose": "PET_PROFILE_REGISTRATION",
"petId": pet_id
}
)
session_id = session_response.json()["data"]["id"]
print(f"Created session: {session_id}")
# 2. Upload fingerprints (auto-linked via sessionId query parameter)
fingerprint_ids = []
for file_path in fingerprint_files:
with open(file_path, "rb") as f:
response = requests.post(
f"{BASE_URL}/v2/fingerprints:upload?sessionId={session_id}",
headers=HEADERS,
files={"file": (Path(file_path).name, f, "image/jpeg")}
)
fp_id = response.json()["data"]["id"]
fingerprint_ids.append(fp_id)
print(f"Uploaded fingerprint: {fp_id}")
# 3. Upload appearances (auto-linked via sessionId query parameter)
appearance_ids = []
for file_path in appearance_files:
with open(file_path, "rb") as f:
response = requests.post(
f"{BASE_URL}/v2/appearances:upload?sessionId={session_id}",
headers=HEADERS,
files={"file": (Path(file_path).name, f, "image/jpeg")}
)
ap_id = response.json()["data"]["id"]
appearance_ids.append(ap_id)
print(f"Uploaded appearance: {ap_id}")
return session_id, fingerprint_ids, appearance_ids
# Usage example
session_id, fp_ids, ap_ids = upload_biometrics(
pet_id="pet-uuid-1234",
fingerprint_files=["nose1.jpg", "nose2.jpg", "nose3.jpg", "nose4.jpg", "nose5.jpg"],
appearance_files=["full_body.jpg"]
)const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://api.petify.petnow.io";
async function uploadBiometrics(petId, fingerprintFiles, appearanceFiles) {
const headers = { "x-petnow-api-key": API_KEY };
// 1. Create capture session
const sessionResponse = await fetch(`${BASE_URL}/v2/capture-sessions`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
species: "DOG",
purpose: "PET_PROFILE_REGISTRATION",
petId: petId
})
});
const sessionId = (await sessionResponse.json()).data.id;
console.log(`Created session: ${sessionId}`);
// 2. Upload fingerprints (auto-linked via sessionId query parameter)
const fingerprintIds = [];
for (const file of fingerprintFiles) {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(
`${BASE_URL}/v2/fingerprints:upload?sessionId=${sessionId}`,
{
method: "POST",
headers: headers,
body: formData
}
);
const fpId = (await response.json()).data.id;
fingerprintIds.push(fpId);
console.log(`Uploaded fingerprint: ${fpId}`);
}
// 3. Upload appearances (auto-linked via sessionId query parameter)
const appearanceIds = [];
for (const file of appearanceFiles) {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(
`${BASE_URL}/v2/appearances:upload?sessionId=${sessionId}`,
{
method: "POST",
headers: headers,
body: formData
}
);
const apId = (await response.json()).data.id;
appearanceIds.push(apId);
console.log(`Uploaded appearance: ${apId}`);
}
return { sessionId, fingerprintIds, appearanceIds };
}