Методы API
Документация Partner API
Этот справочник описывает, как ваша интеграция взаимодействует с Animal-ID от имени организации: аутентификация, обязательные заголовки и основные эндпоинты MVP (владельцы, животные, процедуры, фото, словари).
SDK и клиентские библиотеки
Официальные open-source SDK, инкапсулирующие подпись запросов и эндпоинты ниже — подключите их вместо ручной подписи запросов.
PHP · Composer
Серверный PHP SDK: HMAC-подпись запросов и типизированные клиенты для каждого эндпоинта.
composer require animal-id/aid-partner-sdk
JavaScript · npm
Пакеты для фреймворков (Node и браузер), построенные на общем ядре.
npm i @animal-id/partner-core
- @animal-id/partner-coreНезависимое от фреймворка ядро: подпись + типизированный клиент.
- @animal-id/partner-reactХуки и хелперы React.
- @animal-id/partner-vueComposables для Vue.
- @animal-id/partner-angularСервисы для Angular.
- @animal-id/partner-nestjsМодуль NestJS для серверных интеграций.
Аутентификация и подпись запроса
Базовый URL: https://gw.animal-id.net · все пути имеют префикс /v1/partner.
Каждый подписанный запрос содержит четыре заголовка. Подпись — это HMAC-SHA256 (hex) канонической строки, ключом для которого служит ваш приватный ключ:
stringToSign = METHOD + "\n" + path[?query] + "\n" + sha256_hex(rawBody) + "\n" + timestamp signature = hex( hmac_sha256(stringToSign, privateKey) )
| Заголовок | Значение |
|---|---|
X-Eternity-App-Id | Идентификатор вашего приложения. |
X-Eternity-Public-Key | Ваш публичный ключ. |
X-Eternity-Timestamp | Unix-секунды; должны находиться в пределах ±300s от времени сервера. |
X-Eternity-Signature | Вычисленный выше HMAC-SHA256 в hex. |
X-Eternity-Idempotency-Key | UUID, обязателен для каждого POST/PATCH/DELETE. Повторы возвращают первый ответ; тот же ключ + другое тело → 409. |
X-Eternity-Animal-ID-Version | Необязательная версия даты (YYYY-MM-DD). По умолчанию — версия, зафиксированная при выдаче вашего приложения; начиная с 2026-07-04, регистрация животного привязывает существующих владельцев по public_id вместо user_gid. |
X-Eternity-Expand | Необязательно. JSON-массив ключей расширения для встраивания дополнительных данных на поддерживаемых эндпоинтах (например, ["owners"]). Смотрите раздел «Expansion» эндпоинта для допустимых ключей; неизвестные ключи → 422. |
Подписывайте и отправляйте точно те же байты тела. Для GET/DELETE тело пустое (его sha256 — это хеш пустой строки). path включает строку запроса, если она присутствует. Для загрузок multipart/form-data (фотографии) необработанное тело также не является частью подписи — подписывайте с sha256 пустого тела.
Каждый успешный ответ оборачивает результаты в массив payload. Эндпоинты создания/получения одного ресурса возвращают массив из одного элемента (например, { "payload": [ { … } ] }); приведённые ниже примеры по эндпоинтам для краткости показывают один объект.
APP_ID="aid_app_xxx"; PUBLIC_KEY="pk_xxx"; PRIVATE_KEY="sk_xxx"
METHOD="POST"
PATH_Q="/v1/partner/owners" # path (+ "?query" if any), exactly as sent
BODY='{"email":"jane@example.com","consent":{"account_creation":true}}'
TS=$(date +%s)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 | awk '{print $2}')
STRING_TO_SIGN=$(printf '%s\n%s\n%s\n%s' "$METHOD" "$PATH_Q" "$BODY_HASH" "$TS")
SIG=$(printf '%s' "$STRING_TO_SIGN" | openssl dgst -sha256 -hmac "$PRIVATE_KEY" | awk '{print $2}')
curl -X "$METHOD" "https://gw.animal-id.net$PATH_Q" \
-H "X-Eternity-App-Id: $APP_ID" \
-H "X-Eternity-Public-Key: $PUBLIC_KEY" \
-H "X-Eternity-Timestamp: $TS" \
-H "X-Eternity-Signature: $SIG" \
-H "X-Eternity-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d "$BODY"const crypto = require('crypto');
function sign({ method, pathQ, body = '', privateKey }) {
const ts = Math.floor(Date.now() / 1000).toString();
const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
const stringToSign = [method, pathQ, bodyHash, ts].join('\n');
const signature = crypto.createHmac('sha256', privateKey).update(stringToSign).digest('hex');
return { ts, signature };
}
const body = JSON.stringify({ email: 'jane@example.com' });
const { ts, signature } = sign({ method: 'POST', pathQ: '/v1/partner/owners', body, privateKey: 'sk_xxx' });
await fetch('https://gw.animal-id.net/v1/partner/owners', {
method: 'POST',
headers: {
'X-Eternity-App-Id': 'aid_app_xxx',
'X-Eternity-Public-Key': 'pk_xxx',
'X-Eternity-Timestamp': ts,
'X-Eternity-Signature': signature,
'X-Eternity-Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body, // sign and send the SAME bytes
});import hashlib, hmac, time, json, uuid, requests
def sign(method, path_q, body, private_key):
ts = str(int(time.time()))
body_hash = hashlib.sha256(body.encode()).hexdigest()
string_to_sign = "\n".join([method, path_q, body_hash, ts])
signature = hmac.new(private_key.encode(), string_to_sign.encode(), hashlib.sha256).hexdigest()
return ts, signature
body = json.dumps({"email": "jane@example.com"}, separators=(",", ":"))
ts, signature = sign("POST", "/v1/partner/owners", body, "sk_xxx")
requests.post(
"https://gw.animal-id.net/v1/partner/owners",
data=body, # send the SAME bytes you signed
headers={
"X-Eternity-App-Id": "aid_app_xxx",
"X-Eternity-Public-Key": "pk_xxx",
"X-Eternity-Timestamp": ts,
"X-Eternity-Signature": signature,
"X-Eternity-Idempotency-Key": str(uuid.uuid4()),
"Content-Type": "application/json",
},
)<?php
function signRequest(string $method, string $pathQ, string $body, string $privateKey): array {
$ts = (string) time();
$bodyHash = hash('sha256', $body);
$stringToSign = implode("\n", [$method, $pathQ, $bodyHash, $ts]);
$signature = hash_hmac('sha256', $stringToSign, $privateKey);
return [$ts, $signature];
}
$body = json_encode(['email' => 'jane@example.com']);
[$ts, $signature] = signRequest('POST', '/v1/partner/owners', $body, 'sk_xxx');
$ch = curl_init('https://gw.animal-id.net/v1/partner/owners');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body, // send the SAME bytes you signed
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Eternity-App-Id: aid_app_xxx",
"X-Eternity-Public-Key: pk_xxx",
"X-Eternity-Timestamp: $ts",
"X-Eternity-Signature: $signature",
'X-Eternity-Idempotency-Key: ' . bin2hex(random_bytes(16)),
'Content-Type: application/json',
],
]);
$response = curl_exec($ch);Вебхуки
Получайте отложенные события — например, когда владелец одобряет запрос ветеринара на доступ — в виде подписанных POST-запросов на ваш сервер. Укажите URL и найдите секрет подписи на вкладке API-ключей.
Подписанная доставка вебхукаПодпись и тело вебхука▸Мы отправляем POST для каждого события на указанный вами URL вебхука, подписанный вашим отдельным секретом вебхука (показывается один раз при генерации). Проверьте подпись, прежде чем доверять доставке: пересчитайте HMAC по каноничной строке и сравните за постоянное время:
canonical = "POST" + "\n" + path[?query] + "\n" + sha256_hex(rawBody) + "\n" + timestamp signature = hex( hmac_sha256(canonical, webhookSecret) )
| Заголовок | Значение |
|---|---|
X-Eternity-Webhook-Id | Уникальный идентификатор доставки (UUID); неизменен при повторных отправках того же события. |
X-Eternity-Webhook-Event | Ключ события, напр. animal_access.approved. |
X-Eternity-Webhook-Timestamp | Unix-секунды, когда доставка была подписана; отклоняйте доставки с расходящимся временем. |
X-Eternity-Webhook-Signature | HMAC-SHA256 (hex) каноничной строки, ключом является ваш секрет вебхука (не приватный ключ API). |
Тело доставки (JSON)
{
"id": "5f1c0b8e-3a2d-4c7b-9b1a-2e6f0d4c8a91",
"event": "animal_access.approved",
"occurred_at": "2026-06-24T09:15:00+00:00",
"result": {
"animal_id": "8xK3pQzVnB7rL2qF",
"requester_user_gid": 90231,
"status": "granted",
"requested_at": "2026-06-23T09:00:00+00:00",
"expires_at": "2026-06-30T09:00:00+00:00",
"retry_after_seconds": 0,
"decided_at": "2026-06-24T09:15:00+00:00"
}
}| Поле | Описание |
|---|---|
id | Уникальный идентификатор события (совпадает с заголовком X-Eternity-Webhook-Id). |
event | Ключ события, напр. animal_access.approved. |
occurred_at | Когда произошло событие (ISO 8601). |
result | Решение о доступе. Повторяет GET /v1/partner/animals/{id}/access-request и добавляет animal_id, requester_user_gid и decided_at. |
События
| Ключ | Значение |
|---|---|
animal_access.approved | Владелец одобрил ваш ожидающий запрос на доступ — теперь вы можете редактировать животное. |
animal_access.denied | Владелец отклонил ваш запрос на доступ — вы можете запросить снова после истечения срока. |
Подтверждайте любым ответом 2xx. Статус, отличный от 2xx, или таймаут записывается как неудачная доставка, которую вы можете отправить повторно из журнала доставок вебхуков в кабинете.
const crypto = require('crypto');
// Express: mount with a raw-body parser so you verify the EXACT bytes received.
// app.post('/animal-id/webhook', express.raw({ type: 'application/json' }), handler)
function verify(req, webhookSecret) {
const ts = req.header('X-Eternity-Webhook-Timestamp');
const sig = req.header('X-Eternity-Webhook-Signature');
const path = req.originalUrl; // path (+ ?query) exactly as received
const raw = req.body; // Buffer of the raw request body
const bodyHash = crypto.createHash('sha256').update(raw).digest('hex');
const canonical = ['POST', path, bodyHash, ts].join('\n');
const expected = crypto.createHmac('sha256', webhookSecret).update(canonical).digest('hex');
return sig && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}<?php
// Verify a webhook delivery from Animal ID before trusting it.
function verifyWebhook(string $rawBody, string $path, string $webhookSecret): bool {
$ts = $_SERVER['HTTP_X_ETERNITY_WEBHOOK_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_ETERNITY_WEBHOOK_SIGNATURE'] ?? '';
$canonical = implode("\n", ['POST', $path, hash('sha256', $rawBody), $ts]);
$expected = hash_hmac('sha256', $canonical, $webhookSecret);
return $sig !== '' && hash_equals($expected, $sig);
}
$rawBody = file_get_contents('php://input'); // verify the EXACT bytes received
if (!verifyWebhook($rawBody, $_SERVER['REQUEST_URI'], getenv('AID_WEBHOOK_SECRET'))) {
http_response_code(401);
exit;
}
$event = json_decode($rawBody, true); // ['id' => …, 'event' => …, 'result' => …]
http_response_code(204); // acknowledge with any 2xxМетоды API
Начните вводить — разделы отфильтруются по ключевым словам.
/v1/partner/dictionariesСправочники (многоязычные, с фильтрацией).▸Авторизация: Публичный (подпись не требуется). Кэшируется в CDN через ETag.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
include | нет | string (csv) | — | Разделённые запятыми ключи словарей для возврата; пусто → все. Ключи: species, sex, sizes, lost_statuses, other_identifiers, procedure_types, countries, languages, cites. |
q | нет | string | — | Фильтровать записи по локализованному названию на любом активном языке. |
lang | нет | string | languages | Проецировать названия в одну локаль (uk, en, ru, de, es). По умолчанию: все активные. |
Пример ответа
{
"payload": [
{
"key": "species",
"items": [
{ "code": 3, "names": { "uk": "Собаки", "en": "Dogs", "ru": "Собаки", "de": "Dogs" } },
{ "code": 4, "names": { "uk": "Коти", "en": "Cats" } }
]
},
{
"key": "countries",
"items": [
{ "code": "804", "alpha2": "UA", "alpha3": "UKR",
"names": { "uk": "Україна", "en": "Ukraine", "ru": "Украина", "de": "Ukraine", "es": "Ukraine" } }
]
},
{
"key": "languages",
"items": [
{ "code": "uk", "native": "Українська",
"names": { "uk": "Українська", "en": "Ukrainian", "ru": "Украинский", "de": "Ukrainian", "es": "Ukrainian" } }
]
}
],
"metadata": { "etag": "W/\"dict-…\"", "generated_at": "2026-05-30T08:00:00+00:00", "languages": ["uk","en","ru","de","es"] },
"links": [],
"message": null
}| Поле | Описание |
|---|---|
payload[].key | Ключ словаря. |
payload[].items[].code | Стабильный идентификатор, используемый в качестве значения в эндпоинтах записи. Числовой для большинства словарей (species, sex, …); для countries это дополненный нулями числовой код ISO 3166-1 в виде строки (например, "004", "804"); для languages это код ISO 639-1 (например, "uk"). |
payload[].items[].names | Сопоставление локаль → локализованное название. Локали без перевода откатываются на английский. |
payload[].items[].alpha2 / alpha3 | Только страны: коды ISO 3166-1 alpha-2 / alpha-3 для удобного сопоставления. |
payload[].items[].native | Только языки: собственное название языка (эндоним), удобно для выбора языка. |
metadata.etag | Передайте обратно в If-None-Match, чтобы получить 304 при отсутствии изменений. |
metadata.languages | Активные локали, реально присутствующие в этой сборке. |
Статусы ответа
| Статус | Значение |
|---|---|
200 | OK — словари возвращены. |
304 | Not Modified — ваш If-None-Match совпадает; используйте кэшированную копию. |
/v1/partner/ownersСоздать (или найти) владельца; возвращает глобальный ID пользователя.▸Авторизация: HMAC. Любой партнёрский ключ. · X-Eternity-Idempotency-Key обязательно
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
email | условно | string | — | Email владельца. Требуется один из email/phone — именно так с владельцем позже связываются/аутентифицируют его. |
phone | условно | string | — | Телефон владельца (E.164). Требуется один из email/phone. |
first_name | нет | string | — | Имя. |
last_name | нет | string | — | Фамилия. |
language | нет | string | languages | Предпочитаемая локаль. |
country | нет | string | countries | Дополненный нулями числовой код ISO 3166-1 в виде строки (например, "804") — соответствует коду из словаря countries. |
consent | да | object | — | Блок согласия (неизменяемый аудит). |
consent.account_creation | да | bool | — | Должно быть true — владелец согласился на создание учётной записи. Время фиксации записывается на стороне сервера. |
Тело запроса (JSON)
{
"email": "jane@example.com",
"phone": "+380681234567",
"first_name": "Jane",
"last_name": "Doe",
"language": "uk",
"country": "804",
"consent": {
"account_creation": true
}
}Пример ответа
{
"payload": {
"user_gid": 90231,
"public_id": "V1StGXR8Z5jd",
"has_account": true,
"email": "jane@example.com",
"phone": null,
"display_hint": "Ол*** К.",
"language": "uk",
"country_id": 804
}
}| Поле | Описание |
|---|---|
public_id | Стабильный публичный идентификатор владельца — передайте его в POST /animals owners[].public_id (версия API >= 2026-07-04). |
user_gid | Устаревший числовой идентификатор владельца (привязка владельца в старых версиях API). |
has_account | Есть ли у владельца уже работающая учётная запись. |
email | Email, имеющийся в записи для этого владельца (null, если неизвестен). |
phone | Телефон, имеющийся в записи для этого владельца (null, если неизвестен). |
display_hint | Маскированное отображаемое имя (без PII). |
Статусы ответа
| Статус | Значение |
|---|---|
201 | Создан (или сопоставлен существующий владелец — идемпотентно по email/phone). |
409 | X-Eternity-Idempotency-Key повторно использован с другим телом или ещё обрабатывается. |
422 | Ошибка валидации (отсутствует email/phone или consent.account_creation не принят). |
/v1/partner/owners/searchНайти владельца по точному email или телефону.▸Авторизация: HMAC. Любой партнёрский ключ.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
email_or_phone | да | string | — | Точный email или телефон (одно поле; email определяется по формату). |
Пример ответа
{
"payload": { "user_gid": 90231, "public_id": "V1StGXR8Z5jd", "has_account": true, "email": "jane@example.com", "phone": null, "display_hint": "Ол*** К.", "language": "uk", "country_id": 804 }
}| Поле | Описание |
|---|---|
public_id | Стабильный публичный идентификатор владельца — передайте его в POST /animals owners[].public_id (версия API >= 2026-07-04). |
user_gid | Устаревший числовой идентификатор владельца (привязка владельца в старых версиях API). |
email | Email, имеющийся в записи для этого владельца (null, если неизвестен). |
phone | Телефон, имеющийся в записи для этого владельца (null, если неизвестен). |
Статусы ответа
| Статус | Значение |
|---|---|
200 | Владелец найден. |
404 | Нет владельца с таким email_or_phone. |
422 | Требуется email_or_phone. |
/v1/partner/animalsЗарегистрировать животное (по чипу).▸Авторизация: HMAC. Ключ ветеринара/организации. · X-Eternity-Idempotency-Key обязательно
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
species | да | int | species | Идентификатор из словаря species. |
is_microchip | да | bool | — | Чипировано ли животное. true → microchip обязателен; false → microchip игнорируется, и реестр присваивает временный номер WC. |
microchip | условно | string | — | Микрочип (транспондер). Требуется только когда is_microchip = true. |
nickname | да | string | — | Кличка животного. |
qr_tag | нет | string | — | Серийный номер QR-паспорта для привязки при регистрации. |
owners | нет | array | — | Владельцы; первый становится main_owner, остальные — owners (с устранением дубликатов). Каждая запись либо привязывает существующего владельца, ЛИБО регистрирует нового на месте. |
owners[].public_id | условно | string | — | Режим привязки (версия API >= 2026-07-04): public_id существующего владельца из POST/GET owners. Пропустите, чтобы зарегистрировать встроенно. |
owners[].user_gid | условно | int | — | Режим привязки (версии API до 2026-07-04): устаревший числовой идентификатор владельца. Пропустите, чтобы зарегистрировать встроенно. |
owners[].email | условно | string | — | Встроенный режим: email владельца. Требуется одно из email/phone, когда нет public_id/user_gid (upsert — без дубликатов). |
owners[].phone | условно | string | — | Режим на месте: телефон владельца (E.164). |
owners[].first_name | нет | string | — | Режим на месте: имя. |
owners[].last_name | нет | string | — | Режим на месте: фамилия. |
owners[].language | нет | string | languages | Режим на месте: предпочитаемая локаль. |
owners[].country | нет | string | countries | Режим на месте: дополненный нулями числовой код ISO 3166-1 в виде строки (например, "804") — соответствует коду из словаря countries. |
owners[].consent.account_creation | условно | bool | — | Режим на месте: должно быть true — владелец согласился на создание учётной записи. Требуется только при регистрации на месте. |
breed | нет | string | — | Порода (свободный текст — без словаря). |
color | нет | string | — | Окрас (свободный текст — без словаря). |
gender_id | нет | int | sex | Идентификатор из словаря gender. |
dob | нет | date | — | Дата рождения (ISO 8601). |
microchip_date | нет | date | — | Когда был имплантирован чип (ISO 8601). |
sterilization | нет | bool | — | Флаг стерилизации. |
size | нет | int | sizes | Идентификатор из словаря size. |
identifiers | нет | array | — | Дополнительные идентификаторы помимо microchip/qr. |
identifiers[].type | да | int | other_identifiers | Идентификатор типа из словаря other_identifiers (tattoo, ring, …). |
identifiers[].value | да | string | — | Значение идентификатора. |
identifiers[].added_at | нет | date | — | Когда идентификатор был присвоен (ISO 8601). |
Тело запроса (JSON)
{
"species": 3,
"is_microchip": true,
"microchip": "900263000123456",
"nickname": "Барсік",
"qr_tag": null,
"gender_id": 1,
"breed": "Labrador",
"color": "black",
"dob": "2022-03-01T00:00:00+00:00",
"microchip_date": "2022-11-20T00:00:00+00:00",
"sterilization": true,
"size": 2,
"owners": [
{ "public_id": "V1StGXR8Z5jd" },
{
"email": "jane@example.com",
"phone": "+380681234567",
"first_name": "Jane",
"last_name": "Doe",
"consent": { "account_creation": true }
}
],
"identifiers": [
{ "type": 3, "value": "TAT-001", "added_at": "2026-05-01T00:00:00+00:00" }
]
}Пример ответа
{ "payload": { "id": "8xK3pQzVnB7rL2qF" } }| Поле | Описание |
|---|---|
id | Неугадываемый публичный идентификатор животного (NanoID). Используйте его во всех последующих вызовах по животному. |
Статусы ответа
| Статус | Значение |
|---|---|
201 | Животное зарегистрировано. |
409 | Конфликт X-Eternity-Idempotency-Key (тот же ключ, другое тело). |
422 | Ошибка валидации: отсутствует species/nickname/is_microchip; is_microchip=true без действительного microchip; дублирующийся microchip (транспондер уже зарегистрирован, поле "transponder"); или владелец на месте без email/phone либо без consent.account_creation. |
/v1/partner/animals/by-identifier/{type}/{value}Поиск по конкретному типу идентификатора. Всегда массив.▸Авторизация: HMAC. Любой партнёрский ключ.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
type | да | path enum | — | microchip или qr_tag. |
value | да | path string | — | Значение идентификатора для поиска. |
Пример ответа
{
"payload": [
{ "id": "8xK3pQzVnB7rL2qF", "species": 3, "breed": "Labrador", "color": "black", "gender_id": 1,
"nickname": "Барсік", "microchip": "900263000123456", "qr_tag": null,
"dob": "2022-03-01", "register_date": "2026-05-30", "sterilization_status": true,
"lost_status": null, "deceased": false, "died_at": null, "status": 1,
"abilities": { "can_edit": true } }
]
}| Поле | Описание |
|---|---|
payload | Массив карточек животных — обычно одна, но значение может разрешиться в несколько. |
microchip / qr_tag | Активные идентификаторы. |
lost_status | "active", когда заявлено об утере, иначе null. |
deceased | True, как только зафиксирована эвтаназия/смерть. |
Возможности
Каждый объект животного содержит объект abilities, описывающий, что аутентифицированный пользователь-партнёр может делать с этим животным. Это первый флаг доступа; со временем будут добавлены и другие.
| Поле | Описание |
|---|---|
abilities.can_edit | Может ли аутентифицированный пользователь-партнёр редактировать это животное — обновлять его данные, добавлять процедуры и управлять фотографиями. True, когда пользователь зарегистрировал животное, имеет к нему любое отношение (animal_user_relation) или является активным участником организации, в реестре которой оно числится (org_animals). |
Расширение (X-Eternity-Expand)
Необязательный заголовок, содержащий JSON-массив ключей расширения (например, ["owners"]). Каждый запрошенный ключ встраивает дополнительное поле в каждый объект животного в ответе; опустите заголовок, чтобы получить обычную карточку. Неизвестные ключи отклоняются с 422.
| Ключ | Добавляет | Описание |
|---|---|---|
owners | owners[] | Владельцы животного — основной владелец плюс любые совладельцы — каждый с контактными данными и флагом is_main_owner. Доступно только в этом партнёрском интерфейсе. |
"owners": [
{
"user_gid": 90231,
"public_id": "V1StGXR8Z5jd",
"has_account": true,
"email": "jane@example.com",
"phone": "+380681234567",
"display_hint": "Ja*** D.",
"language": "uk",
"country_id": "804",
"is_main_owner": true
}
]| Поле | Описание |
|---|---|
owners[].public_id | Стабильный публичный идентификатор владельца — передайте его в POST /animals owners[].public_id (версия API >= 2026-07-04). |
owners[].user_gid | Устаревший числовой идентификатор владельца (привязка владельца в старых версиях API). |
owners[].has_account | Есть ли у владельца уже работающая учётная запись. |
owners[].email | Email, имеющийся в записи для этого владельца (null, если неизвестен). |
owners[].phone | Телефон, имеющийся в записи для этого владельца (null, если неизвестен). |
owners[].display_hint | Маскированное отображаемое имя (без PII). |
owners[].language | Предпочитаемая локаль. |
owners[].country_id | Дополненный нулями числовой код ISO 3166-1 в виде строки (например, "804"). |
owners[].is_main_owner | true для основного владельца (animal_user_relation типа main_owner); false для совладельцев. |
Статусы ответа
| Статус | Значение |
|---|---|
200 | OK — массив (возможно, пустой). |
/v1/partner/animals/by-identifier/{value}Поиск по всем типам идентификаторов сразу. Всегда массив.▸Авторизация: HMAC. Любой партнёрский ключ.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
value | да | path string | — | Значение идентификатора; поиск ведётся по microchip и qr_tag. |
Пример ответа
{ "payload": [ { "id": "8xK3pQzVnB7rL2qF", "nickname": "Барсік", "microchip": "900263000123456" } ] }Возможности
Каждый объект животного содержит объект abilities, описывающий, что аутентифицированный пользователь-партнёр может делать с этим животным. Это первый флаг доступа; со временем будут добавлены и другие.
| Поле | Описание |
|---|---|
abilities.can_edit | Может ли аутентифицированный пользователь-партнёр редактировать это животное — обновлять его данные, добавлять процедуры и управлять фотографиями. True, когда пользователь зарегистрировал животное, имеет к нему любое отношение (animal_user_relation) или является активным участником организации, в реестре которой оно числится (org_animals). |
Расширение (X-Eternity-Expand)
Необязательный заголовок, содержащий JSON-массив ключей расширения (например, ["owners"]). Каждый запрошенный ключ встраивает дополнительное поле в каждый объект животного в ответе; опустите заголовок, чтобы получить обычную карточку. Неизвестные ключи отклоняются с 422.
| Ключ | Добавляет | Описание |
|---|---|---|
owners | owners[] | Владельцы животного — основной владелец плюс любые совладельцы — каждый с контактными данными и флагом is_main_owner. Доступно только в этом партнёрском интерфейсе. |
"owners": [
{
"user_gid": 90231,
"public_id": "V1StGXR8Z5jd",
"has_account": true,
"email": "jane@example.com",
"phone": "+380681234567",
"display_hint": "Ja*** D.",
"language": "uk",
"country_id": "804",
"is_main_owner": true
}
]| Поле | Описание |
|---|---|
owners[].public_id | Стабильный публичный идентификатор владельца — передайте его в POST /animals owners[].public_id (версия API >= 2026-07-04). |
owners[].user_gid | Устаревший числовой идентификатор владельца (привязка владельца в старых версиях API). |
owners[].has_account | Есть ли у владельца уже работающая учётная запись. |
owners[].email | Email, имеющийся в записи для этого владельца (null, если неизвестен). |
owners[].phone | Телефон, имеющийся в записи для этого владельца (null, если неизвестен). |
owners[].display_hint | Маскированное отображаемое имя (без PII). |
owners[].language | Предпочитаемая локаль. |
owners[].country_id | Дополненный нулями числовой код ISO 3166-1 в виде строки (например, "804"). |
owners[].is_main_owner | true для основного владельца (animal_user_relation типа main_owner); false для совладельцев. |
Статусы ответа
| Статус | Значение |
|---|---|
200 | OK — массив (возможно, пустой). |
/v1/partner/animals/by-ownerПоиск животных по контакту владельца. Всегда массив.▸Авторизация: HMAC. Любой партнёрский ключ.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
email_or_phone | да | string | — | Точный email или телефон владельца (одно поле; email определяется по формату). Телефон сначала разрешается до владельца. |
Пример ответа
{ "payload": [ { "id": "8xK3pQzVnB7rL2qF", "nickname": "Барсік", "species": 3 } ] }Возможности
Каждый объект животного содержит объект abilities, описывающий, что аутентифицированный пользователь-партнёр может делать с этим животным. Это первый флаг доступа; со временем будут добавлены и другие.
| Поле | Описание |
|---|---|
abilities.can_edit | Может ли аутентифицированный пользователь-партнёр редактировать это животное — обновлять его данные, добавлять процедуры и управлять фотографиями. True, когда пользователь зарегистрировал животное, имеет к нему любое отношение (animal_user_relation) или является активным участником организации, в реестре которой оно числится (org_animals). |
Расширение (X-Eternity-Expand)
Необязательный заголовок, содержащий JSON-массив ключей расширения (например, ["owners"]). Каждый запрошенный ключ встраивает дополнительное поле в каждый объект животного в ответе; опустите заголовок, чтобы получить обычную карточку. Неизвестные ключи отклоняются с 422.
| Ключ | Добавляет | Описание |
|---|---|---|
owners | owners[] | Владельцы животного — основной владелец плюс любые совладельцы — каждый с контактными данными и флагом is_main_owner. Доступно только в этом партнёрском интерфейсе. |
"owners": [
{
"user_gid": 90231,
"public_id": "V1StGXR8Z5jd",
"has_account": true,
"email": "jane@example.com",
"phone": "+380681234567",
"display_hint": "Ja*** D.",
"language": "uk",
"country_id": "804",
"is_main_owner": true
}
]| Поле | Описание |
|---|---|
owners[].public_id | Стабильный публичный идентификатор владельца — передайте его в POST /animals owners[].public_id (версия API >= 2026-07-04). |
owners[].user_gid | Устаревший числовой идентификатор владельца (привязка владельца в старых версиях API). |
owners[].has_account | Есть ли у владельца уже работающая учётная запись. |
owners[].email | Email, имеющийся в записи для этого владельца (null, если неизвестен). |
owners[].phone | Телефон, имеющийся в записи для этого владельца (null, если неизвестен). |
owners[].display_hint | Маскированное отображаемое имя (без PII). |
owners[].language | Предпочитаемая локаль. |
owners[].country_id | Дополненный нулями числовой код ISO 3166-1 в виде строки (например, "804"). |
owners[].is_main_owner | true для основного владельца (animal_user_relation типа main_owner); false для совладельцев. |
Статусы ответа
| Статус | Значение |
|---|---|
200 | OK — массив (пустой, если у владельца нет email в записи при поиске только по телефону). |
422 | Требуется email_or_phone. |
/v1/partner/animals/{id}Полная карточка животного.▸Авторизация: HMAC. Любой партнёрский ключ.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
id | да | path string | — | Публичный идентификатор животного (NanoID). |
Пример ответа
{ "payload": { "id": "8xK3pQzVnB7rL2qF", "species": 3, "nickname": "Барсік", "microchip": "900263000123456", "deceased": false, "abilities": { "can_edit": true } } }Возможности
Каждый объект животного содержит объект abilities, описывающий, что аутентифицированный пользователь-партнёр может делать с этим животным. Это первый флаг доступа; со временем будут добавлены и другие.
| Поле | Описание |
|---|---|
abilities.can_edit | Может ли аутентифицированный пользователь-партнёр редактировать это животное — обновлять его данные, добавлять процедуры и управлять фотографиями. True, когда пользователь зарегистрировал животное, имеет к нему любое отношение (animal_user_relation) или является активным участником организации, в реестре которой оно числится (org_animals). |
Расширение (X-Eternity-Expand)
Необязательный заголовок, содержащий JSON-массив ключей расширения (например, ["owners"]). Каждый запрошенный ключ встраивает дополнительное поле в каждый объект животного в ответе; опустите заголовок, чтобы получить обычную карточку. Неизвестные ключи отклоняются с 422.
| Ключ | Добавляет | Описание |
|---|---|---|
owners | owners[] | Владельцы животного — основной владелец плюс любые совладельцы — каждый с контактными данными и флагом is_main_owner. Доступно только в этом партнёрском интерфейсе. |
"owners": [
{
"user_gid": 90231,
"public_id": "V1StGXR8Z5jd",
"has_account": true,
"email": "jane@example.com",
"phone": "+380681234567",
"display_hint": "Ja*** D.",
"language": "uk",
"country_id": "804",
"is_main_owner": true
}
]| Поле | Описание |
|---|---|
owners[].public_id | Стабильный публичный идентификатор владельца — передайте его в POST /animals owners[].public_id (версия API >= 2026-07-04). |
owners[].user_gid | Устаревший числовой идентификатор владельца (привязка владельца в старых версиях API). |
owners[].has_account | Есть ли у владельца уже работающая учётная запись. |
owners[].email | Email, имеющийся в записи для этого владельца (null, если неизвестен). |
owners[].phone | Телефон, имеющийся в записи для этого владельца (null, если неизвестен). |
owners[].display_hint | Маскированное отображаемое имя (без PII). |
owners[].language | Предпочитаемая локаль. |
owners[].country_id | Дополненный нулями числовой код ISO 3166-1 в виде строки (например, "804"). |
owners[].is_main_owner | true для основного владельца (animal_user_relation типа main_owner); false для совладельцев. |
Статусы ответа
| Статус | Значение |
|---|---|
200 | OK. |
404 | Животное не найдено. |
/v1/partner/animals/{id}Обновить изменяемые поля / отметить как умершее.▸Авторизация: HMAC. Владелец ИЛИ ветеринар с активным отношением к животному. · X-Eternity-Idempotency-Key обязательно
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
nickname | нет | string | — | Новая кличка. |
color | нет | string | — | Новый окрас (свободный текст — без словаря). |
sterilization_status | нет | bool | — | Установить флаг стерилизации. |
deceased | нет | bool | — | true → пометить животное умершим. |
Тело запроса (JSON)
{
"nickname": "Барсік",
"color": "black",
"sterilization_status": true,
"deceased": false
}Пример ответа
204 No Content
Статусы ответа
| Статус | Значение |
|---|---|
204 | Обновлено. |
403 | Нет доступа к этому животному — запросите его (POST /v1/partner/animals/{id}/access-request) и повторите после одобрения владельцем. |
409 | Конфликт X-Eternity-Idempotency-Key. |
422 | Ошибка валидации. |
/v1/partner/animals/{id}/access-requestПопросить владельца животного предоставить вам доступ.▸Авторизация: HMAC. Ключ ветеринара/организации. · X-Eternity-Idempotency-Key обязательно
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
id | да | path string | — | Публичный идентификатор животного (NanoID). |
Пример ответа
{
"payload": {
"status": "pending",
"requested_at": "2026-06-23T09:00:00+00:00",
"expires_at": "2026-06-30T09:00:00+00:00",
"retry_after_seconds": 604800
}
}| Поле | Описание |
|---|---|
status | "granted" — у вас уже есть доступ, запрос не создавался; "pending" — ожидается решение владельца; "denied" — владелец отказал (можно пересмотреть до истечения срока). |
requested_at | Когда был создан запрос (null при status="granted"). |
expires_at | Когда запрос истекает и вы можете запросить снова (null при status="granted"). |
retry_after_seconds | Секунды до того, как вы сможете снова запросить то же животное (0 после истечения; null при status="granted"). |
Статусы ответа
| Статус | Значение |
|---|---|
201 | Создан новый запрос на доступ — владелец уведомлён по email. |
200 | Новый запрос не создан: у вас уже есть доступ (status "granted") или активный запрос уже существует (status "pending"/"denied") — подождите retry_after_seconds перед повторным запросом. |
404 | Животное не найдено. |
/v1/partner/animals/{id}/access-requestПроверить, одобрен ли ваш запрос на доступ.▸Авторизация: HMAC. Ключ ветеринара/организации.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
id | да | path string | — | Публичный идентификатор животного (NanoID). |
Пример ответа
{
"payload": {
"status": "granted",
"requested_at": "2026-06-23T09:00:00+00:00",
"expires_at": "2026-06-30T09:00:00+00:00",
"retry_after_seconds": 0
}
}| Поле | Описание |
|---|---|
status | "granted" — доступ одобрен и активен; "pending" — ожидается решение владельца; "denied" — отклонён; "none" — нет активного запроса. |
Статусы ответа
| Статус | Значение |
|---|---|
200 | OK. |
404 | Животное не найдено. |
/v1/partner/animals/{id}/proceduresЗапись процедур; открывает приём. Один объект или массив.▸Авторизация: HMAC. Ключ ветеринара/организации с доступом к животному. · X-Eternity-Idempotency-Key обязательно
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
(body) | да | object | array | — | Один объект процедуры или их массив (≤100). |
type | да | int | procedure_types | Идентификатор каталога процедур: 10 вакцинация, 20 вакцинация от бешенства, 30 идентификация транспондером, 40 идентификация токеном, 50 дегельминтизация, 60 стерилизация, 70 эвтаназия / удостоверение смерти. |
occurred_at | да | datetime | — | Когда выполнено (ISO 8601). |
summary | нет | string | — | Заметка в свободной форме. |
revaccination_date | нет | date | — | Переопределить дату следующей вакцинации (вакцинации). |
type_specific_payload | нет | object | — | Поля по типу — валидируются на стороне сервера: 10/20 → {vaccine_name*, batch_number*}; 30 → {transponder_number* — 15 digits}; 40 → {token_number*}; 50 → {drug*, batch_number?, dose?}; 60 → {method?, anesthesia_type?}; 70 → {reason*, death_date*}. Для типа 30: если у животного уже есть микрочип, он сохраняется — новый номер записывается как дополнительный идентификатор (other_identifiers тип 11) вместо замены чипа. |
Тело запроса (JSON)
[
{
"type": 10,
"occurred_at": "2026-05-30T08:00:00+00:00",
"summary": "Annual shot",
"revaccination_date": "2027-05-30",
"type_specific_payload": {
"vaccine_name": "Nobivac",
"batch_number": "A123"
}
},
{
"type": 30,
"occurred_at": "2026-05-30T08:05:00+00:00",
"type_specific_payload": {
"transponder_number": "900263000123456"
}
}
]Пример ответа
{
"payload": {
"appointment_id": 7741,
"procedures": [
{ "id": 99001, "animal_id": "8xK3pQzVnB7rL2qF", "visit_id": 7741, "type": 10,
"occurred_at": "2026-05-30T08:00:00+00:00", "summary": "Annual shot", "revaccination_date": "2027-05-30",
"type_specific_payload": { "vaccine_name": "Nobivac", "batch_number": "A123" } }
]
}
}| Поле | Описание |
|---|---|
appointment_id | Визит, открытый/использованный для этой партии. |
procedures[] | The recorded procedures — same card as GET list/show. |
procedures[].id | Идентификатор записи процедуры. |
procedures[].type | Numeric type id (procedure_types dictionary). |
procedures[].type_specific_payload | Сохранённые данные, специфичные для типа. |
Статусы ответа
| Статус | Значение |
|---|---|
201 | Записано; визит открыт. |
403 | Нет доступа к этому животному — запросите его (POST /v1/partner/animals/{id}/access-request) и повторите после одобрения владельцем. |
404 | Животное не найдено. |
409 | Конфликт X-Eternity-Idempotency-Key. |
422 | Неподдерживаемый тип, отсутствует occurred_at или отсутствуют поля, специфичные для типа. |
/v1/partner/animals/{id}/proceduresСписок процедур животного. Всегда массив.▸Авторизация: HMAC. Ключ ветеринара/организации.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
type | нет | int | procedure_types | Фильтровать по идентификатору каталога процедур. |
since | нет | datetime | — | Только в эту дату или после неё. |
until | нет | datetime | — | Только в эту дату или до неё. |
Пример ответа
{
"payload": [
{ "id": 99001, "animal_id": "8xK3pQzVnB7rL2qF", "visit_id": 7741, "type": 10,
"occurred_at": "2026-05-30T08:00:00+00:00", "summary": null, "revaccination_date": "2027-05-30",
"type_specific_payload": { "vaccine_name": "Nobivac", "batch_number": "A123" } }
]
}| Поле | Описание |
|---|---|
type | Идентификатор каталога процедур (словарь procedure_types). |
visit_id | Приём, под которым была записана процедура. |
type_specific_payload | Поля по типу. |
Статусы ответа
| Статус | Значение |
|---|---|
200 | OK — массив (возможно, пустой). |
404 | Животное не найдено. |
/v1/partner/procedures/{id}Одна процедура.▸Авторизация: HMAC. Ключ ветеринара/организации.
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
id | да | path int | — | Идентификатор записи процедуры. |
Пример ответа
{
"payload": {
"id": 99001, "animal_id": "8xK3pQzVnB7rL2qF", "visit_id": 7741, "type": 10,
"occurred_at": "2026-05-30T08:00:00+00:00", "summary": null, "revaccination_date": "2027-05-30",
"type_specific_payload": { "vaccine_name": "Nobivac", "batch_number": "A123" }
}
}Статусы ответа
| Статус | Значение |
|---|---|
200 | OK. |
404 | Процедура не найдена. |
/v1/partner/animals/{id}/photosЗагрузить фото (multipart). Владелец или ветеринар со связью.▸Авторизация: HMAC + multipart/form-data. Владелец ИЛИ ветеринар с отношением. · X-Eternity-Idempotency-Key обязательно
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
file | да | file | — | Файл изображения. Максимум 8 MB на фото (config partner.photos.max_single_mb). |
kind | нет | enum | — | avatar | gallery | nose_print. avatar устанавливает главное фото. По умолчанию gallery. |
Пример ответа
{ "payload": { "id": 33015 } }| Поле | Описание |
|---|---|
id | Идентификатор нового фото. |
Статусы ответа
| Статус | Значение |
|---|---|
201 | Загружено. |
403 | Нет доступа к этому животному — запросите его (POST /v1/partner/animals/{id}/access-request) и повторите после одобрения владельцем. |
422 | Недопустимый файл или больше 8 MB (уменьшите размер / понизьте качество). |
413 | Весь запрос превышает 15 MB. |
/v1/partner/animals/{id}/photos/{photoId}Мягкое удаление фото.▸Авторизация: HMAC. Владелец ИЛИ ветеринар с отношением. · X-Eternity-Idempotency-Key обязательно
Поля запроса
| Поле | Обяз. | Тип | Словарь | Описание |
|---|---|---|---|---|
id | да | path string | — | Публичный идентификатор животного (NanoID). |
photoId | да | path int | — | Идентификатор фото. |
Пример ответа
204 No Content
Статусы ответа
| Статус | Значение |
|---|---|
204 | Удалено. |
403 | Нет доступа к этому животному — запросите его (POST /v1/partner/animals/{id}/access-request) и повторите после одобрения владельцем. |