Métodos de la API
Documentación de la Partner API
Esta referencia describe cómo su integración se comunica con Animal-ID en nombre de su organización: autenticación, encabezados obligatorios y los endpoints principales del MVP (propietarios, animales, procedimientos, fotos, diccionarios).
SDK y bibliotecas cliente
SDK oficiales de código abierto que encapsulan la firma de solicitudes y los endpoints de abajo — úsalos en lugar de firmar las solicitudes a mano.
PHP · Composer
SDK de PHP del lado servidor: firma HMAC de solicitudes y clientes tipados para cada endpoint.
composer require animal-id/aid-partner-sdk
JavaScript · npm
Paquetes de frameworks para Node y el navegador, sobre un núcleo común.
npm i @animal-id/partner-core
- @animal-id/partner-coreNúcleo independiente del framework: firma + cliente tipado.
- @animal-id/partner-reactHooks y utilidades de React.
- @animal-id/partner-vueComposables de Vue.
- @animal-id/partner-angularServicios de Angular.
- @animal-id/partner-nestjsMódulo de NestJS para integraciones del lado del servidor.
Autenticación y firma de la solicitud
URL base: https://gw.animal-id.net · todas las rutas llevan el prefijo /v1/partner.
Cada solicitud firmada lleva cuatro encabezados. La firma es un HMAC-SHA256 (hex) de una cadena canónica, con clave de tu clave privada:
stringToSign = METHOD + "\n" + path[?query] + "\n" + sha256_hex(rawBody) + "\n" + timestamp signature = hex( hmac_sha256(stringToSign, privateKey) )
| Encabezado | Valor |
|---|---|
X-Eternity-App-Id | El id de tu aplicación. |
X-Eternity-Public-Key | Tu clave pública. |
X-Eternity-Timestamp | Segundos Unix; debe estar dentro de ±300s respecto a la hora del servidor. |
X-Eternity-Signature | El hex HMAC-SHA256 calculado arriba. |
X-Eternity-Idempotency-Key | UUID, obligatorio en cada POST/PATCH/DELETE. Las repeticiones devuelven la primera respuesta; misma clave + cuerpo distinto → 409. |
X-Eternity-Animal-ID-Version | Versión de fecha opcional (YYYY-MM-DD). Por defecto, la versión fijada cuando se emitió tu aplicación; a partir de 2026-07-04, el registro de animales adjunta propietarios existentes por public_id en lugar de user_gid. |
X-Eternity-Expand | Opcional. Array JSON de claves de expansión para incrustar datos adicionales en los endpoints compatibles (p. ej. ["owners"]). Consulta la sección «Expansión» de un endpoint para ver sus claves permitidas; las claves desconocidas → 422. |
Firma y envía los mismos bytes exactos del cuerpo. Para GET/DELETE el cuerpo está vacío (su sha256 es el hash de una cadena vacía). path incluye la cadena de consulta cuando está presente. Para las cargas multipart/form-data (fotos), el cuerpo en bruto tampoco forma parte de la firma: firma con el sha256 de un cuerpo vacío.
Cada respuesta exitosa envuelve los resultados en un array payload. Los endpoints de creación/obtención de un solo recurso devuelven un array de un elemento (p. ej. { "payload": [ { … } ] }); los ejemplos por endpoint que aparecen abajo muestran el objeto único por brevedad.
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);Webhooks
Reciba eventos diferidos — como cuando un propietario aprueba la solicitud de acceso de un veterinario — como solicitudes POST firmadas a su servidor. Configure la URL y encuentre su secreto de firma en la pestaña de claves de API.
Entrega de webhook firmadaFirma y datos del webhook▸Enviamos un POST por cada evento a la URL de webhook que configure, firmado con el secreto de webhook propio de su aplicación (se muestra una sola vez al generarlo). Verifique la firma antes de confiar en una entrega recalculando el HMAC sobre la cadena canónica y comparando en tiempo constante:
canonical = "POST" + "\n" + path[?query] + "\n" + sha256_hex(rawBody) + "\n" + timestamp signature = hex( hmac_sha256(canonical, webhookSecret) )
| Encabezado | Valor |
|---|---|
X-Eternity-Webhook-Id | Identificador único de entrega (UUID); estable entre reenvíos del mismo evento. |
X-Eternity-Webhook-Event | La clave del evento, p. ej. animal_access.approved. |
X-Eternity-Webhook-Timestamp | Segundos Unix en que se firmó la entrega; rechace las entregas con una marca de tiempo desfasada. |
X-Eternity-Webhook-Signature | HMAC-SHA256 (hex) de la cadena canónica, con su secreto de webhook como clave (no su clave privada de API). |
Cuerpo de la entrega (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"
}
}| Campo | Descripción |
|---|---|
id | El identificador único del evento (coincide con el encabezado X-Eternity-Webhook-Id). |
event | La clave del evento, p. ej. animal_access.approved. |
occurred_at | Cuándo ocurrió el evento (ISO 8601). |
result | La decisión de acceso. Refleja GET /v1/partner/animals/{id}/access-request y añade animal_id, requester_user_gid y decided_at. |
Eventos
| Clave | Significado |
|---|---|
animal_access.approved | Un propietario aprobó su solicitud de acceso pendiente: ya puede editar el animal. |
animal_access.denied | Un propietario rechazó su solicitud de acceso: puede volver a solicitarla cuando caduque. |
Confirme con cualquier respuesta 2xx. Un estado distinto de 2xx o un tiempo de espera agotado se registra como una entrega fallida, que puede reenviar desde el registro de entregas de webhook en su panel.
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 2xxMétodos de la API
Empiece a escribir — las secciones se filtran por palabra clave.
/v1/partner/dictionariesDiccionarios de referencia (multilingües, con filtro).▸Autenticación: Público (no requiere firma). Cacheable por CDN mediante ETag.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
include | no | string (csv) | — | Claves de diccionario separadas por comas que se devolverán; vacío → todas. Claves: species, sex, sizes, lost_statuses, other_identifiers, procedure_types, countries, languages, cites. |
q | no | string | — | Filtra las entradas por nombre localizado en cualquier idioma activo. |
lang | no | string | languages | Proyecta los nombres a un solo idioma (uk, en, ru, de, es). Por defecto: todos los activos. |
Respuesta de ejemplo
{
"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
}| Campo | Descripción |
|---|---|
payload[].key | Clave del diccionario. |
payload[].items[].code | Id estable usado como valor en los endpoints de escritura. Numérico para la mayoría de los diccionarios (species, sex, …); para countries es el código numérico ISO 3166-1 rellenado con ceros como cadena (p. ej. "004", "804"); para languages es el código ISO 639-1 (p. ej. "uk"). |
payload[].items[].names | Asigna idioma → nombre localizado. Los idiomas sin traducción recurren al inglés. |
payload[].items[].alpha2 / alpha3 | Solo countries: códigos ISO 3166-1 alpha-2 / alpha-3 para una correspondencia cómoda. |
payload[].items[].native | Solo languages: el nombre propio del idioma (endónimo), útil para selectores de idioma. |
metadata.etag | Devuélvelo en If-None-Match para obtener 304 cuando no haya cambios. |
metadata.languages | Idiomas activos realmente presentes en esta compilación. |
Estados de la respuesta
| Estado | Significado |
|---|---|
200 | OK — diccionarios devueltos. |
304 | No Modificado — tu If-None-Match coincide; reutiliza la copia en caché. |
/v1/partner/ownersCrear (o resolver) un propietario; devuelve el ID global del usuario.▸Autenticación: HMAC. Cualquier clave de asociado. · X-Eternity-Idempotency-Key obligatorio
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
email | condicional | string | — | Correo electrónico del propietario. Se requiere uno de email/phone: es la forma en que se contacta/autentica al propietario más adelante. |
phone | condicional | string | — | Teléfono del propietario (E.164). Se requiere uno de email/phone. |
first_name | no | string | — | Nombre de pila. |
last_name | no | string | — | Apellido. |
language | no | string | languages | Idioma preferido. |
country | no | string | countries | Código numérico ISO 3166-1 rellenado con ceros como cadena (p. ej. "804"): coincide con el código del diccionario countries. |
consent | sí | object | — | Bloque de consentimiento (auditoría inmutable). |
consent.account_creation | sí | bool | — | Debe ser true: el propietario aceptó la creación de la cuenta. El momento de la captura se registra en el servidor. |
Cuerpo de la solicitud (JSON)
{
"email": "jane@example.com",
"phone": "+380681234567",
"first_name": "Jane",
"last_name": "Doe",
"language": "uk",
"country": "804",
"consent": {
"account_creation": true
}
}Respuesta de ejemplo
{
"payload": {
"user_gid": 90231,
"public_id": "V1StGXR8Z5jd",
"has_account": true,
"email": "jane@example.com",
"phone": null,
"display_hint": "Ол*** К.",
"language": "uk",
"country_id": 804
}
}| Campo | Descripción |
|---|---|
public_id | Identificador público estable del propietario: pásalo a POST /animals owners[].public_id (versión de API >= 2026-07-04). |
user_gid | Identificador numérico heredado del propietario (adjuntar propietario en versiones anteriores de la API). |
has_account | Si el propietario ya tiene una cuenta utilizable. |
email | Correo electrónico registrado para este propietario (null si se desconoce). |
phone | Teléfono registrado para este propietario (null si se desconoce). |
display_hint | Nombre para mostrar enmascarado (sin datos personales). |
Estados de la respuesta
| Estado | Significado |
|---|---|
201 | Creado (o se resolvió un propietario existente: idempotente por email/phone). |
409 | X-Eternity-Idempotency-Key reutilizada con un cuerpo distinto, o aún en proceso. |
422 | Error de validación (falta email/phone, o no se aceptó consent.account_creation). |
/v1/partner/owners/searchBuscar un propietario por email o teléfono exacto.▸Autenticación: HMAC. Cualquier clave de asociado.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
email_or_phone | sí | string | — | Correo electrónico o teléfono exacto (campo único; el correo se detecta por su formato). |
Respuesta de ejemplo
{
"payload": { "user_gid": 90231, "public_id": "V1StGXR8Z5jd", "has_account": true, "email": "jane@example.com", "phone": null, "display_hint": "Ол*** К.", "language": "uk", "country_id": 804 }
}| Campo | Descripción |
|---|---|
public_id | Identificador público estable del propietario: pásalo a POST /animals owners[].public_id (versión de API >= 2026-07-04). |
user_gid | Identificador numérico heredado del propietario (adjuntar propietario en versiones anteriores de la API). |
email | Correo electrónico registrado para este propietario (null si se desconoce). |
phone | Teléfono registrado para este propietario (null si se desconoce). |
Estados de la respuesta
| Estado | Significado |
|---|---|
200 | Propietario encontrado. |
404 | No hay ningún propietario con ese email_or_phone. |
422 | email_or_phone es obligatorio. |
/v1/partner/animalsRegistrar un animal (basado en chip).▸Autenticación: HMAC. Clave de veterinario/organización. · X-Eternity-Idempotency-Key obligatorio
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
species | sí | int | species | Id del diccionario species. |
is_microchip | sí | bool | — | Si el animal está microchipeado. true → microchip es obligatorio; false → microchip se ignora y el registro asigna un número WC temporal. |
microchip | condicional | string | — | Microchip (transpondedor). Obligatorio solo cuando is_microchip = true. |
nickname | sí | string | — | Nombre del animal. |
qr_tag | no | string | — | Número de serie del pasaporte QR que se adjuntará en el registro. |
owners | no | array | — | Propietarios; el primero se convierte en main_owner, el resto en owners (sin duplicados). Cada entrada adjunta un propietario existente O registra uno nuevo en línea. |
owners[].public_id | condicional | string | — | Modo adjuntar (versión de API >= 2026-07-04): public_id de un propietario existente de POST/GET owners. Omitir para registrar en línea. |
owners[].user_gid | condicional | int | — | Modo adjuntar (versiones de API anteriores a 2026-07-04): identificador numérico heredado del propietario. Omitir para registrar en línea. |
owners[].email | condicional | string | — | Modo en línea: correo del propietario. Se requiere uno de correo/teléfono cuando no hay public_id/user_gid (upsert: sin duplicados). |
owners[].phone | condicional | string | — | Modo en línea: teléfono del propietario (E.164). |
owners[].first_name | no | string | — | Modo en línea: nombre de pila. |
owners[].last_name | no | string | — | Modo en línea: apellido. |
owners[].language | no | string | languages | Modo en línea: idioma preferido. |
owners[].country | no | string | countries | Modo en línea: código numérico ISO 3166-1 rellenado con ceros como cadena (p. ej. "804"): coincide con el código del diccionario countries. |
owners[].consent.account_creation | condicional | bool | — | Modo en línea: debe ser true: el propietario aceptó la creación de la cuenta. Obligatorio solo al registrar en línea. |
breed | no | string | — | Raza (texto libre, sin diccionario). |
color | no | string | — | Color (texto libre, sin diccionario). |
gender_id | no | int | sex | Id del diccionario gender. |
dob | no | date | — | Fecha de nacimiento (ISO 8601). |
microchip_date | no | date | — | Cuándo se implantó el chip (ISO 8601). |
sterilization | no | bool | — | Indicador de esterilizado. |
size | no | int | sizes | Id del diccionario size. |
identifiers | no | array | — | Identificadores adicionales además del microchip/qr. |
identifiers[].type | sí | int | other_identifiers | Id de tipo de identificador del diccionario other_identifiers (tattoo, ring, …). |
identifiers[].value | sí | string | — | Valor del identificador. |
identifiers[].added_at | no | date | — | Cuándo se asignó el identificador (ISO 8601). |
Cuerpo de la solicitud (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" }
]
}Respuesta de ejemplo
{ "payload": { "id": "8xK3pQzVnB7rL2qF" } }| Campo | Descripción |
|---|---|
id | Id público inadivinable del animal (NanoID). Úsalo en todas las llamadas posteriores del animal. |
Estados de la respuesta
| Estado | Significado |
|---|---|
201 | Animal registrado. |
409 | Conflicto de X-Eternity-Idempotency-Key (misma clave, cuerpo distinto). |
422 | Error de validación: falta species/nickname/is_microchip; is_microchip=true sin un microchip válido; un microchip duplicado (transpondedor ya registrado, campo "transponder"); o un propietario en línea sin email/phone o sin consent.account_creation. |
/v1/partner/animals/by-identifier/{type}/{value}Búsqueda por un tipo de identificador concreto. Siempre un array.▸Autenticación: HMAC. Cualquier clave de asociado.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
type | sí | path enum | — | microchip o qr_tag. |
value | sí | path string | — | Valor del identificador a buscar. |
Respuesta de ejemplo
{
"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 } }
]
}| Campo | Descripción |
|---|---|
payload | Array de fichas de animal: normalmente una, pero un valor puede resolverse en varias. |
microchip / qr_tag | Identificadores activos. |
lost_status | "active" cuando se reporta perdido, de lo contrario null. |
deceased | True una vez que se registra la eutanasia/muerte. |
Capacidades
Cada objeto de animal incluye un objeto abilities que describe lo que el usuario asociado autenticado puede hacer con este animal. Este es el primer indicador de acceso; se añadirán más con el tiempo.
| Campo | Descripción |
|---|---|
abilities.can_edit | Si el usuario asociado autenticado puede editar este animal: actualizar sus datos, añadir procedimientos y gestionar fotos. True cuando el usuario registró el animal, tiene cualquier relación con él (animal_user_relation), o es miembro activo de una organización cuyo registro lo contiene (org_animals). |
Expansión (X-Eternity-Expand)
Encabezado opcional que lleva un array JSON de claves de expansión (p. ej. ["owners"]). Cada clave solicitada incrusta un campo adicional en cada objeto de animal de la respuesta; omite el encabezado para obtener la ficha simple. Las claves desconocidas se rechazan con 422.
| Clave | Añade | Descripción |
|---|---|---|
owners | owners[] | Los propietarios del animal —el propietario principal más cualquier copropietario— cada uno con datos de contacto y un indicador is_main_owner. Disponible solo en esta superficie de asociados. |
"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
}
]| Campo | Descripción |
|---|---|
owners[].public_id | Identificador público estable del propietario: pásalo a POST /animals owners[].public_id (versión de API >= 2026-07-04). |
owners[].user_gid | Identificador numérico heredado del propietario (adjuntar propietario en versiones anteriores de la API). |
owners[].has_account | Si el propietario ya tiene una cuenta utilizable. |
owners[].email | Correo electrónico registrado para este propietario (null si se desconoce). |
owners[].phone | Teléfono registrado para este propietario (null si se desconoce). |
owners[].display_hint | Nombre para mostrar enmascarado (sin datos personales). |
owners[].language | Idioma preferido. |
owners[].country_id | Código numérico ISO 3166-1 rellenado con ceros como cadena (p. ej. "804"). |
owners[].is_main_owner | true para el propietario principal (animal_user_relation tipo main_owner); false para los copropietarios. |
Estados de la respuesta
| Estado | Significado |
|---|---|
200 | OK — array (posiblemente vacío). |
/v1/partner/animals/by-identifier/{value}Búsqueda por todos los tipos de identificador a la vez. Siempre un array.▸Autenticación: HMAC. Cualquier clave de asociado.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
value | sí | path string | — | Valor del identificador; se busca en microchip y qr_tag. |
Respuesta de ejemplo
{ "payload": [ { "id": "8xK3pQzVnB7rL2qF", "nickname": "Барсік", "microchip": "900263000123456" } ] }Capacidades
Cada objeto de animal incluye un objeto abilities que describe lo que el usuario asociado autenticado puede hacer con este animal. Este es el primer indicador de acceso; se añadirán más con el tiempo.
| Campo | Descripción |
|---|---|
abilities.can_edit | Si el usuario asociado autenticado puede editar este animal: actualizar sus datos, añadir procedimientos y gestionar fotos. True cuando el usuario registró el animal, tiene cualquier relación con él (animal_user_relation), o es miembro activo de una organización cuyo registro lo contiene (org_animals). |
Expansión (X-Eternity-Expand)
Encabezado opcional que lleva un array JSON de claves de expansión (p. ej. ["owners"]). Cada clave solicitada incrusta un campo adicional en cada objeto de animal de la respuesta; omite el encabezado para obtener la ficha simple. Las claves desconocidas se rechazan con 422.
| Clave | Añade | Descripción |
|---|---|---|
owners | owners[] | Los propietarios del animal —el propietario principal más cualquier copropietario— cada uno con datos de contacto y un indicador is_main_owner. Disponible solo en esta superficie de asociados. |
"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
}
]| Campo | Descripción |
|---|---|
owners[].public_id | Identificador público estable del propietario: pásalo a POST /animals owners[].public_id (versión de API >= 2026-07-04). |
owners[].user_gid | Identificador numérico heredado del propietario (adjuntar propietario en versiones anteriores de la API). |
owners[].has_account | Si el propietario ya tiene una cuenta utilizable. |
owners[].email | Correo electrónico registrado para este propietario (null si se desconoce). |
owners[].phone | Teléfono registrado para este propietario (null si se desconoce). |
owners[].display_hint | Nombre para mostrar enmascarado (sin datos personales). |
owners[].language | Idioma preferido. |
owners[].country_id | Código numérico ISO 3166-1 rellenado con ceros como cadena (p. ej. "804"). |
owners[].is_main_owner | true para el propietario principal (animal_user_relation tipo main_owner); false para los copropietarios. |
Estados de la respuesta
| Estado | Significado |
|---|---|
200 | OK — array (posiblemente vacío). |
/v1/partner/animals/by-ownerBuscar animales por el contacto del propietario. Siempre un array.▸Autenticación: HMAC. Cualquier clave de asociado.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
email_or_phone | sí | string | — | Correo electrónico o teléfono exacto del propietario (campo único; el correo se detecta por su formato). Un teléfono se resuelve primero al propietario. |
Respuesta de ejemplo
{ "payload": [ { "id": "8xK3pQzVnB7rL2qF", "nickname": "Барсік", "species": 3 } ] }Capacidades
Cada objeto de animal incluye un objeto abilities que describe lo que el usuario asociado autenticado puede hacer con este animal. Este es el primer indicador de acceso; se añadirán más con el tiempo.
| Campo | Descripción |
|---|---|
abilities.can_edit | Si el usuario asociado autenticado puede editar este animal: actualizar sus datos, añadir procedimientos y gestionar fotos. True cuando el usuario registró el animal, tiene cualquier relación con él (animal_user_relation), o es miembro activo de una organización cuyo registro lo contiene (org_animals). |
Expansión (X-Eternity-Expand)
Encabezado opcional que lleva un array JSON de claves de expansión (p. ej. ["owners"]). Cada clave solicitada incrusta un campo adicional en cada objeto de animal de la respuesta; omite el encabezado para obtener la ficha simple. Las claves desconocidas se rechazan con 422.
| Clave | Añade | Descripción |
|---|---|---|
owners | owners[] | Los propietarios del animal —el propietario principal más cualquier copropietario— cada uno con datos de contacto y un indicador is_main_owner. Disponible solo en esta superficie de asociados. |
"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
}
]| Campo | Descripción |
|---|---|
owners[].public_id | Identificador público estable del propietario: pásalo a POST /animals owners[].public_id (versión de API >= 2026-07-04). |
owners[].user_gid | Identificador numérico heredado del propietario (adjuntar propietario en versiones anteriores de la API). |
owners[].has_account | Si el propietario ya tiene una cuenta utilizable. |
owners[].email | Correo electrónico registrado para este propietario (null si se desconoce). |
owners[].phone | Teléfono registrado para este propietario (null si se desconoce). |
owners[].display_hint | Nombre para mostrar enmascarado (sin datos personales). |
owners[].language | Idioma preferido. |
owners[].country_id | Código numérico ISO 3166-1 rellenado con ceros como cadena (p. ej. "804"). |
owners[].is_main_owner | true para el propietario principal (animal_user_relation tipo main_owner); false para los copropietarios. |
Estados de la respuesta
| Estado | Significado |
|---|---|
200 | OK — array (vacío si el propietario no tiene un correo registrado para búsquedas solo por teléfono). |
422 | email_or_phone es obligatorio. |
/v1/partner/animals/{id}Ficha completa del animal.▸Autenticación: HMAC. Cualquier clave de asociado.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
id | sí | path string | — | Id público del animal (NanoID). |
Respuesta de ejemplo
{ "payload": { "id": "8xK3pQzVnB7rL2qF", "species": 3, "nickname": "Барсік", "microchip": "900263000123456", "deceased": false, "abilities": { "can_edit": true } } }Capacidades
Cada objeto de animal incluye un objeto abilities que describe lo que el usuario asociado autenticado puede hacer con este animal. Este es el primer indicador de acceso; se añadirán más con el tiempo.
| Campo | Descripción |
|---|---|
abilities.can_edit | Si el usuario asociado autenticado puede editar este animal: actualizar sus datos, añadir procedimientos y gestionar fotos. True cuando el usuario registró el animal, tiene cualquier relación con él (animal_user_relation), o es miembro activo de una organización cuyo registro lo contiene (org_animals). |
Expansión (X-Eternity-Expand)
Encabezado opcional que lleva un array JSON de claves de expansión (p. ej. ["owners"]). Cada clave solicitada incrusta un campo adicional en cada objeto de animal de la respuesta; omite el encabezado para obtener la ficha simple. Las claves desconocidas se rechazan con 422.
| Clave | Añade | Descripción |
|---|---|---|
owners | owners[] | Los propietarios del animal —el propietario principal más cualquier copropietario— cada uno con datos de contacto y un indicador is_main_owner. Disponible solo en esta superficie de asociados. |
"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
}
]| Campo | Descripción |
|---|---|
owners[].public_id | Identificador público estable del propietario: pásalo a POST /animals owners[].public_id (versión de API >= 2026-07-04). |
owners[].user_gid | Identificador numérico heredado del propietario (adjuntar propietario en versiones anteriores de la API). |
owners[].has_account | Si el propietario ya tiene una cuenta utilizable. |
owners[].email | Correo electrónico registrado para este propietario (null si se desconoce). |
owners[].phone | Teléfono registrado para este propietario (null si se desconoce). |
owners[].display_hint | Nombre para mostrar enmascarado (sin datos personales). |
owners[].language | Idioma preferido. |
owners[].country_id | Código numérico ISO 3166-1 rellenado con ceros como cadena (p. ej. "804"). |
owners[].is_main_owner | true para el propietario principal (animal_user_relation tipo main_owner); false para los copropietarios. |
Estados de la respuesta
| Estado | Significado |
|---|---|
200 | OK. |
404 | Animal no encontrado. |
/v1/partner/animals/{id}Actualizar campos modificables / marcar como fallecido.▸Autenticación: HMAC. Propietario O veterinario con una relación activa con el animal. · X-Eternity-Idempotency-Key obligatorio
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
nickname | no | string | — | Nombre nuevo. |
color | no | string | — | Color nuevo (texto libre, sin diccionario). |
sterilization_status | no | bool | — | Establecer el indicador de esterilizado. |
deceased | no | bool | — | true → marcar el animal como muerto. |
Cuerpo de la solicitud (JSON)
{
"nickname": "Барсік",
"color": "black",
"sterilization_status": true,
"deceased": false
}Respuesta de ejemplo
204 No Content
Estados de la respuesta
| Estado | Significado |
|---|---|
204 | Actualizado. |
403 | Sin acceso a este animal: solicítelo (POST /v1/partner/animals/{id}/access-request) y reinténtelo cuando el propietario lo apruebe. |
409 | Conflicto de X-Eternity-Idempotency-Key. |
422 | Error de validación. |
/v1/partner/animals/{id}/access-requestPedir al propietario del animal que le conceda acceso.▸Autenticación: HMAC. Clave de veterinario/organización. · X-Eternity-Idempotency-Key obligatorio
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
id | sí | path string | — | Id público del animal (NanoID). |
Respuesta de ejemplo
{
"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
}
}| Campo | Descripción |
|---|---|
status | "granted": ya tienes acceso, no se creó ninguna solicitud; "pending": a la espera del propietario; "denied": el propietario lo rechazó (reconsiderable hasta que caduque). |
requested_at | Cuándo se generó la solicitud (null cuando status="granted"). |
expires_at | Cuándo caduca la solicitud y puedes volver a solicitar (null cuando status="granted"). |
retry_after_seconds | Segundos hasta que puedas volver a solicitar el mismo animal (0 una vez transcurrido; null cuando status="granted"). |
Estados de la respuesta
| Estado | Significado |
|---|---|
201 | Se creó una nueva solicitud de acceso: se ha notificado al propietario por correo electrónico. |
200 | No se creó ninguna solicitud nueva: ya tienes acceso (status "granted"), o ya existe una solicitud activa (status "pending"/"denied"): espera retry_after_seconds antes de volver a solicitar. |
404 | Animal no encontrado. |
/v1/partner/animals/{id}/access-requestComprobar si su solicitud de acceso fue aprobada.▸Autenticación: HMAC. Clave de veterinario/organización.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
id | sí | path string | — | Id público del animal (NanoID). |
Respuesta de ejemplo
{
"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
}
}| Campo | Descripción |
|---|---|
status | "granted": el acceso se ha aprobado y está activo; "pending": a la espera del propietario; "denied": rechazado; "none": no hay ninguna solicitud activa. |
Estados de la respuesta
| Estado | Significado |
|---|---|
200 | OK. |
404 | Animal no encontrado. |
/v1/partner/animals/{id}/proceduresRegistrar procedimientos; abre una visita. Objeto único o array.▸Autenticación: HMAC. Clave de veterinario/organización con acceso al animal. · X-Eternity-Idempotency-Key obligatorio
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
(body) | sí | object | array | — | Un único objeto de procedimiento o un array de ellos (≤100). |
type | sí | int | procedure_types | Id del catálogo de procedimientos: 10 vacunación, 20 vacunación antirrábica, 30 identificación por transpondedor, 40 identificación por token, 50 desparasitación, 60 esterilización, 70 eutanasia / certificación de defunción. |
occurred_at | sí | datetime | — | Cuándo se realizó (ISO 8601). |
summary | no | string | — | Nota de texto libre. |
revaccination_date | no | date | — | Sobrescribir la fecha de la próxima vacunación (vacunaciones). |
type_specific_payload | no | object | — | Campos por tipo, validados en el servidor: 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*}. Para el tipo 30: si el animal ya lleva un microchip, se conserva; el nuevo número se almacena como un identificador adicional (other_identifiers tipo 11) en lugar de reemplazar el chip. |
Cuerpo de la solicitud (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"
}
}
]Respuesta de ejemplo
{
"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" } }
]
}
}| Campo | Descripción |
|---|---|
appointment_id | La visita abierta/utilizada para este lote. |
procedures[] | The recorded procedures — same card as GET list/show. |
procedures[].id | Id del registro del procedimiento. |
procedures[].type | Numeric type id (procedure_types dictionary). |
procedures[].type_specific_payload | Carga útil almacenada específica del tipo. |
Estados de la respuesta
| Estado | Significado |
|---|---|
201 | Registrado; visita abierta. |
403 | Sin acceso a este animal: solicítelo (POST /v1/partner/animals/{id}/access-request) y reinténtelo cuando el propietario lo apruebe. |
404 | Animal no encontrado. |
409 | Conflicto de X-Eternity-Idempotency-Key. |
422 | Tipo no admitido, falta occurred_at, o faltan campos específicos del tipo. |
/v1/partner/animals/{id}/proceduresListar los procedimientos de un animal. Siempre un array.▸Autenticación: HMAC. Clave de veterinario/organización.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
type | no | int | procedure_types | Filtrar por id del catálogo de procedimientos. |
since | no | datetime | — | Solo en o después de este momento. |
until | no | datetime | — | Solo en o antes de este momento. |
Respuesta de ejemplo
{
"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" } }
]
}| Campo | Descripción |
|---|---|
type | Id del catálogo de procedimientos (diccionario procedure_types). |
visit_id | Cita bajo la que se registró el procedimiento. |
type_specific_payload | Campos por tipo. |
Estados de la respuesta
| Estado | Significado |
|---|---|
200 | OK — array (posiblemente vacío). |
404 | Animal no encontrado. |
/v1/partner/procedures/{id}Procedimiento único.▸Autenticación: HMAC. Clave de veterinario/organización.
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
id | sí | path int | — | Id del registro del procedimiento. |
Respuesta de ejemplo
{
"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" }
}
}Estados de la respuesta
| Estado | Significado |
|---|---|
200 | OK. |
404 | Procedimiento no encontrado. |
/v1/partner/animals/{id}/photosSubir una foto (multipart). Propietario o veterinario con relación.▸Autenticación: HMAC + multipart/form-data. Propietario O veterinario con relación. · X-Eternity-Idempotency-Key obligatorio
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
file | sí | file | — | Archivo de imagen. Máximo 8 MB por foto (config partner.photos.max_single_mb). |
kind | no | enum | — | avatar | gallery | nose_print. avatar establece la foto principal. Por defecto gallery. |
Respuesta de ejemplo
{ "payload": { "id": 33015 } }| Campo | Descripción |
|---|---|
id | Id de la foto nueva. |
Estados de la respuesta
| Estado | Significado |
|---|---|
201 | Subido. |
403 | Sin acceso a este animal: solicítelo (POST /v1/partner/animals/{id}/access-request) y reinténtelo cuando el propietario lo apruebe. |
422 | Archivo no válido o de más de 8 MB (reduce el tamaño / baja la calidad). |
413 | La solicitud completa supera los 15 MB. |
/v1/partner/animals/{id}/photos/{photoId}Eliminación lógica de una foto.▸Autenticación: HMAC. Propietario O veterinario con relación. · X-Eternity-Idempotency-Key obligatorio
Campos de la solicitud
| Campo | Obl. | Tipo | Diccionario | Descripción |
|---|---|---|---|---|
id | sí | path string | — | Id público del animal (NanoID). |
photoId | sí | path int | — | Id de la foto. |
Respuesta de ejemplo
204 No Content
Estados de la respuesta
| Estado | Significado |
|---|---|
204 | Eliminado. |
403 | Sin acceso a este animal: solicítelo (POST /v1/partner/animals/{id}/access-request) y reinténtelo cuando el propietario lo apruebe. |